Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7750d5c9 authored by Marten Gajda's avatar Marten Gajda
Browse files

use separate adapters for checklist and description that take care of

handling the updates. This gives us more flexibility when dealing with
checklists.
parent 8d54e378
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
package org.dmfs.tasks.model;

public final class CheckListItem
{
	public boolean checked;
	public String text;


	public CheckListItem(boolean checked, String text)
	{
		this.checked = checked;
		this.text = text;
	}
}
+2 −2
Original line number Diff line number Diff line
@@ -96,8 +96,8 @@ public class DefaultModel extends Model
		mFields.add(new FieldDescriptor(mContext, R.string.task_location, TaskFieldAdapters.LOCATION).setViewLayout(TEXT_VIEW).setEditorLayout(TEXT_EDIT));

		// description
		mFields.add(new FieldDescriptor(mContext, R.string.task_description, TaskFieldAdapters.DESCRIPTION).setViewLayout(CHECKLIST_VIEW).setEditorLayout(
			CHECKLIST_EDIT));
		mFields.add(new FieldDescriptor(mContext, R.string.task_description, TaskFieldAdapters.DESCRIPTION).setViewLayout(TEXT_VIEW).setEditorLayout(
			TEXT_EDIT));

		// start
		mFields.add(new FieldDescriptor(mContext, R.string.task_start, TaskFieldAdapters.DTSTART).setViewLayout(TIME_VIEW).setEditorLayout(TIME_EDIT));
+2 −3
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ package org.dmfs.tasks.model;
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.DescriptionStringFieldAdapter;
import org.dmfs.tasks.model.adapters.FloatFieldAdapter;
import org.dmfs.tasks.model.adapters.IntegerFieldAdapter;
import org.dmfs.tasks.model.adapters.StringFieldAdapter;
@@ -27,7 +28,6 @@ import org.dmfs.tasks.model.adapters.TimeFieldAdapter;
import org.dmfs.tasks.model.adapters.TimezoneFieldAdapter;
import org.dmfs.tasks.model.adapters.UrlFieldAdapter;
import org.dmfs.tasks.model.contraints.AdjustPercentComplete;
import org.dmfs.tasks.model.contraints.ChecklistConstraint;
import org.dmfs.tasks.model.contraints.NotAfter;
import org.dmfs.tasks.model.contraints.NotBefore;

@@ -88,8 +88,7 @@ public final class TaskFieldAdapters
	/**
	 * Adapter for the description of a task.
	 */
	public final static StringFieldAdapter DESCRIPTION = (StringFieldAdapter) new StringFieldAdapter(Tasks.DESCRIPTION).addContraint(new ChecklistConstraint(
		STATUS, PERCENT_COMPLETE));
	public final static DescriptionStringFieldAdapter DESCRIPTION = new DescriptionStringFieldAdapter(Tasks.DESCRIPTION);

	/**
	 * Private adapter for the start date of a task. We need this to reference DTSTART from DUE.
+2 −2
Original line number Diff line number Diff line
@@ -384,8 +384,8 @@ public class XmlModel extends Model
		FIELD_INFLATER_MAP.put("title", new FieldInflater(TaskFieldAdapters.TITLE, R.string.task_title, R.layout.text_field_view, R.layout.text_field_editor));
		FIELD_INFLATER_MAP.put("location", new FieldInflater(TaskFieldAdapters.LOCATION, R.string.task_location, R.layout.text_field_view,
			R.layout.text_field_editor));
		FIELD_INFLATER_MAP.put("description", new FieldInflater(TaskFieldAdapters.DESCRIPTION, R.string.task_description, R.layout.checklist_field_view,
			R.layout.checklist_field_editor));
		FIELD_INFLATER_MAP.put("description", new FieldInflater(TaskFieldAdapters.DESCRIPTION, R.string.task_description, R.layout.text_field_view,
			R.layout.text_field_editor));

		FIELD_INFLATER_MAP.put("dtstart", new FieldInflater(TaskFieldAdapters.DTSTART, R.string.task_start, R.layout.time_field_view,
			R.layout.time_field_editor));
+221 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2012 Marten Gajda <marten@dmfs.org>
 *
 * 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 java.util.ArrayList;
import java.util.List;

import org.dmfs.tasks.model.CheckListItem;
import org.dmfs.tasks.model.ContentSet;
import org.dmfs.tasks.model.OnContentChangeListener;

import android.database.Cursor;


/**
 * Knows how to load and store check list from/to a combined description/check list field.
 * 
 * @author Marten Gajda <marten@dmfs.org>
 */
public final class ChecklistFieldAdapter extends FieldAdapter<List<CheckListItem>>
{

	/**
	 * The field name this adapter uses to store the values.
	 */
	private final String mFieldName;

	/**
	 * The default value, if any.
	 */
	private final List<CheckListItem> mDefaultValue;


	/**
	 * Constructor for a new StringFieldAdapter without default value.
	 * 
	 * @param fieldName
	 *            The name of the field to use when loading or storing the value.
	 */
	public ChecklistFieldAdapter(String fieldName)
	{
		this(fieldName, null);
	}


	/**
	 * Constructor for a new StringFieldAdapter without default value.
	 * 
	 * @param fieldName
	 *            The name of the field to use when loading or storing the value.
	 * @param defaultValue
	 *            The default check list
	 */
	public ChecklistFieldAdapter(String fieldName, List<CheckListItem> defaultValue)
	{
		if (fieldName == null)
		{
			throw new IllegalArgumentException("fieldName must not be null");
		}
		mFieldName = fieldName;
		mDefaultValue = defaultValue;
	}


	@Override
	public List<CheckListItem> get(ContentSet values)
	{
		// return the check list
		return extractCheckList(values.getAsString(mFieldName));
	}


	@Override
	public List<CheckListItem> get(Cursor cursor)
	{
		int columnIdx = cursor.getColumnIndex(mFieldName);
		if (columnIdx < 0)
		{
			throw new IllegalArgumentException("The fieldName column missing in cursor.");
		}
		return extractCheckList(cursor.getString(columnIdx));
	}


	@Override
	public List<CheckListItem> getDefault(ContentSet values)
	{
		return mDefaultValue;
	}


	@Override
	public void set(ContentSet values, List<CheckListItem> value)
	{
		String oldDescription = DescriptionStringFieldAdapter.extractDescription(values.getAsString(mFieldName));
		if (value != null && value.size() > 0)
		{
			StringBuilder sb = new StringBuilder(1024);
			if (oldDescription != null)
			{
				sb.append(oldDescription);
				sb.append("\n");
			}

			serializeCheckList(sb, value);

			values.put(mFieldName, sb.toString());
		}
		else
		{
			// store the current value just without check list
			values.put(mFieldName, oldDescription);
		}
	}


	@Override
	public void registerListener(ContentSet values, OnContentChangeListener listener, boolean initalNotification)
	{
		values.addOnChangeListener(listener, mFieldName, initalNotification);
	}


	@Override
	public void unregisterListener(ContentSet values, OnContentChangeListener listener)
	{
		values.removeOnChangeListener(listener, mFieldName);
	}


	private static List<CheckListItem> extractCheckList(String value)
	{
		if (value != null && value.length() >= 3)
		{
			int checklistpos = -1;
			while ((checklistpos = value.indexOf("[", checklistpos + 1)) >= 0)
			{
				if (value.length() > checklistpos + 2 && value.charAt(checklistpos + 2) == ']' && (checklistpos == 0 || value.charAt(checklistpos - 1) == '\n'))
				{
					char checkmark = value.charAt(checklistpos + 1);
					if (checkmark == ' ' || checkmark == 'x' || checkmark == 'X')
					{
						return parseCheckList(value.substring(checklistpos));
					}
				}
			}
		}
		return new ArrayList<CheckListItem>(4);
	}


	private static List<CheckListItem> parseCheckList(String checklist)
	{
		List<CheckListItem> result = new ArrayList<CheckListItem>(16);
		String[] lines = checklist.split("\n");

		for (String line : lines)
		{
			line = line.trim();
			if (line.length() == 0)
			{
				// skip empty lines
				continue;
			}

			if (line.startsWith("[x]") || line.startsWith("[X]"))
			{
				result.add(new CheckListItem(true, line.substring(3).trim()));
			}
			else if (line.startsWith("[ ]"))
			{
				result.add(new CheckListItem(false, line.substring(3).trim()));
			}
			else
			{
				result.add(new CheckListItem(false, line));
			}
		}
		return result;
	}


	private static void serializeCheckList(StringBuilder sb, List<CheckListItem> checklist)
	{
		if (checklist == null || checklist.size() == 0)
		{
			return;
		}

		boolean first = true;
		for (CheckListItem item : checklist)
		{
			if (first)
			{
				first = false;
			}
			else
			{
				sb.append('\n');
			}
			sb.append(item.checked ? "[x] " : "[ ] ");
			sb.append(item.text);
		}
	}

}
Loading