diff --git a/app/build.gradle b/app/build.gradle
index f57f6341..6b2cd06a 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -15,8 +15,9 @@ android {
buildTypes {
release {
- minifyEnabled true
- proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
+ minifyEnabled false
+ // minifyEnabled true
+ // proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
}
}
dexOptions {
diff --git a/app/lint.xml b/app/lint.xml
index d191f74b..69044e98 100644
--- a/app/lint.xml
+++ b/app/lint.xml
@@ -1,5 +1,6 @@
+
diff --git a/build.xml b/build.xml
deleted file mode 100644
index 4a6bb871..00000000
--- a/build.xml
+++ /dev/null
@@ -1,142 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Creating library output jar file...
-
-
-
-
-
-
- Custom jar packaging exclusion: ${android.package.excludes}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/compile-libs/lombok.jar b/compile-libs/lombok.jar
deleted file mode 100644
index 4141433c..00000000
Binary files a/compile-libs/lombok.jar and /dev/null differ
diff --git a/eclipse-libs/lombok-api.jar b/eclipse-libs/lombok-api.jar
deleted file mode 100644
index db3dff79..00000000
Binary files a/eclipse-libs/lombok-api.jar and /dev/null differ
diff --git a/libs/backport-util-concurrent-3.1.jar b/libs/backport-util-concurrent-3.1.jar
deleted file mode 100644
index 3a4c2797..00000000
Binary files a/libs/backport-util-concurrent-3.1.jar and /dev/null differ
diff --git a/libs/commons-codec-1.8.jar b/libs/commons-codec-1.8.jar
deleted file mode 100644
index 32f84c92..00000000
Binary files a/libs/commons-codec-1.8.jar and /dev/null differ
diff --git a/libs/commons-io-2.4.jar b/libs/commons-io-2.4.jar
deleted file mode 100644
index 90035a4f..00000000
Binary files a/libs/commons-io-2.4.jar and /dev/null differ
diff --git a/libs/commons-lang-2.6.jar b/libs/commons-lang-2.6.jar
deleted file mode 100644
index 98467d3a..00000000
Binary files a/libs/commons-lang-2.6.jar and /dev/null differ
diff --git a/libs/commons-logging-1.1.3.jar b/libs/commons-logging-1.1.3.jar
deleted file mode 100644
index ab512540..00000000
Binary files a/libs/commons-logging-1.1.3.jar and /dev/null differ
diff --git a/libs/ez-vcard-0.9.6.jar b/libs/ez-vcard-0.9.6.jar
deleted file mode 100644
index 0bb07c9d..00000000
Binary files a/libs/ez-vcard-0.9.6.jar and /dev/null differ
diff --git a/libs/httpclientandroidlib-1.2.1.jar b/libs/httpclientandroidlib-1.2.1.jar
deleted file mode 100644
index f409b0b2..00000000
Binary files a/libs/httpclientandroidlib-1.2.1.jar and /dev/null differ
diff --git a/libs/ical4j-1.0.6-davdroid141027.jar b/libs/ical4j-1.0.6-davdroid141027.jar
deleted file mode 100644
index 6a6ec1bf..00000000
Binary files a/libs/ical4j-1.0.6-davdroid141027.jar and /dev/null differ
diff --git a/libs/org.xbill.dns_2.1.6.jar b/libs/org.xbill.dns_2.1.6.jar
deleted file mode 100644
index 3d42fbc4..00000000
Binary files a/libs/org.xbill.dns_2.1.6.jar and /dev/null differ
diff --git a/libs/simple-xml-2.7.jar b/libs/simple-xml-2.7.jar
deleted file mode 100644
index cb12687a..00000000
Binary files a/libs/simple-xml-2.7.jar and /dev/null differ
diff --git a/proguard-project.txt b/proguard-project.txt
deleted file mode 100644
index 7c96635c..00000000
--- a/proguard-project.txt
+++ /dev/null
@@ -1,28 +0,0 @@
-# To enable ProGuard in your project, edit project.properties
-# to define the proguard.config property as described in that file.
-#
-# Add project specific ProGuard rules here.
-# By default, the flags in this file are appended to flags specified
-# in ${sdk.dir}/tools/proguard/proguard-android.txt
-# You can edit the include path and order by changing the ProGuard
-# include property in project.properties.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# Add any project specific keep options here:
--dontusemixedcaseclassnames
--dontskipnonpubliclibraryclassmembers
-
--dontwarn edu.emory.mathcs.backport.**
--dontwarn ezvcard.**
--dontwarn net.fortuna.**
--dontwarn org.apache.**
--dontwarn org.simpleframework.xml.**
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
diff --git a/project.properties b/project.properties
deleted file mode 100644
index 6e18427a..00000000
--- a/project.properties
+++ /dev/null
@@ -1,14 +0,0 @@
-# This file is automatically generated by Android Tools.
-# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
-#
-# This file must be checked in Version Control Systems.
-#
-# To customize properties used by the Ant build system edit
-# "ant.properties", and override values to adapt the script to your
-# project structure.
-#
-# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
-#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
-
-# Project target.
-target=android-21
diff --git a/src/at/bitfire/davdroid/ArrayUtils.java b/src/at/bitfire/davdroid/ArrayUtils.java
deleted file mode 100644
index f4f050db..00000000
--- a/src/at/bitfire/davdroid/ArrayUtils.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 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 java.lang.reflect.Array;
-
-public class ArrayUtils {
-
- @SuppressWarnings("unchecked")
- public static T[][] partition(T[] bigArray, int max) {
- int nItems = bigArray.length;
- int nPartArrays = (nItems + max-1)/max;
-
- T[][] partArrays = (T[][])Array.newInstance(bigArray.getClass().getComponentType(), nPartArrays, 0);
-
- // nItems is now the number of remaining items
- for (int i = 0; nItems > 0; i++) {
- int n = (nItems < max) ? nItems : max;
- partArrays[i] = (T[])Array.newInstance(bigArray.getClass().getComponentType(), n);
- System.arraycopy(bigArray, i*max, partArrays[i], 0, n);
-
- nItems -= n;
- }
-
- return partArrays;
- }
-
-}
diff --git a/src/at/bitfire/davdroid/Constants.java b/src/at/bitfire/davdroid/Constants.java
deleted file mode 100644
index cd0a4907..00000000
--- a/src/at/bitfire/davdroid/Constants.java
+++ /dev/null
@@ -1,18 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 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;
-
-public class Constants {
- public static final String
- APP_VERSION = "0.6.8",
- ACCOUNT_TYPE = "bitfire.at.davdroid",
- WEB_URL_HELP = "https://davdroid.bitfire.at/configuration?pk_campaign=davdroid-app",
-
- SETTING_DISABLE_COMPRESSION = "disable_compression",
- SETTING_NETWORK_LOGGING = "network_logging";
-}
diff --git a/src/at/bitfire/davdroid/MainActivity.java b/src/at/bitfire/davdroid/MainActivity.java
deleted file mode 100644
index a665f6a0..00000000
--- a/src/at/bitfire/davdroid/MainActivity.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 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.app.Activity;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.provider.Settings;
-import android.text.Html;
-import android.text.method.LinkMovementMethod;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.widget.TextView;
-import at.bitfire.davdroid.syncadapter.AddAccountActivity;
-import at.bitfire.davdroid.syncadapter.GeneralSettingsActivity;
-
-public class MainActivity extends Activity {
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- setContentView(R.layout.activity_main);
-
- TextView tvWorkaround = (TextView)findViewById(R.id.text_workaround);
- if (fromPlayStore()) {
- tvWorkaround.setVisibility(View.VISIBLE);
- tvWorkaround.setText(Html.fromHtml(getString(R.string.html_main_workaround)));
- tvWorkaround.setMovementMethod(LinkMovementMethod.getInstance());
- }
-
- TextView tvInfo = (TextView)findViewById(R.id.text_info);
- tvInfo.setText(Html.fromHtml(getString(R.string.html_main_info, Constants.APP_VERSION)));
- tvInfo.setMovementMethod(LinkMovementMethod.getInstance());
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- MenuInflater inflater = getMenuInflater();
- inflater.inflate(R.menu.main_activity, menu);
- return true;
- }
-
-
- public void addAccount(MenuItem item) {
- Intent intent = new Intent(this, AddAccountActivity.class);
- startActivity(intent);
- }
-
- public void showDebugSettings(MenuItem item) {
- Intent intent = new Intent(this, GeneralSettingsActivity.class);
- startActivity(intent);
- }
-
- public void showSyncSettings(MenuItem item) {
- Intent intent = new Intent(Settings.ACTION_SYNC_SETTINGS);
- startActivity(intent);
- }
-
- public void showWebsite(MenuItem item) {
- Intent intent = new Intent(Intent.ACTION_VIEW);
- intent.setData(Uri.parse(Constants.WEB_URL_HELP + "&pk_kwd=main-activity"));
- startActivity(intent);
- }
-
-
- private boolean fromPlayStore() {
- try {
- return "com.android.vending".equals(getPackageManager().getInstallerPackageName("at.bitfire.davdroid"));
- } catch(IllegalArgumentException e) {
- }
- return false;
- }
-}
diff --git a/src/at/bitfire/davdroid/URLUtils.java b/src/at/bitfire/davdroid/URLUtils.java
deleted file mode 100644
index 8c6860d0..00000000
--- a/src/at/bitfire/davdroid/URLUtils.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 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 java.net.MalformedURLException;
-import java.net.URL;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import android.annotation.SuppressLint;
-import android.util.Log;
-
-@SuppressLint("DefaultLocale")
-public class URLUtils {
- private static final String TAG = "davdroid.URIUtils";
-
-
- public static String ensureTrailingSlash(String href) {
- if (!href.endsWith("/")) {
- Log.d(TAG, "Implicitly appending trailing slash to collection " + href);
- return href + "/";
- } else
- return href;
- }
-
- public static URL ensureTrailingSlash(URL href) {
- if (!href.getPath().endsWith("/"))
- try {
- URL newURL = new URL(href, href.getPath() + "/");
-
- // "@" is the only character that is not encoded
- //newURL = new URI(newURI.toString().replaceAll("@", "%40"));
-
- Log.d(TAG, "Implicitly appending trailing slash to collection " + href + " -> " + newURL);
- return newURL;
- } catch (MalformedURLException e) {
- Log.e(TAG, "Couldn't append trailing slash to collection URI", e);
- }
- return href;
- }
-
-
- /** handles invalid URLs/paths as good as possible **/
- public static String sanitize(String original) {
- if (original == null)
- return null;
-
- Pattern p = Pattern.compile("^((https?:)?//([^/]+))?(.*)", Pattern.CASE_INSENSITIVE);
- // $1: "http://hostname" or "https://hostname" or "//hostname" or empty (hostname may end with":port")
- // $2: "http:" or "https:" or empty
- // $3: hostname (may end with ":port") or empty
- // $4: path or empty
-
- Matcher m = p.matcher(original);
- if (m.matches()) {
- String schema = m.group(2),
- host = m.group(3),
- path = m.group(4);
-
- if (host != null)
- // sanitize host name (don't replace "[", "]", ":" for IP address literals and port numbers)
- // "@" should be used for user name/password, but this case shouldn't appear in our URLs
- for (char c : new char[] { '@', ' ', '<', '>', '"', '#', '{', '}', '|', '\\', '^', '~', '`' })
- host = host.replace(String.valueOf(c), "%" + Integer.toHexString(c));
-
- if (path != null)
- // rewrite reserved characters:
- // ":" and "@" may be used in the host part but not in the path
- // rewrite unsafe characters:
- // " ", "<", ">", """, "#", "{", "}", "|", "\", "^", "~", "["], "]", "`"
- // do not rewrite "%" because we assume that URLs should be already encoded correctly
- for (char c : new char[] { ':', '@', ' ', '<', '>', '"', '#', '{', '}', '|', '\\', '^', '~', '[', ']', '`' })
- path = path.replace(String.valueOf(c), "%" + Integer.toHexString(c));
-
- String url = (schema != null) ? schema : "";
- if (host != null)
- url = url + "//" + host;
- if (path != null)
- url = url + path;
-
- if (!url.equals(original))
- Log.w(TAG, "Trying to repair invalid URL: " + original + " -> " + url);
- return url;
-
- } else {
-
- Log.w(TAG, "Couldn't sanitize URL: " + original);
- return original;
-
- }
- }
-
-}
diff --git a/src/at/bitfire/davdroid/resource/CalDavCalendar.java b/src/at/bitfire/davdroid/resource/CalDavCalendar.java
deleted file mode 100644
index 770f479e..00000000
--- a/src/at/bitfire/davdroid/resource/CalDavCalendar.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 Ricki Hirner (bitfire web engineering).
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the GNU Public License v3.0
- * which accompanies this distribution, and is available at
- * http://www.gnu.org/licenses/gpl.html
- ******************************************************************************/
-package at.bitfire.davdroid.resource;
-
-import java.net.MalformedURLException;
-
-import at.bitfire.davdroid.webdav.DavMultiget;
-import ch.boye.httpclientandroidlib.impl.client.CloseableHttpClient;
-
-public class CalDavCalendar extends RemoteCollection {
- //private final static String TAG = "davdroid.CalDavCalendar";
-
- @Override
- protected String memberContentType() {
- return "text/calendar";
- }
-
- @Override
- protected DavMultiget.Type multiGetType() {
- return DavMultiget.Type.CALENDAR;
- }
-
- @Override
- protected Event newResourceSkeleton(String name, String ETag) {
- return new Event(name, ETag);
- }
-
-
- public CalDavCalendar(CloseableHttpClient httpClient, String baseURL, String user, String password, boolean preemptiveAuth) throws MalformedURLException {
- super(httpClient, baseURL, user, password, preemptiveAuth);
- }
-}
diff --git a/src/at/bitfire/davdroid/resource/CardDavAddressBook.java b/src/at/bitfire/davdroid/resource/CardDavAddressBook.java
deleted file mode 100644
index eda04fb1..00000000
--- a/src/at/bitfire/davdroid/resource/CardDavAddressBook.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 Ricki Hirner (bitfire web engineering).
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the GNU Public License v3.0
- * which accompanies this distribution, and is available at
- * http://www.gnu.org/licenses/gpl.html
- ******************************************************************************/
-package at.bitfire.davdroid.resource;
-
-import java.net.MalformedURLException;
-
-import at.bitfire.davdroid.webdav.DavMultiget;
-import ch.boye.httpclientandroidlib.impl.client.CloseableHttpClient;
-
-public class CardDavAddressBook extends RemoteCollection {
- //private final static String TAG = "davdroid.CardDavAddressBook";
-
- @Override
- protected String memberContentType() {
- return Contact.MIME_TYPE;
- }
-
- @Override
- protected DavMultiget.Type multiGetType() {
- return DavMultiget.Type.ADDRESS_BOOK;
- }
-
- @Override
- protected Contact newResourceSkeleton(String name, String ETag) {
- return new Contact(name, ETag);
- }
-
-
- public CardDavAddressBook(CloseableHttpClient httpClient, String baseURL, String user, String password, boolean preemptiveAuth) throws MalformedURLException {
- super(httpClient, baseURL, user, password, preemptiveAuth);
- }
-}
diff --git a/src/at/bitfire/davdroid/resource/Contact.java b/src/at/bitfire/davdroid/resource/Contact.java
deleted file mode 100644
index d301450c..00000000
--- a/src/at/bitfire/davdroid/resource/Contact.java
+++ /dev/null
@@ -1,410 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 Ricki Hirner (bitfire web engineering).
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the GNU Public License v3.0
- * which accompanies this distribution, and is available at
- * http://www.gnu.org/licenses/gpl.html
- ******************************************************************************/
-package at.bitfire.davdroid.resource;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.UUID;
-
-import lombok.Getter;
-import lombok.Setter;
-import lombok.ToString;
-
-import org.apache.commons.lang.StringUtils;
-
-import android.util.Log;
-import at.bitfire.davdroid.Constants;
-import ezvcard.Ezvcard;
-import ezvcard.VCard;
-import ezvcard.VCardVersion;
-import ezvcard.ValidationWarnings;
-import ezvcard.parameter.EmailType;
-import ezvcard.parameter.ImageType;
-import ezvcard.parameter.TelephoneType;
-import ezvcard.property.Address;
-import ezvcard.property.Anniversary;
-import ezvcard.property.Birthday;
-import ezvcard.property.Categories;
-import ezvcard.property.Email;
-import ezvcard.property.FormattedName;
-import ezvcard.property.Impp;
-import ezvcard.property.Logo;
-import ezvcard.property.Nickname;
-import ezvcard.property.Note;
-import ezvcard.property.Organization;
-import ezvcard.property.Photo;
-import ezvcard.property.RawProperty;
-import ezvcard.property.Revision;
-import ezvcard.property.Role;
-import ezvcard.property.Sound;
-import ezvcard.property.Source;
-import ezvcard.property.StructuredName;
-import ezvcard.property.Telephone;
-import ezvcard.property.Title;
-import ezvcard.property.Uid;
-import ezvcard.property.Url;
-
-
-/**
- * Represents a contact. Locally, this is a Contact in the Android
- * device; remote, this is a VCard.
- */
-@ToString(callSuper = true)
-public class Contact extends Resource {
- private final static String TAG = "davdroid.Contact";
-
- public final static String MIME_TYPE = "text/vcard";
-
- public final static String
- PROPERTY_STARRED = "X-DAVDROID-STARRED",
- PROPERTY_PHONETIC_FIRST_NAME = "X-PHONETIC-FIRST-NAME",
- PROPERTY_PHONETIC_MIDDLE_NAME = "X-PHONETIC-MIDDLE-NAME",
- PROPERTY_PHONETIC_LAST_NAME = "X-PHONETIC-LAST-NAME",
- PROPERTY_SIP = "X-SIP";
-
- public final static EmailType EMAIL_TYPE_MOBILE = EmailType.get("X-MOBILE");
-
- public final static TelephoneType
- PHONE_TYPE_CALLBACK = TelephoneType.get("X-CALLBACK"),
- PHONE_TYPE_COMPANY_MAIN = TelephoneType.get("X-COMPANY_MAIN"),
- PHONE_TYPE_RADIO = TelephoneType.get("X-RADIO"),
- PHONE_TYPE_ASSISTANT = TelephoneType.get("X-ASSISTANT"),
- PHONE_TYPE_MMS = TelephoneType.get("X-MMS");
-
- @Getter @Setter private String unknownProperties;
-
- @Getter @Setter private boolean starred;
-
- @Getter @Setter private String displayName, nickName;
- @Getter @Setter private String prefix, givenName, middleName, familyName, suffix;
- @Getter @Setter private String phoneticGivenName, phoneticMiddleName, phoneticFamilyName;
- @Getter @Setter private String note;
- @Getter @Setter private Organization organization;
- @Getter @Setter private String jobTitle, jobDescription;
-
- @Getter @Setter private byte[] photo;
-
- @Getter @Setter private Anniversary anniversary;
- @Getter @Setter private Birthday birthDay;
-
- @Getter private List phoneNumbers = new LinkedList();
- @Getter private List emails = new LinkedList();
- @Getter private List impps = new LinkedList();
- @Getter private List addresses = new LinkedList();
- @Getter private List categories = new LinkedList();
- @Getter private List URLs = new LinkedList();
-
-
- /* instance methods */
-
- public Contact(String name, String ETag) {
- super(name, ETag);
- }
-
- public Contact(long localID, String resourceName, String eTag) {
- super(localID, resourceName, eTag);
- }
-
-
- @Override
- public void initialize() {
- generateUID();
- name = uid + ".vcf";
- }
-
- protected void generateUID() {
- uid = UUID.randomUUID().toString();
- }
-
-
- /* VCard methods */
-
- @Override
- public void parseEntity(InputStream is) throws IOException {
- VCard vcard = Ezvcard.parse(is).first();
- if (vcard == null)
- return;
-
- // now work through all supported properties
- // supported properties are removed from the VCard after parsing
- // so that only unknown properties are left and can be stored separately
-
- // UID
- Uid uid = vcard.getUid();
- if (uid != null) {
- this.uid = uid.getValue();
- vcard.removeProperties(Uid.class);
- } else {
- Log.w(TAG, "Received VCard without UID, generating new one");
- generateUID();
- }
-
- // X-DAVDROID-STARRED
- RawProperty starred = vcard.getExtendedProperty(PROPERTY_STARRED);
- if (starred != null && starred.getValue() != null) {
- this.starred = starred.getValue().equals("1");
- vcard.removeExtendedProperty(PROPERTY_STARRED);
- } else
- this.starred = false;
-
- // FN
- FormattedName fn = vcard.getFormattedName();
- if (fn != null) {
- displayName = fn.getValue();
- vcard.removeProperties(FormattedName.class);
- } else
- Log.w(TAG, "Received invalid VCard without FN (formatted name) property");
-
- // N
- StructuredName n = vcard.getStructuredName();
- if (n != null) {
- prefix = StringUtils.join(n.getPrefixes(), " ");
- givenName = n.getGiven();
- middleName = StringUtils.join(n.getAdditional(), " ");
- familyName = n.getFamily();
- suffix = StringUtils.join(n.getSuffixes(), " ");
- vcard.removeProperties(StructuredName.class);
- }
-
- // phonetic names
- RawProperty
- phoneticFirstName = vcard.getExtendedProperty(PROPERTY_PHONETIC_FIRST_NAME),
- phoneticMiddleName = vcard.getExtendedProperty(PROPERTY_PHONETIC_MIDDLE_NAME),
- phoneticLastName = vcard.getExtendedProperty(PROPERTY_PHONETIC_LAST_NAME);
- if (phoneticFirstName != null) {
- phoneticGivenName = phoneticFirstName.getValue();
- vcard.removeExtendedProperty(PROPERTY_PHONETIC_FIRST_NAME);
- }
- if (phoneticMiddleName != null) {
- this.phoneticMiddleName = phoneticMiddleName.getValue();
- vcard.removeExtendedProperty(PROPERTY_PHONETIC_MIDDLE_NAME);
- }
- if (phoneticLastName != null) {
- phoneticFamilyName = phoneticLastName.getValue();
- vcard.removeExtendedProperty(PROPERTY_PHONETIC_LAST_NAME);
- }
-
- // TEL
- phoneNumbers = vcard.getTelephoneNumbers();
- vcard.removeProperties(Telephone.class);
-
- // EMAIL
- emails = vcard.getEmails();
- vcard.removeProperties(Email.class);
-
- // PHOTO
- for (Photo photo : vcard.getPhotos()) {
- this.photo = photo.getData();
- vcard.removeProperties(Photo.class);
- break;
- }
-
- // ORG
- organization = vcard.getOrganization();
- vcard.removeProperties(Organization.class);
- // TITLE
- for (Title title : vcard.getTitles()) {
- jobTitle = title.getValue();
- vcard.removeProperties(Title.class);
- break;
- }
- // ROLE
- for (Role role : vcard.getRoles()) {
- this.jobDescription = role.getValue();
- vcard.removeProperties(Role.class);
- break;
- }
-
- // IMPP
- impps = vcard.getImpps();
- vcard.removeProperties(Impp.class);
-
- // NICKNAME
- Nickname nicknames = vcard.getNickname();
- if (nicknames != null) {
- if (nicknames.getValues() != null)
- nickName = StringUtils.join(nicknames.getValues(), ", ");
- vcard.removeProperties(Nickname.class);
- }
-
- // NOTE
- List notes = new LinkedList();
- for (Note note : vcard.getNotes())
- notes.add(note.getValue());
- if (!notes.isEmpty())
- note = StringUtils.join(notes, "\n---\n");
- vcard.removeProperties(Note.class);
-
- // ADR
- addresses = vcard.getAddresses();
- vcard.removeProperties(Address.class);
-
- // CATEGORY
- Categories categories = vcard.getCategories();
- if (categories != null)
- this.categories = categories.getValues();
- vcard.removeProperties(Categories.class);
-
- // URL
- for (Url url : vcard.getUrls())
- URLs.add(url.getValue());
- vcard.removeProperties(Url.class);
-
- // BDAY
- birthDay = vcard.getBirthday();
- vcard.removeProperties(Birthday.class);
- // ANNIVERSARY
- anniversary = vcard.getAnniversary();
- vcard.removeProperties(Anniversary.class);
-
- // X-SIP
- for (RawProperty sip : vcard.getExtendedProperties(PROPERTY_SIP))
- impps.add(new Impp("sip", sip.getValue()));
- vcard.removeExtendedProperty(PROPERTY_SIP);
-
- // remove binary properties because of potential OutOfMemory / TransactionTooLarge exceptions
- vcard.removeProperties(Logo.class);
- vcard.removeProperties(Sound.class);
- // remove properties that don't apply anymore
- vcard.removeProperties(Revision.class);
- vcard.removeProperties(Source.class);
- // store all remaining properties into unknownProperties
- if (!vcard.getProperties().isEmpty() || !vcard.getExtendedProperties().isEmpty())
- try {
- unknownProperties = vcard.write();
- } catch(Exception e) {
- Log.w(TAG, "Couldn't store unknown properties (maybe illegal syntax), dropping them");
- }
- }
-
-
- @Override
- public ByteArrayOutputStream toEntity() throws IOException {
- VCard vcard = null;
- try {
- if (unknownProperties != null)
- vcard = Ezvcard.parse(unknownProperties).first();
- } catch (Exception e) {
- Log.w(TAG, "Couldn't parse original property set, beginning from scratch");
- }
- if (vcard == null)
- vcard = new VCard();
-
- if (uid != null)
- vcard.setUid(new Uid(uid));
- else
- Log.wtf(TAG, "Generating VCard without UID");
-
- if (starred)
- vcard.setExtendedProperty(PROPERTY_STARRED, "1");
-
- if (displayName != null)
- vcard.setFormattedName(displayName);
- else if (organization != null && organization.getValues() != null && organization.getValues().get(0) != null)
- vcard.setFormattedName(organization.getValues().get(0));
- else
- Log.w(TAG, "No FN (formatted name) available to generate VCard");
-
- // N
- if (familyName != null || middleName != null || givenName != null) {
- StructuredName n = new StructuredName();
- if (prefix != null)
- for (String p : StringUtils.split(prefix))
- n.addPrefix(p);
- n.setGiven(givenName);
- if (middleName != null)
- for (String middle : StringUtils.split(middleName))
- n.addAdditional(middle);
- n.setFamily(familyName);
- if (suffix != null)
- for (String s : StringUtils.split(suffix))
- n.addSuffix(s);
- vcard.setStructuredName(n);
- }
-
- // phonetic names
- if (phoneticGivenName != null)
- vcard.addExtendedProperty(PROPERTY_PHONETIC_FIRST_NAME, phoneticGivenName);
- if (phoneticMiddleName != null)
- vcard.addExtendedProperty(PROPERTY_PHONETIC_MIDDLE_NAME, phoneticMiddleName);
- if (phoneticFamilyName != null)
- vcard.addExtendedProperty(PROPERTY_PHONETIC_LAST_NAME, phoneticFamilyName);
-
- // TEL
- for (Telephone phoneNumber : phoneNumbers)
- vcard.addTelephoneNumber(phoneNumber);
-
- // EMAIL
- for (Email email : emails)
- vcard.addEmail(email);
-
- // ORG, TITLE, ROLE
- if (organization != null)
- vcard.setOrganization(organization);
- if (jobTitle != null)
- vcard.addTitle(jobTitle);
- if (jobDescription != null)
- vcard.addRole(jobDescription);
-
- // IMPP
- for (Impp impp : impps)
- vcard.addImpp(impp);
-
- // NICKNAME
- if (!StringUtils.isBlank(nickName))
- vcard.setNickname(nickName);
-
- // NOTE
- if (!StringUtils.isBlank(note))
- vcard.addNote(note);
-
- // ADR
- for (Address address : addresses)
- vcard.addAddress(address);
-
- // CATEGORY
- if (!categories.isEmpty())
- vcard.setCategories(categories.toArray(new String[0]));
-
- // URL
- for (String url : URLs)
- vcard.addUrl(url);
-
- // ANNIVERSARY
- if (anniversary != null)
- vcard.setAnniversary(anniversary);
- // BDAY
- if (birthDay != null)
- vcard.setBirthday(birthDay);
-
- // PHOTO
- if (photo != null)
- vcard.addPhoto(new Photo(photo, ImageType.JPEG));
-
- // PRODID, REV
- vcard.setProductId("DAVdroid/" + Constants.APP_VERSION + " (ez-vcard/" + Ezvcard.VERSION + ")");
- vcard.setRevision(Revision.now());
-
- // validate and print warnings
- ValidationWarnings warnings = vcard.validate(VCardVersion.V3_0);
- if (!warnings.isEmpty())
- Log.w(TAG, "Created potentially invalid VCard! " + warnings);
-
- ByteArrayOutputStream os = new ByteArrayOutputStream();
- Ezvcard
- .write(vcard)
- .version(VCardVersion.V3_0)
- .versionStrict(false)
- .prodId(false) // we provide our own PRODID
- .go(os);
- return os;
- }
-}
diff --git a/src/at/bitfire/davdroid/resource/DavResourceFinder.java b/src/at/bitfire/davdroid/resource/DavResourceFinder.java
deleted file mode 100644
index 888a1e2f..00000000
--- a/src/at/bitfire/davdroid/resource/DavResourceFinder.java
+++ /dev/null
@@ -1,299 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 Ricki Hirner (bitfire web engineering).
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the GNU Public License v3.0
- * which accompanies this distribution, and is available at
- * http://www.gnu.org/licenses/gpl.html
- ******************************************************************************/
-package at.bitfire.davdroid.resource;
-
-import java.io.Closeable;
-import java.io.IOException;
-import java.net.MalformedURLException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.util.LinkedList;
-import java.util.List;
-
-import org.xbill.DNS.Lookup;
-import org.xbill.DNS.Record;
-import org.xbill.DNS.SRVRecord;
-import org.xbill.DNS.TXTRecord;
-import org.xbill.DNS.TextParseException;
-import org.xbill.DNS.Type;
-
-import android.content.Context;
-import android.util.Log;
-import at.bitfire.davdroid.R;
-import at.bitfire.davdroid.webdav.DavException;
-import at.bitfire.davdroid.webdav.DavHttpClient;
-import at.bitfire.davdroid.webdav.DavIncapableException;
-import at.bitfire.davdroid.webdav.HttpPropfind.Mode;
-import at.bitfire.davdroid.webdav.NotAuthorizedException;
-import at.bitfire.davdroid.webdav.WebDavResource;
-import ch.boye.httpclientandroidlib.HttpException;
-import ch.boye.httpclientandroidlib.impl.client.CloseableHttpClient;
-import ezvcard.VCardVersion;
-
-public class DavResourceFinder implements Closeable {
- private final static String TAG = "davdroid.DavResourceFinder";
-
- protected Context context;
- protected CloseableHttpClient httpClient;
-
-
- public DavResourceFinder(Context context) {
- this.context = context;
-
- // disable compression and enable network logging for debugging purposes
- httpClient = DavHttpClient.create(true, true);
- }
-
- @Override
- public void close() throws IOException {
- httpClient.close();
- }
-
-
- public void findResources(ServerInfo serverInfo) throws URISyntaxException, DavException, HttpException, IOException {
- // CardDAV
- WebDavResource principal = getCurrentUserPrincipal(serverInfo, "carddav");
- if (principal != null) {
- serverInfo.setCardDAV(true);
-
- principal.propfind(Mode.HOME_SETS);
- String pathAddressBooks = principal.getAddressbookHomeSet();
- if (pathAddressBooks != null) {
- Log.i(TAG, "Found address book home set: " + pathAddressBooks);
-
- WebDavResource homeSetAddressBooks = new WebDavResource(principal, pathAddressBooks);
- if (checkHomesetCapabilities(homeSetAddressBooks, "addressbook")) {
- homeSetAddressBooks.propfind(Mode.CARDDAV_COLLECTIONS);
-
- List 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);
-
- principal.propfind(Mode.HOME_SETS);
- String pathCalendars = principal.getCalendarHomeSet();
- if (pathCalendars != null) {
- Log.i(TAG, "Found calendar home set: " + pathCalendars);
-
- WebDavResource homeSetCalendars = new WebDavResource(principal, pathCalendars);
- if (checkHomesetCapabilities(homeSetCalendars, "calendar-access")) {
- homeSetCalendars.propfind(Mode.CALDAV_COLLECTIONS);
-
- List 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));
-
- }
-
-
- /**
- * Finds the initial service URL from a given base URI (HTTP[S] or mailto URI, user name, password)
- * @param serverInfo User-given service information (including base URI, i.e. HTTP[S] URL+user name+password or mailto URI and password)
- * @param serviceName Service name ("carddav" or "caldav")
- * @return Initial service URL (HTTP/HTTPS), without user credentials
- * @throws URISyntaxException when the user-given URI is invalid
- * @throws MalformedURLException when the user-given URI is invalid
- */
- public URL getInitialContextURL(ServerInfo serverInfo, String serviceName) throws URISyntaxException, MalformedURLException {
- String scheme = null,
- domain = null;
- int port = -1;
- String path = "/";
-
- URI baseURI = serverInfo.getBaseURI();
- if ("mailto".equalsIgnoreCase(baseURI.getScheme())) {
- // mailto URIs
- String mailbox = serverInfo.getBaseURI().getSchemeSpecificPart();
-
- // determine service FQDN
- int pos = mailbox.lastIndexOf("@");
- if (pos == -1)
- throw new URISyntaxException(mailbox, "Missing @ sign");
-
- scheme = "https";
- domain = mailbox.substring(pos + 1);
- if (domain.isEmpty())
- throw new URISyntaxException(mailbox, "Missing domain name");
- } else {
- // HTTP(S) URLs
- scheme = baseURI.getScheme();
- domain = baseURI.getHost();
- port = baseURI.getPort();
- path = baseURI.getPath();
- }
-
- // try to determine FQDN and port number using SRV records
- try {
- String name = "_" + serviceName + "s._tcp." + domain;
- Log.d(TAG, "Looking up SRV records for " + name);
- Record[] records = new Lookup(name, Type.SRV).run();
- if (records != null && records.length >= 1) {
- SRVRecord srv = selectSRVRecord(records);
-
- scheme = "https";
- domain = srv.getTarget().toString(true);
- port = srv.getPort();
- Log.d(TAG, "Found " + serviceName + "s service for " + domain + " -> " + domain + ":" + port);
-
- // SRV record found, look for TXT record too (for initial context path)
- records = new Lookup(name, Type.TXT).run();
- if (records != null && records.length >= 1) {
- TXTRecord txt = (TXTRecord)records[0];
- for (Object o : txt.getStrings().toArray()) {
- String segment = (String)o;
- if (segment.startsWith("path=")) {
- path = segment.substring(5);
- Log.d(TAG, "Found initial context path for " + serviceName + " at " + domain + " -> " + path);
- break;
- }
- }
- }
- }
- } catch (TextParseException e) {
- throw new URISyntaxException(domain, "Invalid domain name");
- }
-
- if (port != -1)
- return new URL(scheme, domain, port, path);
- else
- return new URL(scheme, domain, path);
- }
-
-
- /**
- * Detects the current-user-principal for a given WebDavResource. At first, /.well-known/ is tried. Only
- * if no current-user-principal can be detected for the .well-known location, the given location of the resource
- * is tried.
- * @param resource Location that will be queried
- * @param serviceName Well-known service name ("carddav", "caldav")
- * @return WebDavResource of current-user-principal for the given service, or null if it can't be found
- *
- * TODO: If a TXT record is given, always use it instead of trying .well-known first
- */
- WebDavResource getCurrentUserPrincipal(ServerInfo serverInfo, String serviceName) throws URISyntaxException, IOException, NotAuthorizedException {
- URL initialURL = getInitialContextURL(serverInfo, serviceName);
- if (initialURL != null) {
- // determine base URL (host name and initial context path)
- WebDavResource base = new WebDavResource(httpClient,
- initialURL,
- serverInfo.getUserName(), serverInfo.getPassword(), serverInfo.isAuthPreemptive());
-
- // look for well-known service (RFC 5785)
- try {
- WebDavResource wellKnown = new WebDavResource(base, "/.well-known/" + serviceName);
- wellKnown.propfind(Mode.CURRENT_USER_PRINCIPAL);
- if (wellKnown.getCurrentUserPrincipal() != null)
- return new WebDavResource(wellKnown, wellKnown.getCurrentUserPrincipal());
- } catch (NotAuthorizedException e) {
- 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);
- } 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);
- }
-
- // fall back to user-given initial context path
- try {
- base.propfind(Mode.CURRENT_USER_PRINCIPAL);
- if (base.getCurrentUserPrincipal() != null)
- return new WebDavResource(base, base.getCurrentUserPrincipal());
- } catch (NotAuthorizedException e) {
- Log.e(TAG, "Not authorized for querying principal", e);
- throw e;
- } catch (HttpException e) {
- Log.e(TAG, "HTTP error when querying principal", e);
- } catch (DavException e) {
- Log.e(TAG, "DAV error when querying principal", e);
- }
- Log.i(TAG, "Couldn't find current-user-principal for service " + serviceName);
- }
- return null;
- }
-
- public static boolean checkHomesetCapabilities(WebDavResource resource, String davCapability) throws URISyntaxException, IOException {
- // check for necessary capabilities
- try {
- resource.options();
- if (resource.supportsDAV(davCapability) &&
- resource.supportsMethod("PROPFIND")) // check only for methods that MUST be available for home sets
- return true;
- } catch(HttpException e) {
- // for instance, 405 Method not allowed
- }
- return false;
- }
-
-
- SRVRecord selectSRVRecord(Record[] records) {
- if (records.length > 1)
- Log.w(TAG, "Multiple SRV records not supported yet; using first one");
- return (SRVRecord)records[0];
- }
-
-}
diff --git a/src/at/bitfire/davdroid/resource/Event.java b/src/at/bitfire/davdroid/resource/Event.java
deleted file mode 100644
index 91f68012..00000000
--- a/src/at/bitfire/davdroid/resource/Event.java
+++ /dev/null
@@ -1,368 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 Ricki Hirner (bitfire web engineering).
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the GNU Public License v3.0
- * which accompanies this distribution, and is available at
- * http://www.gnu.org/licenses/gpl.html
- ******************************************************************************/
-package at.bitfire.davdroid.resource;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.StringReader;
-import java.util.Calendar;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.SimpleTimeZone;
-import java.util.TimeZone;
-
-import lombok.Getter;
-import lombok.NonNull;
-import lombok.Setter;
-import net.fortuna.ical4j.data.CalendarBuilder;
-import net.fortuna.ical4j.data.CalendarOutputter;
-import net.fortuna.ical4j.data.ParserException;
-import net.fortuna.ical4j.model.Component;
-import net.fortuna.ical4j.model.ComponentList;
-import net.fortuna.ical4j.model.Date;
-import net.fortuna.ical4j.model.DateTime;
-import net.fortuna.ical4j.model.DefaultTimeZoneRegistryFactory;
-import net.fortuna.ical4j.model.Property;
-import net.fortuna.ical4j.model.PropertyList;
-import net.fortuna.ical4j.model.TimeZoneRegistry;
-import net.fortuna.ical4j.model.ValidationException;
-import net.fortuna.ical4j.model.component.VAlarm;
-import net.fortuna.ical4j.model.component.VEvent;
-import net.fortuna.ical4j.model.component.VTimeZone;
-import net.fortuna.ical4j.model.parameter.Value;
-import net.fortuna.ical4j.model.property.Attendee;
-import net.fortuna.ical4j.model.property.Clazz;
-import net.fortuna.ical4j.model.property.DateProperty;
-import net.fortuna.ical4j.model.property.Description;
-import net.fortuna.ical4j.model.property.DtEnd;
-import net.fortuna.ical4j.model.property.DtStart;
-import net.fortuna.ical4j.model.property.Duration;
-import net.fortuna.ical4j.model.property.ExDate;
-import net.fortuna.ical4j.model.property.ExRule;
-import net.fortuna.ical4j.model.property.LastModified;
-import net.fortuna.ical4j.model.property.Location;
-import net.fortuna.ical4j.model.property.Organizer;
-import net.fortuna.ical4j.model.property.ProdId;
-import net.fortuna.ical4j.model.property.RDate;
-import net.fortuna.ical4j.model.property.RRule;
-import net.fortuna.ical4j.model.property.Status;
-import net.fortuna.ical4j.model.property.Summary;
-import net.fortuna.ical4j.model.property.Transp;
-import net.fortuna.ical4j.model.property.Uid;
-import net.fortuna.ical4j.model.property.Version;
-import net.fortuna.ical4j.util.SimpleHostInfo;
-import net.fortuna.ical4j.util.UidGenerator;
-import android.text.format.Time;
-import android.util.Log;
-import at.bitfire.davdroid.Constants;
-import at.bitfire.davdroid.syncadapter.DavSyncAdapter;
-
-
-public class Event extends Resource {
- private final static String TAG = "davdroid.Event";
-
- public final static String MIME_TYPE = "text/calendar";
-
- private final static TimeZoneRegistry tzRegistry = new DefaultTimeZoneRegistryFactory().createRegistry();
-
- @Getter @Setter private String summary, location, description;
-
- @Getter private DtStart dtStart;
- @Getter private DtEnd dtEnd;
- @Getter @Setter private Duration duration;
- @Getter @Setter private RDate rdate;
- @Getter @Setter private RRule rrule;
- @Getter @Setter private ExDate exdate;
- @Getter @Setter private ExRule exrule;
-
- @Getter @Setter private Boolean forPublic;
- @Getter @Setter private Status status;
-
- @Getter @Setter private boolean opaque;
-
- @Getter @Setter private Organizer organizer;
- @Getter private List attendees = new LinkedList();
- public void addAttendee(Attendee attendee) {
- attendees.add(attendee);
- }
-
- @Getter private List alarms = new LinkedList();
- public void addAlarm(VAlarm alarm) {
- alarms.add(alarm);
- }
-
-
- public Event(String name, String ETag) {
- super(name, ETag);
- }
-
- public Event(long localID, String name, String ETag) {
- super(localID, name, ETag);
- }
-
-
- @Override
- public void initialize() {
- generateUID();
- name = uid.replace("@", "_") + ".ics";
- }
-
- protected void generateUID() {
- UidGenerator generator = new UidGenerator(new SimpleHostInfo(DavSyncAdapter.getAndroidID()), String.valueOf(android.os.Process.myPid()));
- uid = generator.generateUid().getValue();
- }
-
-
- @Override
- @SuppressWarnings("unchecked")
- public void parseEntity(@NonNull InputStream entity) throws IOException, InvalidResourceException {
- net.fortuna.ical4j.model.Calendar ical;
- try {
- CalendarBuilder builder = new CalendarBuilder();
- ical = builder.build(entity);
-
- if (ical == null)
- throw new InvalidResourceException("No iCalendar found");
- } catch (ParserException e) {
- throw new InvalidResourceException(e);
- }
-
- // event
- ComponentList events = ical.getComponents(Component.VEVENT);
- if (events == null || events.isEmpty())
- throw new InvalidResourceException("No VEVENT found");
- VEvent event = (VEvent)events.get(0);
-
- if (event.getUid() != null)
- uid = event.getUid().getValue();
- else {
- Log.w(TAG, "Received VEVENT without UID, generating new one");
- generateUID();
- }
-
- if ((dtStart = event.getStartDate()) == null || (dtEnd = event.getEndDate()) == null)
- throw new InvalidResourceException("Invalid start time/end time/duration");
-
- if (hasTime(dtStart)) {
- validateTimeZone(dtStart);
- validateTimeZone(dtEnd);
- }
-
- // all-day events and "events on that day":
- // * related UNIX times must be in UTC
- // * must have a duration (set to one day if missing)
- if (!hasTime(dtStart) && !dtEnd.getDate().after(dtStart.getDate())) {
- Log.i(TAG, "Repairing iCal: DTEND := DTSTART+1");
- Calendar c = Calendar.getInstance(TimeZone.getTimeZone(Time.TIMEZONE_UTC));
- c.setTime(dtStart.getDate());
- c.add(Calendar.DATE, 1);
- dtEnd.setDate(new Date(c.getTimeInMillis()));
- }
-
- rrule = (RRule)event.getProperty(Property.RRULE);
- rdate = (RDate)event.getProperty(Property.RDATE);
- exrule = (ExRule)event.getProperty(Property.EXRULE);
- exdate = (ExDate)event.getProperty(Property.EXDATE);
-
- if (event.getSummary() != null)
- summary = event.getSummary().getValue();
- if (event.getLocation() != null)
- location = event.getLocation().getValue();
- if (event.getDescription() != null)
- description = event.getDescription().getValue();
-
- status = event.getStatus();
-
- opaque = true;
- if (event.getTransparency() == Transp.TRANSPARENT)
- opaque = false;
-
- organizer = event.getOrganizer();
- for (Object o : event.getProperties(Property.ATTENDEE))
- attendees.add((Attendee)o);
-
- Clazz classification = event.getClassification();
- if (classification != null) {
- if (classification == Clazz.PUBLIC)
- forPublic = true;
- else if (classification == Clazz.CONFIDENTIAL || classification == Clazz.PRIVATE)
- forPublic = false;
- }
-
- this.alarms = event.getAlarms();
- }
-
- @Override
- @SuppressWarnings("unchecked")
- public ByteArrayOutputStream toEntity() throws IOException {
- net.fortuna.ical4j.model.Calendar ical = new net.fortuna.ical4j.model.Calendar();
- ical.getProperties().add(Version.VERSION_2_0);
- ical.getProperties().add(new ProdId("-//bitfire web engineering//DAVdroid " + Constants.APP_VERSION + " (ical4j 1.0.x)//EN"));
-
- VEvent event = new VEvent();
- PropertyList props = event.getProperties();
-
- if (uid != null)
- props.add(new Uid(uid));
-
- props.add(dtStart);
- if (dtEnd != null)
- props.add(dtEnd);
- if (duration != null)
- props.add(duration);
-
- if (rrule != null)
- props.add(rrule);
- if (rdate != null)
- props.add(rdate);
- if (exrule != null)
- props.add(exrule);
- if (exdate != null)
- props.add(exdate);
-
- if (summary != null && !summary.isEmpty())
- props.add(new Summary(summary));
- if (location != null && !location.isEmpty())
- props.add(new Location(location));
- if (description != null && !description.isEmpty())
- props.add(new Description(description));
-
- if (status != null)
- props.add(status);
- if (!opaque)
- props.add(Transp.TRANSPARENT);
-
- if (organizer != null)
- props.add(organizer);
- props.addAll(attendees);
-
- if (forPublic != null)
- event.getProperties().add(forPublic ? Clazz.PUBLIC : Clazz.PRIVATE);
-
- event.getAlarms().addAll(alarms);
-
- props.add(new LastModified());
- ical.getComponents().add(event);
-
- // add VTIMEZONE components
- net.fortuna.ical4j.model.TimeZone
- tzStart = (dtStart == null ? null : dtStart.getTimeZone()),
- tzEnd = (dtEnd == null ? null : dtEnd.getTimeZone());
- if (tzStart != null)
- ical.getComponents().add(tzStart.getVTimeZone());
- if (tzEnd != null && tzEnd != tzStart)
- ical.getComponents().add(tzEnd.getVTimeZone());
-
- CalendarOutputter output = new CalendarOutputter(false);
- ByteArrayOutputStream os = new ByteArrayOutputStream();
- try {
- output.output(ical, os);
- } catch (ValidationException e) {
- Log.e(TAG, "Generated invalid iCalendar");
- }
- return os;
- }
-
-
- public long getDtStartInMillis() {
- return dtStart.getDate().getTime();
- }
-
- public String getDtStartTzID() {
- return getTzId(dtStart);
- }
-
- public void setDtStart(long tsStart, String tzID) {
- if (tzID == null) { // all-day
- dtStart = new DtStart(new Date(tsStart));
- } else {
- DateTime start = new DateTime(tsStart);
- start.setTimeZone(tzRegistry.getTimeZone(tzID));
- dtStart = new DtStart(start);
- }
- }
-
-
- public long getDtEndInMillis() {
- return dtEnd.getDate().getTime();
- }
-
- public String getDtEndTzID() {
- return getTzId(dtEnd);
- }
-
- public void setDtEnd(long tsEnd, String tzID) {
- if (tzID == null) { // all-day
- dtEnd = new DtEnd(new Date(tsEnd));
- } else {
- DateTime end = new DateTime(tsEnd);
- end.setTimeZone(tzRegistry.getTimeZone(tzID));
- dtEnd = new DtEnd(end);
- }
- }
-
-
- // helpers
-
- public boolean isAllDay() {
- return !hasTime(dtStart);
- }
-
- protected static boolean hasTime(DateProperty date) {
- return date.getDate() instanceof DateTime;
- }
-
- protected static String getTzId(DateProperty date) {
- if (date.isUtc() || !hasTime(date))
- return Time.TIMEZONE_UTC;
- else if (date.getTimeZone() != null)
- return date.getTimeZone().getID();
- else if (date.getParameter(Value.TZID) != null)
- return date.getParameter(Value.TZID).getValue();
-
- // fallback
- return Time.TIMEZONE_UTC;
- }
-
- /* guess matching Android timezone ID */
- protected static void validateTimeZone(DateProperty date) {
- if (date.isUtc() || !hasTime(date))
- return;
-
- String tzID = getTzId(date);
- if (tzID == null)
- return;
-
- String localTZ = Time.TIMEZONE_UTC;
-
- String availableTZs[] = SimpleTimeZone.getAvailableIDs();
- for (String availableTZ : availableTZs)
- if (tzID.indexOf(availableTZ, 0) != -1) {
- localTZ = availableTZ;
- break;
- }
-
- Log.d(TAG, "Assuming time zone " + localTZ + " for " + tzID);
- date.setTimeZone(tzRegistry.getTimeZone(localTZ));
- }
-
- public static String TimezoneDefToTzId(String timezoneDef) throws IllegalArgumentException {
- try {
- if (timezoneDef != null) {
- CalendarBuilder builder = new CalendarBuilder();
- net.fortuna.ical4j.model.Calendar cal = builder.build(new StringReader(timezoneDef));
- VTimeZone timezone = (VTimeZone)cal.getComponent(VTimeZone.VTIMEZONE);
- return timezone.getTimeZoneId().getValue();
- }
- } catch (Exception ex) {
- Log.w(TAG, "Can't understand time zone definition, ignoring", ex);
- }
- throw new IllegalArgumentException();
- }
-}
diff --git a/src/at/bitfire/davdroid/resource/InvalidResourceException.java b/src/at/bitfire/davdroid/resource/InvalidResourceException.java
deleted file mode 100644
index ee24e1f6..00000000
--- a/src/at/bitfire/davdroid/resource/InvalidResourceException.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 Ricki Hirner (bitfire web engineering).
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the GNU Public License v3.0
- * which accompanies this distribution, and is available at
- * http://www.gnu.org/licenses/gpl.html
- ******************************************************************************/
-package at.bitfire.davdroid.resource;
-
-public class InvalidResourceException extends Exception {
- private static final long serialVersionUID = 1593585432655578220L;
-
- public InvalidResourceException(String message) {
- super(message);
- }
-
- public InvalidResourceException(Throwable throwable) {
- super(throwable);
- }
-}
diff --git a/src/at/bitfire/davdroid/resource/LocalAddressBook.java b/src/at/bitfire/davdroid/resource/LocalAddressBook.java
deleted file mode 100644
index b8711cbb..00000000
--- a/src/at/bitfire/davdroid/resource/LocalAddressBook.java
+++ /dev/null
@@ -1,1007 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 Ricki Hirner (bitfire web engineering).
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the GNU Public License v3.0
- * which accompanies this distribution, and is available at
- * http://www.gnu.org/licenses/gpl.html
- ******************************************************************************/
-package at.bitfire.davdroid.resource;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Locale;
-import java.util.Set;
-
-import lombok.Cleanup;
-
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang.StringUtils;
-import org.apache.commons.lang.WordUtils;
-
-import android.accounts.Account;
-import android.content.ContentProviderClient;
-import android.content.ContentProviderOperation;
-import android.content.ContentProviderOperation.Builder;
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.content.res.AssetFileDescriptor;
-import android.database.Cursor;
-import android.database.DatabaseUtils;
-import android.net.Uri;
-import android.os.RemoteException;
-import android.provider.ContactsContract.CommonDataKinds;
-import android.provider.ContactsContract.CommonDataKinds.Email;
-import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
-import android.provider.ContactsContract.CommonDataKinds.Im;
-import android.provider.ContactsContract.CommonDataKinds.Nickname;
-import android.provider.ContactsContract.CommonDataKinds.Note;
-import android.provider.ContactsContract.CommonDataKinds.Organization;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.CommonDataKinds.Photo;
-import android.provider.ContactsContract.CommonDataKinds.SipAddress;
-import android.provider.ContactsContract.CommonDataKinds.StructuredName;
-import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
-import android.provider.ContactsContract.CommonDataKinds.Website;
-import android.provider.ContactsContract.Data;
-import android.provider.ContactsContract.Groups;
-import android.provider.ContactsContract.RawContacts;
-import android.util.Log;
-import at.bitfire.davdroid.syncadapter.AccountSettings;
-import ezvcard.parameter.AddressType;
-import ezvcard.parameter.EmailType;
-import ezvcard.parameter.ImppType;
-import ezvcard.parameter.TelephoneType;
-import ezvcard.property.Address;
-import ezvcard.property.Anniversary;
-import ezvcard.property.Birthday;
-import ezvcard.property.DateOrTimeProperty;
-import ezvcard.property.Impp;
-import ezvcard.property.Telephone;
-
-
-public class LocalAddressBook extends LocalCollection {
- private final static String TAG = "davdroid.LocalAddressBook";
-
- protected final static String COLUMN_UNKNOWN_PROPERTIES = RawContacts.SYNC3;
-
-
- protected AccountSettings accountSettings;
-
-
- /* database fields */
-
- @Override
- protected Uri entriesURI() {
- return syncAdapterURI(RawContacts.CONTENT_URI);
- }
-
- protected String entryColumnAccountType() { return RawContacts.ACCOUNT_TYPE; }
- protected String entryColumnAccountName() { return RawContacts.ACCOUNT_NAME; }
-
- protected String entryColumnParentID() { return null; /* maybe use RawContacts.DATA_SET some day? */ }
- protected String entryColumnID() { return RawContacts._ID; }
- protected String entryColumnRemoteName() { return RawContacts.SOURCE_ID; }
- protected String entryColumnETag() { return RawContacts.SYNC2; }
-
- protected String entryColumnDirty() { return RawContacts.DIRTY; }
- protected String entryColumnDeleted() { return RawContacts.DELETED; }
-
- protected String entryColumnUID() { return RawContacts.SYNC1; }
-
-
-
- public LocalAddressBook(Account account, ContentProviderClient providerClient, AccountSettings accountSettings) {
- super(account, providerClient);
- this.accountSettings = accountSettings;
- }
-
-
- /* collection operations */
-
- @Override
- public long getId() {
- return -1;
- }
-
- @Override
- public String getCTag() {
- return accountSettings.getAddressBookCTag();
- }
-
- @Override
- public void setCTag(String cTag) {
- accountSettings.setAddressBookCTag(cTag);
- }
-
-
- /* create/update/delete */
-
- public Contact newResource(long localID, String resourceName, String eTag) {
- return new Contact(localID, resourceName, eTag);
- }
-
- public void deleteAllExceptRemoteNames(Resource[] remoteResources) {
- String where;
-
- if (remoteResources.length != 0) {
- List sqlFileNames = new LinkedList();
- for (Resource res : remoteResources)
- sqlFileNames.add(DatabaseUtils.sqlEscapeString(res.getName()));
- where = entryColumnRemoteName() + " NOT IN (" + StringUtils.join(sqlFileNames, ",") + ")";
- } else
- where = entryColumnRemoteName() + " IS NOT NULL";
-
- Builder builder = ContentProviderOperation.newDelete(entriesURI()).withSelection(where, null);
- pendingOperations.add(builder
- .withYieldAllowed(true)
- .build());
- }
-
- @Override
- public void commit() throws LocalStorageException {
- super.commit();
-
- // update group details for groups we have just created
- Uri groupsUri = syncAdapterURI(Groups.CONTENT_URI);
- try {
- // newly created groups don't have a TITLE
- @Cleanup Cursor cursor = providerClient.query(groupsUri,
- new String[] { Groups.SOURCE_ID },
- Groups.TITLE + " IS NULL", null, null
- );
- while (cursor != null && cursor.moveToNext()) {
- // found group, set TITLE to SOURCE_ID and other details
- String sourceID = cursor.getString(0);
- pendingOperations.add(ContentProviderOperation.newUpdate(groupsUri)
- .withSelection(Groups.SOURCE_ID + "=?", new String[] { sourceID })
- .withValue(Groups.TITLE, sourceID)
- .withValue(Groups.GROUP_VISIBLE, 1)
- .build());
- super.commit();
- }
- } catch (RemoteException e) {
- throw new LocalStorageException("Couldn't update group names", e);
- }
- }
-
-
- /* methods for populating the data object from the content provider */
-
- @Override
- public void populate(Resource res) throws LocalStorageException {
- Contact c = (Contact)res;
-
- try {
- @Cleanup Cursor cursor = providerClient.query(ContentUris.withAppendedId(entriesURI(), c.getLocalID()),
- new String[] { entryColumnUID(), COLUMN_UNKNOWN_PROPERTIES, RawContacts.STARRED }, null, null, null);
- if (cursor != null && cursor.moveToNext()) {
- c.setUid(cursor.getString(0));
- c.setUnknownProperties(cursor.getString(1));
- c.setStarred(cursor.getInt(2) != 0);
- } else
- throw new RecordNotFoundException();
-
- populateStructuredName(c);
- populatePhoneNumbers(c);
- populateEmailAddresses(c);
- populatePhoto(c);
- populateOrganization(c);
- populateIMPPs(c);
- populateNickname(c);
- populateNote(c);
- populatePostalAddresses(c);
- populateCategories(c);
- populateURLs(c);
- populateEvents(c);
- populateSipAddress(c);
- } catch(RemoteException ex) {
- throw new LocalStorageException(ex);
- }
- }
-
- private void populateStructuredName(Contact c) throws RemoteException {
- @Cleanup Cursor cursor = providerClient.query(dataURI(), new String[] {
- /* 0 */ StructuredName.DISPLAY_NAME, StructuredName.PREFIX, StructuredName.GIVEN_NAME,
- /* 3 */ StructuredName.MIDDLE_NAME, StructuredName.FAMILY_NAME, StructuredName.SUFFIX,
- /* 6 */ StructuredName.PHONETIC_GIVEN_NAME, StructuredName.PHONETIC_MIDDLE_NAME, StructuredName.PHONETIC_FAMILY_NAME
- }, StructuredName.RAW_CONTACT_ID + "=? AND " + Data.MIMETYPE + "=?",
- new String[] { String.valueOf(c.getLocalID()), StructuredName.CONTENT_ITEM_TYPE }, null);
-
- if (cursor != null && cursor.moveToNext()) {
- c.setDisplayName(cursor.getString(0));
-
- c.setPrefix(cursor.getString(1));
- c.setGivenName(cursor.getString(2));
- c.setMiddleName(cursor.getString(3));
- c.setFamilyName(cursor.getString(4));
- c.setSuffix(cursor.getString(5));
-
- c.setPhoneticGivenName(cursor.getString(6));
- c.setPhoneticMiddleName(cursor.getString(7));
- c.setPhoneticFamilyName(cursor.getString(8));
- }
- }
-
- protected void populatePhoneNumbers(Contact c) throws RemoteException {
- @Cleanup Cursor cursor = providerClient.query(dataURI(), new String[] { Phone.TYPE, Phone.LABEL, Phone.NUMBER, Phone.IS_SUPER_PRIMARY },
- Phone.RAW_CONTACT_ID + "=? AND " + Data.MIMETYPE + "=?",
- new String[] { String.valueOf(c.getLocalID()), Phone.CONTENT_ITEM_TYPE }, null);
- while (cursor != null && cursor.moveToNext()) {
- ezvcard.property.Telephone number = new ezvcard.property.Telephone(cursor.getString(2));
- switch (cursor.getInt(0)) {
- case Phone.TYPE_HOME:
- number.addType(TelephoneType.HOME);
- break;
- case Phone.TYPE_MOBILE:
- number.addType(TelephoneType.CELL);
- break;
- case Phone.TYPE_WORK:
- number.addType(TelephoneType.WORK);
- break;
- case Phone.TYPE_FAX_WORK:
- number.addType(TelephoneType.FAX);
- number.addType(TelephoneType.WORK);
- break;
- case Phone.TYPE_FAX_HOME:
- number.addType(TelephoneType.FAX);
- number.addType(TelephoneType.HOME);
- break;
- case Phone.TYPE_PAGER:
- number.addType(TelephoneType.PAGER);
- break;
- case Phone.TYPE_CALLBACK:
- number.addType(Contact.PHONE_TYPE_CALLBACK);
- break;
- case Phone.TYPE_CAR:
- number.addType(TelephoneType.CAR);
- break;
- case Phone.TYPE_COMPANY_MAIN:
- number.addType(Contact.PHONE_TYPE_COMPANY_MAIN);
- break;
- case Phone.TYPE_ISDN:
- number.addType(TelephoneType.ISDN);
- break;
- case Phone.TYPE_MAIN:
- number.addType(TelephoneType.PREF);
- break;
- case Phone.TYPE_OTHER_FAX:
- number.addType(TelephoneType.FAX);
- break;
- case Phone.TYPE_RADIO:
- number.addType(Contact.PHONE_TYPE_RADIO);
- break;
- case Phone.TYPE_TELEX:
- number.addType(TelephoneType.TEXTPHONE);
- break;
- case Phone.TYPE_TTY_TDD:
- number.addType(TelephoneType.TEXT);
- break;
- case Phone.TYPE_WORK_MOBILE:
- number.addType(TelephoneType.CELL);
- number.addType(TelephoneType.WORK);
- break;
- case Phone.TYPE_WORK_PAGER:
- number.addType(TelephoneType.PAGER);
- number.addType(TelephoneType.WORK);
- break;
- case Phone.TYPE_ASSISTANT:
- number.addType(Contact.PHONE_TYPE_ASSISTANT);
- break;
- case Phone.TYPE_MMS:
- number.addType(Contact.PHONE_TYPE_MMS);
- break;
- case Phone.TYPE_CUSTOM:
- String customType = cursor.getString(1);
- if (!StringUtils.isEmpty(customType))
- number.addType(TelephoneType.get(labelToXName(customType)));
- }
- if (cursor.getInt(3) != 0) // IS_PRIMARY
- number.addType(TelephoneType.PREF);
- c.getPhoneNumbers().add(number);
- }
- }
-
- protected void populateEmailAddresses(Contact c) throws RemoteException {
- @Cleanup Cursor cursor = providerClient.query(dataURI(), new String[] { Email.TYPE, Email.ADDRESS, Email.LABEL, Email.IS_SUPER_PRIMARY },
- Email.RAW_CONTACT_ID + "=? AND " + Data.MIMETYPE + "=?",
- new String[] { String.valueOf(c.getLocalID()), Email.CONTENT_ITEM_TYPE }, null);
- while (cursor != null && cursor.moveToNext()) {
- ezvcard.property.Email email = new ezvcard.property.Email(cursor.getString(1));
- switch (cursor.getInt(0)) {
- case Email.TYPE_HOME:
- email.addType(EmailType.HOME);
- break;
- case Email.TYPE_WORK:
- email.addType(EmailType.WORK);
- break;
- case Email.TYPE_MOBILE:
- email.addType(Contact.EMAIL_TYPE_MOBILE);
- break;
- case Email.TYPE_CUSTOM:
- String customType = cursor.getString(2);
- if (!StringUtils.isEmpty(customType))
- email.addType(EmailType.get(labelToXName(customType)));
- }
- if (cursor.getInt(3) != 0) // IS_PRIMARY
- email.addType(EmailType.PREF);
- c.getEmails().add(email);
- }
- }
-
- protected void populatePhoto(Contact c) throws RemoteException {
- @Cleanup Cursor cursor = providerClient.query(dataURI(),
- new String[] { Photo.PHOTO_FILE_ID, Photo.PHOTO },
- Photo.RAW_CONTACT_ID + "=? AND " + Data.MIMETYPE + "=?",
- new String[] { String.valueOf(c.getLocalID()), Photo.CONTENT_ITEM_TYPE }, null);
- if (cursor != null && cursor.moveToNext()) {
- if (!cursor.isNull(0)) {
- Uri photoUri = Uri.withAppendedPath(
- ContentUris.withAppendedId(RawContacts.CONTENT_URI, c.getLocalID()),
- RawContacts.DisplayPhoto.CONTENT_DIRECTORY);
- try {
- @Cleanup AssetFileDescriptor fd = providerClient.openAssetFile(photoUri, "r");
- @Cleanup InputStream is = fd.createInputStream();
- c.setPhoto(IOUtils.toByteArray(is));
- } catch(IOException ex) {
- Log.w(TAG, "Couldn't read high-res contact photo", ex);
- }
- } else
- c.setPhoto(cursor.getBlob(1));
- }
- }
-
- protected void populateOrganization(Contact c) throws RemoteException {
- @Cleanup Cursor cursor = providerClient.query(dataURI(),
- new String[] { Organization.COMPANY, Organization.DEPARTMENT, Organization.TITLE, Organization.JOB_DESCRIPTION },
- Organization.RAW_CONTACT_ID + "=? AND " + Data.MIMETYPE + "=?",
- new String[] { String.valueOf(c.getLocalID()), Organization.CONTENT_ITEM_TYPE }, null);
- if (cursor != null && cursor.moveToNext()) {
- String company = cursor.getString(0),
- department = cursor.getString(1),
- title = cursor.getString(2),
- role = cursor.getString(3);
- if (!StringUtils.isEmpty(company) || !StringUtils.isEmpty(department)) {
- ezvcard.property.Organization org = new ezvcard.property.Organization();
- if (!StringUtils.isEmpty(company))
- org.addValue(company);
- if (!StringUtils.isEmpty(department))
- org.addValue(department);
- c.setOrganization(org);
- }
- if (!StringUtils.isEmpty(title))
- c.setJobTitle(title);
- if (!StringUtils.isEmpty(role))
- c.setJobDescription(role);
- }
- }
-
- protected void populateIMPPs(Contact c) throws RemoteException {
- @Cleanup Cursor cursor = providerClient.query(dataURI(), new String[] { Im.DATA, Im.TYPE, Im.LABEL, Im.PROTOCOL, Im.CUSTOM_PROTOCOL },
- Im.RAW_CONTACT_ID + "=? AND " + Data.MIMETYPE + "=?",
- new String[] { String.valueOf(c.getLocalID()), Im.CONTENT_ITEM_TYPE }, null);
- while (cursor != null && cursor.moveToNext()) {
- String handle = cursor.getString(0);
-
- Impp impp = null;
- switch (cursor.getInt(3)) {
- case Im.PROTOCOL_AIM:
- impp = Impp.aim(handle);
- break;
- case Im.PROTOCOL_MSN:
- impp = Impp.msn(handle);
- break;
- case Im.PROTOCOL_YAHOO:
- impp = Impp.yahoo(handle);
- break;
- case Im.PROTOCOL_SKYPE:
- impp = Impp.skype(handle);
- break;
- case Im.PROTOCOL_QQ:
- impp = new Impp("qq", handle);
- break;
- case Im.PROTOCOL_GOOGLE_TALK:
- impp = new Impp("google-talk", handle);
- break;
- case Im.PROTOCOL_ICQ:
- impp = Impp.icq(handle);
- break;
- case Im.PROTOCOL_JABBER:
- impp = Impp.xmpp(handle);
- break;
- case Im.PROTOCOL_NETMEETING:
- impp = new Impp("netmeeting", handle);
- break;
- case Im.PROTOCOL_CUSTOM:
- impp = new Impp(cursor.getString(4), handle);
- }
-
- if (impp != null) {
- switch (cursor.getInt(1)) {
- case Im.TYPE_HOME:
- impp.addType(ImppType.HOME);
- break;
- case Im.TYPE_WORK:
- impp.addType(ImppType.WORK);
- break;
- case Im.TYPE_CUSTOM:
- String customType = cursor.getString(2);
- if (!StringUtils.isEmpty(customType))
- impp.addType(ImppType.get(labelToXName(customType)));
- }
- c.getImpps().add(impp);
- }
- }
- }
-
- protected void populateNickname(Contact c) throws RemoteException {
- @Cleanup Cursor cursor = providerClient.query(dataURI(), new String[] { Nickname.NAME },
- Nickname.RAW_CONTACT_ID + "=? AND " + Data.MIMETYPE + "=?",
- new String[] { String.valueOf(c.getLocalID()), Nickname.CONTENT_ITEM_TYPE }, null);
- if (cursor != null && cursor.moveToNext())
- c.setNickName(cursor.getString(0));
- }
-
- protected void populateNote(Contact c) throws RemoteException {
- @Cleanup Cursor cursor = providerClient.query(dataURI(), new String[] { Note.NOTE },
- Note.RAW_CONTACT_ID + "=? AND " + Data.MIMETYPE + "=?",
- new String[] { String.valueOf(c.getLocalID()), Note.CONTENT_ITEM_TYPE }, null);
- if (cursor != null && cursor.moveToNext())
- c.setNote(cursor.getString(0));
- }
-
- protected void populatePostalAddresses(Contact c) throws RemoteException {
- @Cleanup Cursor cursor = providerClient.query(dataURI(), new String[] {
- /* 0 */ StructuredPostal.FORMATTED_ADDRESS, StructuredPostal.TYPE, StructuredPostal.LABEL,
- /* 3 */ StructuredPostal.STREET, StructuredPostal.POBOX, StructuredPostal.NEIGHBORHOOD,
- /* 6 */ StructuredPostal.CITY, StructuredPostal.REGION, StructuredPostal.POSTCODE,
- /* 9 */ StructuredPostal.COUNTRY
- }, StructuredPostal.RAW_CONTACT_ID + "=? AND " + Data.MIMETYPE + "=?",
- new String[] { String.valueOf(c.getLocalID()), StructuredPostal.CONTENT_ITEM_TYPE }, null);
- while (cursor != null && cursor.moveToNext()) {
- Address address = new Address();
-
- address.setLabel(cursor.getString(0));
- switch (cursor.getInt(1)) {
- case StructuredPostal.TYPE_HOME:
- address.addType(AddressType.HOME);
- break;
- case StructuredPostal.TYPE_WORK:
- address.addType(AddressType.WORK);
- break;
- case StructuredPostal.TYPE_CUSTOM:
- String customType = cursor.getString(2);
- if (!StringUtils.isEmpty(customType))
- address.addType(AddressType.get(labelToXName(customType)));
- break;
- }
- address.setStreetAddress(cursor.getString(3));
- address.setPoBox(cursor.getString(4));
- address.setExtendedAddress(cursor.getString(5));
- address.setLocality(cursor.getString(6));
- address.setRegion(cursor.getString(7));
- address.setPostalCode(cursor.getString(8));
- address.setCountry(cursor.getString(9));
- c.getAddresses().add(address);
- }
- }
-
- protected void populateCategories(Contact c) throws RemoteException {
- @Cleanup Cursor cursorMemberships = providerClient.query(dataURI(),
- new String[] { GroupMembership.GROUP_ROW_ID, GroupMembership.GROUP_SOURCE_ID },
- GroupMembership.RAW_CONTACT_ID + "=? AND " + Data.MIMETYPE + "=?",
- new String[] { String.valueOf(c.getLocalID()), GroupMembership.CONTENT_ITEM_TYPE }, null);
- List categories = c.getCategories();
- while (cursorMemberships != null && cursorMemberships.moveToNext()) {
- long rowID = cursorMemberships.getLong(0);
- String sourceID = cursorMemberships.getString(1);
-
- // either a row ID or a source ID must be available
- String where, whereArg;
- if (sourceID == null) {
- where = Groups._ID + "=?";
- whereArg = String.valueOf(rowID);
- } else {
- where = Groups.SOURCE_ID + "=?";
- whereArg = sourceID;
- }
- where += " AND " + Groups.DELETED + "=0"; // ignore deleted groups
- Log.d(TAG, "Populating group from " + where + " " + whereArg);
-
- // fetch group
- @Cleanup Cursor cursorGroups = providerClient.query(Groups.CONTENT_URI,
- new String[] { Groups.TITLE },
- where, new String[] { whereArg }, null
- );
- if (cursorGroups != null && cursorGroups.moveToNext()) {
- String title = cursorGroups.getString(0);
-
- if (sourceID == null) { // Group wasn't created by DAVdroid
- // SOURCE_ID IS NULL <=> _ID IS NOT NULL
- Log.d(TAG, "Setting SOURCE_ID of non-DAVdroid group to title: " + title);
-
- ContentValues v = new ContentValues(1);
- v.put(Groups.SOURCE_ID, title);
- v.put(Groups.GROUP_IS_READ_ONLY, 0);
- v.put(Groups.GROUP_VISIBLE, 1);
- providerClient.update(syncAdapterURI(Groups.CONTENT_URI), v, Groups._ID + "=?", new String[] { String.valueOf(rowID) });
-
- sourceID = title;
- }
-
- // add group to CATEGORIES
- if (sourceID != null)
- categories.add(sourceID);
- } else
- Log.d(TAG, "Group not found (maybe deleted)");
- }
- }
-
- protected void populateURLs(Contact c) throws RemoteException {
- @Cleanup Cursor cursor = providerClient.query(dataURI(), new String[] { Website.URL },
- Website.RAW_CONTACT_ID + "=? AND " + Data.MIMETYPE + "=?",
- new String[] { String.valueOf(c.getLocalID()), Website.CONTENT_ITEM_TYPE }, null);
- while (cursor != null && cursor.moveToNext())
- c.getURLs().add(cursor.getString(0));
- }
-
- protected void populateEvents(Contact c) throws RemoteException {
- @Cleanup Cursor cursor = providerClient.query(dataURI(), new String[] { CommonDataKinds.Event.TYPE, CommonDataKinds.Event.START_DATE },
- Photo.RAW_CONTACT_ID + "=? AND " + Data.MIMETYPE + "=?",
- new String[] { String.valueOf(c.getLocalID()), CommonDataKinds.Event.CONTENT_ITEM_TYPE }, null);
- while (cursor != null && cursor.moveToNext()) {
- SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd", Locale.US);
- try {
- Date date = formatter.parse(cursor.getString(1));
- switch (cursor.getInt(0)) {
- case CommonDataKinds.Event.TYPE_ANNIVERSARY:
- c.setAnniversary(new Anniversary(date));
- break;
- case CommonDataKinds.Event.TYPE_BIRTHDAY:
- c.setBirthDay(new Birthday(date));
- break;
- }
- } catch (ParseException e) {
- Log.w(TAG, "Couldn't parse local birthday/anniversary date", e);
- }
- }
- }
-
- protected void populateSipAddress(Contact c) throws RemoteException {
- @Cleanup Cursor cursor = providerClient.query(dataURI(),
- new String[] { SipAddress.SIP_ADDRESS, SipAddress.TYPE, SipAddress.LABEL },
- SipAddress.RAW_CONTACT_ID + "=? AND " + Data.MIMETYPE + "=?",
- new String[] { String.valueOf(c.getLocalID()), SipAddress.CONTENT_ITEM_TYPE }, null);
- if (cursor != null && cursor.moveToNext()) {
- Impp impp = new Impp("sip:" + cursor.getString(0));
- switch (cursor.getInt(1)) {
- case SipAddress.TYPE_HOME:
- impp.addType(ImppType.HOME);
- break;
- case SipAddress.TYPE_WORK:
- impp.addType(ImppType.WORK);
- break;
- case SipAddress.TYPE_CUSTOM:
- String customType = cursor.getString(2);
- if (!StringUtils.isEmpty(customType))
- impp.addType(ImppType.get(labelToXName(customType)));
- }
- c.getImpps().add(impp);
- }
- }
-
-
- /* content builder methods */
-
- @Override
- protected Builder buildEntry(Builder builder, Resource resource) {
- Contact contact = (Contact)resource;
-
- return builder
- .withValue(RawContacts.ACCOUNT_NAME, account.name)
- .withValue(RawContacts.ACCOUNT_TYPE, account.type)
- .withValue(entryColumnRemoteName(), contact.getName())
- .withValue(entryColumnUID(), contact.getUid())
- .withValue(entryColumnETag(), contact.getETag())
- .withValue(COLUMN_UNKNOWN_PROPERTIES, contact.getUnknownProperties())
- .withValue(RawContacts.STARRED, contact.isStarred() ? 1 : 0);
- }
-
-
- @Override
- protected void addDataRows(Resource resource, long localID, int backrefIdx) {
- Contact contact = (Contact)resource;
-
- queueOperation(buildStructuredName(newDataInsertBuilder(localID, backrefIdx), contact));
-
- for (Telephone number : contact.getPhoneNumbers())
- queueOperation(buildPhoneNumber(newDataInsertBuilder(localID, backrefIdx), number));
-
- for (ezvcard.property.Email email : contact.getEmails())
- queueOperation(buildEmail(newDataInsertBuilder(localID, backrefIdx), email));
-
- if (contact.getPhoto() != null)
- queueOperation(buildPhoto(newDataInsertBuilder(localID, backrefIdx), contact.getPhoto()));
-
- queueOperation(buildOrganization(newDataInsertBuilder(localID, backrefIdx), contact));
-
- for (Impp impp : contact.getImpps())
- queueOperation(buildIMPP(newDataInsertBuilder(localID, backrefIdx), impp));
-
- if (contact.getNickName() != null)
- queueOperation(buildNickName(newDataInsertBuilder(localID, backrefIdx), contact.getNickName()));
-
- if (contact.getNote() != null)
- queueOperation(buildNote(newDataInsertBuilder(localID, backrefIdx), contact.getNote()));
-
- for (Address address : contact.getAddresses())
- queueOperation(buildAddress(newDataInsertBuilder(localID, backrefIdx), address));
-
- for (String category : contact.getCategories())
- queueOperation(buildGroupMembership(newDataInsertBuilder(localID, backrefIdx), category));
-
- for (String url : contact.getURLs())
- queueOperation(buildURL(newDataInsertBuilder(localID, backrefIdx), url));
-
- // events
- if (contact.getAnniversary() != null)
- queueOperation(buildEvent(newDataInsertBuilder(localID, backrefIdx), contact.getAnniversary(), CommonDataKinds.Event.TYPE_ANNIVERSARY));
- if (contact.getBirthDay() != null)
- queueOperation(buildEvent(newDataInsertBuilder(localID, backrefIdx), contact.getBirthDay(), CommonDataKinds.Event.TYPE_BIRTHDAY));
-
- // TODO relations
-
- // SIP addresses are built by buildIMPP
- }
-
- @Override
- protected void removeDataRows(Resource resource) {
- pendingOperations.add(ContentProviderOperation.newDelete(dataURI())
- .withSelection(Data.RAW_CONTACT_ID + "=?",
- new String[] { String.valueOf(resource.getLocalID()) }).build());
- }
-
-
- protected Builder buildStructuredName(Builder builder, Contact contact) {
- return builder
- .withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE)
- .withValue(StructuredName.PREFIX, contact.getPrefix())
- .withValue(StructuredName.DISPLAY_NAME, contact.getDisplayName())
- .withValue(StructuredName.GIVEN_NAME, contact.getGivenName())
- .withValue(StructuredName.MIDDLE_NAME, contact.getMiddleName())
- .withValue(StructuredName.FAMILY_NAME, contact.getFamilyName())
- .withValue(StructuredName.SUFFIX, contact.getSuffix())
- .withValue(StructuredName.PHONETIC_GIVEN_NAME, contact.getPhoneticGivenName())
- .withValue(StructuredName.PHONETIC_MIDDLE_NAME, contact.getPhoneticMiddleName())
- .withValue(StructuredName.PHONETIC_FAMILY_NAME, contact.getPhoneticFamilyName());
- }
-
- protected Builder buildPhoneNumber(Builder builder, Telephone number) {
- int typeCode = Phone.TYPE_OTHER;
- String typeLabel = null;
- boolean is_primary = false;
-
- Set types = number.getTypes();
- // preferred number?
- if (types.contains(TelephoneType.PREF))
- is_primary = true;
-
- // 1 Android type <-> 2 VCard types: fax, cell, pager
- if (types.contains(TelephoneType.FAX)) {
- if (types.contains(TelephoneType.HOME))
- typeCode = Phone.TYPE_FAX_HOME;
- else if (types.contains(TelephoneType.WORK))
- typeCode = Phone.TYPE_FAX_WORK;
- else
- typeCode = Phone.TYPE_OTHER_FAX;
- } else if (types.contains(TelephoneType.CELL)) {
- if (types.contains(TelephoneType.WORK))
- typeCode = Phone.TYPE_WORK_MOBILE;
- else
- typeCode = Phone.TYPE_MOBILE;
- } else if (types.contains(TelephoneType.PAGER)) {
- if (types.contains(TelephoneType.WORK))
- typeCode = Phone.TYPE_WORK_PAGER;
- else
- typeCode = Phone.TYPE_PAGER;
- // types with 1:1 translation
- } else if (types.contains(TelephoneType.HOME)) {
- typeCode = Phone.TYPE_HOME;
- } else if (types.contains(TelephoneType.WORK)) {
- typeCode = Phone.TYPE_WORK;
- } else if (types.contains(Contact.PHONE_TYPE_CALLBACK)) {
- typeCode = Phone.TYPE_CALLBACK;
- } else if (types.contains(TelephoneType.CAR)) {
- typeCode = Phone.TYPE_CAR;
- } else if (types.contains(Contact.PHONE_TYPE_COMPANY_MAIN)) {
- typeCode = Phone.TYPE_COMPANY_MAIN;
- } else if (types.contains(TelephoneType.ISDN)) {
- typeCode = Phone.TYPE_ISDN;
- } else if (types.contains(TelephoneType.PREF)) {
- typeCode = Phone.TYPE_MAIN;
- } else if (types.contains(Contact.PHONE_TYPE_RADIO)) {
- typeCode = Phone.TYPE_RADIO;
- } else if (types.contains(TelephoneType.TEXTPHONE)) {
- typeCode = Phone.TYPE_TELEX;
- } else if (types.contains(TelephoneType.TEXT)) {
- typeCode = Phone.TYPE_TTY_TDD;
- } else if (types.contains(Contact.PHONE_TYPE_ASSISTANT)) {
- typeCode = Phone.TYPE_ASSISTANT;
- } else if (types.contains(Contact.PHONE_TYPE_MMS)) {
- typeCode = Phone.TYPE_MMS;
- } else if (!types.isEmpty()) {
- TelephoneType type = types.iterator().next();
- typeCode = Phone.TYPE_CUSTOM;
- typeLabel = xNameToLabel(type.getValue());
- }
-
- builder = builder
- .withValue(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE)
- .withValue(Phone.NUMBER, number.getText())
- .withValue(Phone.TYPE, typeCode)
- .withValue(Phone.IS_PRIMARY, is_primary ? 1 : 0)
- .withValue(Phone.IS_SUPER_PRIMARY, is_primary ? 1 : 0);
- if (typeLabel != null)
- builder = builder.withValue(Phone.LABEL, typeLabel);
- return builder;
- }
-
- protected Builder buildEmail(Builder builder, ezvcard.property.Email email) {
- int typeCode = 0;
- String typeLabel = null;
- boolean is_primary = false;
-
- for (EmailType type : email.getTypes())
- if (type == EmailType.PREF)
- is_primary = true;
- else if (type == EmailType.HOME)
- typeCode = Email.TYPE_HOME;
- else if (type == EmailType.WORK)
- typeCode = Email.TYPE_WORK;
- else if (type == Contact.EMAIL_TYPE_MOBILE)
- typeCode = Email.TYPE_MOBILE;
- if (typeCode == 0) {
- if (email.getTypes().isEmpty())
- typeCode = Email.TYPE_OTHER;
- else {
- typeCode = Email.TYPE_CUSTOM;
- typeLabel = xNameToLabel(email.getTypes().iterator().next().getValue());
- }
- }
-
- builder = builder
- .withValue(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE)
- .withValue(Email.ADDRESS, email.getValue())
- .withValue(Email.TYPE, typeCode)
- .withValue(Email.IS_PRIMARY, is_primary ? 1 : 0)
- .withValue(Phone.IS_SUPER_PRIMARY, is_primary ? 1 : 0);;
- if (typeLabel != null)
- builder = builder.withValue(Email.LABEL, typeLabel);
- return builder;
- }
-
- protected Builder buildPhoto(Builder builder, byte[] photo) {
- return builder
- .withValue(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE)
- .withValue(Photo.PHOTO, photo);
- }
-
- protected Builder buildOrganization(Builder builder, Contact contact) {
- if (contact.getOrganization() == null && contact.getJobTitle() == null && contact.getJobDescription() == null)
- return null;
-
- ezvcard.property.Organization organization = contact.getOrganization();
- String company = null, department = null;
- if (organization != null) {
- Iterator org = organization.getValues().iterator();
- if (org.hasNext())
- company = org.next();
- if (org.hasNext())
- department = org.next();
- }
-
- return builder
- .withValue(Data.MIMETYPE, Organization.CONTENT_ITEM_TYPE)
- .withValue(Organization.COMPANY, company)
- .withValue(Organization.DEPARTMENT, department)
- .withValue(Organization.TITLE, contact.getJobTitle())
- .withValue(Organization.JOB_DESCRIPTION, contact.getJobDescription());
- }
-
- protected Builder buildIMPP(Builder builder, Impp impp) {
- int typeCode = 0;
- String typeLabel = null;
- for (ImppType type : impp.getTypes())
- if (type == ImppType.HOME)
- typeCode = Im.TYPE_HOME;
- else if (type == ImppType.WORK || type == ImppType.BUSINESS)
- typeCode = Im.TYPE_WORK;
- if (typeCode == 0)
- if (impp.getTypes().isEmpty())
- typeCode = Im.TYPE_OTHER;
- else {
- typeCode = Im.TYPE_CUSTOM;
- typeLabel = xNameToLabel(impp.getTypes().iterator().next().getValue());
- }
-
- int protocolCode = 0;
- String protocolLabel = null;
-
- String protocol = impp.getProtocol();
- if (protocol == null) {
- Log.w(TAG, "Ignoring IMPP address without protocol");
- return null;
- }
-
- // SIP addresses are IMPP entries in the VCard but locally stored in SipAddress rather than Im
- boolean sipAddress = false;
-
- if (impp.isAim())
- protocolCode = Im.PROTOCOL_AIM;
- else if (impp.isMsn())
- protocolCode = Im.PROTOCOL_MSN;
- else if (impp.isYahoo())
- protocolCode = Im.PROTOCOL_YAHOO;
- else if (impp.isSkype())
- protocolCode = Im.PROTOCOL_SKYPE;
- else if (protocol.equalsIgnoreCase("qq"))
- protocolCode = Im.PROTOCOL_QQ;
- else if (protocol.equalsIgnoreCase("google-talk"))
- protocolCode = Im.PROTOCOL_GOOGLE_TALK;
- else if (impp.isIcq())
- protocolCode = Im.PROTOCOL_ICQ;
- else if (impp.isXmpp() || protocol.equalsIgnoreCase("jabber"))
- protocolCode = Im.PROTOCOL_JABBER;
- else if (protocol.equalsIgnoreCase("netmeeting"))
- protocolCode = Im.PROTOCOL_NETMEETING;
- else if (protocol.equalsIgnoreCase("sip"))
- sipAddress = true;
- else {
- protocolCode = Im.PROTOCOL_CUSTOM;
- protocolLabel = protocol;
- }
-
- if (sipAddress)
- // save as SIP address
- builder = builder
- .withValue(Data.MIMETYPE, SipAddress.CONTENT_ITEM_TYPE)
- .withValue(Im.DATA, impp.getHandle())
- .withValue(Im.TYPE, typeCode);
- else {
- // save as IM address
- builder = builder
- .withValue(Data.MIMETYPE, Im.CONTENT_ITEM_TYPE)
- .withValue(Im.DATA, impp.getHandle())
- .withValue(Im.TYPE, typeCode)
- .withValue(Im.PROTOCOL, protocolCode);
- if (protocolLabel != null)
- builder = builder.withValue(Im.CUSTOM_PROTOCOL, protocolLabel);
- }
- if (typeLabel != null)
- builder = builder.withValue(Im.LABEL, typeLabel);
- return builder;
- }
-
- protected Builder buildNickName(Builder builder, String nickName) {
- return builder
- .withValue(Data.MIMETYPE, Nickname.CONTENT_ITEM_TYPE)
- .withValue(Nickname.NAME, nickName);
- }
-
- protected Builder buildNote(Builder builder, String note) {
- return builder
- .withValue(Data.MIMETYPE, Note.CONTENT_ITEM_TYPE)
- .withValue(Note.NOTE, note);
- }
-
- protected Builder buildAddress(Builder builder, Address address) {
- /* street po.box (extended)
- * region
- * postal code city
- * country
- */
- String formattedAddress = address.getLabel();
- if (StringUtils.isEmpty(formattedAddress)) {
- String lineStreet = StringUtils.join(new String[] { address.getStreetAddress(), address.getPoBox(), address.getExtendedAddress() }, " "),
- lineLocality = StringUtils.join(new String[] { address.getPostalCode(), address.getLocality() }, " ");
-
- List lines = new LinkedList();
- if (lineStreet != null)
- lines.add(lineStreet);
- if (address.getRegion() != null && !address.getRegion().isEmpty())
- lines.add(address.getRegion());
- if (lineLocality != null)
- lines.add(lineLocality);
-
- formattedAddress = StringUtils.join(lines, "\n");
- }
-
- int typeCode = 0;
- String typeLabel = null;
- for (AddressType type : address.getTypes())
- if (type == AddressType.HOME)
- typeCode = StructuredPostal.TYPE_HOME;
- else if (type == AddressType.WORK)
- typeCode = StructuredPostal.TYPE_WORK;
- if (typeCode == 0)
- if (address.getTypes().isEmpty())
- typeCode = StructuredPostal.TYPE_OTHER;
- else {
- typeCode = StructuredPostal.TYPE_CUSTOM;
- typeLabel = xNameToLabel(address.getTypes().iterator().next().getValue());
- }
-
- builder = builder
- .withValue(Data.MIMETYPE, StructuredPostal.CONTENT_ITEM_TYPE)
- .withValue(StructuredPostal.FORMATTED_ADDRESS, formattedAddress)
- .withValue(StructuredPostal.TYPE, typeCode)
- .withValue(StructuredPostal.STREET, address.getStreetAddress())
- .withValue(StructuredPostal.POBOX, address.getPoBox())
- .withValue(StructuredPostal.NEIGHBORHOOD, address.getExtendedAddress())
- .withValue(StructuredPostal.CITY, address.getLocality())
- .withValue(StructuredPostal.REGION, address.getRegion())
- .withValue(StructuredPostal.POSTCODE, address.getPostalCode())
- .withValue(StructuredPostal.COUNTRY, address.getCountry());
- if (typeLabel != null)
- builder = builder.withValue(StructuredPostal.LABEL, typeLabel);
- return builder;
- }
-
- protected Builder buildGroupMembership(Builder builder, String group) {
- return builder
- .withValue(Data.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE)
- .withValue(GroupMembership.GROUP_SOURCE_ID, group);
- }
-
- protected Builder buildURL(Builder builder, String url) {
- return builder
- .withValue(Data.MIMETYPE, Website.CONTENT_ITEM_TYPE)
- .withValue(Website.URL, url);
- }
-
- protected Builder buildEvent(Builder builder, DateOrTimeProperty date, int type) {
- SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd", Locale.US);
- if (date.getDate() == null) {
- Log.i(TAG, "Ignoring contact event without date");
- return null;
- }
- return builder
- .withValue(Data.MIMETYPE, CommonDataKinds.Event.CONTENT_ITEM_TYPE)
- .withValue(CommonDataKinds.Event.TYPE, type)
- .withValue(CommonDataKinds.Event.START_DATE, formatter.format(date.getDate()));
- }
-
-
-
- /* helper methods */
-
- protected Uri dataURI() {
- return syncAdapterURI(Data.CONTENT_URI);
- }
-
- protected static String labelToXName(String label) {
- return "X-" + label.replaceAll(" ","_").replaceAll("[^\\p{L}\\p{Nd}\\-_]", "").toUpperCase(Locale.US);
- }
-
- private Builder newDataInsertBuilder(long raw_contact_id, Integer backrefIdx) {
- return newDataInsertBuilder(dataURI(), Data.RAW_CONTACT_ID, raw_contact_id, backrefIdx);
- }
-
- protected static String xNameToLabel(String xname) {
- // "X-MY_PROPERTY"
- // 1. ensure lower case -> "x-my_property"
- // 2. remove x- from beginning -> "my_property"
- // 3. replace "_" by " " -> "my property"
- // 4. capitalize -> "My Property"
- String lowerCase = StringUtils.lowerCase(xname, Locale.US),
- withoutPrefix = StringUtils.removeStart(lowerCase, "x-"),
- withSpaces = StringUtils.replace(withoutPrefix, "_", " ");
- return WordUtils.capitalize(withSpaces);
- }
-
-}
diff --git a/src/at/bitfire/davdroid/resource/LocalCalendar.java b/src/at/bitfire/davdroid/resource/LocalCalendar.java
deleted file mode 100644
index 9fb60064..00000000
--- a/src/at/bitfire/davdroid/resource/LocalCalendar.java
+++ /dev/null
@@ -1,612 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 Ricki Hirner (bitfire web engineering).
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the GNU Public License v3.0
- * which accompanies this distribution, and is available at
- * http://www.gnu.org/licenses/gpl.html
- ******************************************************************************/
-package at.bitfire.davdroid.resource;
-
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.text.ParseException;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import lombok.Cleanup;
-import lombok.Getter;
-import net.fortuna.ical4j.model.Dur;
-import net.fortuna.ical4j.model.Parameter;
-import net.fortuna.ical4j.model.ParameterList;
-import net.fortuna.ical4j.model.PropertyList;
-import net.fortuna.ical4j.model.component.VAlarm;
-import net.fortuna.ical4j.model.parameter.Cn;
-import net.fortuna.ical4j.model.parameter.CuType;
-import net.fortuna.ical4j.model.parameter.PartStat;
-import net.fortuna.ical4j.model.parameter.Role;
-import net.fortuna.ical4j.model.property.Action;
-import net.fortuna.ical4j.model.property.Attendee;
-import net.fortuna.ical4j.model.property.Description;
-import net.fortuna.ical4j.model.property.Duration;
-import net.fortuna.ical4j.model.property.ExDate;
-import net.fortuna.ical4j.model.property.ExRule;
-import net.fortuna.ical4j.model.property.Organizer;
-import net.fortuna.ical4j.model.property.RDate;
-import net.fortuna.ical4j.model.property.RRule;
-import net.fortuna.ical4j.model.property.Status;
-
-import org.apache.commons.lang.StringUtils;
-
-import android.accounts.Account;
-import android.annotation.SuppressLint;
-import android.annotation.TargetApi;
-import android.content.ContentProviderClient;
-import android.content.ContentProviderOperation;
-import android.content.ContentProviderOperation.Builder;
-import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.database.DatabaseUtils;
-import android.net.Uri;
-import android.os.Build;
-import android.os.RemoteException;
-import android.provider.CalendarContract;
-import android.provider.CalendarContract.Attendees;
-import android.provider.CalendarContract.Calendars;
-import android.provider.CalendarContract.Events;
-import android.provider.CalendarContract.Reminders;
-import android.provider.ContactsContract;
-import android.util.Log;
-
-/**
- * Represents a locally stored calendar, containing Events.
- * Communicates with the Android Contacts Provider which uses an SQLite
- * database to store the contacts.
- */
-public class LocalCalendar extends LocalCollection {
- private static final String TAG = "davdroid.LocalCalendar";
-
- @Getter protected long id;
- @Getter protected String url;
-
- protected static String COLLECTION_COLUMN_CTAG = Calendars.CAL_SYNC1;
-
-
- /* database fields */
-
- @Override
- protected Uri entriesURI() {
- return syncAdapterURI(Events.CONTENT_URI);
- }
-
- protected String entryColumnAccountType() { return Events.ACCOUNT_TYPE; }
- protected String entryColumnAccountName() { return Events.ACCOUNT_NAME; }
-
- protected String entryColumnParentID() { return Events.CALENDAR_ID; }
- protected String entryColumnID() { return Events._ID; }
- protected String entryColumnRemoteName() { return Events._SYNC_ID; }
- protected String entryColumnETag() { return Events.SYNC_DATA1; }
-
- protected String entryColumnDirty() { return Events.DIRTY; }
- protected String entryColumnDeleted() { return Events.DELETED; }
-
- @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
- protected String entryColumnUID() {
- return (android.os.Build.VERSION.SDK_INT >= 17) ?
- Events.UID_2445 : Events.SYNC_DATA2;
- }
-
-
- /* class methods, constructor */
-
- @SuppressLint("InlinedApi")
- public static void create(Account account, ContentResolver resolver, ServerInfo.ResourceInfo info) throws LocalStorageException {
- ContentProviderClient client = resolver.acquireContentProviderClient(CalendarContract.AUTHORITY);
- if (client == null)
- throw new LocalStorageException("No Calendar Provider found (Calendar app disabled?)");
-
- int color = 0xFFC3EA6E; // fallback: "DAVdroid green"
- if (info.getColor() != null) {
- Pattern p = Pattern.compile("#?(\\p{XDigit}{6})(\\p{XDigit}{2})?");
- Matcher m = p.matcher(info.getColor());
- if (m.find()) {
- int color_rgb = Integer.parseInt(m.group(1), 16);
- int color_alpha = m.group(2) != null ? (Integer.parseInt(m.group(2), 16) & 0xFF) : 0xFF;
- color = (color_alpha << 24) | color_rgb;
- }
- }
-
- ContentValues values = new ContentValues();
- values.put(Calendars.ACCOUNT_NAME, account.name);
- values.put(Calendars.ACCOUNT_TYPE, account.type);
- values.put(Calendars.NAME, info.getURL());
- values.put(Calendars.CALENDAR_DISPLAY_NAME, info.getTitle());
- values.put(Calendars.CALENDAR_COLOR, color);
- values.put(Calendars.OWNER_ACCOUNT, account.name);
- values.put(Calendars.SYNC_EVENTS, 1);
- values.put(Calendars.VISIBLE, 1);
- values.put(Calendars.ALLOWED_REMINDERS, Reminders.METHOD_ALERT);
-
- if (info.isReadOnly())
- values.put(Calendars.CALENDAR_ACCESS_LEVEL, Calendars.CAL_ACCESS_READ);
- else {
- values.put(Calendars.CALENDAR_ACCESS_LEVEL, Calendars.CAL_ACCESS_OWNER);
- values.put(Calendars.CAN_ORGANIZER_RESPOND, 1);
- values.put(Calendars.CAN_MODIFY_TIME_ZONE, 1);
- }
-
- if (android.os.Build.VERSION.SDK_INT >= 15) {
- values.put(Calendars.ALLOWED_AVAILABILITY, Events.AVAILABILITY_BUSY + "," + Events.AVAILABILITY_FREE + "," + Events.AVAILABILITY_TENTATIVE);
- values.put(Calendars.ALLOWED_ATTENDEE_TYPES, Attendees.TYPE_NONE + "," + Attendees.TYPE_OPTIONAL + "," + Attendees.TYPE_REQUIRED + "," + Attendees.TYPE_RESOURCE);
- }
-
- if (info.getTimezone() != null)
- values.put(Calendars.CALENDAR_TIME_ZONE, info.getTimezone());
-
- Log.i(TAG, "Inserting calendar: " + values.toString() + " -> " + calendarsURI(account).toString());
- try {
- client.insert(calendarsURI(account), values);
- } catch(RemoteException e) {
- throw new LocalStorageException(e);
- }
- }
-
- public static LocalCalendar[] findAll(Account account, ContentProviderClient providerClient) throws RemoteException {
- @Cleanup Cursor cursor = providerClient.query(calendarsURI(account),
- new String[] { Calendars._ID, Calendars.NAME },
- Calendars.DELETED + "=0 AND " + Calendars.SYNC_EVENTS + "=1", null, null);
-
- LinkedList calendars = new LinkedList();
- while (cursor != null && cursor.moveToNext())
- calendars.add(new LocalCalendar(account, providerClient, cursor.getInt(0), cursor.getString(1)));
- return calendars.toArray(new LocalCalendar[0]);
- }
-
- public LocalCalendar(Account account, ContentProviderClient providerClient, long id, String url) throws RemoteException {
- super(account, providerClient);
- this.id = id;
- this.url = url;
- }
-
-
- /* collection operations */
-
- @Override
- public String getCTag() throws LocalStorageException {
- try {
- @Cleanup Cursor c = providerClient.query(ContentUris.withAppendedId(calendarsURI(), id),
- new String[] { COLLECTION_COLUMN_CTAG }, null, null, null);
- if (c.moveToFirst()) {
- return c.getString(0);
- } else
- throw new LocalStorageException("Couldn't query calendar CTag");
- } catch(RemoteException e) {
- throw new LocalStorageException(e);
- }
- }
-
- @Override
- public void setCTag(String cTag) throws LocalStorageException {
- ContentValues values = new ContentValues(1);
- values.put(COLLECTION_COLUMN_CTAG, cTag);
- try {
- providerClient.update(ContentUris.withAppendedId(calendarsURI(), id), values, null, null);
- } catch(RemoteException e) {
- throw new LocalStorageException(e);
- }
- }
-
-
- /* create/update/delete */
-
- public Event newResource(long localID, String resourceName, String eTag) {
- return new Event(localID, resourceName, eTag);
- }
-
- public void deleteAllExceptRemoteNames(Resource[] remoteResources) {
- String where;
-
- if (remoteResources.length != 0) {
- List sqlFileNames = new LinkedList();
- for (Resource res : remoteResources)
- sqlFileNames.add(DatabaseUtils.sqlEscapeString(res.getName()));
- where = entryColumnRemoteName() + " NOT IN (" + StringUtils.join(sqlFileNames, ",") + ")";
- } else
- where = entryColumnRemoteName() + " IS NOT NULL";
-
- Builder builder = ContentProviderOperation.newDelete(entriesURI())
- .withSelection(entryColumnParentID() + "=? AND (" + where + ")", new String[] { String.valueOf(id) });
- pendingOperations.add(builder
- .withYieldAllowed(true)
- .build());
- }
-
-
- /* methods for populating the data object from the content provider */
-
-
- @Override
- public void populate(Resource resource) throws LocalStorageException {
- Event e = (Event)resource;
-
- try {
- @Cleanup Cursor cursor = providerClient.query(ContentUris.withAppendedId(entriesURI(), e.getLocalID()),
- new String[] {
- /* 0 */ Events.TITLE, Events.EVENT_LOCATION, Events.DESCRIPTION,
- /* 3 */ Events.DTSTART, Events.DTEND, Events.EVENT_TIMEZONE, Events.EVENT_END_TIMEZONE, Events.ALL_DAY,
- /* 8 */ Events.STATUS, Events.ACCESS_LEVEL,
- /* 10 */ Events.RRULE, Events.RDATE, Events.EXRULE, Events.EXDATE,
- /* 14 */ Events.HAS_ATTENDEE_DATA, Events.ORGANIZER, Events.SELF_ATTENDEE_STATUS,
- /* 17 */ entryColumnUID(), Events.DURATION, Events.AVAILABILITY
- }, null, null, null);
- if (cursor != null && cursor.moveToNext()) {
- e.setUid(cursor.getString(17));
-
- e.setSummary(cursor.getString(0));
- e.setLocation(cursor.getString(1));
- e.setDescription(cursor.getString(2));
-
- boolean allDay = cursor.getInt(7) != 0;
- long tsStart = cursor.getLong(3),
- tsEnd = cursor.getLong(4);
- String duration = cursor.getString(18);
-
- String tzId = null;
- if (allDay) {
- e.setDtStart(tsStart, null);
- // provide only DTEND and not DURATION for all-day events
- if (tsEnd == 0) {
- Dur dur = new Dur(duration);
- java.util.Date dEnd = dur.getTime(new java.util.Date(tsStart));
- tsEnd = dEnd.getTime();
- }
- e.setDtEnd(tsEnd, null);
-
- } else {
- // use the start time zone for the end time, too
- // because apps like Samsung Planner allow the user to change "the" time zone but change the start time zone only
- tzId = cursor.getString(5);
- e.setDtStart(tsStart, tzId);
- if (tsEnd != 0)
- e.setDtEnd(tsEnd, tzId);
- else if (!StringUtils.isEmpty(duration))
- e.setDuration(new Duration(new Dur(duration)));
- }
-
- // recurrence
- try {
- String strRRule = cursor.getString(10);
- if (!StringUtils.isEmpty(strRRule))
- e.setRrule(new RRule(strRRule));
-
- String strRDate = cursor.getString(11);
- if (!StringUtils.isEmpty(strRDate)) {
- RDate rDate = new RDate();
- rDate.setValue(strRDate);
- e.setRdate(rDate);
- }
-
- String strExRule = cursor.getString(12);
- if (!StringUtils.isEmpty(strExRule)) {
- ExRule exRule = new ExRule();
- exRule.setValue(strExRule);
- e.setExrule(exRule);
- }
-
- String strExDate = cursor.getString(13);
- if (!StringUtils.isEmpty(strExDate)) {
- // ignored, see https://code.google.com/p/android/issues/detail?id=21426
- ExDate exDate = new ExDate();
- exDate.setValue(strExDate);
- e.setExdate(exDate);
- }
- } catch (ParseException ex) {
- Log.w(TAG, "Couldn't parse recurrence rules, ignoring", ex);
- } catch (IllegalArgumentException ex) {
- Log.w(TAG, "Invalid recurrence rules, ignoring", ex);
- }
-
- // status
- switch (cursor.getInt(8)) {
- case Events.STATUS_CONFIRMED:
- e.setStatus(Status.VEVENT_CONFIRMED);
- break;
- case Events.STATUS_TENTATIVE:
- e.setStatus(Status.VEVENT_TENTATIVE);
- break;
- case Events.STATUS_CANCELED:
- e.setStatus(Status.VEVENT_CANCELLED);
- }
-
- // availability
- e.setOpaque(cursor.getInt(19) != Events.AVAILABILITY_FREE);
-
- // attendees
- if (cursor.getInt(14) != 0) { // has attendees
- try {
- e.setOrganizer(new Organizer(new URI("mailto", cursor.getString(15), null)));
- } catch (URISyntaxException ex) {
- Log.e(TAG, "Error when creating ORGANIZER URI, ignoring", ex);
- }
- populateAttendees(e);
- }
-
- // classification
- switch (cursor.getInt(9)) {
- case Events.ACCESS_CONFIDENTIAL:
- case Events.ACCESS_PRIVATE:
- e.setForPublic(false);
- break;
- case Events.ACCESS_PUBLIC:
- e.setForPublic(true);
- }
-
- populateReminders(e);
- } else
- throw new RecordNotFoundException();
- } catch(RemoteException ex) {
- throw new LocalStorageException(ex);
- }
- }
-
-
- void populateAttendees(Event e) throws RemoteException {
- Uri attendeesUri = Attendees.CONTENT_URI.buildUpon()
- .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true")
- .build();
- @Cleanup Cursor c = providerClient.query(attendeesUri, new String[] {
- /* 0 */ Attendees.ATTENDEE_EMAIL, Attendees.ATTENDEE_NAME, Attendees.ATTENDEE_TYPE,
- /* 3 */ Attendees.ATTENDEE_RELATIONSHIP, Attendees.STATUS
- }, Attendees.EVENT_ID + "=?", new String[] { String.valueOf(e.getLocalID()) }, null);
- while (c != null && c.moveToNext()) {
- try {
- Attendee attendee = new Attendee(new URI("mailto", c.getString(0), null));
- ParameterList params = attendee.getParameters();
-
- String cn = c.getString(1);
- if (cn != null)
- params.add(new Cn(cn));
-
- // type
- int type = c.getInt(2);
- params.add((type == Attendees.TYPE_RESOURCE) ? CuType.RESOURCE : CuType.INDIVIDUAL);
-
- // role
- int relationship = c.getInt(3);
- switch (relationship) {
- case Attendees.RELATIONSHIP_ORGANIZER:
- params.add(Role.CHAIR);
- break;
- case Attendees.RELATIONSHIP_ATTENDEE:
- case Attendees.RELATIONSHIP_PERFORMER:
- case Attendees.RELATIONSHIP_SPEAKER:
- params.add((type == Attendees.TYPE_REQUIRED) ? Role.REQ_PARTICIPANT : Role.OPT_PARTICIPANT);
- break;
- case Attendees.RELATIONSHIP_NONE:
- params.add(Role.NON_PARTICIPANT);
- }
-
- // status
- switch (c.getInt(4)) {
- case Attendees.ATTENDEE_STATUS_INVITED:
- params.add(PartStat.NEEDS_ACTION);
- break;
- case Attendees.ATTENDEE_STATUS_ACCEPTED:
- params.add(PartStat.ACCEPTED);
- break;
- case Attendees.ATTENDEE_STATUS_DECLINED:
- params.add(PartStat.DECLINED);
- break;
- case Attendees.ATTENDEE_STATUS_TENTATIVE:
- params.add(PartStat.TENTATIVE);
- break;
- }
-
- e.addAttendee(attendee);
- } catch (URISyntaxException ex) {
- Log.e(TAG, "Couldn't parse attendee information, ignoring", ex);
- }
- }
- }
-
- void populateReminders(Event e) throws RemoteException {
- // reminders
- Uri remindersUri = Reminders.CONTENT_URI.buildUpon()
- .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true")
- .build();
- @Cleanup Cursor c = providerClient.query(remindersUri, new String[] {
- /* 0 */ Reminders.MINUTES, Reminders.METHOD
- }, Reminders.EVENT_ID + "=?", new String[] { String.valueOf(e.getLocalID()) }, null);
- while (c != null && c.moveToNext()) {
- VAlarm alarm = new VAlarm(new Dur(0, 0, -c.getInt(0), 0));
-
- PropertyList props = alarm.getProperties();
- switch (c.getInt(1)) {
- /*case Reminders.METHOD_EMAIL:
- props.add(Action.EMAIL);
- break;*/
- default:
- props.add(Action.DISPLAY);
- props.add(new Description(e.getSummary()));
- }
- e.addAlarm(alarm);
- }
- }
-
-
- /* content builder methods */
-
- @Override
- protected Builder buildEntry(Builder builder, Resource resource) {
- Event event = (Event)resource;
-
- builder = builder
- .withValue(Events.CALENDAR_ID, id)
- .withValue(entryColumnRemoteName(), event.getName())
- .withValue(entryColumnETag(), event.getETag())
- .withValue(entryColumnUID(), event.getUid())
- .withValue(Events.ALL_DAY, event.isAllDay() ? 1 : 0)
- .withValue(Events.DTSTART, event.getDtStartInMillis())
- .withValue(Events.EVENT_TIMEZONE, event.getDtStartTzID())
- .withValue(Events.HAS_ATTENDEE_DATA, event.getAttendees().isEmpty() ? 0 : 1)
- .withValue(Events.GUESTS_CAN_INVITE_OTHERS, 1)
- .withValue(Events.GUESTS_CAN_MODIFY, 1)
- .withValue(Events.GUESTS_CAN_SEE_GUESTS, 1);
-
- boolean recurring = false;
- if (event.getRrule() != null) {
- recurring = true;
- builder = builder.withValue(Events.RRULE, event.getRrule().getValue());
- }
- if (event.getRdate() != null) {
- recurring = true;
- builder = builder.withValue(Events.RDATE, event.getRdate().getValue());
- }
- if (event.getExrule() != null)
- builder = builder.withValue(Events.EXRULE, event.getExrule().getValue());
- if (event.getExdate() != null)
- builder = builder.withValue(Events.EXDATE, event.getExdate().getValue());
-
- // set either DTEND for single-time events or DURATION for recurring events
- // because that's the way Android likes it (see docs)
- if (recurring) {
- // calculate DURATION from start and end date
- Duration duration = new Duration(event.getDtStart().getDate(), event.getDtEnd().getDate());
- builder = builder.withValue(Events.DURATION, duration.getValue());
- } else {
- builder = builder
- .withValue(Events.DTEND, event.getDtEndInMillis())
- .withValue(Events.EVENT_END_TIMEZONE, event.getDtEndTzID());
- }
-
- if (event.getSummary() != null)
- builder = builder.withValue(Events.TITLE, event.getSummary());
- if (event.getLocation() != null)
- builder = builder.withValue(Events.EVENT_LOCATION, event.getLocation());
- if (event.getDescription() != null)
- builder = builder.withValue(Events.DESCRIPTION, event.getDescription());
-
- if (event.getOrganizer() != null && event.getOrganizer().getCalAddress() != null) {
- URI organizer = event.getOrganizer().getCalAddress();
- if (organizer.getScheme() != null && organizer.getScheme().equalsIgnoreCase("mailto"))
- builder = builder.withValue(Events.ORGANIZER, organizer.getSchemeSpecificPart());
- }
-
- Status status = event.getStatus();
- if (status != null) {
- int statusCode = Events.STATUS_TENTATIVE;
- if (status == Status.VEVENT_CONFIRMED)
- statusCode = Events.STATUS_CONFIRMED;
- else if (status == Status.VEVENT_CANCELLED)
- statusCode = Events.STATUS_CANCELED;
- builder = builder.withValue(Events.STATUS, statusCode);
- }
-
- builder = builder.withValue(Events.AVAILABILITY, event.isOpaque() ? Events.AVAILABILITY_BUSY : Events.AVAILABILITY_FREE);
-
- if (event.getForPublic() != null)
- builder = builder.withValue(Events.ACCESS_LEVEL, event.getForPublic() ? Events.ACCESS_PUBLIC : Events.ACCESS_PRIVATE);
-
- return builder;
- }
-
-
- @Override
- protected void addDataRows(Resource resource, long localID, int backrefIdx) {
- Event event = (Event)resource;
- for (Attendee attendee : event.getAttendees())
- pendingOperations.add(buildAttendee(newDataInsertBuilder(Attendees.CONTENT_URI, Attendees.EVENT_ID, localID, backrefIdx), attendee).build());
- for (VAlarm alarm : event.getAlarms())
- pendingOperations.add(buildReminder(newDataInsertBuilder(Reminders.CONTENT_URI, Reminders.EVENT_ID, localID, backrefIdx), alarm).build());
- }
-
- @Override
- protected void removeDataRows(Resource resource) {
- Event event = (Event)resource;
- pendingOperations.add(ContentProviderOperation.newDelete(syncAdapterURI(Attendees.CONTENT_URI))
- .withSelection(Attendees.EVENT_ID + "=?",
- new String[] { String.valueOf(event.getLocalID()) }).build());
- pendingOperations.add(ContentProviderOperation.newDelete(syncAdapterURI(Reminders.CONTENT_URI))
- .withSelection(Reminders.EVENT_ID + "=?",
- new String[] { String.valueOf(event.getLocalID()) }).build());
- }
-
-
- @SuppressLint("InlinedApi")
- protected Builder buildAttendee(Builder builder, Attendee attendee) {
- Uri member = Uri.parse(attendee.getValue());
- String email = member.getSchemeSpecificPart();
-
- Cn cn = (Cn)attendee.getParameter(Parameter.CN);
- if (cn != null)
- builder = builder.withValue(Attendees.ATTENDEE_NAME, cn.getValue());
-
- int type = Attendees.TYPE_NONE;
-
- CuType cutype = (CuType)attendee.getParameter(Parameter.CUTYPE);
- if (cutype == CuType.RESOURCE)
- type = Attendees.TYPE_RESOURCE;
- else {
- Role role = (Role)attendee.getParameter(Parameter.ROLE);
- int relationship;
- if (role == Role.CHAIR)
- relationship = Attendees.RELATIONSHIP_ORGANIZER;
- else {
- relationship = Attendees.RELATIONSHIP_ATTENDEE;
- if (role == Role.OPT_PARTICIPANT)
- type = Attendees.TYPE_OPTIONAL;
- else if (role == Role.REQ_PARTICIPANT)
- type = Attendees.TYPE_REQUIRED;
- }
- builder = builder.withValue(Attendees.ATTENDEE_RELATIONSHIP, relationship);
- }
-
- int status = Attendees.ATTENDEE_STATUS_NONE;
- PartStat partStat = (PartStat)attendee.getParameter(Parameter.PARTSTAT);
- if (partStat == null || partStat == PartStat.NEEDS_ACTION)
- status = Attendees.ATTENDEE_STATUS_INVITED;
- else if (partStat == PartStat.ACCEPTED)
- status = Attendees.ATTENDEE_STATUS_ACCEPTED;
- else if (partStat == PartStat.DECLINED)
- status = Attendees.ATTENDEE_STATUS_DECLINED;
- else if (partStat == PartStat.TENTATIVE)
- status = Attendees.ATTENDEE_STATUS_TENTATIVE;
-
- return builder
- .withValue(Attendees.ATTENDEE_EMAIL, email)
- .withValue(Attendees.ATTENDEE_TYPE, type)
- .withValue(Attendees.ATTENDEE_STATUS, status);
- }
-
- protected Builder buildReminder(Builder builder, VAlarm alarm) {
- int minutes = 0;
-
- Dur duration;
- if (alarm.getTrigger() != null && (duration = alarm.getTrigger().getDuration()) != null)
- minutes = duration.getDays() * 24*60 + duration.getHours()*60 + duration.getMinutes();
-
- Log.d(TAG, "Adding alarm " + minutes + " min before");
-
- return builder
- .withValue(Reminders.METHOD, Reminders.METHOD_ALERT)
- .withValue(Reminders.MINUTES, minutes);
- }
-
-
-
- /* private helper methods */
-
- protected static Uri calendarsURI(Account account) {
- return Calendars.CONTENT_URI.buildUpon().appendQueryParameter(Calendars.ACCOUNT_NAME, account.name)
- .appendQueryParameter(Calendars.ACCOUNT_TYPE, account.type)
- .appendQueryParameter(CalendarContract.CALLER_IS_SYNCADAPTER, "true").build();
- }
-
- protected Uri calendarsURI() {
- return calendarsURI(account);
- }
-
-}
diff --git a/src/at/bitfire/davdroid/resource/LocalCollection.java b/src/at/bitfire/davdroid/resource/LocalCollection.java
deleted file mode 100644
index 24293421..00000000
--- a/src/at/bitfire/davdroid/resource/LocalCollection.java
+++ /dev/null
@@ -1,361 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 Ricki Hirner (bitfire web engineering).
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the GNU Public License v3.0
- * which accompanies this distribution, and is available at
- * http://www.gnu.org/licenses/gpl.html
- ******************************************************************************/
-package at.bitfire.davdroid.resource;
-
-import java.util.ArrayList;
-
-import lombok.Cleanup;
-import android.accounts.Account;
-import android.content.ContentProviderClient;
-import android.content.ContentProviderOperation;
-import android.content.ContentProviderOperation.Builder;
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.content.OperationApplicationException;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.RemoteException;
-import android.provider.CalendarContract;
-import android.util.Log;
-
-/**
- * Represents a locally-stored synchronizable collection (for instance, the
- * address book or a calendar). Manages a CTag that stores the last known
- * remote CTag (the remote CTag changes whenever something in the remote collection changes).
- *
- * @param Subtype of Resource that can be stored in the collection
- */
-public abstract class LocalCollection {
- private static final String TAG = "davdroid.LocalCollection";
-
- protected Account account;
- protected ContentProviderClient providerClient;
- protected ArrayList pendingOperations = new ArrayList();
-
-
- // database fields
-
- /** base Uri of the collection's entries (for instance, Events.CONTENT_URI);
- * apply syncAdapterURI() before returning a value */
- abstract protected Uri entriesURI();
-
- /** column name of the type of the account the entry belongs to */
- abstract protected String entryColumnAccountType();
- /** column name of the name of the account the entry belongs to */
- abstract protected String entryColumnAccountName();
-
- /** column name of the collection ID the entry belongs to */
- abstract protected String entryColumnParentID();
- /** column name of an entry's ID */
- abstract protected String entryColumnID();
- /** column name of an entry's file name on the WebDAV server */
- abstract protected String entryColumnRemoteName();
- /** column name of an entry's last ETag on the WebDAV server; null if entry hasn't been uploaded yet */
- abstract protected String entryColumnETag();
-
- /** column name of an entry's "dirty" flag (managed by content provider) */
- abstract protected String entryColumnDirty();
- /** column name of an entry's "deleted" flag (managed by content provider) */
- abstract protected String entryColumnDeleted();
-
- /** column name of an entry's UID */
- abstract protected String entryColumnUID();
-
-
- LocalCollection(Account account, ContentProviderClient providerClient) {
- this.account = account;
- this.providerClient = providerClient;
- }
-
-
- // collection operations
-
- /** gets the ID if the collection (for instance, ID of the Android calendar) */
- abstract public long getId();
- /** gets the CTag of the collection */
- abstract public String getCTag() throws LocalStorageException;
- /** sets the CTag of the collection */
- abstract public void setCTag(String cTag) throws LocalStorageException;
-
-
- // content provider (= database) querying
-
- /**
- * Finds new resources (resources which haven't been uploaded yet).
- * New resources are 1) dirty, and 2) don't have an ETag yet.
- *
- * @return IDs of new resources
- * @throws LocalStorageException when the content provider couldn't be queried
- */
- public long[] findNew() throws LocalStorageException {
- String where = entryColumnDirty() + "=1 AND " + entryColumnETag() + " IS NULL";
- if (entryColumnParentID() != null)
- where += " AND " + entryColumnParentID() + "=" + String.valueOf(getId());
- try {
- @Cleanup Cursor cursor = providerClient.query(entriesURI(),
- new String[] { entryColumnID() },
- where, null, null);
- if (cursor == null)
- throw new LocalStorageException("Couldn't query new records");
-
- long[] fresh = new long[cursor.getCount()];
- for (int idx = 0; cursor.moveToNext(); idx++) {
- long id = cursor.getLong(0);
-
- // new record: generate UID + remote file name so that we can upload
- T resource = findById(id, false);
- resource.initialize();
- // write generated UID + remote file name into database
- ContentValues values = new ContentValues(2);
- values.put(entryColumnUID(), resource.getUid());
- values.put(entryColumnRemoteName(), resource.getName());
- providerClient.update(ContentUris.withAppendedId(entriesURI(), id), values, null, null);
-
- fresh[idx] = id;
- }
- return fresh;
- } catch(RemoteException ex) {
- throw new LocalStorageException(ex);
- }
- }
-
- /**
- * Finds updated resources (resources which have already been uploaded, but have changed locally).
- * Updated resources are 1) dirty, and 2) already have an ETag.
- *
- * @return IDs of updated resources
- * @throws LocalStorageException when the content provider couldn't be queried
- */
- public long[] findUpdated() throws LocalStorageException {
- String where = entryColumnDirty() + "=1 AND " + entryColumnETag() + " IS NOT NULL";
- if (entryColumnParentID() != null)
- where += " AND " + entryColumnParentID() + "=" + String.valueOf(getId());
- try {
- @Cleanup Cursor cursor = providerClient.query(entriesURI(),
- new String[] { entryColumnID(), entryColumnRemoteName(), entryColumnETag() },
- where, null, null);
- if (cursor == null)
- throw new LocalStorageException("Couldn't query updated records");
-
- long[] updated = new long[cursor.getCount()];
- for (int idx = 0; cursor.moveToNext(); idx++)
- updated[idx] = cursor.getLong(0);
- return updated;
- } catch(RemoteException ex) {
- throw new LocalStorageException(ex);
- }
- }
-
- /**
- * Finds deleted resources (resources which have been marked for deletion).
- * Deleted resources have the "deleted" flag set.
- *
- * @return IDs of deleted resources
- * @throws LocalStorageException when the content provider couldn't be queried
- */
- public long[] findDeleted() throws LocalStorageException {
- String where = entryColumnDeleted() + "=1";
- if (entryColumnParentID() != null)
- where += " AND " + entryColumnParentID() + "=" + String.valueOf(getId());
- try {
- @Cleanup Cursor cursor = providerClient.query(entriesURI(),
- new String[] { entryColumnID(), entryColumnRemoteName(), entryColumnETag() },
- where, null, null);
- if (cursor == null)
- throw new LocalStorageException("Couldn't query dirty records");
-
- long deleted[] = new long[cursor.getCount()];
- for (int idx = 0; cursor.moveToNext(); idx++)
- deleted[idx] = cursor.getLong(0);
- return deleted;
- } catch(RemoteException ex) {
- throw new LocalStorageException(ex);
- }
- }
-
- /**
- * Finds a specific resource by ID.
- * @param localID ID of the resource
- * @param populate true: populates all data fields (for instance, contact or event details);
- * false: only remote file name and ETag are populated
- * @return resource with either ID/remote file/name/ETag or all fields populated
- * @throws RecordNotFoundException when the resource couldn't be found
- * @throws LocalStorageException when the content provider couldn't be queried
- */
- public T findById(long localID, boolean populate) throws LocalStorageException {
- try {
- @Cleanup Cursor cursor = providerClient.query(ContentUris.withAppendedId(entriesURI(), localID),
- new String[] { entryColumnRemoteName(), entryColumnETag() }, null, null, null);
- if (cursor != null && cursor.moveToNext()) {
- T resource = newResource(localID, cursor.getString(0), cursor.getString(1));
- if (populate)
- populate(resource);
- return resource;
- } else
- throw new RecordNotFoundException();
- } catch(RemoteException ex) {
- throw new LocalStorageException(ex);
- }
- }
-
- /**
- * Finds a specific resource by remote file name.
- * @param localID remote file name of the resource
- * @param populate true: populates all data fields (for instance, contact or event details);
- * false: only remote file name and ETag are populated
- * @return resource with either ID/remote file/name/ETag or all fields populated
- * @throws RecordNotFoundException when the resource couldn't be found
- * @throws LocalStorageException when the content provider couldn't be queried
- */
- public T findByRemoteName(String remoteName, boolean populate) throws LocalStorageException {
- try {
- @Cleanup Cursor cursor = providerClient.query(entriesURI(),
- new String[] { entryColumnID(), entryColumnRemoteName(), entryColumnETag() },
- entryColumnRemoteName() + "=?", new String[] { remoteName }, null);
- if (cursor != null && cursor.moveToNext()) {
- T resource = newResource(cursor.getLong(0), cursor.getString(1), cursor.getString(2));
- if (populate)
- populate(resource);
- return resource;
- } else
- throw new RecordNotFoundException();
- } catch(RemoteException ex) {
- throw new LocalStorageException(ex);
- }
- }
-
- /** populates all data fields from the content provider */
- public abstract void populate(Resource record) throws LocalStorageException;
-
-
- // create/update/delete
-
- /**
- * Creates a new resource object in memory. No content provider operations involved.
- * @param localID the ID of the resource
- * @param resourceName the (remote) file name of the resource
- * @param ETag of the resource
- * @return the new resource object */
- abstract public T newResource(long localID, String resourceName, String eTag);
-
- /** Enqueues adding the resource (including all data) to the local collection. Requires commit(). */
- public void add(Resource resource) {
- int idx = pendingOperations.size();
- pendingOperations.add(
- buildEntry(ContentProviderOperation.newInsert(entriesURI()), resource)
- .withYieldAllowed(true)
- .build());
-
- addDataRows(resource, -1, idx);
- }
-
- /** Enqueues updating an existing resource in the local collection. The resource will be found by
- * the remote file name and all data will be updated. Requires commit(). */
- public void updateByRemoteName(Resource remoteResource) throws LocalStorageException {
- T localResource = findByRemoteName(remoteResource.getName(), false);
- pendingOperations.add(
- buildEntry(ContentProviderOperation.newUpdate(ContentUris.withAppendedId(entriesURI(), localResource.getLocalID())), remoteResource)
- .withValue(entryColumnETag(), remoteResource.getETag())
- .withYieldAllowed(true)
- .build());
-
- removeDataRows(localResource);
- addDataRows(remoteResource, localResource.getLocalID(), -1);
- }
-
- /** Enqueues deleting a resource from the local collection. Requires commit(). */
- public void delete(Resource resource) {
- pendingOperations.add(ContentProviderOperation
- .newDelete(ContentUris.withAppendedId(entriesURI(), resource.getLocalID()))
- .withYieldAllowed(true)
- .build());
- }
-
- /**
- * Enqueues deleting all resources except the give ones from the local collection. Requires commit().
- * @param remoteResources resources with these remote file names will be kept
- */
- public abstract void deleteAllExceptRemoteNames(Resource[] remoteResources);
-
- /** Updates the locally-known ETag of a resource. */
- public void updateETag(Resource res, String eTag) throws LocalStorageException {
- Log.d(TAG, "Setting ETag of local resource " + res + " to " + eTag);
-
- ContentValues values = new ContentValues(1);
- values.put(entryColumnETag(), eTag);
- try {
- providerClient.update(ContentUris.withAppendedId(entriesURI(), res.getLocalID()), values, null, new String[] {});
- } catch (RemoteException e) {
- throw new LocalStorageException(e);
- }
- }
-
- /** Enqueues removing the dirty flag from a locally-stored resource. Requires commit(). */
- public void clearDirty(Resource resource) {
- pendingOperations.add(ContentProviderOperation
- .newUpdate(ContentUris.withAppendedId(entriesURI(), resource.getLocalID()))
- .withValue(entryColumnDirty(), 0)
- .build());
- }
-
- /** Commits enqueued operations to the content provider (for batch operations). */
- public void commit() throws LocalStorageException {
- if (!pendingOperations.isEmpty())
- try {
- Log.d(TAG, "Committing " + pendingOperations.size() + " operations");
- providerClient.applyBatch(pendingOperations);
- pendingOperations.clear();
- } catch (RemoteException ex) {
- throw new LocalStorageException(ex);
- } catch(OperationApplicationException ex) {
- throw new LocalStorageException(ex);
- }
- }
-
-
- // helpers
-
- protected void queueOperation(Builder builder) {
- if (builder != null)
- pendingOperations.add(builder.build());
- }
-
- /** Appends account type, name and CALLER_IS_SYNCADAPTER to an Uri. */
- protected Uri syncAdapterURI(Uri baseURI) {
- return baseURI.buildUpon()
- .appendQueryParameter(entryColumnAccountType(), account.type)
- .appendQueryParameter(entryColumnAccountName(), account.name)
- .appendQueryParameter(CalendarContract.CALLER_IS_SYNCADAPTER, "true")
- .build();
- }
-
- protected Builder newDataInsertBuilder(Uri dataUri, String refFieldName, long raw_ref_id, Integer backrefIdx) {
- Builder builder = ContentProviderOperation.newInsert(syncAdapterURI(dataUri));
- if (backrefIdx != -1)
- return builder.withValueBackReference(refFieldName, backrefIdx);
- else
- return builder.withValue(refFieldName, raw_ref_id);
- }
-
-
- // content builders
-
- /**
- * Builds the main entry (for instance, a ContactsContract.RawContacts row) from a resource.
- * The entry is built for insertion to the location identified by entriesURI().
- *
- * @param builder Builder to be extended by all resource data that can be stored without extra data rows.
- */
- protected abstract Builder buildEntry(Builder builder, Resource resource);
-
- /** Enqueues adding extra data rows of the resource to the local collection. */
- protected abstract void addDataRows(Resource resource, long localID, int backrefIdx);
-
- /** Enqueues removing all extra data rows of the resource from the local collection. */
- protected abstract void removeDataRows(Resource resource);
-}
diff --git a/src/at/bitfire/davdroid/resource/LocalStorageException.java b/src/at/bitfire/davdroid/resource/LocalStorageException.java
deleted file mode 100644
index d33ad46a..00000000
--- a/src/at/bitfire/davdroid/resource/LocalStorageException.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 Ricki Hirner (bitfire web engineering).
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the GNU Public License v3.0
- * which accompanies this distribution, and is available at
- * http://www.gnu.org/licenses/gpl.html
- ******************************************************************************/
-package at.bitfire.davdroid.resource;
-
-public class LocalStorageException extends Exception {
- private static final long serialVersionUID = -7787658815291629529L;
-
- private static final String detailMessage = "Couldn't access local content provider";
-
-
- public LocalStorageException(String detailMessage, Throwable throwable) {
- super(detailMessage, throwable);
- }
-
- public LocalStorageException(String detailMessage) {
- super(detailMessage);
- }
-
- public LocalStorageException(Throwable throwable) {
- super(detailMessage, throwable);
- }
-
- public LocalStorageException() {
- super(detailMessage);
- }
-}
diff --git a/src/at/bitfire/davdroid/resource/RecordNotFoundException.java b/src/at/bitfire/davdroid/resource/RecordNotFoundException.java
deleted file mode 100644
index 98daa77c..00000000
--- a/src/at/bitfire/davdroid/resource/RecordNotFoundException.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 Ricki Hirner (bitfire web engineering).
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the GNU Public License v3.0
- * which accompanies this distribution, and is available at
- * http://www.gnu.org/licenses/gpl.html
- ******************************************************************************/
-package at.bitfire.davdroid.resource;
-
-/**
- * Thrown when a local record (for instance, Contact with ID 12345) should be read
- * but could not be found.
- */
-public class RecordNotFoundException extends LocalStorageException {
- private static final long serialVersionUID = 4961024282198632578L;
-
- private static final String detailMessage = "Record not found in local content provider";
-
-
- RecordNotFoundException(Throwable ex) {
- super(detailMessage, ex);
- }
-
- RecordNotFoundException() {
- super(detailMessage);
- }
-
-}
diff --git a/src/at/bitfire/davdroid/resource/RemoteCollection.java b/src/at/bitfire/davdroid/resource/RemoteCollection.java
deleted file mode 100644
index d287e823..00000000
--- a/src/at/bitfire/davdroid/resource/RemoteCollection.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 Ricki Hirner (bitfire web engineering).
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the GNU Public License v3.0
- * which accompanies this distribution, and is available at
- * http://www.gnu.org/licenses/gpl.html
- ******************************************************************************/
-package at.bitfire.davdroid.resource;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.MalformedURLException;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.util.LinkedList;
-import java.util.List;
-
-import lombok.Cleanup;
-import lombok.Getter;
-import net.fortuna.ical4j.model.ValidationException;
-import android.util.Log;
-import at.bitfire.davdroid.webdav.DavException;
-import at.bitfire.davdroid.webdav.DavMultiget;
-import at.bitfire.davdroid.webdav.DavNoContentException;
-import at.bitfire.davdroid.webdav.HttpException;
-import at.bitfire.davdroid.webdav.HttpPropfind;
-import at.bitfire.davdroid.webdav.WebDavResource;
-import at.bitfire.davdroid.webdav.WebDavResource.PutMode;
-import ch.boye.httpclientandroidlib.impl.client.CloseableHttpClient;
-import ezvcard.io.text.VCardParseException;
-
-/**
- * Represents a remotely stored synchronizable collection (collection as in
- * WebDAV terminology).
- *
- * @param Subtype of Resource that can be stored in the collection
- */
-public abstract class RemoteCollection {
- private static final String TAG = "davdroid.RemoteCollection";
-
- CloseableHttpClient httpClient;
- @Getter WebDavResource collection;
-
- abstract protected String memberContentType();
- abstract protected DavMultiget.Type multiGetType();
- abstract protected T newResourceSkeleton(String name, String ETag);
-
- public RemoteCollection(CloseableHttpClient httpClient, String baseURL, String user, String password, boolean preemptiveAuth) throws MalformedURLException {
- this.httpClient = httpClient;
-
- collection = new WebDavResource(httpClient, new URL(baseURL), user, password, preemptiveAuth);
- }
-
-
- /* collection operations */
-
- public String getCTag() throws URISyntaxException, IOException, HttpException {
- try {
- if (collection.getCTag() == null && collection.getMembers() == null) // not already fetched
- collection.propfind(HttpPropfind.Mode.COLLECTION_CTAG);
- } catch (DavException e) {
- return null;
- }
- return collection.getCTag();
- }
-
- public Resource[] getMemberETags() throws URISyntaxException, IOException, DavException, HttpException {
- collection.propfind(HttpPropfind.Mode.MEMBERS_ETAG);
-
- List resources = new LinkedList();
- if (collection.getMembers() != null) {
- for (WebDavResource member : collection.getMembers())
- resources.add(newResourceSkeleton(member.getName(), member.getETag()));
- }
- return resources.toArray(new Resource[0]);
- }
-
- @SuppressWarnings("unchecked")
- public Resource[] multiGet(Resource[] resources) throws URISyntaxException, IOException, DavException, HttpException {
- try {
- if (resources.length == 1)
- return (T[]) new Resource[] { get(resources[0]) };
-
- Log.i(TAG, "Multi-getting " + resources.length + " remote resource(s)");
-
- LinkedList names = new LinkedList();
- for (Resource resource : resources)
- names.add(resource.getName());
-
- LinkedList foundResources = new LinkedList();
- collection.multiGet(multiGetType(), names.toArray(new String[0]));
- if (collection.getMembers() == null)
- throw new DavNoContentException();
-
- for (WebDavResource member : collection.getMembers()) {
- T resource = newResourceSkeleton(member.getName(), member.getETag());
- try {
- if (member.getContent() != null) {
- @Cleanup InputStream is = new ByteArrayInputStream(member.getContent());
- resource.parseEntity(is);
- foundResources.add(resource);
- } else
- Log.e(TAG, "Ignoring entity without content");
- } catch (InvalidResourceException e) {
- Log.e(TAG, "Ignoring unparseable entity in multi-response", e);
- }
- }
-
- return foundResources.toArray(new Resource[0]);
- } catch (InvalidResourceException e) {
- Log.e(TAG, "Couldn't parse entity from GET", e);
- }
-
- return new Resource[0];
- }
-
-
- /* internal member operations */
-
- public Resource get(Resource resource) throws URISyntaxException, IOException, HttpException, DavException, InvalidResourceException {
- WebDavResource member = new WebDavResource(collection, resource.getName());
-
- if (resource instanceof Contact)
- member.get(Contact.MIME_TYPE);
- else if (resource instanceof Event)
- member.get(Event.MIME_TYPE);
- else {
- Log.wtf(TAG, "Should fetch something, but neither contact nor calendar");
- throw new InvalidResourceException("Didn't now which MIME type to accept");
- }
-
- byte[] data = member.getContent();
- if (data == null)
- throw new DavNoContentException();
-
- @Cleanup InputStream is = new ByteArrayInputStream(data);
- try {
- resource.parseEntity(is);
- } catch(VCardParseException e) {
- throw new InvalidResourceException(e);
- }
- return resource;
- }
-
- // returns ETag of the created resource, if returned by server
- public String add(Resource res) throws URISyntaxException, IOException, HttpException, ValidationException {
- WebDavResource member = new WebDavResource(collection, res.getName(), res.getETag());
- member.setContentType(memberContentType());
-
- @Cleanup ByteArrayOutputStream os = res.toEntity();
- String eTag = member.put(os.toByteArray(), PutMode.ADD_DONT_OVERWRITE);
-
- // after a successful upload, the collection has implicitely changed, too
- collection.invalidateCTag();
-
- return eTag;
- }
-
- public void delete(Resource res) throws URISyntaxException, IOException, HttpException {
- WebDavResource member = new WebDavResource(collection, res.getName(), res.getETag());
- member.delete();
-
- collection.invalidateCTag();
- }
-
- // returns ETag of the updated resource, if returned by server
- public String update(Resource res) throws URISyntaxException, IOException, HttpException, ValidationException {
- WebDavResource member = new WebDavResource(collection, res.getName(), res.getETag());
- member.setContentType(memberContentType());
-
- @Cleanup ByteArrayOutputStream os = res.toEntity();
- String eTag = member.put(os.toByteArray(), PutMode.UPDATE_DONT_OVERWRITE);
-
- // after a successful upload, the collection has implicitely changed, too
- collection.invalidateCTag();
-
- return eTag;
- }
-}
diff --git a/src/at/bitfire/davdroid/resource/Resource.java b/src/at/bitfire/davdroid/resource/Resource.java
deleted file mode 100644
index bda3e2e5..00000000
--- a/src/at/bitfire/davdroid/resource/Resource.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 Ricki Hirner (bitfire web engineering).
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the GNU Public License v3.0
- * which accompanies this distribution, and is available at
- * http://www.gnu.org/licenses/gpl.html
- ******************************************************************************/
-package at.bitfire.davdroid.resource;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-
-import lombok.Getter;
-import lombok.Setter;
-import lombok.ToString;
-
-/**
- * Represents a resource that can be contained in a LocalCollection or RemoteCollection
- * for synchronization by WebDAV.
- */
-@ToString
-public abstract class Resource {
- @Getter @Setter protected String name, ETag;
- @Getter @Setter protected String uid;
- @Getter protected long localID;
-
-
- public Resource(String name, String ETag) {
- this.name = name;
- this.ETag = ETag;
- }
-
- public Resource(long localID, String name, String ETag) {
- this(name, ETag);
- this.localID = localID;
- }
-
- /** initializes UID and remote file name (required for first upload) */
- public abstract void initialize();
-
- /** fills the resource data from an input stream (for instance, .vcf file for Contact) */
- public abstract void parseEntity(InputStream entity) throws IOException, InvalidResourceException;
- /** writes the resource data to an output stream (for instance, .vcf file for Contact) */
- public abstract ByteArrayOutputStream toEntity() throws IOException;
-}
diff --git a/src/at/bitfire/davdroid/resource/ServerInfo.java b/src/at/bitfire/davdroid/resource/ServerInfo.java
deleted file mode 100644
index 14f5b6f7..00000000
--- a/src/at/bitfire/davdroid/resource/ServerInfo.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 Ricki Hirner (bitfire web engineering).
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the GNU Public License v3.0
- * which accompanies this distribution, and is available at
- * http://www.gnu.org/licenses/gpl.html
- ******************************************************************************/
-package at.bitfire.davdroid.resource;
-
-import java.io.Serializable;
-import java.net.URI;
-import java.util.LinkedList;
-import java.util.List;
-
-import ezvcard.VCardVersion;
-import lombok.Data;
-import lombok.RequiredArgsConstructor;
-
-@RequiredArgsConstructor(suppressConstructorProperties=true)
-@Data
-public class ServerInfo implements Serializable {
- private static final long serialVersionUID = 6744847358282980437L;
-
- enum Scheme {
- HTTP, HTTPS, MAILTO
- }
-
- final private URI baseURI;
- final private String userName, password;
- final boolean authPreemptive;
-
- private String errorMessage;
-
- private boolean calDAV = false, cardDAV = false;
- private List
- addressBooks = new LinkedList(),
- calendars = new LinkedList();
-
-
- public boolean hasEnabledCalendars() {
- for (ResourceInfo calendar : calendars)
- if (calendar.enabled)
- return true;
- return false;
- }
-
-
- @RequiredArgsConstructor(suppressConstructorProperties=true)
- @Data
- public static class ResourceInfo implements Serializable {
- private static final long serialVersionUID = -5516934508229552112L;
-
- public enum Type {
- ADDRESS_BOOK,
- CALENDAR
- }
-
- boolean enabled = false;
-
- final Type type;
- final boolean readOnly;
- final String URL, title, description, color;
-
- VCardVersion vCardVersion;
-
- String timezone;
- }
-}
diff --git a/src/at/bitfire/davdroid/syncadapter/AccountAuthenticatorService.java b/src/at/bitfire/davdroid/syncadapter/AccountAuthenticatorService.java
deleted file mode 100644
index f78cc4f6..00000000
--- a/src/at/bitfire/davdroid/syncadapter/AccountAuthenticatorService.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 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.syncadapter;
-
-import android.accounts.AbstractAccountAuthenticator;
-import android.accounts.Account;
-import android.accounts.AccountAuthenticatorResponse;
-import android.accounts.AccountManager;
-import android.accounts.NetworkErrorException;
-import android.app.Service;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.IBinder;
-
-public class AccountAuthenticatorService extends Service {
- private static AccountAuthenticator accountAuthenticator;
-
- private AccountAuthenticator getAuthenticator() {
- if (accountAuthenticator != null)
- return accountAuthenticator;
- return accountAuthenticator = new AccountAuthenticator(this);
- }
-
- @Override
- public IBinder onBind(Intent intent) {
- if (intent.getAction().equals(android.accounts.AccountManager.ACTION_AUTHENTICATOR_INTENT))
- return getAuthenticator().getIBinder();
- return null;
- }
-
-
- private static class AccountAuthenticator extends AbstractAccountAuthenticator {
- Context context;
-
- public AccountAuthenticator(Context context) {
- super(context);
- this.context = context;
- }
-
- @Override
- public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType,
- String[] requiredFeatures, Bundle options) throws NetworkErrorException {
- Intent intent = new Intent(context, AddAccountActivity.class);
- intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
- Bundle bundle = new Bundle();
- bundle.putParcelable(AccountManager.KEY_INTENT, intent);
- return bundle;
- }
-
- @Override
- public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account, Bundle options) throws NetworkErrorException {
- return null;
- }
-
- @Override
- public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) {
- return null;
- }
-
- @Override
- public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
- return null;
- }
-
- @Override
- public String getAuthTokenLabel(String authTokenType) {
- return null;
- }
-
- @Override
- public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features) throws NetworkErrorException {
- return null;
- }
-
- @Override
- public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
- return null;
- }
- }
-}
diff --git a/src/at/bitfire/davdroid/syncadapter/AccountDetailsFragment.java b/src/at/bitfire/davdroid/syncadapter/AccountDetailsFragment.java
deleted file mode 100644
index 37ab4734..00000000
--- a/src/at/bitfire/davdroid/syncadapter/AccountDetailsFragment.java
+++ /dev/null
@@ -1,147 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 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.syncadapter;
-
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.app.Fragment;
-import android.content.ContentResolver;
-import android.os.Bundle;
-import android.provider.CalendarContract;
-import android.provider.ContactsContract;
-import android.text.Editable;
-import android.text.TextWatcher;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.EditText;
-import android.widget.TextView;
-import android.widget.Toast;
-import at.bitfire.davdroid.Constants;
-import at.bitfire.davdroid.R;
-import at.bitfire.davdroid.resource.LocalCalendar;
-import at.bitfire.davdroid.resource.LocalStorageException;
-import at.bitfire.davdroid.resource.ServerInfo;
-
-public class AccountDetailsFragment extends Fragment implements TextWatcher {
- public static final String KEY_SERVER_INFO = "server_info";
-
- ServerInfo serverInfo;
-
- EditText editAccountName;
-
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- View v = inflater.inflate(R.layout.account_details, container, false);
-
- serverInfo = (ServerInfo)getArguments().getSerializable(KEY_SERVER_INFO);
-
- editAccountName = (EditText)v.findViewById(R.id.account_name);
- editAccountName.addTextChangedListener(this);
- editAccountName.setText(serverInfo.getUserName());
-
- TextView textAccountNameInfo = (TextView)v.findViewById(R.id.account_name_info);
- if (!serverInfo.hasEnabledCalendars())
- textAccountNameInfo.setVisibility(View.GONE);
-
- setHasOptionsMenu(true);
- return v;
- }
-
-
- @Override
- public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
- inflater.inflate(R.menu.account_details, menu);
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.add_account:
- addAccount();
- break;
- default:
- return false;
- }
- return true;
- }
-
-
- // actions
-
- void addAccount() {
- ServerInfo serverInfo = (ServerInfo)getArguments().getSerializable(KEY_SERVER_INFO);
- String accountName = editAccountName.getText().toString();
-
- AccountManager accountManager = AccountManager.get(getActivity());
- Account account = new Account(accountName, Constants.ACCOUNT_TYPE);
- Bundle userData = AccountSettings.createBundle(serverInfo);
-
- boolean syncContacts = false;
- for (ServerInfo.ResourceInfo addressBook : serverInfo.getAddressBooks())
- if (addressBook.isEnabled()) {
- ContentResolver.setIsSyncable(account, ContactsContract.AUTHORITY, 1);
- syncContacts = true;
- continue;
- }
- if (syncContacts) {
- ContentResolver.setIsSyncable(account, ContactsContract.AUTHORITY, 1);
- ContentResolver.setSyncAutomatically(account, ContactsContract.AUTHORITY, true);
- } else
- ContentResolver.setIsSyncable(account, ContactsContract.AUTHORITY, 0);
-
- if (accountManager.addAccountExplicitly(account, serverInfo.getPassword(), userData)) {
- // account created, now create calendars
- boolean syncCalendars = false;
- for (ServerInfo.ResourceInfo calendar : serverInfo.getCalendars())
- if (calendar.isEnabled())
- try {
- LocalCalendar.create(account, getActivity().getContentResolver(), calendar);
- syncCalendars = true;
- } catch (LocalStorageException e) {
- Toast.makeText(getActivity(), "Couldn't create calendar(s): " + e.getMessage(), Toast.LENGTH_LONG).show();
- }
- if (syncCalendars) {
- ContentResolver.setIsSyncable(account, CalendarContract.AUTHORITY, 1);
- ContentResolver.setSyncAutomatically(account, CalendarContract.AUTHORITY, true);
- } else
- ContentResolver.setIsSyncable(account, CalendarContract.AUTHORITY, 0);
-
- getActivity().finish();
- } else
- Toast.makeText(getActivity(), "Couldn't create account (account with this name already existing?)", Toast.LENGTH_LONG).show();
- }
-
-
- // input validation
-
- @Override
- public void onPrepareOptionsMenu(Menu menu) {
- boolean ok = false;
- ok = editAccountName.getText().length() > 0;
- MenuItem item = menu.findItem(R.id.add_account);
- item.setEnabled(ok);
- }
-
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- }
-
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- getActivity().invalidateOptionsMenu();
- }
-
- @Override
- public void afterTextChanged(Editable s) {
- }
-}
diff --git a/src/at/bitfire/davdroid/syncadapter/AccountSettings.java b/src/at/bitfire/davdroid/syncadapter/AccountSettings.java
deleted file mode 100644
index a58fe407..00000000
--- a/src/at/bitfire/davdroid/syncadapter/AccountSettings.java
+++ /dev/null
@@ -1,183 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 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.syncadapter;
-
-import java.net.URI;
-import java.net.URISyntaxException;
-
-import lombok.Cleanup;
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.content.Context;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Bundle;
-import android.provider.CalendarContract;
-import android.provider.CalendarContract.Calendars;
-import android.util.Log;
-import at.bitfire.davdroid.resource.ServerInfo;
-import ezvcard.VCardVersion;
-
-public class AccountSettings {
- private final static String TAG = "davdroid.AccountSettings";
-
- private final static int CURRENT_VERSION = 1;
- private final static String
- KEY_SETTINGS_VERSION = "version",
-
- KEY_USERNAME = "user_name",
- KEY_AUTH_PREEMPTIVE = "auth_preemptive",
-
- KEY_ADDRESSBOOK_URL = "addressbook_url",
- KEY_ADDRESSBOOK_CTAG = "addressbook_ctag",
- KEY_ADDRESSBOOK_VCARD_VERSION = "addressbook_vcard_version";
-
- Context context;
- AccountManager accountManager;
- Account account;
-
-
- public AccountSettings(Context context, Account account) {
- this.context = context;
- this.account = account;
-
- accountManager = AccountManager.get(context);
-
- synchronized(AccountSettings.class) {
- int version = 0;
- try {
- version = Integer.parseInt(accountManager.getUserData(account, KEY_SETTINGS_VERSION));
- } catch(NumberFormatException e) {
- }
- if (version < CURRENT_VERSION)
- update(version);
- }
- }
-
-
- public static Bundle createBundle(ServerInfo serverInfo) {
- Bundle bundle = new Bundle();
- bundle.putString(KEY_SETTINGS_VERSION, String.valueOf(CURRENT_VERSION));
- bundle.putString(KEY_USERNAME, serverInfo.getUserName());
- bundle.putString(KEY_AUTH_PREEMPTIVE, Boolean.toString(serverInfo.isAuthPreemptive()));
- for (ServerInfo.ResourceInfo addressBook : serverInfo.getAddressBooks())
- if (addressBook.isEnabled()) {
- bundle.putString(KEY_ADDRESSBOOK_URL, addressBook.getURL());
- bundle.putString(KEY_ADDRESSBOOK_VCARD_VERSION, addressBook.getVCardVersion().getVersion());
- continue;
- }
- return bundle;
- }
-
-
- // general settings
-
- public String getUserName() {
- return accountManager.getUserData(account, KEY_USERNAME);
- }
-
- public String getPassword() {
- return accountManager.getPassword(account);
- }
-
- public boolean getPreemptiveAuth() {
- return Boolean.parseBoolean(accountManager.getUserData(account, KEY_AUTH_PREEMPTIVE));
- }
-
-
- // address book (CardDAV) settings
-
- public String getAddressBookURL() {
- return accountManager.getUserData(account, KEY_ADDRESSBOOK_URL);
- }
-
- public String getAddressBookCTag() {
- return accountManager.getUserData(account, KEY_ADDRESSBOOK_CTAG);
- }
-
- public void setAddressBookCTag(String cTag) {
- accountManager.setUserData(account, KEY_ADDRESSBOOK_CTAG, cTag);
- }
-
- public VCardVersion getAddressBookVCardVersion() {
- VCardVersion version = VCardVersion.V3_0;
- String versionStr = accountManager.getUserData(account, KEY_ADDRESSBOOK_VCARD_VERSION);
- if (versionStr != null)
- version = VCardVersion.valueOfByStr(versionStr);
- return version;
- }
-
-
- // update from previous account settings
-
- private void update(int fromVersion) {
- Log.i(TAG, "Account settings must be updated from v" + fromVersion + " to v" + CURRENT_VERSION);
- for (int toVersion = CURRENT_VERSION; toVersion > fromVersion; toVersion--)
- update(fromVersion, toVersion);
- }
-
- private void update(int fromVersion, int toVersion) {
- Log.i(TAG, "Updating account settings from v" + fromVersion + " to " + toVersion);
- try {
- if (fromVersion == 0 && toVersion == 1)
- update_0_1();
- else
- Log.wtf(TAG, "Don't know how to update settings from v" + fromVersion + " to v" + toVersion);
- } catch(Exception e) {
- Log.e(TAG, "Couldn't update account settings (DAVdroid will probably crash)!", e);
- }
- }
-
- private void update_0_1() throws URISyntaxException {
- String v0_principalURL = accountManager.getUserData(account, "principal_url"),
- v0_addressBookPath = accountManager.getUserData(account, "addressbook_path");
- Log.d(TAG, "Old principal URL = " + v0_principalURL);
- Log.d(TAG, "Old address book path = " + v0_addressBookPath);
-
- URI principalURI = new URI(v0_principalURL);
-
- // update address book
- if (v0_addressBookPath != null) {
- String addressBookURL = principalURI.resolve(v0_addressBookPath).toASCIIString();
- Log.d(TAG, "New address book URL = " + addressBookURL);
- accountManager.setUserData(account, "addressbook_url", addressBookURL);
- }
-
- // update calendars
- ContentResolver resolver = context.getContentResolver();
- Uri calendars = Calendars.CONTENT_URI.buildUpon()
- .appendQueryParameter(Calendars.ACCOUNT_NAME, account.name)
- .appendQueryParameter(Calendars.ACCOUNT_TYPE, account.type)
- .appendQueryParameter(CalendarContract.CALLER_IS_SYNCADAPTER, "true").build();
- @Cleanup Cursor cursor = resolver.query(calendars, new String[] { Calendars._ID, Calendars.NAME }, null, null, null);
- while (cursor != null && cursor.moveToNext()) {
- int id = cursor.getInt(0);
- String v0_path = cursor.getString(1),
- v1_url = principalURI.resolve(v0_path).toASCIIString();
- Log.d(TAG, "Updating calendar #" + id + " name: " + v0_path + " -> " + v1_url);
- Uri calendar = ContentUris.appendId(Calendars.CONTENT_URI.buildUpon()
- .appendQueryParameter(Calendars.ACCOUNT_NAME, account.name)
- .appendQueryParameter(Calendars.ACCOUNT_TYPE, account.type)
- .appendQueryParameter(CalendarContract.CALLER_IS_SYNCADAPTER, "true"), id).build();
- ContentValues newValues = new ContentValues(1);
- newValues.put(Calendars.NAME, v1_url);
- if (resolver.update(calendar, newValues, null, null) != 1)
- Log.e(TAG, "Number of modified calendars != 1");
- }
-
- Log.d(TAG, "Cleaning old principal URL and address book path");
- accountManager.setUserData(account, "principal_url", null);
- accountManager.setUserData(account, "addressbook_path", null);
-
- Log.d(TAG, "Updated settings successfully!");
- accountManager.setUserData(account, KEY_SETTINGS_VERSION, "1");
- }
-}
diff --git a/src/at/bitfire/davdroid/syncadapter/AddAccountActivity.java b/src/at/bitfire/davdroid/syncadapter/AddAccountActivity.java
deleted file mode 100644
index f65f42f9..00000000
--- a/src/at/bitfire/davdroid/syncadapter/AddAccountActivity.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 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.syncadapter;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import at.bitfire.davdroid.Constants;
-import at.bitfire.davdroid.R;
-
-public class AddAccountActivity extends Activity {
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- setContentView(R.layout.add_account);
-
- if (savedInstanceState == null) { // first call
- getFragmentManager().beginTransaction()
- .add(R.id.fragment_container, new LoginTypeFragment(), "login_type")
- .commit();
- }
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- MenuInflater inflater = getMenuInflater();
- inflater.inflate(R.menu.add_account, menu);
- return true;
- }
-
- public void showHelp(MenuItem item) {
- startActivityForResult(new Intent(Intent.ACTION_VIEW, Uri.parse(Constants.WEB_URL_HELP)), 0);
- }
-
-}
diff --git a/src/at/bitfire/davdroid/syncadapter/CalendarsSyncAdapterService.java b/src/at/bitfire/davdroid/syncadapter/CalendarsSyncAdapterService.java
deleted file mode 100644
index f00678cd..00000000
--- a/src/at/bitfire/davdroid/syncadapter/CalendarsSyncAdapterService.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 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.syncadapter;
-
-import java.net.MalformedURLException;
-import java.util.HashMap;
-import java.util.Map;
-
-import android.accounts.Account;
-import android.app.Service;
-import android.content.ContentProviderClient;
-import android.content.Context;
-import android.content.Intent;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Log;
-import at.bitfire.davdroid.resource.CalDavCalendar;
-import at.bitfire.davdroid.resource.LocalCalendar;
-import at.bitfire.davdroid.resource.LocalCollection;
-import at.bitfire.davdroid.resource.RemoteCollection;
-
-public class CalendarsSyncAdapterService extends Service {
- private static SyncAdapter syncAdapter;
-
-
- @Override
- public void onCreate() {
- if (syncAdapter == null)
- syncAdapter = new SyncAdapter(getApplicationContext());
- }
-
- @Override
- public void onDestroy() {
- syncAdapter.close();
- syncAdapter = null;
- }
-
- @Override
- public IBinder onBind(Intent intent) {
- return syncAdapter.getSyncAdapterBinder();
- }
-
-
- private static class SyncAdapter extends DavSyncAdapter {
- private final static String TAG = "davdroid.CalendarsSyncAdapter";
-
-
- private SyncAdapter(Context context) {
- super(context);
- }
-
- @Override
- protected Map, RemoteCollection>> getSyncPairs(Account account, ContentProviderClient provider) {
- AccountSettings settings = new AccountSettings(getContext(), account);
- String userName = settings.getUserName(),
- password = settings.getPassword();
- boolean preemptive = settings.getPreemptiveAuth();
-
- try {
- Map, RemoteCollection>> map = new HashMap, RemoteCollection>>();
-
- for (LocalCalendar calendar : LocalCalendar.findAll(account, provider)) {
- RemoteCollection> dav = new CalDavCalendar(httpClient, calendar.getUrl(), userName, password, preemptive);
- map.put(calendar, dav);
- }
- return map;
- } catch (RemoteException ex) {
- Log.e(TAG, "Couldn't find local calendars", ex);
- } catch (MalformedURLException ex) {
- Log.e(TAG, "Couldn't build calendar URI", ex);
- }
-
- return null;
- }
- }
-}
diff --git a/src/at/bitfire/davdroid/syncadapter/ContactsSyncAdapterService.java b/src/at/bitfire/davdroid/syncadapter/ContactsSyncAdapterService.java
deleted file mode 100644
index 88328700..00000000
--- a/src/at/bitfire/davdroid/syncadapter/ContactsSyncAdapterService.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 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.syncadapter;
-
-import java.net.MalformedURLException;
-import java.util.HashMap;
-import java.util.Map;
-
-import android.accounts.Account;
-import android.app.Service;
-import android.content.ContentProviderClient;
-import android.content.Context;
-import android.content.Intent;
-import android.os.IBinder;
-import android.util.Log;
-import at.bitfire.davdroid.resource.CardDavAddressBook;
-import at.bitfire.davdroid.resource.LocalAddressBook;
-import at.bitfire.davdroid.resource.LocalCollection;
-import at.bitfire.davdroid.resource.RemoteCollection;
-
-public class ContactsSyncAdapterService extends Service {
- private static ContactsSyncAdapter syncAdapter;
-
-
- @Override
- public void onCreate() {
- if (syncAdapter == null)
- syncAdapter = new ContactsSyncAdapter(getApplicationContext());
- }
-
- @Override
- public void onDestroy() {
- syncAdapter.close();
- syncAdapter = null;
- }
-
- @Override
- public IBinder onBind(Intent intent) {
- return syncAdapter.getSyncAdapterBinder();
- }
-
-
- private static class ContactsSyncAdapter extends DavSyncAdapter {
- private final static String TAG = "davdroid.ContactsSyncAdapter";
-
-
- private ContactsSyncAdapter(Context context) {
- super(context);
- }
-
- @Override
- protected Map, RemoteCollection>> getSyncPairs(Account account, ContentProviderClient provider) {
- AccountSettings settings = new AccountSettings(getContext(), account);
- String userName = settings.getUserName(),
- password = settings.getPassword();
- boolean preemptive = settings.getPreemptiveAuth();
-
- String addressBookURL = settings.getAddressBookURL();
- if (addressBookURL == null)
- return null;
-
- try {
- LocalCollection> database = new LocalAddressBook(account, provider, settings);
- RemoteCollection> dav = new CardDavAddressBook(httpClient, addressBookURL, userName, password, preemptive);
-
- Map, RemoteCollection>> map = new HashMap, RemoteCollection>>();
- map.put(database, dav);
-
- return map;
- } catch (MalformedURLException ex) {
- Log.e(TAG, "Couldn't build address book URI", ex);
- }
-
- return null;
- }
- }
-}
diff --git a/src/at/bitfire/davdroid/syncadapter/DavSyncAdapter.java b/src/at/bitfire/davdroid/syncadapter/DavSyncAdapter.java
deleted file mode 100644
index 71189f4e..00000000
--- a/src/at/bitfire/davdroid/syncadapter/DavSyncAdapter.java
+++ /dev/null
@@ -1,166 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 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.syncadapter;
-
-import java.io.Closeable;
-import java.io.IOException;
-import java.net.URISyntaxException;
-import java.util.Map;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-
-import lombok.Getter;
-
-import org.apache.http.HttpStatus;
-
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.content.AbstractThreadedSyncAdapter;
-import android.content.ContentProviderClient;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.content.SyncResult;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.preference.PreferenceManager;
-import android.provider.Settings;
-import android.util.Log;
-import at.bitfire.davdroid.Constants;
-import at.bitfire.davdroid.resource.LocalCollection;
-import at.bitfire.davdroid.resource.LocalStorageException;
-import at.bitfire.davdroid.resource.RemoteCollection;
-import at.bitfire.davdroid.webdav.DavException;
-import at.bitfire.davdroid.webdav.DavHttpClient;
-import at.bitfire.davdroid.webdav.HttpException;
-import ch.boye.httpclientandroidlib.impl.client.CloseableHttpClient;
-
-public abstract class DavSyncAdapter extends AbstractThreadedSyncAdapter implements Closeable {
- private final static String TAG = "davdroid.DavSyncAdapter";
-
- @Getter private static String androidID;
-
- protected AccountManager accountManager;
-
- /* We use one static httpClient for
- * - all sync adapters (CalendarsSyncAdapter, ContactsSyncAdapter)
- * - and all threads (= accounts) of each sync adapter
- * so that HttpClient's threaded pool management can do its best.
- */
- protected static CloseableHttpClient httpClient;
-
- /* One static read/write lock pair for the static httpClient:
- * Use the READ lock when httpClient will only be called (to prevent it from being unset while being used).
- * Use the WRITE lock when httpClient will be modified (set/unset). */
- private final static ReentrantReadWriteLock httpClientLock = new ReentrantReadWriteLock();
-
-
- public DavSyncAdapter(Context context) {
- super(context, true);
-
- synchronized(this) {
- if (androidID == null)
- androidID = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
- }
-
- accountManager = AccountManager.get(context);
- }
-
- @Override
- public void close() {
- Log.d(TAG, "Closing httpClient");
-
- // may be called from a GUI thread, so we need an AsyncTask
- new AsyncTask() {
- @Override
- protected Void doInBackground(Void... params) {
- try {
- httpClientLock.writeLock().lock();
- if (httpClient != null) {
- httpClient.close();
- httpClient = null;
- }
- httpClientLock.writeLock().unlock();
- } catch (IOException e) {
- Log.w(TAG, "Couldn't close HTTP client", e);
- }
- return null;
- }
- }.execute();
- }
-
- protected abstract Map, RemoteCollection>> getSyncPairs(Account account, ContentProviderClient provider);
-
-
- @Override
- public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) {
- Log.i(TAG, "Performing sync for authority " + authority);
-
- // set class loader for iCal4j ResourceLoader
- Thread.currentThread().setContextClassLoader(getContext().getClassLoader());
-
- // create httpClient, if necessary
- httpClientLock.writeLock().lock();
- if (httpClient == null) {
- Log.d(TAG, "Creating new DavHttpClient");
- SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(getContext());
- httpClient = DavHttpClient.create(
- settings.getBoolean(Constants.SETTING_DISABLE_COMPRESSION, false),
- settings.getBoolean(Constants.SETTING_NETWORK_LOGGING, false)
- );
- }
-
- // prevent httpClient shutdown until we're ready by holding a read lock
- // acquiring read lock before releasing write lock will downgrade the write lock to a read lock
- httpClientLock.readLock().lock();
- httpClientLock.writeLock().unlock();
-
- // TODO use VCard 4.0 if possible
- AccountSettings accountSettings = new AccountSettings(getContext(), account);
- Log.d(TAG, "Server supports VCard version " + accountSettings.getAddressBookVCardVersion());
-
- try {
- // get local <-> remote collection pairs
- Map, RemoteCollection>> syncCollections = getSyncPairs(account, provider);
- if (syncCollections == null)
- Log.i(TAG, "Nothing to synchronize");
- else
- try {
- for (Map.Entry, RemoteCollection>> entry : syncCollections.entrySet())
- new SyncManager(entry.getKey(), entry.getValue()).synchronize(extras.containsKey(ContentResolver.SYNC_EXTRAS_MANUAL), syncResult);
- } catch (DavException ex) {
- syncResult.stats.numParseExceptions++;
- Log.e(TAG, "Invalid DAV response", ex);
- } catch (HttpException ex) {
- if (ex.getCode() == HttpStatus.SC_UNAUTHORIZED) {
- Log.e(TAG, "HTTP Unauthorized " + ex.getCode(), ex);
- syncResult.stats.numAuthExceptions++;
- } else if (ex.isClientError()) {
- Log.e(TAG, "Hard HTTP error " + ex.getCode(), ex);
- syncResult.stats.numParseExceptions++;
- } else {
- Log.w(TAG, "Soft HTTP error " + ex.getCode() + " (Android will try again later)", ex);
- syncResult.stats.numIoExceptions++;
- }
- } catch (LocalStorageException ex) {
- syncResult.databaseError = true;
- Log.e(TAG, "Local storage (content provider) exception", ex);
- } catch (IOException ex) {
- syncResult.stats.numIoExceptions++;
- Log.e(TAG, "I/O error (Android will try again later)", ex);
- } catch (URISyntaxException ex) {
- Log.e(TAG, "Invalid URI (file name) syntax", ex);
- }
- } finally {
- // allow httpClient shutdown
- httpClientLock.readLock().unlock();
- }
-
- Log.i(TAG, "Sync complete for " + authority);
- }
-
-}
diff --git a/src/at/bitfire/davdroid/syncadapter/GeneralSettingsActivity.java b/src/at/bitfire/davdroid/syncadapter/GeneralSettingsActivity.java
deleted file mode 100644
index 9135b428..00000000
--- a/src/at/bitfire/davdroid/syncadapter/GeneralSettingsActivity.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 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.syncadapter;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.preference.PreferenceFragment;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import at.bitfire.davdroid.R;
-
-public class GeneralSettingsActivity extends Activity {
- final static String URL_REPORT_ISSUE = "https://github.com/bitfireAT/davdroid/blob/master/CONTRIBUTING.md#reporting-issues";
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- getFragmentManager().beginTransaction()
- .replace(android.R.id.content, new GeneralSettingsFragment())
- .commit();
- }
-
- public void reportIssue(MenuItem item) {
- startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(URL_REPORT_ISSUE)));
- }
-
-
- public static class GeneralSettingsFragment extends PreferenceFragment {
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- getPreferenceManager().setSharedPreferencesMode(Context.MODE_MULTI_PROCESS);
- addPreferencesFromResource(R.xml.general_settings);
-
- setHasOptionsMenu(true);
- }
-
- @Override
- public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
- inflater.inflate(R.menu.debug_settings, menu);
- }
- }
-}
diff --git a/src/at/bitfire/davdroid/syncadapter/LoginEmailFragment.java b/src/at/bitfire/davdroid/syncadapter/LoginEmailFragment.java
deleted file mode 100644
index e7d2e3ec..00000000
--- a/src/at/bitfire/davdroid/syncadapter/LoginEmailFragment.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 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.syncadapter;
-
-import java.net.URI;
-import java.net.URISyntaxException;
-
-import android.app.DialogFragment;
-import android.app.Fragment;
-import android.app.FragmentTransaction;
-import android.os.Bundle;
-import android.text.Editable;
-import android.text.TextWatcher;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.EditText;
-import at.bitfire.davdroid.R;
-
-public class LoginEmailFragment extends Fragment implements TextWatcher {
-
- protected EditText editEmail, editPassword;
-
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- View v = inflater.inflate(R.layout.login_email, container, false);
-
- editEmail = (EditText)v.findViewById(R.id.email_address);
- editEmail.addTextChangedListener(this);
- editPassword = (EditText)v.findViewById(R.id.password);
- editPassword.addTextChangedListener(this);
-
- setHasOptionsMenu(true);
- return v;
- }
-
- @Override
- public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
- inflater.inflate(R.menu.only_next, menu);
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.next:
- FragmentTransaction ft = getFragmentManager().beginTransaction();
-
- Bundle args = new Bundle();
- String email = editEmail.getText().toString();
- args.putString(QueryServerDialogFragment.EXTRA_BASE_URI, "mailto:" + email);
- args.putString(QueryServerDialogFragment.EXTRA_USER_NAME, email);
- args.putString(QueryServerDialogFragment.EXTRA_PASSWORD, editPassword.getText().toString());
- args.putBoolean(QueryServerDialogFragment.EXTRA_AUTH_PREEMPTIVE, true);
-
- DialogFragment dialog = new QueryServerDialogFragment();
- dialog.setArguments(args);
- dialog.show(ft, QueryServerDialogFragment.class.getName());
- break;
- default:
- return false;
- }
- return true;
- }
-
-
- // input validation
-
- @Override
- public void onPrepareOptionsMenu(Menu menu) {
- boolean passwordOk = editPassword.getText().length() > 0,
- emailOk = false;
-
- String email = editEmail.getText().toString();
- try {
- URI uri = new URI("mailto:" + email);
- if (uri.isOpaque()) {
- int pos = email.lastIndexOf("@");
- if (pos != -1)
- emailOk = !email.substring(pos+1).isEmpty();
- }
- } catch (URISyntaxException e) {
- // invalid mailto: URI
- }
-
- MenuItem item = menu.findItem(R.id.next);
- item.setEnabled(emailOk && passwordOk);
- }
-
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- }
-
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- getActivity().invalidateOptionsMenu();
- }
-
- @Override
- public void afterTextChanged(Editable s) {
- }
-
-}
diff --git a/src/at/bitfire/davdroid/syncadapter/LoginTypeFragment.java b/src/at/bitfire/davdroid/syncadapter/LoginTypeFragment.java
deleted file mode 100644
index 8507bcc6..00000000
--- a/src/at/bitfire/davdroid/syncadapter/LoginTypeFragment.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 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.syncadapter;
-
-import android.app.Fragment;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.RadioButton;
-import at.bitfire.davdroid.R;
-
-public class LoginTypeFragment extends Fragment {
-
- protected RadioButton btnTypeEmail, btnTypeURL;
-
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- View v = inflater.inflate(R.layout.login_type, container, false);
-
- btnTypeEmail = (RadioButton)v.findViewById(R.id.login_type_email);
- btnTypeURL = (RadioButton)v.findViewById(R.id.login_type_url);
-
- setHasOptionsMenu(true);
-
- return v;
- }
-
- @Override
- public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
- inflater.inflate(R.menu.only_next, menu);
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.next:
- Fragment loginFragment = btnTypeEmail.isChecked() ? new LoginEmailFragment() : new LoginURLFragment();
- getFragmentManager().beginTransaction()
- .replace(R.id.fragment_container, loginFragment)
- .addToBackStack(null)
- .commitAllowingStateLoss();
- return true;
- default:
- return false;
- }
- }
-}
diff --git a/src/at/bitfire/davdroid/syncadapter/LoginURLFragment.java b/src/at/bitfire/davdroid/syncadapter/LoginURLFragment.java
deleted file mode 100644
index d504ff56..00000000
--- a/src/at/bitfire/davdroid/syncadapter/LoginURLFragment.java
+++ /dev/null
@@ -1,149 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 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.syncadapter;
-
-import java.net.URI;
-import java.net.URISyntaxException;
-
-import org.apache.commons.lang.StringUtils;
-
-import android.app.DialogFragment;
-import android.app.Fragment;
-import android.app.FragmentTransaction;
-import android.os.Bundle;
-import android.text.Editable;
-import android.text.TextWatcher;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemSelectedListener;
-import android.widget.Button;
-import android.widget.CheckBox;
-import android.widget.EditText;
-import android.widget.Spinner;
-import android.widget.TextView;
-import at.bitfire.davdroid.R;
-import at.bitfire.davdroid.URLUtils;
-
-public class LoginURLFragment extends Fragment implements TextWatcher {
- protected String scheme;
-
- protected TextView textHttpWarning;
- protected EditText editBaseURI, editUserName, editPassword;
- protected CheckBox checkboxPreemptive;
- protected Button btnNext;
-
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- View v = inflater.inflate(R.layout.login_url, container, false);
-
- // protocol selection spinner
- textHttpWarning = (TextView) v.findViewById(R.id.http_warning);
-
- Spinner spnrScheme = (Spinner) v.findViewById(R.id.login_scheme);
- spnrScheme.setOnItemSelectedListener(new OnItemSelectedListener() {
- @Override
- public void onItemSelected(AdapterView> parent, View view, int position, long id) {
- scheme = parent.getAdapter().getItem(position).toString();
- textHttpWarning.setVisibility(scheme.equals("https://") ? View.GONE : View.VISIBLE);
- }
-
- @Override
- public void onNothingSelected(AdapterView> parent) {
- scheme = null;
- }
- });
- spnrScheme.setSelection(1); // HTTPS
-
- // other input fields
- editBaseURI = (EditText) v.findViewById(R.id.login_host_path);
- editBaseURI.addTextChangedListener(this);
-
- editUserName = (EditText) v.findViewById(R.id.userName);
- editUserName.addTextChangedListener(this);
-
- editPassword = (EditText) v.findViewById(R.id.password);
- editPassword.addTextChangedListener(this);
-
- checkboxPreemptive = (CheckBox) v.findViewById(R.id.auth_preemptive);
-
- // hook into action bar
- setHasOptionsMenu(true);
-
- return v;
- }
-
- @Override
- public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
- inflater.inflate(R.menu.only_next, menu);
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.next:
- FragmentTransaction ft = getFragmentManager().beginTransaction();
-
- Bundle args = new Bundle();
- String host_path = editBaseURI.getText().toString();
- args.putString(QueryServerDialogFragment.EXTRA_BASE_URI, URLUtils.sanitize(scheme + host_path));
- args.putString(QueryServerDialogFragment.EXTRA_USER_NAME, editUserName.getText().toString());
- args.putString(QueryServerDialogFragment.EXTRA_PASSWORD, editPassword.getText().toString());
- args.putBoolean(QueryServerDialogFragment.EXTRA_AUTH_PREEMPTIVE, checkboxPreemptive.isChecked());
-
- DialogFragment dialog = new QueryServerDialogFragment();
- dialog.setArguments(args);
- dialog.show(ft, QueryServerDialogFragment.class.getName());
- break;
- default:
- return false;
- }
- return true;
- }
-
-
- // input validation
-
- @Override
- public void onPrepareOptionsMenu(Menu menu) {
- boolean ok =
- editUserName.getText().length() > 0 &&
- editPassword.getText().length() > 0;
-
- if (ok)
- // check host name
- try {
- URI uri = new URI(URLUtils.sanitize(scheme + editBaseURI.getText().toString()));
- if (StringUtils.isBlank(uri.getHost()))
- ok = false;
- } catch (URISyntaxException e) {
- ok = false;
- }
-
- MenuItem item = menu.findItem(R.id.next);
- item.setEnabled(ok);
- }
-
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- }
-
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- getActivity().invalidateOptionsMenu();
- }
-
- @Override
- public void afterTextChanged(Editable s) {
- }
-}
diff --git a/src/at/bitfire/davdroid/syncadapter/QueryServerDialogFragment.java b/src/at/bitfire/davdroid/syncadapter/QueryServerDialogFragment.java
deleted file mode 100644
index 2596cfe0..00000000
--- a/src/at/bitfire/davdroid/syncadapter/QueryServerDialogFragment.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 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.syncadapter;
-
-import java.io.IOException;
-import java.net.URI;
-import java.net.URISyntaxException;
-
-import lombok.Cleanup;
-import android.app.DialogFragment;
-import android.app.LoaderManager.LoaderCallbacks;
-import android.content.AsyncTaskLoader;
-import android.content.Context;
-import android.content.Loader;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ProgressBar;
-import android.widget.Toast;
-import at.bitfire.davdroid.R;
-import at.bitfire.davdroid.resource.DavResourceFinder;
-import at.bitfire.davdroid.resource.ServerInfo;
-import at.bitfire.davdroid.webdav.DavException;
-import ch.boye.httpclientandroidlib.HttpException;
-
-public class QueryServerDialogFragment extends DialogFragment implements LoaderCallbacks {
- private static final String TAG = "davdroid.QueryServerDialogFragment";
- public static final String
- EXTRA_BASE_URI = "base_uri",
- EXTRA_USER_NAME = "user_name",
- EXTRA_PASSWORD = "password",
- EXTRA_AUTH_PREEMPTIVE = "auth_preemptive";
-
- ProgressBar progressBar;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setStyle(DialogFragment.STYLE_NO_TITLE, android.R.style.Theme_Holo_Light_Dialog);
- setCancelable(false);
-
- Loader loader = getLoaderManager().initLoader(0, getArguments(), this);
- if (savedInstanceState == null) // http://code.google.com/p/android/issues/detail?id=14944
- loader.forceLoad();
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- View v = inflater.inflate(R.layout.query_server, container, false);
- return v;
- }
-
- @Override
- public Loader onCreateLoader(int id, Bundle args) {
- Log.i(TAG, "onCreateLoader");
- return new ServerInfoLoader(getActivity(), args);
- }
-
- @Override
- public void onLoadFinished(Loader loader, ServerInfo serverInfo) {
- if (serverInfo.getErrorMessage() != null)
- Toast.makeText(getActivity(), serverInfo.getErrorMessage(), Toast.LENGTH_LONG).show();
- else {
- SelectCollectionsFragment selectCollections = new SelectCollectionsFragment();
- Bundle arguments = new Bundle();
- arguments.putSerializable(SelectCollectionsFragment.KEY_SERVER_INFO, serverInfo);
- selectCollections.setArguments(arguments);
-
- getFragmentManager().beginTransaction()
- .replace(R.id.fragment_container, selectCollections)
- .addToBackStack(null)
- .commitAllowingStateLoss();
- }
-
- getDialog().dismiss();
- }
-
- @Override
- public void onLoaderReset(Loader arg0) {
- }
-
-
- static class ServerInfoLoader extends AsyncTaskLoader {
- private static final String TAG = "davdroid.ServerInfoLoader";
- final Bundle args;
- final Context context;
-
- public ServerInfoLoader(Context context, Bundle args) {
- super(context);
- this.context = context;
- this.args = args;
- }
-
- @Override
- public ServerInfo loadInBackground() {
- ServerInfo serverInfo = new ServerInfo(
- URI.create(args.getString(EXTRA_BASE_URI)),
- args.getString(EXTRA_USER_NAME),
- args.getString(EXTRA_PASSWORD),
- args.getBoolean(EXTRA_AUTH_PREEMPTIVE)
- );
-
- try {
- @Cleanup DavResourceFinder finder = new DavResourceFinder(context);
- finder.findResources(serverInfo);
- } catch (URISyntaxException e) {
- serverInfo.setErrorMessage(getContext().getString(R.string.exception_uri_syntax, e.getMessage()));
- } catch (IOException e) {
- serverInfo.setErrorMessage(getContext().getString(R.string.exception_io, e.getLocalizedMessage()));
- } catch (HttpException e) {
- Log.e(TAG, "HTTP error while querying server info", e);
- serverInfo.setErrorMessage(getContext().getString(R.string.exception_http, e.getLocalizedMessage()));
- } catch (DavException e) {
- Log.e(TAG, "DAV error while querying server info", e);
- serverInfo.setErrorMessage(getContext().getString(R.string.exception_incapable_resource, e.getLocalizedMessage()));
- }
-
- return serverInfo;
- }
-
- }
-}
diff --git a/src/at/bitfire/davdroid/syncadapter/SelectCollectionsAdapter.java b/src/at/bitfire/davdroid/syncadapter/SelectCollectionsAdapter.java
deleted file mode 100644
index 95c871ed..00000000
--- a/src/at/bitfire/davdroid/syncadapter/SelectCollectionsAdapter.java
+++ /dev/null
@@ -1,161 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 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.syncadapter;
-
-import lombok.Getter;
-import android.annotation.SuppressLint;
-import android.content.Context;
-import android.text.Html;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-import android.widget.CheckedTextView;
-import android.widget.ListAdapter;
-import at.bitfire.davdroid.R;
-import at.bitfire.davdroid.resource.ServerInfo;
-import at.bitfire.davdroid.resource.ServerInfo.ResourceInfo.Type;
-
-public class SelectCollectionsAdapter extends BaseAdapter implements ListAdapter {
- final static int TYPE_ADDRESS_BOOKS_HEADING = 0,
- TYPE_ADDRESS_BOOKS_ROW = 1,
- TYPE_CALENDARS_HEADING = 2,
- TYPE_CALENDARS_ROW = 3;
-
- protected Context context;
- protected ServerInfo serverInfo;
- @Getter protected int nAddressBooks, nCalendars;
-
-
- public SelectCollectionsAdapter(Context context, ServerInfo serverInfo) {
- this.context = context;
-
- this.serverInfo = serverInfo;
- nAddressBooks = (serverInfo.getAddressBooks() == null) ? 0 : serverInfo.getAddressBooks().size();
- nCalendars = (serverInfo.getCalendars() == null) ? 0 : serverInfo.getCalendars().size();
- }
-
-
- // item data
-
- @Override
- public int getCount() {
- return nAddressBooks + nCalendars + 2;
- }
-
- @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);
- return null;
- }
-
- @Override
- public boolean hasStableIds() {
- return true;
- }
-
- @Override
- public long getItemId(int position) {
- return position;
- }
-
-
- // item views
-
- @Override
- public int getViewTypeCount() {
- return 4;
- }
-
- @Override
- public int getItemViewType(int position) {
- if (position == 0)
- return TYPE_ADDRESS_BOOKS_HEADING;
- else if (position <= nAddressBooks)
- return TYPE_ADDRESS_BOOKS_ROW;
- else if (position == nAddressBooks + 1)
- return TYPE_CALENDARS_HEADING;
- else if (position <= nAddressBooks + nCalendars + 1)
- return TYPE_CALENDARS_ROW;
- else
- return IGNORE_ITEM_VIEW_TYPE;
- }
-
- @Override
- @SuppressLint("InflateParams")
- public View getView(int position, View convertView, ViewGroup parent) {
- View v = convertView;
-
- // step 1: get view (either by creating or recycling)
- if (v == null) {
- LayoutInflater inflater = LayoutInflater.from(parent.getContext());
- switch (getItemViewType(position)) {
- case TYPE_ADDRESS_BOOKS_HEADING:
- v = inflater.inflate(R.layout.address_books_heading, parent, false);
- break;
- case TYPE_ADDRESS_BOOKS_ROW:
- v = inflater.inflate(android.R.layout.simple_list_item_single_choice, null);
- v.setPadding(0, 8, 0, 8);
- break;
- case TYPE_CALENDARS_HEADING:
- v = inflater.inflate(R.layout.calendars_heading, parent, false);
- break;
- case TYPE_CALENDARS_ROW:
- v = inflater.inflate(android.R.layout.simple_list_item_multiple_choice, null);
- v.setPadding(0, 8, 0, 8);
- }
- }
-
- // step 2: fill view with content
- switch (getItemViewType(position)) {
- case TYPE_ADDRESS_BOOKS_ROW:
- setContent((CheckedTextView)v, R.drawable.addressbook, (ServerInfo.ResourceInfo)getItem(position));
- break;
- case TYPE_CALENDARS_ROW:
- setContent((CheckedTextView)v, R.drawable.calendar, (ServerInfo.ResourceInfo)getItem(position));
- }
-
- return v;
- }
-
- protected void setContent(CheckedTextView view, int collectionIcon, ServerInfo.ResourceInfo info) {
- // set layout and icons
- view.setCompoundDrawablesWithIntrinsicBounds(collectionIcon, 0, info.isReadOnly() ? R.drawable.ic_read_only : 0, 0);
- 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 = "" + title + "";
- if (info.isReadOnly())
- title = title + " (" + context.getString(R.string.setup_read_only) + ")";
-
- String description = info.getDescription();
- if (description == null)
- description = info.getURL();
-
- // FIXME escape HTML
- view.setText(Html.fromHtml(title + "
" + description));
- }
-
- @Override
- public boolean areAllItemsEnabled() {
- return false;
- }
-
- @Override
- public boolean isEnabled(int position) {
- int type = getItemViewType(position);
- return (type == TYPE_ADDRESS_BOOKS_ROW || type == TYPE_CALENDARS_ROW);
- }
-}
diff --git a/src/at/bitfire/davdroid/syncadapter/SelectCollectionsFragment.java b/src/at/bitfire/davdroid/syncadapter/SelectCollectionsFragment.java
deleted file mode 100644
index c7a044b2..00000000
--- a/src/at/bitfire/davdroid/syncadapter/SelectCollectionsFragment.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 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.syncadapter;
-
-import android.app.ListFragment;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemClickListener;
-import android.widget.ListAdapter;
-import android.widget.ListView;
-import at.bitfire.davdroid.R;
-import at.bitfire.davdroid.resource.ServerInfo;
-
-public class SelectCollectionsFragment extends ListFragment {
- public static final String KEY_SERVER_INFO = "server_info";
-
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- View v = super.onCreateView(inflater, container, savedInstanceState);
- setHasOptionsMenu(true);
- return v;
- }
-
- @Override
- public void onDestroyView() {
- super.onDestroyView();
- setListAdapter(null);
- }
-
- @Override
- public void onViewCreated(View view, Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
-
- final ListView listView = getListView();
- listView.setPadding(20, 30, 20, 30);
-
- View header = getActivity().getLayoutInflater().inflate(R.layout.select_collections_header, getListView(), false);
- listView.addHeaderView(header, getListView(), false);
-
- final ServerInfo serverInfo = (ServerInfo)getArguments().getSerializable(KEY_SERVER_INFO);
- final SelectCollectionsAdapter adapter = new SelectCollectionsAdapter(view.getContext(), serverInfo);
- setListAdapter(adapter);
-
- listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
- listView.setOnItemClickListener(new OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView> parent, View view, int position, long id) {
- int itemPosition = position - 1; // one list header view at pos. 0
- if (adapter.getItemViewType(itemPosition) == SelectCollectionsAdapter.TYPE_ADDRESS_BOOKS_ROW) {
- // unselect all other address books
- for (int pos = 1; pos <= adapter.getNAddressBooks(); pos++)
- if (pos != itemPosition)
- listView.setItemChecked(pos + 1, false);
- }
-
- getActivity().invalidateOptionsMenu();
- }
- });
- }
-
- @Override
- public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
- inflater.inflate(R.menu.only_next, menu);
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.next:
- ServerInfo serverInfo = (ServerInfo)getArguments().getSerializable(KEY_SERVER_INFO);
-
- // synchronize only selected collections
- for (ServerInfo.ResourceInfo addressBook : serverInfo.getAddressBooks())
- addressBook.setEnabled(false);
- for (ServerInfo.ResourceInfo calendar : serverInfo.getCalendars())
- calendar.setEnabled(false);
-
- ListAdapter adapter = getListView().getAdapter();
- for (long id : getListView().getCheckedItemIds()) {
- int position = (int)id + 1; // +1 because header view is inserted at pos. 0
- ServerInfo.ResourceInfo info = (ServerInfo.ResourceInfo)adapter.getItem(position);
- info.setEnabled(true);
- }
-
- // pass to "account details" fragment
- AccountDetailsFragment accountDetails = new AccountDetailsFragment();
- Bundle arguments = new Bundle();
- arguments.putSerializable(SelectCollectionsFragment.KEY_SERVER_INFO, serverInfo);
- accountDetails.setArguments(arguments);
-
- getFragmentManager().beginTransaction()
- .replace(R.id.fragment_container, accountDetails)
- .addToBackStack(null)
- .commitAllowingStateLoss();
- break;
- default:
- return false;
- }
- return true;
- }
-
-
- // input validation
-
- @Override
- public void onPrepareOptionsMenu(Menu menu) {
- boolean ok = false;
- try {
- ok = getListView().getCheckedItemCount() > 0;
- } catch(IllegalStateException e) {
- }
- MenuItem item = menu.findItem(R.id.next);
- item.setEnabled(ok);
- }
-}
diff --git a/src/at/bitfire/davdroid/syncadapter/SyncManager.java b/src/at/bitfire/davdroid/syncadapter/SyncManager.java
deleted file mode 100644
index c1bf2fa2..00000000
--- a/src/at/bitfire/davdroid/syncadapter/SyncManager.java
+++ /dev/null
@@ -1,214 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 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.syncadapter;
-
-import java.io.IOException;
-import java.net.URISyntaxException;
-import java.util.HashSet;
-import java.util.Set;
-
-import net.fortuna.ical4j.model.ValidationException;
-import android.content.SyncResult;
-import android.util.Log;
-import at.bitfire.davdroid.ArrayUtils;
-import at.bitfire.davdroid.resource.LocalCollection;
-import at.bitfire.davdroid.resource.LocalStorageException;
-import at.bitfire.davdroid.resource.RecordNotFoundException;
-import at.bitfire.davdroid.resource.RemoteCollection;
-import at.bitfire.davdroid.resource.Resource;
-import at.bitfire.davdroid.webdav.DavException;
-import at.bitfire.davdroid.webdav.HttpException;
-import at.bitfire.davdroid.webdav.NotFoundException;
-import at.bitfire.davdroid.webdav.PreconditionFailedException;
-
-public class SyncManager {
- private static final String TAG = "davdroid.SyncManager";
-
- private static final int MAX_MULTIGET_RESOURCES = 35;
-
- protected LocalCollection extends Resource> local;
- protected RemoteCollection extends Resource> remote;
-
-
- public SyncManager(LocalCollection extends Resource> local, RemoteCollection extends Resource> remote) {
- this.local = local;
- this.remote = remote;
- }
-
-
- public void synchronize(boolean manualSync, SyncResult syncResult) throws URISyntaxException, LocalStorageException, IOException, HttpException, DavException {
- // PHASE 1: push local changes to server
- int deletedRemotely = pushDeleted(),
- addedRemotely = pushNew(),
- updatedRemotely = pushDirty();
-
- syncResult.stats.numEntries = deletedRemotely + addedRemotely + updatedRemotely;
-
- // PHASE 2A: check if there's a reason to do a sync with remote (= forced sync or remote CTag changed)
- boolean fetchCollection = syncResult.stats.numEntries > 0;
- if (manualSync) {
- Log.i(TAG, "Synchronization forced");
- fetchCollection = true;
- }
- if (!fetchCollection) {
- String currentCTag = remote.getCTag(),
- lastCTag = local.getCTag();
- Log.d(TAG, "Last local CTag = " + lastCTag + "; current remote CTag = " + currentCTag);
- if (currentCTag == null || !currentCTag.equals(lastCTag))
- fetchCollection = true;
- }
-
- if (!fetchCollection) {
- Log.i(TAG, "No local changes and CTags match, no need to sync");
- return;
- }
-
- // PHASE 2B: detect details of remote changes
- Log.i(TAG, "Fetching remote resource list");
- Set remotelyAdded = new HashSet(),
- remotelyUpdated = new HashSet();
-
- Resource[] remoteResources = remote.getMemberETags();
- for (Resource remoteResource : remoteResources) {
- try {
- Resource localResource = local.findByRemoteName(remoteResource.getName(), false);
- if (localResource.getETag() == null || !localResource.getETag().equals(remoteResource.getETag()))
- remotelyUpdated.add(remoteResource);
- } catch(RecordNotFoundException e) {
- remotelyAdded.add(remoteResource);
- }
- }
-
- // PHASE 3: pull remote changes from server
- syncResult.stats.numInserts = pullNew(remotelyAdded.toArray(new Resource[0]));
- syncResult.stats.numUpdates = pullChanged(remotelyUpdated.toArray(new Resource[0]));
- syncResult.stats.numEntries += syncResult.stats.numInserts + syncResult.stats.numUpdates;
-
- Log.i(TAG, "Removing non-dirty resources that are not present remotely anymore");
- local.deleteAllExceptRemoteNames(remoteResources);
- local.commit();
-
- // update collection CTag
- Log.i(TAG, "Sync complete, fetching new CTag");
- local.setCTag(remote.getCTag());
- }
-
-
- private int pushDeleted() throws URISyntaxException, LocalStorageException, IOException, HttpException {
- int count = 0;
- long[] deletedIDs = local.findDeleted();
-
- try {
- Log.i(TAG, "Remotely removing " + deletedIDs.length + " deleted resource(s) (if not changed)");
- for (long id : deletedIDs)
- try {
- Resource res = local.findById(id, false);
- if (res.getName() != null) // is this resource even present remotely?
- try {
- remote.delete(res);
- } catch(NotFoundException e) {
- Log.i(TAG, "Locally-deleted resource has already been removed from server");
- } catch(PreconditionFailedException e) {
- Log.i(TAG, "Locally-deleted resource has been changed on the server in the meanwhile");
- }
-
- // always delete locally so that the record with the DELETED flag doesn't cause another deletion attempt
- local.delete(res);
-
- count++;
- } catch (RecordNotFoundException e) {
- Log.wtf(TAG, "Couldn't read locally-deleted record", e);
- }
- } finally {
- local.commit();
- }
- return count;
- }
-
- private int pushNew() throws URISyntaxException, LocalStorageException, IOException, HttpException {
- int count = 0;
- long[] newIDs = local.findNew();
- Log.i(TAG, "Uploading " + newIDs.length + " new resource(s) (if not existing)");
- try {
- for (long id : newIDs)
- try {
- Resource res = local.findById(id, true);
- String eTag = remote.add(res);
- if (eTag != null)
- local.updateETag(res, eTag);
- local.clearDirty(res);
- count++;
- } catch(PreconditionFailedException e) {
- Log.i(TAG, "Didn't overwrite existing resource with other content");
- } catch (ValidationException e) {
- Log.e(TAG, "Couldn't create entity for adding: " + e.toString());
- } catch (RecordNotFoundException e) {
- Log.wtf(TAG, "Couldn't read new record", e);
- }
- } finally {
- local.commit();
- }
- return count;
- }
-
- private int pushDirty() throws URISyntaxException, LocalStorageException, IOException, HttpException {
- int count = 0;
- long[] dirtyIDs = local.findUpdated();
- Log.i(TAG, "Uploading " + dirtyIDs.length + " modified resource(s) (if not changed)");
- try {
- for (long id : dirtyIDs) {
- try {
- Resource res = local.findById(id, true);
- String eTag = remote.update(res);
- if (eTag != null)
- local.updateETag(res, eTag);
- local.clearDirty(res);
- count++;
- } catch(PreconditionFailedException e) {
- Log.i(TAG, "Locally changed resource has been changed on the server in the meanwhile");
- } catch (ValidationException e) {
- Log.e(TAG, "Couldn't create entity for updating: " + e.toString());
- } catch (RecordNotFoundException e) {
- Log.e(TAG, "Couldn't read dirty record", e);
- }
- }
- } finally {
- local.commit();
- }
- return count;
- }
-
- private int pullNew(Resource[] resourcesToAdd) throws URISyntaxException, LocalStorageException, IOException, HttpException, DavException {
- int count = 0;
- Log.i(TAG, "Fetching " + resourcesToAdd.length + " new remote resource(s)");
-
- for (Resource[] resources : ArrayUtils.partition(resourcesToAdd, MAX_MULTIGET_RESOURCES))
- for (Resource res : remote.multiGet(resources)) {
- Log.d(TAG, "Adding " + res.getName());
- local.add(res);
- local.commit();
- count++;
- }
- return count;
- }
-
- private int pullChanged(Resource[] resourcesToUpdate) throws URISyntaxException, LocalStorageException, IOException, HttpException, DavException {
- int count = 0;
- Log.i(TAG, "Fetching " + resourcesToUpdate.length + " updated remote resource(s)");
-
- for (Resource[] resources : ArrayUtils.partition(resourcesToUpdate, MAX_MULTIGET_RESOURCES))
- for (Resource res : remote.multiGet(resources)) {
- Log.i(TAG, "Updating " + res.getName());
- local.updateByRemoteName(res);
- local.commit();
- count++;
- }
- return count;
- }
-
-}
diff --git a/src/at/bitfire/davdroid/syncadapter/WebDavResourceAdapter.java b/src/at/bitfire/davdroid/syncadapter/WebDavResourceAdapter.java
deleted file mode 100644
index 9263874d..00000000
--- a/src/at/bitfire/davdroid/syncadapter/WebDavResourceAdapter.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 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.syncadapter;
-
-import java.util.List;
-
-import android.content.Context;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-import android.widget.TextView;
-import at.bitfire.davdroid.webdav.WebDavResource;
-
-public class WebDavResourceAdapter extends BaseAdapter {
- protected int viewId;
- protected LayoutInflater inflater;
- WebDavResource[] items;
-
- public WebDavResourceAdapter(Context context, int textViewResourceId, List objects) {
- viewId = textViewResourceId;
- inflater = LayoutInflater.from(context);
- items = objects.toArray(new WebDavResource[0]);
- }
-
- @Override
- public View getView(int position, View view, ViewGroup parent) {
- WebDavResource item = items[position];
- View itemView = (View)inflater.inflate(viewId, null);
-
- TextView textName = (TextView) itemView.findViewById(android.R.id.text1);
- textName.setText(item.getDisplayName());
-
- TextView textDescription = (TextView) itemView.findViewById(android.R.id.text2);
- String description = item.getDescription();
- if (description == null)
- description = item.getLocation().getPath();
- textDescription.setText(description);
-
- return itemView;
- }
-
- @Override
- public int getCount() {
- return items.length;
- }
-
- @Override
- public Object getItem(int position) {
- return items[position];
- }
-
- @Override
- public long getItemId(int position) {
- return position;
- }
-}
diff --git a/src/at/bitfire/davdroid/webdav/DavAddressbookMultiget.java b/src/at/bitfire/davdroid/webdav/DavAddressbookMultiget.java
deleted file mode 100644
index 9b32960f..00000000
--- a/src/at/bitfire/davdroid/webdav/DavAddressbookMultiget.java
+++ /dev/null
@@ -1,21 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 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.webdav;
-
-import org.simpleframework.xml.Namespace;
-import org.simpleframework.xml.NamespaceList;
-import org.simpleframework.xml.Root;
-
-@Root(name="addressbook-multiget")
-@NamespaceList({
- @Namespace(reference="DAV:"),
- @Namespace(prefix="CD",reference="urn:ietf:params:xml:ns:carddav")
-})
-@Namespace(prefix="CD",reference="urn:ietf:params:xml:ns:carddav")
-public class DavAddressbookMultiget extends DavMultiget {
-}
diff --git a/src/at/bitfire/davdroid/webdav/DavCalendarMultiget.java b/src/at/bitfire/davdroid/webdav/DavCalendarMultiget.java
deleted file mode 100644
index 75a7ce71..00000000
--- a/src/at/bitfire/davdroid/webdav/DavCalendarMultiget.java
+++ /dev/null
@@ -1,21 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 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.webdav;
-
-import org.simpleframework.xml.Namespace;
-import org.simpleframework.xml.NamespaceList;
-import org.simpleframework.xml.Root;
-
-@Root(name="calendar-multiget")
-@NamespaceList({
- @Namespace(reference="DAV:"),
- @Namespace(prefix="C",reference="urn:ietf:params:xml:ns:caldav")
-})
-@Namespace(prefix="C",reference="urn:ietf:params:xml:ns:caldav")
-public class DavCalendarMultiget extends DavMultiget {
-}
diff --git a/src/at/bitfire/davdroid/webdav/DavException.java b/src/at/bitfire/davdroid/webdav/DavException.java
deleted file mode 100644
index 506af6da..00000000
--- a/src/at/bitfire/davdroid/webdav/DavException.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 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.webdav;
-
-public class DavException extends Exception {
- private static final long serialVersionUID = -2118919144443165706L;
-
- final private static String prefix = "Invalid DAV response: ";
-
- /* used to indiciate DAV protocol errors */
-
-
- public DavException(String message) {
- super(prefix + message);
- }
-
- public DavException(String message, Throwable ex) {
- super(prefix + message, ex);
- }
-}
diff --git a/src/at/bitfire/davdroid/webdav/DavHref.java b/src/at/bitfire/davdroid/webdav/DavHref.java
deleted file mode 100644
index 438c4e49..00000000
--- a/src/at/bitfire/davdroid/webdav/DavHref.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 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.webdav;
-
-import org.simpleframework.xml.Namespace;
-import org.simpleframework.xml.Root;
-import org.simpleframework.xml.Text;
-
-@Root(name="href")
-@Namespace(prefix="D",reference="DAV:")
-public class DavHref {
- @Text
- String href;
-
- DavHref() {
- }
-
- public DavHref(String href) {
- this.href = href;
- }
-}
diff --git a/src/at/bitfire/davdroid/webdav/DavHttpClient.java b/src/at/bitfire/davdroid/webdav/DavHttpClient.java
deleted file mode 100644
index b4d1ee16..00000000
--- a/src/at/bitfire/davdroid/webdav/DavHttpClient.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 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.webdav;
-
-import android.util.Log;
-import at.bitfire.davdroid.Constants;
-import ch.boye.httpclientandroidlib.client.config.RequestConfig;
-import ch.boye.httpclientandroidlib.config.Registry;
-import ch.boye.httpclientandroidlib.config.RegistryBuilder;
-import ch.boye.httpclientandroidlib.conn.socket.ConnectionSocketFactory;
-import ch.boye.httpclientandroidlib.conn.socket.PlainConnectionSocketFactory;
-import ch.boye.httpclientandroidlib.impl.client.CloseableHttpClient;
-import ch.boye.httpclientandroidlib.impl.client.HttpClientBuilder;
-import ch.boye.httpclientandroidlib.impl.client.HttpClients;
-import ch.boye.httpclientandroidlib.impl.conn.ManagedHttpClientConnectionFactory;
-import ch.boye.httpclientandroidlib.impl.conn.PoolingHttpClientConnectionManager;
-
-public class DavHttpClient {
- private final static String TAG = "davdroid.DavHttpClient";
-
- private final static RequestConfig defaultRqConfig;
- private final static Registry socketFactoryRegistry;
-
- static {
- socketFactoryRegistry = RegistryBuilder. create()
- .register("http", PlainConnectionSocketFactory.getSocketFactory())
- .register("https", TlsSniSocketFactory.INSTANCE)
- .build();
-
- // use request defaults from AndroidHttpClient
- defaultRqConfig = RequestConfig.copy(RequestConfig.DEFAULT)
- .setConnectTimeout(20*1000)
- .setSocketTimeout(45*1000)
- .setStaleConnectionCheckEnabled(false)
- .build();
- }
-
-
- public static CloseableHttpClient create(boolean disableCompression, boolean logTraffic) {
- PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
- // limits per DavHttpClient (= per DavSyncAdapter extends AbstractThreadedSyncAdapter)
- connectionManager.setMaxTotal(3); // max. 3 connections in total
- connectionManager.setDefaultMaxPerRoute(2); // max. 2 connections per host
-
- HttpClientBuilder builder = HttpClients.custom()
- .useSystemProperties()
- .setConnectionManager(connectionManager)
- .setDefaultRequestConfig(defaultRqConfig)
- .setRetryHandler(DavHttpRequestRetryHandler.INSTANCE)
- .setRedirectStrategy(DavRedirectStrategy.INSTANCE)
- .setUserAgent("DAVdroid/" + Constants.APP_VERSION)
- .disableCookieManagement();
-
- if (disableCompression) {
- Log.d(TAG, "Disabling compression for debugging purposes");
- builder = builder.disableContentCompression();
- }
-
- if (logTraffic)
- Log.d(TAG, "Logging network traffic for debugging purposes");
- ManagedHttpClientConnectionFactory.INSTANCE.wirelog.enableDebug(logTraffic);
- ManagedHttpClientConnectionFactory.INSTANCE.log.enableDebug(logTraffic);
-
- return builder.build();
- }
-
-}
diff --git a/src/at/bitfire/davdroid/webdav/DavHttpRequestRetryHandler.java b/src/at/bitfire/davdroid/webdav/DavHttpRequestRetryHandler.java
deleted file mode 100644
index 6ab2a509..00000000
--- a/src/at/bitfire/davdroid/webdav/DavHttpRequestRetryHandler.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 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.webdav;
-
-import java.util.Locale;
-
-import org.apache.commons.lang.ArrayUtils;
-
-import ch.boye.httpclientandroidlib.HttpRequest;
-import ch.boye.httpclientandroidlib.impl.client.DefaultHttpRequestRetryHandler;
-
-public class DavHttpRequestRetryHandler extends DefaultHttpRequestRetryHandler {
- final static DavHttpRequestRetryHandler INSTANCE = new DavHttpRequestRetryHandler();
-
- // see http://www.iana.org/assignments/http-methods/http-methods.xhtml
- private final static String idempotentMethods[] = {
- "DELETE", "GET", "HEAD", "MKCALENDAR", "MKCOL", "OPTIONS", "PROPFIND", "PROPPATCH",
- "PUT", "REPORT", "SEARCH", "TRACE"
- };
-
- public DavHttpRequestRetryHandler() {
- super(/* retry count */ 3, /* retry already sent requests? */ false);
- }
-
- @Override
- protected boolean handleAsIdempotent(final HttpRequest request) {
- final String method = request.getRequestLine().getMethod().toUpperCase(Locale.ROOT);
- return ArrayUtils.contains(idempotentMethods, method);
- }
-}
diff --git a/src/at/bitfire/davdroid/webdav/DavIncapableException.java b/src/at/bitfire/davdroid/webdav/DavIncapableException.java
deleted file mode 100644
index 794865ec..00000000
--- a/src/at/bitfire/davdroid/webdav/DavIncapableException.java
+++ /dev/null
@@ -1,18 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 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.webdav;
-
-public class DavIncapableException extends DavException {
- private static final long serialVersionUID = -7199786680939975667L;
-
- /* used to indicate that the server doesn't support DAV */
-
- public DavIncapableException(String msg) {
- super(msg);
- }
-}
diff --git a/src/at/bitfire/davdroid/webdav/DavMultiget.java b/src/at/bitfire/davdroid/webdav/DavMultiget.java
deleted file mode 100644
index 64162b19..00000000
--- a/src/at/bitfire/davdroid/webdav/DavMultiget.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 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.webdav;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.simpleframework.xml.Element;
-import org.simpleframework.xml.ElementList;
-import org.simpleframework.xml.Order;
-
-@Order(elements={"prop","href"})
-public class DavMultiget {
- public enum Type {
- ADDRESS_BOOK,
- CALENDAR
- }
-
- @Element
- DavProp prop;
-
- @ElementList(inline=true)
- List hrefs;
-
-
- public static DavMultiget newRequest(Type type, String names[]) {
- DavMultiget multiget = (type == Type.ADDRESS_BOOK) ? new DavAddressbookMultiget() : new DavCalendarMultiget();
-
- multiget.prop = new DavProp();
- multiget.prop.getetag = new DavProp.GetETag();
-
- if (type == Type.ADDRESS_BOOK)
- multiget.prop.addressData = new DavProp.AddressData();
- else if (type == Type.CALENDAR)
- multiget.prop.calendarData = new DavProp.CalendarData();
-
- multiget.hrefs = new ArrayList(names.length);
- for (String name : names)
- multiget.hrefs.add(new DavHref(name));
-
- return multiget;
- }
-}
diff --git a/src/at/bitfire/davdroid/webdav/DavMultistatus.java b/src/at/bitfire/davdroid/webdav/DavMultistatus.java
deleted file mode 100644
index dce27c2b..00000000
--- a/src/at/bitfire/davdroid/webdav/DavMultistatus.java
+++ /dev/null
@@ -1,21 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 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.webdav;
-
-import java.util.List;
-
-import org.simpleframework.xml.ElementList;
-import org.simpleframework.xml.Namespace;
-import org.simpleframework.xml.Root;
-
-@Namespace(reference="DAV:")
-@Root(strict=false)
-public class DavMultistatus {
- @ElementList(inline=true,entry="response",required=false)
- List response;
-}
diff --git a/src/at/bitfire/davdroid/webdav/DavNoContentException.java b/src/at/bitfire/davdroid/webdav/DavNoContentException.java
deleted file mode 100644
index 6e3464b9..00000000
--- a/src/at/bitfire/davdroid/webdav/DavNoContentException.java
+++ /dev/null
@@ -1,18 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 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.webdav;
-
-public class DavNoContentException extends DavException {
- private static final long serialVersionUID = 6256645020350945477L;
-
- private final static String message = "HTTP response entity (content) expected but not received";
-
- public DavNoContentException() {
- super(message);
- }
-}
diff --git a/src/at/bitfire/davdroid/webdav/DavNoMultiStatusException.java b/src/at/bitfire/davdroid/webdav/DavNoMultiStatusException.java
deleted file mode 100644
index 07eb6e18..00000000
--- a/src/at/bitfire/davdroid/webdav/DavNoMultiStatusException.java
+++ /dev/null
@@ -1,18 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 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.webdav;
-
-public class DavNoMultiStatusException extends DavException {
- private static final long serialVersionUID = -3600405724694229828L;
-
- private final static String message = "207 Multi-Status expected but not received";
-
- public DavNoMultiStatusException() {
- super(message);
- }
-}
diff --git a/src/at/bitfire/davdroid/webdav/DavProp.java b/src/at/bitfire/davdroid/webdav/DavProp.java
deleted file mode 100644
index 93f25645..00000000
--- a/src/at/bitfire/davdroid/webdav/DavProp.java
+++ /dev/null
@@ -1,211 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 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.webdav;
-
-import java.util.List;
-
-import lombok.Getter;
-
-import org.simpleframework.xml.Attribute;
-import org.simpleframework.xml.Element;
-import org.simpleframework.xml.ElementList;
-import org.simpleframework.xml.Namespace;
-import org.simpleframework.xml.Root;
-import org.simpleframework.xml.Text;
-
-@Namespace(prefix="D",reference="DAV:")
-@Root(strict=false)
-public class DavProp {
-
- /* RFC 4918 WebDAV */
-
- @Element(required=false)
- ResourceType resourcetype;
-
- @Element(required=false)
- DisplayName displayname;
-
- @Element(required=false)
- GetCTag getctag;
-
- @Element(required=false)
- GetETag getetag;
-
- @Root(strict=false)
- public static class ResourceType {
- @Element(required=false)
- @Getter private Collection collection;
- public static class Collection { }
-
- @Element(required=false)
- @Getter private Addressbook addressbook;
- @Namespace(prefix="CD",reference="urn:ietf:params:xml:ns:carddav")
- public static class Addressbook { }
-
- @Element(required=false)
- @Getter private Calendar calendar;
- @Namespace(prefix="C",reference="urn:ietf:params:xml:ns:caldav")
- public static class Calendar { }
- }
-
- public static class DisplayName {
- @Text(required=false)
- @Getter private String displayName;
- }
-
- @Namespace(prefix="CS",reference="http://calendarserver.org/ns/")
- public static class GetCTag {
- @Text(required=false)
- @Getter private String CTag;
- }
-
- public static class GetETag {
- @Text(required=false)
- @Getter private String ETag;
- }
-
-
- /* RFC 5397 WebDAV Current Principal Extension */
-
- @Element(required=false,name="current-user-principal")
- CurrentUserPrincipal currentUserPrincipal;
-
- public static class CurrentUserPrincipal {
- @Element(required=false)
- @Getter private DavHref href;
- }
-
-
- /* RFC 3744 WebDAV Access Control Protocol */
-
- @ElementList(required=false,name="current-user-privilege-set",entry="privilege")
- List currentUserPrivilegeSet;
-
- public static class Privilege {
- @Element(required=false)
- @Getter private PrivAll all;
-
- @Element(required=false)
- @Getter private PrivBind bind;
-
- @Element(required=false)
- @Getter private PrivUnbind unbind;
-
- @Element(required=false)
- @Getter private PrivWrite write;
-
- @Element(required=false,name="write-content")
- @Getter private PrivWriteContent writeContent;
-
- public static class PrivAll { }
- public static class PrivBind { }
- public static class PrivUnbind { }
- public static class PrivWrite { }
- public static class PrivWriteContent { }
- }
-
-
-
- /* RFC 4791 CalDAV, RFC 6352 CardDAV */
-
- @Element(required=false,name="addressbook-home-set")
- AddressbookHomeSet addressbookHomeSet;
-
- @Element(required=false,name="calendar-home-set")
- CalendarHomeSet calendarHomeSet;
-
- @Element(required=false,name="addressbook-description")
- AddressbookDescription addressbookDescription;
-
- @Namespace(prefix="CD",reference="urn:ietf:params:xml:ns:carddav")
- @ElementList(required=false,name="supported-address-data",entry="address-data-type")
- List supportedAddressData;
-
- @Element(required=false,name="calendar-description")
- CalendarDescription calendarDescription;
-
- @Element(required=false,name="calendar-color")
- CalendarColor calendarColor;
-
- @Element(required=false,name="calendar-timezone")
- CalendarTimezone calendarTimezone;
-
- @Namespace(prefix="C",reference="urn:ietf:params:xml:ns:caldav")
- @ElementList(required=false,name="supported-calendar-component-set",entry="comp")
- List supportedCalendarComponentSet;
-
- @Element(name="address-data",required=false)
- AddressData addressData;
-
- @Element(name="calendar-data",required=false)
- CalendarData calendarData;
-
-
- @Namespace(prefix="CD",reference="urn:ietf:params:xml:ns:carddav")
- public static class AddressbookHomeSet {
- @Element(required=false)
- @Getter private DavHref href;
- }
-
- @Namespace(prefix="C",reference="urn:ietf:params:xml:ns:caldav")
- public static class CalendarHomeSet {
- @Element(required=false)
- @Getter private DavHref href;
- }
-
- @Namespace(prefix="CD",reference="urn:ietf:params:xml:ns:carddav")
- public static class AddressbookDescription {
- @Text(required=false)
- @Getter private String description;
- }
-
- @Namespace(prefix="CD",reference="urn:ietf:params:xml:ns:carddav")
- public static class AddressDataType {
- @Attribute(name="content-type")
- @Getter private String contentType;
-
- @Attribute
- @Getter private String version;
- }
-
- @Namespace(prefix="C",reference="urn:ietf:params:xml:ns:caldav")
- public static class CalendarDescription {
- @Text(required=false)
- @Getter private String description;
- }
-
- @Namespace(prefix="A",reference="http://apple.com/ns/ical/")
- public static class CalendarColor {
- @Text(required=false)
- @Getter private String color;
- }
-
- @Namespace(prefix="C",reference="urn:ietf:params:xml:ns:caldav")
- public static class CalendarTimezone {
- @Text(required=false)
- @Getter private String timezone;
- }
-
- @Namespace(prefix="C",reference="urn:ietf:params:xml:ns:caldav")
- public static class Comp {
- @Attribute
- @Getter String name;
- }
-
- @Namespace(prefix="CD",reference="urn:ietf:params:xml:ns:carddav")
- public static class AddressData {
- @Text(required=false)
- @Getter String vcard;
- }
-
- @Namespace(prefix="C",reference="urn:ietf:params:xml:ns:caldav")
- public static class CalendarData {
- @Text(required=false)
- @Getter String ical;
- }
-}
diff --git a/src/at/bitfire/davdroid/webdav/DavPropfind.java b/src/at/bitfire/davdroid/webdav/DavPropfind.java
deleted file mode 100644
index abf4104e..00000000
--- a/src/at/bitfire/davdroid/webdav/DavPropfind.java
+++ /dev/null
@@ -1,19 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 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.webdav;
-
-import org.simpleframework.xml.Element;
-import org.simpleframework.xml.Namespace;
-import org.simpleframework.xml.Root;
-
-@Namespace(reference="DAV:")
-@Root(name="propfind")
-public class DavPropfind {
- @Element(required=false)
- protected DavProp prop;
-}
diff --git a/src/at/bitfire/davdroid/webdav/DavPropstat.java b/src/at/bitfire/davdroid/webdav/DavPropstat.java
deleted file mode 100644
index 74ee66d6..00000000
--- a/src/at/bitfire/davdroid/webdav/DavPropstat.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 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.webdav;
-
-import org.simpleframework.xml.Element;
-import org.simpleframework.xml.Root;
-
-@Root(strict=false,name="propstat")
-public class DavPropstat {
- @Element
- DavProp prop;
-
- @Element
- String status;
-}
diff --git a/src/at/bitfire/davdroid/webdav/DavRedirectStrategy.java b/src/at/bitfire/davdroid/webdav/DavRedirectStrategy.java
deleted file mode 100644
index 7019d06c..00000000
--- a/src/at/bitfire/davdroid/webdav/DavRedirectStrategy.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 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.webdav;
-
-import java.net.MalformedURLException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.URL;
-
-import android.util.Log;
-import ch.boye.httpclientandroidlib.Header;
-import ch.boye.httpclientandroidlib.HttpHost;
-import ch.boye.httpclientandroidlib.HttpRequest;
-import ch.boye.httpclientandroidlib.HttpResponse;
-import ch.boye.httpclientandroidlib.ProtocolException;
-import ch.boye.httpclientandroidlib.RequestLine;
-import ch.boye.httpclientandroidlib.client.RedirectStrategy;
-import ch.boye.httpclientandroidlib.client.methods.HttpUriRequest;
-import ch.boye.httpclientandroidlib.client.methods.RequestBuilder;
-import ch.boye.httpclientandroidlib.client.protocol.HttpClientContext;
-import ch.boye.httpclientandroidlib.client.utils.URIUtils;
-import ch.boye.httpclientandroidlib.protocol.HttpContext;
-
-/**
- * Custom Redirect Strategy that handles 30x for CalDAV/CardDAV-specific requests correctly
- */
-public class DavRedirectStrategy implements RedirectStrategy {
- private final static String TAG = "davdroid.DavRedirectStrategy";
- final static DavRedirectStrategy INSTANCE = new DavRedirectStrategy();
-
- protected final static String REDIRECTABLE_METHODS[] = {
- "OPTIONS", "GET", "PUT", "DELETE"
- };
-
-
- @Override
- public HttpUriRequest getRedirect(HttpRequest request, HttpResponse response, HttpContext context) throws ProtocolException {
- RequestLine line = request.getRequestLine();
-
- String location = getLocation(request, response, context).toString();
- Log.i(TAG, "Following redirection: " + line.getMethod() + " " + line.getUri() + " -> " + location);
-
- return RequestBuilder.copy(request)
- .setUri(location)
- .removeHeaders("Content-Length") // Content-Length will be set again automatically, if required;
- // remove it now to avoid duplicate header
- .build();
- }
-
- /**
- * Determines whether a response indicates a redirection and if it does, whether to follow this redirection.
- * PROPFIND and REPORT must handle redirections explicitely because multi-status processing requires knowledge of the content location.
- * @return true for 3xx responses on OPTIONS, GET, PUT, DELETE requests that have a valid Location header; false otherwise
- */
- @Override
- public boolean isRedirected(HttpRequest request, HttpResponse response, HttpContext context) throws ProtocolException {
- if (response.getStatusLine().getStatusCode()/100 == 3) {
- boolean redirectable = false;
- for (String method : REDIRECTABLE_METHODS)
- if (method.equalsIgnoreCase(request.getRequestLine().getMethod())) {
- redirectable = true;
- break;
- }
- return redirectable && getLocation(request, response, context) != null;
- }
- return false;
- }
-
- /**
- * Gets the destination of a redirection
- * @return absolute URL of new location; null if not available
- */
- static URL getLocation(HttpRequest request, HttpResponse response, HttpContext context) {
- Header locationHdr = response.getFirstHeader("Location");
- if (locationHdr == null) {
- Log.e(TAG, "Received redirection without Location header, ignoring");
- return null;
- }
- try {
- URI location = new URI(locationHdr.getValue());
-
- // some servers don't return absolute URLs as required by RFC 2616
- if (!location.isAbsolute()) {
- Log.w(TAG, "Received invalid redirection to relative URL, repairing");
- URI originalURI = new URI(request.getRequestLine().getUri());
- if (!originalURI.isAbsolute()) {
- final HttpHost target = HttpClientContext.adapt(context).getTargetHost();
- if (target != null)
- originalURI = URIUtils.rewriteURI(originalURI, target);
- else
- return null;
- }
- return new URL(originalURI.toURL(), location.toString());
- }
- return location.toURL();
- } catch (URISyntaxException e) {
- Log.e(TAG, "Received redirection from/to invalid URI, ignoring", e);
- } catch (MalformedURLException e) {
- Log.e(TAG, "Received redirection from/to invalid URL, ignoring", e);
- }
- return null;
- }
-
-}
diff --git a/src/at/bitfire/davdroid/webdav/DavResponse.java b/src/at/bitfire/davdroid/webdav/DavResponse.java
deleted file mode 100644
index b1ba1fd9..00000000
--- a/src/at/bitfire/davdroid/webdav/DavResponse.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 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.webdav;
-
-import java.util.List;
-
-import lombok.Getter;
-
-import org.simpleframework.xml.Element;
-import org.simpleframework.xml.ElementList;
-import org.simpleframework.xml.Root;
-
-@Root(strict=false)
-public class DavResponse {
- @Element
- @Getter DavHref href;
-
- @ElementList(inline=true)
- @Getter List propstat;
-}
diff --git a/src/at/bitfire/davdroid/webdav/HttpException.java b/src/at/bitfire/davdroid/webdav/HttpException.java
deleted file mode 100644
index d2efd1b7..00000000
--- a/src/at/bitfire/davdroid/webdav/HttpException.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 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.webdav;
-
-import lombok.Getter;
-
-public class HttpException extends ch.boye.httpclientandroidlib.HttpException {
- private static final long serialVersionUID = -4805778240079377401L;
-
- @Getter private int code;
-
- HttpException(int code, String message) {
- super(message);
- this.code = code;
- }
-
- public boolean isClientError() {
- return code/100 == 4;
- }
-
-}
diff --git a/src/at/bitfire/davdroid/webdav/HttpPropfind.java b/src/at/bitfire/davdroid/webdav/HttpPropfind.java
deleted file mode 100644
index 0e6e211a..00000000
--- a/src/at/bitfire/davdroid/webdav/HttpPropfind.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 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.webdav;
-
-import java.io.StringWriter;
-import java.net.URI;
-import java.util.LinkedList;
-
-import org.simpleframework.xml.Serializer;
-import org.simpleframework.xml.core.Persister;
-
-import android.util.Log;
-import ch.boye.httpclientandroidlib.client.methods.HttpEntityEnclosingRequestBase;
-import ch.boye.httpclientandroidlib.entity.StringEntity;
-
-public class HttpPropfind extends HttpEntityEnclosingRequestBase {
- private static final String TAG = "davdroid.HttpPropfind";
-
- public final static String METHOD_NAME = "PROPFIND";
-
- public enum Mode {
- CURRENT_USER_PRINCIPAL,
- HOME_SETS,
- CARDDAV_COLLECTIONS,
- CALDAV_COLLECTIONS,
- COLLECTION_CTAG,
- MEMBERS_ETAG
- }
-
-
- HttpPropfind(URI uri) {
- setURI(uri);
- }
-
- HttpPropfind(URI uri, Mode mode) {
- this(uri);
-
- DavPropfind propfind = new DavPropfind();
- propfind.prop = new DavProp();
-
- int depth = 0;
- switch (mode) {
- case CURRENT_USER_PRINCIPAL:
- propfind.prop.currentUserPrincipal = new DavProp.CurrentUserPrincipal();
- break;
- case HOME_SETS:
- propfind.prop.addressbookHomeSet = new DavProp.AddressbookHomeSet();
- propfind.prop.calendarHomeSet = new DavProp.CalendarHomeSet();
- break;
- case CARDDAV_COLLECTIONS:
- depth = 1;
- propfind.prop.displayname = new DavProp.DisplayName();
- propfind.prop.resourcetype = new DavProp.ResourceType();
- propfind.prop.currentUserPrivilegeSet = new LinkedList();
- propfind.prop.addressbookDescription = new DavProp.AddressbookDescription();
- propfind.prop.supportedAddressData = new LinkedList();
- break;
- case CALDAV_COLLECTIONS:
- depth = 1;
- propfind.prop.displayname = new DavProp.DisplayName();
- propfind.prop.resourcetype = new DavProp.ResourceType();
- propfind.prop.currentUserPrivilegeSet = new LinkedList();
- propfind.prop.calendarDescription = new DavProp.CalendarDescription();
- propfind.prop.calendarColor = new DavProp.CalendarColor();
- propfind.prop.calendarTimezone = new DavProp.CalendarTimezone();
- propfind.prop.supportedCalendarComponentSet = new LinkedList();
- break;
- case COLLECTION_CTAG:
- propfind.prop.getctag = new DavProp.GetCTag();
- break;
- case MEMBERS_ETAG:
- depth = 1;
- propfind.prop.getctag = new DavProp.GetCTag();
- propfind.prop.getetag = new DavProp.GetETag();
- break;
- }
-
- try {
- Serializer serializer = new Persister();
- StringWriter writer = new StringWriter();
- serializer.write(propfind, writer);
-
- setHeader("Content-Type", "text/xml; charset=UTF-8");
- setHeader("Accept", "text/xml");
- setHeader("Depth", String.valueOf(depth));
- setEntity(new StringEntity(writer.toString(), "UTF-8"));
- } catch(Exception ex) {
- Log.e(TAG, "Couldn't prepare PROPFIND request for " + uri, ex);
- abort();
- }
- }
-
- @Override
- public String getMethod() {
- return METHOD_NAME;
- }
-}
diff --git a/src/at/bitfire/davdroid/webdav/HttpReport.java b/src/at/bitfire/davdroid/webdav/HttpReport.java
deleted file mode 100644
index 044deba5..00000000
--- a/src/at/bitfire/davdroid/webdav/HttpReport.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 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.webdav;
-
-import java.net.URI;
-
-import ch.boye.httpclientandroidlib.client.methods.HttpEntityEnclosingRequestBase;
-import ch.boye.httpclientandroidlib.entity.StringEntity;
-
-public class HttpReport extends HttpEntityEnclosingRequestBase {
-
- public final static String METHOD_NAME = "REPORT";
-
-
- HttpReport(URI uri) {
- setURI(uri);
- }
-
- HttpReport(URI uri, String entity) {
- this(uri);
-
- setHeader("Content-Type", "text/xml; charset=UTF-8");
- setHeader("Accept", "text/xml");
- setHeader("Depth", "0");
-
- setEntity(new StringEntity(entity, "UTF-8"));
- }
-
- @Override
- public String getMethod() {
- return METHOD_NAME;
- }
-}
diff --git a/src/at/bitfire/davdroid/webdav/NotAuthorizedException.java b/src/at/bitfire/davdroid/webdav/NotAuthorizedException.java
deleted file mode 100644
index 4eab6adb..00000000
--- a/src/at/bitfire/davdroid/webdav/NotAuthorizedException.java
+++ /dev/null
@@ -1,19 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 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.webdav;
-
-import org.apache.http.HttpStatus;
-
-public class NotAuthorizedException extends HttpException {
- private static final long serialVersionUID = 2490525047224413586L;
-
- public NotAuthorizedException(String reason) {
- super(HttpStatus.SC_UNAUTHORIZED, reason);
- }
-
-}
diff --git a/src/at/bitfire/davdroid/webdav/NotFoundException.java b/src/at/bitfire/davdroid/webdav/NotFoundException.java
deleted file mode 100644
index f612d733..00000000
--- a/src/at/bitfire/davdroid/webdav/NotFoundException.java
+++ /dev/null
@@ -1,18 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 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.webdav;
-
-import org.apache.http.HttpStatus;
-
-public class NotFoundException extends HttpException {
- private static final long serialVersionUID = 1565961502781880483L;
-
- public NotFoundException(String reason) {
- super(HttpStatus.SC_NOT_FOUND, reason);
- }
-}
diff --git a/src/at/bitfire/davdroid/webdav/PreconditionFailedException.java b/src/at/bitfire/davdroid/webdav/PreconditionFailedException.java
deleted file mode 100644
index 064ff38a..00000000
--- a/src/at/bitfire/davdroid/webdav/PreconditionFailedException.java
+++ /dev/null
@@ -1,18 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 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.webdav;
-
-import org.apache.http.HttpStatus;
-
-public class PreconditionFailedException extends HttpException {
- private static final long serialVersionUID = 102282229174086113L;
-
- public PreconditionFailedException(String reason) {
- super(HttpStatus.SC_PRECONDITION_FAILED, reason);
- }
-}
diff --git a/src/at/bitfire/davdroid/webdav/TlsSniSocketFactory.java b/src/at/bitfire/davdroid/webdav/TlsSniSocketFactory.java
deleted file mode 100644
index ce5ec2dd..00000000
--- a/src/at/bitfire/davdroid/webdav/TlsSniSocketFactory.java
+++ /dev/null
@@ -1,186 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 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.webdav;
-
-import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.net.Socket;
-import java.net.UnknownHostException;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
-import javax.net.ssl.HostnameVerifier;
-import javax.net.ssl.SSLPeerUnverifiedException;
-import javax.net.ssl.SSLSession;
-import javax.net.ssl.SSLSocket;
-import org.apache.commons.lang.StringUtils;
-import android.annotation.SuppressLint;
-import android.annotation.TargetApi;
-import android.net.SSLCertificateSocketFactory;
-import android.os.Build;
-import android.util.Log;
-import ch.boye.httpclientandroidlib.HttpHost;
-import ch.boye.httpclientandroidlib.conn.socket.LayeredConnectionSocketFactory;
-import ch.boye.httpclientandroidlib.conn.ssl.BrowserCompatHostnameVerifier;
-import ch.boye.httpclientandroidlib.protocol.HttpContext;
-
-public class TlsSniSocketFactory implements LayeredConnectionSocketFactory {
- private static final String TAG = "davdroid.SNISocketFactory";
-
- final static TlsSniSocketFactory INSTANCE = new TlsSniSocketFactory();
-
- private final static SSLCertificateSocketFactory sslSocketFactory =
- (SSLCertificateSocketFactory)SSLCertificateSocketFactory.getDefault(0);
- private final static HostnameVerifier hostnameVerifier = new BrowserCompatHostnameVerifier();
-
-
- /*
- For SSL connections without HTTP(S) proxy:
- 1) createSocket() is called
- 2) connectSocket() is called which creates a new SSL connection
- 2a) SNI is set up, and then
- 2b) the connection is established, hands are shaken and certificate/host name are verified
-
- Layered sockets are used with HTTP(S) proxies:
- 1) a new plain socket is created by the HTTP library
- 2) the plain socket is connected to http://proxy:8080
- 3) a CONNECT request is sent to the proxy and the response is parsed
- 4) now, createLayeredSocket() is called which wraps an SSL socket around the proxy connection,
- doing all the set-up and verfication
- 4a) Because SSLSocket.createSocket(socket, ...) always does a handshake without allowing
- to set up SNI before, *** SNI is not available for layered connections *** (unless
- active by Android's defaults, which it isn't at the moment).
- */
-
-
- @Override
- public Socket createSocket(HttpContext context) throws IOException {
- SSLSocket ssl = (SSLSocket)sslSocketFactory.createSocket();
- setReasonableEncryption(ssl);
- return ssl;
- }
-
- @Override
- public Socket connectSocket(int timeout, Socket plain, HttpHost host, InetSocketAddress remoteAddr, InetSocketAddress localAddr, HttpContext context) throws IOException {
- Log.d(TAG, "Preparing direct SSL connection (without proxy) to " + host);
-
- // we'll rather use an SSLSocket directly
- plain.close();
-
- // create a plain SSL socket, but don't do hostname/certificate verification yet
- SSLSocket ssl = (SSLSocket)sslSocketFactory.createSocket(remoteAddr.getAddress(), host.getPort());
- setReasonableEncryption(ssl);
-
- // connect, set SNI, shake hands, verify, print connection info
- connectWithSNI(ssl, host.getHostName());
-
- return ssl;
- }
-
- @Override
- public Socket createLayeredSocket(Socket plain, String host, int port, HttpContext context) throws IOException, UnknownHostException {
- Log.d(TAG, "Preparing layered SSL connection (over proxy) to " + host);
-
- // create a layered SSL socket, but don't do hostname/certificate verification yet
- SSLSocket ssl = (SSLSocket)sslSocketFactory.createSocket(plain, host, port, true);
- setReasonableEncryption(ssl);
-
- // already connected, but verify host name again and print some connection info
- Log.w(TAG, "Setting SNI/TLSv1.2 will silently fail because the handshake is already done");
- connectWithSNI(ssl, host);
-
- return ssl;
- }
-
-
- @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
- private void connectWithSNI(SSLSocket ssl, String host) throws SSLPeerUnverifiedException {
- // - set SNI host name
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
- Log.d(TAG, "Using documented SNI with host name " + host);
- sslSocketFactory.setHostname(ssl, host);
- } else {
- Log.d(TAG, "No documented SNI support on Android <4.2, trying with reflection");
- try {
- java.lang.reflect.Method setHostnameMethod = ssl.getClass().getMethod("setHostname", String.class);
- setHostnameMethod.invoke(ssl, host);
- } catch (Exception e) {
- Log.w(TAG, "SNI not useable", e);
- }
- }
-
- // verify hostname and certificate
- SSLSession session = ssl.getSession();
- if (!hostnameVerifier.verify(host, session))
- throw new SSLPeerUnverifiedException("Cannot verify hostname: " + host);
-
- Log.d(TAG, "Established " + session.getProtocol() + " connection with " + session.getPeerHost() +
- " using " + session.getCipherSuite());
- }
-
-
- @SuppressLint("DefaultLocale")
- private void setReasonableEncryption(SSLSocket ssl) {
- // set reasonable SSL/TLS settings before the handshake
-
- // Android 5.0+ (API level21) provides reasonable default settings
- // but it still allows SSLv3
- // https://developer.android.com/about/versions/android-5.0-changes.html#ssl
-
- // - enable all supported protocols (enables TLSv1.1 and TLSv1.2 on Android <5.0, if available)
- // - remove all SSL versions (especially SSLv3) because they're insecure now
- List protocols = new LinkedList();
- for (String protocol : ssl.getSupportedProtocols())
- if (!protocol.toUpperCase().contains("SSL"))
- protocols.add(protocol);
- Log.v(TAG, "Setting allowed TLS protocols: " + StringUtils.join(protocols, ", "));
- ssl.setEnabledProtocols(protocols.toArray(new String[0]));
-
- if (android.os.Build.VERSION.SDK_INT < 21) {
- // choose secure cipher suites
- List allowedCiphers = Arrays.asList(new String[] {
- // allowed secure ciphers according to NIST.SP.800-52r1.pdf Section 3.3.1 (see docs directory)
- // TLS 1.2
- "TLS_RSA_WITH_AES_256_GCM_SHA384",
- "TLS_RSA_WITH_AES_128_GCM_SHA256",
- "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
- "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
- "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
- "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
- "TLS_ECHDE_RSA_WITH_AES_128_GCM_SHA256",
- // maximum interoperability
- "TLS_RSA_WITH_3DES_EDE_CBC_SHA",
- "TLS_RSA_WITH_AES_128_CBC_SHA",
- // additionally
- "TLS_RSA_WITH_AES_256_CBC_SHA",
- "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA",
- "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
- "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
- "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
- });
-
- List availableCiphers = Arrays.asList(ssl.getSupportedCipherSuites());
-
- // preferred ciphers = allowed Ciphers \ availableCiphers
- HashSet preferredCiphers = new HashSet(allowedCiphers);
- preferredCiphers.retainAll(availableCiphers);
-
- // add preferred ciphers to enabled ciphers
- // for maximum security, preferred ciphers should *replace* enabled ciphers,
- // but I guess for the security level of DAVdroid, disabling of insecure
- // ciphers should be a server-side task
- HashSet enabledCiphers = preferredCiphers;
- enabledCiphers.addAll(new HashSet(Arrays.asList(ssl.getEnabledCipherSuites())));
-
- Log.v(TAG, "Setting allowed TLS ciphers: " + StringUtils.join(enabledCiphers, ", "));
- ssl.setEnabledCipherSuites(enabledCiphers.toArray(new String[0]));
- }
- }
-
-}
diff --git a/src/at/bitfire/davdroid/webdav/WebDavResource.java b/src/at/bitfire/davdroid/webdav/WebDavResource.java
deleted file mode 100644
index 92859b7f..00000000
--- a/src/at/bitfire/davdroid/webdav/WebDavResource.java
+++ /dev/null
@@ -1,584 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 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.webdav;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.StringWriter;
-import java.net.MalformedURLException;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Set;
-
-import lombok.Cleanup;
-import lombok.Getter;
-import lombok.ToString;
-
-import org.apache.commons.lang.StringUtils;
-import org.simpleframework.xml.Serializer;
-import org.simpleframework.xml.core.Persister;
-
-import android.util.Log;
-import at.bitfire.davdroid.URLUtils;
-import at.bitfire.davdroid.resource.Event;
-import at.bitfire.davdroid.webdav.DavProp.Comp;
-import ch.boye.httpclientandroidlib.Header;
-import ch.boye.httpclientandroidlib.HttpEntity;
-import ch.boye.httpclientandroidlib.HttpHost;
-import ch.boye.httpclientandroidlib.HttpResponse;
-import ch.boye.httpclientandroidlib.HttpStatus;
-import ch.boye.httpclientandroidlib.StatusLine;
-import ch.boye.httpclientandroidlib.auth.AuthScope;
-import ch.boye.httpclientandroidlib.auth.UsernamePasswordCredentials;
-import ch.boye.httpclientandroidlib.client.AuthCache;
-import ch.boye.httpclientandroidlib.client.methods.CloseableHttpResponse;
-import ch.boye.httpclientandroidlib.client.methods.HttpDelete;
-import ch.boye.httpclientandroidlib.client.methods.HttpGet;
-import ch.boye.httpclientandroidlib.client.methods.HttpOptions;
-import ch.boye.httpclientandroidlib.client.methods.HttpPut;
-import ch.boye.httpclientandroidlib.client.protocol.HttpClientContext;
-import ch.boye.httpclientandroidlib.entity.ByteArrayEntity;
-import ch.boye.httpclientandroidlib.impl.auth.BasicScheme;
-import ch.boye.httpclientandroidlib.impl.client.BasicAuthCache;
-import ch.boye.httpclientandroidlib.impl.client.BasicCredentialsProvider;
-import ch.boye.httpclientandroidlib.impl.client.CloseableHttpClient;
-import ch.boye.httpclientandroidlib.message.BasicLineParser;
-import ch.boye.httpclientandroidlib.util.EntityUtils;
-import ezvcard.VCardVersion;
-
-
-/**
- * Represents a WebDAV resource (file or collection).
- * This class is used for all CalDAV/CardDAV communcation.
- */
-@ToString
-public class WebDavResource {
- private static final String TAG = "davdroid.WebDavResource";
-
- public enum Property {
- CURRENT_USER_PRINCIPAL, // resource detection
- ADDRESSBOOK_HOMESET, CALENDAR_HOMESET,
- CONTENT_TYPE, READ_ONLY, // WebDAV (common)
- DISPLAY_NAME, DESCRIPTION, ETAG,
- IS_COLLECTION, CTAG, // collections
- IS_CALENDAR, COLOR, TIMEZONE, // CalDAV
- IS_ADDRESSBOOK, VCARD_VERSION // CardDAV
- }
- public enum PutMode {
- ADD_DONT_OVERWRITE,
- UPDATE_DONT_OVERWRITE
- }
-
- // location of this resource
- @Getter protected URL location;
-
- // DAV capabilities (DAV: header) and allowed DAV methods (set for OPTIONS request)
- protected Set capabilities = new HashSet(),
- methods = new HashSet();
-
- // DAV properties
- protected HashMap properties = new HashMap();
- @Getter protected List supportedComponents;
-
- // list of members (only for collections)
- @Getter protected List members;
-
- // content (available after GET)
- @Getter protected byte[] content;
-
- protected CloseableHttpClient httpClient;
- protected HttpClientContext context;
-
-
- public WebDavResource(CloseableHttpClient httpClient, URL baseURL) {
- this.httpClient = httpClient;
- location = baseURL;
-
- context = HttpClientContext.create();
- context.setCredentialsProvider(new BasicCredentialsProvider());
- }
-
- public WebDavResource(CloseableHttpClient httpClient, URL baseURL, String username, String password, boolean preemptive) {
- this(httpClient, baseURL);
-
- HttpHost host = new HttpHost(baseURL.getHost(), baseURL.getPort(), baseURL.getProtocol());
- context.getCredentialsProvider().setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));
-
- if (preemptive) {
- Log.d(TAG, "Using preemptive authentication (not compatible with Digest auth)");
- AuthCache authCache = context.getAuthCache();
- if (authCache == null)
- authCache = new BasicAuthCache();
- authCache.put(host, new BasicScheme());
- context.setAuthCache(authCache);
- }
- }
-
- WebDavResource(WebDavResource parent) { // copy constructor: based on existing WebDavResource, reuse settings
- httpClient = parent.httpClient;
- context = parent.context;
- location = parent.location;
- }
-
- protected WebDavResource(WebDavResource parent, URL url) {
- this(parent);
- location = url;
- }
-
- public WebDavResource(WebDavResource parent, String member) throws MalformedURLException {
- this(parent);
- location = new URL(parent.location, URLUtils.sanitize(member));
- }
-
- public WebDavResource(WebDavResource parent, String member, String ETag) throws MalformedURLException {
- this(parent, member);
- properties.put(Property.ETAG, ETag);
- }
-
-
-
- /* feature detection */
-
- public void options() throws URISyntaxException, IOException, HttpException {
- HttpOptions options = new HttpOptions(location.toURI());
- CloseableHttpResponse response = httpClient.execute(options, context);
- try {
- checkResponse(response);
-
- Header[] allowHeaders = response.getHeaders("Allow");
- for (Header allowHeader : allowHeaders)
- methods.addAll(Arrays.asList(allowHeader.getValue().split(", ?")));
-
- Header[] capHeaders = response.getHeaders("DAV");
- for (Header capHeader : capHeaders)
- capabilities.addAll(Arrays.asList(capHeader.getValue().split(", ?")));
- } finally {
- response.close();
- }
- }
-
- public boolean supportsDAV(String capability) {
- return capabilities.contains(capability);
- }
-
- public boolean supportsMethod(String method) {
- return methods.contains(method);
- }
-
-
- /* file hierarchy methods */
-
- public String getName() {
- String[] names = StringUtils.split(location.getPath(), "/");
- return names[names.length - 1];
- }
-
-
- /* property methods */
-
- public String getCurrentUserPrincipal() {
- return properties.get(Property.CURRENT_USER_PRINCIPAL);
- }
-
- public String getAddressbookHomeSet() {
- return properties.get(Property.ADDRESSBOOK_HOMESET);
- }
-
- public String getCalendarHomeSet() {
- return properties.get(Property.CALENDAR_HOMESET);
- }
-
- public String getContentType() {
- return properties.get(Property.CONTENT_TYPE);
- }
-
- public void setContentType(String mimeType) {
- properties.put(Property.CONTENT_TYPE, mimeType);
- }
-
- public boolean isReadOnly() {
- return properties.containsKey(Property.READ_ONLY);
- }
-
- public String getDisplayName() {
- return properties.get(Property.DISPLAY_NAME);
- }
-
- public String getDescription() {
- return properties.get(Property.DESCRIPTION);
- }
-
- public String getCTag() {
- return properties.get(Property.CTAG);
- }
- public void invalidateCTag() {
- properties.remove(Property.CTAG);
- }
-
- public String getETag() {
- return properties.get(Property.ETAG);
- }
-
- public boolean isCalendar() {
- return properties.containsKey(Property.IS_CALENDAR);
- }
-
- public String getColor() {
- return properties.get(Property.COLOR);
- }
-
- public String getTimezone() {
- return properties.get(Property.TIMEZONE);
- }
-
- public boolean isAddressBook() {
- return properties.containsKey(Property.IS_ADDRESSBOOK);
- }
-
- public VCardVersion getVCardVersion() {
- String versionStr = properties.get(Property.VCARD_VERSION);
- return (versionStr != null) ? VCardVersion.valueOfByStr(versionStr) : null;
- }
-
-
- /* collection operations */
-
- public void propfind(HttpPropfind.Mode mode) throws URISyntaxException, IOException, DavException, HttpException {
- CloseableHttpResponse response = null;
-
- // processMultiStatus() requires knowledge of the actual content location,
- // so we have to handle redirections manually and create a new request for the new location
- for (int i = context.getRequestConfig().getMaxRedirects(); i > 0; i--) {
- HttpPropfind propfind = new HttpPropfind(location.toURI(), mode);
- response = httpClient.execute(propfind, context);
-
- if (response.getStatusLine().getStatusCode()/100 == 3) {
- location = DavRedirectStrategy.getLocation(propfind, response, context);
- Log.i(TAG, "Redirection on PROPFIND; trying again at new content URL: " + location);
- // don't forget to throw away the unneeded response content
- HttpEntity entity = response.getEntity();
- if (entity != null) { @Cleanup InputStream content = entity.getContent(); }
- } else
- break; // answer was NOT a redirection, continue
- }
- if (response == null)
- throw new DavNoContentException();
-
- try {
- checkResponse(response); // will also handle Content-Location
- processMultiStatus(response);
- } finally {
- response.close();
- }
- }
-
- public void multiGet(DavMultiget.Type type, String[] names) throws URISyntaxException, IOException, DavException, HttpException {
- CloseableHttpResponse response = null;
-
- // processMultiStatus() requires knowledge of the actual content location,
- // so we have to handle redirections manually and create a new request for the new location
- for (int i = context.getRequestConfig().getMaxRedirects(); i > 0; i--) {
- // build multi-get XML request
- List hrefs = new LinkedList();
- for (String name : names)
- hrefs.add(new URL(location, name).getPath());
- DavMultiget multiget = DavMultiget.newRequest(type, hrefs.toArray(new String[0]));
-
- StringWriter writer = new StringWriter();
- try {
- Serializer serializer = new Persister();
- serializer.write(multiget, writer);
- } catch (Exception ex) {
- Log.e(TAG, "Couldn't create XML multi-get request", ex);
- throw new DavException("Couldn't create multi-get request");
- }
-
- // submit REPORT request
- HttpReport report = new HttpReport(location.toURI(), writer.toString());
- response = httpClient.execute(report, context);
-
- if (response.getStatusLine().getStatusCode()/100 == 3) {
- location = DavRedirectStrategy.getLocation(report, response, context);
- Log.i(TAG, "Redirection on REPORT multi-get; trying again at new content URL: " + location);
-
- // don't forget to throw away the unneeded response content
- HttpEntity entity = response.getEntity();
- if (entity != null) { @Cleanup InputStream content = entity.getContent(); }
- } else
- break; // answer was NOT a redirection, continue
- }
- if (response == null)
- throw new DavNoContentException();
-
- try {
- checkResponse(response); // will also handle Content-Location
- processMultiStatus(response);
- } finally {
- response.close();
- }
- }
-
-
- /* resource operations */
-
- public void get(String acceptedType) throws URISyntaxException, IOException, HttpException, DavException {
- HttpGet get = new HttpGet(location.toURI());
- get.addHeader("Accept", acceptedType);
-
- CloseableHttpResponse response = httpClient.execute(get, context);
- try {
- checkResponse(response);
-
- HttpEntity entity = response.getEntity();
- if (entity == null)
- throw new DavNoContentException();
-
- content = EntityUtils.toByteArray(entity);
- } finally {
- response.close();
- }
- }
-
- // returns the ETag of the created/updated resource, if available (null otherwise)
- public String put(byte[] data, PutMode mode) throws URISyntaxException, IOException, HttpException {
- HttpPut put = new HttpPut(location.toURI());
- put.setEntity(new ByteArrayEntity(data));
-
- switch (mode) {
- case ADD_DONT_OVERWRITE:
- put.addHeader("If-None-Match", "*");
- break;
- case UPDATE_DONT_OVERWRITE:
- put.addHeader("If-Match", (getETag() != null) ? getETag() : "*");
- break;
- }
-
- if (getContentType() != null)
- put.addHeader("Content-Type", getContentType());
-
- CloseableHttpResponse response = httpClient.execute(put, context);
- try {
- checkResponse(response);
-
- Header eTag = response.getLastHeader("ETag");
- if (eTag != null)
- return eTag.getValue();
- } finally {
- response.close();
- }
-
- return null;
- }
-
- public void delete() throws URISyntaxException, IOException, HttpException {
- HttpDelete delete = new HttpDelete(location.toURI());
-
- if (getETag() != null)
- delete.addHeader("If-Match", getETag());
-
- CloseableHttpResponse response = httpClient.execute(delete, context);
- try {
- checkResponse(response);
- } finally {
- response.close();
- }
- }
-
-
- /* helpers */
-
- protected void checkResponse(HttpResponse response) throws HttpException {
- checkResponse(response.getStatusLine());
-
- // handle Content-Location header (see RFC 4918 5.2 Collection Resources)
- Header contentLocationHdr = response.getFirstHeader("Content-Location");
- if (contentLocationHdr != null)
- try {
- // Content-Location was set, update location correspondingly
- location = new URL(location, contentLocationHdr.getValue());
- Log.d(TAG, "Set Content-Location to " + location);
- } catch (MalformedURLException e) {
- Log.w(TAG, "Ignoring invalid Content-Location", e);
- }
- }
-
- protected static void checkResponse(StatusLine statusLine) throws HttpException {
- int code = statusLine.getStatusCode();
-
- if (code/100 == 1 || code/100 == 2) // everything OK
- return;
-
- String reason = code + " " + statusLine.getReasonPhrase();
- switch (code) {
- case HttpStatus.SC_UNAUTHORIZED:
- throw new NotAuthorizedException(reason);
- case HttpStatus.SC_NOT_FOUND:
- throw new NotFoundException(reason);
- case HttpStatus.SC_PRECONDITION_FAILED:
- throw new PreconditionFailedException(reason);
- default:
- throw new HttpException(code, reason);
- }
- }
-
- protected void processMultiStatus(HttpResponse response) throws IOException, HttpException, DavException {
- if (response.getStatusLine().getStatusCode() != HttpStatus.SC_MULTI_STATUS)
- throw new DavNoMultiStatusException();
-
- HttpEntity entity = response.getEntity();
- if (entity == null)
- throw new DavNoContentException();
- @Cleanup InputStream content = entity.getContent();
-
- DavMultistatus multiStatus;
- try {
- Serializer serializer = new Persister();
- multiStatus = serializer.read(DavMultistatus.class, content, false);
- } catch (Exception ex) {
- throw new DavException("Couldn't parse Multi-Status response on REPORT multi-get", ex);
- }
-
- if (multiStatus.response == null) // empty response
- throw new DavNoContentException();
-
- // member list will be built from response
- List members = new LinkedList();
-
- // iterate through all resources (either ourselves or member)
- for (DavResponse singleResponse : multiStatus.response) {
- URL href;
- try {
- href = new URL(location, URLUtils.sanitize(singleResponse.getHref().href));
- } catch(IllegalArgumentException ex) {
- Log.w(TAG, "Ignoring illegal member URI in multi-status response", ex);
- continue;
- }
- Log.d(TAG, "Processing multi-status element: " + href);
-
- // process known properties
- HashMap properties = new HashMap();
- List supportedComponents = null;
- byte[] data = null;
-
- for (DavPropstat singlePropstat : singleResponse.getPropstat()) {
- StatusLine status = BasicLineParser.parseStatusLine(singlePropstat.status, new BasicLineParser());
-
- // ignore information about missing properties etc.
- if (status.getStatusCode()/100 != 1 && status.getStatusCode()/100 != 2)
- continue;
- DavProp prop = singlePropstat.prop;
-
- if (prop.currentUserPrincipal != null && prop.currentUserPrincipal.getHref() != null)
- properties.put(Property.CURRENT_USER_PRINCIPAL, prop.currentUserPrincipal.getHref().href);
-
- if (prop.currentUserPrivilegeSet != null) {
- // privilege info available
- boolean mayAll = false,
- mayBind = false,
- mayUnbind = false,
- mayWrite = false,
- mayWriteContent = false;
- for (DavProp.Privilege privilege : prop.currentUserPrivilegeSet) {
- if (privilege.getAll() != null) mayAll = true;
- if (privilege.getBind() != null) mayBind = true;
- if (privilege.getUnbind() != null) mayUnbind = true;
- if (privilege.getWrite() != null) mayWrite = true;
- if (privilege.getWriteContent() != null) mayWriteContent = true;
- }
- if (!mayAll && !mayWrite && !(mayWriteContent && mayBind && mayUnbind))
- properties.put(Property.READ_ONLY, "1");
- }
-
- if (prop.addressbookHomeSet != null && prop.addressbookHomeSet.getHref() != null)
- properties.put(Property.ADDRESSBOOK_HOMESET, URLUtils.ensureTrailingSlash(prop.addressbookHomeSet.getHref().href));
-
- if (prop.calendarHomeSet != null && prop.calendarHomeSet.getHref() != null)
- properties.put(Property.CALENDAR_HOMESET, URLUtils.ensureTrailingSlash(prop.calendarHomeSet.getHref().href));
-
- if (prop.displayname != null)
- properties.put(Property.DISPLAY_NAME, prop.displayname.getDisplayName());
-
- if (prop.resourcetype != null) {
- if (prop.resourcetype.getCollection() != null) {
- properties.put(Property.IS_COLLECTION, "1");
- // is a collection, ensure trailing slash
- href = URLUtils.ensureTrailingSlash(href);
- }
- if (prop.resourcetype.getAddressbook() != null) { // CardDAV collection properties
- properties.put(Property.IS_ADDRESSBOOK, "1");
-
- if (prop.addressbookDescription != null)
- properties.put(Property.DESCRIPTION, prop.addressbookDescription.getDescription());
- if (prop.supportedAddressData != null)
- for (DavProp.AddressDataType dataType : prop.supportedAddressData)
- if ("text/vcard".equalsIgnoreCase(dataType.getContentType()))
- // ignore "3.0" as it MUST be supported anyway
- if ("4.0".equals(dataType.getVersion()))
- properties.put(Property.VCARD_VERSION, VCardVersion.V4_0.getVersion());
- }
- if (prop.resourcetype.getCalendar() != null) { // CalDAV collection propertioes
- properties.put(Property.IS_CALENDAR, "1");
-
- if (prop.calendarDescription != null)
- properties.put(Property.DESCRIPTION, prop.calendarDescription.getDescription());
-
- if (prop.calendarColor != null)
- properties.put(Property.COLOR, prop.calendarColor.getColor());
-
- if (prop.calendarTimezone != null)
- try {
- properties.put(Property.TIMEZONE, Event.TimezoneDefToTzId(prop.calendarTimezone.getTimezone()));
- } catch(IllegalArgumentException e) {
- }
-
- if (prop.supportedCalendarComponentSet != null) {
- supportedComponents = new LinkedList();
- for (Comp component : prop.supportedCalendarComponentSet)
- supportedComponents.add(component.getName());
- }
- }
- }
-
- if (prop.getctag != null)
- properties.put(Property.CTAG, prop.getctag.getCTag());
-
- if (prop.getetag != null)
- properties.put(Property.ETAG, prop.getetag.getETag());
-
- if (prop.calendarData != null && prop.calendarData.ical != null)
- data = prop.calendarData.ical.getBytes();
- else if (prop.addressData != null && prop.addressData.vcard != null)
- data = prop.addressData.vcard.getBytes();
- }
-
- // about which resource is this response?
- if (location.equals(href) || URLUtils.ensureTrailingSlash(location).equals(href)) { // about ourselves
- this.properties.putAll(properties);
- if (supportedComponents != null)
- this.supportedComponents = supportedComponents;
- this.content = data;
-
- } else { // about a member
- WebDavResource member = new WebDavResource(this, href);
- member.properties = properties;
- member.supportedComponents = supportedComponents;
- member.content = data;
-
- members.add(member);
- }
- }
-
- this.members = members;
- }
-
-}
diff --git a/src/ical4j.properties b/src/ical4j.properties
deleted file mode 100644
index 6db152a4..00000000
--- a/src/ical4j.properties
+++ /dev/null
@@ -1,6 +0,0 @@
-
-net.fortuna.ical4j.timezone.update.enabled=false
-
-ical4j.unfolding.relaxed=true
-ical4j.parsing.relaxed=true
-ical4j.compatibility.outlook=true
diff --git a/test/AndroidManifest.xml b/test/AndroidManifest.xml
deleted file mode 100644
index d63a92b4..00000000
--- a/test/AndroidManifest.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/test/assets/all-day-0sec.ics b/test/assets/all-day-0sec.ics
deleted file mode 100644
index 07679f29..00000000
--- a/test/assets/all-day-0sec.ics
+++ /dev/null
@@ -1,11 +0,0 @@
-BEGIN:VCALENDAR
-VERSION:2.0
-PRODID:-//hacksw/handcal//NONSGML v1.0//EN
-BEGIN:VEVENT
-UID:all-day-0sec@example.com
-DTSTAMP:20140101T000000Z
-DTSTART;VALUE=DATE:19970714
-DTEND;VALUE=DATE:19970714
-SUMMARY:0 Sec Event
-END:VEVENT
-END:VCALENDAR
\ No newline at end of file
diff --git a/test/assets/all-day-10days.ics b/test/assets/all-day-10days.ics
deleted file mode 100644
index 52e6dbdf..00000000
--- a/test/assets/all-day-10days.ics
+++ /dev/null
@@ -1,11 +0,0 @@
-BEGIN:VCALENDAR
-VERSION:2.0
-PRODID:-//hacksw/handcal//NONSGML v1.0//EN
-BEGIN:VEVENT
-UID:all-day-10days@example.com
-DTSTAMP:20140101T000000Z
-DTSTART;VALUE=DATE:19970714
-DTEND;VALUE=DATE:19970724
-SUMMARY:All-Day 10 Days
-END:VEVENT
-END:VCALENDAR
\ No newline at end of file
diff --git a/test/assets/all-day-1day.ics b/test/assets/all-day-1day.ics
deleted file mode 100644
index ead04361..00000000
--- a/test/assets/all-day-1day.ics
+++ /dev/null
@@ -1,11 +0,0 @@
-BEGIN:VCALENDAR
-VERSION:2.0
-PRODID:-//hacksw/handcal//NONSGML v1.0//EN
-BEGIN:VEVENT
-UID:all-day-1day@example.com
-DTSTAMP:20140101T000000Z
-DTSTART;VALUE=DATE:19970714
-DTEND;VALUE=DATE:19970714
-SUMMARY:All-Day 1 Day
-END:VEVENT
-END:VCALENDAR
\ No newline at end of file
diff --git a/test/assets/event-on-that-day.ics b/test/assets/event-on-that-day.ics
deleted file mode 100644
index 0ccbd4f3..00000000
--- a/test/assets/event-on-that-day.ics
+++ /dev/null
@@ -1,11 +0,0 @@
-BEGIN:VCALENDAR
-VERSION:2.0
-PRODID:-//hacksw/handcal//NONSGML v1.0//EN
-BEGIN:VEVENT
-UID:event-on-that-day@example.com
-DTSTAMP:19970714T170000Z
-ORGANIZER;CN=John Doe:MAILTO:john.doe@example.com
-DTSTART;VALUE=DATE:19970714
-SUMMARY:Bastille Day Party
-END:VEVENT
-END:VCALENDAR
\ No newline at end of file
diff --git a/test/assets/impp.vcf b/test/assets/impp.vcf
deleted file mode 100644
index c08f9029..00000000
--- a/test/assets/impp.vcf
+++ /dev/null
@@ -1,9 +0,0 @@
-BEGIN:VCARD
-VERSION:3.0
-UID:2de59c6cc9
-PRODID:-//ownCloud//NONSGML Contacts 0.2.5//EN
-REV:2013-12-08T00:04:30+00:00
-FN:test mctest
-N:mctest;test;;;
-IMPP;TYPE=WORK;X-SERVICE-TYPE=jabber:test-without-valid-scheme@test.tld
-END:VCARD
diff --git a/test/assets/invalid-unknown-properties.vcf b/test/assets/invalid-unknown-properties.vcf
deleted file mode 100644
index 3fd77fce..00000000
--- a/test/assets/invalid-unknown-properties.vcf
+++ /dev/null
@@ -1,5 +0,0 @@
-BEGIN:VCARD
-VERSION:3.0
-FN:VCard with invalid unknown properties
-X-UNKNOWN@PROPERTY:MUST-NOT_CONTAIN?OTHER*LETTERS;
-END:VCARD
\ No newline at end of file
diff --git a/test/assets/reference.vcf b/test/assets/reference.vcf
deleted file mode 100644
index ba6a6b4d..00000000
--- a/test/assets/reference.vcf
+++ /dev/null
@@ -1,16 +0,0 @@
-BEGIN:VCARD
-VERSION:3.0
-N:Gump;Forrest;Mr.
-FN:Forrest Gump
-ORG:Bubba Gump Shrimp Co.
-TITLE:Shrimp Man
-PHOTO;VALUE=URL;TYPE=GIF:http://www.example.com/dir_photos/my_photo.gif
-TEL;TYPE=WORK,VOICE:(111) 555-1212
-TEL;TYPE=HOME,VOICE:(404) 555-1212
-ADR;TYPE=WORK:;;100 Waters Edge;Baytown;LA;30314;United States of America
-LABEL;TYPE=WORK:100 Waters Edge\nBaytown, LA 30314\nUnited States of America
-ADR;TYPE=HOME:;;42 Plantation St.;Baytown;LA;30314;United States of America
-LABEL;TYPE=HOME:42 Plantation St.\nBaytown, LA 30314\nUnited States of America
-EMAIL;TYPE=PREF,INTERNET:forrestgump@example.com
-REV:2008-04-24T19:52:43Z
-END:VCARD
\ No newline at end of file
diff --git a/test/assets/test.random b/test/assets/test.random
deleted file mode 100644
index eb3e5b02..00000000
Binary files a/test/assets/test.random and /dev/null differ
diff --git a/test/assets/vcard3-sample1.vcf b/test/assets/vcard3-sample1.vcf
deleted file mode 100644
index f5c9f977..00000000
--- a/test/assets/vcard3-sample1.vcf
+++ /dev/null
@@ -1,16 +0,0 @@
-BEGIN:VCARD
-VERSION:3.0
-N:Gump;Forrest
-FN:Forrest Gump
-ORG:Bubba Gump Shrimp Co.
-TITLE:Shrimp Man
-PHOTO;VALUE=URL;TYPE=GIF:http://www.example.com/dir_photos/my_photo.gif
-TEL;TYPE=WORK,VOICE:(111) 555-1212
-TEL;TYPE=HOME,VOICE:(404) 555-1212
-ADR;TYPE=WORK:;;100 Waters Edge;Baytown;LA;30314;United States of America
-LABEL;TYPE=WORK:100 Waters Edge\nBaytown, LA 30314\nUnited States of America
-ADR;TYPE=HOME:;;42 Plantation St.;Baytown;LA;30314;United States of America
-LABEL;TYPE=HOME:42 Plantation St.\nBaytown, LA 30314\nUnited States of America
-EMAIL;TYPE=PREF,INTERNET:forrestgump@example.com
-REV:2008-04-24T19:52:43Z
-END:VCARD
\ No newline at end of file
diff --git a/test/assets/vienna-evolution.ics b/test/assets/vienna-evolution.ics
deleted file mode 100644
index 5f11911a..00000000
--- a/test/assets/vienna-evolution.ics
+++ /dev/null
@@ -1,33 +0,0 @@
-BEGIN:VCALENDAR
-PRODID:-//Ximian//NONSGML Evolution Calendar//EN
-VERSION:2.0
-METHOD:PUBLISH
-BEGIN:VTIMEZONE
-TZID:/freeassociation.sourceforge.net/Tzfile/Europe/Vienna
-X-LIC-LOCATION:Europe/Vienna
-BEGIN:STANDARD
-TZNAME:CET
-DTSTART:19701027T030000
-RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
-TZOFFSETFROM:+0200
-TZOFFSETTO:+0100
-END:STANDARD
-BEGIN:DAYLIGHT
-TZNAME:CEST
-DTSTART:19700331T020000
-RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3
-TZOFFSETFROM:+0100
-TZOFFSETTO:+0200
-END:DAYLIGHT
-END:VTIMEZONE
-BEGIN:VEVENT
-UID:c252087c-7354-4722-aea9-0e7d86c01a25
-DTSTAMP:20130926T151211Z
-SUMMARY:Test-Ereignis im schönen Wien
-DTSTART;TZID=/freeassociation.sourceforge.net/Tzfile/Europe/Vienna:20131009T170000
-DTEND;TZID=/freeassociation.sourceforge.net/Tzfile/Europe/Vienna:20131009T180000
-X-RADICALE-NAME:97929342-291a-434e-bf1a-fa1749bf99d0.ics
-X-EVOLUTION-CALDAV-HREF:/radicale/rfc2822/default.ics/97929342-291a-434e-bf1a-fa1749bf99d0.ics
-X-EVOLUTION-CALDAV-ETAG:\"-3264224243575339985\"
-END:VEVENT
-END:VCALENDAR
diff --git a/test/proguard-project.txt b/test/proguard-project.txt
deleted file mode 100644
index f2fe1559..00000000
--- a/test/proguard-project.txt
+++ /dev/null
@@ -1,20 +0,0 @@
-# To enable ProGuard in your project, edit project.properties
-# to define the proguard.config property as described in that file.
-#
-# Add project specific ProGuard rules here.
-# By default, the flags in this file are appended to flags specified
-# in ${sdk.dir}/tools/proguard/proguard-android.txt
-# You can edit the include path and order by changing the ProGuard
-# include property in project.properties.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# Add any project specific keep options here:
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
diff --git a/test/project.properties b/test/project.properties
deleted file mode 100644
index 4ab12569..00000000
--- a/test/project.properties
+++ /dev/null
@@ -1,14 +0,0 @@
-# This file is automatically generated by Android Tools.
-# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
-#
-# This file must be checked in Version Control Systems.
-#
-# To customize properties used by the Ant build system edit
-# "ant.properties", and override values to adapt the script to your
-# project structure.
-#
-# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
-#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
-
-# Project target.
-target=android-19
diff --git a/test/res/drawable-hdpi/ic_launcher.png b/test/res/drawable-hdpi/ic_launcher.png
deleted file mode 100644
index 96a442e5..00000000
Binary files a/test/res/drawable-hdpi/ic_launcher.png and /dev/null differ
diff --git a/test/res/drawable-ldpi/ic_launcher.png b/test/res/drawable-ldpi/ic_launcher.png
deleted file mode 100644
index 99238729..00000000
Binary files a/test/res/drawable-ldpi/ic_launcher.png and /dev/null differ
diff --git a/test/res/drawable-mdpi/ic_launcher.png b/test/res/drawable-mdpi/ic_launcher.png
deleted file mode 100644
index 359047df..00000000
Binary files a/test/res/drawable-mdpi/ic_launcher.png and /dev/null differ
diff --git a/test/res/drawable-xhdpi/ic_launcher.png b/test/res/drawable-xhdpi/ic_launcher.png
deleted file mode 100644
index 71c6d760..00000000
Binary files a/test/res/drawable-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/test/res/values/strings.xml b/test/res/values/strings.xml
deleted file mode 100644
index 2539643a..00000000
--- a/test/res/values/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
- DavdroidTest
-
-
diff --git a/test/robohydra/.gitignore b/test/robohydra/.gitignore
deleted file mode 100644
index 3c3629e6..00000000
--- a/test/robohydra/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-node_modules
diff --git a/test/robohydra/davdroid.conf b/test/robohydra/davdroid.conf
deleted file mode 100644
index 37807fd7..00000000
--- a/test/robohydra/davdroid.conf
+++ /dev/null
@@ -1,6 +0,0 @@
-{"plugins":[
- "assets",
- "redirect",
- "dav",
- "dav-invalid"
-]}
diff --git a/test/robohydra/plugins/assets/index.js b/test/robohydra/plugins/assets/index.js
deleted file mode 100644
index 2a254b9a..00000000
--- a/test/robohydra/plugins/assets/index.js
+++ /dev/null
@@ -1,12 +0,0 @@
-var RoboHydraHeadFilesystem = require("robohydra").heads.RoboHydraHeadFilesystem;
-
-exports.getBodyParts = function(conf) {
- return {
- heads: [
- new RoboHydraHeadFilesystem({
- mountPath: '/assets/',
- documentRoot: '../assets'
- })
- ]
- };
-};
diff --git a/test/robohydra/plugins/dav-invalid/index.js b/test/robohydra/plugins/dav-invalid/index.js
deleted file mode 100644
index b14ee944..00000000
--- a/test/robohydra/plugins/dav-invalid/index.js
+++ /dev/null
@@ -1,51 +0,0 @@
-var roboHydraHeadDAV = require("../headdav");
-
-exports.getBodyParts = function(conf) {
- return {
- heads: [
- /* address-book home set */
- new RoboHydraHeadDAV({
- path: "/dav-invalid/addressbooks/user%40domain/",
- handler: function(req,res,next) {
- if (req.method == "PROPFIND" && req.rawBody.toString().match(/addressbook-description/)) {
- res.statusCode = 207;
- res.write('\\
- \
- \
- /dav/addressbooks/user@domain/My Contacts:1.vcf/\
- \
- \
- \
- \
- \
- \
- \
- Address Book with dubious characters in path\
- \
- \
- HTTP/1.1 200 OK\
- \
- \
- \
- HTTPS://example.com/user@domain/absolute-url.vcf\
- \
- \
- \
- \
- \
- \
- \
- Address Book with absolute URL and at sign in path\
- \
- \
- HTTP/1.1 200 OK\
- \
- \
- \
- ');
- }
- }
- })
- ]
- };
-};
diff --git a/test/robohydra/plugins/dav/index.js b/test/robohydra/plugins/dav/index.js
deleted file mode 100644
index bdf7cee3..00000000
--- a/test/robohydra/plugins/dav/index.js
+++ /dev/null
@@ -1,269 +0,0 @@
-var roboHydraHeadDAV = require("../headdav");
-
-exports.getBodyParts = function(conf) {
- return {
- heads: [
- /* base URL, provide default DAV here */
- new RoboHydraHeadDAV({ path: "/dav/" }),
-
- /* multistatus parsing */
- new RoboHydraHeadDAV({
- path: "/dav/collection-response-with-trailing-slash",
- handler: function(req,res,next) {
- if (req.method == "PROPFIND") {
- res.statusCode = 207;
- res.write('\\
- \
- \
- /dav/collection-response-with-trailing-slash/ \
- \
- \
- \
- /principals/ok\
- \
- \
- \
- \
- \
- HTTP/1.1 200 OK\
- \
- \
- \
- ');
- }
- }
- }),
- new RoboHydraHeadDAV({
- path: "/dav/collection-response-without-trailing-slash",
- handler: function(req,res,next) {
- if (req.method == "PROPFIND") {
- res.statusCode = 207;
- res.write('\\
- \
- \
- /dav/collection-response-without-trailing-slash \
- \
- \
- \
- /principals/ok\
- \
- \
- \
- \
- \
- HTTP/1.1 200 OK\
- \
- \
- \
- ');
- }
- }
- }),
-
- /* principal URL */
- new RoboHydraHeadDAV({
- path: "/dav/principals/users/test",
- handler: function(req,res,next) {
- if (req.method == "PROPFIND" && req.rawBody.toString().match(/home-?set/)) {
- res.statusCode = 207;
- res.write('\\
- \
- \
- ' + req.url + ' \
- \
- \
- \
- /dav/addressbooks/test\
- \
- \
- /dav/calendars/test/\
- \
- \
- HTTP/1.1 200 OK\
- \
- \
- \
- ');
- }
- }
- }),
-
- /* address-book home set */
- new RoboHydraHeadDAV({
- path: "/dav/addressbooks/test/",
- handler: function(req,res,next) {
- if (!req.url.match(/\/$/)) {
- res.statusCode = 302;
- res.headers['location'] = "/dav/addressbooks/test/";
- }
- else if (req.method == "PROPFIND" && req.rawBody.toString().match(/addressbook-description/)) {
- res.statusCode = 207;
- res.write('\\
- \
- \
- /dav/addressbooks/test/useless-member\
- \
- \
- \
- \
- HTTP/1.1 200 OK\
- \
- \
- \
- /dav/addressbooks/test/default-v4.vcf/\
- \
- \
- \
- \
- \
- \
- Default Address Book\
- \
- \
- \
- \
- \
- HTTP/1.1 200 OK\
- \
- \
- \
- ');
- }
- }
- }),
-
- /* calendar home set */
- new RoboHydraHeadDAV({
- path: "/dav/calendars/test/",
- handler: function(req,res,next) {
- if (req.method == "PROPFIND" && req.rawBody.toString().match(/calendar-description/)) {
- res.statusCode = 207;
- res.write('\\
- \
- \
- /dav/calendars/test/shared.forbidden\
- \
- \
- \
- \
- HTTP/1.1 403 Forbidden\
- \
- \
- \
- /dav/calendars/test/private.ics\
- \
- \
- \
- \
- \
- \
- Private Calendar\
- This is my private calendar.\
- \
- HTTP/1.1 200 OK\
- \
- \
- \
- /dav/calendars/test/work.ics\
- \
- \
- \
- \
- \
- \
- \
- \
- \
- Work Calendar\
- 0xFF00FF\
- \
- HTTP/1.1 200 OK\
- \
- \
- \
- ');
- }
- }
- }),
-
- /* non-existing file */
- new RoboHydraHeadDAV({
- path: "/dav/collection/new.file",
- handler: function(req,res,next) {
- if (req.method == "PUT") {
- if (req.headers['if-match']) /* can't overwrite new file */
- res.statusCode = 412;
- else {
- res.statusCode = 201;
- res.headers["ETag"] = "has-just-been-created";
- }
-
- } else if (req.method == "DELETE")
- res.statusCode = 404;
- }
- }),
-
- /* existing file */
- new RoboHydraHeadDAV({
- path: "/dav/collection/existing.file",
- handler: function(req,res,next) {
- if (req.method == "PUT") {
- if (req.headers['if-none-match']) /* requested "don't overwrite", but this file exists */
- res.statusCode = 412;
- else {
- res.statusCode = 204;
- res.headers["ETag"] = "has-just-been-updated";
- }
-
- } else if (req.method == "DELETE")
- res.statusCode = 204;
- }
- }),
-
- /* address-book multiget */
- new RoboHydraHeadDAV({
- path: "/dav/addressbooks/default.vcf/",
- handler: function(req,res,next) {
- if (req.method == "REPORT" && req.rawBody.toString().match(/addressbook-multiget[\s\S]+[\s\S]+/m)) {
- res.statusCode = 207;
- res.write('\\
- \
- \
- /dav/addressbooks/default.vcf/1.vcf\
- \
- \
- \
- BEGIN:VCARD\
- VERSION:3.0\
- NICKNAME:MULTIGET1\
- UID:1\
- END:VCARD\
- \
- \
- HTTP/1.1 200 OK\
- \
- \
- \
- /dav/addressbooks/default.vcf/2.vcf\
- \
- \
- \
- BEGIN:VCARD\
- VERSION:3.0\
- NICKNAME:MULTIGET2\
- UID:2\
- END:VCARD\
- \
- \
- HTTP/1.1 200 OK\
- \
- \
- \
- ');
- }
- }
- }),
-
- ]
- };
-};
diff --git a/test/robohydra/plugins/headdav.js b/test/robohydra/plugins/headdav.js
deleted file mode 100644
index 613a2678..00000000
--- a/test/robohydra/plugins/headdav.js
+++ /dev/null
@@ -1,57 +0,0 @@
-var roboHydra = require("robohydra"),
- roboHydraHeads = roboHydra.heads,
- roboHydraHead = roboHydraHeads.RoboHydraHead;
-
-RoboHydraHeadDAV = roboHydraHeads.roboHydraHeadType({
- name: 'WebDAV Server',
- mandatoryProperties: [ 'path' ],
- optionalProperties: [ 'handler' ],
-
- parentPropBuilder: function() {
- var myHandler = this.handler;
- return {
- path: this.path,
- handler: function(req,res,next) {
- // default DAV behavior
- res.headers['DAV'] = 'addressbook, calendar-access';
- res.statusCode = 500;
-
- // verify Accept header
- var accept = req.headers['accept'];
- if (req.method == "GET" && (accept == undefined || !accept.match(/text\/(calendar|vcard|xml)/)) ||
- (req.method == "PROPFIND" || req.method == "REPORT") && (accept == undefined || accept != "text/xml"))
- res.statusCode = 406;
-
- // DAV operations that work on all URLs
- else if (req.method == "OPTIONS") {
- res.statusCode = 204;
- res.headers['Allow'] = 'OPTIONS, PROPFIND, GET, PUT, DELETE, REPORT';
-
- } else if (req.method == "PROPFIND" && req.rawBody.toString().match(/current-user-principal/)) {
- res.statusCode = 207;
- res.write('\\
- \
- \
- ' + req.url + ' \
- \
- \
- \
- /dav/principals/users/test\
- \
- \
- HTTP/1.1 200 OK\
- \
- \
- \
- ');
-
- } else if (typeof myHandler != 'undefined')
- myHandler(req,res,next);
-
- res.end();
- }
- }
- }
-});
-
-module.exports = RoboHydraHeadDAV;
diff --git a/test/robohydra/plugins/redirect/index.js b/test/robohydra/plugins/redirect/index.js
deleted file mode 100644
index 4c2cbab0..00000000
--- a/test/robohydra/plugins/redirect/index.js
+++ /dev/null
@@ -1,57 +0,0 @@
-require('../simple');
-
-var RoboHydraHead = require('robohydra').heads.RoboHydraHead;
-
-exports.getBodyParts = function(conf) {
- return {
- heads: [
- // well-known URIs
- new SimpleResponseHead({
- path: '/.well-known/caldav',
- status: 302,
- headers: { Location: '/dav/' }
- }),
- new SimpleResponseHead({
- path: '/.well-known/carddav',
- status: 302,
- headers: { Location: '/dav/' }
- }),
-
- // generic redirections
- new RoboHydraHead({
- path: '/redirect/301',
- handler: function(req,res,next) {
- res.statusCode = 301;
- var location = req.queryParams['to'] || '/assets/test.random';
- res.headers = {
- Location: location
- }
- res.end();
- }
- }),
- new RoboHydraHead({
- path: '/redirect/302',
- handler: function(req,res,next) {
- res.statusCode = 302;
- var location = req.queryParams['to'] || '/assets/test.random';
- res.headers = {
- Location: location
- }
- res.end();
- }
- }),
-
- // special redirections
- new SimpleResponseHead({
- path: '/redirect/relative',
- status: 302,
- headers: { Location: '/new/location' }
- }),
- new SimpleResponseHead({
- path: '/redirect/without-location',
- status: 302
- })
-
- ]
- };
-};
diff --git a/test/robohydra/plugins/simple.js b/test/robohydra/plugins/simple.js
deleted file mode 100644
index 3d506ee2..00000000
--- a/test/robohydra/plugins/simple.js
+++ /dev/null
@@ -1,28 +0,0 @@
-var roboHydra = require("robohydra"),
- roboHydraHeads = roboHydra.heads,
- roboHydraHead = roboHydraHeads.RoboHydraHead;
-
-SimpleResponseHead = roboHydraHeads.roboHydraHeadType({
- name: 'Simple HTTP Response',
- mandatoryProperties: [ 'path', 'status' ],
- optionalProperties: [ 'headers', 'body' ],
-
- parentPropBuilder: function() {
- var head = this;
- return {
- path: this.path,
- handler: function(req,res,next) {
- res.statusCode = head.status;
- if (typeof head.headers != 'undefined')
- res.headers = head.headers;
- if (typeof head.body != 'undefined')
- res.write(head.body);
- else
- res.write();
- res.end();
- }
- }
- }
-});
-
-module.exports = SimpleResponseHead;
diff --git a/test/robohydra/run.sh b/test/robohydra/run.sh
deleted file mode 100755
index f53c7967..00000000
--- a/test/robohydra/run.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/sh
-node_modules/robohydra/bin/robohydra.js davdroid.conf -I plugins
diff --git a/test/src/at/bitfire/davdroid/resource/test/ContactTest.java b/test/src/at/bitfire/davdroid/resource/test/ContactTest.java
deleted file mode 100644
index 26ee6b1b..00000000
--- a/test/src/at/bitfire/davdroid/resource/test/ContactTest.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 Ricki Hirner (bitfire web engineering).
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the GNU Public License v3.0
- * which accompanies this distribution, and is available at
- * http://www.gnu.org/licenses/gpl.html
- ******************************************************************************/
-package at.bitfire.davdroid.resource.test;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-import ezvcard.property.Email;
-import ezvcard.property.Telephone;
-import lombok.Cleanup;
-import android.content.res.AssetManager;
-import android.test.InstrumentationTestCase;
-import at.bitfire.davdroid.resource.Contact;
-import at.bitfire.davdroid.resource.InvalidResourceException;
-
-public class ContactTest extends InstrumentationTestCase {
- AssetManager assetMgr;
-
- public void setUp() throws IOException, InvalidResourceException {
- assetMgr = getInstrumentation().getContext().getResources().getAssets();
- }
-
- public void testReferenceVCard() throws IOException, InvalidResourceException {
- Contact c = parseVCF("reference.vcf");
- assertEquals("Gump", c.getFamilyName());
- assertEquals("Forrest", c.getGivenName());
- assertEquals("Forrest Gump", c.getDisplayName());
- assertEquals("Bubba Gump Shrimp Co.", c.getOrganization().getValues().get(0));
- assertEquals("Shrimp Man", c.getJobTitle());
-
- Telephone phone1 = c.getPhoneNumbers().get(0);
- assertEquals("(111) 555-1212", phone1.getText());
- assertEquals("WORK", phone1.getParameters("TYPE").get(0));
- assertEquals("VOICE", phone1.getParameters("TYPE").get(1));
-
- Telephone phone2 = c.getPhoneNumbers().get(1);
- assertEquals("(404) 555-1212", phone2.getText());
- assertEquals("HOME", phone2.getParameters("TYPE").get(0));
- assertEquals("VOICE", phone2.getParameters("TYPE").get(1));
-
- Email email = c.getEmails().get(0);
- assertEquals("forrestgump@example.com", email.getValue());
- assertEquals("PREF", email.getParameters("TYPE").get(0));
- assertEquals("INTERNET", email.getParameters("TYPE").get(1));
- }
-
- public void testParseInvalidUnknownProperties() throws IOException, InvalidResourceException {
- Contact c = parseVCF("invalid-unknown-properties.vcf");
- assertEquals("VCard with invalid unknown properties", c.getDisplayName());
- assertNull(c.getUnknownProperties());
- }
-
-
- protected Contact parseVCF(String fname) throws IOException, InvalidResourceException {
- @Cleanup InputStream in = assetMgr.open(fname, AssetManager.ACCESS_STREAMING);
- Contact c = new Contact(fname, null);
- c.parseEntity(in);
- return c;
- }
-}
diff --git a/test/src/at/bitfire/davdroid/resource/test/EventTest.java b/test/src/at/bitfire/davdroid/resource/test/EventTest.java
deleted file mode 100644
index 89afdc1c..00000000
--- a/test/src/at/bitfire/davdroid/resource/test/EventTest.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 Ricki Hirner (bitfire web engineering).
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the GNU Public License v3.0
- * which accompanies this distribution, and is available at
- * http://www.gnu.org/licenses/gpl.html
- ******************************************************************************/
-package at.bitfire.davdroid.resource.test;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-import lombok.Cleanup;
-import net.fortuna.ical4j.data.ParserException;
-import android.content.res.AssetManager;
-import android.test.InstrumentationTestCase;
-import android.text.format.Time;
-import at.bitfire.davdroid.resource.Event;
-import at.bitfire.davdroid.resource.InvalidResourceException;
-
-public class EventTest extends InstrumentationTestCase {
- AssetManager assetMgr;
-
- Event eViennaEvolution,
- eOnThatDay, eAllDay1Day, eAllDay10Days, eAllDay0Sec;
-
- public void setUp() throws IOException, InvalidResourceException {
- assetMgr = getInstrumentation().getContext().getResources().getAssets();
-
- eViennaEvolution = parseCalendar("vienna-evolution.ics");
- eOnThatDay = parseCalendar("event-on-that-day.ics");
- eAllDay1Day = parseCalendar("all-day-1day.ics");
- eAllDay10Days = parseCalendar("all-day-10days.ics");
- eAllDay0Sec = parseCalendar("all-day-0sec.ics");
-
- //assertEquals("Test-Ereignis im schönen Wien", e.getSummary());
- }
-
-
- public void testStartEndTimes() throws IOException, ParserException {
- // event with start+end date-time
- assertEquals(1381330800000L, eViennaEvolution.getDtStartInMillis());
- assertEquals("Europe/Vienna", eViennaEvolution.getDtStartTzID());
- assertEquals(1381334400000L, eViennaEvolution.getDtEndInMillis());
- assertEquals("Europe/Vienna", eViennaEvolution.getDtEndTzID());
- }
-
- public void testStartEndTimesAllDay() throws IOException, ParserException {
- // event with start date only
- assertEquals(868838400000L, eOnThatDay.getDtStartInMillis());
- assertEquals(Time.TIMEZONE_UTC, eOnThatDay.getDtStartTzID());
- // DTEND missing in VEVENT, must have been set to DTSTART+1 day
- assertEquals(868838400000L + 86400000, eOnThatDay.getDtEndInMillis());
- assertEquals(Time.TIMEZONE_UTC, eOnThatDay.getDtEndTzID());
-
- // event with start+end date for all-day event (one day)
- assertEquals(868838400000L, eAllDay1Day.getDtStartInMillis());
- assertEquals(Time.TIMEZONE_UTC, eAllDay1Day.getDtStartTzID());
- assertEquals(868838400000L + 86400000, eAllDay1Day.getDtEndInMillis());
- assertEquals(Time.TIMEZONE_UTC, eAllDay1Day.getDtEndTzID());
-
- // event with start+end date for all-day event (ten days)
- assertEquals(868838400000L, eAllDay10Days.getDtStartInMillis());
- assertEquals(Time.TIMEZONE_UTC, eAllDay10Days.getDtStartTzID());
- assertEquals(868838400000L + 10*86400000, eAllDay10Days.getDtEndInMillis());
- assertEquals(Time.TIMEZONE_UTC, eAllDay10Days.getDtEndTzID());
-
- // event with start+end date on some day (invalid 0 sec-event)
- assertEquals(868838400000L, eAllDay0Sec.getDtStartInMillis());
- assertEquals(Time.TIMEZONE_UTC, eAllDay0Sec.getDtStartTzID());
- // DTEND invalid in VEVENT, must have been set to DTSTART+1 day
- assertEquals(868838400000L + 86400000, eAllDay0Sec.getDtEndInMillis());
- assertEquals(Time.TIMEZONE_UTC, eAllDay0Sec.getDtEndTzID());
- }
-
- public void testTimezoneDefToTzId() {
- // test valid definition
- final String VTIMEZONE_SAMPLE = // taken from RFC 4791, 5.2.2. CALDAV:calendar-timezone Property
- "BEGIN:VCALENDAR\n" +
- "PRODID:-//Example Corp.//CalDAV Client//EN\n" +
- "VERSION:2.0\n" +
- "BEGIN:VTIMEZONE\n" +
- "TZID:US-Eastern\n" +
- "LAST-MODIFIED:19870101T000000Z\n" +
- "BEGIN:STANDARD\n" +
- "DTSTART:19671029T020000\n" +
- "RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10\n" +
- "TZOFFSETFROM:-0400\n" +
- "TZOFFSETTO:-0500\n" +
- "TZNAME:Eastern Standard Time (US & Canada)\n" +
- "END:STANDARD\n" +
- "BEGIN:DAYLIGHT\n" +
- "DTSTART:19870405T020000\n" +
- "RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4\n" +
- "TZOFFSETFROM:-0500\n" +
- "TZOFFSETTO:-0400\n" +
- "TZNAME:Eastern Daylight Time (US & Canada)\n" +
- "END:DAYLIGHT\n" +
- "END:VTIMEZONE\n" +
- "END:VCALENDAR";
- assertEquals("US-Eastern", Event.TimezoneDefToTzId(VTIMEZONE_SAMPLE));
-
- // test null value
- try {
- Event.TimezoneDefToTzId(null);
- fail();
- } catch(IllegalArgumentException e) {
- assert(true);
- }
-
- // test invalid time zone
- try {
- Event.TimezoneDefToTzId("/* invalid content */");
- fail();
- } catch(IllegalArgumentException e) {
- assert(true);
- }
- }
-
-
- protected Event parseCalendar(String fname) throws IOException, InvalidResourceException {
- @Cleanup InputStream in = assetMgr.open(fname, AssetManager.ACCESS_STREAMING);
- Event e = new Event(fname, null);
- e.parseEntity(in);
- return e;
- }
-}
diff --git a/test/src/at/bitfire/davdroid/resource/test/LocalCalendarTest.java b/test/src/at/bitfire/davdroid/resource/test/LocalCalendarTest.java
deleted file mode 100644
index 6fe5afea..00000000
--- a/test/src/at/bitfire/davdroid/resource/test/LocalCalendarTest.java
+++ /dev/null
@@ -1,154 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 Ricki Hirner (bitfire web engineering).
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the GNU Public License v3.0
- * which accompanies this distribution, and is available at
- * http://www.gnu.org/licenses/gpl.html
- ******************************************************************************/
-package at.bitfire.davdroid.resource.test;
-
-import java.util.Calendar;
-
-import lombok.Cleanup;
-import android.accounts.Account;
-import android.annotation.TargetApi;
-import android.content.ContentProviderClient;
-import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Build;
-import android.os.RemoteException;
-import android.provider.CalendarContract;
-import android.provider.CalendarContract.Attendees;
-import android.provider.CalendarContract.Calendars;
-import android.provider.CalendarContract.Events;
-import android.provider.CalendarContract.Reminders;
-import android.test.InstrumentationTestCase;
-import android.util.Log;
-import at.bitfire.davdroid.resource.LocalCalendar;
-import at.bitfire.davdroid.resource.LocalStorageException;
-
-public class LocalCalendarTest extends InstrumentationTestCase {
-
- private static final String
- TAG = "davroid.LocalCalendarTest",
- calendarName = "DAVdroid_Test";
-
- ContentProviderClient providerClient;
- Account testAccount = new Account(calendarName, CalendarContract.ACCOUNT_TYPE_LOCAL);
- LocalCalendar testCalendar;
-
-
- // helpers
-
- private Uri syncAdapterURI(Uri uri) {
- return uri.buildUpon()
- .appendQueryParameter(Calendars.ACCOUNT_NAME, calendarName)
- .appendQueryParameter(Calendars.ACCOUNT_TYPE, CalendarContract.ACCOUNT_TYPE_LOCAL)
- .appendQueryParameter(CalendarContract.CALLER_IS_SYNCADAPTER, "true").
- build();
- }
-
- private long insertNewEvent() throws LocalStorageException, RemoteException {
- ContentValues values = new ContentValues();
- values.put(Events.CALENDAR_ID, testCalendar.getId());
- values.put(Events.TITLE, "Test Event");
- values.put(Events.ALL_DAY, 0);
- values.put(Events.DTSTART, Calendar.getInstance().getTimeInMillis());
- values.put(Events.DTEND, Calendar.getInstance().getTimeInMillis());
- values.put(Events.EVENT_TIMEZONE, "UTC");
- values.put(Events.DIRTY, 1);
- return ContentUris.parseId(providerClient.insert(syncAdapterURI(Events.CONTENT_URI), values));
- }
-
- private void deleteEvent(long id) throws RemoteException {
- providerClient.delete(syncAdapterURI(ContentUris.withAppendedId(Events.CONTENT_URI, id)), null, null);
- }
-
-
- // initialization
-
- @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1)
- protected void setUp() throws Exception {
- ContentResolver resolver = getInstrumentation().getContext().getContentResolver();
- providerClient = resolver.acquireContentProviderClient(CalendarContract.AUTHORITY);
-
- long id;
-
- @Cleanup Cursor cursor = providerClient.query(Calendars.CONTENT_URI,
- new String[] { Calendars._ID },
- Calendars.ACCOUNT_TYPE + "=? AND " + Calendars.NAME + "=?",
- new String[] { CalendarContract.ACCOUNT_TYPE_LOCAL, calendarName },
- null);
- if (cursor.moveToNext()) {
- // found local test calendar
- id = cursor.getLong(0);
- Log.d(TAG, "Found test calendar with ID " + id);
-
- } else {
- // no local test calendar found, create
- ContentValues values = new ContentValues();
- values.put(Calendars.ACCOUNT_NAME, testAccount.name);
- values.put(Calendars.ACCOUNT_TYPE, testAccount.type);
- values.put(Calendars.NAME, calendarName);
- values.put(Calendars.CALENDAR_DISPLAY_NAME, calendarName);
- values.put(Calendars.CALENDAR_ACCESS_LEVEL, Calendars.CAL_ACCESS_OWNER);
- values.put(Calendars.ALLOWED_REMINDERS, Reminders.METHOD_ALERT);
- values.put(Calendars.SYNC_EVENTS, 0);
- values.put(Calendars.VISIBLE, 1);
-
- if (android.os.Build.VERSION.SDK_INT >= 15) {
- values.put(Calendars.ALLOWED_AVAILABILITY, Events.AVAILABILITY_BUSY + "," + Events.AVAILABILITY_FREE + "," + Events.AVAILABILITY_TENTATIVE);
- values.put(Calendars.ALLOWED_ATTENDEE_TYPES, Attendees.TYPE_NONE + "," + Attendees.TYPE_OPTIONAL + "," + Attendees.TYPE_REQUIRED + "," + Attendees.TYPE_RESOURCE);
- }
-
- Uri calendarURI = providerClient.insert(syncAdapterURI(Calendars.CONTENT_URI), values);
-
- id = ContentUris.parseId(calendarURI);
- Log.d(TAG, "Created test calendar with ID " + id);
- }
-
- testCalendar = new LocalCalendar(testAccount, providerClient, id, null);
- }
-
- protected void tearDown() throws Exception {
- Uri uri = ContentUris.withAppendedId(syncAdapterURI(Calendars.CONTENT_URI), testCalendar.getId());
- providerClient.delete(uri, null, null);
- }
-
-
- // tests
-
- public void testCTags() throws LocalStorageException {
- assertNull(testCalendar.getCTag());
-
- final String cTag = "just-modified";
- testCalendar.setCTag(cTag);
-
- assertEquals(cTag, testCalendar.getCTag());
- }
-
- public void testFindNew() throws LocalStorageException, RemoteException {
- // at the beginning, there are no dirty events
- assertTrue(testCalendar.findNew().length == 0);
- assertTrue(testCalendar.findUpdated().length == 0);
-
- // insert a "new" event
- long id = insertNewEvent();
- try {
- // there must be one "new" event now
- assertTrue(testCalendar.findNew().length == 1);
- assertTrue(testCalendar.findUpdated().length == 0);
-
- // nothing has changed, the record must still be "new"
- // see issue #233
- assertTrue(testCalendar.findNew().length == 1);
- assertTrue(testCalendar.findUpdated().length == 0);
- } finally {
- deleteEvent(id);
- }
- }
-
-}
diff --git a/test/src/at/bitfire/davdroid/syncadapter/DavResourceFinderTest.java b/test/src/at/bitfire/davdroid/syncadapter/DavResourceFinderTest.java
deleted file mode 100644
index 85f7e1e9..00000000
--- a/test/src/at/bitfire/davdroid/syncadapter/DavResourceFinderTest.java
+++ /dev/null
@@ -1,70 +0,0 @@
-package at.bitfire.davdroid.syncadapter;
-
-import java.io.IOException;
-import java.net.URI;
-import java.net.URL;
-import java.util.List;
-
-import android.test.InstrumentationTestCase;
-import at.bitfire.davdroid.resource.DavResourceFinder;
-import at.bitfire.davdroid.resource.ServerInfo;
-import at.bitfire.davdroid.resource.ServerInfo.ResourceInfo;
-import at.bitfire.davdroid.test.Constants;
-import ezvcard.VCardVersion;
-
-public class DavResourceFinderTest extends InstrumentationTestCase {
-
- DavResourceFinder finder;
-
- @Override
- protected void setUp() {
- finder = new DavResourceFinder(getInstrumentation().getContext());
- }
-
- @Override
- protected void tearDown() throws IOException {
- finder.close();
- }
-
-
- public void testFindResourcesRobohydra() throws Exception {
- ServerInfo info = new ServerInfo(new URI(Constants.ROBOHYDRA_BASE), "test", "test", true);
- finder.findResources(info);
-
- // CardDAV
- assertTrue(info.isCardDAV());
- List collections = info.getAddressBooks();
- assertEquals(1, collections.size());
-
- assertEquals("Default Address Book", collections.get(0).getDescription());
- assertEquals(VCardVersion.V4_0, collections.get(0).getVCardVersion());
-
- // CalDAV
- assertTrue(info.isCalDAV());
- collections = info.getCalendars();
- assertEquals(2, collections.size());
-
- ResourceInfo resource = collections.get(0);
- assertEquals("Private Calendar", resource.getTitle());
- assertEquals("This is my private calendar.", resource.getDescription());
- assertFalse(resource.isReadOnly());
-
- resource = collections.get(1);
- assertEquals("Work Calendar", resource.getTitle());
- assertTrue(resource.isReadOnly());
- }
-
-
- public void testGetInitialContextURL() throws Exception {
- // without SRV records, but with well-known paths
- ServerInfo roboHydra = new ServerInfo(new URI(Constants.ROBOHYDRA_BASE), "test", "test", true);
- assertEquals(new URL(Constants.roboHydra, "/"), finder.getInitialContextURL(roboHydra, "caldav"));
- assertEquals(new URL(Constants.roboHydra, "/"), finder.getInitialContextURL(roboHydra, "carddav"));
-
- // with SRV records and well-known paths
- ServerInfo iCloud = new ServerInfo(new URI("mailto:test@icloud.com"), "", "", true);
- assertEquals(new URL("https://contacts.icloud.com/"), finder.getInitialContextURL(iCloud, "carddav"));
- assertEquals(new URL("https://caldav.icloud.com/"), finder.getInitialContextURL(iCloud, "caldav"));
- }
-
-}
diff --git a/test/src/at/bitfire/davdroid/test/ArrayUtilsTest.java b/test/src/at/bitfire/davdroid/test/ArrayUtilsTest.java
deleted file mode 100644
index 2761d2be..00000000
--- a/test/src/at/bitfire/davdroid/test/ArrayUtilsTest.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 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.test;
-import java.util.Arrays;
-
-import junit.framework.TestCase;
-import at.bitfire.davdroid.ArrayUtils;
-
-
-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)));
- }
-
-}
diff --git a/test/src/at/bitfire/davdroid/test/Constants.java b/test/src/at/bitfire/davdroid/test/Constants.java
deleted file mode 100644
index 707762b3..00000000
--- a/test/src/at/bitfire/davdroid/test/Constants.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package at.bitfire.davdroid.test;
-
-import java.net.MalformedURLException;
-import java.net.URL;
-
-import android.util.Log;
-
-public class Constants {
- public static final String ROBOHYDRA_BASE = "http://10.0.0.11:3000/";
-
- public static URL roboHydra;
- static {
- try {
- roboHydra = new URL(ROBOHYDRA_BASE);
- } catch(MalformedURLException e) {
- Log.wtf("davdroid.test.Constants", "Invalid RoboHydra base URL");
- }
- }
-}
diff --git a/test/src/at/bitfire/davdroid/test/ContactTest.java b/test/src/at/bitfire/davdroid/test/ContactTest.java
deleted file mode 100644
index a98ed78f..00000000
--- a/test/src/at/bitfire/davdroid/test/ContactTest.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 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.test;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-import lombok.Cleanup;
-import net.fortuna.ical4j.data.ParserException;
-import android.content.res.AssetManager;
-import android.test.InstrumentationTestCase;
-import at.bitfire.davdroid.resource.Contact;
-import ezvcard.property.Impp;
-
-public class ContactTest extends InstrumentationTestCase {
- AssetManager assetMgr;
-
- public void setUp() {
- assetMgr = getInstrumentation().getContext().getResources().getAssets();
- }
-
- public void testIMPP() throws IOException {
- Contact c = parseVCard("impp.vcf");
- assertEquals("test mctest", c.getDisplayName());
-
- Impp jabber = c.getImpps().get(0);
- assertNull(jabber.getProtocol());
- assertEquals("test-without-valid-scheme@test.tld", jabber.getHandle());
- }
-
-
- public void testParseVcard3() throws IOException, ParserException {
- Contact c = parseVCard("vcard3-sample1.vcf");
-
- assertEquals("Forrest Gump", c.getDisplayName());
- assertEquals("Forrest", c.getGivenName());
- assertEquals("Gump", c.getFamilyName());
-
- assertEquals(2, c.getPhoneNumbers().size());
- assertEquals("(111) 555-1212", c.getPhoneNumbers().get(0).getText());
-
- assertEquals(1, c.getEmails().size());
- assertEquals("forrestgump@example.com", c.getEmails().get(0).getValue());
-
- assertFalse(c.isStarred());
- }
-
-
- private Contact parseVCard(String fileName) throws IOException {
- @Cleanup InputStream in = assetMgr.open(fileName, AssetManager.ACCESS_STREAMING);
-
- Contact c = new Contact(fileName, null);
- c.parseEntity(in);
-
- return c;
- }
-}
diff --git a/test/src/at/bitfire/davdroid/test/URLUtilsTest.java b/test/src/at/bitfire/davdroid/test/URLUtilsTest.java
deleted file mode 100644
index 555b9b6b..00000000
--- a/test/src/at/bitfire/davdroid/test/URLUtilsTest.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 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.test;
-
-import java.net.URL;
-
-import junit.framework.TestCase;
-import at.bitfire.davdroid.URLUtils;
-
-public class URLUtilsTest extends TestCase {
-
- public void testEnsureTrailingSlash() throws Exception {
- assertEquals("/test/", URLUtils.ensureTrailingSlash("/test"));
- assertEquals("/test/", URLUtils.ensureTrailingSlash("/test/"));
-
- String withoutSlash = "http://www.test.at/dav/collection",
- withSlash = withoutSlash + "/";
- assertEquals(new URL(withSlash), URLUtils.ensureTrailingSlash(new URL(withoutSlash)));
- assertEquals(new URL(withSlash), URLUtils.ensureTrailingSlash(new URL(withSlash)));
- }
-
- public void testSanitize() {
- assertNull(URLUtils.sanitize(null));
-
- // escape "@"
- assertEquals("https://my%40server/my%40email.com/dir", URLUtils.sanitize("https://my@server/my@email.com/dir"));
- assertEquals("http://my%40server/my%40email.com/dir", URLUtils.sanitize("http://my@server/my@email.com/dir"));
- assertEquals("//my%40server/my%40email.com/dir", URLUtils.sanitize("//my@server/my@email.com/dir"));
- assertEquals("/my%40email.com/dir", URLUtils.sanitize("/my@email.com/dir"));
- assertEquals("my%40email.com/dir", URLUtils.sanitize("my@email.com/dir"));
-
- // escape ":" in path but not as port separator
- assertEquals("https://www.test.at:80/my%3afile.vcf", URLUtils.sanitize("https://www.test.at:80/my:file.vcf"));
- assertEquals("http://www.test.at:80/my%3afile.vcf", URLUtils.sanitize("http://www.test.at:80/my:file.vcf"));
- assertEquals("//www.test.at:80/my%3afile.vcf", URLUtils.sanitize("//www.test.at:80/my:file.vcf"));
- assertEquals("/my%3afile.vcf", URLUtils.sanitize("/my:file.vcf"));
- assertEquals("my%3afile.vcf", URLUtils.sanitize("my:file.vcf"));
-
- // keep literal IPv6 addresses (only in host name)
- assertEquals("https://[1:2::1]/", URLUtils.sanitize("https://[1:2::1]/"));
- assertEquals("/%5b1%3a2%3a%3a1%5d/", URLUtils.sanitize("/[1:2::1]/"));
- }
-}
diff --git a/test/src/at/bitfire/davdroid/webdav/DavRedirectStrategyTest.java b/test/src/at/bitfire/davdroid/webdav/DavRedirectStrategyTest.java
deleted file mode 100644
index 7467175f..00000000
--- a/test/src/at/bitfire/davdroid/webdav/DavRedirectStrategyTest.java
+++ /dev/null
@@ -1,74 +0,0 @@
-package at.bitfire.davdroid.webdav;
-
-import java.io.IOException;
-import java.net.URL;
-
-import junit.framework.TestCase;
-import at.bitfire.davdroid.test.Constants;
-import ch.boye.httpclientandroidlib.HttpResponse;
-import ch.boye.httpclientandroidlib.client.methods.HttpOptions;
-import ch.boye.httpclientandroidlib.client.methods.HttpUriRequest;
-import ch.boye.httpclientandroidlib.client.protocol.HttpClientContext;
-import ch.boye.httpclientandroidlib.impl.client.CloseableHttpClient;
-import ch.boye.httpclientandroidlib.impl.client.HttpClientBuilder;
-import ch.boye.httpclientandroidlib.protocol.HttpContext;
-
-public class DavRedirectStrategyTest extends TestCase {
-
- CloseableHttpClient httpClient;
- DavRedirectStrategy strategy = DavRedirectStrategy.INSTANCE;
-
- @Override
- protected void setUp() {
- httpClient = HttpClientBuilder.create()
- .useSystemProperties()
- .disableRedirectHandling()
- .build();
- }
-
- @Override
- protected void tearDown() throws IOException {
- httpClient.close();
- }
-
-
- // happy cases
-
- public void testNonRedirection() throws Exception {
- HttpUriRequest request = new HttpOptions(Constants.roboHydra.toURI());
- HttpResponse response = httpClient.execute(request);
- assertFalse(strategy.isRedirected(request, response, null));
- }
-
- public void testDefaultRedirection() throws Exception {
- final String newLocation = "/new-location";
-
- HttpContext context = HttpClientContext.create();
- HttpUriRequest request = new HttpOptions(new URL(Constants.roboHydra, "redirect/301?to=" + newLocation).toURI());
- HttpResponse response = httpClient.execute(request, context);
- assertTrue(strategy.isRedirected(request, response, context));
-
- HttpUriRequest redirected = strategy.getRedirect(request, response, context);
- assertEquals(new URL(Constants.roboHydra, newLocation).toURI(), redirected.getURI());
- }
-
-
- // error cases
-
- public void testMissingLocation() throws Exception {
- HttpContext context = HttpClientContext.create();
- HttpUriRequest request = new HttpOptions(new URL(Constants.roboHydra, "redirect/without-location").toURI());
- HttpResponse response = httpClient.execute(request, context);
- assertFalse(strategy.isRedirected(request, response, context));
- }
-
- public void testRelativeLocation() throws Exception {
- HttpContext context = HttpClientContext.create();
- HttpUriRequest request = new HttpOptions(new URL(Constants.roboHydra, "redirect/relative").toURI());
- HttpResponse response = httpClient.execute(request, context);
- assertTrue(strategy.isRedirected(request, response, context));
-
- HttpUriRequest redirected = strategy.getRedirect(request, response, context);
- assertEquals(new URL(Constants.roboHydra, "/new/location").toURI(), redirected.getURI());
- }
-}
diff --git a/test/src/at/bitfire/davdroid/webdav/TlsSniSocketFactoryTest.java b/test/src/at/bitfire/davdroid/webdav/TlsSniSocketFactoryTest.java
deleted file mode 100644
index f00844d5..00000000
--- a/test/src/at/bitfire/davdroid/webdav/TlsSniSocketFactoryTest.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package at.bitfire.davdroid.webdav;
-
-import java.io.IOException;
-
-import javax.net.ssl.SSLSocket;
-
-import android.util.Log;
-import junit.framework.TestCase;
-
-public class TlsSniSocketFactoryTest extends TestCase {
- private static final String TAG = "davdroid.TlsSniSocketFactoryTest";
-
- public void testCiphers() throws IOException {
- SSLSocket socket = (SSLSocket)TlsSniSocketFactory.INSTANCE.createSocket(null);
-
- Log.i(TAG, "Enabled:");
- for (String cipher : socket.getEnabledCipherSuites())
- Log.i(TAG, cipher);
-
- Log.i(TAG, "Supported:");
- for (String cipher : socket.getSupportedCipherSuites())
- Log.i(TAG, cipher);
-
- assert(true);
- }
-
-}
diff --git a/test/src/at/bitfire/davdroid/webdav/WebDavResourceTest.java b/test/src/at/bitfire/davdroid/webdav/WebDavResourceTest.java
deleted file mode 100644
index 4a5b0510..00000000
--- a/test/src/at/bitfire/davdroid/webdav/WebDavResourceTest.java
+++ /dev/null
@@ -1,234 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 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.webdav;
-
-import java.io.InputStream;
-import java.net.URL;
-import java.util.Arrays;
-import java.util.List;
-
-import javax.net.ssl.SSLPeerUnverifiedException;
-
-import lombok.Cleanup;
-
-import org.apache.commons.io.IOUtils;
-
-import android.content.res.AssetManager;
-import android.test.InstrumentationTestCase;
-import at.bitfire.davdroid.test.Constants;
-import at.bitfire.davdroid.webdav.HttpPropfind.Mode;
-import at.bitfire.davdroid.webdav.WebDavResource.PutMode;
-import ch.boye.httpclientandroidlib.impl.client.CloseableHttpClient;
-
-// tests require running robohydra!
-
-public class WebDavResourceTest extends InstrumentationTestCase {
- static byte[] SAMPLE_CONTENT = new byte[] { 1, 2, 3, 4, 5 };
-
- final static String PATH_SIMPLE_FILE = "collection/new.file";
-
- AssetManager assetMgr;
- CloseableHttpClient httpClient;
-
- WebDavResource baseDAV;
- WebDavResource simpleFile,
- davCollection, davNonExistingFile, davExistingFile,
- davInvalid;
-
- @Override
- protected void setUp() throws Exception {
- httpClient = DavHttpClient.create(true, true);
-
- assetMgr = getInstrumentation().getContext().getResources().getAssets();
-
- baseDAV = new WebDavResource(httpClient, new URL(Constants.roboHydra, "/dav/"));
-
- simpleFile = new WebDavResource(httpClient, new URL(Constants.ROBOHYDRA_BASE + "assets/test.random"));
-
- davCollection = new WebDavResource(httpClient, new URL(Constants.ROBOHYDRA_BASE + "dav/"));
- davNonExistingFile = new WebDavResource(davCollection, "collection/new.file");
- davExistingFile = new WebDavResource(davCollection, "collection/existing.file");
-
- davInvalid = new WebDavResource(httpClient, new URL(Constants.ROBOHYDRA_BASE + "dav-invalid/"));
- }
-
- @Override
- protected void tearDown() throws Exception {
- httpClient.close();
- }
-
-
- /* test feature detection */
-
- public void testOptions() throws Exception {
- String[] davMethods = new String[] { "PROPFIND", "GET", "PUT", "DELETE", "REPORT" },
- davCapabilities = new String[] { "addressbook", "calendar-access" };
-
- WebDavResource capable = new WebDavResource(baseDAV);
- capable.options();
- for (String davMethod : davMethods)
- assert(capable.supportsMethod(davMethod));
- for (String capability : davCapabilities)
- assert(capable.supportsDAV(capability));
- }
-
- public void testPropfindCurrentUserPrincipal() throws Exception {
- davCollection.propfind(HttpPropfind.Mode.CURRENT_USER_PRINCIPAL);
- assertEquals("/dav/principals/users/test", davCollection.getCurrentUserPrincipal());
-
- try {
- simpleFile.propfind(HttpPropfind.Mode.CURRENT_USER_PRINCIPAL);
- fail();
-
- } catch(DavException ex) {
- }
- assertNull(simpleFile.getCurrentUserPrincipal());
- }
-
- public void testPropfindHomeSets() throws Exception {
- WebDavResource dav = new WebDavResource(davCollection, "principals/users/test");
- dav.propfind(HttpPropfind.Mode.HOME_SETS);
- assertEquals("/dav/addressbooks/test/", dav.getAddressbookHomeSet());
- assertEquals("/dav/calendars/test/", dav.getCalendarHomeSet());
- }
-
- public void testPropfindAddressBooks() throws Exception {
- WebDavResource dav = new WebDavResource(davCollection, "addressbooks/test");
- dav.propfind(HttpPropfind.Mode.CARDDAV_COLLECTIONS);
- assertEquals(2, dav.getMembers().size());
- for (WebDavResource member : dav.getMembers()) {
- if (member.getName().equals("default-v4.vcf"))
- assertTrue(member.isAddressBook());
- else
- assertFalse(member.isAddressBook());
- assertFalse(member.isCalendar());
- }
- }
-
- public void testPropfindCalendars() throws Exception {
- WebDavResource dav = new WebDavResource(davCollection, "calendars/test");
- dav.propfind(Mode.CALDAV_COLLECTIONS);
- assertEquals(3, dav.getMembers().size());
- assertEquals("0xFF00FF", dav.getMembers().get(2).getColor());
- for (WebDavResource member : dav.getMembers()) {
- if (member.getName().contains(".ics"))
- assertTrue(member.isCalendar());
- else
- assertFalse(member.isCalendar());
- assertFalse(member.isAddressBook());
- }
- }
-
- public void testPropfindTrailingSlashes() throws Exception {
- final String principalOK = "/principals/ok";
-
- String requestPaths[] = {
- "/dav/collection-response-with-trailing-slash",
- "/dav/collection-response-with-trailing-slash/",
- "/dav/collection-response-without-trailing-slash",
- "/dav/collection-response-without-trailing-slash/"
- };
-
- for (String path : requestPaths) {
- WebDavResource davSlash = new WebDavResource(davCollection, path);
- davSlash.propfind(Mode.CARDDAV_COLLECTIONS);
- assertEquals(principalOK, davSlash.getCurrentUserPrincipal());
- }
- }
-
-
- /* test normal HTTP/WebDAV */
-
- public void testPropfindRedirection() throws Exception {
- // PROPFIND redirection
- WebDavResource redirected = new WebDavResource(baseDAV, "/redirect/301?to=/dav/");
- redirected.propfind(Mode.CURRENT_USER_PRINCIPAL);
- assertEquals("/dav/", redirected.getLocation().getPath());
- }
-
- public void testGet() throws Exception {
- simpleFile.get("*/*");
- @Cleanup InputStream is = assetMgr.open("test.random", AssetManager.ACCESS_STREAMING);
- byte[] expected = IOUtils.toByteArray(is);
- assertTrue(Arrays.equals(expected, simpleFile.getContent()));
- }
-
- public void testGetHttpsWithSni() throws Exception {
- WebDavResource file = new WebDavResource(httpClient, new URL("https://sni.velox.ch"));
-
- boolean sniWorking = false;
- try {
- file.get("*/*");
- sniWorking = true;
- } catch (SSLPeerUnverifiedException e) {
- }
-
- assertTrue(sniWorking);
- }
-
- public void testMultiGet() throws Exception {
- WebDavResource davAddressBook = new WebDavResource(davCollection, "addressbooks/default.vcf");
- davAddressBook.multiGet(DavMultiget.Type.ADDRESS_BOOK, new String[] { "1.vcf", "2.vcf" });
- assertEquals(2, davAddressBook.getMembers().size());
- for (WebDavResource member : davAddressBook.getMembers()) {
- assertNotNull(member.getContent());
- }
- }
-
- public void testPutAddDontOverwrite() throws Exception {
- // should succeed on a non-existing file
- assertEquals("has-just-been-created", davNonExistingFile.put(SAMPLE_CONTENT, PutMode.ADD_DONT_OVERWRITE));
-
- // should fail on an existing file
- try {
- davExistingFile.put(SAMPLE_CONTENT, PutMode.ADD_DONT_OVERWRITE);
- fail();
- } catch(PreconditionFailedException ex) {
- }
- }
-
- public void testPutUpdateDontOverwrite() throws Exception {
- // should succeed on an existing file
- assertEquals("has-just-been-updated", davExistingFile.put(SAMPLE_CONTENT, PutMode.UPDATE_DONT_OVERWRITE));
-
- // should fail on a non-existing file
- try {
- davNonExistingFile.put(SAMPLE_CONTENT, PutMode.UPDATE_DONT_OVERWRITE);
- fail();
- } catch(PreconditionFailedException ex) {
- }
- }
-
- public void testDelete() throws Exception {
- // should succeed on an existing file
- davExistingFile.delete();
-
- // should fail on a non-existing file
- try {
- davNonExistingFile.delete();
- fail();
- } catch (NotFoundException e) {
- }
- }
-
-
- /* test CalDAV/CardDAV */
-
-
- /* special test */
-
- public void testInvalidURLs() throws Exception {
- WebDavResource dav = new WebDavResource(davInvalid, "addressbooks/user%40domain/");
- dav.propfind(HttpPropfind.Mode.CARDDAV_COLLECTIONS);
- List members = dav.getMembers();
- assertEquals(2, members.size());
- assertEquals(Constants.ROBOHYDRA_BASE + "dav/addressbooks/user%40domain/My%20Contacts%3a1.vcf/", members.get(0).getLocation().toString());
- assertEquals("https://example.com/user%40domain/absolute-url.vcf/", members.get(1).getLocation().toString());
- }
-
-}