mirror of
https://github.com/etesync/android
synced 2024-12-26 08:28:17 +00:00
Sync tasks
* remove VJOURNAL/notes sync (will be implemented later) * setup: add "install Tasks app" fragment * version bump to 0.8.0-beta1 * use Tasks instead of Mirakel * handle task list colors * allow independent selection of calendar/task sync for the same CalDAV calendar * minor refactoring (don't use return value of Builder) * handle more task fields and time zones * sync interval setting for tasks
This commit is contained in:
parent
aa7e582bc9
commit
af011a65db
29
app/src/androidTest/res/setup_task_lists_heading.xml
Normal file
29
app/src/androidTest/res/setup_task_lists_heading.xml
Normal file
@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2013 – 2015 Ricki Hirner (bitfire web engineering).
|
||||
~ All rights reserved. This program and the accompanying materials
|
||||
~ are made available under the terms of the GNU Public License v3.0
|
||||
~ which accompanies this distribution, and is available at
|
||||
~ http://www.gnu.org/licenses/gpl.html
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical" >
|
||||
|
||||
<TextView
|
||||
style="@style/TextView.Heading"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:text="@string/setup_task_lists" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:text="@string/setup_select_task_lists" />
|
||||
|
||||
</LinearLayout>
|
@ -9,7 +9,7 @@
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="at.bitfire.davdroid"
|
||||
android:versionCode="63" android:versionName="0.7.7"
|
||||
android:versionCode="64" android:versionName="0.8.0-beta1"
|
||||
android:installLocation="internalOnly">
|
||||
|
||||
<uses-sdk
|
||||
@ -26,11 +26,8 @@
|
||||
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
|
||||
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
|
||||
|
||||
<uses-permission android:name="at.bitfire.notebooks.provider.READ_WRITE_NOTES" />
|
||||
|
||||
<uses-permission android:name="de.azapps.mirakel.provider.READ_WRITE_DATA" />
|
||||
<uses-permission android:name="de.azapps.mirakel.provider.READ_DATA" />
|
||||
<uses-permission android:name="de.azapps.mirakel.provider.WRITE_DATA" />
|
||||
<uses-permission android:name="org.dmfs.permission.READ_TASKS" />
|
||||
<uses-permission android:name="org.dmfs.permission.WRITE_TASKS" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
@ -73,16 +70,6 @@
|
||||
android:name="android.content.SyncAdapter"
|
||||
android:resource="@xml/sync_calendars" />
|
||||
</service>
|
||||
<service
|
||||
android:name=".syncadapter.NotesSyncAdapterService"
|
||||
android:exported="true" >
|
||||
<intent-filter>
|
||||
<action android:name="android.content.SyncAdapter" />
|
||||
</intent-filter>
|
||||
<meta-data
|
||||
android:name="android.content.SyncAdapter"
|
||||
android:resource="@xml/sync_notes" />
|
||||
</service>
|
||||
<service
|
||||
android:name=".syncadapter.TasksSyncAdapterService"
|
||||
android:exported="true" >
|
||||
|
@ -11,7 +11,7 @@ import net.fortuna.ical4j.model.property.ProdId;
|
||||
|
||||
public class Constants {
|
||||
public static final String
|
||||
APP_VERSION = "0.7.7",
|
||||
APP_VERSION = "0.8.0-beta1",
|
||||
ACCOUNT_TYPE = "bitfire.at.davdroid",
|
||||
WEB_URL_HELP = "https://davdroid.bitfire.at/configuration?pk_campaign=davdroid-app",
|
||||
WEB_URL_VIEW_LOGS = "https://github.com/bitfireAT/davdroid/wiki/How-to-view-the-logs";
|
||||
|
20
app/src/main/java/at/bitfire/davdroid/DAVUtils.java
Normal file
20
app/src/main/java/at/bitfire/davdroid/DAVUtils.java
Normal file
@ -0,0 +1,20 @@
|
||||
package at.bitfire.davdroid;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class DAVUtils {
|
||||
public static int CalDAVtoARGBColor(String davColor) {
|
||||
int color = 0xFFC3EA6E; // fallback: "DAVdroid green"
|
||||
if (davColor != null) {
|
||||
Pattern p = Pattern.compile("#?(\\p{XDigit}{6})(\\p{XDigit}{2})?");
|
||||
Matcher m = p.matcher(davColor);
|
||||
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;
|
||||
}
|
||||
}
|
||||
return color;
|
||||
}
|
||||
}
|
@ -11,6 +11,10 @@ package at.bitfire.davdroid;
|
||||
import android.text.format.Time;
|
||||
import android.util.Log;
|
||||
|
||||
import net.fortuna.ical4j.model.DefaultTimeZoneRegistryFactory;
|
||||
import net.fortuna.ical4j.model.TimeZone;
|
||||
import net.fortuna.ical4j.model.TimeZoneRegistry;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
import java.util.SimpleTimeZone;
|
||||
@ -18,13 +22,16 @@ import java.util.SimpleTimeZone;
|
||||
public class DateUtils {
|
||||
private final static String TAG = "davdroid.DateUtils";
|
||||
|
||||
private final static TimeZoneRegistry tzRegistry = new DefaultTimeZoneRegistryFactory().createRegistry();
|
||||
|
||||
|
||||
public static String findAndroidTimezoneID(String tzID) {
|
||||
String localTZ = null;
|
||||
String availableTZs[] = SimpleTimeZone.getAvailableIDs();
|
||||
|
||||
// first, try to find an exact match (case insensitive)
|
||||
for (String availableTZ : availableTZs)
|
||||
if (tzID.equalsIgnoreCase(availableTZ)) {
|
||||
if (availableTZ.equalsIgnoreCase(tzID)) {
|
||||
localTZ = availableTZ;
|
||||
break;
|
||||
}
|
||||
@ -48,4 +55,9 @@ public class DateUtils {
|
||||
Log.d(TAG, "Assuming time zone " + localTZ + " for " + tzID);
|
||||
return localTZ;
|
||||
}
|
||||
|
||||
public static TimeZone getTimeZone(String tzID) {
|
||||
return tzRegistry.getTimeZone(tzID);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,72 +0,0 @@
|
||||
package at.bitfire.davdroid.resource;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.simpleframework.xml.Serializer;
|
||||
import org.simpleframework.xml.core.Persister;
|
||||
|
||||
import java.io.StringWriter;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
import at.bitfire.davdroid.webdav.DavCalendarQuery;
|
||||
import at.bitfire.davdroid.webdav.DavCompFilter;
|
||||
import at.bitfire.davdroid.webdav.DavFilter;
|
||||
import at.bitfire.davdroid.webdav.DavMultiget;
|
||||
import at.bitfire.davdroid.webdav.DavProp;
|
||||
|
||||
public class CalDavNotebook extends RemoteCollection<Note> {
|
||||
private final static String TAG = "davdroid.CalDAVNotebook";
|
||||
|
||||
public CalDavNotebook(CloseableHttpClient httpClient, String baseURL, String user, String password, boolean preemptiveAuth) throws URISyntaxException {
|
||||
super(httpClient, baseURL, user, password, preemptiveAuth);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String memberAcceptedMimeTypes()
|
||||
{
|
||||
return "text/calendar";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DavMultiget.Type multiGetType() {
|
||||
return DavMultiget.Type.CALENDAR;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Note newResourceSkeleton(String name, String ETag) {
|
||||
return new Note(name, ETag);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getMemberETagsQuery() {
|
||||
DavCalendarQuery query = new DavCalendarQuery();
|
||||
|
||||
// prop
|
||||
DavProp prop = new DavProp();
|
||||
prop.setGetetag(new DavProp.GetETag());
|
||||
query.setProp(prop);
|
||||
|
||||
// filter
|
||||
DavFilter filter = new DavFilter();
|
||||
query.setFilter(filter);
|
||||
|
||||
DavCompFilter compFilter = new DavCompFilter("VCALENDAR");
|
||||
filter.setCompFilter(compFilter);
|
||||
|
||||
compFilter.setCompFilter(new DavCompFilter("VJOURNAL"));
|
||||
|
||||
Serializer serializer = new Persister();
|
||||
StringWriter writer = new StringWriter();
|
||||
try {
|
||||
serializer.write(query, writer);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Couldn't prepare REPORT query", e);
|
||||
return null;
|
||||
}
|
||||
|
||||
return writer.toString();
|
||||
}
|
||||
|
||||
}
|
@ -9,6 +9,7 @@ package at.bitfire.davdroid.resource;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.apache.http.HttpException;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
@ -127,7 +128,9 @@ public class DavResourceFinder implements Closeable {
|
||||
if (homeSetCalendars.getMembers() != null)
|
||||
possibleCalendars.addAll(homeSetCalendars.getMembers());
|
||||
|
||||
List<ServerInfo.ResourceInfo> calendars = new LinkedList<>();
|
||||
List<ServerInfo.ResourceInfo>
|
||||
calendars = new LinkedList<>(),
|
||||
todoLists = new LinkedList<>();
|
||||
for (WebDavResource resource : possibleCalendars)
|
||||
if (resource.isCalendar()) {
|
||||
Log.i(TAG, "Found calendar: " + resource.getLocation().getPath());
|
||||
@ -140,36 +143,41 @@ public class DavResourceFinder implements Closeable {
|
||||
);
|
||||
info.setTimezone(resource.getTimezone());
|
||||
|
||||
boolean isCalendar = false,
|
||||
isTodoList = false;
|
||||
if (resource.getSupportedComponents() == null) {
|
||||
// no info about supported components, assuming all components are supported
|
||||
info.setSupportingEvents(true);
|
||||
info.setSupportingNotes(true);
|
||||
isCalendar = true;
|
||||
isTodoList = true;
|
||||
} else {
|
||||
// CALDAV:supported-calendar-component-set available
|
||||
for (String supportedComponent : resource.getSupportedComponents())
|
||||
if ("VEVENT".equalsIgnoreCase(supportedComponent))
|
||||
info.setSupportingEvents(true);
|
||||
else if ("VJOURNAL".equalsIgnoreCase(supportedComponent))
|
||||
info.setSupportingNotes(true);
|
||||
isCalendar = true;
|
||||
else if ("VTODO".equalsIgnoreCase(supportedComponent))
|
||||
info.setSupportingTasks(true);
|
||||
isTodoList = true;
|
||||
|
||||
if (!info.isSupportingEvents() && !info.isSupportingNotes() && !info.isSupportingTasks()) {
|
||||
Log.i(TAG, "Ignoring this calendar because it supports neither VEVENT nor VJOURNAL nor VTODO");
|
||||
if (!isCalendar && !isTodoList) {
|
||||
Log.i(TAG, "Ignoring this calendar because it supports neither VEVENT nor VTODO");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
calendars.add(info);
|
||||
// use a copy constructor to allow different "enabled" status for calendars and todo lists
|
||||
if (isCalendar)
|
||||
calendars.add(new ServerInfo.ResourceInfo(info));
|
||||
if (isTodoList)
|
||||
todoLists.add(new ServerInfo.ResourceInfo(info));
|
||||
}
|
||||
|
||||
serverInfo.setCalendars(calendars);
|
||||
serverInfo.setTodoLists(todoLists);
|
||||
} 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));
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -658,8 +658,7 @@ public class LocalAddressBook extends LocalCollection<Contact> {
|
||||
Contact contact = (Contact)resource;
|
||||
|
||||
if (!update)
|
||||
builder = builder
|
||||
.withValue(RawContacts.ACCOUNT_NAME, account.name)
|
||||
builder .withValue(RawContacts.ACCOUNT_NAME, account.name)
|
||||
.withValue(RawContacts.ACCOUNT_TYPE, account.type);
|
||||
|
||||
return builder
|
||||
@ -800,14 +799,13 @@ public class LocalAddressBook extends LocalCollection<Contact> {
|
||||
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);
|
||||
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);
|
||||
builder.withValue(Phone.LABEL, typeLabel);
|
||||
return builder;
|
||||
}
|
||||
|
||||
@ -834,14 +832,13 @@ public class LocalAddressBook extends LocalCollection<Contact> {
|
||||
}
|
||||
}
|
||||
|
||||
builder = builder
|
||||
.withValue(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE)
|
||||
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);
|
||||
builder.withValue(Email.LABEL, typeLabel);
|
||||
return builder;
|
||||
}
|
||||
|
||||
@ -928,22 +925,20 @@ public class LocalAddressBook extends LocalCollection<Contact> {
|
||||
|
||||
if (sipAddress)
|
||||
// save as SIP address
|
||||
builder = builder
|
||||
.withValue(Data.MIMETYPE, SipAddress.CONTENT_ITEM_TYPE)
|
||||
.withValue(Im.DATA, impp.getHandle())
|
||||
.withValue(Im.TYPE, typeCode);
|
||||
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);
|
||||
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);
|
||||
builder.withValue(Im.CUSTOM_PROTOCOL, protocolLabel);
|
||||
}
|
||||
if (typeLabel != null)
|
||||
builder = builder.withValue(Im.LABEL, typeLabel);
|
||||
builder.withValue(Im.LABEL, typeLabel);
|
||||
return builder;
|
||||
}
|
||||
|
||||
@ -996,19 +991,18 @@ public class LocalAddressBook extends LocalCollection<Contact> {
|
||||
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());
|
||||
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);
|
||||
builder.withValue(StructuredPostal.LABEL, typeLabel);
|
||||
return builder;
|
||||
}
|
||||
|
||||
|
@ -75,6 +75,7 @@ import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import at.bitfire.davdroid.DAVUtils;
|
||||
import at.bitfire.davdroid.DateUtils;
|
||||
import lombok.Cleanup;
|
||||
import lombok.Getter;
|
||||
@ -120,24 +121,13 @@ public class LocalCalendar extends LocalCollection<Event> {
|
||||
final 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.CALENDAR_COLOR, DAVUtils.CalDAVtoARGBColor(info.getColor()));
|
||||
values.put(Calendars.OWNER_ACCOUNT, account.name);
|
||||
values.put(Calendars.SYNC_EVENTS, 1);
|
||||
values.put(Calendars.VISIBLE, 1);
|
||||
@ -527,12 +517,10 @@ public class LocalCalendar extends LocalCollection<Event> {
|
||||
final Event event = (Event)resource;
|
||||
|
||||
if (!update)
|
||||
builder = builder
|
||||
.withValue(Events.ACCOUNT_TYPE, account.type)
|
||||
builder .withValue(Events.ACCOUNT_TYPE, account.type)
|
||||
.withValue(Events.ACCOUNT_NAME, account.name);
|
||||
|
||||
builder = builder
|
||||
.withValue(Events.CALENDAR_ID, id)
|
||||
builder .withValue(Events.CALENDAR_ID, id)
|
||||
.withValue(Events.ALL_DAY, event.isAllDay() ? 1 : 0)
|
||||
.withValue(Events.DTSTART, event.getDtStartInMillis())
|
||||
.withValue(Events.EVENT_TIMEZONE, event.getDtStartTzID())
|
||||
@ -545,12 +533,11 @@ public class LocalCalendar extends LocalCollection<Event> {
|
||||
final RecurrenceId recurrenceId = event.getRecurrenceId();
|
||||
if (recurrenceId == null) {
|
||||
// this event is a "master event" (not an exception)
|
||||
builder = builder
|
||||
.withValue(entryColumnRemoteName(), event.getName())
|
||||
builder .withValue(entryColumnRemoteName(), event.getName())
|
||||
.withValue(entryColumnETag(), event.getETag())
|
||||
.withValue(entryColumnUID(), event.getUid());
|
||||
} else {
|
||||
builder = builder.withValue(Events.ORIGINAL_SYNC_ID, event.getName());
|
||||
builder.withValue(Events.ORIGINAL_SYNC_ID, event.getName());
|
||||
|
||||
// ORIGINAL_INSTANCE_TIME and ORIGINAL_ALL_DAY is set in buildExceptions.
|
||||
// It's not possible to use only the RECURRENCE-ID to calculate
|
||||
@ -561,40 +548,38 @@ public class LocalCalendar extends LocalCollection<Event> {
|
||||
boolean recurring = false;
|
||||
if (event.getRrule() != null) {
|
||||
recurring = true;
|
||||
builder = builder.withValue(Events.RRULE, event.getRrule().getValue());
|
||||
builder.withValue(Events.RRULE, event.getRrule().getValue());
|
||||
}
|
||||
if (!event.getRdates().isEmpty()) {
|
||||
recurring = true;
|
||||
builder = builder.withValue(Events.RDATE, recurrenceSetsToAndroidString(event.getRdates()));
|
||||
builder.withValue(Events.RDATE, recurrenceSetsToAndroidString(event.getRdates()));
|
||||
}
|
||||
if (event.getExrule() != null)
|
||||
builder = builder.withValue(Events.EXRULE, event.getExrule().getValue());
|
||||
builder.withValue(Events.EXRULE, event.getExrule().getValue());
|
||||
if (!event.getExdates().isEmpty())
|
||||
builder = builder.withValue(Events.EXDATE, recurrenceSetsToAndroidString(event.getExdates()));
|
||||
builder.withValue(Events.EXDATE, recurrenceSetsToAndroidString(event.getExdates()));
|
||||
|
||||
// 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())
|
||||
builder.withValue(Events.DURATION, duration.getValue());
|
||||
} else
|
||||
builder .withValue(Events.DTEND, event.getDtEndInMillis())
|
||||
.withValue(Events.EVENT_END_TIMEZONE, event.getDtEndTzID());
|
||||
}
|
||||
|
||||
|
||||
if (event.getSummary() != null)
|
||||
builder = builder.withValue(Events.TITLE, event.getSummary());
|
||||
builder.withValue(Events.TITLE, event.getSummary());
|
||||
if (event.getLocation() != null)
|
||||
builder = builder.withValue(Events.EVENT_LOCATION, event.getLocation());
|
||||
builder.withValue(Events.EVENT_LOCATION, event.getLocation());
|
||||
if (event.getDescription() != null)
|
||||
builder = builder.withValue(Events.DESCRIPTION, event.getDescription());
|
||||
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());
|
||||
builder.withValue(Events.ORGANIZER, organizer.getSchemeSpecificPart());
|
||||
}
|
||||
|
||||
Status status = event.getStatus();
|
||||
@ -604,13 +589,13 @@ public class LocalCalendar extends LocalCollection<Event> {
|
||||
statusCode = Events.STATUS_CONFIRMED;
|
||||
else if (status == Status.VEVENT_CANCELLED)
|
||||
statusCode = Events.STATUS_CANCELED;
|
||||
builder = builder.withValue(Events.STATUS, statusCode);
|
||||
builder.withValue(Events.STATUS, statusCode);
|
||||
}
|
||||
|
||||
builder = builder.withValue(Events.AVAILABILITY, event.isOpaque() ? Events.AVAILABILITY_BUSY : Events.AVAILABILITY_FREE);
|
||||
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);
|
||||
builder.withValue(Events.ACCESS_LEVEL, event.getForPublic() ? Events.ACCESS_PUBLIC : Events.ACCESS_PRIVATE);
|
||||
|
||||
return builder;
|
||||
}
|
||||
@ -683,7 +668,7 @@ public class LocalCalendar extends LocalCollection<Event> {
|
||||
|
||||
final Cn cn = (Cn)attendee.getParameter(Parameter.CN);
|
||||
if (cn != null)
|
||||
builder = builder.withValue(Attendees.ATTENDEE_NAME, cn.getValue());
|
||||
builder.withValue(Attendees.ATTENDEE_NAME, cn.getValue());
|
||||
|
||||
int type = Attendees.TYPE_NONE;
|
||||
|
||||
@ -702,7 +687,7 @@ public class LocalCalendar extends LocalCollection<Event> {
|
||||
else if (role == Role.REQ_PARTICIPANT)
|
||||
type = Attendees.TYPE_REQUIRED;
|
||||
}
|
||||
builder = builder.withValue(Attendees.ATTENDEE_RELATIONSHIP, relationship);
|
||||
builder.withValue(Attendees.ATTENDEE_RELATIONSHIP, relationship);
|
||||
}
|
||||
|
||||
int status = Attendees.ATTENDEE_STATUS_NONE;
|
||||
|
@ -1,179 +0,0 @@
|
||||
package at.bitfire.davdroid.resource;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.content.ContentProviderClient;
|
||||
import android.content.ContentProviderOperation;
|
||||
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.RemoteException;
|
||||
import android.provider.CalendarContract;
|
||||
import android.util.Log;
|
||||
|
||||
import net.fortuna.ical4j.model.DateTime;
|
||||
import net.fortuna.ical4j.model.property.Created;
|
||||
import net.fortuna.ical4j.model.property.DtStamp;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import at.bitfire.davdroid.Constants;
|
||||
import at.bitfire.notebooks.provider.NoteContract;
|
||||
import lombok.Cleanup;
|
||||
import lombok.Getter;
|
||||
|
||||
public class LocalNotebook extends LocalCollection<Note> {
|
||||
private final static String TAG = "davdroid.LocalNotebook";
|
||||
|
||||
@Getter protected final String url;
|
||||
@Getter protected final long id;
|
||||
|
||||
protected static String COLLECTION_COLUMN_CTAG = NoteContract.Notebooks.SYNC1;
|
||||
|
||||
@Override protected Uri entriesURI() { return syncAdapterURI(NoteContract.Notes.CONTENT_URI); }
|
||||
@Override protected String entryColumnAccountType() { return NoteContract.Notes.ACCOUNT_TYPE; }
|
||||
@Override protected String entryColumnAccountName() { return NoteContract.Notes.ACCOUNT_NAME; }
|
||||
@Override protected String entryColumnParentID() { return NoteContract.Notes.NOTEBOOK_ID; }
|
||||
@Override protected String entryColumnID() { return NoteContract.Notes._ID; }
|
||||
@Override protected String entryColumnRemoteName() { return NoteContract.Notes._SYNC_ID; }
|
||||
@Override protected String entryColumnETag() { return NoteContract.Notes.SYNC1; }
|
||||
@Override protected String entryColumnDirty() { return NoteContract.Notes.DIRTY; }
|
||||
@Override protected String entryColumnDeleted() { return NoteContract.Notes.DELETED; }
|
||||
@Override protected String entryColumnUID() { return NoteContract.Notes.UID; }
|
||||
|
||||
|
||||
public static Uri create(Account account, ContentResolver resolver, ServerInfo.ResourceInfo info) throws LocalStorageException {
|
||||
final ContentProviderClient client = resolver.acquireContentProviderClient(NoteContract.AUTHORITY);
|
||||
if (client == null)
|
||||
throw new LocalStorageException("No notes provider found");
|
||||
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(NoteContract.Notebooks._SYNC_ID, info.getURL());
|
||||
values.put(NoteContract.Notebooks.NAME, info.getTitle());
|
||||
|
||||
Log.i(TAG, "Inserting notebook: " + values.toString());
|
||||
try {
|
||||
return client.insert(notebooksURI(account), values);
|
||||
} catch (RemoteException e) {
|
||||
throw new LocalStorageException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static LocalNotebook[] findAll(Account account, ContentProviderClient providerClient) throws RemoteException {
|
||||
@Cleanup Cursor cursor = providerClient.query(notebooksURI(account),
|
||||
new String[] { NoteContract.Notebooks._ID, NoteContract.Notebooks._SYNC_ID },
|
||||
NoteContract.Notebooks.DELETED + "=0", null, null);
|
||||
|
||||
LinkedList<LocalNotebook> notebooks = new LinkedList<>();
|
||||
while (cursor != null && cursor.moveToNext())
|
||||
notebooks.add(new LocalNotebook(account, providerClient, cursor.getInt(0), cursor.getString(1)));
|
||||
return notebooks.toArray(new LocalNotebook[0]);
|
||||
}
|
||||
|
||||
public LocalNotebook(Account account, ContentProviderClient providerClient, long id, String url) throws RemoteException {
|
||||
super(account, providerClient);
|
||||
this.id = id;
|
||||
this.url = url;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getCTag() throws LocalStorageException {
|
||||
try {
|
||||
@Cleanup Cursor c = providerClient.query(ContentUris.withAppendedId(notebooksURI(account), id),
|
||||
new String[] { COLLECTION_COLUMN_CTAG }, null, null, null);
|
||||
if (c != null && c.moveToFirst())
|
||||
return c.getString(0);
|
||||
else
|
||||
throw new LocalStorageException("Couldn't query notebook 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(notebooksURI(account), id), values, null, null);
|
||||
} catch(RemoteException e) {
|
||||
throw new LocalStorageException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Note newResource(long localID, String resourceName, String eTag) {
|
||||
return new Note(localID, resourceName, eTag);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void populate(Resource record) throws LocalStorageException {
|
||||
try {
|
||||
@Cleanup final Cursor cursor = providerClient.query(entriesURI(),
|
||||
new String[] {
|
||||
/* 0 */ entryColumnUID(), NoteContract.Notes.CREATED_AT, NoteContract.Notes.UPDATED_AT, NoteContract.Notes.DTSTART,
|
||||
/* 4 */ NoteContract.Notes.SUMMARY, NoteContract.Notes.DESCRIPTION, NoteContract.Notes.COMMENT,
|
||||
/* 7 */ NoteContract.Notes.ORGANIZER, NoteContract.Notes.STATUS, NoteContract.Notes.CLASSIFICATION,
|
||||
/* 10 */ NoteContract.Notes.CONTACT, NoteContract.Notes.URL
|
||||
}, entryColumnID() + "=?", new String[]{ String.valueOf(record.getLocalID()) }, null);
|
||||
|
||||
Note note = (Note)record;
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
note.setUid(cursor.getString(0));
|
||||
|
||||
if (!cursor.isNull(1))
|
||||
note.setCreated(new Created(new DateTime(cursor.getLong(1))));
|
||||
|
||||
note.setSummary(cursor.getString(4));
|
||||
note.setDescription(cursor.getString(5));
|
||||
}
|
||||
|
||||
} catch (RemoteException e) {
|
||||
throw new LocalStorageException("Couldn't process locally stored note", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ContentProviderOperation.Builder buildEntry(ContentProviderOperation.Builder builder, Resource resource, boolean update) {
|
||||
final Note note = (Note)resource;
|
||||
builder = builder
|
||||
.withValue(entryColumnParentID(), id)
|
||||
.withValue(entryColumnRemoteName(), note.getName())
|
||||
.withValue(entryColumnUID(), note.getUid())
|
||||
.withValue(entryColumnETag(), note.getETag())
|
||||
.withValue(NoteContract.Notes.SUMMARY, note.getSummary())
|
||||
.withValue(NoteContract.Notes.DESCRIPTION, note.getDescription());
|
||||
|
||||
if (note.getCreated() != null)
|
||||
builder = builder.withValue(NoteContract.Notes.CREATED_AT, note.getCreated().getDateTime().getTime());
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addDataRows(Resource resource, long localID, int backrefIdx) {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void removeDataRows(Resource resource) {
|
||||
}
|
||||
|
||||
|
||||
// helpers
|
||||
|
||||
protected static Uri notebooksURI(Account account) {
|
||||
return NoteContract.Notebooks.CONTENT_URI.buildUpon()
|
||||
.appendQueryParameter(NoteContract.Notebooks.ACCOUNT_TYPE, account.type)
|
||||
.appendQueryParameter(NoteContract.Notebooks.ACCOUNT_NAME, account.name)
|
||||
.appendQueryParameter(NoteContract.CALLER_IS_SYNCADAPTER, "true")
|
||||
.build();
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@ import android.content.ContentProviderOperation;
|
||||
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.RemoteException;
|
||||
@ -13,15 +14,25 @@ import android.util.Log;
|
||||
|
||||
import net.fortuna.ical4j.model.Date;
|
||||
import net.fortuna.ical4j.model.DateTime;
|
||||
import net.fortuna.ical4j.model.Dur;
|
||||
import net.fortuna.ical4j.model.TimeZone;
|
||||
import net.fortuna.ical4j.model.TimeZoneRegistry;
|
||||
import net.fortuna.ical4j.model.property.Clazz;
|
||||
import net.fortuna.ical4j.model.property.Completed;
|
||||
import net.fortuna.ical4j.model.property.Created;
|
||||
import net.fortuna.ical4j.model.property.DtStart;
|
||||
import net.fortuna.ical4j.model.property.Due;
|
||||
import net.fortuna.ical4j.model.property.Duration;
|
||||
import net.fortuna.ical4j.model.property.Status;
|
||||
import net.fortuna.ical4j.util.TimeZones;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.dmfs.provider.tasks.TaskContract;
|
||||
|
||||
import java.util.LinkedList;
|
||||
|
||||
import at.bitfire.davdroid.DAVUtils;
|
||||
import at.bitfire.davdroid.DateUtils;
|
||||
import lombok.Cleanup;
|
||||
import lombok.Getter;
|
||||
|
||||
@ -31,9 +42,11 @@ public class LocalTaskList extends LocalCollection<Task> {
|
||||
@Getter protected String url;
|
||||
@Getter protected long id;
|
||||
|
||||
public static final String TASKS_AUTHORITY = "org.dmfs.tasks";
|
||||
|
||||
protected static String COLLECTION_COLUMN_CTAG = TaskContract.TaskLists.SYNC1;
|
||||
|
||||
@Override protected Uri entriesURI() { return syncAdapterURI(TaskContract.Tasks.CONTENT_URI); }
|
||||
@Override protected Uri entriesURI() { return syncAdapterURI(TaskContract.Tasks.getContentUri(TASKS_AUTHORITY)); }
|
||||
@Override protected String entryColumnAccountType() { return TaskContract.Tasks.ACCOUNT_TYPE; }
|
||||
@Override protected String entryColumnAccountName() { return TaskContract.Tasks.ACCOUNT_NAME; }
|
||||
@Override protected String entryColumnParentID() { return TaskContract.Tasks.LIST_ID; }
|
||||
@ -46,15 +59,16 @@ public class LocalTaskList extends LocalCollection<Task> {
|
||||
|
||||
|
||||
public static Uri create(Account account, ContentResolver resolver, ServerInfo.ResourceInfo info) throws LocalStorageException {
|
||||
final ContentProviderClient client = resolver.acquireContentProviderClient(TaskContract.AUTHORITY);
|
||||
final ContentProviderClient client = resolver.acquireContentProviderClient(TASKS_AUTHORITY);
|
||||
if (client == null)
|
||||
throw new LocalStorageException("No tasks provider found");
|
||||
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(TaskContract.TaskLists.ACCOUNT_NAME, account.name);
|
||||
values.put(TaskContract.TaskLists.ACCOUNT_TYPE, /*account.type*/"davdroid.new");
|
||||
values.put(TaskContract.TaskLists.ACCOUNT_TYPE, account.type);
|
||||
values.put(TaskContract.TaskLists._SYNC_ID, info.getURL());
|
||||
values.put(TaskContract.TaskLists.LIST_NAME, info.getTitle());
|
||||
values.put(TaskContract.TaskLists.LIST_COLOR, DAVUtils.CalDAVtoARGBColor(info.getColor()));
|
||||
values.put(TaskContract.TaskLists.OWNER, account.name);
|
||||
values.put(TaskContract.TaskLists.ACCESS_LEVEL, 0);
|
||||
values.put(TaskContract.TaskLists.SYNC_ENABLED, 1);
|
||||
@ -122,19 +136,36 @@ public class LocalTaskList extends LocalCollection<Task> {
|
||||
try {
|
||||
@Cleanup final Cursor cursor = providerClient.query(entriesURI(),
|
||||
new String[] {
|
||||
/* 0 */ entryColumnUID(), TaskContract.Tasks.TITLE, TaskContract.Tasks.LOCATION, TaskContract.Tasks.DESCRIPTION, TaskContract.Tasks.URL,
|
||||
/* 5 */ TaskContract.Tasks.CLASSIFICATION, TaskContract.Tasks.STATUS, TaskContract.Tasks.PERCENT_COMPLETE,
|
||||
/* 8 */ TaskContract.Tasks.DTSTART, TaskContract.Tasks.IS_ALLDAY, /*TaskContract.Tasks.COMPLETED, TaskContract.Tasks.COMPLETED_IS_ALLDAY*/
|
||||
/* 0 */ entryColumnUID(), TaskContract.Tasks.TITLE, TaskContract.Tasks.LOCATION, TaskContract.Tasks.DESCRIPTION, TaskContract.Tasks.URL,
|
||||
/* 5 */ TaskContract.Tasks.CLASSIFICATION, TaskContract.Tasks.STATUS, TaskContract.Tasks.PERCENT_COMPLETE,
|
||||
/* 8 */ TaskContract.Tasks.TZ, TaskContract.Tasks.DTSTART, TaskContract.Tasks.IS_ALLDAY,
|
||||
/* 11 */ TaskContract.Tasks.DUE, TaskContract.Tasks.DURATION, TaskContract.Tasks.COMPLETED,
|
||||
/* 14 */ TaskContract.Tasks.CREATED, TaskContract.Tasks.LAST_MODIFIED, TaskContract.Tasks.PRIORITY
|
||||
}, entryColumnID() + "=?", new String[]{ String.valueOf(record.getLocalID()) }, null);
|
||||
|
||||
Task task = (Task)record;
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
task.setUid(cursor.getString(0));
|
||||
|
||||
task.setSummary(cursor.getString(1));
|
||||
task.setLocation(cursor.getString(2));
|
||||
task.setDescription(cursor.getString(3));
|
||||
task.setUrl(cursor.getString(4));
|
||||
if (!cursor.isNull(14))
|
||||
task.setCreatedAt(new DateTime(cursor.getLong(14)));
|
||||
if (!cursor.isNull(15))
|
||||
task.setLastModified(new DateTime(cursor.getLong(15)));
|
||||
|
||||
if (!StringUtils.isEmpty(cursor.getString(1)))
|
||||
task.setSummary(cursor.getString(1));
|
||||
|
||||
if (!StringUtils.isEmpty(cursor.getString(2)))
|
||||
task.setLocation(cursor.getString(2));
|
||||
|
||||
if (!StringUtils.isEmpty(cursor.getString(3)))
|
||||
task.setDescription(cursor.getString(3));
|
||||
|
||||
if (!StringUtils.isEmpty(cursor.getString(4)))
|
||||
task.setUrl(cursor.getString(4));
|
||||
|
||||
if (!cursor.isNull(16))
|
||||
task.setPriority(cursor.getInt(16));
|
||||
|
||||
if (!cursor.isNull(5))
|
||||
switch (cursor.getInt(5)) {
|
||||
@ -165,17 +196,37 @@ public class LocalTaskList extends LocalCollection<Task> {
|
||||
if (!cursor.isNull(7))
|
||||
task.setPercentComplete(cursor.getInt(7));
|
||||
|
||||
if (!cursor.isNull(8) && !cursor.isNull(9)) {
|
||||
long ts = cursor.getLong(8);
|
||||
boolean allDay = cursor.getInt(9) != 0;
|
||||
task.setDtStart(new DtStart(allDay ? new Date(ts) : new DateTime(ts)));
|
||||
TimeZone tz = null;
|
||||
if (!cursor.isNull(8))
|
||||
tz = DateUtils.getTimeZone(cursor.getString(8));
|
||||
|
||||
if (!cursor.isNull(9) && !cursor.isNull(10)) {
|
||||
long ts = cursor.getLong(9);
|
||||
boolean allDay = cursor.getInt(10) != 0;
|
||||
|
||||
Date dt;
|
||||
if (allDay)
|
||||
dt = new Date(ts);
|
||||
else {
|
||||
dt = new DateTime(ts);
|
||||
if (tz != null)
|
||||
((DateTime)dt).setTimeZone(tz);
|
||||
}
|
||||
task.setDtStart(new DtStart(dt));
|
||||
}
|
||||
|
||||
/*if (!cursor.isNull(10) && !cursor.isNull(11)) {
|
||||
long ts = cursor.getLong(10);
|
||||
// boolean allDay = cursor.getInt(11) != 0;
|
||||
task.setCompletedAt(new Completed(allDay ? new Date(ts) : new DateTime(ts)));
|
||||
}*/
|
||||
if (!cursor.isNull(11)) {
|
||||
DateTime dt = new DateTime(cursor.getLong(11));
|
||||
if (tz != null)
|
||||
dt.setTimeZone(tz);
|
||||
task.setDue(new Due(dt));
|
||||
}
|
||||
|
||||
if (!cursor.isNull(12))
|
||||
task.setDuration(new Duration(new Dur(cursor.getString(12))));
|
||||
|
||||
if (!cursor.isNull(13))
|
||||
task.setCompletedAt(new Completed(new DateTime(cursor.getLong(13))));
|
||||
}
|
||||
|
||||
} catch (RemoteException e) {
|
||||
@ -188,17 +239,21 @@ public class LocalTaskList extends LocalCollection<Task> {
|
||||
final Task task = (Task)resource;
|
||||
|
||||
if (!update)
|
||||
builder = builder
|
||||
.withValue(entryColumnParentID(), id)
|
||||
builder .withValue(entryColumnParentID(), id)
|
||||
.withValue(entryColumnRemoteName(), task.getName());
|
||||
|
||||
builder = builder
|
||||
.withValue(entryColumnUID(), task.getUid())
|
||||
builder.withValue(entryColumnUID(), task.getUid())
|
||||
.withValue(entryColumnETag(), task.getETag())
|
||||
.withValue(TaskContract.Tasks.TITLE, task.getSummary())
|
||||
.withValue(TaskContract.Tasks.LOCATION, task.getLocation())
|
||||
.withValue(TaskContract.Tasks.DESCRIPTION, task.getDescription())
|
||||
.withValue(TaskContract.Tasks.URL, task.getUrl());
|
||||
.withValue(TaskContract.Tasks.URL, task.getUrl())
|
||||
.withValue(TaskContract.Tasks.PRIORITY, task.getPriority());
|
||||
|
||||
if (task.getCreatedAt() != null)
|
||||
builder.withValue(TaskContract.Tasks.CREATED, task.getCreatedAt().getTime());
|
||||
if (task.getLastModified() != null)
|
||||
builder.withValue(TaskContract.Tasks.LAST_MODIFIED, task.getLastModified().getTime());
|
||||
|
||||
if (task.getClassification() != null) {
|
||||
int classCode = TaskContract.Tasks.CLASSIFICATION_PRIVATE;
|
||||
@ -220,40 +275,54 @@ public class LocalTaskList extends LocalCollection<Task> {
|
||||
else if (task.getStatus() == Status.VTODO_CANCELLED)
|
||||
statusCode = TaskContract.Tasks.STATUS_CANCELLED;
|
||||
}
|
||||
builder = builder
|
||||
.withValue(TaskContract.Tasks.STATUS, statusCode)
|
||||
builder .withValue(TaskContract.Tasks.STATUS, statusCode)
|
||||
.withValue(TaskContract.Tasks.PERCENT_COMPLETE, task.getPercentComplete());
|
||||
|
||||
/*if (task.getCreatedAt() != null)
|
||||
builder = builder.withValue(TaskContract.Tasks.CREATED, task.getCreatedAt().getDate().getTime());/*
|
||||
TimeZone tz = null;
|
||||
|
||||
if (task.getDtStart() != null) {
|
||||
Date start = task.getDtStart().getDate();
|
||||
boolean allDay;
|
||||
if (start instanceof DateTime)
|
||||
if (start instanceof DateTime) {
|
||||
allDay = false;
|
||||
else {
|
||||
task.getDtStart().setUtc(true);
|
||||
tz = ((DateTime)start).getTimeZone();
|
||||
} else
|
||||
allDay = true;
|
||||
}
|
||||
long ts = start.getTime();
|
||||
builder = builder.withValue(TaskContract.Tasks.DTSTART, ts);
|
||||
builder = builder.withValue(TaskContract.Tasks.IS_ALLDAY, allDay ? 1 : 0);
|
||||
builder .withValue(TaskContract.Tasks.DTSTART, ts)
|
||||
.withValue(TaskContract.Tasks.IS_ALLDAY, allDay ? 1 : 0);
|
||||
}
|
||||
|
||||
/*if (task.getCompletedAt() != null) {
|
||||
if (task.getDue() != null) {
|
||||
Due due = task.getDue();
|
||||
builder.withValue(TaskContract.Tasks.DUE, due.getDate().getTime());
|
||||
if (tz == null)
|
||||
tz = due.getTimeZone();
|
||||
|
||||
} else if (task.getDuration() != null)
|
||||
builder.withValue(TaskContract.Tasks.DURATION, task.getDuration().getValue());
|
||||
|
||||
if (task.getCompletedAt() != null) {
|
||||
Date completed = task.getCompletedAt().getDate();
|
||||
boolean allDay;
|
||||
if (completed instanceof DateTime)
|
||||
if (completed instanceof DateTime) {
|
||||
allDay = false;
|
||||
else {
|
||||
if (tz == null)
|
||||
tz = ((DateTime)completed).getTimeZone();
|
||||
} else {
|
||||
task.getCompletedAt().setUtc(true);
|
||||
allDay = true;
|
||||
}
|
||||
long ts = completed.getTime();
|
||||
builder = builder.withValue(TaskContract.Tasks.COMPLETED, ts);
|
||||
builder = builder.withValue(TaskContract.Tasks.COMPLETED_IS_ALLDAY, allDay ? 1 : 0);
|
||||
}*/
|
||||
builder .withValue(TaskContract.Tasks.COMPLETED, ts)
|
||||
.withValue(TaskContract.Tasks.COMPLETED_IS_ALLDAY, allDay ? 1 : 0);
|
||||
}
|
||||
|
||||
// TZ *must* be provided when DTSTART or DUE is set
|
||||
if ((task.getDtStart() != null || task.getDue() != null) && tz == null)
|
||||
tz = DateUtils.getTimeZone(TimeZones.GMT_ID);
|
||||
if (tz != null)
|
||||
builder.withValue(TaskContract.Tasks.TZ, DateUtils.findAndroidTimezoneID(tz.getID()));
|
||||
|
||||
return builder;
|
||||
}
|
||||
@ -269,18 +338,28 @@ public class LocalTaskList extends LocalCollection<Task> {
|
||||
|
||||
// helpers
|
||||
|
||||
public static boolean isAvailable(Context context) {
|
||||
try {
|
||||
@Cleanup("release") ContentProviderClient client = context.getContentResolver().acquireContentProviderClient(TASKS_AUTHORITY);
|
||||
return client != null;
|
||||
} catch (SecurityException e) {
|
||||
Log.e(TAG, "DAVdroid is not allowed to access tasks", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Uri syncAdapterURI(Uri baseURI) {
|
||||
return baseURI.buildUpon()
|
||||
.appendQueryParameter(entryColumnAccountType(), /*account.type*/"davdroid.new")
|
||||
.appendQueryParameter(entryColumnAccountType(), account.type)
|
||||
.appendQueryParameter(entryColumnAccountName(), account.name)
|
||||
.appendQueryParameter(TaskContract.CALLER_IS_SYNCADAPTER, "true")
|
||||
.build();
|
||||
}
|
||||
|
||||
protected static Uri taskListsURI(Account account) {
|
||||
return TaskContract.TaskLists.CONTENT_URI.buildUpon()
|
||||
.appendQueryParameter(TaskContract.TaskLists.ACCOUNT_TYPE, /*account.type*/"davdroid.new")
|
||||
return TaskContract.TaskLists.getContentUri(TASKS_AUTHORITY).buildUpon()
|
||||
.appendQueryParameter(TaskContract.TaskLists.ACCOUNT_TYPE, account.type)
|
||||
.appendQueryParameter(TaskContract.TaskLists.ACCOUNT_NAME, account.name)
|
||||
.appendQueryParameter(TaskContract.CALLER_IS_SYNCADAPTER, "true")
|
||||
.build();
|
||||
|
@ -19,9 +19,7 @@ import lombok.RequiredArgsConstructor;
|
||||
|
||||
@RequiredArgsConstructor(suppressConstructorProperties=true)
|
||||
@Data
|
||||
public class ServerInfo implements Serializable {
|
||||
private static final long serialVersionUID = 6744847358282980437L;
|
||||
|
||||
public class ServerInfo {
|
||||
enum Scheme {
|
||||
HTTP, HTTPS, MAILTO
|
||||
}
|
||||
@ -34,8 +32,9 @@ public class ServerInfo implements Serializable {
|
||||
|
||||
private boolean calDAV = false, cardDAV = false;
|
||||
private List<ResourceInfo>
|
||||
addressBooks = new LinkedList<ResourceInfo>(),
|
||||
calendars = new LinkedList<ResourceInfo>();
|
||||
addressBooks = new LinkedList<>(),
|
||||
calendars = new LinkedList<>(),
|
||||
todoLists = new LinkedList<>();
|
||||
|
||||
|
||||
public boolean hasEnabledCalendars() {
|
||||
@ -48,9 +47,7 @@ public class ServerInfo implements Serializable {
|
||||
|
||||
@RequiredArgsConstructor(suppressConstructorProperties=true)
|
||||
@Data
|
||||
public static class ResourceInfo implements Serializable {
|
||||
private static final long serialVersionUID = -5516934508229552112L;
|
||||
|
||||
public static class ResourceInfo {
|
||||
public enum Type {
|
||||
ADDRESS_BOOK,
|
||||
CALENDAR
|
||||
@ -69,9 +66,25 @@ public class ServerInfo implements Serializable {
|
||||
VCardVersion vCardVersion;
|
||||
|
||||
String timezone;
|
||||
boolean supportingEvents = false,
|
||||
supportingNotes = false,
|
||||
supportingTasks = false;
|
||||
|
||||
|
||||
// copy constructor
|
||||
public ResourceInfo(ResourceInfo src) {
|
||||
enabled = src.enabled;
|
||||
type = src.type;
|
||||
readOnly = src.readOnly;
|
||||
|
||||
URL = src.URL;
|
||||
title = src.title;
|
||||
description = src.description;
|
||||
color = src.color;
|
||||
|
||||
vCardVersion = src.vCardVersion;
|
||||
timezone = src.timezone;
|
||||
}
|
||||
|
||||
|
||||
// some logic
|
||||
|
||||
public String getTitle() {
|
||||
if (title == null) {
|
||||
|
@ -7,6 +7,8 @@ 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.PropertyList;
|
||||
import net.fortuna.ical4j.model.ValidationException;
|
||||
import net.fortuna.ical4j.model.component.VToDo;
|
||||
@ -15,6 +17,9 @@ import net.fortuna.ical4j.model.property.Completed;
|
||||
import net.fortuna.ical4j.model.property.Created;
|
||||
import net.fortuna.ical4j.model.property.Description;
|
||||
import net.fortuna.ical4j.model.property.DtStart;
|
||||
import net.fortuna.ical4j.model.property.Due;
|
||||
import net.fortuna.ical4j.model.property.Duration;
|
||||
import net.fortuna.ical4j.model.property.LastModified;
|
||||
import net.fortuna.ical4j.model.property.Location;
|
||||
import net.fortuna.ical4j.model.property.PercentComplete;
|
||||
import net.fortuna.ical4j.model.property.Priority;
|
||||
@ -41,13 +46,17 @@ import lombok.Setter;
|
||||
public class Task extends Resource {
|
||||
private final static String TAG = "davdroid.Task";
|
||||
|
||||
@Getter @Setter DateTime createdAt;
|
||||
@Getter @Setter DateTime lastModified;
|
||||
|
||||
@Getter @Setter String summary, location, description, url;
|
||||
@Getter @Setter int priority;
|
||||
@Getter @Setter Clazz classification;
|
||||
@Getter @Setter Status status;
|
||||
|
||||
@Getter @Setter Created createdAt;
|
||||
@Getter @Setter DtStart dtStart;
|
||||
@Getter @Setter Due due;
|
||||
@Getter @Setter Duration duration;
|
||||
@Getter @Setter Completed completedAt;
|
||||
@Getter @Setter Integer percentComplete;
|
||||
|
||||
@ -90,6 +99,11 @@ public class Task extends Resource {
|
||||
if (todo.getUid() != null)
|
||||
uid = todo.getUid().getValue();
|
||||
|
||||
if (todo.getCreated() != null)
|
||||
createdAt = todo.getCreated().getDateTime();
|
||||
if (todo.getLastModified() != null)
|
||||
lastModified = todo.getLastModified().getDateTime();
|
||||
|
||||
if (todo.getSummary() != null)
|
||||
summary = todo.getSummary().getValue();
|
||||
if (todo.getLocation() != null)
|
||||
@ -105,8 +119,10 @@ public class Task extends Resource {
|
||||
if (todo.getStatus() != null)
|
||||
status = todo.getStatus();
|
||||
|
||||
if (todo.getCreated() != null)
|
||||
createdAt = todo.getCreated();
|
||||
if (todo.getDue() != null)
|
||||
due = todo.getDue();
|
||||
if (todo.getDuration() != null)
|
||||
duration = todo.getDuration();
|
||||
if (todo.getStartDate() != null)
|
||||
dtStart = todo.getStartDate();
|
||||
if (todo.getDateCompleted() != null)
|
||||
@ -134,6 +150,11 @@ public class Task extends Resource {
|
||||
if (uid != null)
|
||||
props.add(new Uid(uid));
|
||||
|
||||
if (createdAt != null)
|
||||
props.add(new Created(createdAt));
|
||||
if (lastModified != null)
|
||||
props.add(new LastModified(lastModified));
|
||||
|
||||
if (summary != null)
|
||||
props.add(new Summary(summary));
|
||||
if (location != null)
|
||||
@ -153,8 +174,10 @@ public class Task extends Resource {
|
||||
if (status != null)
|
||||
props.add(status);
|
||||
|
||||
if (createdAt != null)
|
||||
props.add(createdAt);
|
||||
if (due != null)
|
||||
props.add(due);
|
||||
if (duration != null)
|
||||
props.add(duration);
|
||||
if (dtStart != null)
|
||||
props.add(dtStart);
|
||||
if (completedAt != null)
|
||||
|
@ -102,12 +102,12 @@ public class AccountSettings {
|
||||
|
||||
// sync. settings
|
||||
|
||||
public Long getContactsSyncInterval() {
|
||||
if (ContentResolver.getIsSyncable(account, ContactsContract.AUTHORITY) <= 0)
|
||||
public Long getSyncInterval(String authority) {
|
||||
if (ContentResolver.getIsSyncable(account, authority) <= 0)
|
||||
return null;
|
||||
|
||||
if (ContentResolver.getSyncAutomatically(account, ContactsContract.AUTHORITY)) {
|
||||
List<PeriodicSync> syncs = ContentResolver.getPeriodicSyncs(account, ContactsContract.AUTHORITY);
|
||||
if (ContentResolver.getSyncAutomatically(account, authority)) {
|
||||
List<PeriodicSync> syncs = ContentResolver.getPeriodicSyncs(account, authority);
|
||||
if (syncs.isEmpty())
|
||||
return SYNC_INTERVAL_MANUALLY;
|
||||
else
|
||||
@ -116,35 +116,12 @@ public class AccountSettings {
|
||||
return SYNC_INTERVAL_MANUALLY;
|
||||
}
|
||||
|
||||
public void setContactsSyncInterval(long seconds) {
|
||||
public void setSyncInterval(String authority, long seconds) {
|
||||
if (seconds == SYNC_INTERVAL_MANUALLY) {
|
||||
ContentResolver.setSyncAutomatically(account, ContactsContract.AUTHORITY, false);
|
||||
ContentResolver.setSyncAutomatically(account, authority, false);
|
||||
} else {
|
||||
ContentResolver.setSyncAutomatically(account, ContactsContract.AUTHORITY, true);
|
||||
ContentResolver.addPeriodicSync(account, ContactsContract.AUTHORITY, new Bundle(), seconds);
|
||||
}
|
||||
}
|
||||
|
||||
public Long getCalendarsSyncInterval() {
|
||||
if (ContentResolver.getIsSyncable(account, CalendarContract.AUTHORITY) <= 0)
|
||||
return null;
|
||||
|
||||
if (ContentResolver.getSyncAutomatically(account, CalendarContract.AUTHORITY)) {
|
||||
List<PeriodicSync> syncs = ContentResolver.getPeriodicSyncs(account, CalendarContract.AUTHORITY);
|
||||
if (syncs.isEmpty())
|
||||
return SYNC_INTERVAL_MANUALLY;
|
||||
else
|
||||
return syncs.get(0).period;
|
||||
} else
|
||||
return SYNC_INTERVAL_MANUALLY;
|
||||
}
|
||||
|
||||
public void setCalendarsSyncInterval(long seconds) {
|
||||
if (seconds == SYNC_INTERVAL_MANUALLY) {
|
||||
ContentResolver.setSyncAutomatically(account, CalendarContract.AUTHORITY, false);
|
||||
} else {
|
||||
ContentResolver.setSyncAutomatically(account, CalendarContract.AUTHORITY, true);
|
||||
ContentResolver.addPeriodicSync(account, CalendarContract.AUTHORITY, new Bundle(), seconds);
|
||||
ContentResolver.setSyncAutomatically(account, authority, true);
|
||||
ContentResolver.addPeriodicSync(account, authority, new Bundle(), seconds);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,73 +0,0 @@
|
||||
package at.bitfire.davdroid.syncadapter;
|
||||
|
||||
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 java.net.URISyntaxException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import at.bitfire.davdroid.resource.CalDavNotebook;
|
||||
import at.bitfire.davdroid.resource.LocalCollection;
|
||||
import at.bitfire.davdroid.resource.LocalNotebook;
|
||||
import at.bitfire.davdroid.resource.RemoteCollection;
|
||||
|
||||
public class NotesSyncAdapterService extends Service {
|
||||
private static NotesSyncAdapter syncAdapter;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
if (syncAdapter == null)
|
||||
syncAdapter = new NotesSyncAdapter(getApplicationContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
syncAdapter.close();
|
||||
syncAdapter = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return syncAdapter.getSyncAdapterBinder();
|
||||
}
|
||||
|
||||
|
||||
private static class NotesSyncAdapter extends DavSyncAdapter {
|
||||
private final static String TAG = "davdroid.NotesSync";
|
||||
|
||||
private NotesSyncAdapter(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<LocalCollection<?>, 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<LocalCollection<?>, RemoteCollection<?>> map = new HashMap<LocalCollection<?>, RemoteCollection<?>>();
|
||||
|
||||
for (LocalNotebook noteList : LocalNotebook.findAll(account, provider)) {
|
||||
RemoteCollection<?> dav = new CalDavNotebook(httpClient, noteList.getUrl(), userName, password, preemptive);
|
||||
map.put(noteList, dav);
|
||||
}
|
||||
return map;
|
||||
} catch (RemoteException ex) {
|
||||
Log.e(TAG, "Couldn't find local notebooks", ex);
|
||||
} catch (URISyntaxException ex) {
|
||||
Log.e(TAG, "Couldn't build calendar URI", ex);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -20,9 +20,7 @@ import java.net.URISyntaxException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import at.bitfire.davdroid.resource.CalDavCalendar;
|
||||
import at.bitfire.davdroid.resource.CalDavTaskList;
|
||||
import at.bitfire.davdroid.resource.LocalCalendar;
|
||||
import at.bitfire.davdroid.resource.LocalCollection;
|
||||
import at.bitfire.davdroid.resource.LocalTaskList;
|
||||
import at.bitfire.davdroid.resource.RemoteCollection;
|
||||
|
@ -17,8 +17,13 @@ import android.preference.ListPreference;
|
||||
import android.preference.Preference;
|
||||
import android.preference.PreferenceFragment;
|
||||
import android.preference.SwitchPreference;
|
||||
import android.provider.CalendarContract;
|
||||
import android.provider.ContactsContract;
|
||||
|
||||
import org.dmfs.provider.tasks.TaskContract;
|
||||
|
||||
import at.bitfire.davdroid.R;
|
||||
import at.bitfire.davdroid.resource.LocalTaskList;
|
||||
import at.bitfire.davdroid.syncadapter.AccountSettings;
|
||||
import ezvcard.VCardVersion;
|
||||
import lombok.Setter;
|
||||
@ -78,7 +83,7 @@ public class AccountFragment extends PreferenceFragment {
|
||||
|
||||
// category: synchronization
|
||||
final ListPreference prefSyncContacts = (ListPreference)findPreference("sync_interval_contacts");
|
||||
final Long syncIntervalContacts = settings.getContactsSyncInterval();
|
||||
final Long syncIntervalContacts = settings.getSyncInterval(ContactsContract.AUTHORITY);
|
||||
if (syncIntervalContacts != null) {
|
||||
prefSyncContacts.setValue(syncIntervalContacts.toString());
|
||||
if (syncIntervalContacts == AccountSettings.SYNC_INTERVAL_MANUALLY)
|
||||
@ -88,7 +93,7 @@ public class AccountFragment extends PreferenceFragment {
|
||||
prefSyncContacts.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
settings.setContactsSyncInterval(Long.parseLong((String)newValue));
|
||||
settings.setSyncInterval(ContactsContract.AUTHORITY, Long.parseLong((String) newValue));
|
||||
readFromAccount();
|
||||
return true;
|
||||
}
|
||||
@ -99,7 +104,7 @@ public class AccountFragment extends PreferenceFragment {
|
||||
}
|
||||
|
||||
final ListPreference prefSyncCalendars = (ListPreference)findPreference("sync_interval_calendars");
|
||||
final Long syncIntervalCalendars = settings.getCalendarsSyncInterval();
|
||||
final Long syncIntervalCalendars = settings.getSyncInterval(CalendarContract.AUTHORITY);
|
||||
if (syncIntervalCalendars != null) {
|
||||
prefSyncCalendars.setValue(syncIntervalCalendars.toString());
|
||||
if (syncIntervalCalendars == AccountSettings.SYNC_INTERVAL_MANUALLY)
|
||||
@ -109,7 +114,7 @@ public class AccountFragment extends PreferenceFragment {
|
||||
prefSyncCalendars.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
settings.setCalendarsSyncInterval(Long.parseLong((String)newValue));
|
||||
settings.setSyncInterval(CalendarContract.AUTHORITY, Long.parseLong((String) newValue));
|
||||
readFromAccount();
|
||||
return true;
|
||||
}
|
||||
@ -119,6 +124,27 @@ public class AccountFragment extends PreferenceFragment {
|
||||
prefSyncCalendars.setSummary(R.string.settings_sync_summary_not_available);
|
||||
}
|
||||
|
||||
final ListPreference prefSyncTasks = (ListPreference)findPreference("sync_interval_tasks");
|
||||
final Long syncIntervalTasks = settings.getSyncInterval(LocalTaskList.TASKS_AUTHORITY);
|
||||
if (syncIntervalTasks != null) {
|
||||
prefSyncTasks.setValue(syncIntervalTasks.toString());
|
||||
if (syncIntervalTasks == AccountSettings.SYNC_INTERVAL_MANUALLY)
|
||||
prefSyncTasks.setSummary(R.string.settings_sync_summary_manually);
|
||||
else
|
||||
prefSyncTasks.setSummary(getString(R.string.settings_sync_summary_periodically, syncIntervalTasks / 60));
|
||||
prefSyncTasks.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
settings.setSyncInterval(LocalTaskList.TASKS_AUTHORITY, Long.parseLong((String) newValue));
|
||||
readFromAccount();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
prefSyncTasks.setEnabled(false);
|
||||
prefSyncTasks.setSummary(R.string.settings_sync_summary_not_available);
|
||||
}
|
||||
|
||||
// category: address book
|
||||
final CheckBoxPreference prefVCard4 = (CheckBoxPreference) findPreference("vcard4_support");
|
||||
if (settings.getAddressBookURL() != null) { // does this account even have an address book?
|
||||
|
@ -16,6 +16,7 @@ import android.provider.CalendarContract;
|
||||
import android.provider.ContactsContract;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
@ -26,22 +27,19 @@ import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.dmfs.provider.tasks.TaskContract;
|
||||
import java.util.List;
|
||||
|
||||
import at.bitfire.davdroid.Constants;
|
||||
import at.bitfire.davdroid.R;
|
||||
import at.bitfire.davdroid.resource.LocalCalendar;
|
||||
import at.bitfire.davdroid.resource.LocalNotebook;
|
||||
import at.bitfire.davdroid.resource.LocalStorageException;
|
||||
import at.bitfire.davdroid.resource.LocalTaskList;
|
||||
import at.bitfire.davdroid.resource.ServerInfo;
|
||||
import at.bitfire.davdroid.resource.Task;
|
||||
import at.bitfire.davdroid.syncadapter.AccountSettings;
|
||||
import at.bitfire.notebooks.provider.NoteContract;
|
||||
|
||||
public class AccountDetailsFragment extends Fragment implements TextWatcher {
|
||||
public static final String KEY_SERVER_INFO = "server_info";
|
||||
|
||||
public static final String TAG = "davdroid.AccountDetails";
|
||||
|
||||
ServerInfo serverInfo;
|
||||
|
||||
EditText editAccountName;
|
||||
@ -51,7 +49,7 @@ public class AccountDetailsFragment extends Fragment implements TextWatcher {
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View v = inflater.inflate(R.layout.setup_account_details, container, false);
|
||||
|
||||
serverInfo = (ServerInfo)getArguments().getSerializable(KEY_SERVER_INFO);
|
||||
serverInfo = ((AddAccountActivity)getActivity()).serverInfo;
|
||||
|
||||
editAccountName = (EditText)v.findViewById(R.id.account_name);
|
||||
editAccountName.addTextChangedListener(this);
|
||||
@ -87,81 +85,59 @@ public class AccountDetailsFragment extends Fragment implements TextWatcher {
|
||||
// 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() && calendar.isSupportingEvents())
|
||||
try {
|
||||
LocalCalendar.create(account, getActivity().getContentResolver(), calendar);
|
||||
syncCalendars = true;
|
||||
} catch (LocalStorageException e) {
|
||||
Toast.makeText(getActivity(), "Couldn't create calendar: " + 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);
|
||||
addSync(account, ContactsContract.AUTHORITY, serverInfo.getAddressBooks(), null);
|
||||
|
||||
// ... and notes
|
||||
boolean syncNotes = false;
|
||||
for (ServerInfo.ResourceInfo calendar : serverInfo.getCalendars())
|
||||
if (calendar.isEnabled() && calendar.isSupportingNotes())
|
||||
try {
|
||||
LocalNotebook.create(account, getActivity().getContentResolver(), calendar);
|
||||
syncNotes = true;
|
||||
} catch (LocalStorageException e) {
|
||||
Toast.makeText(getActivity(), "Couldn't create notebook: " + e.getMessage(), Toast.LENGTH_LONG).show();
|
||||
}
|
||||
if (syncNotes) {
|
||||
ContentResolver.setIsSyncable(account, NoteContract.AUTHORITY, 1);
|
||||
ContentResolver.setSyncAutomatically(account, NoteContract.AUTHORITY, true);
|
||||
} else
|
||||
ContentResolver.setIsSyncable(account, NoteContract.AUTHORITY, 0);
|
||||
addSync(account, CalendarContract.AUTHORITY, serverInfo.getCalendars(), new AddSyncCallback() {
|
||||
@Override
|
||||
public void createLocalCollection(Account account, ServerInfo.ResourceInfo calendar) throws LocalStorageException {
|
||||
LocalCalendar.create(account, getActivity().getContentResolver(), calendar);
|
||||
}
|
||||
});
|
||||
|
||||
// ... and tasks
|
||||
boolean syncTasks = false;
|
||||
for (ServerInfo.ResourceInfo calendar : serverInfo.getCalendars())
|
||||
if (calendar.isEnabled() && calendar.isSupportingTasks())
|
||||
try {
|
||||
LocalTaskList.create(account, getActivity().getContentResolver(), calendar);
|
||||
syncTasks = true;
|
||||
} catch (LocalStorageException e) {
|
||||
Toast.makeText(getActivity(), "Couldn't create task list: " + e.getMessage(), Toast.LENGTH_LONG).show();
|
||||
}
|
||||
if (syncTasks) {
|
||||
ContentResolver.setIsSyncable(account, TaskContract.AUTHORITY, 1);
|
||||
ContentResolver.setSyncAutomatically(account, TaskContract.AUTHORITY, true);
|
||||
} else
|
||||
ContentResolver.setIsSyncable(account, TaskContract.AUTHORITY, 0);
|
||||
addSync(account, LocalTaskList.TASKS_AUTHORITY, serverInfo.getTodoLists(), new AddSyncCallback() {
|
||||
@Override
|
||||
public void createLocalCollection(Account account, ServerInfo.ResourceInfo todoList) throws LocalStorageException {
|
||||
LocalTaskList.create(account, getActivity().getContentResolver(), todoList);
|
||||
}
|
||||
});
|
||||
|
||||
getActivity().finish();
|
||||
} else
|
||||
Toast.makeText(getActivity(), "Couldn't create account (account with this name already existing?)", Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
|
||||
protected interface AddSyncCallback {
|
||||
void createLocalCollection(Account account, ServerInfo.ResourceInfo resource) throws LocalStorageException;
|
||||
}
|
||||
|
||||
protected void addSync(Account account, String authority, List<ServerInfo.ResourceInfo> resourceList, AddSyncCallback callback) {
|
||||
boolean sync = false;
|
||||
for (ServerInfo.ResourceInfo resource : resourceList)
|
||||
if (resource.isEnabled()) {
|
||||
sync = true;
|
||||
if (callback != null)
|
||||
try {
|
||||
callback.createLocalCollection(account, resource);
|
||||
} catch(LocalStorageException e) {
|
||||
Log.e(TAG, "Couldn't add sync collection", e);
|
||||
Toast.makeText(getActivity(), "Couldn't set up synchronization for " + authority, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
if (sync) {
|
||||
ContentResolver.setIsSyncable(account, authority, 1);
|
||||
ContentResolver.setSyncAutomatically(account, authority, true);
|
||||
} else
|
||||
ContentResolver.setIsSyncable(account, authority, 0);
|
||||
}
|
||||
|
||||
|
||||
// input validation
|
||||
|
||||
@Override
|
||||
|
@ -14,12 +14,17 @@ import android.os.Bundle;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
|
||||
import at.bitfire.davdroid.Constants;
|
||||
import at.bitfire.davdroid.R;
|
||||
import at.bitfire.davdroid.resource.ServerInfo;
|
||||
|
||||
public class AddAccountActivity extends Activity {
|
||||
|
||||
protected ServerInfo serverInfo;
|
||||
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
@ -40,6 +45,11 @@ public class AddAccountActivity extends Activity {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void installTasksApp(View view) {
|
||||
final Intent intent = new Intent(Intent.ACTION_VIEW).setData(Uri.parse("market://details?id=org.dmfs.tasks"));
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
public void showHelp(MenuItem item) {
|
||||
startActivityForResult(new Intent(Intent.ACTION_VIEW, Uri.parse(Constants.WEB_URL_HELP)), 0);
|
||||
}
|
||||
|
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (c) 2013 – 2015 Ricki Hirner (bitfire web engineering).
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the GNU Public License v3.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.gnu.org/licenses/gpl.html
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid.ui.setup;
|
||||
|
||||
import android.app.DialogFragment;
|
||||
import android.app.Fragment;
|
||||
import android.app.FragmentManager;
|
||||
import android.app.FragmentTransaction;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.util.Log;
|
||||
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.TextView;
|
||||
|
||||
import at.bitfire.davdroid.R;
|
||||
import at.bitfire.davdroid.resource.LocalTaskList;
|
||||
|
||||
public class InstallAppsFragment extends Fragment implements Runnable {
|
||||
private static final String TAG = "davdroid.setup";
|
||||
|
||||
final protected Handler timerHandler = new Handler();
|
||||
|
||||
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View v = inflater.inflate(R.layout.setup_install_apps, container, false);
|
||||
setHasOptionsMenu(true);
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
inflater.inflate(R.menu.only_skip, menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
timerHandler.postDelayed(this, 1000);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
timerHandler.removeCallbacks(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.skip:
|
||||
skip(true);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (LocalTaskList.isAvailable(getActivity()))
|
||||
skip(false);
|
||||
else
|
||||
timerHandler.postDelayed(this, 1000);
|
||||
}
|
||||
|
||||
protected void skip(boolean addToBackStack) {
|
||||
FragmentManager fm = getFragmentManager();
|
||||
|
||||
if (!addToBackStack)
|
||||
fm.popBackStack();
|
||||
|
||||
getFragmentManager().beginTransaction()
|
||||
.replace(R.id.right_pane, new SelectCollectionsFragment())
|
||||
.addToBackStack(null)
|
||||
.commit();
|
||||
}
|
||||
|
||||
}
|
@ -8,6 +8,7 @@
|
||||
package at.bitfire.davdroid.ui.setup;
|
||||
|
||||
import android.app.DialogFragment;
|
||||
import android.app.Fragment;
|
||||
import android.app.LoaderManager.LoaderCallbacks;
|
||||
import android.content.AsyncTaskLoader;
|
||||
import android.content.Context;
|
||||
@ -30,12 +31,13 @@ import java.security.cert.CertPathValidatorException;
|
||||
|
||||
import at.bitfire.davdroid.R;
|
||||
import at.bitfire.davdroid.resource.DavResourceFinder;
|
||||
import at.bitfire.davdroid.resource.LocalTaskList;
|
||||
import at.bitfire.davdroid.resource.ServerInfo;
|
||||
import at.bitfire.davdroid.webdav.DavException;
|
||||
import lombok.Cleanup;
|
||||
|
||||
public class QueryServerDialogFragment extends DialogFragment implements LoaderCallbacks<ServerInfo> {
|
||||
private static final String TAG = "davdroid.QueryServerDialogFragment";
|
||||
private static final String TAG = "davdroid.QueryServer";
|
||||
public static final String
|
||||
EXTRA_BASE_URI = "base_uri",
|
||||
EXTRA_USER_NAME = "user_name",
|
||||
@ -71,13 +73,16 @@ public class QueryServerDialogFragment extends DialogFragment implements LoaderC
|
||||
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);
|
||||
|
||||
((AddAccountActivity)getActivity()).serverInfo = serverInfo;
|
||||
|
||||
Fragment nextFragment;
|
||||
if (!serverInfo.getTodoLists().isEmpty() && !LocalTaskList.isAvailable(getActivity()))
|
||||
nextFragment = new InstallAppsFragment();
|
||||
else
|
||||
nextFragment = new SelectCollectionsFragment();
|
||||
|
||||
getFragmentManager().beginTransaction()
|
||||
.replace(R.id.right_pane, selectCollections)
|
||||
.replace(R.id.right_pane, nextFragment)
|
||||
.addToBackStack(null)
|
||||
.commitAllowingStateLoss();
|
||||
}
|
||||
|
@ -18,28 +18,50 @@ import android.widget.CheckedTextView;
|
||||
import android.widget.ListAdapter;
|
||||
|
||||
import at.bitfire.davdroid.R;
|
||||
import at.bitfire.davdroid.resource.LocalTaskList;
|
||||
import at.bitfire.davdroid.resource.ServerInfo;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* Order of display:
|
||||
*
|
||||
* number of rows type
|
||||
* nAddressBookHeadings (0 or 1) heading: "address books"
|
||||
* nAddressBooks address book info
|
||||
* nCalendarHeadings (0 or 1) heading: "calendars"
|
||||
* nCalendars calendar info
|
||||
* nNotebookHeadings (0 or 1) heading: "notebooks"
|
||||
* nNotebooks notebook info
|
||||
* nTaskListHeadings (0 or 1) heading: "task lists"
|
||||
* nTaskLists task list info
|
||||
*/
|
||||
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;
|
||||
final static int
|
||||
TYPE_ADDRESS_BOOKS_HEADING = 0,
|
||||
TYPE_ADDRESS_BOOKS_ROW = 1,
|
||||
TYPE_CALENDARS_HEADING = 2,
|
||||
TYPE_CALENDARS_ROW = 3,
|
||||
TYPE_TASK_LISTS_HEADING = 4,
|
||||
TYPE_TASK_LISTS_ROW = 5;
|
||||
|
||||
protected Context context;
|
||||
protected ServerInfo serverInfo;
|
||||
@Getter protected int nAddressBooks, nAddressbookHeadings, nCalendars, nCalendarHeadings;
|
||||
@Getter protected int
|
||||
nAddressBooks, nAddressBookHeadings,
|
||||
nCalendars, nCalendarHeadings,
|
||||
nTaskLists, nTaskListHeadings;
|
||||
|
||||
|
||||
public SelectCollectionsAdapter(Context context, ServerInfo serverInfo) {
|
||||
this.context = context;
|
||||
|
||||
this.serverInfo = serverInfo;
|
||||
nAddressBooks = (serverInfo.getAddressBooks() == null) ? 0 : serverInfo.getAddressBooks().size();
|
||||
nAddressbookHeadings = (nAddressBooks == 0) ? 0 : 1;
|
||||
nCalendars = (serverInfo.getCalendars() == null) ? 0 : serverInfo.getCalendars().size();
|
||||
nCalendarHeadings = (nCalendars == 0) ? 0 : 1;
|
||||
nAddressBooks = serverInfo.getAddressBooks() == null ? 0 : serverInfo.getAddressBooks().size();
|
||||
nAddressBookHeadings = nAddressBooks == 0 ? 0 : 1;
|
||||
nCalendars = serverInfo.getCalendars() == null ? 0 : serverInfo.getCalendars().size();
|
||||
nCalendarHeadings = nCalendars == 0 ? 0 : 1;
|
||||
nTaskLists = serverInfo.getTodoLists() == null ? 0 : serverInfo.getTodoLists().size();
|
||||
nTaskListHeadings = nTaskLists == 0 ? 0 : 1;
|
||||
}
|
||||
|
||||
|
||||
@ -47,17 +69,23 @@ public class SelectCollectionsAdapter extends BaseAdapter implements ListAdapter
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return nAddressbookHeadings + nAddressBooks + nCalendarHeadings + nCalendars;
|
||||
return nAddressBookHeadings + nAddressBooks + nCalendarHeadings + nCalendars + nTaskListHeadings + nTaskLists;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItem(int position) {
|
||||
if (position >= nAddressbookHeadings &&
|
||||
position < (nAddressbookHeadings + nAddressBooks))
|
||||
return serverInfo.getAddressBooks().get(position - nAddressbookHeadings);
|
||||
else if (position >= (nAddressbookHeadings + nAddressBooks + nCalendarHeadings) &&
|
||||
(position < (nAddressbookHeadings + nAddressBooks + nCalendarHeadings + nCalendars)))
|
||||
return serverInfo.getCalendars().get(position - (nAddressbookHeadings + nAddressBooks + nCalendarHeadings));
|
||||
if (position >= nAddressBookHeadings &&
|
||||
position < (nAddressBookHeadings + nAddressBooks))
|
||||
return serverInfo.getAddressBooks().get(position - nAddressBookHeadings);
|
||||
|
||||
else if (position >= (nAddressBookHeadings + nAddressBooks + nCalendarHeadings) &&
|
||||
(position < (nAddressBookHeadings + nAddressBooks + nCalendarHeadings + nCalendars)))
|
||||
return serverInfo.getCalendars().get(position - (nAddressBookHeadings + nAddressBooks + nCalendarHeadings));
|
||||
|
||||
else if (position >= (nAddressBookHeadings + nAddressBooks + nCalendarHeadings + nCalendars + nTaskListHeadings) &&
|
||||
(position < (nAddressBookHeadings + nAddressBooks + nCalendarHeadings + nCalendars + + nTaskListHeadings + nTaskLists)))
|
||||
return serverInfo.getTodoLists().get(position - (nAddressBookHeadings + nAddressBooks + nCalendarHeadings + nCalendars + nTaskListHeadings));
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -76,19 +104,26 @@ public class SelectCollectionsAdapter extends BaseAdapter implements ListAdapter
|
||||
|
||||
@Override
|
||||
public int getViewTypeCount() {
|
||||
return 4;
|
||||
return TYPE_TASK_LISTS_ROW + 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
if ((nAddressbookHeadings != 0) && (position == 0))
|
||||
if ((nAddressBookHeadings != 0) && (position == 0))
|
||||
return TYPE_ADDRESS_BOOKS_HEADING;
|
||||
else if ((nAddressbookHeadings != 0) && (position > 0) && (position < nAddressbookHeadings + nAddressBooks))
|
||||
else if ((nAddressBooks != 0) && (position > 0) && (position < nAddressBookHeadings + nAddressBooks))
|
||||
return TYPE_ADDRESS_BOOKS_ROW;
|
||||
else if ((nCalendars != 0) && (position == nAddressbookHeadings + nAddressBooks))
|
||||
|
||||
else if ((nCalendarHeadings != 0) && (position == nAddressBookHeadings + nAddressBooks))
|
||||
return TYPE_CALENDARS_HEADING;
|
||||
else if ((nCalendars != 0) && (position > nAddressbookHeadings + nAddressBooks) && (position < nAddressbookHeadings + nAddressBooks + nCalendarHeadings + nCalendars))
|
||||
else if ((nCalendars != 0) && (position > nAddressBookHeadings + nAddressBooks) && (position < nAddressBookHeadings + nAddressBooks + nCalendarHeadings + nCalendars))
|
||||
return TYPE_CALENDARS_ROW;
|
||||
|
||||
else if ((nTaskListHeadings != 0) && (position == nAddressBookHeadings + nAddressBooks + nCalendarHeadings + nCalendars))
|
||||
return TYPE_TASK_LISTS_HEADING;
|
||||
else if ((nTaskLists != 0) && (position > nAddressBookHeadings + nAddressBooks + nCalendarHeadings + nCalendars) && (position < nAddressBookHeadings + nAddressBooks + nCalendarHeadings + nCalendars + nTaskListHeadings + nTaskLists))
|
||||
return TYPE_TASK_LISTS_ROW;
|
||||
|
||||
else
|
||||
return IGNORE_ITEM_VIEW_TYPE;
|
||||
}
|
||||
@ -97,11 +132,13 @@ public class SelectCollectionsAdapter extends BaseAdapter implements ListAdapter
|
||||
@SuppressLint("InflateParams")
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
View v = convertView;
|
||||
|
||||
int viewType = getItemViewType(position);
|
||||
|
||||
// step 1: get view (either by creating or recycling)
|
||||
if (v == null) {
|
||||
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
|
||||
switch (getItemViewType(position)) {
|
||||
switch (viewType) {
|
||||
case TYPE_ADDRESS_BOOKS_HEADING:
|
||||
v = inflater.inflate(R.layout.setup_address_books_heading, parent, false);
|
||||
break;
|
||||
@ -112,21 +149,38 @@ public class SelectCollectionsAdapter extends BaseAdapter implements ListAdapter
|
||||
case TYPE_CALENDARS_HEADING:
|
||||
v = inflater.inflate(R.layout.setup_calendars_heading, parent, false);
|
||||
break;
|
||||
case TYPE_TASK_LISTS_HEADING:
|
||||
v = inflater.inflate(R.layout.setup_task_lists_heading, parent, false);
|
||||
break;
|
||||
case TYPE_CALENDARS_ROW:
|
||||
case TYPE_TASK_LISTS_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)) {
|
||||
switch (viewType) {
|
||||
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));
|
||||
case TYPE_TASK_LISTS_ROW:
|
||||
setContent((CheckedTextView)v, R.drawable.calendar, (ServerInfo.ResourceInfo) getItem(position));
|
||||
}
|
||||
|
||||
|
||||
// disable task list selection if there's no local task provider
|
||||
if (viewType == TYPE_TASK_LISTS_ROW && !LocalTaskList.isAvailable(context)) {
|
||||
final CheckedTextView check = (CheckedTextView)v;
|
||||
check.setEnabled(false);
|
||||
check.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
check.setChecked(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
@ -156,6 +210,6 @@ public class SelectCollectionsAdapter extends BaseAdapter implements ListAdapter
|
||||
@Override
|
||||
public boolean isEnabled(int position) {
|
||||
int type = getItemViewType(position);
|
||||
return (type == TYPE_ADDRESS_BOOKS_ROW || type == TYPE_CALENDARS_ROW);
|
||||
return (type == TYPE_ADDRESS_BOOKS_ROW || type == TYPE_CALENDARS_ROW || type == TYPE_TASK_LISTS_ROW);
|
||||
}
|
||||
}
|
||||
|
@ -24,13 +24,17 @@ 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";
|
||||
|
||||
|
||||
|
||||
protected ServerInfo serverInfo;
|
||||
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
serverInfo = ((AddAccountActivity)getActivity()).serverInfo;
|
||||
|
||||
View v = super.onCreateView(inflater, container, savedInstanceState);
|
||||
setHasOptionsMenu(true);
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
@ -50,7 +54,6 @@ public class SelectCollectionsFragment extends ListFragment {
|
||||
View header = getActivity().getLayoutInflater().inflate(R.layout.setup_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);
|
||||
|
||||
@ -80,13 +83,13 @@ public class SelectCollectionsFragment extends ListFragment {
|
||||
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);
|
||||
for (ServerInfo.ResourceInfo todoList : serverInfo.getTodoLists())
|
||||
todoList.setEnabled(false);
|
||||
|
||||
ListAdapter adapter = getListView().getAdapter();
|
||||
for (long id : getListView().getCheckedItemIds()) {
|
||||
@ -95,14 +98,8 @@ public class SelectCollectionsFragment extends ListFragment {
|
||||
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.right_pane, accountDetails)
|
||||
.replace(R.id.right_pane, new AccountDetailsFragment())
|
||||
.addToBackStack(null)
|
||||
.commitAllowingStateLoss();
|
||||
break;
|
||||
|
@ -1 +0,0 @@
|
||||
../../../../../../../../../notebooks/app/src/main/java/at/bitfire/notebooks/provider/NoteContract.java
|
File diff suppressed because it is too large
Load Diff
1
app/src/main/java/org/dmfs/provider/tasks/TaskContract.java
Symbolic link
1
app/src/main/java/org/dmfs/provider/tasks/TaskContract.java
Symbolic link
@ -0,0 +1 @@
|
||||
/home/rfc2822/Entwicklung/Android/task-provider/src/org/dmfs/provider/tasks/TaskContract.java
|
1
app/src/main/java/org/dmfs/provider/tasks/UriFactory.java
Symbolic link
1
app/src/main/java/org/dmfs/provider/tasks/UriFactory.java
Symbolic link
@ -0,0 +1 @@
|
||||
/home/rfc2822/Entwicklung/Android/task-provider/src/org/dmfs/provider/tasks/UriFactory.java
|
BIN
app/src/main/res/drawable-hdpi/navigation_skip.png
Normal file
BIN
app/src/main/res/drawable-hdpi/navigation_skip.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 661 B |
BIN
app/src/main/res/drawable-mdpi/navigation_skip.png
Normal file
BIN
app/src/main/res/drawable-mdpi/navigation_skip.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 488 B |
BIN
app/src/main/res/drawable-xhdpi/navigation_skip.png
Normal file
BIN
app/src/main/res/drawable-xhdpi/navigation_skip.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 813 B |
BIN
app/src/main/res/drawable-xxhdpi/navigation_skip.png
Normal file
BIN
app/src/main/res/drawable-xxhdpi/navigation_skip.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
29
app/src/main/res/layout-sw720dp/setup_task_lists_heading.xml
Normal file
29
app/src/main/res/layout-sw720dp/setup_task_lists_heading.xml
Normal file
@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2013 – 2015 Ricki Hirner (bitfire web engineering).
|
||||
~ All rights reserved. This program and the accompanying materials
|
||||
~ are made available under the terms of the GNU Public License v3.0
|
||||
~ which accompanies this distribution, and is available at
|
||||
~ http://www.gnu.org/licenses/gpl.html
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical" >
|
||||
|
||||
<TextView
|
||||
style="@style/TextView.Heading"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:text="@string/setup_task_lists" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:text="@string/setup_select_task_lists" />
|
||||
|
||||
</LinearLayout>
|
45
app/src/main/res/layout/setup_install_apps.xml
Normal file
45
app/src/main/res/layout/setup_install_apps.xml
Normal file
@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2013 – 2015 Ricki Hirner (bitfire web engineering).
|
||||
~ All rights reserved. This program and the accompanying materials
|
||||
~ are made available under the terms of the GNU Public License v3.0
|
||||
~ which accompanies this distribution, and is available at
|
||||
~ http://www.gnu.org/licenses/gpl.html
|
||||
-->
|
||||
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="20dp"
|
||||
tools:context=".MainActivity" >
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:text="@string/setup_install_apps_info"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:text="@string/setup_install_tasks_app_info"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
<Button
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/setup_install_tasks_app"
|
||||
android:onClick="installTasksApp"
|
||||
android:layout_gravity="center_horizontal"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
@ -1,3 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2013 – 2015 Ricki Hirner (bitfire web engineering).
|
||||
~ All rights reserved. This program and the accompanying materials
|
||||
@ -6,10 +7,13 @@
|
||||
~ http://www.gnu.org/licenses/gpl.html
|
||||
-->
|
||||
|
||||
<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:accountType="bitfire.at.davdroid"
|
||||
android:contentAuthority="at.bitfire.notebooks.provider"
|
||||
android:allowParallelSyncs="true"
|
||||
android:supportsUploading="true"
|
||||
android:isAlwaysSyncable="true"
|
||||
android:userVisible="true" />
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item
|
||||
android:id="@+id/skip"
|
||||
android:icon="@drawable/navigation_skip"
|
||||
android:showAsAction="always|withText"
|
||||
android:title="@string/skip">
|
||||
</item>
|
||||
|
||||
</menu>
|
@ -11,6 +11,7 @@
|
||||
<!-- common strings -->
|
||||
<string name="app_name">DAVdroid</string>
|
||||
<string name="next">Weiter</string>
|
||||
<string name="skip">Überspringen</string>
|
||||
<string name="help">Hilfe</string>
|
||||
|
||||
<string name="exception_cert_path_validation">Nicht vertrauenswürdiges Zertifikat in der Zertifikatskette (siehe FAQ)</string>
|
||||
@ -127,6 +128,7 @@
|
||||
<string name="settings_sync_summary_periodically">Alle %d Minuten + sofort bei lokalen Änderungen</string>
|
||||
<string name="settings_sync_summary_not_available">Nicht verfügbar</string>
|
||||
<string name="settings_sync_interval_calendars">Häufigkeit der Kalender-Synchronisierung</string>
|
||||
<string name="settings_sync_interval_tasks">Häufigkeit der Aufgaben-Synchronisierung</string>
|
||||
<string-array name="settings_sync_interval_names">
|
||||
<item>Nur manuell</item>
|
||||
<item>Alle 5 Minuten</item>
|
||||
@ -146,11 +148,18 @@
|
||||
<string name="setup_neither_caldav_nor_carddav">An dieser Adresse konnte kein CalDAV- oder CardDAV-Dienst gefunden werden.</string>
|
||||
<string name="setup_add_account">Konto hinzufügen</string>
|
||||
<string name="setup_querying_server">Daten werden vom Server abgefragt. Bitte warten…</string>
|
||||
<string name="setup_install_apps_info">Android bietet im Gegensatz zu Kontakten und Terminen keine integrierte Lösung für Aufgaben.</string>
|
||||
<string name="setup_install_tasks_app_info">DAVdroid kann mit der "Aufgaben"-App von Marten Gajda synchronisieren. Sie können diese App installieren, um Aufgaben zu synchronisieren, oder auf Aufgaben verzichten und die Installation überspringen.</string>
|
||||
<string name="setup_install_tasks_app">Aufgaben-App installieren</string>
|
||||
<string name="setup_what_to_sync">Welche Ordner sollen synchronisiert werden?</string>
|
||||
<string name="setup_address_books">Adressbücher</string>
|
||||
<string name="setup_calendars">Kalender</string>
|
||||
<string name="setup_notebooks">Notizbücher</string>
|
||||
<string name="setup_task_lists">Aufgabenlisten</string>
|
||||
<string name="setup_select_address_book">Ein oder kein Adressbuch auswählen (nochmal berühren, um abzuwählen):</string>
|
||||
<string name="setup_select_calendars">Kalender zur Synchronisation auswählen:</string>
|
||||
<string name="setup_select_notebooks">Notizbücher zur Synchronisation auswählen:</string>
|
||||
<string name="setup_select_task_lists">Aufgabenlisten zur Synchronisation auswählen:</string>
|
||||
|
||||
<string name="setup_account_details">Konto-Details</string>
|
||||
<string name="setup_account_name">Kontoname:</string>
|
||||
|
@ -12,6 +12,7 @@
|
||||
<!-- common strings -->
|
||||
<string name="app_name">DAVdroid</string>
|
||||
<string name="next">Next</string>
|
||||
<string name="skip">Skip</string>
|
||||
<string name="help">Help</string>
|
||||
|
||||
<string name="exception_cert_path_validation">Untrusted certificate in certificate path. See FAQ for more info.</string>
|
||||
@ -131,6 +132,7 @@
|
||||
<string name="settings_sync_summary_periodically">Every %d minutes + immediately on local changes</string>
|
||||
<string name="settings_sync_summary_not_available">Not available</string>
|
||||
<string name="settings_sync_interval_calendars">Calendars sync. interval</string>
|
||||
<string name="settings_sync_interval_tasks">Tasks sync. interval</string>
|
||||
<string-array name="settings_sync_interval_seconds">
|
||||
<item>-1</item>
|
||||
<item>300</item>
|
||||
@ -160,11 +162,16 @@
|
||||
<string name="setup_neither_caldav_nor_carddav">No CalDAV-/CardDAV service is available at this location.</string>
|
||||
<string name="setup_add_account">Add account</string>
|
||||
<string name="setup_querying_server">Querying server. Please wait…</string>
|
||||
<string name="setup_install_apps_info">Plain Android doesn\'t support to-do lists (in contrast to contacts and calendars).</string>
|
||||
<string name="setup_install_tasks_app_info">DAVdroid is able to synchronize tasks with the "Tasks" app (by Marten Gajda). You may install this app if you want tasks to be synchronized or skip the installation.</string>
|
||||
<string name="setup_install_tasks_app">Install Tasks app</string>
|
||||
<string name="setup_what_to_sync">Which collections shall be synchronized?</string>
|
||||
<string name="setup_address_books">Address books</string>
|
||||
<string name="setup_calendars">Calendars</string>
|
||||
<string name="setup_select_address_book">Select up to one address book (tap again to unselect):</string>
|
||||
<string name="setup_select_calendars">Select your calendars:</string>
|
||||
<string name="setup_task_lists">Task lists</string>
|
||||
<string name="setup_select_address_book">Select up to one address book (tap again to unselect) for synchronization:</string>
|
||||
<string name="setup_select_calendars">Select calendars for synchronization:</string>
|
||||
<string name="setup_select_task_lists">Select task lists for synchronization:</string>
|
||||
|
||||
<string name="setup_account_details">Account details</string>
|
||||
<string name="setup_account_name">Account name:</string>
|
||||
|
@ -50,6 +50,13 @@
|
||||
android:entries="@array/settings_sync_interval_names"
|
||||
android:entryValues="@array/settings_sync_interval_seconds" />
|
||||
|
||||
<ListPreference
|
||||
android:key="sync_interval_tasks"
|
||||
android:persistent="false"
|
||||
android:title="@string/settings_sync_interval_tasks"
|
||||
android:entries="@array/settings_sync_interval_names"
|
||||
android:entryValues="@array/settings_sync_interval_seconds" />
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory android:title="@string/settings_carddav">
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:accountType="bitfire.at.davdroid"
|
||||
android:contentAuthority="de.azapps.mirakel.provider"
|
||||
android:contentAuthority="org.dmfs.tasks"
|
||||
android:allowParallelSyncs="true"
|
||||
android:supportsUploading="true"
|
||||
android:isAlwaysSyncable="true"
|
||||
|
Loading…
Reference in New Issue
Block a user