Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New constraints and defaults (forbid DUE=START) #309

Merged
merged 2 commits into from Jul 3, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -22,7 +22,6 @@
import org.dmfs.tasks.R;
import org.dmfs.tasks.groupings.cursorloaders.SearchHistoryCursorLoaderFactory;
import org.dmfs.tasks.model.TaskFieldAdapters;
import org.dmfs.tasks.model.adapters.TimeFieldAdapter;
import org.dmfs.tasks.utils.ExpandableChildDescriptor;
import org.dmfs.tasks.utils.ExpandableGroupDescriptor;
import org.dmfs.tasks.utils.ExpandableGroupDescriptorAdapter;
Expand Down Expand Up @@ -56,11 +55,6 @@
*/
public class BySearch extends AbstractGroupingFactory
{
/**
* An adapter to load the due date from the tasks projection.
*/
public final static TimeFieldAdapter TASK_DUE_ADAPTER = new TimeFieldAdapter(Tasks.DUE, Tasks.TZ, Tasks.IS_ALLDAY);

/**
* A {@link ViewDescriptor} that knows how to present the tasks in the task list grouped by priority.
*/
Expand Down
Expand Up @@ -17,12 +17,16 @@

package org.dmfs.tasks.model;

import android.text.format.Time;

import org.dmfs.provider.tasks.TaskContract;
import org.dmfs.provider.tasks.TaskContract.Tasks;
import org.dmfs.tasks.model.adapters.BooleanFieldAdapter;
import org.dmfs.tasks.model.adapters.ChecklistFieldAdapter;
import org.dmfs.tasks.model.adapters.ColorFieldAdapter;
import org.dmfs.tasks.model.adapters.CustomizedDefaultFieldAdapter;
import org.dmfs.tasks.model.adapters.DescriptionStringFieldAdapter;
import org.dmfs.tasks.model.adapters.FieldAdapter;
import org.dmfs.tasks.model.adapters.FloatFieldAdapter;
import org.dmfs.tasks.model.adapters.FormattedStringFieldAdapter;
import org.dmfs.tasks.model.adapters.IntegerFieldAdapter;
Expand All @@ -31,9 +35,11 @@
import org.dmfs.tasks.model.adapters.TimezoneFieldAdapter;
import org.dmfs.tasks.model.adapters.UrlFieldAdapter;
import org.dmfs.tasks.model.constraints.AdjustPercentComplete;
import org.dmfs.tasks.model.constraints.After;
import org.dmfs.tasks.model.constraints.BeforeOrShiftTime;
import org.dmfs.tasks.model.constraints.ChecklistConstraint;
import org.dmfs.tasks.model.constraints.NotBefore;
import org.dmfs.tasks.model.constraints.ShiftIfAfter;
import org.dmfs.tasks.model.defaults.DefaultAfter;
import org.dmfs.tasks.model.defaults.DefaultBefore;


/**
Expand Down Expand Up @@ -109,17 +115,17 @@ public final class TaskFieldAdapters
* Private adapter for the start date of a task. We need this to reference DTSTART from DUE.
*/
private final static TimeFieldAdapter _DTSTART = new TimeFieldAdapter(Tasks.DTSTART, Tasks.TZ, Tasks.IS_ALLDAY);
private final static TimeFieldAdapter _DUE = new TimeFieldAdapter(Tasks.DUE, Tasks.TZ, Tasks.IS_ALLDAY);

/**
* Adapter for the due date of a task.
*/
public final static TimeFieldAdapter DUE = (TimeFieldAdapter) new TimeFieldAdapter(Tasks.DUE, Tasks.TZ, Tasks.IS_ALLDAY).addContraint(new NotBefore(
_DTSTART));
public final static FieldAdapter<Time> DUE = new CustomizedDefaultFieldAdapter<Time>(_DUE, new DefaultAfter(_DTSTART)).addContraint(new After(_DTSTART));

/**
* Adapter for the start date of a task.
*/
public final static TimeFieldAdapter DTSTART = (TimeFieldAdapter) _DTSTART.addContraint(new ShiftIfAfter(DUE));
public final static FieldAdapter<Time> DTSTART = new CustomizedDefaultFieldAdapter<Time>(_DTSTART, new DefaultBefore(DUE)).addContraint(new BeforeOrShiftTime(DUE));

/**
* Adapter for the completed date of a task.
Expand Down
@@ -0,0 +1,93 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

package org.dmfs.tasks.model.adapters;

import android.content.ContentValues;
import android.database.Cursor;

import org.dmfs.tasks.model.ContentSet;
import org.dmfs.tasks.model.OnContentChangeListener;
import org.dmfs.tasks.model.defaults.Default;


/**
* Enhances an existing {@link FieldAdapter} with a custom default value generator.
*
* @param <Type> Type of the {@link FieldAdapter}
*/
public class CustomizedDefaultFieldAdapter<Type> extends FieldAdapter<Type> {

private final FieldAdapter<Type> mFieldAdapter;
private final Default<Type> mDefault;

/**
* Constructor for a new CustomizedDefaultFieldAdapter
* @param fieldAdapter FieldAdapter which forms the base for this Adapter.
* @param defaultGenerator Custom default value generator.
*/
public CustomizedDefaultFieldAdapter(FieldAdapter<Type> fieldAdapter, Default<Type> defaultGenerator) {
if (fieldAdapter == null) {
throw new IllegalArgumentException("fieldAdapter must not be null");
}
if (defaultGenerator == null) {
throw new IllegalArgumentException("defaultGenerator must not be null");
}
this.mFieldAdapter = fieldAdapter;
this.mDefault = defaultGenerator;
}

@Override
public Type get(ContentSet values) {
return mFieldAdapter.get(values);
}

@Override
public Type get(Cursor cursor) {
return mFieldAdapter.get(cursor);
}

/**
* Get a default value for the {@link FieldAdapter} based on the {@link Default} instance.
*
* @param values The {@link ContentSet}.
* @return A default Value
*/
@Override
public Type getDefault(ContentSet values) {
Type defaultValue = mFieldAdapter.getDefault(values);
return mDefault.getCustomDefault(values, defaultValue);
}

@Override
public void set(ContentSet values, Type value) {
mFieldAdapter.set(values, value);
}

@Override
public void set(ContentValues values, Type value) {
mFieldAdapter.set(values, value);
}

@Override
public void registerListener(ContentSet values, OnContentChangeListener listener, boolean initialNotification) {
mFieldAdapter.registerListener(values, listener, initialNotification);
}

@Override
public void unregisterListener(ContentSet values, OnContentChangeListener listener) {
mFieldAdapter.unregisterListener(values, listener);
}
}
Expand Up @@ -156,13 +156,7 @@ public Time getDefault(ContentSet values)
{
// make it an allday value
value.set(value.monthDay, value.month, value.year);
}
else
{
value.second = 0;
// round up to next quarter-hour
value.minute = ((value.minute + 14) / 15) * 15;
value.normalize(false);
value.timezone = Time.TIMEZONE_UTC; // all-day values are saved in UTC
}

return value;
Expand Down
@@ -0,0 +1,53 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

package org.dmfs.tasks.model.constraints;

import android.text.format.Time;

import org.dmfs.tasks.model.ContentSet;
import org.dmfs.tasks.model.adapters.FieldAdapter;
import org.dmfs.tasks.model.defaults.Default;
import org.dmfs.tasks.model.defaults.DefaultAfter;


/**
* Ensure a time is after a specific reference time. The new value will be set using {@link DefaultAfter} otherwise.
*/
public class After extends AbstractConstraint<Time>
{
private final FieldAdapter<Time> mReferenceAdapter;
private final Default<Time> mDefault;


public After(FieldAdapter<Time> referenceAdapter)
{
mReferenceAdapter = referenceAdapter;
mDefault = new DefaultAfter(referenceAdapter);
}


@Override
public Time apply(ContentSet currentValues, Time oldValue, Time newValue)
{
Time reference = mReferenceAdapter.get(currentValues);
if (reference != null && newValue != null && !newValue.after(reference))
{
newValue.set(mDefault.getCustomDefault(currentValues, reference));
}
return newValue;
}

}
@@ -0,0 +1,77 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

package org.dmfs.tasks.model.constraints;

import android.text.format.Time;

import org.dmfs.tasks.model.ContentSet;
import org.dmfs.tasks.model.adapters.FieldAdapter;
import org.dmfs.tasks.model.defaults.Default;
import org.dmfs.tasks.model.defaults.DefaultAfter;


/**
* Ensure a time is before a specific reference time.
* Otherwise, shift the reference time by the same amount that value has been shifted.
* If this still violated the constraint, then set the reference value to its default value.
*/
public class BeforeOrShiftTime extends AbstractConstraint<Time>
{
private final FieldAdapter<Time> mReferenceAdapter;
private final Default<Time> mDefault;


public BeforeOrShiftTime(FieldAdapter<Time> referenceAdapter)
{
mReferenceAdapter = referenceAdapter;
mDefault = new DefaultAfter(null);
}


@Override
public Time apply(ContentSet currentValues, Time oldValue, Time newValue)
{
Time reference = mReferenceAdapter.get(currentValues);
if (reference != null && newValue != null)
{
if (oldValue != null && !newValue.before(reference))
{
// try to shift the reference value
long diff = newValue.toMillis(false) - oldValue.toMillis(false);
if (diff > 0)
{
boolean isAllDay = reference.allDay;
reference.set(reference.toMillis(false) + diff);

// ensure the event is still allday if is was allday before.
if (isAllDay)
{
reference.set(reference.monthDay, reference.month, reference.year);
}
mReferenceAdapter.set(currentValues, reference);
}
}
if (!newValue.before(reference))
{
// constraint is still violated, so set reference to its default value
reference.set(mDefault.getCustomDefault(currentValues, newValue));
mReferenceAdapter.set(currentValues, reference);
}
}
return newValue;
}

}
Expand Up @@ -18,7 +18,7 @@
package org.dmfs.tasks.model.constraints;

import org.dmfs.tasks.model.ContentSet;
import org.dmfs.tasks.model.adapters.TimeFieldAdapter;
import org.dmfs.tasks.model.adapters.FieldAdapter;

import android.text.format.Time;

Expand All @@ -30,10 +30,10 @@
*/
public class NotAfter extends AbstractConstraint<Time>
{
private final TimeFieldAdapter mTimeAdapter;
private final FieldAdapter<Time> mTimeAdapter;


public NotAfter(TimeFieldAdapter adapter)
public NotAfter(FieldAdapter<Time> adapter)
{
mTimeAdapter = adapter;
}
Expand Down
Expand Up @@ -18,7 +18,7 @@
package org.dmfs.tasks.model.constraints;

import org.dmfs.tasks.model.ContentSet;
import org.dmfs.tasks.model.adapters.TimeFieldAdapter;
import org.dmfs.tasks.model.adapters.FieldAdapter;

import android.text.format.Time;

Expand All @@ -30,10 +30,10 @@
*/
public class NotBefore extends AbstractConstraint<Time>
{
private final TimeFieldAdapter mTimeAdapter;
private final FieldAdapter<Time> mTimeAdapter;


public NotBefore(TimeFieldAdapter adapter)
public NotBefore(FieldAdapter<Time> adapter)
{
mTimeAdapter = adapter;
}
Expand Down
Expand Up @@ -18,7 +18,7 @@
package org.dmfs.tasks.model.constraints;

import org.dmfs.tasks.model.ContentSet;
import org.dmfs.tasks.model.adapters.TimeFieldAdapter;
import org.dmfs.tasks.model.adapters.FieldAdapter;

import android.text.format.Time;

Expand All @@ -30,10 +30,10 @@
*/
public class ShiftIfAfter extends AbstractConstraint<Time>
{
private final TimeFieldAdapter mTimeAdapter;
private final FieldAdapter<Time> mTimeAdapter;


public ShiftIfAfter(TimeFieldAdapter adapter)
public ShiftIfAfter(FieldAdapter<Time> adapter)
{
mTimeAdapter = adapter;
}
Expand Down