Merge remote branch 'upstream/master' into dev

pull/14/head
Arne Jans 16 years ago
commit afe876d4fd

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.timsu.astrid"
android:versionName="3.1.0" android:versionCode="146">
android:versionName="3.2.0" android:versionCode="147">
<!-- widgets, alarms, and services will break if Astrid is installed on SD card -->
<!-- android:installLocation="internalOnly"> -->

@ -246,8 +246,9 @@ public class Notifications extends BroadcastReceiver {
}
}
// quiet hours + periodic = no vibrate
if(quietHours && (type == ReminderService.TYPE_RANDOM)) {
// quiet hours && ! due date or snooze = no vibrate
if(quietHours && !(type == ReminderService.TYPE_DUE ||
type == ReminderService.TYPE_SNOOZE)) {
notification.vibrate = null;
} else {
if (Preferences.getBoolean(R.string.p_rmd_vibrate, true)
@ -264,6 +265,23 @@ public class Notifications extends BroadcastReceiver {
notificationManager.notify(notificationId, notification);
}
/**
* Schedules alarms for a single task
*
* @param shouldPerformPropertyCheck
* whether to check if task has requisite properties
*/
public static void cancelNotifications(long taskId) {
if(notificationManager == null)
synchronized(Notifications.class) {
if(notificationManager == null)
notificationManager = new AndroidNotificationManager(
ContextManager.getContext());
}
notificationManager.cancel((int)taskId);
}
// --- notification manager
public static void setNotificationManager(

@ -2,9 +2,9 @@ package com.todoroo.astrid.tags;
import java.util.LinkedHashSet;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.data.Property.CountProperty;
import com.todoroo.andlib.data.Property.StringProperty;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.sql.Criterion;
@ -71,6 +71,11 @@ public final class TagService {
public String tag;
int count;
public Tag(String tag, int count) {
this.tag = tag;
this.count = count;
}
@Override
public String toString() {
return tag;
@ -114,9 +119,7 @@ public final class TagService {
Tag[] array = new Tag[cursor.getCount()];
for (int i = 0; i < array.length; i++) {
cursor.moveToNext();
array[i] = new Tag();
array[i].tag = cursor.get(TAG);
array[i].count = cursor.get(COUNT);
array[i] = new Tag(cursor.get(TAG), cursor.get(COUNT));
}
return array;
} finally {

@ -1,16 +1,25 @@
package com.todoroo.astrid.tags;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
import android.app.Activity;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.inputmethod.EditorInfo;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
import com.timsu.astrid.R;
import com.todoroo.andlib.data.AbstractModel;
@ -32,6 +41,7 @@ public final class TagsControlSet implements TaskEditControlSet {
// --- instance variables
private final Spinner tagSpinner;
private final TagService tagService = TagService.getInstance();
private final Tag[] allTags;
private String[] loadedTags;
@ -42,6 +52,36 @@ public final class TagsControlSet implements TaskEditControlSet {
allTags = tagService.getGroupedTags(TagService.GROUPED_TAGS_BY_SIZE, Criterion.all);
this.activity = activity;
this.tagsContainer = (LinearLayout) activity.findViewById(tagsContainer);
this.tagSpinner = (Spinner) activity.findViewById(R.id.tags_dropdown);
if(allTags.length == 0) {
tagSpinner.setVisibility(View.GONE);
} else {
ArrayList<Tag> dropDownList = new ArrayList<Tag>(Arrays.asList(allTags));
dropDownList.add(0, new Tag(activity.getString(R.string.TEA_tag_dropdown), 0));
ArrayAdapter<Tag> tagAdapter = new ArrayAdapter<Tag>(activity,
android.R.layout.simple_spinner_item,
dropDownList);
tagAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
tagSpinner.setAdapter(tagAdapter);
tagSpinner.setSelection(0);
tagSpinner.setOnItemSelectedListener(new OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> arg0, View arg1,
int position, long arg3) {
if(position == 0 || position > allTags.length)
return;
addTag(allTags[position - 1].tag, true);
tagSpinner.setSelection(0);
}
@Override
public void onNothingSelected(AdapterView<?> arg0) {
// nothing!
}
});
}
}
@Override
@ -55,16 +95,14 @@ public final class TagsControlSet implements TaskEditControlSet {
for(int i = 0; i < loadedTags.length; i++) {
cursor.moveToNext();
String tag = cursor.get(TagService.TAG);
addTag(tag);
addTag(tag, true);
loadedTags[i] = tag;
}
} finally {
cursor.close();
}
}
if(tagsContainer.getChildCount() == 0)
addTag(""); //$NON-NLS-1$
addTag("", false); //$NON-NLS-1$
}
@Override
@ -96,10 +134,25 @@ public final class TagsControlSet implements TaskEditControlSet {
}
/** Adds a tag to the tag field */
boolean addTag(String tagName) {
boolean addTag(String tagName, boolean reuse) {
LayoutInflater inflater = activity.getLayoutInflater();
final View tagItem = inflater.inflate(R.layout.tag_edit_row, null);
tagsContainer.addView(tagItem);
// check if already exists
TextView lastText = null;
for(int i = 0; i < tagsContainer.getChildCount(); i++) {
View view = tagsContainer.getChildAt(i);
lastText = (TextView) view.findViewById(R.id.text1);
if(lastText.getText().equals(tagName))
return false;
}
final View tagItem;
if(reuse && lastText != null && lastText.getText().length() == 0) {
tagItem = (View) lastText.getParent();
} else {
tagItem = inflater.inflate(R.layout.tag_edit_row, null);
tagsContainer.addView(tagItem);
}
final AutoCompleteTextView textView = (AutoCompleteTextView)tagItem.
findViewById(R.id.text1);
@ -109,36 +162,46 @@ public final class TagsControlSet implements TaskEditControlSet {
android.R.layout.simple_dropdown_item_1line, allTags);
textView.setAdapter(tagsAdapter);
textView.setOnClickListener(new OnClickListener() {
textView.addTextChangedListener(new TextWatcher() {
@Override
public void onClick(View arg0) {
View lastItem = tagsContainer.getChildAt(tagsContainer.getChildCount()-1);
TextView lastText = (TextView) lastItem.findViewById(R.id.text1);
if(lastText.getText().length() != 0) {
addTag(""); //$NON-NLS-1$
}
public void afterTextChanged(Editable s) {
//
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
//
}
@Override
public void onTextChanged(CharSequence s, int start, int before,
int count) {
if(count > 0 && tagsContainer.getChildAt(tagsContainer.getChildCount()-1) ==
tagItem)
addTag("", false); //$NON-NLS-1$
}
});
/*textView.setOnEditorActionListener(new OnEditorActionListener() {
textView.setOnEditorActionListener(new OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView arg0, int actionId, KeyEvent arg2) {
if(actionId != EditorInfo.IME_NULL)
return false;
View lastItem = tagsContainer.getChildAt(tagsContainer.getChildCount()-1);
TextView lastText = (TextView) lastItem.findViewById(R.id.text1);
if(lastText.getText().length() != 0) {
addTag(""); //$NON-NLS-1$
if(getLastTextView().getText().length() != 0) {
addTag("", false); //$NON-NLS-1$
}
return true;
}
});*/
});
ImageButton reminderRemoveButton;
reminderRemoveButton = (ImageButton)tagItem.findViewById(R.id.button1);
reminderRemoveButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
if(tagsContainer.getChildCount() > 0)
TextView lastView = getLastTextView();
if(lastView == textView && textView.getText().length() == 0)
return;
if(tagsContainer.getChildCount() > 1)
tagsContainer.removeView(tagItem);
else
textView.setText(""); //$NON-NLS-1$
@ -147,4 +210,16 @@ public final class TagsControlSet implements TaskEditControlSet {
return true;
}
/**
* Get tags container last text view. might be null
* @return
*/
private TextView getLastTextView() {
if(tagsContainer.getChildCount() == 0)
return null;
View lastItem = tagsContainer.getChildAt(tagsContainer.getChildCount()-1);
TextView lastText = (TextView) lastItem.findViewById(R.id.text1);
return lastText;
}
}

@ -65,6 +65,10 @@
<LinearLayout
android:id="@+id/tags_container"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<Spinner
android:id="@+id/tags_dropdown"
android:paddingBottom="5dip"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />

@ -11,6 +11,9 @@
<!-- Tags hint -->
<string name="TEA_tag_hint">Tag Name</string>
<!-- Tags dropdown -->
<string name="TEA_tag_dropdown">Select a tag</string>
<!-- ========================================================== Filters == -->

@ -27,15 +27,15 @@ import java.util.List;
import android.app.AlertDialog;
import android.app.DatePickerDialog;
import android.app.TabActivity;
import android.app.DatePickerDialog.OnDateSetListener;
import android.app.TabActivity;
import android.content.BroadcastReceiver;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.DialogInterface.OnCancelListener;
import android.content.res.Resources;
import android.os.Bundle;
import android.text.format.DateUtils;
@ -45,6 +45,7 @@ import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
@ -59,7 +60,6 @@ import android.widget.TabHost;
import android.widget.TimePicker;
import android.widget.Toast;
import android.widget.ToggleButton;
import android.widget.AdapterView.OnItemSelectedListener;
import com.flurry.android.FlurryAgent;
import com.timsu.astrid.R;
@ -155,6 +155,9 @@ public final class TaskEditActivity extends TabActivity {
// --- other instance variables
/** true if editing started with a new task */
boolean isNewTask = false;
/** task model */
private Task model = null;
@ -251,25 +254,10 @@ public final class TaskEditActivity extends TabActivity {
});
}
// read data
populateFields();
// request add-on controls
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_REQUEST_EDIT_CONTROLS);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, model.getId());
sendOrderedBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
// set up listeners
setUpListeners();
}
/**
* @return true if task is newly created
*/
private boolean isNewTask() {
return model == null ? true : model.getValue(Task.TITLE).length() == 0;
}
/** Set up button listeners */
private void setUpListeners() {
final View.OnClickListener mSaveListener = new View.OnClickListener() {
@ -324,9 +312,13 @@ public final class TaskEditActivity extends TabActivity {
values = AndroidUtilities.contentValuesFromSerializedString(valuesAsString);
model = TaskListActivity.createWithValues(values, null, taskService, metadataService);
}
if(model.getValue(Task.TITLE).length() == 0)
if(model.getValue(Task.TITLE).length() == 0) {
FlurryAgent.onEvent("create-task");
FlurryAgent.onEvent("edit-task");
isNewTask = true;
} else {
FlurryAgent.onEvent("edit-task");
}
if(model == null) {
exceptionService.reportError("task-edit-no-task",
@ -341,7 +333,7 @@ public final class TaskEditActivity extends TabActivity {
Resources r = getResources();
loadItem(getIntent());
if(isNewTask())
if(isNewTask)
setTitle(R.string.TEA_view_titleNew);
else
setTitle(r.getString(R.string.TEA_view_title, model.getValue(Task.TITLE)));
@ -352,14 +344,6 @@ public final class TaskEditActivity extends TabActivity {
/** Save task model from values in UI components */
private void save() {
// abandon editing in this case
if(title.getText().length() == 0) {
if(isNewTask())
taskService.delete(model);
discardButtonClick();
return;
}
for(TaskEditControlSet controlSet : controls)
controlSet.writeToModel(model);
@ -367,6 +351,18 @@ public final class TaskEditActivity extends TabActivity {
showSaveToast();
}
@Override
public void finish() {
super.finish();
// abandon editing in this case
if(title.getText().length() == 0 && isNewTask) {
taskService.delete(model);
showCancelToast();
setResult(RESULT_CANCELED);
}
}
/* ======================================================================
* ================================================ edit control handling
* ====================================================================== */
@ -416,7 +412,7 @@ public final class TaskEditActivity extends TabActivity {
*/
private void showSaveToast() {
// if we have no title, or nothing's changed, don't show toast
if(isNewTask())
if(isNewTask)
return;
int stringResource;
@ -445,7 +441,7 @@ public final class TaskEditActivity extends TabActivity {
// abandon editing in this case
if(title.getText().length() == 0) {
if(isNewTask())
if(isNewTask)
taskService.delete(model);
}
@ -540,6 +536,7 @@ public final class TaskEditActivity extends TabActivity {
super.onResume();
registerReceiver(controlReceiver,
new IntentFilter(AstridApiConstants.BROADCAST_SEND_EDIT_CONTROLS));
populateFields();
}
@Override
@ -552,14 +549,6 @@ public final class TaskEditActivity extends TabActivity {
protected void onStop() {
super.onStop();
FlurryAgent.onEndSession(this);
// don't save if user accidentally created a new task
if(title.getText().length() == 0) {
if(model.isSaved())
taskService.delete(model);
showCancelToast();
return;
}
}
/* ======================================================================

@ -4,6 +4,7 @@
package com.todoroo.astrid.adapter;
import java.util.ArrayList;
import java.util.concurrent.LinkedBlockingQueue;
import android.app.Activity;
import android.content.BroadcastReceiver;
@ -23,11 +24,15 @@ import android.widget.ImageView;
import android.widget.TextView;
import com.timsu.astrid.R;
import com.todoroo.andlib.data.Property.CountProperty;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.api.FilterCategory;
import com.todoroo.astrid.api.FilterListHeader;
import com.todoroo.astrid.api.FilterListItem;
import com.todoroo.astrid.service.TaskService;
import com.todoroo.astrid.utility.Preferences;
public class FilterAdapter extends BaseExpandableListAdapter {
@ -40,18 +45,45 @@ public class FilterAdapter extends BaseExpandableListAdapter {
// --- instance variables
@Autowired
private TaskService taskService;
/** parent activity */
protected final Activity activity;
/** owner listview */
protected final ExpandableListView listView;
/** list of filters */
private final ArrayList<FilterListItem> items;
/** display metrics for scaling icons */
private final DisplayMetrics metrics = new DisplayMetrics();
/** receiver for new filters */
private final FilterReceiver filterReceiver = new FilterReceiver();
/** row layout to inflate */
private final int layout;
/** layout inflater */
private final LayoutInflater inflater;
/** whether to skip Filters that launch intents instead of being real filters */
private final boolean skipIntentFilters;
/** queue for loading list sizes */
private final LinkedBlockingQueue<Filter> filterQueue = new LinkedBlockingQueue<Filter>();
/** thread for loading list sizes */
private Thread filterSizeLoadingThread = null;
public FilterAdapter(Activity activity, ExpandableListView listView,
int rowLayout, boolean skipIntentFilters) {
super();
DependencyInjectionService.getInstance().inject(this);
this.activity = activity;
this.items = new ArrayList<FilterListItem>();
this.listView = listView;
@ -64,6 +96,35 @@ public class FilterAdapter extends BaseExpandableListAdapter {
activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
listView.setGroupIndicator(
activity.getResources().getDrawable(R.drawable.expander_group));
startFilterSizeLoadingThread();
}
private void startFilterSizeLoadingThread() {
filterSizeLoadingThread = new Thread() {
@Override
public void run() {
CountProperty cp = new CountProperty();
while(true) {
try {
Filter filter = filterQueue.take();
int size = taskService.countTasks(filter, cp);
filter.listingTitle = filter.listingTitle + (" (" + //$NON-NLS-1$
size + ")"); //$NON-NLS-1$
activity.runOnUiThread(new Runnable() {
public void run() {
notifyDataSetInvalidated();
}
});
} catch (InterruptedException e) {
break;
} catch (Exception e) {
Log.e("astrid-filter-adapter", "Error loading filter size", e); //$NON-NLS-1$ //$NON-NLS-2$
}
}
}
};
filterSizeLoadingThread.start();
}
public boolean hasStableIds() {
@ -72,6 +133,14 @@ public class FilterAdapter extends BaseExpandableListAdapter {
public void add(FilterListItem item) {
items.add(item);
// load sizes
if(item instanceof Filter) {
filterQueue.offer((Filter) item);
} else if(item instanceof FilterCategory) {
for(Filter filter : ((FilterCategory)item).children)
filterQueue.offer(filter);
}
}
public void clear() {
@ -300,6 +369,8 @@ public class FilterAdapter extends BaseExpandableListAdapter {
*/
public void unregisterRecevier() {
activity.unregisterReceiver(filterReceiver);
if(filterSizeLoadingThread != null)
filterSizeLoadingThread.interrupt();
}
/**

@ -21,6 +21,7 @@ import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria;
import com.todoroo.astrid.model.Task;
import com.todoroo.astrid.provider.Astrid2TaskProvider;
import com.todoroo.astrid.reminders.Notifications;
import com.todoroo.astrid.reminders.ReminderService;
import com.todoroo.astrid.utility.Preferences;
import com.todoroo.astrid.widget.TasksWidget;
@ -223,7 +224,7 @@ public class TaskDao extends GenericDao<Task> {
* @param skipHooks whether this save occurs as part of a sync
*/
private void afterSave(Task task, ContentValues values) {
if(values.containsKey(Task.COMPLETION_DATE.name) && task.isCompleted())
if(values != null && values.containsKey(Task.COMPLETION_DATE.name) && task.isCompleted())
afterComplete(task, values);
else
ReminderService.getInstance().scheduleAlarm(task);
@ -245,6 +246,8 @@ public class TaskDao extends GenericDao<Task> {
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_EVENT_TASK_COMPLETED);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, task.getId());
context.sendOrderedBroadcast(broadcastIntent, null);
Notifications.cancelNotifications(task.getId());
}
}

@ -1,6 +1,7 @@
package com.todoroo.astrid.service;
import com.todoroo.andlib.data.Property;
import com.todoroo.andlib.data.Property.CountProperty;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
@ -9,10 +10,11 @@ import com.todoroo.andlib.sql.Functions;
import com.todoroo.andlib.sql.Order;
import com.todoroo.andlib.sql.Query;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.api.PermaSql;
import com.todoroo.astrid.dao.MetadataDao;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.dao.TaskDao.TaskCriteria;
import com.todoroo.astrid.model.Metadata;
import com.todoroo.astrid.model.Task;
@ -231,6 +233,16 @@ public class TaskService {
}
}
public int countTasks(Filter filter, CountProperty countProperty) {
TodorooCursor<Task> cursor = query(Query.select(countProperty).withQueryTemplate(filter.sqlQuery));
try {
cursor.moveToFirst();
return cursor.getInt(0);
} finally {
cursor.close();
}
}
}

@ -98,7 +98,9 @@ public class TasksWidget extends AppWidgetProvider {
TasksWidget.class);
AppWidgetManager manager = AppWidgetManager.getInstance(this);
int extrasId = intent.getIntExtra(EXTRA_WIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
int extrasId = AppWidgetManager.INVALID_APPWIDGET_ID;
if(intent != null)
extrasId = intent.getIntExtra(EXTRA_WIDGET_ID, extrasId);
if(extrasId == AppWidgetManager.INVALID_APPWIDGET_ID) {
for(int id : manager.getAppWidgetIds(thisWidget)) {
RemoteViews updateViews = buildUpdate(this, id);

Loading…
Cancel
Save