at.bitfire.ical4android.AndroidTask.java Source code

Java tutorial

Introduction

Here is the source code for at.bitfire.ical4android.AndroidTask.java

Source

/*
 * Copyright (c) 2013  2015 Ricki Hirner (bitfire web engineering).
 *
 * This program is free software: you can redistribute it and/or modify it under the terms of the GNU
 * General Public License as published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 * PURPOSE.  See the GNU General Public License for more details.
 */

package at.bitfire.ical4android;

import android.content.ContentProviderOperation;
import android.content.ContentProviderOperation.Builder;
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.text.TextUtils;
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.property.Clazz;
import net.fortuna.ical4j.model.property.Completed;
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.ExDate;
import net.fortuna.ical4j.model.property.Geo;
import net.fortuna.ical4j.model.property.Organizer;
import net.fortuna.ical4j.model.property.RDate;
import net.fortuna.ical4j.model.property.RRule;
import net.fortuna.ical4j.model.property.Status;

import org.apache.commons.lang3.StringUtils;
import org.dmfs.provider.tasks.TaskContract.Tasks;

import java.io.FileNotFoundException;
import java.net.URI;
import java.net.URISyntaxException;
import java.text.ParseException;

import lombok.Cleanup;
import lombok.Getter;

public abstract class AndroidTask {
    private static final String TAG = "ical4android.Task";

    final protected AndroidTaskList taskList;

    @Getter
    protected Long id;

    protected Task task;

    protected AndroidTask(AndroidTaskList taskList, long id) {
        this.taskList = taskList;
        this.id = id;
    }

    protected AndroidTask(AndroidTaskList taskList, Task task) {
        this.taskList = taskList;
        this.task = task;
    }

    public Task getTask() throws FileNotFoundException, CalendarStorageException {
        if (task != null)
            return task;

        try {
            task = new Task();
            @Cleanup
            final Cursor cursor = taskList.provider.client.query(taskSyncURI(), null, null, null, null);
            if (cursor != null && cursor.moveToFirst()) {
                ContentValues values = new ContentValues(cursor.getColumnCount());
                DatabaseUtils.cursorRowToContentValues(cursor, values);
                populateTask(values);
            } else
                throw new FileNotFoundException("Couldn't load details of task #" + id);
            return task;
        } catch (RemoteException e) {
            throw new CalendarStorageException("Couldn't read locally stored event", e);
        } catch (ParseException e) {
            throw new CalendarStorageException("Couldn't parse locally stored event", e);
        }
    }

    protected void populateTask(ContentValues values)
            throws FileNotFoundException, RemoteException, ParseException {
        task.uid = values.getAsString(Tasks._UID);
        task.summary = values.getAsString(Tasks.TITLE);
        task.location = values.getAsString(Tasks.LOCATION);

        if (values.containsKey(Tasks.GEO)) {
            String geo = values.getAsString(Tasks.GEO);
            if (geo != null)
                task.geoPosition = new Geo(geo);
        }
        ;

        task.description = StringUtils.stripToNull(values.getAsString(Tasks.DESCRIPTION));
        task.url = StringUtils.stripToNull(values.getAsString(Tasks.URL));

        String organizer = values.getAsString(Tasks.ORGANIZER);
        if (!TextUtils.isEmpty(organizer))
            try {
                task.organizer = new Organizer("mailto:" + values.getAsString(Tasks.ORGANIZER));
            } catch (URISyntaxException e) {
                Log.w(TAG, "Invalid ORGANIZER email", e);
            }

        Integer priority = values.getAsInteger(Tasks.PRIORITY);
        if (priority != null)
            task.priority = priority;

        Integer classification = values.getAsInteger(Tasks.CLASSIFICATION);
        if (classification != null)
            switch (classification) {
            case Tasks.CLASSIFICATION_PUBLIC:
                task.classification = Clazz.PUBLIC;
                break;
            case Tasks.CLASSIFICATION_CONFIDENTIAL:
                task.classification = Clazz.CONFIDENTIAL;
                break;
            default:
                task.classification = Clazz.PRIVATE;
            }

        Long completed = values.getAsLong(Tasks.COMPLETED);
        if (completed != null)
            // COMPLETED must always be a DATE-TIME
            task.completedAt = new Completed(new DateTime(completed));

        Integer percentComplete = values.getAsInteger(Tasks.PERCENT_COMPLETE);
        if (percentComplete != null)
            task.percentComplete = percentComplete;

        Integer status = values.getAsInteger(Tasks.STATUS);
        if (status != null)
            switch (status) {
            case Tasks.STATUS_IN_PROCESS:
                task.status = Status.VTODO_IN_PROCESS;
                break;
            case Tasks.STATUS_COMPLETED:
                task.status = Status.VTODO_COMPLETED;
                break;
            case Tasks.STATUS_CANCELLED:
                task.status = Status.VTODO_CANCELLED;
                break;
            default:
                task.status = Status.VTODO_NEEDS_ACTION;
            }

        boolean allDay = false;
        if (values.getAsInteger(Tasks.IS_ALLDAY) != null)
            allDay = values.getAsInteger(Tasks.IS_ALLDAY) != 0;

        String tzID = values.getAsString(Tasks.TZ);
        TimeZone tz = (tzID != null) ? DateUtils.tzRegistry.getTimeZone(tzID) : null;

        Long createdAt = values.getAsLong(Tasks.CREATED);
        if (createdAt != null)
            task.createdAt = createdAt;

        Long lastModified = values.getAsLong(Tasks.LAST_MODIFIED);
        if (lastModified != null)
            task.lastModified = lastModified;

        Long dtStart = values.getAsLong(Tasks.DTSTART);
        if (dtStart != null) {
            long ts = dtStart;

            Date dt;
            if (allDay)
                dt = new Date(ts);
            else {
                dt = new DateTime(ts);
                if (tz != null)
                    ((DateTime) dt).setTimeZone(tz);
            }
            task.dtStart = new DtStart(dt);
        }

        Long due = values.getAsLong(Tasks.DUE);
        if (due != null) {
            long ts = due;

            Date dt;
            if (allDay)
                dt = new Date(ts);
            else {
                dt = new DateTime(ts);
                if (tz != null)
                    ((DateTime) dt).setTimeZone(tz);
            }
            task.due = new Due(dt);
        }

        String duration = values.getAsString(Tasks.DURATION);
        if (duration != null)
            task.duration = new Duration(new Dur(duration));

        String rDate = values.getAsString(Tasks.RDATE);
        if (rDate != null)
            task.getRDates().add((RDate) DateUtils.androidStringToRecurrenceSet(rDate, RDate.class, allDay));

        String exDate = values.getAsString(Tasks.EXDATE);
        if (exDate != null)
            task.getExDates().add((ExDate) DateUtils.androidStringToRecurrenceSet(exDate, ExDate.class, allDay));

        String rRule = values.getAsString(Tasks.RRULE);
        if (rRule != null)
            task.rRule = new RRule(rRule);
    }

    public Uri add() throws CalendarStorageException {
        BatchOperation batch = new BatchOperation(taskList.provider.client);
        Builder builder = ContentProviderOperation.newInsert(taskList.syncAdapterURI(taskList.provider.tasksUri()));
        buildTask(builder, false);
        batch.enqueue(builder.build());
        batch.commit();
        return batch.getResult(0).uri;
    }

    public void update(Task task) throws CalendarStorageException {
        this.task = task;

        BatchOperation batch = new BatchOperation(taskList.provider.client);
        Builder builder = ContentProviderOperation.newUpdate(taskList.syncAdapterURI(taskSyncURI()));
        buildTask(builder, true);
        batch.enqueue(builder.build());
        batch.commit();
    }

    public int delete() throws CalendarStorageException {
        try {
            return taskList.provider.client.delete(taskList.syncAdapterURI(taskList.provider.tasksUri()), null,
                    null);
        } catch (RemoteException e) {
            throw new CalendarStorageException("Couldn't delete event", e);
        }
    }

    protected void buildTask(Builder builder, boolean update) {
        if (!update)
            builder.withValue(Tasks.LIST_ID, taskList.getId()).withValue(Tasks._DIRTY, 0);

        builder
                //.withValue(Tasks._UID, task.uid)          // not available in F-Droid OpenTasks version yet (15 Oct 2015)
                .withValue(Tasks.TITLE, task.summary).withValue(Tasks.LOCATION, task.location);

        if (task.geoPosition != null)
            builder.withValue(Tasks.GEO, task.geoPosition.getValue());

        builder.withValue(Tasks.DESCRIPTION, task.description).withValue(Tasks.URL, task.url);

        if (task.organizer != null)
            try {
                URI organizer = new URI(task.organizer.getValue());
                if ("mailto".equals(organizer.getScheme()))
                    builder.withValue(Tasks.ORGANIZER, organizer.getSchemeSpecificPart());
                else
                    Log.w(TAG, "Found non-mailto ORGANIZER URI, ignoring");
            } catch (URISyntaxException e) {
                e.printStackTrace();
            }

        builder.withValue(Tasks.PRIORITY, task.priority);

        if (task.classification != null) {
            int classCode = Tasks.CLASSIFICATION_PRIVATE;
            if (task.classification == Clazz.PUBLIC)
                classCode = Tasks.CLASSIFICATION_PUBLIC;
            else if (task.classification == Clazz.CONFIDENTIAL)
                classCode = Tasks.CLASSIFICATION_CONFIDENTIAL;
            builder.withValue(Tasks.CLASSIFICATION, classCode);
        }

        if (task.completedAt != null) {
            // COMPLETED must always be a DATE-TIME
            builder.withValue(Tasks.COMPLETED, task.completedAt.getDateTime().getTime())
                    .withValue(Tasks.COMPLETED_IS_ALLDAY, 0);
        }
        builder.withValue(Tasks.PERCENT_COMPLETE, task.percentComplete);

        int statusCode = Tasks.STATUS_DEFAULT;
        if (task.status != null) {
            if (task.status == Status.VTODO_NEEDS_ACTION)
                statusCode = Tasks.STATUS_NEEDS_ACTION;
            else if (task.status == Status.VTODO_IN_PROCESS)
                statusCode = Tasks.STATUS_IN_PROCESS;
            else if (task.status == Status.VTODO_COMPLETED)
                statusCode = Tasks.STATUS_COMPLETED;
            else if (task.status == Status.VTODO_CANCELLED)
                statusCode = Tasks.STATUS_CANCELLED;
        }
        builder.withValue(Tasks.STATUS, statusCode);

        final boolean allDay = task.isAllDay();
        if (allDay)
            builder.withValue(Tasks.IS_ALLDAY, 1);
        else {
            builder.withValue(Tasks.IS_ALLDAY, 0);

            java.util.TimeZone tz = task.getTimeZone();
            builder.withValue(Tasks.TZ, tz.getID());
        }

        if (task.createdAt != null)
            builder.withValue(Tasks.CREATED, task.createdAt);
        if (task.lastModified != null)
            builder.withValue(Tasks.LAST_MODIFIED, task.lastModified);

        if (task.dtStart != null)
            builder.withValue(Tasks.DTSTART, task.dtStart.getDate().getTime());
        if (task.due != null)
            builder.withValue(Tasks.DUE, task.due.getDate().getTime());
        if (task.duration != null)
            builder.withValue(Tasks.DURATION, task.duration.getValue());

        if (!task.getRDates().isEmpty())
            try {
                builder.withValue(Tasks.RDATE, DateUtils.recurrenceSetsToAndroidString(task.getRDates(), allDay));
            } catch (ParseException e) {
                Log.e(TAG, "Couldn't parse RDate(s)", e);
            }
        if (!task.getExDates().isEmpty())
            try {
                builder.withValue(Tasks.EXDATE, DateUtils.recurrenceSetsToAndroidString(task.getExDates(), allDay));
            } catch (ParseException e) {
                Log.e(TAG, "Couldn't parse ExDate(s)", e);
            }
        if (task.rRule != null)
            builder.withValue(Tasks.EXDATE, task.rRule.getValue());
    }

    protected Uri tasksSyncURI() {
        return taskList.syncAdapterURI(taskList.provider.tasksUri());
    }

    protected Uri taskSyncURI() {
        if (id == null)
            throw new IllegalStateException("Task doesn't have an ID yet");
        return taskList.syncAdapterURI(ContentUris.withAppendedId(taskList.provider.tasksUri(), id));
    }

}