Deleted some cruft, fixed a bunch of unit tests

pull/14/head
Tim Su 15 years ago
parent 8c2d742336
commit c3025f355f

@ -1,126 +0,0 @@
/*
* 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.todoroo.andlib.widget;
import java.util.LinkedList;
import java.util.List;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.FrameLayout.LayoutParams;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
/** Dialog box with an arbitrary number of number pickers */
public class NNumberPickerDialog extends AlertDialog implements OnClickListener {
@Autowired
private Integer nNumberPickerLayout;
public interface OnNNumberPickedListener {
void onNumbersPicked(int[] number);
}
private final List<NumberPickerWidget> pickers = new LinkedList<NumberPickerWidget>();
private final OnNNumberPickedListener mCallback;
/** Instantiate the dialog box.
*
* @param context
* @param callBack callback function to get the numbers you requested
* @param title title of the dialog box
* @param initialValue initial picker values array
* @param incrementBy picker increment by array
* @param start picker range start array
* @param end picker range end array
* @param separators text separating the spinners. whole array, or individual
* elements can be null
*/
public NNumberPickerDialog(Context context, OnNNumberPickedListener callBack,
String title, int[] initialValue, int[] incrementBy, int[] start,
int[] end, String[] separators) {
super(context);
mCallback = callBack;
DependencyInjectionService.getInstance().inject(this);
setButton(context.getText(android.R.string.ok), this);
setButton2(context.getText(android.R.string.cancel), (OnClickListener) null);
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(nNumberPickerLayout, null);
setView(view);
LinearLayout container = (LinearLayout)view;
setTitle(title);
LayoutParams npLayout = new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.FILL_PARENT);
npLayout.gravity = 1;
LayoutParams sepLayout = new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.FILL_PARENT);
for(int i = 0; i < incrementBy.length; i++) {
NumberPickerWidget np = new NumberPickerWidget(context, null);
np.setIncrementBy(incrementBy[i]);
np.setLayoutParams(npLayout);
np.setRange(start[i], end[i]);
np.setCurrent(initialValue[i]);
container.addView(np);
pickers.add(np);
if(separators != null && separators[i] != null) {
TextView text = new TextView(context);
text.setText(separators[i]);
if(separators[i].length() < 3)
text.setTextSize(48);
else
text.setTextSize(20);
text.setGravity(Gravity.CENTER);
text.setLayoutParams(sepLayout);
container.addView(text);
}
}
}
public void setInitialValues(int[] values) {
for(int i = 0; i < pickers.size(); i++)
pickers.get(i).setCurrent(values[i]);
}
public void onClick(DialogInterface dialog, int which) {
if (mCallback != null) {
int[] values = new int[pickers.size()];
for(int i = 0; i < pickers.size(); i++) {
pickers.get(i).clearFocus();
values[i] = pickers.get(i).getCurrent();
}
mCallback.onNumbersPicked(values);
}
}
}

@ -1,79 +0,0 @@
/*
* 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.todoroo.andlib.widget;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.view.LayoutInflater;
import android.view.View;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
public class NumberPickerDialog extends AlertDialog implements OnClickListener {
@Autowired
private Integer numberPickerDialogLayout;
@Autowired
private Integer numberPickerId;
public interface OnNumberPickedListener {
void onNumberPicked(NumberPickerWidget view, int number);
}
private final NumberPickerWidget mPicker;
private final OnNumberPickedListener mCallback;
public NumberPickerDialog(Context context, OnNumberPickedListener callBack,
String title, int initialValue, int incrementBy, int start, int end) {
super(context);
DependencyInjectionService.getInstance().inject(this);
mCallback = callBack;
setButton(context.getText(android.R.string.ok), this);
setButton2(context.getText(android.R.string.cancel), (OnClickListener) null);
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(numberPickerDialogLayout, null);
setView(view);
setTitle(title);
mPicker = (NumberPickerWidget) view.findViewById(numberPickerId);
mPicker.setIncrementBy(incrementBy);
mPicker.setRange(start, end);
mPicker.setCurrent(initialValue);
}
public void setInitialValue(int initialValue) {
mPicker.setCurrent(initialValue);
}
public void onClick(DialogInterface dialog, int which) {
if (mCallback != null) {
mPicker.clearFocus();
mCallback.onNumberPicked(mPicker, mPicker.getCurrent());
}
}
}

@ -1,460 +0,0 @@
/*
* 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.todoroo.andlib.widget;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.text.InputFilter;
import android.text.Spanned;
import android.text.method.NumberKeyListener;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnFocusChangeListener;
import android.view.View.OnLongClickListener;
import android.view.animation.Animation;
import android.view.animation.TranslateAnimation;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
public class NumberPickerWidget extends LinearLayout implements OnClickListener,
OnFocusChangeListener, OnLongClickListener {
@Autowired
private Integer numberPickerLayout;
@Autowired
private Integer numberPickerIncrementId;
@Autowired
private Integer numberPickerDecrementId;
@Autowired
private Integer numberPickerInputId;
public interface OnChangedListener {
void onChanged(NumberPickerWidget picker, int oldVal, int newVal);
}
public interface Formatter {
String toString(int value);
}
/*
* Use a custom NumberPicker formatting callback to use two-digit minutes
* strings like "01". Keeping a static formatter etc. is the most efficient
* way to do this; it avoids creating temporary objects on every call to
* format().
*/
public static final NumberPickerWidget.Formatter TWO_DIGIT_FORMATTER = new NumberPickerWidget.Formatter() {
final StringBuilder mBuilder = new StringBuilder();
final java.util.Formatter mFmt = new java.util.Formatter(mBuilder);
final Object[] mArgs = new Object[1];
public String toString(int value) {
mArgs[0] = value;
mBuilder.delete(0, mBuilder.length());
mFmt.format("%02d", mArgs); //$NON-NLS-1$
return mFmt.toString();
}
};
protected int incrementBy = 1;
public void setIncrementBy(int incrementBy) {
this.incrementBy = incrementBy;
}
protected final Handler mHandler;
private final Runnable mRunnable = new Runnable() {
public void run() {
if (mIncrement) {
changeCurrent(mCurrent + incrementBy, mSlideUpInAnimation, mSlideUpOutAnimation);
mHandler.postDelayed(this, mSpeed);
} else if (mDecrement) {
changeCurrent(mCurrent - incrementBy, mSlideDownInAnimation, mSlideDownOutAnimation);
mHandler.postDelayed(this, mSpeed);
}
}
};
private final LayoutInflater mInflater;
private final TextView mText;
protected final InputFilter mInputFilter;
protected final InputFilter mNumberInputFilter;
protected final Animation mSlideUpOutAnimation;
protected final Animation mSlideUpInAnimation;
protected final Animation mSlideDownOutAnimation;
protected final Animation mSlideDownInAnimation;
protected String[] mDisplayedValues;
protected int mStart;
protected int mEnd;
protected int mCurrent;
protected int mPrevious;
private OnChangedListener mListener;
private Formatter mFormatter;
protected long mSpeed = 500;
protected boolean mIncrement;
protected boolean mDecrement;
public NumberPickerWidget(Context context) {
this(context, null);
}
public NumberPickerWidget(Context context, AttributeSet attrs) {
super(context, attrs);
DependencyInjectionService.getInstance().inject(this);
setOrientation(VERTICAL);
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mInflater.inflate(numberPickerLayout, this, true);
mHandler = new Handler(Looper.getMainLooper());
mInputFilter = new NumberPickerInputFilter();
mNumberInputFilter = new NumberRangeKeyListener();
mIncrementButton = (NumberPickerWidgetButton) findViewById(numberPickerIncrementId);
mIncrementButton.setOnClickListener(this);
mIncrementButton.setOnLongClickListener(this);
mIncrementButton.setNumberPicker(this);
mDecrementButton = (NumberPickerWidgetButton) findViewById(numberPickerDecrementId);
mDecrementButton.setOnClickListener(this);
mDecrementButton.setOnLongClickListener(this);
mDecrementButton.setNumberPicker(this);
mText = (TextView) findViewById(numberPickerInputId);
mText.setOnFocusChangeListener(this);
mText.setFilters(new InputFilter[] { mInputFilter });
mSlideUpOutAnimation = new TranslateAnimation(
Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 0,
Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, -100);
mSlideUpOutAnimation.setDuration(200);
mSlideUpInAnimation = new TranslateAnimation(
Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 0,
Animation.RELATIVE_TO_SELF, 100, Animation.RELATIVE_TO_SELF, 0);
mSlideUpInAnimation.setDuration(200);
mSlideDownOutAnimation = new TranslateAnimation(
Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 0,
Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 100);
mSlideDownOutAnimation.setDuration(200);
mSlideDownInAnimation = new TranslateAnimation(
Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 0,
Animation.RELATIVE_TO_SELF, -100, Animation.RELATIVE_TO_SELF, 0);
mSlideDownInAnimation.setDuration(200);
if (!isEnabled()) {
setEnabled(false);
}
}
@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
mIncrementButton.setEnabled(enabled);
mDecrementButton.setEnabled(enabled);
mText.setEnabled(enabled);
}
public void setOnChangeListener(OnChangedListener listener) {
mListener = listener;
}
public void setFormatter(Formatter formatter) {
mFormatter = formatter;
}
/**
* Set the range of numbers allowed for the number picker. The current value
* will be automatically set to the start.
*
* @param start
* the start of the range (inclusive)
* @param end
* the end of the range (inclusive)
*/
public void setRange(int start, int end) {
mStart = start;
mEnd = end;
mCurrent = start;
updateView();
}
/**
* Set the range of numbers allowed for the number picker. The current value
* will be automatically set to the start. Also provide a mapping for values
* used to display to the user.
*
* @param start
* the start of the range (inclusive)
* @param end
* the end of the range (inclusive)
* @param displayedValues
* the values displayed to the user.
*/
public void setRange(int start, int end, String[] displayedValues) {
mDisplayedValues = displayedValues;
mStart = start;
mEnd = end;
mCurrent = start;
updateView();
}
public void setCurrent(int current) {
mCurrent = current;
updateView();
}
/**
* The speed (in milliseconds) at which the numbers will scroll when the the
* +/- buttons are longpressed. Default is 300ms.
*/
public void setSpeed(long speed) {
mSpeed = speed;
}
public void onClick(View v) {
/*
* The text view may still have focus so clear it's focus which will
* trigger the on focus changed and any typed values to be pulled.
*/
mText.clearFocus();
// now perform the increment/decrement
if (numberPickerIncrementId == v.getId()) {
changeCurrent(mCurrent + incrementBy, mSlideUpInAnimation,
mSlideUpOutAnimation);
} else if (numberPickerDecrementId == v.getId()) {
changeCurrent(mCurrent - incrementBy, mSlideDownInAnimation,
mSlideDownOutAnimation);
}
}
private String formatNumber(int value) {
return (mFormatter != null) ? mFormatter.toString(value)
: String.valueOf(value);
}
protected void changeCurrent(int current,
@SuppressWarnings("unused") Animation in,
@SuppressWarnings("unused") Animation out) {
// Wrap around the values if we go past the start or end
if (current > mEnd) {
current = mStart;
} else if (current < mStart) {
current = mEnd;
}
mPrevious = mCurrent;
mCurrent = current;
notifyChange();
updateView();
}
private void notifyChange() {
if (mListener != null) {
mListener.onChanged(this, mPrevious, mCurrent);
}
}
private void updateView() {
/*
* If we don't have displayed values then use the current number else
* find the correct value in the displayed values for the current
* number.
*/
if (mDisplayedValues == null) {
mText.setText(formatNumber(mCurrent));
} else {
mText.setText(mDisplayedValues[mCurrent - mStart]);
}
}
private void validateCurrentView(CharSequence str) {
int val = getSelectedPos(str.toString());
if ((val >= mStart) && (val <= mEnd)) {
mPrevious = mCurrent;
mCurrent = val;
notifyChange();
}
updateView();
}
public void onFocusChange(View v, boolean hasFocus) {
/*
* When focus is lost check that the text field has valid values.
*/
if (!hasFocus && v instanceof TextView) {
String str = String.valueOf(((TextView) v).getText());
if ("".equals(str)) { //$NON-NLS-1$
// Restore to the old value as we don't allow empty values
updateView();
} else {
// Check the new value and ensure it's in range
validateCurrentView(str);
}
}
}
/**
* We start the long click here but rely on the {@link NumberPickerWidgetButton}
* to inform us when the long click has ended.
*/
public boolean onLongClick(View v) {
/*
* The text view may still have focus so clear it's focus which will
* trigger the on focus changed and any typed values to be pulled.
*/
mText.clearFocus();
if (numberPickerIncrementId == v.getId()) {
mIncrement = true;
mHandler.post(mRunnable);
} else if (numberPickerDecrementId == v.getId()) {
mDecrement = true;
mHandler.post(mRunnable);
}
return true;
}
public void cancelIncrement() {
mIncrement = false;
}
public void cancelDecrement() {
mDecrement = false;
}
protected static final char[] DIGIT_CHARACTERS = new char[] { '0', '1', '2',
'3', '4', '5', '6', '7', '8', '9' };
private NumberPickerWidgetButton mIncrementButton;
private NumberPickerWidgetButton mDecrementButton;
class NumberPickerInputFilter implements InputFilter {
public CharSequence filter(CharSequence source, int start, int end,
Spanned dest, int dstart, int dend) {
if (mDisplayedValues == null) {
return mNumberInputFilter.filter(source, start, end, dest,
dstart, dend);
}
CharSequence filtered = String.valueOf(source.subSequence(start,
end));
String result = String.valueOf(dest.subSequence(0, dstart))
+ filtered + dest.subSequence(dend, dest.length());
String str = String.valueOf(result).toLowerCase();
for (String val : mDisplayedValues) {
val = val.toLowerCase();
if (val.startsWith(str)) {
return filtered;
}
}
return ""; //$NON-NLS-1$
}
}
class NumberRangeKeyListener extends NumberKeyListener {
@Override
protected char[] getAcceptedChars() {
return DIGIT_CHARACTERS;
}
@Override
public CharSequence filter(CharSequence source, int start, int end,
Spanned dest, int dstart, int dend) {
CharSequence filtered = super.filter(source, start, end, dest,
dstart, dend);
if (filtered == null) {
filtered = source.subSequence(start, end);
}
String result = String.valueOf(dest.subSequence(0, dstart))
+ filtered + dest.subSequence(dend, dest.length());
if ("".equals(result)) { //$NON-NLS-1$
return result;
}
int val = getSelectedPos(result);
/*
* Ensure the user can't type in a value greater than the max
* allowed. We have to allow less than min as the user might want to
* delete some numbers and then type a new number.
*/
if (val > mEnd) {
return ""; //$NON-NLS-1$
} else {
return filtered;
}
}
public int getInputType() {
return 0;
}
}
protected int getSelectedPos(String str) {
if (mDisplayedValues == null) {
return Integer.parseInt(str);
} else {
for (int i = 0; i < mDisplayedValues.length; i++) {
/* Don't force the user to type in jan when ja will do */
str = str.toLowerCase();
if (mDisplayedValues[i].toLowerCase().startsWith(str)) {
return mStart + i;
}
}
/*
* The user might have typed in a number into the month field i.e.
* 10 instead of OCT so support that too.
*/
try {
return Integer.parseInt(str);
} catch (NumberFormatException e) {
/* Ignore as if it's not a number we don't care */
}
}
return mStart;
}
/**
* @return the current value.
*/
public int getCurrent() {
return mCurrent;
}
}

@ -1,99 +0,0 @@
/*
* 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.todoroo.andlib.widget;
import android.content.Context;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.widget.ImageButton;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
/**
* This class exists purely to cancel long click events.
*/
public class NumberPickerWidgetButton extends ImageButton {
private NumberPickerWidget mNumberPicker;
@Autowired
private Integer numberPickerIncrementId;
@Autowired
private Integer numberPickerDecrementId;
public NumberPickerWidgetButton(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
DependencyInjectionService.getInstance().inject(this);
}
public NumberPickerWidgetButton(Context context, AttributeSet attrs) {
super(context, attrs);
DependencyInjectionService.getInstance().inject(this);
}
public NumberPickerWidgetButton(Context context) {
super(context);
DependencyInjectionService.getInstance().inject(this);
}
public void setNumberPicker(NumberPickerWidget picker) {
mNumberPicker = picker;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
cancelLongpressIfRequired(event);
return super.onTouchEvent(event);
}
@Override
public boolean onTrackballEvent(MotionEvent event) {
cancelLongpressIfRequired(event);
return super.onTrackballEvent(event);
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_DPAD_CENTER)
|| (keyCode == KeyEvent.KEYCODE_ENTER)) {
cancelLongpress();
}
return super.onKeyUp(keyCode, event);
}
private void cancelLongpressIfRequired(MotionEvent event) {
if ((event.getAction() == MotionEvent.ACTION_CANCEL)
|| (event.getAction() == MotionEvent.ACTION_UP)) {
cancelLongpress();
}
}
private void cancelLongpress() {
if (numberPickerIncrementId == getId()) {
mNumberPicker.cancelIncrement();
} else if (numberPickerDecrementId == getId()) {
mNumberPicker.cancelDecrement();
}
}
}

@ -47,7 +47,7 @@ public class BackupActivity extends Activity {
}; };
new FilePickerBuilder(this, new FilePickerBuilder(this,
getString(R.string.import_file_prompt), getString(R.string.import_file_prompt),
BackupConstants.getExportDirectory(), BackupConstants.defaultExportDirectory(),
listener).show(); listener).show();
} }
@ -58,7 +58,7 @@ public class BackupActivity extends Activity {
setResult(RESULT_OK); setResult(RESULT_OK);
finish(); finish();
} }
}); }, null);
} }
} }

@ -62,7 +62,7 @@ public class BackupConstants {
* @return export directory for tasks, or null if no SD card * @return export directory for tasks, or null if no SD card
*/ */
@CheckForNull @CheckForNull
public static File getExportDirectory() { public static File defaultExportDirectory() {
String storageState = Environment.getExternalStorageState(); String storageState = Environment.getExternalStorageState();
if (storageState.equals(Environment.MEDIA_MOUNTED)) { if (storageState.equals(Environment.MEDIA_MOUNTED)) {
String path = Environment.getExternalStorageDirectory().getAbsolutePath(); String path = Environment.getExternalStorageDirectory().getAbsolutePath();

@ -77,7 +77,8 @@ public class BackupService extends Service {
Log.e("error-deleting", "Error deleting old backups", e); //$NON-NLS-1$ //$NON-NLS-2$ Log.e("error-deleting", "Error deleting old backups", e); //$NON-NLS-1$ //$NON-NLS-2$
} }
TasksXmlExporter.exportTasks(context, true, null); TasksXmlExporter.exportTasks(context, true, null,
backupDirectorySetting.getBackupDirectory());
} catch (Exception e) { } catch (Exception e) {
Log.e("error-backup", "Error starting backups", e); //$NON-NLS-1$ //$NON-NLS-2$ Log.e("error-backup", "Error starting backups", e); //$NON-NLS-1$ //$NON-NLS-2$
@ -149,7 +150,7 @@ public class BackupService extends Service {
private BackupDirectorySetting backupDirectorySetting = new BackupDirectorySetting() { private BackupDirectorySetting backupDirectorySetting = new BackupDirectorySetting() {
public File getBackupDirectory() { public File getBackupDirectory() {
return null; //TasksXmlExporter.getExportDirectory(); return BackupConstants.defaultExportDirectory();
} }
}; };

@ -17,8 +17,8 @@ import android.widget.Toast;
import com.timsu.astrid.R; import com.timsu.astrid.R;
import com.todoroo.andlib.data.AbstractModel; import com.todoroo.andlib.data.AbstractModel;
import com.todoroo.andlib.data.Property; import com.todoroo.andlib.data.Property;
import com.todoroo.andlib.data.Property.PropertyVisitor;
import com.todoroo.andlib.data.TodorooCursor; import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.data.Property.PropertyVisitor;
import com.todoroo.andlib.service.ExceptionService; import com.todoroo.andlib.service.ExceptionService;
import com.todoroo.andlib.sql.Order; import com.todoroo.andlib.sql.Order;
import com.todoroo.andlib.sql.Query; import com.todoroo.andlib.sql.Query;
@ -38,11 +38,15 @@ public class TasksXmlExporter {
/** /**
* Import tasks from the given file * Import tasks from the given file
* *
* @param input * @param context context
* @param runAfterExport * @param isService if false, displays ui dialogs
* @param runAfterExport runnable to run after exporting
* @param backupDirectoryOverride new backupdirectory, or null to use default
*/ */
public static void exportTasks(Context context, boolean isService, Runnable runAfterExport) { public static void exportTasks(Context context, boolean isService,
new TasksXmlExporter(context, isService, runAfterExport); Runnable runAfterExport, File backupDirectoryOverride) {
new TasksXmlExporter(context, isService, runAfterExport,
backupDirectoryOverride);
} }
// --- implementation // --- implementation
@ -58,6 +62,7 @@ public class TasksXmlExporter {
private final ProgressDialog progressDialog; private final ProgressDialog progressDialog;
private final Handler handler; private final Handler handler;
private final File backupDirectory;
private void setProgress(final int taskNumber, final int total) { private void setProgress(final int taskNumber, final int total) {
handler.post(new Runnable() { handler.post(new Runnable() {
@ -69,9 +74,11 @@ public class TasksXmlExporter {
} }
private TasksXmlExporter(final Context context, final boolean isService, private TasksXmlExporter(final Context context, final boolean isService,
final Runnable runAfterExport) { final Runnable runAfterExport, File backupDirectoryOverride) {
this.context = context; this.context = context;
this.exportCount = 0; this.exportCount = 0;
this.backupDirectory = backupDirectoryOverride == null ?
BackupConstants.defaultExportDirectory() : backupDirectoryOverride;
handler = new Handler(); handler = new Handler();
progressDialog = new ProgressDialog(context); progressDialog = new ProgressDialog(context);
@ -91,7 +98,7 @@ public class TasksXmlExporter {
@Override @Override
public void run() { public void run() {
try { try {
String output = setupFile(BackupConstants.getExportDirectory(), String output = setupFile(backupDirectory,
isService); isService);
int tasks = taskService.countTasks(); int tasks = taskService.countTasks();

@ -222,7 +222,10 @@ public class NotificationActivity extends TaskListActivity implements OnTimeSetL
} }
public void snoozeTime(long time) { public void snoozeTime(long time) {
ReminderService.getInstance().scheduleSnoozeAlarm(taskId, time); Task task = new Task();
task.setId(taskId);
task.setValue(Task.REMINDER_SNOOZE, time);
taskService.save(task);
finish(); finish();
} }

@ -136,11 +136,7 @@ public class Notifications extends BroadcastReceiver {
// update last reminder time // update last reminder time
task.setValue(Task.REMINDER_LAST, DateUtilities.now()); task.setValue(Task.REMINDER_LAST, DateUtilities.now());
boolean saved = taskDao.saveExisting(task); taskDao.saveExisting(task);
// schedule next notification (unless couldn't save last time)
if(saved)
ReminderService.getInstance().scheduleAlarm(task);
Context context = ContextManager.getContext(); Context context = ContextManager.getContext();
String title = context.getString(R.string.app_name); String title = context.getString(R.string.app_name);

@ -309,22 +309,6 @@ public final class ReminderService {
return NO_ALARM; return NO_ALARM;
} }
/**
* Schedule a snooze alarm for this task
* @param taskId
* @param time
*/
public void scheduleSnoozeAlarm(long taskId, long time) {
if(time < DateUtilities.now())
return;
Task task = taskDao.fetch(taskId, PROPERTIES);
scheduler.createAlarm(task, time, TYPE_SNOOZE);
// record snooze time
task.setValue(Task.REMINDER_SNOOZE, time);
taskDao.saveExisting(task);
}
// --- alarm manager alarm creation // --- alarm manager alarm creation
/** /**

@ -233,7 +233,9 @@ public class TaskDao extends DatabaseDao<Task> {
afterComplete(task, values); afterComplete(task, values);
else if(values.containsKey(Task.DUE_DATE.name) || else if(values.containsKey(Task.DUE_DATE.name) ||
values.containsKey(Task.REMINDER_FLAGS.name) || values.containsKey(Task.REMINDER_FLAGS.name) ||
values.containsKey(Task.REMINDER_PERIOD.name)) values.containsKey(Task.REMINDER_PERIOD.name) ||
values.containsKey(Task.REMINDER_LAST.name) ||
values.containsKey(Task.REMINDER_SNOOZE.name))
ReminderService.getInstance().scheduleAlarm(task); ReminderService.getInstance().scheduleAlarm(task);
} }

@ -3,7 +3,6 @@
*/ */
package com.todoroo.astrid.service; package com.todoroo.astrid.service;
import com.timsu.astrid.R;
import com.todoroo.andlib.service.AbstractDependencyInjector; import com.todoroo.andlib.service.AbstractDependencyInjector;
import com.todoroo.andlib.service.DependencyInjectionService; import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.service.ExceptionService.AndroidLogReporter; import com.todoroo.andlib.service.ExceptionService.AndroidLogReporter;
@ -43,15 +42,6 @@ public class AstridDependencyInjector extends AbstractDependencyInjector {
// com.todoroo.android.service // com.todoroo.android.service
injectables.put("applicationName", "astrid"); injectables.put("applicationName", "astrid");
// com.todoroo.android.utility
injectables.put("nNumberPickerLayout", R.layout.n_number_picker_dialog);
injectables.put("numberPickerLayout", R.layout.number_picker);
injectables.put("numberPickerIncrementId", R.id.increment);
injectables.put("numberPickerDecrementId", R.id.decrement);
injectables.put("numberPickerId", R.id.numberPicker);
injectables.put("numberPickerInputId", R.id.timepicker_input);
injectables.put("numberPickerDialogLayout", R.layout.number_picker_dialog);
// com.todoroo.astrid.dao // com.todoroo.astrid.dao
injectables.put("database", Database.class); injectables.put("database", Database.class);
injectables.put("taskDao", TaskDao.class); injectables.put("taskDao", TaskDao.class);

@ -158,7 +158,7 @@ public class StartupService {
try { try {
if(Preferences.getCurrentVersion() > 135 && !context.getDatabasePath(database.getName()).exists()) { if(Preferences.getCurrentVersion() > 135 && !context.getDatabasePath(database.getName()).exists()) {
// we didn't have a database! restore latest file // we didn't have a database! restore latest file
File directory = BackupConstants.getExportDirectory(); File directory = BackupConstants.defaultExportDirectory();
if(!directory.exists()) if(!directory.exists())
return; return;
File[] children = directory.listFiles(); File[] children = directory.listFiles();

3
tests/.gitignore vendored

@ -0,0 +1,3 @@
bin/
instrumented/
coverage.em

@ -3,13 +3,16 @@
# This file must be checked in Version Control Systems, as it is # This file must be checked in Version Control Systems, as it is
# integral to the build system of your project. # integral to the build system of your project.
# The name of your application package as defined in the manifest. # This file is only used by the Ant script.
# Used by the 'uninstall' rule.
#application-package=com.example.myproject
# The name of the source folder. # You can use this to override default values such as
#source-folder=src # 'source.dir' for the location of your java source folder and
# 'out.dir' for the location of your output folder.
# The name of the output folder. # You can also use it define how the release builds are signed by declaring
#out-folder=bin # the following properties:
# 'key.store' for the location of your keystore and
# 'key.alias' for the name of the key to use.
# The password will be asked during the build when you use the 'release' target.
tested.project.dir=../astrid

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project name="Main" default="help"> <project name="astrid-test" default="help">
<!-- The local.properties file is created and updated by the 'android' tool. <!-- The local.properties file is created and updated by the 'android' tool.
It contain the path to the SDK. It should *NOT* be checked in in Version It contains the path to the SDK. It should *NOT* be checked in in Version
Control Systems. --> Control Systems. -->
<property file="local.properties" /> <property file="local.properties" />
@ -11,13 +11,13 @@
used by the Ant rules. used by the Ant rules.
Here are some properties you may want to change/update: Here are some properties you may want to change/update:
application-package application.package
the name of your application package as defined in the manifest. Used by the the name of your application package as defined in the manifest. Used by the
'uninstall' rule. 'uninstall' rule.
source-folder source.dir
the name of the source folder. Default is 'src'. the name of the source directory. Default is 'src'.
out-folder out.dir
the name of the output folder. Default is 'bin'. the name of the output directory. Default is 'bin'.
Properties related to the SDK location or the project target should be updated Properties related to the SDK location or the project target should be updated
using the 'android' tool with the 'update' action. using the 'android' tool with the 'update' action.
@ -37,11 +37,11 @@
<!-- Custom Android task to deal with the project target, and import the proper rules. <!-- Custom Android task to deal with the project target, and import the proper rules.
This requires ant 1.6.0 or above. --> This requires ant 1.6.0 or above. -->
<path id="android.antlibs"> <path id="android.antlibs">
<pathelement path="${sdk-location}/tools/lib/anttasks.jar" /> <pathelement path="${sdk.dir}/tools/lib/anttasks.jar" />
<pathelement path="${sdk-location}/tools/lib/sdklib.jar" /> <pathelement path="${sdk.dir}/tools/lib/sdklib.jar" />
<pathelement path="${sdk-location}/tools/lib/androidprefs.jar" /> <pathelement path="${sdk.dir}/tools/lib/androidprefs.jar" />
<pathelement path="${sdk-location}/tools/lib/apkbuilder.jar" /> <pathelement path="${sdk.dir}/tools/lib/apkbuilder.jar" />
<pathelement path="${sdk-location}/tools/lib/jarutils.jar" /> <pathelement path="${sdk.dir}/tools/lib/jarutils.jar" />
</path> </path>
<taskdef name="setup" <taskdef name="setup"
@ -49,13 +49,19 @@
classpathref="android.antlibs" /> classpathref="android.antlibs" />
<!-- Execute the Android Setup task that will setup some properties specific to the target, <!-- Execute the Android Setup task that will setup some properties specific to the target,
and import the rules files. and import the build rules files.
To customize the rules, copy/paste them below the task, and disable import by setting
the import attribute to false: The rules file is imported from
<setup import="false" /> <SDK>/platforms/<target_platform>/templates/android_rules.xml
To customize some build steps for your project:
- copy the content of the main node <project> from android_rules.xml
- paste it in this build.xml below the <setup /> task.
- disable the import by changing the setup task below to <setup import="false" />
This will ensure that the properties are setup correctly but that your customized This will ensure that the properties are setup correctly but that your customized
targets are used. build steps are used.
--> -->
<setup /> <setup />
</project> </project>

@ -10,4 +10,4 @@
# Indicates whether an apk should be generated for each density. # Indicates whether an apk should be generated for each density.
split.density=false split.density=false
# Project target. # Project target.
target=android-4 target=android-8

@ -8,11 +8,14 @@ import java.util.Date;
import com.timsu.astrid.R; import com.timsu.astrid.R;
import com.todoroo.andlib.test.TodorooTestCase; import com.todoroo.andlib.test.TodorooTestCase;
import com.todoroo.andlib.utility.AndroidUtilities;
import com.todoroo.astrid.backup.BackupService.BackupDirectorySetting; import com.todoroo.astrid.backup.BackupService.BackupDirectorySetting;
import com.todoroo.astrid.utility.Preferences; import com.todoroo.astrid.utility.Preferences;
public class BackupServiceTests extends TodorooTestCase { public class BackupServiceTests extends TodorooTestCase {
private static final long BACKUP_WAIT_TIME = 2000L;
File temporaryDirectory = null; File temporaryDirectory = null;
BackupDirectorySetting setting = new BackupDirectorySetting() { BackupDirectorySetting setting = new BackupDirectorySetting() {
@ -66,6 +69,8 @@ public class BackupServiceTests extends TodorooTestCase {
service.setBackupDirectorySetting(setting); service.setBackupDirectorySetting(setting);
service.testBackup(getContext()); service.testBackup(getContext());
AndroidUtilities.sleepDeep(BACKUP_WAIT_TIME);
// assert file created // assert file created
File[] files = temporaryDirectory.listFiles(); File[] files = temporaryDirectory.listFiles();
assertEquals(1, files.length); assertEquals(1, files.length);
@ -98,6 +103,8 @@ public class BackupServiceTests extends TodorooTestCase {
}); });
service.testBackup(getContext()); service.testBackup(getContext());
AndroidUtilities.sleepDeep(BACKUP_WAIT_TIME);
// assert no file created // assert no file created
File[] files = temporaryDirectory.listFiles(); File[] files = temporaryDirectory.listFiles();
assertEquals(0, files.length); assertEquals(0, files.length);
@ -141,6 +148,8 @@ public class BackupServiceTests extends TodorooTestCase {
service.setBackupDirectorySetting(setting); service.setBackupDirectorySetting(setting);
service.testBackup(getContext()); service.testBackup(getContext());
AndroidUtilities.sleepDeep(BACKUP_WAIT_TIME);
// assert the oldest file was deleted // assert the oldest file was deleted
assertTrue(temporaryDirectory.listFiles().length < 11); assertTrue(temporaryDirectory.listFiles().length < 11);
assertFalse(files[4].exists()); assertFalse(files[4].exists());

@ -23,6 +23,11 @@ public class NotificationTests extends DatabaseTestCase {
boolean value = false; boolean value = false;
} }
@Override
protected void tearDown() throws Exception {
Notifications.setNotificationManager(null);
}
/** test that a normal task gets a notification */ /** test that a normal task gets a notification */
public void testAlarmToNotification() { public void testAlarmToNotification() {
final Task task = new Task(); final Task task = new Task();

@ -1,5 +1,7 @@
package com.todoroo.astrid.reminders; package com.todoroo.astrid.reminders;
import java.util.Date;
import com.todoroo.andlib.service.Autowired; import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.utility.DateUtilities; import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.dao.TaskDao; import com.todoroo.astrid.dao.TaskDao;
@ -37,14 +39,23 @@ public class ReminderServiceTests extends DatabaseTestCase {
Task task = new Task(); Task task = new Task();
task.setValue(Task.TITLE, "water"); task.setValue(Task.TITLE, "water");
task.setValue(Task.REMINDER_FLAGS, 0); task.setValue(Task.REMINDER_FLAGS, 0);
task.setValue(Task.REMINDER_PERIOD, 0L);
taskDao.save(task); taskDao.save(task);
service.scheduleAlarm(task); service.scheduleAlarm(task);
} }
/** tests with due date */ /** tests with due date */
public void testDueDates() { public void testDueDates() {
service.setScheduler(new AlarmExpected() {
@Override
public void createAlarm(Task task, long time, int type) {
super.createAlarm(task, time, type);
assertEquals((long)task.getValue(Task.DUE_DATE), time);
assertEquals(type, ReminderService.TYPE_DUE);
}
});
// test due date in the past // test due date in the past
service.setScheduler(new NoAlarmExpected());
final Task task = new Task(); final Task task = new Task();
task.setValue(Task.TITLE, "water"); task.setValue(Task.TITLE, "water");
task.setValue(Task.DUE_DATE, DateUtilities.now() - DateUtilities.ONE_DAY); task.setValue(Task.DUE_DATE, DateUtilities.now() - DateUtilities.ONE_DAY);
@ -53,14 +64,6 @@ public class ReminderServiceTests extends DatabaseTestCase {
// test due date in the future // test due date in the future
task.setValue(Task.DUE_DATE, DateUtilities.now() + DateUtilities.ONE_DAY); task.setValue(Task.DUE_DATE, DateUtilities.now() + DateUtilities.ONE_DAY);
service.setScheduler(new AlarmExpected() {
@Override
public void createAlarm(Task task, long time, int type) {
super.createAlarm(task, time, type);
assertEquals((long)task.getValue(Task.DUE_DATE), time);
assertEquals(type, ReminderService.TYPE_DUE);
}
});
taskDao.save(task); taskDao.save(task);
assertTrue(((AlarmExpected)service.getScheduler()).alarmCreated); assertTrue(((AlarmExpected)service.getScheduler()).alarmCreated);
} }
@ -87,7 +90,15 @@ public class ReminderServiceTests extends DatabaseTestCase {
/** tests with overdue */ /** tests with overdue */
public void testOverdue() { public void testOverdue() {
// test due date in the future // test due date in the future
service.setScheduler(new NoAlarmExpected()); service.setScheduler(new AlarmExpected() {
@Override
public void createAlarm(Task task, long time, int type) {
super.createAlarm(task, time, type);
assertTrue(time > task.getValue(Task.DUE_DATE));
assertTrue(time < task.getValue(Task.DUE_DATE) + DateUtilities.ONE_DAY);
assertEquals(type, ReminderService.TYPE_OVERDUE);
}
});
final Task task = new Task(); final Task task = new Task();
task.setValue(Task.TITLE, "water"); task.setValue(Task.TITLE, "water");
task.setValue(Task.DUE_DATE, DateUtilities.now() + DateUtilities.ONE_DAY); task.setValue(Task.DUE_DATE, DateUtilities.now() + DateUtilities.ONE_DAY);
@ -100,13 +111,27 @@ public class ReminderServiceTests extends DatabaseTestCase {
@Override @Override
public void createAlarm(Task task, long time, int type) { public void createAlarm(Task task, long time, int type) {
super.createAlarm(task, time, type); super.createAlarm(task, time, type);
assertTrue(time > DateUtilities.now()); assertTrue(time > DateUtilities.now() - 1000L);
assertTrue(time < DateUtilities.now() + 2 * DateUtilities.ONE_DAY); assertTrue(time < DateUtilities.now() + 2 * DateUtilities.ONE_DAY);
assertEquals(type, ReminderService.TYPE_OVERDUE); assertEquals(type, ReminderService.TYPE_OVERDUE);
} }
}); });
taskDao.save(task); taskDao.save(task);
assertTrue(((AlarmExpected)service.getScheduler()).alarmCreated); assertTrue(((AlarmExpected)service.getScheduler()).alarmCreated);
// test due date in the past, but recently notified
task.setValue(Task.REMINDER_LAST, DateUtilities.now());
service.setScheduler(new AlarmExpected() {
@Override
public void createAlarm(Task task, long time, int type) {
super.createAlarm(task, time, type);
assertTrue(time > DateUtilities.now() + DateUtilities.ONE_HOUR);
assertTrue(time < DateUtilities.now() + DateUtilities.ONE_DAY);
assertEquals(type, ReminderService.TYPE_OVERDUE);
}
});
taskDao.save(task);
assertTrue(((AlarmExpected)service.getScheduler()).alarmCreated);
} }
/** tests with multiple */ /** tests with multiple */
@ -149,11 +174,50 @@ public class ReminderServiceTests extends DatabaseTestCase {
assertTrue(((AlarmExpected)service.getScheduler()).alarmCreated); assertTrue(((AlarmExpected)service.getScheduler()).alarmCreated);
} }
/** tests with snooze */
public void testSnoozeReminders() {
// test due date and snooze in the future
final Task task = new Task();
task.setValue(Task.TITLE, "spacemen");
task.setValue(Task.DUE_DATE, DateUtilities.now() + 5000L);
task.setValue(Task.REMINDER_FLAGS, Task.NOTIFY_AT_DEADLINE);
task.setValue(Task.REMINDER_SNOOZE, DateUtilities.now() + DateUtilities.ONE_WEEK);
service.setScheduler(new AlarmExpected() {
@Override
public void createAlarm(Task task, long time, int type) {
super.createAlarm(task, time, type);
assertTrue(time > DateUtilities.now() + DateUtilities.ONE_WEEK - 1000L);
assertTrue(time < DateUtilities.now() + DateUtilities.ONE_WEEK + 1000L);
assertEquals(type, ReminderService.TYPE_SNOOZE);
}
});
taskDao.save(task);
assertTrue(((AlarmExpected)service.getScheduler()).alarmCreated);
// snooze in the past
task.setValue(Task.REMINDER_SNOOZE, DateUtilities.now() - DateUtilities.ONE_WEEK);
service.setScheduler(new AlarmExpected() {
@Override
public void createAlarm(Task task, long time, int type) {
super.createAlarm(task, time, type);
assertTrue(time > DateUtilities.now() - 1000L);
assertTrue(time < DateUtilities.now() + 5000L);
assertEquals(type, ReminderService.TYPE_DUE);
}
});
taskDao.save(task);
assertTrue(((AlarmExpected)service.getScheduler()).alarmCreated);
}
// --- helper classes // --- helper classes
public class NoAlarmExpected implements AlarmScheduler { public class NoAlarmExpected implements AlarmScheduler {
public void createAlarm(Task task, long time, int type) { public void createAlarm(Task task, long time, int type) {
fail("created alarm, no alarm expected"); if(time == 0 || time == Long.MAX_VALUE)
return;
fail("created alarm, no alarm expected (" + type + ": " + new Date(time));
} }
} }

@ -2,7 +2,6 @@ package com.todoroo.astrid.service;
import android.test.AndroidTestCase; import android.test.AndroidTestCase;
import com.timsu.astrid.R;
import com.todoroo.andlib.service.Autowired; import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService; import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.utility.DialogUtilities; import com.todoroo.andlib.utility.DialogUtilities;
@ -40,31 +39,15 @@ public class AstridDependencyInjectorTests extends AndroidTestCase {
assertTrue(((String)helper.getObject()).length() > 0); assertTrue(((String)helper.getObject()).length() > 0);
} }
public void testWithInteger() {
Helper helper = new Helper() {
@Autowired
public Integer informationDialogTitleResource;
@Override
public Object getObject() {
return informationDialogTitleResource;
};
};
DependencyInjectionService.getInstance().inject(helper);
assertEquals(R.string.DLG_information_title, helper.getObject());
}
public void testWithClass() { public void testWithClass() {
Helper helper = new Helper() { Helper helper = new Helper() {
@Autowired @Autowired
public DialogUtilities dialogUtilities; public TaskService taskService;
@Override @Override
public Object getObject() { public Object getObject() {
return dialogUtilities; return taskService;
}; };
}; };
@ -73,17 +56,17 @@ public class AstridDependencyInjectorTests extends AndroidTestCase {
Helper helper2 = new Helper() { Helper helper2 = new Helper() {
@Autowired @Autowired
public DialogUtilities dialogUtilities; public TaskService taskService;
@Override @Override
public Object getObject() { public Object getObject() {
return dialogUtilities; return taskService;
}; };
}; };
DependencyInjectionService.getInstance().inject(helper2); DependencyInjectionService.getInstance().inject(helper2);
assertEquals(helper.getObject(), helper2.getObject()); assertTrue(helper.getObject() == helper2.getObject());
} }
} }

Loading…
Cancel
Save