mirror of https://github.com/tasks/tasks
First pass at adding tagging features to astrid. Add tags from the editor view, also a tags listing page to view the tags you have.
parent
0af4cf9d99
commit
065fc1a9d4
@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2008 The Android Open Source Project
|
||||
|
||||
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.
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<AutoCompleteTextView android:id="@+id/text1"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_gravity="center_vertical"/>
|
||||
|
||||
<ImageButton android:id="@+id/button1"
|
||||
style="?android:attr/buttonStyleInset"
|
||||
android:src="@android:drawable/ic_delete"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_marginTop="2dip"
|
||||
android:layout_marginRight="2dip"
|
||||
android:layout_marginBottom="2dip"
|
||||
android:gravity="center_vertical"
|
||||
/>
|
||||
</LinearLayout>
|
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout android:id="@+id/taglist_layout"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent">
|
||||
|
||||
<ListView android:id="@+id/taglist"
|
||||
android:layout_weight="1"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
</LinearLayout>
|
@ -0,0 +1,231 @@
|
||||
/*
|
||||
* ASTRID: Android's Simple Task Recording Dashboard
|
||||
*
|
||||
* Copyright (c) 2009 Tim Su
|
||||
*
|
||||
* 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 2 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
package com.timsu.astrid.activities;
|
||||
import java.util.List;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Bundle;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ContextMenu.ContextMenuInfo;
|
||||
import android.view.View.OnCreateContextMenuListener;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.AdapterView.AdapterContextMenuInfo;
|
||||
import android.widget.AdapterView.OnItemClickListener;
|
||||
|
||||
import com.timsu.astrid.R;
|
||||
import com.timsu.astrid.data.tag.TagController;
|
||||
import com.timsu.astrid.data.tag.TagIdentifier;
|
||||
import com.timsu.astrid.data.tag.TagModelForView;
|
||||
import com.timsu.astrid.data.task.TaskIdentifier;
|
||||
|
||||
|
||||
/** List all tags and allows a user to see all tasks for a given tag
|
||||
*
|
||||
* @author Tim Su (timsu@stanfordalumni.org)
|
||||
*
|
||||
*/
|
||||
public class TagList extends Activity {
|
||||
private static final int ACTIVITY_LIST = 0;
|
||||
private static final int ACTIVITY_CREATE = 1;
|
||||
|
||||
private static final int CONTEXT_CREATE_ID = Menu.FIRST + 10;
|
||||
private static final int CONTEXT_DELETE_ID = Menu.FIRST + 11;
|
||||
|
||||
private TagController controller;
|
||||
private ListView listView;
|
||||
|
||||
private List<TagModelForView> tagArray;
|
||||
|
||||
/** Called when loading up the activity for the first time */
|
||||
private void onLoad() {
|
||||
controller = new TagController(this);
|
||||
controller.open();
|
||||
|
||||
listView = (ListView)findViewById(R.id.taglist);
|
||||
|
||||
fillData();
|
||||
}
|
||||
|
||||
/** Fill in the Tag List with our tags */
|
||||
private void fillData() {
|
||||
Resources r = getResources();
|
||||
|
||||
tagArray = controller.getAllTags();
|
||||
|
||||
// set up the title
|
||||
StringBuilder title = new StringBuilder().
|
||||
append(r.getString(R.string.tagList_titlePrefix)).
|
||||
append(" ").append(r.getQuantityString(R.plurals.Ntags,
|
||||
tagArray.size(), tagArray.size()));
|
||||
setTitle(title);
|
||||
|
||||
// set up our adapter
|
||||
TagListAdapter tagAdapter = new TagListAdapter(this,
|
||||
android.R.layout.simple_list_item_1, tagArray);
|
||||
listView.setAdapter(tagAdapter);
|
||||
|
||||
// list view listener
|
||||
listView.setOnItemClickListener(new OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view,
|
||||
int position, long id) {
|
||||
TagModelForView tag = (TagModelForView)view.getTag();
|
||||
|
||||
Intent intent = new Intent(TagList.this, TaskList.class);
|
||||
intent.putExtra(TaskList.TAG_TOKEN, tag.
|
||||
getTagIdentifier().getId());
|
||||
startActivityForResult(intent, ACTIVITY_LIST);
|
||||
}
|
||||
});
|
||||
|
||||
listView.setOnCreateContextMenuListener(new OnCreateContextMenuListener() {
|
||||
@Override
|
||||
public void onCreateContextMenu(ContextMenu menu, View v,
|
||||
ContextMenuInfo menuInfo) {
|
||||
AdapterContextMenuInfo adapterMenuInfo =
|
||||
(AdapterContextMenuInfo)menuInfo;
|
||||
int position = adapterMenuInfo.position;
|
||||
|
||||
menu.add(position, CONTEXT_CREATE_ID, Menu.NONE,
|
||||
R.string.tagList_context_create);
|
||||
menu.add(position, CONTEXT_DELETE_ID, Menu.NONE,
|
||||
R.string.tagList_context_delete);
|
||||
|
||||
menu.setHeaderTitle(tagArray.get(position).getName());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode,
|
||||
Intent intent) {
|
||||
fillData();
|
||||
}
|
||||
|
||||
// --- list adapter
|
||||
|
||||
private class TagListAdapter extends ArrayAdapter<TagModelForView> {
|
||||
|
||||
private List<TagModelForView> objects;
|
||||
private int resource;
|
||||
private LayoutInflater inflater;
|
||||
|
||||
public TagListAdapter(Context context, int resource,
|
||||
List<TagModelForView> objects) {
|
||||
super(context, resource, objects);
|
||||
|
||||
inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
this.objects = objects;
|
||||
this.resource = resource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
View view;
|
||||
|
||||
view = inflater.inflate(resource, parent, false);
|
||||
setupView(view, objects.get(position));
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
public void setupView(View view, final TagModelForView tag) {
|
||||
// set up basic properties
|
||||
view.setTag(tag);
|
||||
|
||||
List<TaskIdentifier> tasks = controller.getTaggedTasks(
|
||||
tag.getTagIdentifier());
|
||||
|
||||
final TextView name = ((TextView)view.findViewById(android.R.id.text1));
|
||||
name.setText(new StringBuilder(tag.getName()).
|
||||
append(" (").append(tasks.size()).append(")"));
|
||||
}
|
||||
}
|
||||
|
||||
// --- ui control handlers
|
||||
|
||||
private void createTask(TagModelForView tag) {
|
||||
Intent intent = new Intent(this, TaskEdit.class);
|
||||
intent.putExtra(TaskEdit.TAG_NAME_TOKEN, tag.getName());
|
||||
startActivityForResult(intent, ACTIVITY_CREATE);
|
||||
}
|
||||
|
||||
private void deleteTag(final TagIdentifier tagId) {
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle(R.string.delete_title)
|
||||
.setMessage(R.string.delete_this_tag_title)
|
||||
.setIcon(android.R.drawable.ic_dialog_alert)
|
||||
.setPositiveButton(android.R.string.ok,
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
controller.deleteTag(tagId);
|
||||
fillData();
|
||||
}
|
||||
})
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMenuItemSelected(int featureId, MenuItem item) {
|
||||
switch(item.getItemId()) {
|
||||
case CONTEXT_CREATE_ID:
|
||||
TagModelForView tag = tagArray.get(item.getGroupId());
|
||||
createTask(tag);
|
||||
return true;
|
||||
case CONTEXT_DELETE_ID:
|
||||
tag = tagArray.get(item.getGroupId());
|
||||
deleteTag(tag.getTagIdentifier());
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.onMenuItemSelected(featureId, item);
|
||||
}
|
||||
|
||||
// --- creating stuff
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.tag_list);
|
||||
|
||||
onLoad();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
controller.close();
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package com.timsu.astrid.data;
|
||||
|
||||
/** Identifier of a single object. Extend this class to create your own */
|
||||
public abstract class Identifier {
|
||||
private long id;
|
||||
|
||||
public Identifier(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String idAsString() {
|
||||
return Long.toString(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (int)id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if(o == null || o.getClass() != getClass())
|
||||
return false;
|
||||
|
||||
return ((Identifier)o).getId() == getId();
|
||||
}
|
||||
}
|
@ -0,0 +1,177 @@
|
||||
package com.timsu.astrid.data.tag;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.util.Log;
|
||||
|
||||
import com.timsu.astrid.data.AbstractController;
|
||||
import com.timsu.astrid.data.AbstractModel;
|
||||
|
||||
|
||||
/** Abstract model of a task. Subclasses implement the getters and setters
|
||||
* they are interested in.
|
||||
*
|
||||
* @author timsu
|
||||
*
|
||||
*/
|
||||
public abstract class AbstractTagModel extends AbstractModel {
|
||||
|
||||
/** Version number of this model */
|
||||
static final int VERSION = 1;
|
||||
|
||||
// field names
|
||||
|
||||
static final String NAME = "name";
|
||||
static final String NOTES = "notes";
|
||||
// reserved fields
|
||||
static final String ICON = "icon";
|
||||
static final String PARENT = "parent";
|
||||
static final String FLAGS = "flags";
|
||||
static final String LOCATION_LAT = "locationLat";
|
||||
static final String LOCATION_LONG = "locationLong";
|
||||
static final String NOTIFICATIONS = "notifications";
|
||||
// end reserved fields
|
||||
static final String CREATION_DATE = "creationDate";
|
||||
|
||||
/** Default values container */
|
||||
private static final ContentValues defaultValues = new ContentValues();
|
||||
|
||||
static {
|
||||
defaultValues.put(NAME, "");
|
||||
defaultValues.put(NOTES, "");
|
||||
defaultValues.put(ICON, (Integer)null);
|
||||
defaultValues.put(PARENT, (Long)null);
|
||||
defaultValues.put(FLAGS, (Integer)0);
|
||||
defaultValues.put(LOCATION_LAT, (Integer)null);
|
||||
defaultValues.put(LOCATION_LONG, (Integer)null);
|
||||
defaultValues.put(NOTIFICATIONS, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContentValues getDefaultValues() {
|
||||
return defaultValues;
|
||||
}
|
||||
|
||||
// --- database helper
|
||||
|
||||
/** Database Helper manages creating new tables and updating old ones */
|
||||
static class TagModelDatabaseHelper extends SQLiteOpenHelper {
|
||||
String tableName;
|
||||
|
||||
TagModelDatabaseHelper(Context context, String databaseName, String tableName) {
|
||||
super(context, databaseName, null, VERSION);
|
||||
this.tableName = tableName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase db) {
|
||||
String sql = new StringBuilder().
|
||||
append("CREATE TABLE ").append(tableName).append(" (").
|
||||
append(AbstractController.KEY_ROWID).append(" integer primary key autoincrement, ").
|
||||
append(NAME).append(" text unique not null,").
|
||||
append(NOTES).append(" text not null,").
|
||||
append(ICON).append(" integer,").
|
||||
append(PARENT).append(" integer,").
|
||||
append(FLAGS).append(" integer not null,").
|
||||
append(LOCATION_LAT).append(" integer,").
|
||||
append(LOCATION_LONG).append(" integer,").
|
||||
append(NOTIFICATIONS).append(" integer,").
|
||||
append(CREATION_DATE).append(" integer").
|
||||
append(");").toString();
|
||||
db.execSQL(sql);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
Log.w(getClass().getSimpleName(), "Upgrading database from version " +
|
||||
oldVersion + " to " + newVersion + ".");
|
||||
|
||||
switch(oldVersion) {
|
||||
default:
|
||||
// we don't know how to handle it... do the unfortunate thing
|
||||
Log.e(getClass().getSimpleName(), "Unsupported migration, table dropped!");
|
||||
db.execSQL("DROP TABLE IF EXISTS " + tableName);
|
||||
onCreate(db);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- utility methods
|
||||
|
||||
|
||||
|
||||
// --- identifier
|
||||
|
||||
private TagIdentifier identifier = null;
|
||||
|
||||
public TagIdentifier getTagIdentifier() {
|
||||
return identifier;
|
||||
}
|
||||
|
||||
void setTagIdentifier(TagIdentifier identifier) {
|
||||
this.identifier = identifier;
|
||||
}
|
||||
|
||||
// --- constructor pass-through
|
||||
|
||||
AbstractTagModel() {
|
||||
super();
|
||||
}
|
||||
|
||||
/** Read identifier from database */
|
||||
AbstractTagModel(Cursor cursor) {
|
||||
super(cursor);
|
||||
|
||||
Integer id = retrieveInteger(AbstractController.KEY_ROWID);
|
||||
setTagIdentifier(new TagIdentifier(id));
|
||||
}
|
||||
|
||||
/** Get identifier from argument */
|
||||
AbstractTagModel(TagIdentifier identifier, Cursor cursor) {
|
||||
super(cursor);
|
||||
|
||||
setTagIdentifier(identifier);
|
||||
}
|
||||
|
||||
// --- getters and setters: expose them as you see fit
|
||||
|
||||
protected String getName() {
|
||||
return retrieveString(NAME);
|
||||
}
|
||||
|
||||
protected String getNotes() {
|
||||
return retrieveString(NOTES);
|
||||
}
|
||||
|
||||
protected Date getCreationDate() {
|
||||
return retrieveDate(CREATION_DATE);
|
||||
}
|
||||
|
||||
// --- setters
|
||||
|
||||
protected void setName(String name) {
|
||||
setValues.put(NAME, name);
|
||||
}
|
||||
|
||||
protected void setNotes(String notes) {
|
||||
setValues.put(NOTES, notes);
|
||||
}
|
||||
|
||||
protected void setCreationDate(Date creationDate) {
|
||||
putDate(setValues, CREATION_DATE, creationDate);
|
||||
}
|
||||
|
||||
// --- utility methods
|
||||
|
||||
static void putDate(ContentValues cv, String fieldName, Date date) {
|
||||
if(date == null)
|
||||
cv.put(fieldName, (Long)null);
|
||||
else
|
||||
cv.put(fieldName, date.getTime());
|
||||
}
|
||||
}
|
@ -0,0 +1,202 @@
|
||||
package com.timsu.astrid.data.tag;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.database.SQLException;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
|
||||
import com.timsu.astrid.data.AbstractController;
|
||||
import com.timsu.astrid.data.tag.AbstractTagModel.TagModelDatabaseHelper;
|
||||
import com.timsu.astrid.data.tag.TagToTaskMapping.TagToTaskMappingDatabaseHelper;
|
||||
import com.timsu.astrid.data.task.TaskIdentifier;
|
||||
|
||||
public class TagController extends AbstractController {
|
||||
|
||||
private SQLiteDatabase tagDatabase, tagToTaskMapDatabase;
|
||||
|
||||
// --- tag batch operations
|
||||
|
||||
/** Get a list of all tags */
|
||||
public List<TagModelForView> getAllTags()
|
||||
throws SQLException {
|
||||
List<TagModelForView> list = new LinkedList<TagModelForView>();
|
||||
Cursor cursor = tagDatabase.query(TAG_TABLE_NAME,
|
||||
TagModelForView.FIELD_LIST, null, null, null, null, null, null);
|
||||
activity.startManagingCursor(cursor);
|
||||
|
||||
if(cursor.getCount() == 0)
|
||||
return list;
|
||||
do {
|
||||
cursor.moveToNext();
|
||||
list.add(new TagModelForView(cursor));
|
||||
} while(!cursor.isLast());
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
// --- tag to task map batch operations
|
||||
|
||||
/** Get a list of all tags as an id => tag map */
|
||||
public Map<TagIdentifier, TagModelForView> getAllTagsAsMap() throws SQLException {
|
||||
Map<TagIdentifier, TagModelForView> map = new HashMap<TagIdentifier, TagModelForView>();
|
||||
for(TagModelForView tag : getAllTags())
|
||||
map.put(tag.getTagIdentifier(), tag);
|
||||
return map;
|
||||
}
|
||||
|
||||
/** Get a list of tag identifiers for the given task */
|
||||
public List<TagIdentifier> getTaskTags(TaskIdentifier
|
||||
taskId) throws SQLException {
|
||||
List<TagIdentifier> list = new LinkedList<TagIdentifier>();
|
||||
Cursor cursor = tagToTaskMapDatabase.query(TAG_TASK_MAP_NAME,
|
||||
TagToTaskMapping.FIELD_LIST, TagToTaskMapping.TASK + " = ?",
|
||||
new String[] { taskId.idAsString() }, null, null, null);
|
||||
activity.startManagingCursor(cursor);
|
||||
|
||||
if(cursor.getCount() == 0)
|
||||
return list;
|
||||
do {
|
||||
cursor.moveToNext();
|
||||
list.add(new TagToTaskMapping(cursor).getTag());
|
||||
} while(!cursor.isLast());
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/** Get a list of task identifiers for the given tag */
|
||||
public List<TaskIdentifier> getTaggedTasks(TagIdentifier
|
||||
tagId) throws SQLException {
|
||||
List<TaskIdentifier> list = new LinkedList<TaskIdentifier>();
|
||||
Cursor cursor = tagToTaskMapDatabase.query(TAG_TASK_MAP_NAME,
|
||||
TagToTaskMapping.FIELD_LIST, TagToTaskMapping.TAG + " = ?",
|
||||
new String[] { tagId.idAsString() }, null, null, null);
|
||||
activity.startManagingCursor(cursor);
|
||||
|
||||
if(cursor.getCount() == 0)
|
||||
return list;
|
||||
do {
|
||||
cursor.moveToNext();
|
||||
list.add(new TagToTaskMapping(cursor).getTask());
|
||||
} while(!cursor.isLast());
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
// --- single tag operations
|
||||
|
||||
public TagIdentifier createTag(String name) throws SQLException {
|
||||
if(name == null)
|
||||
throw new NullPointerException("Name can't be null");
|
||||
|
||||
TagModelForView newTag = new TagModelForView(name);
|
||||
long row = tagDatabase.insertOrThrow(TAG_TABLE_NAME, AbstractTagModel.NAME,
|
||||
newTag.getMergedValues());
|
||||
return new TagIdentifier(row);
|
||||
}
|
||||
|
||||
/** Creates or saves the given tag */
|
||||
public boolean saveTag(AbstractTagModel tag) throws SQLException {
|
||||
boolean saveSucessful;
|
||||
|
||||
if(tag.getTagIdentifier() == null) {
|
||||
long newRow = tagDatabase.insert(TAG_TABLE_NAME, AbstractTagModel.NAME,
|
||||
tag.getMergedValues());
|
||||
tag.setTagIdentifier(new TagIdentifier(newRow));
|
||||
|
||||
saveSucessful = newRow >= 0;
|
||||
} else {
|
||||
long id = tag.getTagIdentifier().getId();
|
||||
saveSucessful = tagDatabase.update(TAG_TABLE_NAME, tag.getSetValues(),
|
||||
KEY_ROWID + "=" + id, null) > 0;
|
||||
}
|
||||
|
||||
return saveSucessful;
|
||||
}
|
||||
|
||||
/** Returns a TaskModelForView corresponding to the given TaskIdentifier */
|
||||
public TagModelForView fetchTagForView(TagIdentifier tagId) throws SQLException {
|
||||
long id = tagId.getId();
|
||||
Cursor cursor = tagDatabase.query(true, TAG_TABLE_NAME,
|
||||
TagModelForView.FIELD_LIST,
|
||||
KEY_ROWID + "=" + id, null, null, null, null, null);
|
||||
|
||||
if (cursor != null) {
|
||||
cursor.moveToFirst();
|
||||
TagModelForView model = new TagModelForView(cursor);
|
||||
return model;
|
||||
}
|
||||
|
||||
throw new SQLException("Returned empty set!");
|
||||
}
|
||||
|
||||
/** Deletes the tag and removes tag/task mappings */
|
||||
public boolean deleteTag( TagIdentifier tagId)
|
||||
throws SQLException{
|
||||
if(tagToTaskMapDatabase.delete(TAG_TASK_MAP_NAME,
|
||||
TagToTaskMapping.TAG + " = " + tagId.idAsString(), null) < 0)
|
||||
return false;
|
||||
|
||||
return tagDatabase.delete(TAG_TABLE_NAME,
|
||||
KEY_ROWID + " = " + tagId.toString(), null) > 0;
|
||||
}
|
||||
|
||||
// --- single tag to task operations
|
||||
|
||||
/** Remove the given tag from the task */
|
||||
public boolean removeTag(TaskIdentifier taskId, TagIdentifier tagId)
|
||||
throws SQLException{
|
||||
return tagToTaskMapDatabase.delete(TAG_TASK_MAP_NAME,
|
||||
String.format("%s = ? AND %s = ?",
|
||||
TagToTaskMapping.TAG, TagToTaskMapping.TASK),
|
||||
new String[] { tagId.idAsString(), taskId.idAsString() }) > 0;
|
||||
}
|
||||
|
||||
/** Add the given tag to the task */
|
||||
public boolean addTag(TaskIdentifier taskId, TagIdentifier tagId)
|
||||
throws SQLException {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(TagToTaskMapping.TAG, tagId.getId());
|
||||
values.put(TagToTaskMapping.TASK, taskId.getId());
|
||||
return tagToTaskMapDatabase.insert(TAG_TASK_MAP_NAME, TagToTaskMapping.TAG,
|
||||
values) >= 0;
|
||||
}
|
||||
|
||||
// --- boilerplate
|
||||
|
||||
/**
|
||||
* Constructor - takes the context to allow the database to be
|
||||
* opened/created
|
||||
*/
|
||||
public TagController(Activity activity) {
|
||||
this.activity = activity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the notes database. If it cannot be opened, try to create a new
|
||||
* instance of the database. If it cannot be created, throw an exception to
|
||||
* signal the failure
|
||||
*
|
||||
* @return this (self reference, allowing this to be chained in an
|
||||
* initialization call)
|
||||
* @throws SQLException if the database could be neither opened or created
|
||||
*/
|
||||
public TagController open() throws SQLException {
|
||||
tagToTaskMapDatabase = new TagToTaskMappingDatabaseHelper(activity,
|
||||
TAG_TASK_MAP_NAME, TAG_TASK_MAP_NAME).getWritableDatabase();
|
||||
tagDatabase = new TagModelDatabaseHelper(activity,
|
||||
TAG_TABLE_NAME, TAG_TABLE_NAME).getWritableDatabase();
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Closes database resource */
|
||||
public void close() {
|
||||
tagDatabase.close();
|
||||
tagToTaskMapDatabase.close();
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package com.timsu.astrid.data.tag;
|
||||
|
||||
import com.timsu.astrid.data.Identifier;
|
||||
|
||||
|
||||
public class TagIdentifier extends Identifier {
|
||||
|
||||
public TagIdentifier(long id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
package com.timsu.astrid.data.tag;
|
||||
|
||||
import android.database.Cursor;
|
||||
|
||||
import com.timsu.astrid.data.AbstractController;
|
||||
|
||||
|
||||
|
||||
/** Fields that you would want to see in the TaskView activity */
|
||||
public class TagModelForView extends AbstractTagModel {
|
||||
|
||||
static String[] FIELD_LIST = new String[] {
|
||||
AbstractController.KEY_ROWID,
|
||||
NAME,
|
||||
};
|
||||
|
||||
// --- constructors
|
||||
|
||||
/** Constructor for creating a new model */
|
||||
TagModelForView(String name) {
|
||||
super();
|
||||
setName(name);
|
||||
}
|
||||
|
||||
/** Constructor for getting an existing model */
|
||||
TagModelForView(Cursor cursor) {
|
||||
super(cursor);
|
||||
getName();
|
||||
}
|
||||
|
||||
// --- getters and setters
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return super.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getName();
|
||||
}
|
||||
}
|
@ -0,0 +1,116 @@
|
||||
package com.timsu.astrid.data.tag;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.util.Log;
|
||||
|
||||
import com.timsu.astrid.data.AbstractController;
|
||||
import com.timsu.astrid.data.AbstractModel;
|
||||
import com.timsu.astrid.data.task.TaskIdentifier;
|
||||
|
||||
|
||||
/** A single tag on a task
|
||||
*
|
||||
* @author timsu
|
||||
*
|
||||
*/
|
||||
public class TagToTaskMapping extends AbstractModel {
|
||||
|
||||
/** Version number of this model */
|
||||
static final int VERSION = 2;
|
||||
|
||||
// field names
|
||||
|
||||
static final String TASK = "task";
|
||||
static final String TAG = "tag";
|
||||
|
||||
/** Default values container */
|
||||
private static final ContentValues defaultValues = new ContentValues();
|
||||
|
||||
@Override
|
||||
public ContentValues getDefaultValues() {
|
||||
return defaultValues;
|
||||
}
|
||||
|
||||
static String[] FIELD_LIST = new String[] {
|
||||
AbstractController.KEY_ROWID,
|
||||
TASK,
|
||||
TAG,
|
||||
};
|
||||
|
||||
// --- database helper
|
||||
|
||||
/** Database Helper manages creating new tables and updating old ones */
|
||||
static class TagToTaskMappingDatabaseHelper extends SQLiteOpenHelper {
|
||||
String tableName;
|
||||
|
||||
TagToTaskMappingDatabaseHelper(Context context, String databaseName, String tableName) {
|
||||
super(context, databaseName, null, VERSION);
|
||||
this.tableName = tableName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase db) {
|
||||
String sql = new StringBuilder().
|
||||
append("CREATE TABLE ").append(tableName).append(" (").
|
||||
append(AbstractController.KEY_ROWID).append(" integer primary key autoincrement, ").
|
||||
append(TASK).append(" integer not null,").
|
||||
append(TAG).append(" integer not null,").
|
||||
append("unique (").append(TASK).append(",").append(TAG).append(")").
|
||||
append(");").toString();
|
||||
db.execSQL(sql);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
Log.w(getClass().getSimpleName(), "Upgrading database from version " +
|
||||
oldVersion + " to " + newVersion + ".");
|
||||
|
||||
switch(oldVersion) {
|
||||
default:
|
||||
// we don't know how to handle it... do the unfortunate thing
|
||||
Log.e(getClass().getSimpleName(), "Unsupported migration, table dropped!");
|
||||
db.execSQL("DROP TABLE IF EXISTS " + tableName);
|
||||
onCreate(db);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// --- constructor pass-through
|
||||
|
||||
TagToTaskMapping(TaskIdentifier task, TagIdentifier tag) {
|
||||
super();
|
||||
setTask(task);
|
||||
setTag(tag);
|
||||
}
|
||||
|
||||
TagToTaskMapping(Cursor cursor) {
|
||||
super(cursor);
|
||||
}
|
||||
|
||||
// --- getters and setters: expose them as you see fit
|
||||
|
||||
public boolean isNew() {
|
||||
return getCursor() == null;
|
||||
}
|
||||
|
||||
public TaskIdentifier getTask() {
|
||||
return new TaskIdentifier(retrieveInteger(TASK));
|
||||
}
|
||||
|
||||
public TagIdentifier getTag() {
|
||||
return new TagIdentifier(retrieveInteger(TAG));
|
||||
}
|
||||
|
||||
private void setTask(TaskIdentifier task) {
|
||||
setValues.put(TASK, task.getId());
|
||||
}
|
||||
|
||||
private void setTag(TagIdentifier tag) {
|
||||
setValues.put(TAG, tag.getId());
|
||||
}
|
||||
}
|
@ -1,15 +1,11 @@
|
||||
package com.timsu.astrid.data.task;
|
||||
|
||||
import com.timsu.astrid.data.Identifier;
|
||||
|
||||
/** A little class that identifies a task. For saving state and passing around */
|
||||
public class TaskIdentifier {
|
||||
private long id;
|
||||
|
||||
public TaskIdentifier(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
public class TaskIdentifier extends Identifier {
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
public TaskIdentifier(long id) {
|
||||
super(id);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* ASTRID: Android's Simple Task Recording Dame
|
||||
*
|
||||
* 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 2 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
package com.timsu.astrid.widget;
|
||||
|
||||
import java.text.Format;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.DatePickerDialog;
|
||||
import android.app.TimePickerDialog;
|
||||
import android.app.DatePickerDialog.OnDateSetListener;
|
||||
import android.app.TimePickerDialog.OnTimeSetListener;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.DatePicker;
|
||||
import android.widget.TimePicker;
|
||||
import android.widget.CompoundButton.OnCheckedChangeListener;
|
||||
|
||||
public class DateControlSet implements OnTimeSetListener,
|
||||
OnDateSetListener, View.OnClickListener {
|
||||
|
||||
private static final Format dateFormatter = new SimpleDateFormat("EEE, MMM d, yyyy");
|
||||
private static final Format timeFormatter = new SimpleDateFormat("h:mm a");
|
||||
|
||||
private final Activity activity;
|
||||
private CheckBox activatedCheckBox;
|
||||
private Button dateButton;
|
||||
private Button timeButton;
|
||||
private Date date;
|
||||
|
||||
public DateControlSet(Activity activity, int checkBoxId, int dateButtonId, int timeButtonId) {
|
||||
this.activity = activity;
|
||||
activatedCheckBox = (CheckBox)activity.findViewById(checkBoxId);
|
||||
dateButton = (Button)activity.findViewById(dateButtonId);
|
||||
timeButton = (Button)activity.findViewById(timeButtonId);
|
||||
|
||||
activatedCheckBox.setOnCheckedChangeListener(
|
||||
new OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView,
|
||||
boolean isChecked) {
|
||||
dateButton.setEnabled(isChecked);
|
||||
timeButton.setEnabled(isChecked);
|
||||
}
|
||||
});
|
||||
dateButton.setOnClickListener(this);
|
||||
timeButton.setOnClickListener(this);
|
||||
}
|
||||
|
||||
public Date getDate() {
|
||||
if(!activatedCheckBox.isChecked())
|
||||
return null;
|
||||
return date;
|
||||
}
|
||||
|
||||
/** Initialize the components for the given date field */
|
||||
public void setDate(Date newDate) {
|
||||
this.date = newDate;
|
||||
if(newDate == null) {
|
||||
date = new Date();
|
||||
date.setMinutes(0);
|
||||
}
|
||||
|
||||
activatedCheckBox.setChecked(newDate != null);
|
||||
dateButton.setEnabled(newDate != null);
|
||||
timeButton.setEnabled(newDate != null);
|
||||
|
||||
updateDate();
|
||||
updateTime();
|
||||
}
|
||||
|
||||
public void onDateSet(DatePicker view, int year, int month, int monthDay) {
|
||||
date.setYear(year - 1900);
|
||||
date.setMonth(month);
|
||||
date.setDate(monthDay);
|
||||
updateDate();
|
||||
}
|
||||
|
||||
public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
|
||||
date.setHours(hourOfDay);
|
||||
date.setMinutes(minute);
|
||||
updateTime();
|
||||
}
|
||||
|
||||
public void updateDate() {
|
||||
dateButton.setText(dateFormatter.format(date));
|
||||
|
||||
}
|
||||
|
||||
public void updateTime() {
|
||||
timeButton.setText(timeFormatter.format(date));
|
||||
}
|
||||
|
||||
public void onClick(View v) {
|
||||
if(v == timeButton)
|
||||
new TimePickerDialog(activity, this, date.getHours(),
|
||||
date.getMinutes(), false).show();
|
||||
else
|
||||
new DatePickerDialog(activity, this, 1900 +
|
||||
date.getYear(), date.getMonth(), date.getDate()).show();
|
||||
}
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* ASTRID: Android's Simple Task Recording Dame
|
||||
*
|
||||
* 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 2 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
package com.timsu.astrid.widget;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.res.Resources;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
|
||||
import com.timsu.astrid.R;
|
||||
import com.timsu.astrid.utilities.DateUtilities;
|
||||
import com.timsu.astrid.widget.NumberPickerDialog.OnNumberPickedListener;
|
||||
|
||||
public class TimeDurationControlSet implements OnNumberPickedListener,
|
||||
View.OnClickListener {
|
||||
|
||||
private final Activity activity;
|
||||
private Button timeButton;
|
||||
private int timeDuration;
|
||||
private final NumberPickerDialog dialog;
|
||||
|
||||
public TimeDurationControlSet(Activity activity, int timeButtonId) {
|
||||
this.activity = activity;
|
||||
timeButton = (Button)activity.findViewById(timeButtonId);
|
||||
timeButton.setOnClickListener(this);
|
||||
dialog = new NumberPickerDialog(activity, this,
|
||||
activity.getResources().getString(R.string.minutes_dialog),
|
||||
0, 5, 0, 999);
|
||||
}
|
||||
|
||||
public int getTimeDurationInSeconds() {
|
||||
return timeDuration;
|
||||
}
|
||||
|
||||
public void setTimeElapsed(Integer timeDurationInSeconds) {
|
||||
if(timeDurationInSeconds == null)
|
||||
timeDurationInSeconds = 0;
|
||||
|
||||
timeDuration = timeDurationInSeconds;
|
||||
|
||||
Resources r = activity.getResources();
|
||||
if(timeDurationInSeconds == 0) {
|
||||
timeButton.setText(r.getString(R.string.blank_button_title));
|
||||
return;
|
||||
}
|
||||
|
||||
timeButton.setText(DateUtilities.getDurationString(r,
|
||||
timeDurationInSeconds, 2));
|
||||
dialog.setInitialValue(timeDuration/60);
|
||||
}
|
||||
|
||||
@Override
|
||||
/** Called when NumberPicker activity is completed */
|
||||
public void onNumberPicked(NumberPicker view, int value) {
|
||||
setTimeElapsed(value * 60);
|
||||
}
|
||||
|
||||
/** Called when time button is clicked */
|
||||
public void onClick(View v) {
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
|
||||
}
|
Loading…
Reference in New Issue