mirror of https://github.com/tasks/tasks
Merge branch 'master' into dev
Conflicts: astrid/src/com/todoroo/astrid/adapter/TaskAdapter.javapull/14/head
commit
44e0f00b38
@ -0,0 +1,42 @@
|
|||||||
|
package com.todoroo.andlib.widget;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.gesture.Gesture;
|
||||||
|
import android.gesture.GestureLibraries;
|
||||||
|
import android.gesture.GestureLibrary;
|
||||||
|
import android.gesture.GestureOverlayView;
|
||||||
|
import android.gesture.Prediction;
|
||||||
|
import android.gesture.GestureOverlayView.OnGesturePerformedListener;
|
||||||
|
|
||||||
|
import com.todoroo.andlib.widget.GestureService.GestureInterface;
|
||||||
|
|
||||||
|
public class Api4GestureDetector implements OnGesturePerformedListener {
|
||||||
|
private final GestureLibrary mLibrary;
|
||||||
|
private final GestureInterface listener;
|
||||||
|
|
||||||
|
public Api4GestureDetector(Activity activity, int view, int gestureLibrary, GestureInterface listener) {
|
||||||
|
this.listener = listener;
|
||||||
|
mLibrary = GestureLibraries.fromRawResource(activity, gestureLibrary);
|
||||||
|
|
||||||
|
if(mLibrary.load()) {
|
||||||
|
GestureOverlayView gestures = (GestureOverlayView) activity.findViewById(view);
|
||||||
|
gestures.addOnGesturePerformedListener(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onGesturePerformed(GestureOverlayView overlay, Gesture gesture) {
|
||||||
|
ArrayList<Prediction> predictions = mLibrary.recognize(gesture);
|
||||||
|
|
||||||
|
// We want at least one prediction
|
||||||
|
if (predictions.size() > 0) {
|
||||||
|
Prediction prediction = predictions.get(0);
|
||||||
|
// We want at least some confidence in the result
|
||||||
|
if (prediction.score > 1.0) {
|
||||||
|
listener.gesturePerformed(prediction.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
package com.todoroo.andlib.widget;
|
||||||
|
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
|
||||||
|
import com.todoroo.andlib.utility.AndroidUtilities;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All API versions-friendly gesture detector. On SDK < 4, nothing happens
|
||||||
|
* @author Tim Su <tim@todoroo.com>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class GestureService {
|
||||||
|
|
||||||
|
public interface GestureInterface {
|
||||||
|
public void gesturePerformed(String gesture);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register gesture detector. If android SDK version is not correct,
|
||||||
|
* a {@link VerifyError} will be throw. Catch this explicitly.
|
||||||
|
*
|
||||||
|
* @param activity
|
||||||
|
* @param view
|
||||||
|
* @param gestureLibrary
|
||||||
|
* @param listener
|
||||||
|
* @throws VerifyError
|
||||||
|
*/
|
||||||
|
public static void registerGestureDetector(Activity activity, int view,
|
||||||
|
int gestureLibrary, GestureInterface listener) throws VerifyError {
|
||||||
|
if(AndroidUtilities.getSdkVersion() > 3)
|
||||||
|
new Api4GestureDetector(activity, view, gestureLibrary, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,111 +1,34 @@
|
|||||||
/**
|
|
||||||
* See the file "LICENSE" for the full license governing this code.
|
|
||||||
*/
|
|
||||||
package com.todoroo.astrid.alarms;
|
package com.todoroo.astrid.alarms;
|
||||||
|
|
||||||
|
|
||||||
import android.content.ContentValues;
|
|
||||||
|
|
||||||
import com.todoroo.andlib.data.AbstractModel;
|
|
||||||
import com.todoroo.andlib.data.Property;
|
|
||||||
import com.todoroo.andlib.data.Property.IntegerProperty;
|
import com.todoroo.andlib.data.Property.IntegerProperty;
|
||||||
import com.todoroo.andlib.data.Property.LongProperty;
|
import com.todoroo.andlib.data.Property.LongProperty;
|
||||||
import com.todoroo.andlib.data.Property.StringProperty;
|
import com.todoroo.astrid.model.Metadata;
|
||||||
import com.todoroo.andlib.data.Table;
|
|
||||||
import com.todoroo.andlib.data.TodorooCursor;
|
|
||||||
import com.todoroo.astrid.model.Task;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Data Model which represents an alarm
|
* Metadata entry for a task alarm
|
||||||
*
|
*
|
||||||
* @author Tim Su <tim@todoroo.com>
|
* @author Tim Su <tim@todoroo.com>
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("nls")
|
public class Alarm {
|
||||||
public class Alarm extends AbstractModel {
|
|
||||||
|
|
||||||
// --- table
|
|
||||||
|
|
||||||
public static final Table TABLE = new Table("alarm", Alarm.class);
|
|
||||||
|
|
||||||
// --- properties
|
|
||||||
|
|
||||||
/** ID */
|
|
||||||
public static final LongProperty ID = new LongProperty(
|
|
||||||
TABLE, ID_PROPERTY_NAME);
|
|
||||||
|
|
||||||
/** Associated Task */
|
/** metadata key */
|
||||||
public static final LongProperty TASK = new LongProperty(
|
public static final String METADATA_KEY = "alarm"; //$NON-NLS-1$
|
||||||
TABLE, "task");
|
|
||||||
|
|
||||||
/** Alarm Time */
|
/** time of alarm */
|
||||||
public static final LongProperty TIME = new LongProperty(
|
public static final LongProperty TIME = new LongProperty(Metadata.TABLE,
|
||||||
TABLE, "time");
|
Metadata.VALUE1.name);
|
||||||
|
|
||||||
/** Alarm Type (see constants) */
|
/** alarm type */
|
||||||
public static final IntegerProperty TYPE = new IntegerProperty(
|
public static final IntegerProperty TYPE = new IntegerProperty(Metadata.TABLE,
|
||||||
TABLE, "type");
|
Metadata.VALUE2.name);
|
||||||
|
|
||||||
/** Alarm Ringtone */
|
|
||||||
public static final StringProperty RINGTONE = new StringProperty(
|
|
||||||
TABLE, "ringtone");
|
|
||||||
|
|
||||||
/** List of all properties for this model */
|
|
||||||
public static final Property<?>[] PROPERTIES = generateProperties(Alarm.class);
|
|
||||||
|
|
||||||
// --- constants
|
// --- constants
|
||||||
|
|
||||||
/** this alarm was already triggered */
|
|
||||||
public static final int TYPE_TRIGGERED = 0;
|
|
||||||
|
|
||||||
/** this alarm is single-shot */
|
/** this alarm is single-shot */
|
||||||
public static final int TYPE_SINGLE = 1;
|
public static final int TYPE_SINGLE = 1;
|
||||||
|
|
||||||
/** this alarm repeats itself until turned off */
|
/** this alarm repeats itself until turned off */
|
||||||
public static final int TYPE_REPEATING = 2;
|
public static final int TYPE_REPEATING = 2;
|
||||||
|
|
||||||
// --- defaults
|
|
||||||
|
|
||||||
/** Default values container */
|
|
||||||
private static final ContentValues defaultValues = new ContentValues();
|
|
||||||
|
|
||||||
static {
|
|
||||||
defaultValues.put(TYPE.name, TYPE_SINGLE);
|
|
||||||
defaultValues.put(RINGTONE.name, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ContentValues getDefaultValues() {
|
|
||||||
return defaultValues;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- data access boilerplate
|
|
||||||
|
|
||||||
public Alarm() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Alarm(TodorooCursor<Alarm> cursor) {
|
|
||||||
this();
|
|
||||||
readPropertiesFromCursor(cursor);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void readFromCursor(TodorooCursor<Alarm> cursor) {
|
|
||||||
super.readPropertiesFromCursor(cursor);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getId() {
|
|
||||||
return getIdHelper(ID);
|
|
||||||
};
|
|
||||||
|
|
||||||
// --- parcelable helpers
|
|
||||||
|
|
||||||
private static final Creator<Task> CREATOR = new ModelCreator<Task>(Task.class);
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Creator<? extends AbstractModel> getCreator() {
|
|
||||||
return CREATOR;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,92 @@
|
|||||||
|
package com.todoroo.astrid.alarms;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.ImageButton;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
|
import com.timsu.astrid.R;
|
||||||
|
import com.todoroo.andlib.data.TodorooCursor;
|
||||||
|
import com.todoroo.andlib.widget.DateControlSet;
|
||||||
|
import com.todoroo.astrid.activity.TaskEditActivity.TaskEditControlSet;
|
||||||
|
import com.todoroo.astrid.model.Metadata;
|
||||||
|
import com.todoroo.astrid.model.Task;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Control set to manage adding and removing tags
|
||||||
|
*
|
||||||
|
* @author Tim Su <tim@todoroo.com>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public final class AlarmControlSet implements TaskEditControlSet {
|
||||||
|
|
||||||
|
// --- instance variables
|
||||||
|
|
||||||
|
private final LinearLayout alertsContainer;
|
||||||
|
private final Activity activity;
|
||||||
|
|
||||||
|
public AlarmControlSet(Activity activity, ViewGroup parent) {
|
||||||
|
View v = LayoutInflater.from(activity).inflate(R.layout.alarm_control, parent, true);
|
||||||
|
|
||||||
|
this.activity = activity;
|
||||||
|
this.alertsContainer = (LinearLayout) v.findViewById(R.id.alert_container);
|
||||||
|
v.findViewById(R.id.alarms_add).setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View arg0) {
|
||||||
|
addAlarm(new Date());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void readFromTask(Task task) {
|
||||||
|
alertsContainer.removeAllViews();
|
||||||
|
TodorooCursor<Metadata> cursor = AlarmService.getInstance().getAlarms(task.getId());
|
||||||
|
try {
|
||||||
|
for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext())
|
||||||
|
addAlarm(new Date(cursor.get(Alarm.TIME)));
|
||||||
|
} finally {
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeToModel(Task task) {
|
||||||
|
LinkedHashSet<Long> alarms = new LinkedHashSet<Long>();
|
||||||
|
for(int i = 0; i < alertsContainer.getChildCount(); i++) {
|
||||||
|
DateControlSet set = (DateControlSet) alertsContainer.getChildAt(i).getTag();
|
||||||
|
if(set == null)
|
||||||
|
continue;
|
||||||
|
Date date = set.getDate();
|
||||||
|
if(date != null)
|
||||||
|
alarms.add(set.getDate().getTime());
|
||||||
|
}
|
||||||
|
AlarmService.getInstance().synchronizeAlarms(task.getId(), alarms);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean addAlarm(Date alert) {
|
||||||
|
final View alertItem = LayoutInflater.from(activity).inflate(R.layout.alarm_edit_row, null);
|
||||||
|
alertsContainer.addView(alertItem);
|
||||||
|
|
||||||
|
DateControlSet dcs = new DateControlSet(activity, (Button)alertItem.findViewById(R.id.date),
|
||||||
|
(Button)alertItem.findViewById(R.id.time));
|
||||||
|
dcs.setDate(alert);
|
||||||
|
alertItem.setTag(dcs);
|
||||||
|
|
||||||
|
ImageButton reminderRemoveButton;
|
||||||
|
reminderRemoveButton = (ImageButton)alertItem.findViewById(R.id.button1);
|
||||||
|
reminderRemoveButton.setOnClickListener(new View.OnClickListener() {
|
||||||
|
public void onClick(View v) {
|
||||||
|
alertsContainer.removeView(alertItem);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,79 @@
|
|||||||
|
/**
|
||||||
|
* See the file "LICENSE" for the full license governing this code.
|
||||||
|
*/
|
||||||
|
package com.todoroo.astrid.alarms;
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.text.format.DateUtils;
|
||||||
|
|
||||||
|
import com.timsu.astrid.R;
|
||||||
|
import com.todoroo.andlib.data.TodorooCursor;
|
||||||
|
import com.todoroo.andlib.utility.DateUtilities;
|
||||||
|
import com.todoroo.astrid.api.AstridApiConstants;
|
||||||
|
import com.todoroo.astrid.api.DetailExposer;
|
||||||
|
import com.todoroo.astrid.model.Metadata;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exposes Task Detail for tags, i.e. "Tags: frogs, animals"
|
||||||
|
*
|
||||||
|
* @author Tim Su <tim@todoroo.com>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class AlarmDetailExposer extends BroadcastReceiver implements DetailExposer {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
// get tags associated with this task
|
||||||
|
long taskId = intent.getLongExtra(AstridApiConstants.EXTRAS_TASK_ID, -1);
|
||||||
|
if(taskId == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
boolean extended = intent.getBooleanExtra(AstridApiConstants.EXTRAS_EXTENDED, false);
|
||||||
|
String taskDetail = getTaskDetails(context, taskId, extended);
|
||||||
|
if(taskDetail == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// transmit
|
||||||
|
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_DETAILS);
|
||||||
|
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_ADDON, AlarmService.IDENTIFIER);
|
||||||
|
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_RESPONSE, taskDetail);
|
||||||
|
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_EXTENDED, extended);
|
||||||
|
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, taskId);
|
||||||
|
context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getTaskDetails(Context context, long id, boolean extended) {
|
||||||
|
if(extended)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
TodorooCursor<Metadata> cursor = AlarmService.getInstance().getAlarms(id);
|
||||||
|
long nextTime = -1;
|
||||||
|
try {
|
||||||
|
for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
|
||||||
|
long time = cursor.get(Alarm.TIME);
|
||||||
|
if(time > DateUtilities.now()) {
|
||||||
|
nextTime = time;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(nextTime == -1)
|
||||||
|
return null;
|
||||||
|
CharSequence durationString = DateUtils.getRelativeDateTimeString(context,
|
||||||
|
nextTime, DateUtils.MINUTE_IN_MILLIS, DateUtils.WEEK_IN_MILLIS,
|
||||||
|
DateUtils.FORMAT_ABBREV_ALL);
|
||||||
|
return context.getString(R.string.alarm_ADE_detail, durationString);
|
||||||
|
} finally {
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPluginIdentifier() {
|
||||||
|
return AlarmService.IDENTIFIER;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,47 @@
|
|||||||
|
package com.todoroo.astrid.alarms;
|
||||||
|
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
|
||||||
|
import com.todoroo.andlib.data.TodorooCursor;
|
||||||
|
import com.todoroo.andlib.utility.DateUtilities;
|
||||||
|
import com.todoroo.astrid.api.AstridApiConstants;
|
||||||
|
import com.todoroo.astrid.model.Metadata;
|
||||||
|
|
||||||
|
public class AlarmTaskRepeatListener extends BroadcastReceiver {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
long taskId = intent.getLongExtra(AstridApiConstants.EXTRAS_TASK_ID, -1);
|
||||||
|
if(taskId == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
long oldDueDate = intent.getLongExtra(AstridApiConstants.EXTRAS_OLD_DUE_DATE, 0);
|
||||||
|
if(oldDueDate == 0)
|
||||||
|
oldDueDate = DateUtilities.now();
|
||||||
|
long newDueDate = intent.getLongExtra(AstridApiConstants.EXTRAS_NEW_DUE_DATE, -1);
|
||||||
|
if(newDueDate <= 0 || newDueDate <= oldDueDate)
|
||||||
|
return;
|
||||||
|
|
||||||
|
TodorooCursor<Metadata> cursor = AlarmService.getInstance().getAlarms(taskId);
|
||||||
|
try {
|
||||||
|
if(cursor.getCount() == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Metadata metadata = new Metadata();
|
||||||
|
LinkedHashSet<Long> alarms = new LinkedHashSet<Long>(cursor.getCount());
|
||||||
|
for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
|
||||||
|
metadata.readFromCursor(cursor);
|
||||||
|
alarms.add(metadata.getValue(Alarm.TIME) + (newDueDate - oldDueDate));
|
||||||
|
}
|
||||||
|
AlarmService.getInstance().synchronizeAlarms(taskId, alarms);
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 3.1 KiB |
@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- See the file "LICENSE" for the full license governing this code. -->
|
||||||
|
<merge xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<TextView android:id="@+id/alarms_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/alarm_ACS_label"
|
||||||
|
style="@style/TextAppearance.GEN_EditLabel" />
|
||||||
|
|
||||||
|
<LinearLayout android:id="@+id/alert_container"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<Button android:id="@+id/alarms_add"
|
||||||
|
android:text="@string/alarm_ACS_button"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"/>
|
||||||
|
|
||||||
|
</merge>
|
||||||
|
|
||||||
@ -0,0 +1,42 @@
|
|||||||
|
<?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">
|
||||||
|
|
||||||
|
<Button android:id="@+id/date"
|
||||||
|
android:layout_weight="0.7"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"/>
|
||||||
|
|
||||||
|
<Button android:id="@+id/time"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"/>
|
||||||
|
|
||||||
|
<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,99 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- See the file "LICENSE" for the full license governing this code. -->
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/taskListParent"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="fill_parent"
|
||||||
|
android:background="@drawable/background_gradient"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<!-- Header -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:background="@drawable/edit_header">
|
||||||
|
|
||||||
|
<!-- Back Button -->
|
||||||
|
<ImageView android:id="@+id/back"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="fill_parent"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:src="@drawable/tango_previous"
|
||||||
|
android:paddingLeft="5dip"
|
||||||
|
android:paddingRight="8dip"/>
|
||||||
|
|
||||||
|
<!-- List Label -->
|
||||||
|
<TextView android:id="@+id/listLabel"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="100"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:paddingTop="6dip"
|
||||||
|
android:paddingRight="50dip"
|
||||||
|
style="@style/TextAppearance.TLA_Header"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="100">
|
||||||
|
|
||||||
|
<!-- No Tasks label -->
|
||||||
|
<TextView android:id="@android:id/empty"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="fill_parent"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:text="@string/TLA_no_items"
|
||||||
|
style="@style/TextAppearance.TLA_NoItems"/>
|
||||||
|
|
||||||
|
<!-- Task List -->
|
||||||
|
<ListView android:id="@android:id/list"
|
||||||
|
android:scrollbars="vertical"
|
||||||
|
android:cacheColorHint="#00000000"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="fill_parent"/>
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
<!-- Footer -->
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/taskListFooter"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<!-- Quick Add Button -->
|
||||||
|
<ImageButton android:id="@+id/quickAddButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:src="@drawable/tango_add"
|
||||||
|
android:scaleType="fitCenter"/>
|
||||||
|
|
||||||
|
<!-- Quick Add Task -->
|
||||||
|
<EditText android:id="@+id/quickAddText"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="fill_parent"
|
||||||
|
android:layout_weight="100"
|
||||||
|
android:hint="@string/TLA_quick_add_hint"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:autoText="true"
|
||||||
|
android:capitalize="sentences"/>
|
||||||
|
|
||||||
|
<!-- Extended Add Button -->
|
||||||
|
<ImageButton android:id="@+id/extendedAddButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:src="@drawable/tango_edit"
|
||||||
|
android:scaleType="fitCenter"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
Binary file not shown.
@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- See the file "LICENSE" for the full license governing this code. -->
|
||||||
|
<resources xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<!-- Resources for built-in timers plug-in -->
|
||||||
|
|
||||||
|
<!-- Task Edit Activity: Container Label -->
|
||||||
|
<string name="alarm_ACS_label">Alarms</string>
|
||||||
|
|
||||||
|
<!-- Task Edit Activity: Add New Alarn -->
|
||||||
|
<string name="alarm_ACS_button">Add an Alarm</string>
|
||||||
|
|
||||||
|
<!-- Task Detail for Alarms (%s -> time)-->
|
||||||
|
<string name="alarm_ADE_detail">Alarm %s</string>
|
||||||
|
|
||||||
|
<string-array name="reminders_alarm">
|
||||||
|
<!-- reminders related to alarm -->
|
||||||
|
<item>Alarm!</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
|
</resources>
|
||||||
@ -0,0 +1,102 @@
|
|||||||
|
/**
|
||||||
|
* See the file "LICENSE" for the full license governing this code.
|
||||||
|
*/
|
||||||
|
package com.todoroo.astrid.legacy;
|
||||||
|
|
||||||
|
|
||||||
|
import android.content.ContentValues;
|
||||||
|
|
||||||
|
import com.todoroo.andlib.data.AbstractModel;
|
||||||
|
import com.todoroo.andlib.data.Property;
|
||||||
|
import com.todoroo.andlib.data.Property.LongProperty;
|
||||||
|
import com.todoroo.andlib.data.Table;
|
||||||
|
import com.todoroo.andlib.data.TodorooCursor;
|
||||||
|
import com.todoroo.astrid.model.Task;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data Model which represents an alarm. This is a transitional class -
|
||||||
|
* Alarms are moved over to metadata
|
||||||
|
*
|
||||||
|
* @author Tim Su <tim@todoroo.com>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("nls")
|
||||||
|
@Deprecated
|
||||||
|
public class TransitionalAlarm extends AbstractModel {
|
||||||
|
|
||||||
|
// --- table
|
||||||
|
|
||||||
|
public static final Table TABLE = new Table("alarm", TransitionalAlarm.class);
|
||||||
|
|
||||||
|
// --- properties
|
||||||
|
|
||||||
|
/** ID */
|
||||||
|
public static final LongProperty ID = new LongProperty(
|
||||||
|
TABLE, ID_PROPERTY_NAME);
|
||||||
|
|
||||||
|
/** Associated Task */
|
||||||
|
public static final LongProperty TASK = new LongProperty(
|
||||||
|
TABLE, "task");
|
||||||
|
|
||||||
|
/** Alarm Time */
|
||||||
|
public static final LongProperty TIME = new LongProperty(
|
||||||
|
TABLE, "time");
|
||||||
|
|
||||||
|
/** List of all properties for this model */
|
||||||
|
public static final Property<?>[] PROPERTIES = generateProperties(TransitionalAlarm.class);
|
||||||
|
|
||||||
|
// --- constants
|
||||||
|
|
||||||
|
/** this alarm was already triggered */
|
||||||
|
public static final int TYPE_TRIGGERED = 0;
|
||||||
|
|
||||||
|
/** this alarm is single-shot */
|
||||||
|
public static final int TYPE_SINGLE = 1;
|
||||||
|
|
||||||
|
/** this alarm repeats itself until turned off */
|
||||||
|
public static final int TYPE_REPEATING = 2;
|
||||||
|
|
||||||
|
// --- defaults
|
||||||
|
|
||||||
|
/** Default values container */
|
||||||
|
private static final ContentValues defaultValues = new ContentValues();
|
||||||
|
|
||||||
|
static {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ContentValues getDefaultValues() {
|
||||||
|
return defaultValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- data access boilerplate
|
||||||
|
|
||||||
|
public TransitionalAlarm() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TransitionalAlarm(TodorooCursor<TransitionalAlarm> cursor) {
|
||||||
|
this();
|
||||||
|
readPropertiesFromCursor(cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void readFromCursor(TodorooCursor<TransitionalAlarm> cursor) {
|
||||||
|
super.readPropertiesFromCursor(cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getId() {
|
||||||
|
return getIdHelper(ID);
|
||||||
|
};
|
||||||
|
|
||||||
|
// --- parcelable helpers
|
||||||
|
|
||||||
|
private static final Creator<Task> CREATOR = new ModelCreator<Task>(Task.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Creator<? extends AbstractModel> getCreator() {
|
||||||
|
return CREATOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,134 @@
|
|||||||
|
package com.todoroo.astrid.repeats;
|
||||||
|
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import com.google.ical.values.Frequency;
|
||||||
|
import com.google.ical.values.RRule;
|
||||||
|
import com.google.ical.values.Weekday;
|
||||||
|
import com.google.ical.values.WeekdayNum;
|
||||||
|
import com.todoroo.andlib.test.TodorooTestCase;
|
||||||
|
import com.todoroo.andlib.utility.DateUtilities;
|
||||||
|
import com.todoroo.astrid.model.Task;
|
||||||
|
|
||||||
|
public class AdvancedRepeatTests extends TodorooTestCase {
|
||||||
|
|
||||||
|
|
||||||
|
public static void assertDatesEqual(long date, long other) {
|
||||||
|
assertEquals("Expected: " + new Date(date) + ", Actual: " + new Date(other),
|
||||||
|
date, other);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDailyWithDaysOfWeek() throws ParseException {
|
||||||
|
RRule rrule = new RRule();
|
||||||
|
rrule.setInterval(1);
|
||||||
|
rrule.setFreq(Frequency.DAILY);
|
||||||
|
rrule.setByDay(Collections.singletonList(new WeekdayNum(0, Weekday.FR)));
|
||||||
|
|
||||||
|
Task task = new Task();
|
||||||
|
long thursday = task.createDueDate(Task.URGENCY_SPECIFIC_DAY, new Date(113, 7, 1).getTime());
|
||||||
|
task.setValue(Task.DUE_DATE, thursday);
|
||||||
|
|
||||||
|
// repeat once => due date should become friday
|
||||||
|
long friday = thursday + DateUtilities.ONE_DAY;
|
||||||
|
long nextDueDate = RepeatTaskCompleteListener.computeNextDueDate(task, rrule.toIcal());
|
||||||
|
assertDatesEqual(friday, nextDueDate);
|
||||||
|
|
||||||
|
// repeat again => due date should be one week from friday
|
||||||
|
long nextFriday = friday + DateUtilities.ONE_WEEK;
|
||||||
|
task.setValue(Task.DUE_DATE, friday);
|
||||||
|
nextDueDate = RepeatTaskCompleteListener.computeNextDueDate(task, rrule.toIcal());
|
||||||
|
assertDatesEqual(nextFriday, nextDueDate);
|
||||||
|
|
||||||
|
// now try with thursday, and repeat every 2 days. expect next friday
|
||||||
|
rrule.setInterval(2);
|
||||||
|
task.setValue(Task.DUE_DATE, thursday);
|
||||||
|
nextDueDate = RepeatTaskCompleteListener.computeNextDueDate(task, rrule.toIcal());
|
||||||
|
assertDatesEqual(nextFriday, nextDueDate);
|
||||||
|
|
||||||
|
// again with friday, expect next friday
|
||||||
|
task.setValue(Task.DUE_DATE, friday);
|
||||||
|
nextDueDate = RepeatTaskCompleteListener.computeNextDueDate(task, rrule.toIcal());
|
||||||
|
assertDatesEqual(nextFriday, nextDueDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testMonthlyWithDaysOfWeek() throws ParseException {
|
||||||
|
RRule rrule = new RRule();
|
||||||
|
rrule.setInterval(1);
|
||||||
|
rrule.setFreq(Frequency.MONTHLY);
|
||||||
|
rrule.setByDay(Arrays.asList(new WeekdayNum[] {
|
||||||
|
new WeekdayNum(0, Weekday.SU),
|
||||||
|
new WeekdayNum(0, Weekday.MO),
|
||||||
|
new WeekdayNum(0, Weekday.TU),
|
||||||
|
new WeekdayNum(0, Weekday.WE),
|
||||||
|
new WeekdayNum(0, Weekday.TH),
|
||||||
|
new WeekdayNum(0, Weekday.FR),
|
||||||
|
new WeekdayNum(0, Weekday.SA),
|
||||||
|
}));
|
||||||
|
|
||||||
|
Task task = new Task();
|
||||||
|
long thursday = task.createDueDate(Task.URGENCY_SPECIFIC_DAY, new Date(113, 7, 1).getTime());
|
||||||
|
task.setValue(Task.DUE_DATE, thursday);
|
||||||
|
|
||||||
|
// repeat once => due date should become next month on the first
|
||||||
|
long nextMonth = task.createDueDate(Task.URGENCY_SPECIFIC_DAY, new Date(113, 8, 1).getTime());
|
||||||
|
long nextDueDate = RepeatTaskCompleteListener.computeNextDueDate(task, rrule.toIcal());
|
||||||
|
assertDatesEqual(nextMonth, nextDueDate);
|
||||||
|
|
||||||
|
// only allow thursdays
|
||||||
|
rrule.setByDay(Arrays.asList(new WeekdayNum[] {
|
||||||
|
new WeekdayNum(0, Weekday.TH),
|
||||||
|
}));
|
||||||
|
long nextMonthOnThursday = task.createDueDate(Task.URGENCY_SPECIFIC_DAY, new Date(113, 8, 5).getTime());
|
||||||
|
nextDueDate = RepeatTaskCompleteListener.computeNextDueDate(task, rrule.toIcal());
|
||||||
|
assertDatesEqual(nextMonthOnThursday, nextDueDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDueDateInPast() throws ParseException {
|
||||||
|
RRule rrule = new RRule();
|
||||||
|
rrule.setInterval(1);
|
||||||
|
rrule.setFreq(Frequency.DAILY);
|
||||||
|
|
||||||
|
Task task = new Task();
|
||||||
|
|
||||||
|
// repeat once => due date should become tomorrow
|
||||||
|
long past = task.createDueDate(Task.URGENCY_SPECIFIC_DAY, new Date(110, 7, 1).getTime());
|
||||||
|
task.setValue(Task.DUE_DATE, past);
|
||||||
|
long today = task.createDueDate(Task.URGENCY_SPECIFIC_DAY, DateUtilities.now());
|
||||||
|
long nextDueDate = RepeatTaskCompleteListener.computeNextDueDate(task, rrule.toIcal());
|
||||||
|
assertDatesEqual(today, nextDueDate);
|
||||||
|
|
||||||
|
// test specific day & time
|
||||||
|
long pastWithTime = task.createDueDate(Task.URGENCY_SPECIFIC_DAY_TIME, new Date(110, 7, 1, 10, 4).getTime());
|
||||||
|
task.setValue(Task.DUE_DATE, pastWithTime);
|
||||||
|
Date date = new Date(DateUtilities.now() / 1000L * 1000L);
|
||||||
|
date.setHours(10);
|
||||||
|
date.setMinutes(4);
|
||||||
|
date.setSeconds(0);
|
||||||
|
long todayWithTime = task.createDueDate(Task.URGENCY_SPECIFIC_DAY_TIME, date.getTime()) / 1000L * 1000L;
|
||||||
|
if(todayWithTime < DateUtilities.now())
|
||||||
|
todayWithTime += DateUtilities.ONE_DAY;
|
||||||
|
nextDueDate = RepeatTaskCompleteListener.computeNextDueDate(task, rrule.toIcal());
|
||||||
|
assertDatesEqual(todayWithTime, nextDueDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDueDateInPastRepeatMultiple() throws ParseException {
|
||||||
|
RRule rrule = new RRule();
|
||||||
|
rrule.setInterval(1);
|
||||||
|
rrule.setFreq(Frequency.DAILY);
|
||||||
|
Task task = new Task();
|
||||||
|
|
||||||
|
// repeat once => due date should become tomorrow
|
||||||
|
long past = task.createDueDate(Task.URGENCY_SPECIFIC_DAY_TIME, new Date(110, 7, 1, 0, 0, 0).getTime());
|
||||||
|
task.setValue(Task.DUE_DATE, past);
|
||||||
|
long nextDueDate = RepeatTaskCompleteListener.computeNextDueDate(task, rrule.toIcal());
|
||||||
|
assertTrue(nextDueDate > DateUtilities.now());
|
||||||
|
task.setValue(Task.DUE_DATE, nextDueDate);
|
||||||
|
long evenMoreNextDueDate = RepeatTaskCompleteListener.computeNextDueDate(task, rrule.toIcal());
|
||||||
|
assertNotSame(nextDueDate, evenMoreNextDueDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue