Merge upstream/4.0

pull/14/head
Sam Bosley 13 years ago
commit ad7fc4ce80

@ -65,4 +65,11 @@ public final class Table extends SqlTable {
return Field.field(alias + "." + property.name);
return Field.field(name + "." + property.name);
}
@Override
public String toString() {
if(hasAlias())
return expression + " AS " + alias; //$NON-NLS-1$
return expression;
}
}

@ -31,6 +31,9 @@ public class TodorooCursor<TYPE extends AbstractModel> extends CursorWrapper {
/** Property reading visitor */
private static final CursorReadingVisitor reader = new CursorReadingVisitor();
/** Wrapped cursor */
private final Cursor cursor;
/**
* Create an <code>AstridCursor</code> from the supplied {@link Cursor}
* object.
@ -41,6 +44,7 @@ public class TodorooCursor<TYPE extends AbstractModel> extends CursorWrapper {
public TodorooCursor(Cursor cursor, Property<?>[] properties) {
super(cursor);
this.cursor = cursor;
this.properties = properties;
columnIndexCache = new WeakHashMap<String, Integer>();
}
@ -56,6 +60,13 @@ public class TodorooCursor<TYPE extends AbstractModel> extends CursorWrapper {
return (PROPERTY_TYPE)property.accept(reader, this);
}
/**
* @return underlying cursor
*/
public Cursor getCursor() {
return cursor;
}
/**
* Gets entire property list
* @return

@ -46,7 +46,7 @@ public abstract class DBObject<T extends DBObject<?>> implements Cloneable {
}
@Override
public final String toString() {
public String toString() {
if (hasAlias()) {
return alias;
}

@ -48,9 +48,10 @@ public class DialogUtilities {
* @param html
* @param title
*/
@SuppressWarnings("nls")
public static void htmlDialog(Context context, String html, int title) {
WebView webView = new WebView(context);
webView.loadData(html, "text/html", "utf-8"); //$NON-NLS-1$ //$NON-NLS-2$
webView.loadDataWithBaseURL("file:///android_asset/", html, "text/html", "utf-8", null);
webView.setBackgroundColor(0);
new AlertDialog.Builder(context)

@ -19,6 +19,7 @@ public class SortHelper {
public static final int FLAG_SHOW_COMPLETED = 1 << 1;
public static final int FLAG_SHOW_HIDDEN = 1 << 2;
public static final int FLAG_SHOW_DELETED = 1 << 3;
public static final int FLAG_DRAG_DROP = 1 << 4;
public static final int SORT_AUTO = 0;
public static final int SORT_ALPHA = 1;
@ -66,7 +67,18 @@ public class SortHelper {
return originalSql;
}
public static boolean isManualSort(int flags) {
return (flags & FLAG_DRAG_DROP) > 0;
}
public static int setManualSort(int flags, boolean status) {
flags = (flags & ~FLAG_DRAG_DROP);
if(status)
flags |= FLAG_DRAG_DROP;
return flags;
}
@SuppressWarnings("nls")
public static Order orderForSortType(int sortType) {
Order order;
switch(sortType) {
@ -100,7 +112,7 @@ public class SortHelper {
@SuppressWarnings("nls")
public static Order defaultTaskOrder() {
return Order.asc(Functions.caseStatement(Task.DUE_DATE.eq(0),
DateUtilities.now() * 2,
Functions.now() + "*2",
Task.DUE_DATE) + " + " + (2 * DateUtilities.ONE_DAY) + " * " +
Task.IMPORTANCE + " + 2*" + Task.COMPLETION_DATE);
}

@ -139,9 +139,11 @@ abstract public class SyncProviderPreferences extends TodorooPreferenceActivity
new Date(getUtilities().getLastSyncDate())));
}
} else {
status = r.getString(R.string.sync_status_errors,
long lastSyncDate = getUtilities().getLastSyncDate();
String dateString = lastSyncDate > 0 ?
DateUtilities.getDateStringWithTime(SyncProviderPreferences.this,
new Date(getUtilities().getLastSyncDate())));
new Date(lastSyncDate)) : ""; //$NON-NLS-1$
status = r.getString(R.string.sync_status_errors, dateString);
statusColor = Color.rgb(100, 100, 0);
}
preference.setOnPreferenceClickListener(new OnPreferenceClickListener() {

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.timsu.astrid"
android:versionName="4.0-beta"
android:versionCode="250">
android:versionName="4.0.1"
android:versionCode="252">
<!-- widgets, alarms, and services will break if Astrid is installed on SD card -->
<!-- android:installLocation="internalOnly"> -->
@ -102,12 +102,6 @@
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity android:name="com.todoroo.astrid.activity.AdTestActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<!-- Activity launched from ShareLink menu item -->
<activity android:name="com.todoroo.astrid.activity.ShareLinkActivity"
android:clearTaskOnLaunch="true">

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

@ -35,19 +35,24 @@ import android.widget.ImageView;
import android.widget.ListView;
import com.timsu.astrid.R;
import com.todoroo.andlib.utility.AndroidUtilities;
@SuppressWarnings("nls")
public class TouchListView extends ListView {
private ImageView mDragView;
private View mOriginalView;
private WindowManager mWindowManager;
private WindowManager.LayoutParams mWindowParams;
private int mDragPos; // which item is being dragged
private int mFirstDragPos; // where was the dragged item originally
private int mDragPoint; // at what offset inside the item did the user grab it
private int mCoordOffset; // the difference between screen coordinates and coordinates in this view
private int mDragStartX;
private float mDragStartX, mDragCurrentX;
private long mDragStartTime;
private DragListener mDragListener;
private DropListener mDropListener;
private SwipeListener mSwipeListener;
private GrabberClickListener mClickListener;
private int mUpperBound;
private int mLowerBound;
private int mHeight;
@ -63,30 +68,57 @@ public class TouchListView extends ListView {
private int mItemHeightExpanded=-1;
private int grabberId=-1;
private int dragndropBackgroundColor=0x00000000;
private Thread longPressThread;
public TouchListView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public TouchListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
public TouchListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
if (attrs!=null) {
TypedArray a=getContext()
.obtainStyledAttributes(attrs,
R.styleable.TouchListView,
0, 0);
if (attrs != null) {
TypedArray a = getContext().obtainStyledAttributes(attrs,
R.styleable.TouchListView, 0, 0);
mItemHeightNormal=a.getDimensionPixelSize(R.styleable.TouchListView_normal_height, 0);
mItemHeightExpanded=a.getDimensionPixelSize(R.styleable.TouchListView_expanded_height, mItemHeightNormal);
grabberId=a.getResourceId(R.styleable.TouchListView_grabber, -1);
dragndropBackgroundColor=a.getColor(R.styleable.TouchListView_dragndrop_background, 0x00000000);
mRemoveMode=a.getInt(R.styleable.TouchListView_remove_mode, -1);
mItemHeightNormal = a.getDimensionPixelSize(
R.styleable.TouchListView_normal_height, 0);
mItemHeightExpanded = a.getDimensionPixelSize(
R.styleable.TouchListView_expanded_height,
mItemHeightNormal);
grabberId = a.getResourceId(R.styleable.TouchListView_grabber, -1);
dragndropBackgroundColor = a.getColor(
R.styleable.TouchListView_dragndrop_background, 0x00000000);
mRemoveMode = a.getInt(R.styleable.TouchListView_remove_mode, -1);
a.recycle();
}
a.recycle();
}
}
@Override
final public void addHeaderView (View v, Object data, boolean isSelectable) {
throw new RuntimeException("Headers are not supported with TouchListView");
}
@Override
final public void addHeaderView (View v) {
throw new RuntimeException("Headers are not supported with TouchListView");
}
@Override
final public void addFooterView (View v, Object data, boolean isSelectable) {
if (mRemoveMode == SLIDE_LEFT || mRemoveMode == SLIDE_RIGHT) {
throw new RuntimeException("Footers are not supported with TouchListView in conjunction with remove_mode");
}
}
@Override
final public void addFooterView (View v) {
if (mRemoveMode == SLIDE_LEFT || mRemoveMode == SLIDE_RIGHT) {
throw new RuntimeException("Footers are not supported with TouchListView in conjunction with remove_mode");
}
}
@Override
@ -100,40 +132,59 @@ public class TouchListView extends ListView {
if (itemnum == AdapterView.INVALID_POSITION) {
break;
}
ViewGroup item = (ViewGroup) getChildAt(itemnum - getFirstVisiblePosition());
mDragPoint = y - item.getTop();
mCoordOffset = ((int)ev.getRawY()) - y;
View dragger = item.findViewById(grabberId);
Rect r = mTempRect;
// dragger.getDrawingRect(r);
r.left=dragger.getLeft();
r.right=dragger.getRight();
r.top=dragger.getTop();
r.bottom=dragger.getBottom();
if ((r.left<x) && (x<r.right)) {
item.setDrawingCacheEnabled(true);
// Create a copy of the drawing cache so that it does not get recycled
// by the framework when the list tries to clean up memory
Bitmap bitmap = Bitmap.createBitmap(item.getDrawingCache());
startDragging(bitmap, y);
mDragPos = itemnum;
mFirstDragPos = mDragPos;
mHeight = getHeight();
int touchSlop = mTouchSlop;
mUpperBound = Math.min(y - touchSlop, mHeight / 3);
mLowerBound = Math.max(y + touchSlop, mHeight * 2 /3);
mDragStartX = x;
return false;
View item = (View) getChildAt(itemnum - getFirstVisiblePosition());
if (isDraggableRow(item)) {
mDragPoint = y - item.getTop();
mCoordOffset = ((int)ev.getRawY()) - y;
View dragger = item.findViewById(grabberId);
Rect r = mTempRect;
// dragger.getDrawingRect(r);
r.left=dragger.getLeft();
r.right=dragger.getRight();
r.top=dragger.getTop();
r.bottom=dragger.getBottom();
if ((r.left<x) && (x<r.right)) {
item.setDrawingCacheEnabled(true);
// Create a copy of the drawing cache so that it does not get recycled
// by the framework when the list tries to clean up memory
Bitmap bitmap = Bitmap.createBitmap(item.getDrawingCache());
item.setDrawingCacheEnabled(false);
Rect listBounds=new Rect();
getGlobalVisibleRect(listBounds, null);
startDragging(bitmap, listBounds.left, y);
mDragPos = itemnum;
mFirstDragPos = mDragPos;
mHeight = getHeight();
mDragStartX = ev.getX();
mDragStartTime = System.currentTimeMillis();
mOriginalView = item;
int touchSlop = mTouchSlop;
mUpperBound = Math.min(y - touchSlop, mHeight / 3);
mLowerBound = Math.max(y + touchSlop, mHeight * 2 /3);
return false;
}
mDragView = null;
}
mDragView = null;
break;
}
}
return super.onInterceptTouchEvent(ev);
}
protected boolean isDraggableRow(View view) {
return(view.findViewById(grabberId)!=null);
}
/*
* pointToPosition() doesn't consider invisible views, but we
* need to, so implement a slightly different version.
@ -152,7 +203,7 @@ public class TouchListView extends ListView {
}
private int getItemForPosition(int y) {
int adjustedy = y - mDragPoint - 32;
int adjustedy = y - mDragPoint - (mItemHeightNormal/2);
int pos = myPointToPosition(0, adjustedy);
if (pos >= 0) {
if (pos <= mFirstDragPos) {
@ -180,7 +231,7 @@ public class TouchListView extends ListView {
for (int i = 0;; i++) {
View v = getChildAt(i);
if (v == null) {
if (deletion && getCount() > 0) {
if (deletion) {
// HACK force update of mItemCount
int position = getFirstVisiblePosition();
int y = getChildAt(0).getTop();
@ -194,10 +245,13 @@ public class TouchListView extends ListView {
break;
}
}
ViewGroup.LayoutParams params = v.getLayoutParams();
params.height = mItemHeightNormal;
v.setLayoutParams(params);
v.setVisibility(View.VISIBLE);
if (isDraggableRow(v)) {
ViewGroup.LayoutParams params = v.getLayoutParams();
params.height = mItemHeightNormal;
v.setLayoutParams(params);
v.setVisibility(View.VISIBLE);
}
}
}
@ -242,11 +296,18 @@ public class TouchListView extends ListView {
height = mItemHeightExpanded;
}
}
ViewGroup.LayoutParams params = vv.getLayoutParams();
params.height = height;
vv.setLayoutParams(params);
vv.setVisibility(visibility);
if (isDraggableRow(vv)) {
ViewGroup.LayoutParams params = vv.getLayoutParams();
params.height = height;
vv.setLayoutParams(params);
vv.setVisibility(visibility);
}
}
// Request re-layout since we changed the items layout
// and not doing this would cause bogus hitbox calculation
// in myPointToPosition
layoutChildren();
}
@Override
@ -261,7 +322,7 @@ public class TouchListView extends ListView {
case MotionEvent.ACTION_CANCEL:
Rect r = mTempRect;
mDragView.getDrawingRect(r);
stopDragging();
stopDragging(ev);
if (mDragPos == mFirstDragPos && ev.getX() > mDragStartX + 20) {
if (mSwipeListener!= null) {
mSwipeListener.swipeRight(mFirstDragPos);
@ -323,12 +384,12 @@ public class TouchListView extends ListView {
return super.onTouchEvent(ev);
}
private void startDragging(Bitmap bm, int y) {
stopDragging();
private void startDragging(Bitmap bm, int x, int y) {
stopDragging(null);
mWindowParams = new WindowManager.LayoutParams();
mWindowParams.gravity = Gravity.TOP;
mWindowParams.x = 0;
mWindowParams.gravity = Gravity.TOP|Gravity.LEFT;
mWindowParams.x = x;
mWindowParams.y = y - mDragPoint + mCoordOffset;
mWindowParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
@ -341,49 +402,92 @@ public class TouchListView extends ListView {
mWindowParams.windowAnimations = 0;
ImageView v = new ImageView(getContext());
// int backGroundColor = getContext().getResources().getColor(R.color.dragndrop_background);
v.setBackgroundColor(dragndropBackgroundColor);
v.setImageBitmap(bm);
mDragBitmap = bm;
mWindowManager = (WindowManager)getContext().getSystemService("window"); //$NON-NLS-1$
mWindowManager = (WindowManager)getContext().getSystemService(Context.WINDOW_SERVICE);
mWindowManager.addView(v, mWindowParams);
mDragView = v;
}
private void dragView(int x, int y) {
float alpha = 1.0f;
int width = mDragView.getWidth();
if (mRemoveMode == SLIDE_RIGHT) {
if (x > width / 2) {
alpha = ((float)(width - x)) / (width / 2);
}
mWindowParams.alpha = alpha;
if(mClickListener != null) {
longPressThread = new Thread(longPressRunnable);
longPressThread.start();
}
else if (mRemoveMode == SLIDE_LEFT) {
if (x < width / 2) {
alpha = ((float)x) / (width / 2);
}
mWindowParams.alpha = alpha;
}
mWindowParams.y = y - mDragPoint + mCoordOffset;
mWindowManager.updateViewLayout(mDragView, mWindowParams);
}
private void stopDragging() {
if (mDragView != null) {
WindowManager wm = (WindowManager)getContext().getSystemService("window"); //$NON-NLS-1$
wm.removeView(mDragView);
mDragView.setImageDrawable(null);
mDragView = null;
}
if (mDragBitmap != null) {
mDragBitmap.recycle();
mDragBitmap = null;
}
private final Runnable longPressRunnable = new Runnable() {
public void run() {
AndroidUtilities.sleepDeep(1000L);
if(Thread.currentThread().isInterrupted())
return;
if(mDragView != null && mDragPos == mFirstDragPos &&
Math.abs(mDragCurrentX - mDragStartX) < 10) {
post(new Runnable() {
public void run() {
stopDragging(null);
mClickListener.onLongClick(mOriginalView);
invalidate();
}
});
}
}
};
private void dragView(int x, int y) {
float alpha = 1.0f;
int width = mDragView.getWidth();
if (mRemoveMode == SLIDE_RIGHT) {
if (x > width / 2) {
alpha = ((float)(width - x)) / (width / 2);
}
mWindowParams.alpha = alpha;
}
else if (mRemoveMode == SLIDE_LEFT) {
if (x < width / 2) {
alpha = ((float)x) / (width / 2);
}
mWindowParams.alpha = alpha;
}
mWindowParams.y = y - mDragPoint + mCoordOffset;
mWindowManager.updateViewLayout(mDragView, mWindowParams);
mDragCurrentX = x;
}
private void stopDragging(MotionEvent ev) {
if (mDragBitmap != null) {
mDragBitmap.recycle();
mDragBitmap = null;
}
if (mDragView != null) {
WindowManager wm = (WindowManager) getContext().getSystemService(
Context.WINDOW_SERVICE);
wm.removeView(mDragView);
mDragView.setImageDrawable(null);
mDragView = null;
if (ev != null && mClickListener != null) {
// detect press & long press case
if(mDragPos == mFirstDragPos &&
Math.abs(mDragCurrentX - mDragStartX) < 10) {
long pressTime = System.currentTimeMillis() - mDragStartTime;
if(pressTime < 1000)
mClickListener.onClick(mOriginalView);
else
mClickListener.onLongClick(mOriginalView);
}
}
}
if(longPressThread != null) {
longPressThread.interrupt();
longPressThread = null;
}
}
public void setDragListener(DragListener l) {
mDragListener = l;
}
@ -396,6 +500,19 @@ public class TouchListView extends ListView {
mSwipeListener = l;
}
public void setClickListener(GrabberClickListener listener) {
this.mClickListener = listener;
}
public void setDragndropBackgroundColor(int color) {
this.dragndropBackgroundColor = color;
}
public interface GrabberClickListener {
public void onClick(View v);
public void onLongClick(View v);
}
public interface DragListener {
void drag(int from, int to);
}

@ -51,6 +51,7 @@ import android.view.WindowManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import com.facebook.android.AsyncFacebookRunner;
@ -333,8 +334,11 @@ public class ActFmLoginActivity extends FragmentActivity implements AuthListener
body.getChildAt(body.indexOfChild(password) - 1).setVisibility(View.GONE);
password.setVisibility(View.GONE);
ScrollView bodyScroll = new ScrollView(ActFmLoginActivity.this);
bodyScroll.addView(body);
dialog.set(new AlertDialog.Builder(ActFmLoginActivity.this).setView(
body).setIcon(R.drawable.icon_32).setTitle(
bodyScroll).setIcon(R.drawable.icon_32).setTitle(
R.string.actfm_ALA_signup_title).setPositiveButton(
android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
@ -439,7 +443,7 @@ public class ActFmLoginActivity extends FragmentActivity implements AuthListener
try {
json = Util.parseJson(response);
String firstName = json.getString("first_name"); //$NON-NLS-1$
String lastName = json.getString("last_name");
String lastName = json.getString("last_name"); //$NON-NLS-1$
String email = json.getString("email"); //$NON-NLS-1$
authenticate(email, firstName, lastName, ActFmInvoker.PROVIDER_FACEBOOK,

@ -1,7 +1,5 @@
package com.todoroo.astrid.actfm;
import greendroid.widget.AsyncImageView;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
@ -50,6 +48,7 @@ import com.todoroo.astrid.actfm.sync.ActFmSyncService;
import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.data.TagData;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.helper.AsyncImageView;
import com.todoroo.astrid.service.AstridDependencyInjector;
import com.todoroo.astrid.service.MetadataService;
import com.todoroo.astrid.service.TagDataService;
@ -167,7 +166,7 @@ public class EditPeopleControlSet extends PopupControlSet {
cbFacebook = (CheckBox) getSharedWithView().findViewById(R.id.checkbox_facebook);
cbTwitter = (CheckBox) getSharedWithView().findViewById(R.id.checkbox_twitter);
sharedWithContainer.addPerson(""); //$NON-NLS-1$
sharedWithContainer.addPerson();
setUpListeners();
}
@ -206,7 +205,7 @@ public class EditPeopleControlSet extends PopupControlSet {
if(people != null) {
for(int i = 0; i < people.length(); i++) {
String person = people.getString(i);
TextView textView = sharedWithContainer.addPerson(person);
TextView textView = sharedWithContainer.addPerson(person, "");
textView.setEnabled(false);
sharedPeople.add(PeopleContainer.createUserJson(textView));
}
@ -250,6 +249,7 @@ public class EditPeopleControlSet extends PopupControlSet {
}
}
@SuppressWarnings("nls")
private static void addMembersFromTagData(TagData tagData, String tag, ArrayList<JSONObject> sharedPeople, ArrayList<JSONObject> collaborators) throws JSONException {
JSONArray members = new JSONArray(tagData.getValue(TagData.MEMBERS));
if (tag == null)
@ -318,13 +318,13 @@ public class EditPeopleControlSet extends PopupControlSet {
}
@SuppressWarnings("nls")
private void buildAssignedToSpinner(Task model, ArrayList<JSONObject> sharedPeople) throws JSONException {
private void buildAssignedToSpinner(Task t, ArrayList<JSONObject> sharedPeople) throws JSONException {
HashSet<Long> userIds = new HashSet<Long>();
HashSet<String> emails = new HashSet<String>();
HashMap<String, AssignedToUser> names = new HashMap<String, AssignedToUser>();
if(model.getValue(Task.USER_ID) > 0) {
JSONObject user = new JSONObject(model.getValue(Task.USER));
if(t.getValue(Task.USER_ID) > 0) {
JSONObject user = new JSONObject(t.getValue(Task.USER));
sharedPeople.add(0, user);
}
@ -332,8 +332,8 @@ public class EditPeopleControlSet extends PopupControlSet {
myself.put("id", Task.USER_ID_SELF);
sharedPeople.add(0, myself);
boolean hasTags = model.getTransitory("tags") != null &&
((HashSet<String>)model.getTransitory("tags")).size() > 0;
boolean hasTags = t.getTransitory("tags") != null &&
((HashSet<String>)t.getTransitory("tags")).size() > 0;
if (actFmPreferenceService.isLoggedIn() && hasTags) {
JSONObject unassigned = new JSONObject();
unassigned.put("id", Task.USER_ID_UNASSIGNED);
@ -381,7 +381,7 @@ public class EditPeopleControlSet extends PopupControlSet {
names.put(name, atu);
}
String assignedStr = model.getValue(Task.USER);
String assignedStr = t.getValue(Task.USER);
int assignedIndex = 0;
if (!TextUtils.isEmpty(assignedStr)) {
JSONObject assigned = new JSONObject(assignedStr);
@ -527,13 +527,13 @@ public class EditPeopleControlSet extends PopupControlSet {
}
@Override
public String writeToModel(Task model) {
public String writeToModel(Task t) {
// do nothing, we use a separate method
return null;
}
@Override
protected String writeToModelAfterInitialized(Task task) {
protected String writeToModelAfterInitialized(Task t) {
// Nothing, we don't lazy load this control set yet
return null;
}
@ -681,7 +681,7 @@ public class EditPeopleControlSet extends PopupControlSet {
private void makePrivateTask() {
sharedWithContainer.removeAllViews();
sharedWithContainer.addPerson(""); //$NON-NLS-1$
sharedWithContainer.addPerson();
assignToMe();
}

@ -1,12 +1,9 @@
package com.todoroo.astrid.actfm;
import greendroid.widget.AsyncImageView;
import java.io.IOException;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.Activity;
import android.content.Context;
@ -24,8 +21,10 @@ import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
@ -34,6 +33,7 @@ import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.service.ExceptionService;
import com.todoroo.andlib.utility.AndroidUtilities;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.andlib.utility.DialogUtilities;
import com.todoroo.andlib.utility.Preferences;
import com.todoroo.astrid.actfm.ActFmCameraModule.CameraResultCallback;
@ -41,8 +41,12 @@ import com.todoroo.astrid.actfm.sync.ActFmPreferenceService;
import com.todoroo.astrid.actfm.sync.ActFmSyncService;
import com.todoroo.astrid.activity.FilterListFragment;
import com.todoroo.astrid.activity.ShortcutActivity;
import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.core.PluginServices;
import com.todoroo.astrid.data.TagData;
import com.todoroo.astrid.helper.AsyncImageView;
import com.todoroo.astrid.helper.ImageDiskCache;
import com.todoroo.astrid.service.StatisticsConstants;
import com.todoroo.astrid.service.StatisticsService;
import com.todoroo.astrid.service.TagDataService;
@ -61,7 +65,7 @@ public class TagSettingsActivity extends FragmentActivity {
private static final int MENU_SAVE_ID = R.string.TEA_menu_save;
private static final int MENU_DISCARD_ID = R.string.TEA_menu_discard;
protected static final int REQUEST_ACTFM_LOGIN = 3;
public static final int REQUEST_ACTFM_LOGIN = 3;
private static final String MEMBERS_IN_PROGRESS = "members"; //$NON-NLS-1$
@ -82,12 +86,14 @@ public class TagSettingsActivity extends FragmentActivity {
private EditText tagDescription;
private CheckBox isSilent;
private Bitmap setBitmap;
private final ImageDiskCache imageCache;
private boolean isNewTag = false;
private boolean isDialog;
public TagSettingsActivity() {
DependencyInjectionService.getInstance().inject(this);
imageCache = ImageDiskCache.getInstance();
}
@Override
@ -170,13 +176,25 @@ public class TagSettingsActivity extends FragmentActivity {
isSilent = (CheckBox) findViewById(R.id.tag_silenced);
isSilent.setChecked(tagData.getFlag(TagData.FLAGS, TagData.FLAG_SILENT));
Button leaveListButton = (Button) findViewById(R.id.leave_list);
leaveListButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
showDeleteDialog(tagData);
}
});
if (isNewTag) {
leaveListButton.setVisibility(View.GONE);
}
else if (tagData.getValue(TagData.MEMBER_COUNT) > 0) {
leaveListButton.setText(getString(R.string.tag_leave_button));
}
if(actFmPreferenceService.isLoggedIn()) {
picture.setVisibility(View.VISIBLE);
picture.setDefaultImageResource(TagService.getDefaultImageIDForTag(tagData.getValue(TagData.NAME)));
findViewById(R.id.picture_label).setVisibility(View.VISIBLE);
findViewById(R.id.listSettingsMore).setVisibility(View.VISIBLE);
findViewById(R.id.tag_silenced_container).setVisibility(View.VISIBLE);
}
picture.setDefaultImageResource(TagService.getDefaultImageIDForTag(tagData.getValue(TagData.NAME)));
picture.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
@ -187,12 +205,13 @@ public class TagSettingsActivity extends FragmentActivity {
if (isNewTag) {
findViewById(R.id.create_shortcut_container).setVisibility(View.GONE);
} else {
findViewById(R.id.create_shortcut).setOnClickListener(new OnClickListener() {
findViewById(R.id.create_shortcut_container).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (filter == null) {
filter = TagFilterExposer.filterFromTagData(TagSettingsActivity.this, tagData);
}
filter.listingIcon = picture.getImageBitmap();
FilterListFragment.showCreateShortcutDialog(TagSettingsActivity.this, ShortcutActivity.createIntent(filter), filter);
}
});
@ -201,6 +220,7 @@ public class TagSettingsActivity extends FragmentActivity {
refreshSettingsPage();
}
@SuppressWarnings("nls")
private void saveSettings() {
String oldName = tagData.getValue(TagData.NAME);
String newName = tagName.getText().toString().trim();
@ -268,14 +288,14 @@ public class TagSettingsActivity extends FragmentActivity {
public void onClick(DialogInterface d, int which) {
tagMembers.removeAllViews();
tagMembers.addPerson(""); //$NON-NLS-1$
tagMembers.addPerson("", ""); //$NON-NLS-1$
}
};
DialogUtilities.okCancelCustomDialog(TagSettingsActivity.this, getString(R.string.actfm_EPA_login_button),
getString(R.string.actfm_TVA_login_to_share), R.string.actfm_EPA_login_button,
R.string.actfm_EPA_dont_share_button, android.R.drawable.ic_dialog_alert,
okListener, cancelListener);
Toast.makeText(this, R.string.tag_list_saved, Toast.LENGTH_LONG).show();
Toast.makeText(this, R.string.tag_list_saved, Toast.LENGTH_LONG).show();
return;
@ -317,6 +337,7 @@ public class TagSettingsActivity extends FragmentActivity {
actFmSyncService.pushTagDataOnSave(tagData, tagData.getMergedValues());
if(setBitmap != null && tagData.getValue(TagData.REMOTE_ID) > 0)
uploadTagPicture(setBitmap);
runOnUiThread(loadTag);
}
}).start();
@ -334,6 +355,18 @@ public class TagSettingsActivity extends FragmentActivity {
finish();
}
private void saveTagPictureLocally(Bitmap bitmap) {
if (bitmap == null) return;
try {
String tagPicture = ImageDiskCache.getPictureHash(tagData);
imageCache.put(tagPicture, bitmap);
tagData.setValue(TagData.PICTURE, tagPicture);
}
catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void finish() {
finishWithAnimation(!isDialog);
@ -346,7 +379,6 @@ public class TagSettingsActivity extends FragmentActivity {
}
}
@SuppressWarnings("nls")
private void refreshSettingsPage() {
tagName.setText(tagData.getValue(TagData.NAME));
ActionBar ab = getSupportActionBar();
@ -356,30 +388,19 @@ public class TagSettingsActivity extends FragmentActivity {
if (isNewTag) {
titleView.setText(getString(R.string.tag_new_list));
} else {
titleView.setText(getString(R.string.tag_settings_title, tagData.getValue(TagData.NAME)));
titleView.setText(getString(R.string.tag_settings_title));
}
} else {
if (isNewTag) {
setTitle(getString(R.string.tag_new_list));
} else {
setTitle(getString(R.string.tag_settings_title, tagData.getValue(TagData.NAME)));
setTitle(getString(R.string.tag_settings_title));
}
}
picture.setUrl(tagData.getValue(TagData.PICTURE));
TextView ownerLabel = (TextView) findViewById(R.id.tag_owner);
try {
if(tagData.getFlag(TagData.FLAGS, TagData.FLAG_EMERGENT)) {
ownerLabel.setText(String.format("<%s>", getString(R.string.actfm_TVA_tag_owner_none)));
} else if(tagData.getValue(TagData.USER_ID) == 0) {
ownerLabel.setText(Preferences.getStringValue(ActFmPreferenceService.PREF_NAME));
} else {
JSONObject owner = new JSONObject(tagData.getValue(TagData.USER));
ownerLabel.setText(owner.getString("name"));
}
} catch (JSONException e) {
Log.e("tag-view-activity", "json error refresh owner", e);
ownerLabel.setText("<error>");
if (!isNewTag) {
ImageView shortcut = (ImageView) findViewById(R.id.create_shortcut);
shortcut.setImageBitmap(FilterListFragment.superImposeListIcon(this, picture.getImageBitmap(), tagData.getValue(TagData.NAME)));
}
String peopleJson = tagData.getValue(TagData.MEMBERS);
@ -401,7 +422,7 @@ public class TagSettingsActivity extends FragmentActivity {
}
}
tagMembers.addPerson(""); //$NON-NLS-1$
tagMembers.addPerson("", ""); //$NON-NLS-1$
}
private void uploadTagPicture(final Bitmap bitmap) {
@ -410,6 +431,10 @@ public class TagSettingsActivity extends FragmentActivity {
public void run() {
try {
String url = actFmSyncService.setTagPicture(tagData.getValue(TagData.REMOTE_ID), bitmap);
if (TextUtils.isEmpty(url)) return;
if (imageCache.contains(tagData.getValue(TagData.PICTURE))) {
imageCache.move(tagData.getValue(TagData.PICTURE), url);
}
tagData.setValue(TagData.PICTURE, url);
Flags.set(Flags.ACTFM_SUPPRESS_SYNC);
tagDataService.save(tagData);
@ -438,6 +463,7 @@ public class TagSettingsActivity extends FragmentActivity {
setBitmap = bitmap;
if(tagData.getValue(TagData.REMOTE_ID) > 0)
uploadTagPicture(bitmap);
saveTagPictureLocally(bitmap);
}
};
if (ActFmCameraModule.activityResult(this, requestCode, resultCode, data, callback)) {
@ -478,10 +504,42 @@ public class TagSettingsActivity extends FragmentActivity {
return super.onOptionsItemSelected(item);
}
protected void showDeleteDialog(TagData td) {
if(td == null)
return;
int string;
if (td.getValue(TagData.MEMBER_COUNT) > 0)
string = R.string.DLG_leave_this_shared_tag_question;
else
string = R.string.DLG_delete_this_tag_question;
DialogUtilities.okCancelDialog(this, getString(string, td.getValue(TagData.NAME)),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
deleteTag();
}
}, null );
}
protected boolean deleteTag() {
tagDataService.delete(tagData.getId());
if(tagData != null) {
tagData.setValue(TagData.DELETION_DATE, DateUtilities.now());
PluginServices.getTagDataService().save(tagData);
}
Intent tagDeleted = new Intent(AstridApiConstants.BROADCAST_EVENT_TAG_DELETED);
tagDeleted.putExtra(TagViewFragment.EXTRA_TAG_NAME, tagData.getValue(TagData.NAME));
tagDeleted.putExtra(TagFilterExposer.TAG_SQL, TagFilterExposer.SHOW_ACTIVE_TASKS);
this.finish();
sendBroadcast(tagDeleted);
return true;
}
}

@ -1,6 +1,5 @@
package com.todoroo.astrid.actfm;
import greendroid.widget.AsyncImageView;
import android.app.Activity;
import android.content.Intent;
import android.database.Cursor;
@ -19,6 +18,7 @@ import android.view.LayoutInflater;
import android.view.MenuInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
@ -29,7 +29,6 @@ import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
import com.timsu.astrid.R;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.utility.AndroidUtilities;
@ -45,6 +44,7 @@ import com.todoroo.astrid.adapter.UpdateAdapter;
import com.todoroo.astrid.dao.UpdateDao;
import com.todoroo.astrid.data.TagData;
import com.todoroo.astrid.data.Update;
import com.todoroo.astrid.helper.AsyncImageView;
import com.todoroo.astrid.helper.ImageDiskCache;
import com.todoroo.astrid.helper.ProgressBarSyncResultCallback;
import com.todoroo.astrid.service.StatisticsConstants;
@ -128,7 +128,8 @@ public class TagUpdatesFragment extends ListFragment {
protected void setUpUpdateList() {
if (getActivity() instanceof TagUpdatesActivity) {
ActionBar ab = ((AstridActivity) getActivity()).getSupportActionBar();
String title = (tagData == null) ? getString(R.string.TLA_all_activity) : getString(R.string.tag_updates_title, tagData.getValue(TagData.NAME));
String title = (tagData == null) ? getString(R.string.TLA_all_activity) :
getString(R.string.tag_updates_title, tagData.getValue(TagData.NAME));
((TextView) ab.getCustomView().findViewById(R.id.title)).setText(title);
}
@ -193,22 +194,49 @@ public class TagUpdatesFragment extends ListFragment {
private void refreshUpdatesList() {
Cursor cursor = null;
ListView listView = ((ListView) getView().findViewById(android.R.id.list));
if(updateAdapter == null) {
TodorooCursor<Update> currentCursor = tagDataService.getUpdates(tagData);
getActivity().startManagingCursor(currentCursor);
cursor = tagDataService.getUpdates(tagData);
getActivity().startManagingCursor(cursor);
String fromUpdateClass = (tagData == null) ? UpdateAdapter.FROM_RECENT_ACTIVITY_VIEW : UpdateAdapter.FROM_TAG_VIEW;
updateAdapter = new UpdateAdapter(this, R.layout.update_adapter_row,
currentCursor, false, null, fromUpdateClass);
ListView listView = ((ListView) getView().findViewById(android.R.id.list));
cursor, false, fromUpdateClass);
addHeaderToListView(listView);
listView.setAdapter(updateAdapter);
} else {
Cursor cursor = updateAdapter.getCursor();
cursor = updateAdapter.getCursor();
cursor.requery();
getActivity().startManagingCursor(cursor);
populateListHeader(listHeader);
}
View activityContainer = getView().findViewById(R.id.no_activity_container);
if (cursor.getCount() == 0) {
activityContainer.setVisibility(View.VISIBLE);
TextView textView = (TextView)activityContainer.findViewById(R.id.no_activity_message);
if(actFmPreferenceService.isLoggedIn()) {
textView.setText(getActivity().getString(R.string.ENA_no_comments));
}
else {
textView.setText(getActivity().getString(R.string.UpS_no_activity_log_in));
activityContainer.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
startActivityForResult(new Intent(getActivity(), ActFmLoginActivity.class),
TagSettingsActivity.REQUEST_ACTFM_LOGIN);
}
});
}
listView.setVisibility(View.GONE);
}
else {
activityContainer.setVisibility(View.GONE);
listView.setVisibility(View.VISIBLE);
}
if (getActivity() instanceof TagUpdatesActivity)
setLastViewed();
}
@ -308,7 +336,7 @@ public class TagUpdatesFragment extends ListFragment {
private String getPictureHashForUpdate(Update u) {
String s = u.getValue(Update.TASK) + "" + u.getValue(Update.CREATION_DATE);
String s = u.getValue(Update.TASK).toString() + u.getValue(Update.CREATION_DATE);
return s;
}
@ -321,6 +349,7 @@ public class TagUpdatesFragment extends ListFragment {
update.setValue(Update.TAGS, "," + tagData.getValue(TagData.REMOTE_ID) + ",");
update.setValue(Update.TAGS_LOCAL, "," + tagData.getId() + ",");
update.setValue(Update.CREATION_DATE, DateUtilities.now());
update.setValue(Update.TARGET_NAME, tagData.getValue(TagData.NAME));
if (picture != null) {
update.setValue(Update.PICTURE, Update.PICTURE_LOADING);
try {

@ -1,6 +1,6 @@
package com.todoroo.astrid.actfm;
import greendroid.widget.AsyncImageView;
import com.todoroo.astrid.helper.AsyncImageView;
import org.json.JSONArray;
import org.json.JSONException;
@ -8,6 +8,7 @@ import org.json.JSONObject;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@ -39,11 +40,13 @@ import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.andlib.utility.Preferences;
import com.todoroo.astrid.actfm.sync.ActFmPreferenceService;
import com.todoroo.astrid.actfm.sync.ActFmSyncService;
import com.todoroo.astrid.activity.AstridActivity;
import com.todoroo.astrid.activity.FilterListFragment;
import com.todoroo.astrid.activity.TaskListActivity;
import com.todoroo.astrid.activity.TaskListFragment;
import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.api.FilterWithCustomIntent;
import com.todoroo.astrid.core.SortHelper;
import com.todoroo.astrid.dao.TaskDao.TaskCriteria;
import com.todoroo.astrid.data.TagData;
@ -52,6 +55,7 @@ import com.todoroo.astrid.data.Update;
import com.todoroo.astrid.helper.ProgressBarSyncResultCallback;
import com.todoroo.astrid.service.SyncV2Service;
import com.todoroo.astrid.service.TagDataService;
import com.todoroo.astrid.subtasks.SubtasksTagListFragment;
import com.todoroo.astrid.tags.TagFilterExposer;
import com.todoroo.astrid.tags.TagService.Tag;
import com.todoroo.astrid.welcome.HelpInfoPopover;
@ -73,7 +77,7 @@ public class TagViewFragment extends TaskListFragment {
public static final String TOKEN_START_ACTIVITY = "startActivity"; //$NON-NLS-1$
private TagData tagData;
protected TagData tagData;
@Autowired TagDataService tagDataService;
@ -83,7 +87,7 @@ public class TagViewFragment extends TaskListFragment {
@Autowired SyncV2Service syncService;
private View taskListView;
protected View taskListView;
private boolean dataLoaded = false;
@ -196,6 +200,7 @@ public class TagViewFragment extends TaskListFragment {
cursor.close();
}
postLoadTagData();
setUpMembersGallery();
super.onNewIntent(intent);
@ -206,6 +211,10 @@ public class TagViewFragment extends TaskListFragment {
}
}
protected void postLoadTagData() {
// stub
}
@Override
public TagData getActiveTagData() {
return tagData;
@ -483,6 +492,25 @@ public class TagViewFragment extends TaskListFragment {
return super.onOptionsItemSelected(item);
}
@Override
protected boolean hasDraggableOption() {
return true;
}
@Override
protected void toggleDragDrop(boolean newState) {
Filter newFilter = TagFilterExposer.filterFromTagData(getActivity(), tagData);
if(newState)
((FilterWithCustomIntent)newFilter).customTaskList =
new ComponentName(getActivity(), SubtasksTagListFragment.class);
else
((FilterWithCustomIntent)newFilter).customTaskList =
new ComponentName(getActivity(), TagViewFragment.class);
((AstridActivity)getActivity()).onFilterItemClicked(newFilter);
}
@Override
protected void refresh() {
setUpMembersGallery();

@ -47,6 +47,7 @@ import com.todoroo.andlib.utility.Preferences;
import com.todoroo.astrid.dao.MetadataDao;
import com.todoroo.astrid.dao.TagDataDao;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.dao.TaskDao.TaskCriteria;
import com.todoroo.astrid.dao.UpdateDao;
import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.data.MetadataApiDao.MetadataCriteria;
@ -310,11 +311,11 @@ public final class ActFmSyncService {
long creationDate;
if (task.containsValue(Task.CREATION_DATE)) {
creationDate = task.getValue(Task.CREATION_DATE);
creationDate = task.getValue(Task.CREATION_DATE) / 1000L; // In seconds
} else {
if (taskForRemote == null)
return;
creationDate = taskForRemote.getValue(Task.CREATION_DATE);
creationDate = taskForRemote.getValue(Task.CREATION_DATE) / 1000L; // In seconds
}
boolean newlyCreated = remoteId == 0;
@ -414,13 +415,14 @@ public final class ActFmSyncService {
if(params.size() == 0 || !checkForToken())
return;
params.add("created_at"); params.add(creationDate);
if(!newlyCreated) {
params.add("id"); params.add(remoteId);
} else if(!values.containsKey(Task.TITLE.name)) {
pushTask(task.getId());
return;
} else {
params.add("created_at"); params.add(creationDate);
}
try {
@ -724,6 +726,7 @@ public final class ActFmSyncService {
Task remote = new Task();
ArrayList<Metadata> metadata = new ArrayList<Metadata>();
HashSet<Long> ids = new HashSet<Long>(list.length());
for(int i = 0; i < list.length(); i++) {
JSONObject item = list.getJSONObject(i);
readIds(locals, item, remote);
@ -742,9 +745,17 @@ public final class ActFmSyncService {
Flags.set(Flags.ACTFM_SUPPRESS_SYNC);
taskService.save(remote);
ids.add(remote.getId());
metadataService.synchronizeMetadata(remote.getId(), metadata, MetadataCriteria.withKey(TagService.KEY));
remote.clear();
}
if(manual) {
Long[] localIds = ids.toArray(new Long[ids.size()]);
taskService.deleteWhere(Criterion.and(TaskCriteria.activeAndVisible(),
Task.REMOTE_ID.isNotNull(),
Criterion.not(Task.ID.in(localIds))));
}
}
@Override
@ -775,6 +786,7 @@ public final class ActFmSyncService {
Task remote = new Task();
ArrayList<Metadata> metadata = new ArrayList<Metadata>();
HashSet<Long> ids = new HashSet<Long>(list.length());
for(int i = 0; i < list.length(); i++) {
JSONObject item = list.getJSONObject(i);
@ -788,16 +800,20 @@ public final class ActFmSyncService {
StatisticsService.reportEvent(StatisticsConstants.ACTFM_TASK_COMPLETED);
}
Flags.set(Flags.ACTFM_SUPPRESS_SYNC);
taskService.save(remote);
ids.add(remote.getId());
metadataService.synchronizeMetadata(remote.getId(), metadata, MetadataCriteria.withKey(TagService.KEY));
remote.clear();
}
if(manual) {
for(Long localId : locals.values())
taskDao.delete(localId);
Long[] localIds = ids.toArray(new Long[ids.size()]);
taskService.deleteWhere(Criterion.and(
TagService.memberOfTagData(tagData.getValue(TagData.REMOTE_ID)),
TaskCriteria.activeAndVisible(),
Task.REMOTE_ID.isNotNull(),
Criterion.not(Task.ID.in(localIds))));
}
}
@ -854,9 +870,6 @@ public final class ActFmSyncService {
}
private void pushQueuedUpdates(TagData tagData) {
Criterion criterion = null;
@ -1053,8 +1066,6 @@ public final class ActFmSyncService {
}
/** Call sync method */
private void invokeFetchList(final String model, final boolean manual, final SyncExceptionHandler handler,
final ListItemProcessor<?> processor, final Runnable done, final String lastSyncKey,
@ -1200,7 +1211,11 @@ public final class ActFmSyncService {
public static void taskFromJson(JSONObject json, Task model, ArrayList<Metadata> metadata) throws JSONException {
metadata.clear();
model.clearValue(Task.REMOTE_ID);
model.setValue(Task.REMOTE_ID, json.getLong("id"));
long remoteId = json.getLong("id");
if (remoteId == 0)
model.setValue(Task.REMOTE_ID, null);
else
model.setValue(Task.REMOTE_ID, remoteId);
readUser(json.getJSONObject("user"), model, Task.USER_ID, Task.USER);
readUser(json.getJSONObject("creator"), model, Task.CREATOR_ID, null);
model.setValue(Task.TITLE, json.getString("title"));

@ -11,8 +11,6 @@ import java.util.concurrent.atomic.AtomicInteger;
import org.json.JSONException;
import android.graphics.Bitmap;
import com.timsu.astrid.C2DMReceiver;
import com.timsu.astrid.R;
import com.todoroo.andlib.data.TodorooCursor;
@ -25,8 +23,6 @@ import com.todoroo.astrid.dao.TaskDao.TaskCriteria;
import com.todoroo.astrid.data.RemoteModel;
import com.todoroo.astrid.data.TagData;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.data.Update;
import com.todoroo.astrid.helper.ImageDiskCache;
import com.todoroo.astrid.service.AstridDependencyInjector;
import com.todoroo.astrid.service.TagDataService;
import com.todoroo.astrid.service.TaskService;

@ -216,12 +216,16 @@ public class TasksXmlExporter {
}
private final XmlWritingPropertyVisitor xmlWritingVisitor = new XmlWritingPropertyVisitor();
public static final String XML_NULL = "null"; //$NON-NLS-1$
public class XmlWritingPropertyVisitor implements PropertyVisitor<Void, AbstractModel> {
private class XmlWritingPropertyVisitor implements PropertyVisitor<Void, AbstractModel> {
@Override
public Void visitInteger(Property<Integer> property, AbstractModel data) {
try {
xml.attribute(null, property.name, data.getValue(property).toString());
Integer value = data.getValue(property);
String valueString = (value == null) ? XML_NULL : value.toString();
xml.attribute(null, property.name, valueString);
} catch (UnsupportedOperationException e) {
// didn't read this value, do nothing
} catch (IllegalArgumentException e) {
@ -237,7 +241,9 @@ public class TasksXmlExporter {
@Override
public Void visitLong(Property<Long> property, AbstractModel data) {
try {
xml.attribute(null, property.name, data.getValue(property).toString());
Long value = data.getValue(property);
String valueString = (value == null) ? XML_NULL : value.toString();
xml.attribute(null, property.name, valueString);
} catch (UnsupportedOperationException e) {
// didn't read this value, do nothing
} catch (IllegalArgumentException e) {
@ -253,7 +259,9 @@ public class TasksXmlExporter {
@Override
public Void visitDouble(Property<Double> property, AbstractModel data) {
try {
xml.attribute(null, property.name, data.getValue(property).toString());
Double value = data.getValue(property);
String valueString = (value == null) ? XML_NULL : value.toString();
xml.attribute(null, property.name, valueString);
} catch (UnsupportedOperationException e) {
// didn't read this value, do nothing
} catch (IllegalArgumentException e) {

@ -296,12 +296,14 @@ public class TasksXmlImporter {
private final XmlReadingPropertyVisitor xmlReadingVisitor = new XmlReadingPropertyVisitor();
private class XmlReadingPropertyVisitor implements PropertyVisitor<Void, AbstractModel> {
@Override
public Void visitInteger(Property<Integer> property,
AbstractModel data) {
String value = xpp.getAttributeValue(null, property.name);
if(value != null)
data.setValue(property, Integer.parseInt(value));
data.setValue(property, TasksXmlExporter.XML_NULL.equals(value) ?
null : Integer.parseInt(value));
return null;
}
@ -309,7 +311,8 @@ public class TasksXmlImporter {
public Void visitLong(Property<Long> property, AbstractModel data) {
String value = xpp.getAttributeValue(null, property.name);
if(value != null)
data.setValue(property, Long.parseLong(value));
data.setValue(property, TasksXmlExporter.XML_NULL.equals(value) ?
null : Long.parseLong(value));
return null;
}
@ -318,7 +321,8 @@ public class TasksXmlImporter {
AbstractModel data) {
String value = xpp.getAttributeValue(null, property.name);
if(value != null)
data.setValue(property, Double.parseDouble(value));
data.setValue(property, TasksXmlExporter.XML_NULL.equals(value) ?
null : Double.parseDouble(value));
return null;
}

@ -48,7 +48,6 @@ public final class CoreFilterExposer extends BroadcastReceiver implements Astrid
// core filters
Filter inbox = buildInboxFilter(r);
// transmit filter list
FilterListItem[] list = new FilterListItem[1];
list[0] = inbox;
@ -71,6 +70,21 @@ public final class CoreFilterExposer extends BroadcastReceiver implements Astrid
return inbox;
}
/**
* Is this the inbox?
* @param filter
* @return
*/
public static boolean isInbox(Filter filter) {
String title = ContextManager.getString(R.string.BFE_Active);
if(filter.listingIcon != null &&
filter.listingTitle != null &&
filter.listingTitle.startsWith(title) &&
title.equals(filter.title))
return true;
return false;
}
@Override
public FilterListItem[] getFilters() {
if (ContextManager.getContext() == null || ContextManager.getContext().getResources() == null)

@ -171,7 +171,6 @@ public class Calendars {
// Fetch the current setting. Invalid calendar id will
// be changed to default value.
String currentSetting = Preferences.getStringValue(R.string.gcal_p_default);
boolean calendarWasSelected = currentSetting != null && !currentSetting.equals("-1");
int currentSettingIndex = -1;
@ -189,7 +188,7 @@ public class Calendars {
listPreference.setValueIndex(0);
listPreference.setEnabled(true);
if (calendars == null || calendars.calendarIds.length == 0 || calendars.calendars.length == 0) {
if (calendars.calendarIds.length == 0 || calendars.calendars.length == 0) {
// Something went wrong when querying calendars
// Leave the preference at disabled.
return;

@ -55,6 +55,7 @@ public class GCalHelper {
return createTaskEvent(task, cr, values, true);
}
@SuppressWarnings("nls")
public static Uri createTaskEvent(Task task, ContentResolver cr, ContentValues values, boolean deleteEventIfExists) {
String eventuri = getTaskEventUri(task);
@ -88,13 +89,15 @@ public class GCalHelper {
return eventUri;
} catch (Exception e) {
// TODO FIX ME
// Log.e("astrid-gcal", "error-creating-calendar-event", e); //$NON-NLS-1$ //$NON-NLS-2$
// won't work on emulator
Log.v("astrid-gcal",
"error-creating-calendar-event", e);
}
return null;
}
@SuppressWarnings("nls")
public static boolean deleteTaskEvent(Task task) {
boolean eventDeleted = false;
String uri;
@ -159,10 +162,11 @@ public class GCalHelper {
values.put("dtend", tzCorrectedDueDateNow);
values.put("allDay", "1");
}
adjustDateForIcs(task, values);
adjustDateForIcs(values);
}
private static void adjustDateForIcs(Task task, ContentValues values) {
@SuppressWarnings("nls")
private static void adjustDateForIcs(ContentValues values) {
if (AndroidUtilities.getSdkVersion() >= 14) {
if ("1".equals(values.get("allDay"))) {
values.put("eventTimezone", Time.TIMEZONE_UTC);

@ -7,54 +7,42 @@ import android.support.v4.view.Menu;
import android.support.v4.view.MenuItem;
import android.view.MenuInflater;
import com.commonsware.cwac.tlv.TouchListView;
import com.commonsware.cwac.tlv.TouchListView.DropListener;
import com.commonsware.cwac.tlv.TouchListView.SwipeListener;
import com.timsu.astrid.R;
import com.todoroo.andlib.data.Property;
import com.todoroo.andlib.data.Property.IntegerProperty;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.andlib.utility.DialogUtilities;
import com.todoroo.andlib.utility.Preferences;
import com.todoroo.astrid.activity.DraggableTaskListFragment;
import com.todoroo.astrid.adapter.TaskAdapter.OnCompletedTaskListener;
import com.todoroo.astrid.dao.StoreObjectDao;
import com.todoroo.astrid.data.StoreObject;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.gtasks.sync.GtasksSyncService;
import com.todoroo.astrid.service.SyncV2Service;
import com.todoroo.astrid.subtasks.OrderedListFragmentHelper;
import com.todoroo.astrid.subtasks.SubtasksListFragment;
public class GtasksListFragment extends DraggableTaskListFragment {
public class GtasksListFragment extends SubtasksListFragment {
protected static final int MENU_CLEAR_COMPLETED_ID = MENU_ADDON_INTENT_ID + 1;
public static final String TOKEN_STORE_ID = "storeId";
public static final String TOKEN_STORE_ID = "storeId"; //$NON-NLS-1$
protected static final int MENU_REFRESH_ID = MENU_SUPPORT_ID + 1;
private static final String LAST_FETCH_KEY_GTASKS = "gtasksLastFetch";
@Autowired private StoreObjectDao storeObjectDao;
@Autowired private GtasksTaskListUpdater gtasksTaskListUpdater;
@Autowired private GtasksSyncService gtasksSyncService;
@Autowired private GtasksMetadataService gtasksMetadataService;
@Autowired private GtasksSyncService gtasksSyncService;
@Autowired private GtasksPreferenceService gtasksPreferenceService;
@Autowired private SyncV2Service syncService;
private StoreObject list;
@Override
protected IntegerProperty getIndentProperty() {
return GtasksMetadata.INDENT;
}
private static final Property<?>[] LIST_PROPERTIES = new Property<?>[] {
StoreObject.ID,
StoreObject.TYPE,
@ -65,63 +53,29 @@ public class GtasksListFragment extends DraggableTaskListFragment {
};
@Override
public void onActivityCreated(Bundle icicle) {
super.onActivityCreated(icicle);
getTouchListView().setDropListener(dropListener);
getTouchListView().setSwipeListener(swipeListener);
if(!Preferences.getBoolean(GtasksPreferenceService.PREF_SHOWN_LIST_HELP, false)) {
Preferences.setBoolean(GtasksPreferenceService.PREF_SHOWN_LIST_HELP, true);
DialogUtilities.okDialog(getActivity(),
getString(R.string.gtasks_help_title),
android.R.drawable.ic_dialog_info,
getString(R.string.gtasks_help_body), null);
}
taskAdapter.addOnCompletedTaskListener(new OnCompletedTaskListener() {
protected OrderedListFragmentHelper<?> createFragmentHelper() {
return new OrderedListFragmentHelper<StoreObject>(this, gtasksTaskListUpdater) {
@Override
public void onCompletedTask(Task item, boolean newState) {
setCompletedForItemAndSubtasks(item, newState);
protected void onMetadataChanged(long targetTaskId) {
gtasksSyncService.triggerMoveForMetadata(gtasksMetadataService.
getTaskMetadata(targetTaskId));
}
});
long storeObjectId = extras.getLong(TOKEN_STORE_ID, 0);
list = storeObjectDao.fetch(storeObjectId, LIST_PROPERTIES);
};
}
private final TouchListView.DropListener dropListener = new DropListener() {
@Override
public void drop(int from, int to) {
long targetTaskId = taskAdapter.getItemId(from);
long destinationTaskId = taskAdapter.getItemId(to);
if(to == getListView().getCount() - 1)
gtasksTaskListUpdater.moveTo(targetTaskId, -1);
else
gtasksTaskListUpdater.moveTo(targetTaskId, destinationTaskId);
gtasksSyncService.triggerMoveForMetadata(gtasksMetadataService.getTaskMetadata(targetTaskId));
loadTaskListContent(true);
}
};
@Override
protected boolean allowResorting() {
return false;
}
private final TouchListView.SwipeListener swipeListener = new SwipeListener() {
@Override
public void swipeRight(int which) {
long targetTaskId = taskAdapter.getItemId(which);
gtasksTaskListUpdater.indent(targetTaskId, 1);
gtasksSyncService.triggerMoveForMetadata(gtasksMetadataService.getTaskMetadata(targetTaskId));
loadTaskListContent(true);
}
@Override
public void onActivityCreated(Bundle icicle) {
super.onActivityCreated(icicle);
@Override
public void swipeLeft(int which) {
long targetTaskId = taskAdapter.getItemId(which);
gtasksTaskListUpdater.indent(targetTaskId, -1);
gtasksSyncService.triggerMoveForMetadata(gtasksMetadataService.getTaskMetadata(targetTaskId));
loadTaskListContent(true);
}
};
long storeObjectId = getActivity().getIntent().getLongExtra(TOKEN_STORE_ID, 0);
list = storeObjectDao.fetch(storeObjectId, LIST_PROPERTIES);
((OrderedListFragmentHelper<StoreObject>)helper).setList(list);
}
@Override
protected void initiateAutomaticSync() {
@ -130,10 +84,16 @@ public class GtasksListFragment extends DraggableTaskListFragment {
}
}
@Override
protected void onTaskDelete(Task task) {
helper.onDeleteTask(task);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
MenuItem item = menu.add(Menu.NONE, MENU_CLEAR_COMPLETED_ID, Menu.FIRST, this.getString(R.string.gtasks_GTA_clear_completed));
MenuItem item = menu.add(Menu.NONE, MENU_CLEAR_COMPLETED_ID, Menu.FIRST,
this.getString(R.string.gtasks_GTA_clear_completed));
item.setIcon(android.R.drawable.ic_input_delete); // Needs new icon
}
@ -153,9 +113,9 @@ public class GtasksListFragment extends DraggableTaskListFragment {
}
private void clearCompletedTasks() {
final ProgressDialog pd = new ProgressDialog(getActivity());
final TodorooCursor<Task> tasks = taskService.fetchFiltered(filter.sqlQuery, null, Task.ID, Task.COMPLETION_DATE);
final TodorooCursor<Task> tasks = taskService.fetchFiltered(filter.sqlQuery,
null, Task.ID, Task.COMPLETION_DATE);
pd.setMessage(this.getString(R.string.gtasks_GTA_clearing));
pd.show();
@ -168,7 +128,8 @@ public class GtasksListFragment extends DraggableTaskListFragment {
Task t = new Task(tasks);
if (t.isCompleted()) {
if (listId == null) {
listId = gtasksMetadataService.getTaskMetadata(t.getId()).getValue(GtasksMetadata.LIST_ID);
listId = gtasksMetadataService.getTaskMetadata(
t.getId()).getValue(GtasksMetadata.LIST_ID);
}
t.setValue(Task.DELETION_DATE, DateUtilities.now());
taskService.save(t);
@ -204,45 +165,4 @@ public class GtasksListFragment extends DraggableTaskListFragment {
}
}
private void setCompletedForItemAndSubtasks(Task item, boolean completedState) {
final TodorooCursor<Task> tasks = taskService.fetchFiltered(filter.sqlQuery, null, Task.ID, Task.COMPLETION_DATE);
final long itemId = item.getId();
final boolean completed = completedState;
new Thread() {
@Override
public void run() {
try {
for (tasks.moveToFirst(); !tasks.isAfterLast(); tasks.moveToNext()) {
Task curr = new Task(tasks);
if (curr.getId() == itemId) {
int itemIndent = gtasksMetadataService.getTaskMetadata(curr.getId()).getValue(GtasksMetadata.INDENT);
tasks.moveToNext();
while (!tasks.isAfterLast()) {
Task next = new Task(tasks);
int currIndent = gtasksMetadataService.getTaskMetadata(next.getId()).getValue(GtasksMetadata.INDENT);
if (currIndent > itemIndent) {
if (completed)
next.setValue(Task.COMPLETION_DATE, DateUtilities.now());
else
next.setValue(Task.COMPLETION_DATE, 0L);
taskService.save(next);
} else break;
tasks.moveToNext();
}
break;
}
}
} finally {
tasks.close();
}
getActivity().runOnUiThread(new Runnable() {
public void run() {
loadTaskListContent(true);
}
});
}
}.start();
}
}

@ -27,6 +27,7 @@ import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.data.StoreObject;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.gtasks.sync.GtasksTaskContainer;
import com.todoroo.astrid.subtasks.OrderedListUpdater.OrderedListIterator;
import com.todoroo.astrid.sync.SyncMetadataService;
import com.todoroo.astrid.sync.SyncProviderUtilities;
@ -128,17 +129,13 @@ public final class GtasksMetadataService extends SyncMetadataService<GtasksTaskC
// --- list iterating helpers
public interface ListIterator {
public void processTask(long taskId, Metadata metadata);
}
public void iterateThroughList(StoreObject list, ListIterator iterator) {
public void iterateThroughList(StoreObject list, OrderedListIterator iterator) {
String listId = list.getValue(GtasksList.REMOTE_ID);
iterateThroughList(listId, iterator, 0, false);
}
@SuppressWarnings("nls")
public void iterateThroughList(String listId, ListIterator iterator, long startAtOrder, boolean reverse) {
public void iterateThroughList(String listId, OrderedListIterator iterator, long startAtOrder, boolean reverse) {
Field orderField = Functions.cast(GtasksMetadata.ORDER, "LONG");
Order order = reverse ? Order.desc(orderField) : Order.asc(orderField);
Criterion startAtCriterion = reverse ? Functions.cast(GtasksMetadata.ORDER, "LONG").lt(startAtOrder) :
@ -195,7 +192,7 @@ public final class GtasksMetadataService extends SyncMetadataService<GtasksTaskC
final AtomicLong parentToMatch = new AtomicLong(gtasksMetadata.getValue(GtasksMetadata.PARENT_TASK).longValue());
final AtomicReference<String> sibling = new AtomicReference<String>();
ListIterator iterator = new ListIterator() {
OrderedListIterator iterator = new OrderedListIterator() {
@Override
public void processTask(long taskId, Metadata metadata) {
Task t = taskDao.fetch(taskId, Task.TITLE, Task.DELETION_DATE);

@ -1,34 +1,28 @@
package com.todoroo.astrid.gtasks;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import android.text.TextUtils;
import android.util.Log;
import com.todoroo.andlib.data.Property.IntegerProperty;
import com.todoroo.andlib.data.Property.LongProperty;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.sql.Criterion;
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.core.PluginServices;
import com.todoroo.astrid.dao.MetadataDao;
import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.data.StoreObject;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.gtasks.sync.GtasksTaskContainer;
import com.todoroo.astrid.subtasks.OrderedListUpdater;
public class GtasksTaskListUpdater {
@Autowired private GtasksListService gtasksListService;
@Autowired private GtasksMetadataService gtasksMetadataService;
@Autowired private MetadataDao metadataDao;
public class GtasksTaskListUpdater extends OrderedListUpdater<StoreObject> {
/** map of task -> parent task */
final HashMap<Long, Long> parents = new HashMap<Long, Long>();
@ -39,257 +33,60 @@ public class GtasksTaskListUpdater {
final HashMap<Long, String> localToRemoteIdMap =
new HashMap<Long, String>();
@Autowired private GtasksListService gtasksListService;
@Autowired private GtasksMetadataService gtasksMetadataService;
@Autowired private MetadataDao metadataDao;
public GtasksTaskListUpdater() {
DependencyInjectionService.getInstance().inject(this);
super();
}
// --- task indenting
/**
* Indent a task and all its children
*/
public void indent(final long targetTaskId, final int delta) {
Metadata targetMetadata = gtasksMetadataService.getTaskMetadata(targetTaskId);
if(targetMetadata == null)
return;
StoreObject list = gtasksListService.getList(targetMetadata.getValue(GtasksMetadata.LIST_ID));
if(list == GtasksListService.LIST_NOT_FOUND_OBJECT)
return;
updateParentSiblingMapsFor(list);
final AtomicInteger targetTaskIndent = new AtomicInteger(-1);
final AtomicInteger previousIndent = new AtomicInteger(-1);
final AtomicLong previousTask = new AtomicLong(-1);
final AtomicReference<StoreObject> listRef = new AtomicReference<StoreObject>(list);
gtasksMetadataService.iterateThroughList(list, new GtasksMetadataService.ListIterator() {
@Override
public void processTask(long taskId, Metadata metadata) {
int indent = metadata.getValue(GtasksMetadata.INDENT);
if(targetTaskId == taskId) {
// if indenting is warranted, indent me and my children
if(indent + delta <= previousIndent.get() + 1 && indent + delta >= 0) {
targetTaskIndent.set(indent);
metadata.setValue(GtasksMetadata.INDENT, indent + delta);
long newParent = computeNewParent(listRef.get(), taskId, indent + delta - 1);
if (newParent == taskId) {
metadata.setValue(GtasksMetadata.PARENT_TASK, Task.NO_ID);
} else {
metadata.setValue(GtasksMetadata.PARENT_TASK, newParent);
}
saveAndUpdateModifiedDate(metadata, taskId);
}
} else if(targetTaskIndent.get() > -1) {
// found first task that is not beneath target
if(indent <= targetTaskIndent.get())
targetTaskIndent.set(-1);
else {
metadata.setValue(GtasksMetadata.INDENT, indent + delta);
saveAndUpdateModifiedDate(metadata, taskId);
}
} else {
previousIndent.set(indent);
previousTask.set(taskId);
}
}
// --- overrides
});
@Override
protected IntegerProperty indentProperty() {
return GtasksMetadata.INDENT;
}
/**
* Helper function to iterate through a list and compute a new parent for the target task
* based on the target parent's indent
* @param list
* @param targetTaskId
* @param newIndent
* @return
*/
private long computeNewParent(StoreObject list, long targetTaskId, int targetParentIndent) {
final AtomicInteger desiredParentIndent = new AtomicInteger(targetParentIndent);
final AtomicLong targetTask = new AtomicLong(targetTaskId);
final AtomicLong lastPotentialParent = new AtomicLong(-1);
final AtomicBoolean computedParent = new AtomicBoolean(false);
GtasksMetadataService.ListIterator iterator = new GtasksMetadataService.ListIterator() {
@Override
public void processTask(long taskId, Metadata metadata) {
if (targetTask.get() == taskId) {
computedParent.set(true);
}
int indent = metadata.getValue(GtasksMetadata.INDENT);
if (!computedParent.get() && indent == desiredParentIndent.get()) {
lastPotentialParent.set(taskId);
}
}
};
gtasksMetadataService.iterateThroughList(list, iterator);
if (lastPotentialParent.get() == -1) return Task.NO_ID;
return lastPotentialParent.get();
@Override
protected LongProperty orderProperty() {
return GtasksMetadata.ORDER;
}
// --- task moving
private static class Node {
public final long taskId;
public Node parent;
public final ArrayList<Node> children = new ArrayList<Node>();
public Node(long taskId, Node parent) {
this.taskId = taskId;
this.parent = parent;
}
@Override
protected LongProperty parentProperty() {
return GtasksMetadata.PARENT_TASK;
}
/**
* Move a task and all its children to the position right above
* taskIdToMoveto. Will change the indent level to match taskIdToMoveTo.
*
* @param newTaskId task we will move above. if -1, moves to end of list
*/
public void moveTo(final long targetTaskId, final long moveBeforeTaskId) {
Metadata targetMetadata = gtasksMetadataService.getTaskMetadata(targetTaskId);
if(targetMetadata == null)
return;
StoreObject list = gtasksListService.getList(targetMetadata.getValue(GtasksMetadata.LIST_ID));
if(list == GtasksListService.LIST_NOT_FOUND_OBJECT)
return;
Node root = buildTreeModel(list);
Node target = findNode(root, targetTaskId);
if(target != null && target.parent != null) {
if(moveBeforeTaskId == -1) {
target.parent.children.remove(target);
root.children.add(target);
target.parent = root;
} else {
Node sibling = findNode(root, moveBeforeTaskId);
if(sibling != null) {
int index = sibling.parent.children.indexOf(sibling);
target.parent.children.remove(target);
sibling.parent.children.add(index, target);
target.parent = sibling.parent;
}
}
}
traverseTreeAndWriteValues(root, new AtomicLong(0), -1);
@Override
protected Metadata getTaskMetadata(StoreObject list, long taskId) {
return gtasksMetadataService.getTaskMetadata(taskId);
}
private void traverseTreeAndWriteValues(Node node, AtomicLong order, int indent) {
if(node.taskId != -1) {
Metadata metadata = gtasksMetadataService.getTaskMetadata(node.taskId);
if(metadata == null)
metadata = GtasksMetadata.createEmptyMetadata(node.taskId);
metadata.setValue(GtasksMetadata.ORDER, order.getAndIncrement());
metadata.setValue(GtasksMetadata.INDENT, indent);
metadata.setValue(GtasksMetadata.PARENT_TASK, node.parent.taskId);
saveAndUpdateModifiedDate(metadata, node.taskId);
}
for(Node child : node.children) {
traverseTreeAndWriteValues(child, order, indent + 1);
}
}
private Node findNode(Node node, long taskId) {
if(node.taskId == taskId)
return node;
for(Node child : node.children) {
Node found = findNode(child, taskId);
if(found != null)
return found;
}
return null;
}
private Node buildTreeModel(StoreObject list) {
final Node root = new Node(-1, null);
final AtomicInteger previoustIndent = new AtomicInteger(-1);
final AtomicReference<Node> currentNode = new AtomicReference<Node>(root);
gtasksMetadataService.iterateThroughList(list, new GtasksMetadataService.ListIterator() {
@Override
public void processTask(long taskId, Metadata metadata) {
int indent = metadata.getValue(GtasksMetadata.INDENT);
int previousIndentValue = previoustIndent.get();
if(indent == previousIndentValue) { // sibling
Node parent = currentNode.get().parent;
currentNode.set(new Node(taskId, parent));
parent.children.add(currentNode.get());
} else if(indent > previousIndentValue) { // child
Node parent = currentNode.get();
currentNode.set(new Node(taskId, parent));
parent.children.add(currentNode.get());
} else { // in a different tree
Node node = currentNode.get().parent;
for(int i = indent; i < previousIndentValue; i++)
node = node.parent;
if(node == null)
node = root;
currentNode.set(new Node(taskId, node));
node.children.add(currentNode.get());
}
previoustIndent.set(indent);
}
});
return root;
}
// --- utility
public void debugPrint(String listId) {
StoreObject list = gtasksListService.getList(listId);
if(list == GtasksListService.LIST_NOT_FOUND_OBJECT)
return;
gtasksMetadataService.iterateThroughList(list, new GtasksMetadataService.ListIterator() {
public void processTask(long taskId, Metadata metadata) {
System.err.format("id %d: order %d, indent:%d, parent:%d\n", taskId, //$NON-NLS-1$
metadata.getValue(GtasksMetadata.ORDER),
metadata.getValue(GtasksMetadata.INDENT),
metadata.getValue(GtasksMetadata.PARENT_TASK));
}
});
@Override
protected Metadata createEmptyMetadata(StoreObject list, long taskId) {
Metadata metadata = GtasksMetadata.createEmptyMetadata(taskId);
metadata.setValue(GtasksMetadata.LIST_ID, list.getValue(GtasksList.REMOTE_ID));
return metadata;
}
@SuppressWarnings("nls")
public void debugPrint(Node root, int depth) {
for(int i = 0; i < depth; i++) System.err.print(" + ");
System.err.format("%03d", root.taskId);
System.err.print("\n");
for(int i = 0; i < root.children.size(); i++)
debugPrint(root.children.get(i), depth + 1);
@Override
protected void beforeIndent(StoreObject list) {
updateParentSiblingMapsFor(list);
}
private final Task taskContainer = new Task();
private void saveAndUpdateModifiedDate(Metadata metadata, long taskId) {
if(metadata.getSetValues().size() == 0)
return;
PluginServices.getMetadataService().save(metadata);
taskContainer.setId(taskId);
taskContainer.setValue(Task.MODIFICATION_DATE, DateUtilities.now());
taskContainer.setValue(Task.DETAILS_DATE, DateUtilities.now());
PluginServices.getTaskService().save(taskContainer);
@Override
protected void iterateThroughList(Filter filter, StoreObject list, OrderedListIterator iterator) {
gtasksMetadataService.iterateThroughList(list, iterator);
}
// --- used during synchronization
/**
* Update order, parent, and indentation fields for all tasks in all lists
* Create a local tree of tasks to expedite sibling and parent lookups
*/
public void updateAllMetadata() {
public void createParentSiblingMaps() {
for(StoreObject list : gtasksListService.getLists()) {
correctMetadataForList(list.getValue(GtasksList.REMOTE_ID));
updateParentSiblingMapsFor(list);
}
}
@ -307,7 +104,7 @@ public class GtasksTaskListUpdater {
final AtomicLong order = new AtomicLong(0);
final AtomicInteger previousIndent = new AtomicInteger(-1);
gtasksMetadataService.iterateThroughList(list, new GtasksMetadataService.ListIterator() {
gtasksMetadataService.iterateThroughList(list, new OrderedListIterator() {
@Override
public void processTask(long taskId, Metadata metadata) {
metadata.setValue(GtasksMetadata.ORDER, order.getAndAdd(1));
@ -333,7 +130,8 @@ public class GtasksTaskListUpdater {
private void orderAndIndentHelper(String listId, AtomicLong order, long parent, int indentLevel) {
TodorooCursor<Metadata> metadata = metadataDao.query(Query.select(Metadata.PROPERTIES)
.where(Criterion.and(Metadata.KEY.eq(GtasksMetadata.METADATA_KEY), GtasksMetadata.LIST_ID.eq(listId), GtasksMetadata.PARENT_TASK.eq(parent)))
.where(Criterion.and(Metadata.KEY.eq(GtasksMetadata.METADATA_KEY),
GtasksMetadata.LIST_ID.eq(listId), GtasksMetadata.PARENT_TASK.eq(parent)))
.orderBy(Order.asc(GtasksMetadata.GTASKS_ORDER)));
try {
if (metadata.getCount() > 0) {
@ -352,20 +150,11 @@ public class GtasksTaskListUpdater {
}
}
/**
* Create a local tree of tasks to expedite sibling and parent lookups
*/
public void createParentSiblingMaps() {
for(StoreObject list : gtasksListService.getLists()) {
updateParentSiblingMapsFor(list);
}
}
private void updateParentSiblingMapsFor(StoreObject list) {
final AtomicLong previousTask = new AtomicLong(-1L);
final AtomicInteger previousIndent = new AtomicInteger(-1);
gtasksMetadataService.iterateThroughList(list, new GtasksMetadataService.ListIterator() {
gtasksMetadataService.iterateThroughList(list, new OrderedListIterator() {
@Override
public void processTask(long taskId, Metadata metadata) {
int indent = metadata.getValue(GtasksMetadata.INDENT);
@ -402,30 +191,5 @@ public class GtasksTaskListUpdater {
});
}
/**
* Must be called after creating parent and sibling maps. Updates a
* task container's parent and sibling fields.
*
* @param container
*/
public void updateParentAndSibling(GtasksTaskContainer container) {
long taskId = container.task.getId();
if(parents.containsKey(taskId)) {
long parentId = parents.get(taskId);
if(localToRemoteIdMap.containsKey(parentId))
container.parentId = localToRemoteIdMap.get(parentId);
}
if(siblings.containsKey(taskId)) {
long siblingId = siblings.get(taskId);
if(localToRemoteIdMap.containsKey(siblingId))
container.priorSiblingId = localToRemoteIdMap.get(siblingId);
}
}
public void addRemoteTaskMapping(long id, String remoteId) {
localToRemoteIdMap.put(id, remoteId);
}
}

@ -18,6 +18,8 @@ import com.todoroo.astrid.gtasks.api.GtasksInvoker;
public class GtasksTokenValidator {
private static final String MANUFACTURER_SAMSUNG = "samsung"; //$NON-NLS-1$
/**
* Invalidates and then revalidates the auth token for the currently logged in user
* Shouldn't be called from the main thread--will block on network calls
@ -52,7 +54,7 @@ public class GtasksTokenValidator {
} catch (IOException i2) {
i2.printStackTrace();
String manufacturer = android.os.Build.MANUFACTURER.toLowerCase();
if (!manufacturer.contains("samsung")) { // Try with the notifyAuthFailure set to true in case it was that that broke things
if (!manufacturer.contains(MANUFACTURER_SAMSUNG)) { // Try with the notifyAuthFailure set to true in case it was that that broke things
accountManager.invalidateAuthToken(token);
future = accountManager.manager.getAuthToken(a, GtasksInvoker.AUTH_TOKEN_TYPE, true, null, null);
try {

@ -81,7 +81,6 @@ public class GtasksLegacyMigrator {
List<com.google.api.services.tasks.model.Task> tasksItems = allTasks.getItems();
if (tasksItems != null) {
for (com.google.api.services.tasks.model.Task t : tasksItems) {
System.err.println("Constructing key with title: " + t.getTitle());
String key = constructKeyFromTitles(t.getTitle(), list.getTitle());
taskAndListTitlesToRemoteTaskIds.put(key, t.getId());
}
@ -102,8 +101,7 @@ public class GtasksLegacyMigrator {
String originalListName = gtasksListService.getListName(
container.gtaskMetadata.getValue(GtasksMetadata.LIST_ID));
String originalListId = null;
System.err.println("Migrating task with title: " + container.task.getValue(Task.TITLE) +
", remote id: " + container.gtaskMetadata.getValue(GtasksMetadata.ID));
//Search through lists to see if one of them has match
String taskTitle = container.task.getValue(Task.TITLE);
boolean foundMatch = false;
@ -117,7 +115,6 @@ public class GtasksLegacyMigrator {
originalListId = list.getId();
if (taskAndListTitlesToRemoteTaskIds.containsKey(expectedKey)) {
System.err.println("Found match");
foundMatch = true;
String newRemoteTaskId = taskAndListTitlesToRemoteTaskIds.get(expectedKey);
String newRemoteListId = list.getId();
@ -130,7 +127,6 @@ public class GtasksLegacyMigrator {
}
if (!foundMatch) {
System.err.println("Resetting metadata");
//For non-matches, make the task look newly created
container.gtaskMetadata = GtasksMetadata.createEmptyMetadata(container.task.getId());
container.gtaskMetadata.setValue(GtasksMetadata.ID, ""); //$NON-NLS-1$

@ -1,686 +0,0 @@
/**
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.gtasks.sync;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;
import org.json.JSONException;
import android.app.Activity;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.text.TextUtils;
import android.util.Log;
import com.google.api.services.tasks.model.TaskList;
import com.google.api.services.tasks.model.TaskLists;
import com.timsu.astrid.R;
import com.todoroo.andlib.data.AbstractModel;
import com.todoroo.andlib.data.Property;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.ContextManager;
import com.todoroo.andlib.utility.AndroidUtilities;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.andlib.utility.Preferences;
import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.core.PluginServices;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.data.StoreObject;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.gtasks.GtasksBackgroundService;
import com.todoroo.astrid.gtasks.GtasksList;
import com.todoroo.astrid.gtasks.GtasksListService;
import com.todoroo.astrid.gtasks.GtasksMetadata;
import com.todoroo.astrid.gtasks.GtasksMetadataService;
import com.todoroo.astrid.gtasks.GtasksPreferenceService;
import com.todoroo.astrid.gtasks.GtasksPreferences;
import com.todoroo.astrid.gtasks.GtasksTaskListUpdater;
import com.todoroo.astrid.gtasks.api.CreateRequest;
import com.todoroo.astrid.gtasks.api.GoogleTasksException;
import com.todoroo.astrid.gtasks.api.GtasksApiUtilities;
import com.todoroo.astrid.gtasks.api.GtasksInvoker;
import com.todoroo.astrid.gtasks.api.MoveListRequest;
import com.todoroo.astrid.gtasks.api.MoveRequest;
import com.todoroo.astrid.gtasks.api.PushRequest;
import com.todoroo.astrid.gtasks.api.UpdateRequest;
import com.todoroo.astrid.gtasks.auth.GtasksLoginActivity;
import com.todoroo.astrid.gtasks.auth.GtasksTokenValidator;
import com.todoroo.astrid.service.AstridDependencyInjector;
import com.todoroo.astrid.service.StatisticsConstants;
import com.todoroo.astrid.service.StatisticsService;
import com.todoroo.astrid.sync.SyncContainer;
import com.todoroo.astrid.sync.SyncProvider;
import com.todoroo.astrid.sync.SyncProviderUtilities;
import com.todoroo.astrid.utility.Constants;
@SuppressWarnings("nls")
public class GtasksSyncProvider extends SyncProvider<GtasksTaskContainer> {
@Autowired private GtasksListService gtasksListService;
@Autowired private GtasksMetadataService gtasksMetadataService;
@Autowired private GtasksPreferenceService gtasksPreferenceService;
@Autowired private GtasksTaskListUpdater gtasksTaskListUpdater;
/** google task service fields */
private GtasksInvoker taskService = null;
public GtasksInvoker getGtasksService() {
return taskService;
}
/** tasks to read id for */
ArrayList<GtasksTaskContainer> createdWithoutId;
ArrayList<GtasksTaskContainer> createdWithoutOrder;
Semaphore pushedTaskSemaphore = new Semaphore(0);
AtomicInteger pushedTaskCount = new AtomicInteger(0);
static {
AstridDependencyInjector.initialize();
}
// ----------------------------------------------------------------------
// ------------------------------------------------------ utility methods
// ----------------------------------------------------------------------
@Override
protected SyncProviderUtilities getUtilities() {
return gtasksPreferenceService;
}
/**
* Sign out of service, deleting all synchronization metadata
*/
public void signOut() {
gtasksPreferenceService.clearLastSyncDate();
gtasksPreferenceService.setToken(null);
Preferences.setString(GtasksPreferenceService.PREF_USER_NAME, null);
gtasksMetadataService.clearMetadata();
}
// ----------------------------------------------------------------------
// ------------------------------------------------------ initiating sync
// ----------------------------------------------------------------------
/**
* initiate sync in background
*/
@Override
protected void initiateBackground() {
try {
String authToken = gtasksPreferenceService.getToken();
authToken = GtasksTokenValidator.validateAuthToken(ContextManager.getContext(), authToken);
gtasksPreferenceService.setToken(authToken);
taskService = new GtasksInvoker(authToken);
performSync();
} catch (IllegalStateException e) {
// occurs when application was closed
} catch (Exception e) {
handleException("gtasks-authenticate", e, true);
} finally {
gtasksPreferenceService.stopOngoing();
}
}
/**
* If user isn't already signed in, show sign in dialog. Else perform sync.
*/
@Override
protected void initiateManual(final Activity activity) {
String authToken = gtasksPreferenceService.getToken();
gtasksPreferenceService.stopOngoing();
// check if we have a token & it works
if(authToken == null) {
Intent intent = new Intent(activity, GtasksLoginActivity.class);
activity.startActivityForResult(intent, 0);
} else {
activity.startService(new Intent(null, null,
activity, GtasksBackgroundService.class));
activity.finish();
}
}
// ----------------------------------------------------------------------
// ----------------------------------------------------- synchronization!
// ----------------------------------------------------------------------
protected void performSync() {
String syncSuccess = "failed";
gtasksPreferenceService.recordSyncStart();
if(Constants.DEBUG)
Log.e("gtasks-debug", "- -------- SYNC STARTED");
createdWithoutId = new ArrayList<GtasksTaskContainer>();
createdWithoutOrder = new ArrayList<GtasksTaskContainer>();
try {
TaskLists allTaskLists = taskService.allGtaskLists();
new GtasksLegacyMigrator(taskService, gtasksListService,
allTaskLists).checkAndMigrateLegacy();
getActiveList(allTaskLists);
gtasksListService.updateLists(allTaskLists);
gtasksTaskListUpdater.createParentSiblingMaps();
// read non-deleted tasks for each list
SyncData<GtasksTaskContainer> syncData = populateSyncData();
try {
synchronizeTasks(syncData);
AndroidUtilities.sleepDeep(3000L); // Wait for changes to be saved (i.e. for repeating tasks to be cloned)
checkForCreatedDuringSync();
} finally {
syncData.localCreated.close();
syncData.localUpdated.close();
}
gtasksTaskListUpdater.updateAllMetadata();
gtasksPreferenceService.recordSuccessfulSync();
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_EVENT_REFRESH);
ContextManager.getContext().sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
if(Constants.DEBUG)
Log.e("gtasks-debug", "- ------ SYNC FINISHED");
syncSuccess = getFinalSyncStatus();
} catch (IllegalStateException e) {
// occurs when application was closed
} catch (IOException e) {
handleException("gtasks-sync", e, true); //$NON-NLS-1$
} finally {
StatisticsService.reportEvent(StatisticsConstants.GTASKS_SYNC_FINISHED,
"success", syncSuccess); //$NON-NLS-1$
}
}
private void checkForCreatedDuringSync() {
TodorooCursor<Task> localCreated = gtasksMetadataService.getLocallyCreated(PROPERTIES);
try {
SyncData<GtasksTaskContainer> localCreatedData = new SyncData<GtasksTaskContainer>(null, localCreated, null);
sendLocallyCreated(localCreatedData, new HashMap<String, Integer>());
} catch (IOException e) {
handleException("gtasks-sync", e, true);
} finally {
localCreated.close();
}
}
private void getActiveList(TaskLists taskView) throws IOException {
String listId;
if(taskView.getItems().size() == 0) {
if(Constants.DEBUG)
Log.e("gtasks-debug", "ACTION: createList(4)");
TaskList newList = taskService.createGtaskList(ContextManager.getString(R.string.app_name));
listId = newList.getId();
} else if (Preferences.getStringValue(GtasksPreferenceService.PREF_DEFAULT_LIST) != null) {
listId = Preferences.getStringValue(GtasksPreferenceService.PREF_DEFAULT_LIST);
} else {
listId = taskService.getGtaskList("@default").getId();
}
Preferences.setString(GtasksPreferenceService.PREF_DEFAULT_LIST, listId);
}
@Override
protected void readRemotelyUpdated(SyncData<GtasksTaskContainer> data)
throws IOException {
// wait for pushed threads
try {
pushedTaskSemaphore.acquire(pushedTaskCount.get());
pushedTaskCount.set(0);
} catch (InterruptedException e) {
return;
}
// match remote tasks to locally created tasks
HashMap<String, GtasksTaskContainer> locals = new HashMap<String, GtasksTaskContainer>();
for(GtasksTaskContainer task : createdWithoutId) {
locals.put(task.gtaskMetadata.getValue(GtasksMetadata.ID), task);
}
verifyCreatedOrder();
// first, pull all tasks. then we can write them
// include deleted tasks so we can delete them in astrid
data.remoteUpdated = readAllRemoteTasks(true, true);
for(GtasksTaskContainer remote : data.remoteUpdated) {
if(remote.task.getId() < 1) {
GtasksTaskContainer local = locals.get(remote.gtaskMetadata.getValue(GtasksMetadata.ID));
if(local != null) {
if(Constants.DEBUG)
Log.e("gtasks-debug", "FOUND LOCAL - " + remote.task.getId());
remote.task.setId(local.task.getId());
}
}
}
super.readRemotelyUpdated(data);
}
private void verifyCreatedOrder() throws IOException {
Collections.sort(createdWithoutOrder, new Comparator<GtasksTaskContainer>() {
@Override
public int compare(GtasksTaskContainer arg0, GtasksTaskContainer arg1) {
long order0 = arg0.gtaskMetadata.getValue(GtasksMetadata.ORDER);
long order1 = arg1.gtaskMetadata.getValue(GtasksMetadata.ORDER);
if (order0 == order1) return 0;
if (order0 < order1) return -1;
else return 1;
}
});
for (GtasksTaskContainer t : createdWithoutOrder) {
String toMove = t.gtaskMetadata.getValue(GtasksMetadata.ID);
String listId = t.gtaskMetadata.getValue(GtasksMetadata.LIST_ID);
String remoteParent = gtasksMetadataService.getRemoteParentId(t.gtaskMetadata);
String remoteSibling = gtasksMetadataService.getRemoteSiblingId(listId, t.gtaskMetadata);
MoveRequest move = new MoveRequest(taskService, toMove, listId, remoteParent, remoteSibling);
move.push();
}
}
// ----------------------------------------------------------------------
// ------------------------------------------------------------ sync data
// ----------------------------------------------------------------------
// all synchronized properties
private static final Property<?>[] PROPERTIES = Task.PROPERTIES;
/**
* Populate SyncData data structure
* @throws JSONException
*/
private SyncData<GtasksTaskContainer> populateSyncData() throws IOException {
// fetch remote tasks
ArrayList<GtasksTaskContainer> remoteTasks = readAllRemoteTasks(false, false);
// fetch locally created tasks
TodorooCursor<Task> localCreated = gtasksMetadataService.getLocallyCreated(PROPERTIES);
// fetch locally updated tasks
TodorooCursor<Task> localUpdated = gtasksMetadataService.getLocallyUpdated(PROPERTIES);
return new SyncData<GtasksTaskContainer>(remoteTasks, localCreated, localUpdated);
}
// ----------------------------------------------------------------------
// ------------------------------------------------- create / push / pull
// ----------------------------------------------------------------------
private ArrayList<GtasksTaskContainer> readAllRemoteTasks(final boolean includeDeleted, final boolean includeHidden) {
final ArrayList<GtasksTaskContainer> list = new ArrayList<GtasksTaskContainer>();
final Semaphore listsFinished = new Semaphore(0);
// launch threads
StoreObject[] lists = gtasksListService.getLists();
for(final StoreObject dashboard : lists) {
new Thread(new Runnable() {
@Override
public void run() {
try {
String listId = dashboard.getValue(GtasksList.REMOTE_ID);
if(Constants.DEBUG)
Log.e("gtasks-debug", "ACTION: getTasks, " + listId);
List<com.google.api.services.tasks.model.Task> remoteTasks = taskService.getAllGtasksFromListId(listId, includeDeleted, includeHidden, 0).getItems();
addRemoteTasksToList(remoteTasks, listId, list);
} catch (Exception e) {
handleException("read-remotes", e, false);
} finally {
listsFinished.release();
}
}
}).start();
}
try {
listsFinished.acquire(lists.length);
} catch (InterruptedException e) {
handleException("wait-for-remotes", e, false);
}
return list;
}
private void addRemoteTasksToList(List<com.google.api.services.tasks.model.Task> remoteTasks, String listId, ArrayList<GtasksTaskContainer> list) {
if (remoteTasks != null) {
long order = 0;
HashMap<String, com.google.api.services.tasks.model.Task> idsToTasks = new HashMap<String, com.google.api.services.tasks.model.Task>();
HashMap<String, Integer> indentation = new HashMap<String, Integer>();
HashMap<String, String> parentToPriorSiblingMap = new HashMap<String, String>();
//Build map of String ids to task objects
for (com.google.api.services.tasks.model.Task task : remoteTasks) {
String id = task.getId();
idsToTasks.put(id, task);
}
for(com.google.api.services.tasks.model.Task remoteTask : remoteTasks) {
if(TextUtils.isEmpty(remoteTask.getTitle()))
continue;
GtasksTaskContainer container = parseRemoteTask(remoteTask, listId);
String id = remoteTask.getId();
// update parents, prior sibling
String parent = remoteTask.getParent(); // can be null, which means top level task
container.parentId = parent;
if(parentToPriorSiblingMap.containsKey(parent))
container.priorSiblingId = parentToPriorSiblingMap.get(parent);
parentToPriorSiblingMap.put(parent, id);
// update order, indent
container.gtaskMetadata.setValue(GtasksMetadata.ORDER, order++);
int indent = findIndentation(idsToTasks, indentation, remoteTask);
indentation.put(id, indent);
container.gtaskMetadata.setValue(GtasksMetadata.INDENT, indent);
// update reminder flags for incoming remote tasks to prevent annoying
if(container.task.hasDueDate() && container.task.getValue(Task.DUE_DATE) < DateUtilities.now())
container.task.setFlag(Task.REMINDER_FLAGS, Task.NOTIFY_AFTER_DEADLINE, false);
gtasksMetadataService.findLocalMatch(container);
synchronized(list) {
list.add(container);
}
}
}
}
private int findIndentation(HashMap<String, com.google.api.services.tasks.model.Task> idsToTasks,
HashMap<String, Integer> indentation, com.google.api.services.tasks.model.Task task) {
if (task == null) return 0; //TODO: Not sure why this is happening...
if(indentation.containsKey(task.getId()))
return indentation.get(task.getId());
if(TextUtils.isEmpty(task.getParent()))
return 0;
return findIndentation(idsToTasks, indentation, idsToTasks.get(task.getParent())) + 1;
}
@Override
protected GtasksTaskContainer create(GtasksTaskContainer local) throws IOException {
String listId = Preferences.getStringValue(GtasksPreferenceService.PREF_DEFAULT_LIST);
if(local.gtaskMetadata.containsNonNullValue(GtasksMetadata.LIST_ID))
listId = local.gtaskMetadata.getValue(GtasksMetadata.LIST_ID);
gtasksTaskListUpdater.updateParentAndSibling(local);
local.gtaskMetadata.setValue(GtasksMetadata.ID, null);
local.gtaskMetadata.setValue(GtasksMetadata.LIST_ID, listId);
com.google.api.services.tasks.model.Task createdTask = new com.google.api.services.tasks.model.Task();
CreateRequest createRequest = new CreateRequest(taskService, listId, createdTask, local.parentId, local.priorSiblingId);
//updateTaskHelper(local, null, createRequest);
localPropertiesToModel(local, null, createRequest.getToPush());
com.google.api.services.tasks.model.Task createResult = createRequest.push();
createdWithoutId.add(local);
createdWithoutOrder.add(local);
String newIdTask = createResult.getId();
local.gtaskMetadata.setValue(GtasksMetadata.ID, newIdTask);
return local;
}//*/
private void localPropertiesToModel(GtasksTaskContainer local, GtasksTaskContainer remote,
com.google.api.services.tasks.model.Task model) {
if(shouldTransmit(local, Task.TITLE, remote))
model.setTitle(local.task.getValue(Task.TITLE));
if(shouldTransmit(local, Task.DUE_DATE, remote)) {
model.setDue(GtasksApiUtilities.unixTimeToGtasksDueDate(local.task.getValue(Task.DUE_DATE)));
}
if(shouldTransmit(local, Task.COMPLETION_DATE, remote)) {
model.setCompleted(GtasksApiUtilities.unixTimeToGtasksCompletionTime(local.task.getValue(Task.COMPLETION_DATE)));
model.setStatus((local.task.isCompleted() ? "completed" : "needsAction"));
}
if(shouldTransmit(local, Task.DELETION_DATE, remote))
model.setDeleted(local.task.isDeleted());
if(shouldTransmit(local, Task.NOTES, remote))
model.setNotes(local.task.getValue(Task.NOTES));
}
private void updateTaskHelper(final GtasksTaskContainer local,
final GtasksTaskContainer remote, final PushRequest request) throws IOException {
final String idTask = local.gtaskMetadata.getValue(GtasksMetadata.ID);
final String idList = local.gtaskMetadata.getValue(GtasksMetadata.LIST_ID);
try {
// set properties
localPropertiesToModel(local, null, request.getToPush());
// write task (and perform move action if requested)
if(request instanceof UpdateRequest) {
if(Constants.DEBUG)
Log.e("gtasks-debug", "ACTION: task edit (6), " + idTask);
} else if(request instanceof CreateRequest) {
if(Constants.DEBUG)
Log.e("gtasks-debug", "ACTION: task create (7), " + local.task.getValue(Task.TITLE));
} else
throw new GoogleTasksException("Unknown request type " + request.getClass());
pushedTaskCount.incrementAndGet();
new Thread(new Runnable() {
@Override
public void run() {
String newIdTask = idTask;
try {
/*if (request instanceof CreateRequest) { //This is commented out until bugs with multithreaded task creation are resolved
com.google.api.services.tasks.v1.model.Task createResult = request.executePush();
newIdTask = createResult.id;
System.err.println("Created " + createResult.title + " successfully, remote id: " + createResult.id);
local.gtaskMetadata.setValue(GtasksMetadata.ID, newIdTask);
}//*/
if(!TextUtils.isEmpty(newIdTask) && remote != null && (local.parentId != remote.parentId ||
local.priorSiblingId != remote.priorSiblingId)) {
if(Constants.DEBUG)
Log.e("gtasks-debug", "ACTION: move(1) - " + newIdTask + ", " + local.parentId + ", " + local.priorSiblingId);
//This case basically defaults to whatever local settings are. Future versions could try and merge better
MoveRequest moveRequest = new MoveRequest(taskService, newIdTask, idList, local.parentId, local.priorSiblingId);
moveRequest.push();
}
if (request instanceof UpdateRequest) {
request.push();
}
//Strategy--delete, migrate properties, recreate, update local AND remote ids; happens in MoveListRequest
if(remote != null && !idList.equals(remote.gtaskMetadata.getValue(
GtasksMetadata.LIST_ID))) {
if(Constants.DEBUG)
Log.e("gtasks-debug", "ACTION: moveTask(5), " + newIdTask + ", " + idList + " to " +
remote.gtaskMetadata.getValue(GtasksMetadata.LIST_ID));
MoveListRequest moveList = new MoveListRequest(taskService, newIdTask, remote.gtaskMetadata.getValue(GtasksMetadata.LIST_ID), idList, null);
com.google.api.services.tasks.model.Task result = moveList.push();
local.gtaskMetadata.setValue(GtasksMetadata.ID, result.getId());
remote.gtaskMetadata.setValue(GtasksMetadata.ID, result.getId());
}
} catch (IOException e) {
handleException("update-task", e, false);
} finally {
pushedTaskSemaphore.release();
}
}
}).start();
} catch (Exception e) {
throw new GoogleTasksException(e);
}
}//*/
/** Create a task container for the given remote task
* @throws JSONException */
private GtasksTaskContainer parseRemoteTask(com.google.api.services.tasks.model.Task remoteTask, String listId) {
Task task = new Task();
ArrayList<Metadata> metadata = new ArrayList<Metadata>();
task.setValue(Task.TITLE, remoteTask.getTitle());
task.setValue(Task.CREATION_DATE, DateUtilities.now());
task.setValue(Task.COMPLETION_DATE, GtasksApiUtilities.gtasksCompletedTimeToUnixTime(remoteTask.getCompleted(), 0));
if (remoteTask.getDeleted() == null || !remoteTask.getDeleted().booleanValue())
task.setValue(Task.DELETION_DATE, 0L);
else if (remoteTask.getDeleted().booleanValue())
task.setValue(Task.DELETION_DATE, DateUtilities.now());
if (remoteTask.getHidden() != null && remoteTask.getHidden().booleanValue())
task.setValue(Task.DELETION_DATE, DateUtilities.now());
long dueDate = GtasksApiUtilities.gtasksDueTimeToUnixTime(remoteTask.getDue(), 0);
long createdDate = Task.createDueDate(Task.URGENCY_SPECIFIC_DAY, dueDate);
task.setValue(Task.DUE_DATE, createdDate);
task.setValue(Task.NOTES, remoteTask.getNotes());
Metadata gtasksMetadata = GtasksMetadata.createEmptyMetadata(AbstractModel.NO_ID);
gtasksMetadata.setValue(GtasksMetadata.ID, remoteTask.getId());
gtasksMetadata.setValue(GtasksMetadata.LIST_ID, listId);
GtasksTaskContainer container = new GtasksTaskContainer(task, metadata,
gtasksMetadata);
return container;
}
@Override
protected GtasksTaskContainer pull(GtasksTaskContainer task) throws IOException {
// we pull all tasks at the end, so here we just
// return the task that was requested
return task;
}
/**
* Send changes for the given Task across the wire. If a remoteTask is
* supplied, we attempt to intelligently only transmit the values that
* have changed.
*/
@Override
protected GtasksTaskContainer push(GtasksTaskContainer local, GtasksTaskContainer remote) throws IOException {
gtasksTaskListUpdater.updateParentAndSibling(local);
String id = local.gtaskMetadata.getValue(GtasksMetadata.ID);
if(Constants.DEBUG)
Log.e("gtasks-debug", "ACTION: modifyTask(3) - " + id);
com.google.api.services.tasks.model.Task toUpdate = taskService.getGtask(local.gtaskMetadata.getValue(GtasksMetadata.LIST_ID), id);
UpdateRequest modifyTask = new UpdateRequest(taskService, local.gtaskMetadata.getValue(GtasksMetadata.LIST_ID), toUpdate);
updateTaskHelper(local, remote, modifyTask);
return pull(remote);
}//*/
// ----------------------------------------------------------------------
// --------------------------------------------------------- read / write
// ----------------------------------------------------------------------
@Override
protected GtasksTaskContainer read(TodorooCursor<Task> cursor) throws IOException {
return gtasksMetadataService.readTaskAndMetadata(cursor);
}
@Override
protected void write(GtasksTaskContainer task) throws IOException {
// merge astrid dates with google dates
if(task.task.isSaved()) {
Task local = PluginServices.getTaskService().fetchById(task.task.getId(), Task.DUE_DATE, Task.COMPLETION_DATE);
mergeDates(task.task, local);
if(task.task.isCompleted() && !local.isCompleted())
StatisticsService.reportEvent(StatisticsConstants.GTASKS_TASK_COMPLETED);
} else { // Set default reminders for remotely created tasks
TaskDao.setDefaultReminders(task.task);
}
gtasksMetadataService.saveTaskAndMetadata(task);
}
/** pick up due time from local task */
private void mergeDates(Task remote, Task local) {
if(remote.hasDueDate() && local.hasDueTime()) {
Date newDate = new Date(remote.getValue(Task.DUE_DATE));
Date oldDate = new Date(local.getValue(Task.DUE_DATE));
newDate.setHours(oldDate.getHours());
newDate.setMinutes(oldDate.getMinutes());
newDate.setSeconds(oldDate.getSeconds());
long setDate = Task.createDueDate(Task.URGENCY_SPECIFIC_DAY_TIME,
newDate.getTime());
remote.setValue(Task.DUE_DATE, setDate);
}
}
// ----------------------------------------------------------------------
// --------------------------------------------------------- misc helpers
// ----------------------------------------------------------------------
@Override
protected int matchTask(ArrayList<GtasksTaskContainer> tasks, GtasksTaskContainer target) {
int length = tasks.size();
for(int i = 0; i < length; i++) {
GtasksTaskContainer task = tasks.get(i);
if(AndroidUtilities.equals(task.gtaskMetadata.getValue(GtasksMetadata.ID),
target.gtaskMetadata.getValue(GtasksMetadata.ID)))
return i;
}
return -1;
}
/**
* Determine whether this task's property should be transmitted
* @param task task to consider
* @param property property to consider
* @param remoteTask remote task proxy
* @return
*/
private boolean shouldTransmit(SyncContainer task, Property<?> property, SyncContainer remoteTask) {
if(!task.task.containsValue(property))
return false;
if(remoteTask == null)
return true;
if(!remoteTask.task.containsValue(property))
return true;
// special cases - match if they're zero or nonzero
if(property == Task.COMPLETION_DATE ||
property == Task.DELETION_DATE)
return !AndroidUtilities.equals((Long)task.task.getValue(property) == 0,
(Long)remoteTask.task.getValue(property) == 0);
return !AndroidUtilities.equals(task.task.getValue(property),
remoteTask.task.getValue(property));
}
@Override
protected int updateNotification(Context context, Notification notification) {
String notificationTitle = context.getString(R.string.gtasks_notification_title);
Intent intent = new Intent(context, GtasksPreferences.class);
PendingIntent notificationIntent = PendingIntent.getActivity(context, 0,
intent, 0);
notification.setLatestEventInfo(context,
notificationTitle, context.getString(R.string.SyP_progress),
notificationIntent);
return Constants.NOTIFICATION_SYNC;
}
@Override
protected void transferIdentifiers(GtasksTaskContainer source,
GtasksTaskContainer destination) {
destination.gtaskMetadata = source.gtaskMetadata;
}
}

@ -30,6 +30,7 @@ import com.todoroo.astrid.utility.Flags;
public final class GtasksSyncService {
private static final String DEFAULT_LIST = "@default"; //$NON-NLS-1$
@Autowired MetadataService metadataService;
@Autowired MetadataDao metadataDao;
@Autowired GtasksMetadataService gtasksMetadataService;
@ -77,7 +78,7 @@ public final class GtasksSyncService {
GtasksInvoker invoker = new GtasksInvoker(gtasksPreferenceService.getToken());
if (op instanceof TaskPushOp) {
TaskPushOp taskPush = (TaskPushOp)op;
pushTaskOnSave(taskPush.model, taskPush.model.getSetValues(), invoker, true);
pushTaskOnSave(taskPush.model, taskPush.model.getMergedValues(), invoker, true);
} else if (op instanceof MoveOp) {
MoveOp move = (MoveOp)op;
pushMetadataOnSave(move.metadata, invoker);
@ -156,12 +157,12 @@ public final class GtasksSyncService {
String remoteId = null;
String listId = Preferences.getStringValue(GtasksPreferenceService.PREF_DEFAULT_LIST);
if (listId == null) {
com.google.api.services.tasks.model.TaskList defaultList = invoker.getGtaskList("@default");
com.google.api.services.tasks.model.TaskList defaultList = invoker.getGtaskList(DEFAULT_LIST);
if (defaultList != null) {
listId = defaultList.getId();
Preferences.setString(GtasksPreferenceService.PREF_DEFAULT_LIST, listId);
} else {
listId = "@default"; //$NON-NLS-1$
listId = DEFAULT_LIST;
}
}

@ -188,7 +188,8 @@ public class GtasksSyncV2Provider extends SyncV2Provider {
}
private void synchronizeListHelper(StoreObject list, GtasksInvoker invoker, SyncExceptionHandler handler, SyncResultCallback callback) {
private void synchronizeListHelper(StoreObject list, GtasksInvoker invoker,
SyncExceptionHandler errorHandler, SyncResultCallback callback) {
// Do stuff
String listId = list.getValue(GtasksList.REMOTE_ID);
long lastSyncDate;
@ -218,8 +219,8 @@ public class GtasksSyncV2Provider extends SyncV2Provider {
gtasksTaskListUpdater.correctOrderAndIndentForList(listId);
}
} catch (IOException e) {
if (handler != null)
handler.handleException("gtasks-sync-io", e); //$NON-NLS-1$
if (errorHandler != null)
errorHandler.handleException("gtasks-sync-io", e); //$NON-NLS-1$
}
}

@ -1,8 +1,5 @@
package com.todoroo.astrid.notes;
import greendroid.widget.AsyncImageView;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@ -58,6 +55,7 @@ import com.todoroo.astrid.dao.UpdateDao;
import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.data.Update;
import com.todoroo.astrid.helper.AsyncImageView;
import com.todoroo.astrid.helper.ImageDiskCache;
import com.todoroo.astrid.helper.ProgressBarSyncResultCallback;
import com.todoroo.astrid.service.MetadataService;
@ -367,21 +365,16 @@ public class EditNoteActivity extends LinearLayout implements TimerActionListene
commentPictureView.setVisibility(View.GONE);
else {
commentPictureView.setVisibility(View.VISIBLE);
if(imageCache.contains(item.commentPicture)) {
try {
commentPictureView.setDefaultImageBitmap(imageCache.get(item.commentPicture));
} catch (IOException e) {
e.printStackTrace();
}
}
else {
commentPictureView.setUrl(item.commentPicture);
}
commentPictureView.setUrl(item.commentPicture);
}
}
}
public void refreshData(boolean manual, SyncResultCallback existingCallback) {
if(!task.containsNonNullValue(Task.REMOTE_ID)) {
return;
}
final SyncResultCallback callback;
if(existingCallback != null)
callback = existingCallback;
@ -400,18 +393,6 @@ public class EditNoteActivity extends LinearLayout implements TimerActionListene
callback.incrementMax(100);
}
// push task if it hasn't been pushed
if(!task.containsNonNullValue(Task.REMOTE_ID) && !TextUtils.isEmpty(task.getValue(Task.TITLE))) {
new Thread(new Runnable() {
@Override
public void run() {
actFmSyncService.pushTask(task.getId());
task = PluginServices.getTaskService().fetchById(task.getId(), Task.NOTES, Task.ID, Task.REMOTE_ID, Task.TITLE);
refreshData(false, callback);
}
}).start();
return;
}
actFmSyncService.fetchUpdatesForTask(task, manual, new Runnable() {
@Override
@ -424,17 +405,15 @@ public class EditNoteActivity extends LinearLayout implements TimerActionListene
}
private void addComment() {
addComment(commentField.getText().toString(), "task_comment", true); //$NON-NLS-1$
addComment(commentField.getText().toString(), UpdateAdapter.UPDATE_TASK_COMMENT, true);
}
private String getPictureHashForUpdate(Update u) {
return String.format("%s%s", u.getValue(Update.TASK), u.getValue(Update.CREATION_DATE)); //$NON-NLS-1$
}
@SuppressWarnings("nls")
private void addComment(String message, String actionCode, boolean usePicture) {
// Allow for users to just add picture
if (TextUtils.isEmpty(message) && usePicture) {
message = " "; //$NON-NLS-1$
message = " ";
}
Update update = new Update();
update.setValue(Update.MESSAGE, message);
@ -444,11 +423,12 @@ public class EditNoteActivity extends LinearLayout implements TimerActionListene
update.setValue(Update.TASK, task.getValue(Task.REMOTE_ID));
update.setValue(Update.TASK_LOCAL, task.getId());
update.setValue(Update.CREATION_DATE, DateUtilities.now());
update.setValue(Update.TARGET_NAME, task.getValue(Task.TITLE));
if (usePicture && pendingCommentPicture != null) {
update.setValue(Update.PICTURE, Update.PICTURE_LOADING);
try {
String updateString = getPictureHashForUpdate(update);
String updateString = ImageDiskCache.getPictureHash(update);
imageCache.put(updateString, pendingCommentPicture);
update.setValue(Update.PICTURE, updateString);
}
@ -540,7 +520,7 @@ public class EditNoteActivity extends LinearLayout implements TimerActionListene
addComment(String.format("%s %s", //$NON-NLS-1$
getContext().getString(R.string.TEA_timer_comment_started),
DateUtilities.getTimeString(getContext(), new Date())),
"task_started", //$NON-NLS-1$
UpdateAdapter.UPDATE_TASK_COMMENT,
false);
}
@ -551,7 +531,7 @@ public class EditNoteActivity extends LinearLayout implements TimerActionListene
getContext().getString(R.string.TEA_timer_comment_stopped),
DateUtilities.getTimeString(getContext(), new Date()),
getContext().getString(R.string.TEA_timer_comment_spent),
elapsedTime), "task_stopped", false); //$NON-NLS-1$
elapsedTime), UpdateAdapter.UPDATE_TASK_COMMENT, false);
}
/*

@ -198,7 +198,6 @@ public class OpencrxControlSet extends PopupControlSet {
@Autowired
private StoreObjectDao storeObjectDao;
@SuppressWarnings("unused")
public OpencrxControlSet(final Activity activity, int viewLayout, int displayViewLayout, int title) {
super(activity, viewLayout, displayViewLayout, title);
DependencyInjectionService.getInstance().inject(this);

@ -123,6 +123,6 @@ public class OpencrxCoreUtils extends SyncProviderUtilities{
@Override
public String getLoggedInUserName() {
return "";
return ""; //$NON-NLS-1$
}
}

@ -177,11 +177,11 @@ public class ProducteevControlSet extends PopupControlSet {
final EditText editor = new EditText(ProducteevControlSet.this.activity);
OnClickListener okListener = new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
public void onClick(DialogInterface dlg, int which) {
Activity context = ProducteevControlSet.this.activity;
String newDashboardName = editor.getText().toString();
if (newDashboardName == null || newDashboardName.length() == 0) {
dialog.cancel();
dlg.cancel();
} else {
// create the real dashboard, select it in the spinner and refresh responsiblespinner
ProgressDialog progressDialog = com.todoroo.andlib.utility.DialogUtilities.progressDialog(context,
@ -213,8 +213,8 @@ public class ProducteevControlSet extends PopupControlSet {
};
OnClickListener cancelListener = new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
public void onClick(DialogInterface dlg, int which) {
dlg.cancel();
dashboardSelector.setSelection(lastDashboardSelection);
}
};

@ -49,13 +49,14 @@ public class Notifications extends BroadcastReceiver {
* Action name for broadcast intent notifying that task was created from repeating template
*/
public static final String BROADCAST_IN_APP_NOTIFY = Constants.PACKAGE + ".IN_APP_NOTIFY"; //$NON-NLS-1$
public static final String EXTRAS_CUSTOM_INTENT = "intent";
public static final String EXTRAS_NOTIF_ID = "notifId";
public static final String EXTRAS_CUSTOM_INTENT = "intent"; //$NON-NLS-1$
public static final String EXTRAS_NOTIF_ID = "notifId"; //$NON-NLS-1$
/** notification type extra */
public static final String EXTRAS_TYPE = "type"; //$NON-NLS-1$
public static final String EXTRAS_TITLE = "title";
public static final String EXTRAS_TEXT = "text";
public static final String EXTRAS_RING_TIMES = "ringTimes";
public static final String EXTRAS_TITLE = "title"; //$NON-NLS-1$
public static final String EXTRAS_TEXT = "text"; //$NON-NLS-1$
public static final String EXTRAS_RING_TIMES = "ringTimes"; //$NON-NLS-1$
// --- instance variables

@ -67,7 +67,6 @@ public class RepeatControlSet extends PopupControlSet {
private Button value;
private Spinner interval;
private Spinner type;
private LinearLayout repeatContainer;
private LinearLayout daysOfWeekContainer;
private final CompoundButton[] daysOfWeek = new CompoundButton[7];
@ -132,6 +131,7 @@ public class RepeatControlSet extends PopupControlSet {
listeners.remove(listener);
}
@SuppressWarnings("nls")
@Override
public void readFromTask(Task task) {
super.readFromTask(task);
@ -232,7 +232,6 @@ public class RepeatControlSet extends PopupControlSet {
value = (Button) getView().findViewById(R.id.repeatValue);
interval = (Spinner) getView().findViewById(R.id.repeatInterval);
type = (Spinner) getView().findViewById(R.id.repeatType);
repeatContainer = (LinearLayout) getView().findViewById(R.id.repeatContainer);
daysOfWeekContainer = (LinearLayout) getView().findViewById(R.id.repeatDayOfWeekContainer);
setRepeatValue(1);
@ -273,6 +272,7 @@ public class RepeatControlSet extends PopupControlSet {
@Override
public void onItemSelected(AdapterView<?> parentView, View view, int position, long id) {
daysOfWeekContainer.setVisibility(position == INTERVAL_WEEKS ? View.VISIBLE : View.GONE);
intervalValue = position;
}
@Override

@ -37,8 +37,6 @@ public class RepeatTaskCompleteListener extends BroadcastReceiver {
@Autowired ActFmPreferenceService actFmPreferenceService;
private static boolean skipActFmCheck = false;
@Override
public void onReceive(Context context, Intent intent) {
ContextManager.setContext(context);
@ -90,11 +88,6 @@ public class RepeatTaskCompleteListener extends BroadcastReceiver {
}
}
/** for debug */
public static void setSkipActFmCheck(boolean skipActFmCheck) {
RepeatTaskCompleteListener.skipActFmCheck = skipActFmCheck;
}
/** Compute next due date */
public static long computeNextDueDate(Task task, String recurrence) throws ParseException {
boolean repeatAfterCompletion = task.getFlag(Task.FLAGS, Task.FLAG_REPEAT_AFTER_COMPLETION);

@ -0,0 +1,330 @@
package com.todoroo.astrid.subtasks;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import android.app.Activity;
import android.content.Context;
import android.database.Cursor;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.MarginLayoutParams;
import android.widget.ListView;
import com.commonsware.cwac.tlv.TouchListView.DropListener;
import com.commonsware.cwac.tlv.TouchListView.GrabberClickListener;
import com.commonsware.cwac.tlv.TouchListView.SwipeListener;
import com.timsu.astrid.R;
import com.todoroo.andlib.data.Property;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.andlib.utility.DialogUtilities;
import com.todoroo.andlib.utility.Preferences;
import com.todoroo.astrid.activity.TaskListFragment;
import com.todoroo.astrid.adapter.TaskAdapter;
import com.todoroo.astrid.adapter.TaskAdapter.OnCompletedTaskListener;
import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.service.MetadataService;
import com.todoroo.astrid.service.TaskService;
import com.todoroo.astrid.service.ThemeService;
import com.todoroo.astrid.subtasks.OrderedListUpdater.Node;
import com.todoroo.astrid.subtasks.OrderedListUpdater.OrderedListNodeVisitor;
import com.todoroo.astrid.ui.DraggableListView;
import com.todoroo.astrid.utility.AstridPreferences;
public class OrderedListFragmentHelper<LIST> {
private final DisplayMetrics metrics = new DisplayMetrics();
private final OrderedListUpdater<LIST> updater;
private final TaskListFragment fragment;
@Autowired TaskService taskService;
@Autowired MetadataService metadataService;
private DraggableTaskAdapter taskAdapter;
private LIST list;
public OrderedListFragmentHelper(TaskListFragment fragment, OrderedListUpdater<LIST> updater) {
DependencyInjectionService.getInstance().inject(this);
this.fragment = fragment;
this.updater = updater;
}
// --- ui component setup
private Activity getActivity() {
return fragment.getActivity();
}
private ListView getListView() {
return fragment.getListView();
}
private Filter getFilter() {
return fragment.getFilter();
}
public DraggableListView getTouchListView() {
DraggableListView tlv = (DraggableListView) fragment.getListView();
return tlv;
}
public void setUpUiComponents() {
TypedValue tv = new TypedValue();
getActivity().getTheme().resolveAttribute(R.attr.asThemeTextColor, tv, false);
getTouchListView().setDragndropBackgroundColor(tv.data);
getTouchListView().setDropListener(dropListener);
getTouchListView().setClickListener(rowClickListener);
getTouchListView().setSwipeListener(swipeListener);
getActivity().getWindowManager().getDefaultDisplay().getMetrics(metrics);
if(Preferences.getInt(AstridPreferences.P_SUBTASKS_HELP, 0) == 0)
showSubtasksHelp();
}
@SuppressWarnings("nls")
private void showSubtasksHelp() {
String body = String.format("<h3>%s</h3><img src='%s'>" +
"<br>%s<br><br><br><img src='%s'><br>%s",
getActivity().getString(R.string.subtasks_help_1),
"subtasks_vertical.png",
getActivity().getString(R.string.subtasks_help_2),
"subtasks_horizontal.png",
getActivity().getString(R.string.subtasks_help_3));
String color = ThemeService.getDialogTextColor();
String html = String.format("<html><body style='text-align:center;color:%s'>%s</body></html>",
color, body);
DialogUtilities.htmlDialog(getActivity(), html, R.string.subtasks_help_title);
Preferences.setInt(AstridPreferences.P_SUBTASKS_HELP, 1);
}
public void beforeSetUpTaskList(Filter filter) {
updater.initialize(list, filter);
}
public Property<?>[] taskProperties() {
ArrayList<Property<?>> properties = new ArrayList<Property<?>>(Arrays.asList(TaskAdapter.PROPERTIES));
properties.add(updater.indentProperty());
properties.add(updater.orderProperty());
return properties.toArray(new Property<?>[properties.size()]);
}
private final DropListener dropListener = new DropListener() {
@Override
public void drop(int from, int to) {
long targetTaskId = taskAdapter.getItemId(from);
if (targetTaskId <= 0) return; // This can happen with gestures on empty parts of the list (e.g. extra space below tasks)
long destinationTaskId = taskAdapter.getItemId(to);
try {
if(to >= getListView().getCount())
updater.moveTo(getFilter(), list, targetTaskId, -1);
else
updater.moveTo(getFilter(), list, targetTaskId, destinationTaskId);
} catch (Exception e) {
Log.e("drag", "Drag Error", e); //$NON-NLS-1$ //$NON-NLS-2$
}
fragment.loadTaskListContent(true);
onMetadataChanged(targetTaskId);
}
};
private final SwipeListener swipeListener = new SwipeListener() {
@Override
public void swipeRight(int which) {
indent(which, 1);
}
@Override
public void swipeLeft(int which) {
indent(which, -1);
}
protected void indent(int which, int delta) {
long targetTaskId = taskAdapter.getItemId(which);
if (targetTaskId <= 0) return; // This can happen with gestures on empty parts of the list (e.g. extra space below tasks)
try {
updater.indent(getFilter(), list, targetTaskId, delta);
} catch (Exception e) {
Log.e("drag", "Indent Error", e); //$NON-NLS-1$ //$NON-NLS-2$
}
fragment.loadTaskListContent(true);
onMetadataChanged(targetTaskId);
}
};
private final GrabberClickListener rowClickListener = new GrabberClickListener() {
@Override
public void onLongClick(final View v) {
if(v == null)
return;
fragment.registerForContextMenu(getListView());
getListView().showContextMenuForChild(v);
fragment.unregisterForContextMenu(getListView());
}
@Override
public void onClick(View v) {
if(v == null)
return;
((DraggableTaskAdapter) taskAdapter).getListener().onClick(v);
}
};
public TaskAdapter createTaskAdapter(TodorooCursor<Task> cursor,
AtomicReference<String> sqlQueryTemplate) {
taskAdapter = new DraggableTaskAdapter(fragment, R.layout.task_adapter_row,
cursor, sqlQueryTemplate, false, null);
taskAdapter.addOnCompletedTaskListener(new OnCompletedTaskListener() {
@Override
public void onCompletedTask(Task item, boolean newState) {
setCompletedForItemAndSubtasks(item, newState);
}
});
return taskAdapter;
}
/**
* @param targetTaskId
*/
protected void onMetadataChanged(long targetTaskId) {
// hook
}
private final class DraggableTaskAdapter extends TaskAdapter {
private DraggableTaskAdapter(TaskListFragment activity, int resource,
Cursor c, AtomicReference<String> query, boolean autoRequery,
OnCompletedTaskListener onCompletedTaskListener) {
super(activity, resource, c, query, autoRequery,
onCompletedTaskListener);
applyListeners = APPLY_LISTENERS_NONE;
}
@Override
protected ViewHolder getTagFromCheckBox(View v) {
return (ViewHolder)((View)v.getParent()).getTag();
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
View view = super.newView(context, cursor, parent);
view.getLayoutParams().height = Math.round(45 * metrics.density);
ViewHolder vh = (ViewHolder) view.getTag();
MarginLayoutParams rowParams = (MarginLayoutParams) vh.rowBody.getLayoutParams();
rowParams.topMargin = rowParams.bottomMargin = 0;
ViewGroup.LayoutParams pictureParams = vh.picture.getLayoutParams();
pictureParams.width = pictureParams.height = Math.round(38 * metrics.density);
pictureParams = vh.pictureBorder.getLayoutParams();
pictureParams.width = pictureParams.height = Math.round(38 * metrics.density);
return view;
}
@Override
public synchronized void setFieldContentsAndVisibility(View view) {
super.setFieldContentsAndVisibility(view);
ViewHolder vh = (ViewHolder) view.getTag();
int indent = vh.task.getValue(updater.indentProperty());
vh.rowBody.setPadding(Math.round(indent * 20 * metrics.density), 0, 0, 0);
}
@Override
protected void addListeners(View container) {
super.addListeners(container);
}
public TaskRowListener getListener() {
return listener;
}
}
private final Map<Long, ArrayList<Long>> chainedCompletions =
Collections.synchronizedMap(new HashMap<Long, ArrayList<Long>>());
private void setCompletedForItemAndSubtasks(final Task item, final boolean completedState) {
final long itemId = item.getId();
final Task model = new Task();
final long completionDate = completedState ? DateUtilities.now() : 0;
if(completedState == false) {
ArrayList<Long> chained = chainedCompletions.get(itemId);
if(chained != null) {
for(Long taskId : chained) {
model.setId(taskId);
model.setValue(Task.COMPLETION_DATE, completionDate);
taskService.save(model);
model.clear();
taskAdapter.getCompletedItems().put(taskId, false);
}
taskAdapter.notifyDataSetInvalidated();
}
return;
}
final ArrayList<Long> chained = new ArrayList<Long>();
final int parentIndent = item.getValue(updater.indentProperty());
updater.applyToChildren(getFilter(), list, itemId, new OrderedListNodeVisitor() {
@Override
public void visitNode(Node node) {
Task childTask = taskService.fetchById(node.taskId, Task.RECURRENCE);
if(!TextUtils.isEmpty(childTask.getValue(Task.RECURRENCE))) {
Metadata metadata = updater.getTaskMetadata(list, node.taskId);
metadata.setValue(updater.indentProperty(), parentIndent);
metadataService.save(metadata);
}
model.setId(node.taskId);
model.setValue(Task.COMPLETION_DATE, completionDate);
taskService.save(model);
model.clear();
taskAdapter.getCompletedItems().put(node.taskId, true);
chained.add(node.taskId);
}
});
if(chained.size() > 0) {
chainedCompletions.put(itemId, chained);
taskAdapter.notifyDataSetInvalidated();
}
}
public void setList(LIST list) {
this.list = list;
}
public void onDeleteTask(Task task) {
updater.onDeleteTask(getFilter(), list, task.getId());
taskAdapter.notifyDataSetInvalidated();
}
}

@ -0,0 +1,370 @@
package com.todoroo.astrid.subtasks;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import com.todoroo.andlib.data.Property.IntegerProperty;
import com.todoroo.andlib.data.Property.LongProperty;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.core.PluginServices;
import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.data.Task;
abstract public class OrderedListUpdater<LIST> {
public OrderedListUpdater() {
DependencyInjectionService.getInstance().inject(this);
}
public interface OrderedListIterator {
public void processTask(long taskId, Metadata metadata);
}
// --- abstract and empty
abstract protected Metadata getTaskMetadata(LIST list, long taskId);
abstract protected IntegerProperty indentProperty();
abstract protected LongProperty orderProperty();
abstract protected LongProperty parentProperty();
abstract protected void iterateThroughList(Filter filter, LIST list, OrderedListIterator iterator);
abstract protected Metadata createEmptyMetadata(LIST list, long taskId);
/**
* @param list
* @param filter
*/
protected void initialize(LIST list, Filter filter) {
//
}
/**
* @param list
*/
protected void beforeIndent(LIST list) {
//
}
/**
* @param list
* @param taskId
* @param metadata
* @param indent
* @param order
*/
protected void beforeSaveIndent(LIST list, long taskId, Metadata metadata, int indent, int order) {
//
}
// --- task indenting
/**
* Indent a task and all its children
*/
public void indent(final Filter filter, final LIST list, final long targetTaskId, final int delta) {
if(list == null)
return;
beforeIndent(list);
final AtomicInteger targetTaskIndent = new AtomicInteger(-1);
final AtomicInteger previousIndent = new AtomicInteger(-1);
final AtomicLong previousTask = new AtomicLong(-1);
final AtomicLong globalOrder = new AtomicLong(-1);
iterateThroughList(filter, list, new OrderedListIterator() {
@Override
public void processTask(long taskId, Metadata metadata) {
if(!metadata.isSaved())
metadata = createEmptyMetadata(list, taskId);
int indent = metadata.containsNonNullValue(indentProperty()) ?
metadata.getValue(indentProperty()) : 0;
long order = globalOrder.incrementAndGet();
metadata.setValue(orderProperty(), order);
if(targetTaskId == taskId) {
// if indenting is warranted, indent me and my children
if(indent + delta <= previousIndent.get() + 1 && indent + delta >= 0) {
targetTaskIndent.set(indent);
metadata.setValue(indentProperty(), indent + delta);
if(parentProperty() != null) {
long newParent = computeNewParent(filter, list,
taskId, indent + delta - 1);
if (newParent == taskId)
metadata.setValue(parentProperty(), Task.NO_ID);
else
metadata.setValue(parentProperty(), newParent);
}
saveAndUpdateModifiedDate(metadata, taskId);
}
} else if(targetTaskIndent.get() > -1) {
// found first task that is not beneath target
if(indent <= targetTaskIndent.get())
targetTaskIndent.set(-1);
else {
metadata.setValue(indentProperty(), indent + delta);
saveAndUpdateModifiedDate(metadata, taskId);
}
} else {
previousIndent.set(indent);
previousTask.set(taskId);
}
if(!metadata.isSaved())
saveAndUpdateModifiedDate(metadata, taskId);
}
});
}
/**
* Helper function to iterate through a list and compute a new parent for the target task
* based on the target parent's indent
* @param list
* @param targetTaskId
* @param newIndent
* @return
*/
private long computeNewParent(Filter filter, LIST list, long targetTaskId, int targetParentIndent) {
final AtomicInteger desiredParentIndent = new AtomicInteger(targetParentIndent);
final AtomicLong targetTask = new AtomicLong(targetTaskId);
final AtomicLong lastPotentialParent = new AtomicLong(-1);
final AtomicBoolean computedParent = new AtomicBoolean(false);
iterateThroughList(filter, list, new OrderedListIterator() {
@Override
public void processTask(long taskId, Metadata metadata) {
if (targetTask.get() == taskId) {
computedParent.set(true);
}
int indent = metadata.getValue(indentProperty());
if (!computedParent.get() && indent == desiredParentIndent.get()) {
lastPotentialParent.set(taskId);
}
}
});
if (lastPotentialParent.get() == -1) return Task.NO_ID;
return lastPotentialParent.get();
}
// --- task moving
/**
* Move a task and all its children to the position right above
* taskIdToMoveto. Will change the indent level to match taskIdToMoveTo.
*
* @param newTaskId task we will move above. if -1, moves to end of list
*/
public void moveTo(Filter filter, LIST list, final long targetTaskId,
final long moveBeforeTaskId) {
if(list == null)
return;
Node root = buildTreeModel(filter, list);
Node target = findNode(root, targetTaskId);
if(target != null && target.parent != null) {
if(moveBeforeTaskId == -1) {
target.parent.children.remove(target);
root.children.add(target);
target.parent = root;
} else {
Node sibling = findNode(root, moveBeforeTaskId);
if(sibling != null && !ancestorOf(target, sibling)) {
int index = sibling.parent.children.indexOf(sibling);
if(target.parent == sibling.parent &&
target.parent.children.indexOf(target) < index)
index--;
target.parent.children.remove(target);
sibling.parent.children.add(index, target);
target.parent = sibling.parent;
}
}
}
traverseTreeAndWriteValues(list, root, new AtomicLong(0), -1);
}
private boolean ancestorOf(Node ancestor, Node descendant) {
if(descendant.parent == ancestor)
return true;
if(descendant.parent == null)
return false;
return ancestorOf(ancestor, descendant.parent);
}
protected static class Node {
public final long taskId;
public Node parent;
public final ArrayList<Node> children = new ArrayList<Node>();
public Node(long taskId, Node parent) {
this.taskId = taskId;
this.parent = parent;
}
}
protected void traverseTreeAndWriteValues(LIST list, Node node, AtomicLong order, int indent) {
if(node.taskId != -1) {
Metadata metadata = getTaskMetadata(list, node.taskId);
if(metadata == null)
metadata = createEmptyMetadata(list, node.taskId);
metadata.setValue(orderProperty(), order.getAndIncrement());
metadata.setValue(indentProperty(), indent);
if(parentProperty() != null)
metadata.setValue(parentProperty(), node.parent.taskId);
saveAndUpdateModifiedDate(metadata, node.taskId);
}
for(Node child : node.children) {
traverseTreeAndWriteValues(list, child, order, indent + 1);
}
}
protected Node findNode(Node node, long taskId) {
if(node.taskId == taskId)
return node;
for(Node child : node.children) {
Node found = findNode(child, taskId);
if(found != null)
return found;
}
return null;
}
protected Node buildTreeModel(Filter filter, LIST list) {
final Node root = new Node(-1, null);
final AtomicInteger previoustIndent = new AtomicInteger(-1);
final AtomicReference<Node> currentNode = new AtomicReference<Node>(root);
iterateThroughList(filter, list, new OrderedListIterator() {
@Override
public void processTask(long taskId, Metadata metadata) {
int indent = metadata.getValue(indentProperty());
int previousIndentValue = previoustIndent.get();
if(indent == previousIndentValue) { // sibling
Node parent = currentNode.get().parent;
currentNode.set(new Node(taskId, parent));
parent.children.add(currentNode.get());
} else if(indent > previousIndentValue) { // child
Node parent = currentNode.get();
currentNode.set(new Node(taskId, parent));
parent.children.add(currentNode.get());
} else { // in a different tree
Node node = currentNode.get().parent;
for(int i = indent; i < previousIndentValue; i++)
node = node.parent;
if(node == null)
node = root;
currentNode.set(new Node(taskId, node));
node.children.add(currentNode.get());
}
previoustIndent.set(indent);
}
});
return root;
}
protected final Task taskContainer = new Task();
protected void saveAndUpdateModifiedDate(Metadata metadata, long taskId) {
if(metadata.getSetValues().size() == 0)
return;
PluginServices.getMetadataService().save(metadata);
taskContainer.setId(taskId);
taskContainer.setValue(Task.MODIFICATION_DATE, DateUtilities.now());
taskContainer.setValue(Task.DETAILS_DATE, DateUtilities.now());
PluginServices.getTaskService().save(taskContainer);
}
// --- task cascading operations
public interface OrderedListNodeVisitor {
public void visitNode(Node node);
}
/**
* Apply an operation only to the children of the task
*/
public void applyToChildren(Filter filter, LIST list, long targetTaskId,
OrderedListNodeVisitor visitor) {
Node root = buildTreeModel(filter, list);
Node target = findNode(root, targetTaskId);
if(target != null)
for(Node child : target.children)
applyVisitor(child, visitor);
}
private void applyVisitor(Node node, OrderedListNodeVisitor visitor) {
visitor.visitNode(node);
for(Node child : node.children)
applyVisitor(child, visitor);
}
/**
* Removes a task from the order hierarchy and un-indent children
* @param filter
* @param list
* @param targetTaskId
*/
public void onDeleteTask(Filter filter, LIST list, final long targetTaskId) {
if(list == null)
return;
Node root = buildTreeModel(filter, list);
Node target = findNode(root, targetTaskId);
if(target != null && target.parent != null) {
int targetIndex = target.parent.children.indexOf(target);
target.parent.children.remove(targetIndex);
for(Node node : target.children)
target.parent.children.add(targetIndex++, node);
}
traverseTreeAndWriteValues(list, root, new AtomicLong(0), -1);
}
// --- utility
public void debugPrint(Filter filter, LIST list) {
iterateThroughList(filter, list, new OrderedListIterator() {
public void processTask(long taskId, Metadata metadata) {
System.err.format("id %d: order %d, indent:%d, parent:%d\n", taskId, //$NON-NLS-1$
metadata.getValue(orderProperty()),
metadata.getValue(indentProperty()),
parentProperty() == null ? -1 : metadata.getValue(parentProperty()));
}
});
}
@SuppressWarnings("nls")
public void debugPrint(Node root, int depth) {
for(int i = 0; i < depth; i++) System.err.print(" + ");
System.err.format("%03d", root.taskId);
System.err.print("\n");
for(int i = 0; i < root.children.size(); i++)
debugPrint(root.children.get(i), depth + 1);
}
}

@ -0,0 +1,79 @@
package com.todoroo.astrid.subtasks;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import com.timsu.astrid.R;
import com.todoroo.andlib.data.Property;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.astrid.activity.TaskListFragment;
import com.todoroo.astrid.adapter.TaskAdapter;
import com.todoroo.astrid.data.Task;
/**
* Fragment for subtasks
*
* @author Tim Su <tim@astrid.com>
*
*/
public class SubtasksListFragment extends TaskListFragment {
protected OrderedListFragmentHelper<?> helper;
@Override
public void onActivityCreated(Bundle savedInstanceState) {
helper = createFragmentHelper();
super.onActivityCreated(savedInstanceState);
}
protected OrderedListFragmentHelper<?> createFragmentHelper() {
OrderedListFragmentHelper<String> olfh =
new OrderedListFragmentHelper<String>(this, new SubtasksUpdater());
olfh.setList(SubtasksMetadata.LIST_ACTIVE_TASKS);
return olfh;
}
@Override
protected View getListBody(ViewGroup root) {
return getActivity().getLayoutInflater().inflate(R.layout.task_list_body_subtasks, root, false);
}
@Override
protected void setUpUiComponents() {
super.setUpUiComponents();
helper.setUpUiComponents();
}
@Override
protected void setUpTaskList() {
helper.beforeSetUpTaskList(filter);
super.setUpTaskList();
unregisterForContextMenu(getListView());
}
@Override
public Property<?>[] taskProperties() {
return helper.taskProperties();
}
@Override
protected boolean isDraggable() {
return true;
}
@Override
protected void onTaskDelete(Task task) {
helper.onDeleteTask(task);
}
@Override
protected TaskAdapter createTaskAdapter(TodorooCursor<Task> cursor) {
return helper.createTaskAdapter(cursor, sqlQueryTemplate);
}
}

@ -0,0 +1,31 @@
package com.todoroo.astrid.subtasks;
import com.todoroo.andlib.data.Property.IntegerProperty;
import com.todoroo.andlib.data.Property.LongProperty;
import com.todoroo.andlib.data.Property.StringProperty;
import com.todoroo.astrid.data.Metadata;
/**
* Metadata entries for a Subtask list
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class SubtasksMetadata {
public static final String LIST_ACTIVE_TASKS = "[AT]"; //$NON-NLS-1$
/** metadata key */
public static final String METADATA_KEY = "subtasks"; //$NON-NLS-1$
/** tag name */
public static final StringProperty TAG = new StringProperty(Metadata.TABLE,
Metadata.VALUE1.name);
public static final IntegerProperty INDENT = new IntegerProperty(Metadata.TABLE,
Metadata.VALUE2.name);
public static final LongProperty ORDER = new LongProperty(Metadata.TABLE,
Metadata.VALUE3.name);
}

@ -0,0 +1,77 @@
package com.todoroo.astrid.subtasks;
import android.view.View;
import android.view.ViewGroup;
import com.timsu.astrid.R;
import com.todoroo.andlib.data.Property;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.astrid.actfm.TagViewFragment;
import com.todoroo.astrid.adapter.TaskAdapter;
import com.todoroo.astrid.data.Task;
public class SubtasksTagListFragment extends TagViewFragment {
private final OrderedListFragmentHelper<String> helper;
public SubtasksTagListFragment() {
super();
helper = new OrderedListFragmentHelper<String>(this, new SubtasksUpdater());
}
@Override
protected void postLoadTagData() {
String list = "td:" + tagData.getId(); //$NON-NLS-1$
helper.setList(list);
}
@Override
protected View getListBody(ViewGroup root) {
ViewGroup parent = (ViewGroup) getActivity().getLayoutInflater().inflate(
R.layout.task_list_body_tag, root, false);
taskListView =
getActivity().getLayoutInflater().inflate(R.layout.task_list_body_subtasks, root, false);
parent.addView(taskListView);
return parent;
}
@Override
protected void setUpUiComponents() {
super.setUpUiComponents();
helper.setUpUiComponents();
}
@Override
protected void setUpTaskList() {
helper.beforeSetUpTaskList(filter);
super.setUpTaskList();
unregisterForContextMenu(getListView());
}
@Override
public Property<?>[] taskProperties() {
return helper.taskProperties();
}
@Override
protected boolean isDraggable() {
return true;
}
@Override
protected void onTaskDelete(Task task) {
helper.onDeleteTask(task);
}
@Override
protected TaskAdapter createTaskAdapter(TodorooCursor<Task> cursor) {
return helper.createTaskAdapter(cursor, sqlQueryTemplate);
}
}

@ -0,0 +1,153 @@
package com.todoroo.astrid.subtasks;
import java.util.HashSet;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import com.todoroo.andlib.data.Property.IntegerProperty;
import com.todoroo.andlib.data.Property.LongProperty;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.sql.Criterion;
import com.todoroo.andlib.sql.Query;
import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.dao.TaskDao.TaskCriteria;
import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.service.MetadataService;
import com.todoroo.astrid.service.TaskService;
public class SubtasksUpdater extends OrderedListUpdater<String> {
private static final String METADATA_ID = "mdi"; //$NON-NLS-1$
@Autowired MetadataService metadataService;
@Autowired TaskService taskService;
@Override
protected IntegerProperty indentProperty() {
return SubtasksMetadata.INDENT;
}
@Override
protected LongProperty orderProperty() {
return SubtasksMetadata.ORDER;
}
@Override
protected LongProperty parentProperty() {
return null;
}
@Override
protected void initialize(String list, Filter filter) {
applySubtasksToFilter(filter, list);
sanitizeTaskList(filter, list);
}
@Override
protected Metadata getTaskMetadata(String list, long taskId) {
TodorooCursor<Metadata> cursor = metadataService.query(Query.select(Metadata.PROPERTIES).where(
Criterion.and(
Metadata.TASK.eq(taskId),
Metadata.KEY.eq(SubtasksMetadata.METADATA_KEY),
SubtasksMetadata.TAG.eq(list))));
try {
cursor.moveToFirst();
if(cursor.isAfterLast())
return null;
return new Metadata(cursor);
} finally {
cursor.close();
}
}
@Override
protected Metadata createEmptyMetadata(String list, long taskId) {
Metadata m = new Metadata();
m.setValue(Metadata.TASK, taskId);
m.setValue(Metadata.KEY, SubtasksMetadata.METADATA_KEY);
m.setValue(SubtasksMetadata.TAG, list);
return m;
}
@Override
protected void iterateThroughList(Filter filter, String list, OrderedListIterator iterator) {
TodorooCursor<Task> cursor = taskService.query(Query.select(Task.ID,
Metadata.ID.as(METADATA_ID), Metadata.TASK, Metadata.KEY, SubtasksMetadata.INDENT,
SubtasksMetadata.ORDER).withQueryTemplate(filter.sqlQuery));
TodorooCursor<Metadata> metadataCursor = new TodorooCursor<Metadata>(cursor.getCursor(),
cursor.getProperties());
Metadata metadata = new Metadata();
try {
for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
metadata.readFromCursor(metadataCursor);
metadata.setId(cursor.getLong(cursor.getColumnIndex(METADATA_ID)));
iterator.processTask(cursor.get(Task.ID), metadata);
}
} finally {
cursor.close();
}
}
@SuppressWarnings("nls")
public void applySubtasksToFilter(Filter filter, String tagName) {
String query = filter.sqlQuery;
if(tagName == null)
tagName = SubtasksMetadata.LIST_ACTIVE_TASKS;
String subtaskJoin = String.format("LEFT JOIN %s ON (%s = %s AND %s = '%s' AND %s = '%s') ",
Metadata.TABLE, Task.ID, Metadata.TASK,
Metadata.KEY, SubtasksMetadata.METADATA_KEY,
SubtasksMetadata.TAG, tagName);
if(!query.contains(subtaskJoin)) {
query = subtaskJoin + query;
query = query.replaceAll("ORDER BY .*", "");
query = query + String.format(" ORDER BY %s, %s, IFNULL(CAST(%s AS LONG), %s)",
Task.DELETION_DATE, Task.COMPLETION_DATE,
SubtasksMetadata.ORDER, Task.CREATION_DATE);
query = query.replace(TaskCriteria.isVisible().toString(),
Criterion.all.toString());
filter.sqlQuery = query;
}
}
public void sanitizeTaskList(Filter filter, String list) {
final AtomicInteger previousIndent = new AtomicInteger(-1);
final AtomicLong previousOrder = new AtomicLong(-1);
final HashSet<Long> taskIds = new HashSet<Long>();
iterateThroughList(filter, list, new OrderedListIterator() {
@Override
public void processTask(long taskId, Metadata metadata) {
if(!metadata.isSaved())
return;
if(taskIds.contains(taskId)) {
metadataService.delete(metadata);
return;
}
long order = metadata.getValue(SubtasksMetadata.ORDER);
if(order <= previousOrder.get()) // bad
order = previousOrder.get() + 1;
int indent = metadata.getValue(SubtasksMetadata.INDENT);
if(indent < 0 || indent > previousIndent.get() + 1) // bad
indent = Math.max(0, previousIndent.get());
metadata.setValue(SubtasksMetadata.ORDER, order);
metadata.setValue(SubtasksMetadata.INDENT, indent);
saveAndUpdateModifiedDate(metadata, taskId);
previousIndent.set(indent);
previousOrder.set(order);
taskIds.add(taskId);
}
});
}
}

@ -35,9 +35,10 @@ public class TagCaseMigrator {
private final HashMap<String, Long> nameToRemoteId = new HashMap<String, Long>();
private final HashMap<String, Integer> nameCountMap = new HashMap<String, Integer>();
public void performTagCaseMigration(Context context) {
public void performTagCaseMigration(@SuppressWarnings("unused") Context context) {
if (!Preferences.getBoolean(PREF_CASE_MIGRATION_PERFORMED, false)) {
TagService.Tag[] allTagData = TagService.getInstance().getGroupedTags(TagService.GROUPED_TAGS_BY_ALPHA, Criterion.all);
TagService.Tag[] allTagData = TagService.getInstance().getGroupedTags(
TagService.GROUPED_TAGS_BY_ALPHA, Criterion.all);
boolean shouldShowDialog = false;
for (int i = 0; i < allTagData.length - 1; i++) {

@ -12,6 +12,7 @@ import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
@ -36,13 +37,16 @@ import com.todoroo.astrid.api.FilterListItem;
import com.todoroo.astrid.api.FilterWithCustomIntent;
import com.todoroo.astrid.api.FilterWithUpdate;
import com.todoroo.astrid.core.PluginServices;
import com.todoroo.astrid.core.SortHelper;
import com.todoroo.astrid.dao.TaskDao.TaskCriteria;
import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.data.TagData;
import com.todoroo.astrid.gtasks.GtasksPreferenceService;
import com.todoroo.astrid.service.AstridDependencyInjector;
import com.todoroo.astrid.service.TagDataService;
import com.todoroo.astrid.subtasks.SubtasksTagListFragment;
import com.todoroo.astrid.tags.TagService.Tag;
import com.todoroo.astrid.utility.AstridPreferences;
/**
* Exposes filters based on tags
@ -54,6 +58,7 @@ public class TagFilterExposer extends BroadcastReceiver implements AstridFilterE
private static final String TAG = "tag"; //$NON-NLS-1$
public static final String TAG_SQL = "tagSql"; //$NON-NLS-1$
public static final String SHOW_ACTIVE_TASKS = "show_main_task_view"; //$NON-NLS-1$
@Autowired TagDataService tagDataService;
@Autowired GtasksPreferenceService gtasksPreferenceService;
@ -76,7 +81,8 @@ public class TagFilterExposer extends BroadcastReceiver implements AstridFilterE
filter.color = Color.GRAY;
}
TagData tagData = PluginServices.getTagDataService().getTag(tag.tag, TagData.ID, TagData.USER_ID, TagData.MEMBER_COUNT);
TagData tagData = PluginServices.getTagDataService().getTag(tag.tag, TagData.ID,
TagData.USER_ID, TagData.MEMBER_COUNT);
int deleteIntentLabel;
if (tagData != null && tagData.getValue(TagData.MEMBER_COUNT) > 0 && tagData.getValue(TagData.USER_ID) != 0)
deleteIntentLabel = R.string.tag_cm_leave;
@ -91,7 +97,13 @@ public class TagFilterExposer extends BroadcastReceiver implements AstridFilterE
newTagIntent(context, RenameTagActivity.class, tag, tagTemplate.toString()),
newTagIntent(context, DeleteTagActivity.class, tag, tagTemplate.toString())
};
filter.customTaskList = new ComponentName(ContextManager.getContext(), TagViewFragment.class);
SharedPreferences publicPrefs = AstridPreferences.getPublicPrefs(context);
int sortFlags = publicPrefs.getInt(SortHelper.PREF_SORT_FLAGS, 0);
Class<?> fragmentClass = SortHelper.isManualSort(sortFlags) ?
SubtasksTagListFragment.class : TagViewFragment.class;
filter.customTaskList = new ComponentName(ContextManager.getContext(), fragmentClass);
if(tag.image != null)
filter.imageUrl = tag.image;
if(tag.updateText != null)

@ -132,12 +132,31 @@ public final class TagService {
* @return
*/
public QueryTemplate queryTemplate(Criterion criterion) {
return new QueryTemplate().join(Join.inner(Metadata.TABLE.as("mtags"),
Criterion.and(Task.ID.eq(Field.field("mtags." + Metadata.TASK.name)),
Field.field("mtags." + Metadata.KEY.name).eq(KEY),
Field.field("mtags." + TAG.name).eqCaseInsensitive(tag)))).where(criterion);
}
/**
* Return SQL selector query for getting tasks with a given tagData
*
* @param tagData
* @return
*/
public static QueryTemplate queryTemplate(Criterion criterion, TagData tagData) {
return new QueryTemplate().join(Join.inner(Metadata.TABLE,
Task.ID.eq(Metadata.TASK))).where(tagEqIgnoreCase(tag, criterion));
Task.ID.eq(Metadata.TASK))).where(tagEqIgnoreCase(tagData.getValue(TagData.NAME), criterion));
}
}
public static Criterion memberOfTagData(long tagDataRemoteId) {
return Task.ID.in(Query.select(Metadata.TASK).from(Metadata.TABLE).where(
Criterion.and(Metadata.KEY.eq(KEY), REMOTE_ID.eq(tagDataRemoteId))));
}
public static Criterion tagEq(String tag, Criterion additionalCriterion) {
return Criterion.and(
MetadataCriteria.withKey(KEY), TAG.eq(tag),

@ -40,8 +40,8 @@ public final class TagsControlSet extends PopupControlSet {
// --- instance variables
//private final Spinner tagSpinner;
//@Autowired private TagDataService tagDataService;
private static final String TRANSITORY_TAGS = "tags";//$NON-NLS-1$
private final TagService tagService = TagService.getInstance();
private ArrayList<String> allTagNames;
@ -124,8 +124,8 @@ public final class TagsControlSet extends PopupControlSet {
tags.add(tagName.getText().toString());
}
} else {
if (model.getTransitory("tags") != null) {
return (LinkedHashSet<String>) model.getTransitory("tags");
if (model.getTransitory(TRANSITORY_TAGS) != null) {
return (LinkedHashSet<String>) model.getTransitory(TRANSITORY_TAGS);
}
}
return tags;
@ -233,7 +233,7 @@ public final class TagsControlSet extends PopupControlSet {
} finally {
cursor.close();
}
model.putTransitory("tags", tags); //$NON-NLS-1$
model.putTransitory(TRANSITORY_TAGS, tags);
}
}
@ -253,7 +253,7 @@ public final class TagsControlSet extends PopupControlSet {
}
private void selectTagsFromModel() {
LinkedHashSet<String> tags = (LinkedHashSet<String>) model.getTransitory("tags");
LinkedHashSet<String> tags = (LinkedHashSet<String>) model.getTransitory(TRANSITORY_TAGS);
if (tags != null) {
for (String tag : tags) {
setTagSelected(tag);

@ -150,19 +150,12 @@ public class TaskRabbitActivity extends FragmentActivity {
public static final String LOCATION_CONTAINER = "other_locations_attributes"; //$NON-NLS-1$
// Non-production values
public static final String TASK_RABBIT_URL = "http://www.taskrabbit.com"; //$NON-NLS-1$
public static final String TASK_RABBIT_CLIENT_ID = "RZUDrMuGn9Q3dXeq4nL24bM6LZmMCi1CEGgfP4ND"; //$NON-NLS-1$
public static final String TASK_RABBIT_CLIENT_APPLICATION_ID = "Va7FUIUTprsmyuwAq9eHSZvAgiRj8FVH1zeaM8Zt"; //$NON-NLS-1$
// public static final String TASK_RABBIT_URL = "http://rs-astrid-api.taskrabbit.com"; //$NON-NLS-1$
// public static final String TASK_RABBIT_CLIENT_ID = "fDTmGeR0uNCvoxopNyqsRWae8xOvbOBqC7jmHaxv"; //$NON-NLS-1$
// public static final String TASK_RABBIT_CLIENT_APPLICATION_ID = "XBpKshU8utH5eaNmhky9N8aAId5rSLTh04Hi60Co"; //$NON-NLS-1$
public static final String TASK_RABBIT_URL = "http://rs-astrid-api.taskrabbit.com"; //$NON-NLS-1$
public static final String TASK_RABBIT_CLIENT_ID = "fDTmGeR0uNCvoxopNyqsRWae8xOvbOBqC7jmHaxv"; //$NON-NLS-1$
public static final String TASK_RABBIT_CLIENT_APPLICATION_ID = "XBpKshU8utH5eaNmhky9N8aAId5rSLTh04Hi60Co"; //$NON-NLS-1$
public static final String TASK_RABBIT_ID = "id"; //$NON-NLS-1$
private TaskRabbitTaskContainer taskRabbitTask;
/* From tag settings */
private boolean isDialog;
public TaskRabbitActivity() {
DependencyInjectionService.getInstance().inject(this);
}
@ -593,7 +586,6 @@ public class TaskRabbitActivity extends FragmentActivity {
Header contentType = new BasicHeader("Content-Type", "application/json");
HttpEntity taskBody = getTaskBody();
String response = null;
try {
response = restClient.post(taskRabbitURL(urlCall), taskBody, contentType, authorization);
Log.e("The response", "The post response: " + response);
@ -769,6 +761,7 @@ public class TaskRabbitActivity extends FragmentActivity {
}
@SuppressWarnings("nls")
@Override
public void onActivityResult (int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_CODE_TASK_RABBIT_OAUTH && resultCode == Activity.RESULT_OK){
@ -780,8 +773,10 @@ public class TaskRabbitActivity extends FragmentActivity {
result = result.substring(result.indexOf(key)+key.length());
Preferences.setString(TASK_RABBIT_TOKEN, result);
String response = restClient.get(taskRabbitURL("account")); //$NON-NLS-1$
saveUserInfo(response);
String url = String.format("%s?oauth_token=%s&client_application=",taskRabbitURL("account"), Preferences.getStringValue(TASK_RABBIT_TOKEN), TASK_RABBIT_CLIENT_APPLICATION_ID);
String response = restClient.get(url);
saveUserInfo(response);//;
}
catch (Exception e){
e.printStackTrace();
@ -819,15 +814,15 @@ public class TaskRabbitActivity extends FragmentActivity {
/* location calls */
private boolean supportsSelectedLocation() {
for (TaskRabbitSetListener controlSet : controls) {
if (TaskRabbitLocationControlSet.class.isAssignableFrom(controlSet.getClass())) {
TaskRabbitLocationControlSet locationControlSet = (TaskRabbitLocationControlSet) controlSet;
if(!TaskRabbitLocationManager.supportsCurrentLocation(locationControlSet.location) && locationControlSet.getDisplayView().getParent() != null) {
return false;
}
for (TaskRabbitSetListener controlSet : controls) {
if (TaskRabbitLocationControlSet.class.isAssignableFrom(controlSet.getClass())) {
TaskRabbitLocationControlSet locationControlSet = (TaskRabbitLocationControlSet) controlSet;
if(!TaskRabbitLocationManager.supportsCurrentLocation(locationControlSet.location) && locationControlSet.getDisplayView().getParent() != null) {
return false;
}
}
return true;
}
return true;
}
public void updateControlSetLocation (Location location) {

@ -19,6 +19,7 @@ import android.widget.Toast;
import com.timsu.astrid.R;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.ContextManager;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.service.RestClient;
import com.todoroo.andlib.utility.Preferences;
@ -143,7 +144,8 @@ public class TaskRabbitControlSet extends TaskEditControlSet implements Assigned
* Show toast for task edit canceling
*/
private void showSuccessToast() {
Toast.makeText(fragment.getActivity(), fragment.getString(R.string.tr_success_toast),
Toast.makeText(ContextManager.getContext(),
ContextManager.getString(R.string.tr_success_toast),
Toast.LENGTH_SHORT).show();
}

@ -102,13 +102,13 @@ public class TaskRabbitDeadlineControlSet extends PopupControlSet implements Tas
@Override
public void saveToDatabase(JSONObject json, String key) throws JSONException {
json.put(key, dateAndTimePicker.constructDueDate());
json.put(key, dateAndTimePicker.constructDueDate()/1000);
}
@Override
public void postToTaskRabbit(JSONObject json, String key) throws JSONException {
long dueDate = dateAndTimePicker.constructDueDate();
long dueDate = dateAndTimePicker.constructDueDate()/1000;
json.put(key, dueDate);
}

@ -38,10 +38,20 @@ public class TaskRabbitLocationManager {
new GeoPoint(47606210, -122332070), //SEA
new GeoPoint(29424120, -98493630) //SAN ANTONIO
};
public boolean isLocationUpdatesEnabled() {
boolean provider_enabled=false;
try{provider_enabled=lm.isProviderEnabled(LocationManager.GPS_PROVIDER);}catch(Exception ex){}
try{provider_enabled=provider_enabled || lm.isProviderEnabled(LocationManager.NETWORK_PROVIDER);}catch(Exception ex){}
boolean provider_enabled = false;
try {
provider_enabled = lm.isProviderEnabled(LocationManager.GPS_PROVIDER);
} catch (Exception ex) {
// suppress
}
try {
provider_enabled = provider_enabled
|| lm.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
} catch (Exception ex) {
// suppress
}
return provider_enabled;
}
public Location getLastKnownLocation()
@ -49,8 +59,9 @@ public class TaskRabbitLocationManager {
boolean gps_supported=false;
boolean network_supported=false;
Location location = null;
try{gps_supported=lm.isProviderEnabled(LocationManager.GPS_PROVIDER);}catch(Exception ex){}
try{network_supported=lm.isProviderEnabled(LocationManager.NETWORK_PROVIDER);}catch(Exception ex){}
gps_supported = isGpsEnabled();;
network_supported = isNetworkProviderEnabled();
if(!gps_supported && !network_supported)
return null;
@ -75,53 +86,59 @@ public class TaskRabbitLocationManager {
return false;
}
public boolean getLocation(LocationResult result)
{
locationResult=result;
try{gps_enabled=lm.isProviderEnabled(LocationManager.GPS_PROVIDER);}catch(Exception ex){}
try{network_enabled=lm.isProviderEnabled(LocationManager.NETWORK_PROVIDER);}catch(Exception ex){}
locationResult = result;
gps_enabled = isGpsEnabled();
network_enabled = isNetworkProviderEnabled();
if(!gps_enabled && !network_enabled)
return false;
if(gps_enabled)
lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locationListenerGps);
lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locationListener);
if(network_enabled)
lm.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListenerNetwork);
lm.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListener);
timer1=new Timer();
timer1.schedule(new GetLastLocation(), 20000);
return true;
}
LocationListener locationListenerGps = new LocationListener() {
public void onLocationChanged(Location location) {
timer1.cancel();
locationResult.gotLocation(location);
lm.removeUpdates(this);
lm.removeUpdates(locationListenerNetwork);
private boolean isNetworkProviderEnabled() {
try {
return lm.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
} catch (Exception ex) {
// suppress
}
public void onProviderDisabled(String provider) {}
public void onProviderEnabled(String provider) {}
public void onStatusChanged(String provider, int status, Bundle extras) {}
};
return false;
}
private boolean isGpsEnabled() {
try {
return lm.isProviderEnabled(LocationManager.GPS_PROVIDER);
} catch (Exception ex) {
// suppress
}
return false;
}
LocationListener locationListenerNetwork = new LocationListener() {
private final LocationListener locationListener = new LocationListener() {
public void onLocationChanged(Location location) {
timer1.cancel();
locationResult.gotLocation(location);
lm.removeUpdates(this);
lm.removeUpdates(locationListenerGps);
lm.removeUpdates(locationListener);
}
public void onProviderDisabled(String provider) {}
public void onProviderEnabled(String provider) {}
public void onStatusChanged(String provider, int status, Bundle extras) {}
public void onProviderDisabled(String provider) { /**/ }
public void onProviderEnabled(String provider) { /**/ }
public void onStatusChanged(String provider, int status, Bundle extras) { /**/ }
};
class GetLastLocation extends TimerTask {
@Override
public void run() {
lm.removeUpdates(locationListenerGps);
lm.removeUpdates(locationListenerNetwork);
lm.removeUpdates(locationListener);
Location net_loc=null, gps_loc=null;
if(gps_enabled)

@ -120,7 +120,7 @@ public class TaskRabbitMapActivity extends MapActivity implements LocationListen
AlertDialog.Builder adb = new AlertDialog.Builder(TaskRabbitMapActivity.this);
adb.setTitle(getString(R.string.tr_alert_location_fail_title));
adb.setMessage(getString(R.string.tr_alert_location_fail_message));
adb.setPositiveButton("Close",null);
adb.setPositiveButton(R.string.DLG_close, null);
adb.show();
}
}
@ -200,7 +200,7 @@ public class TaskRabbitMapActivity extends MapActivity implements LocationListen
} catch (Exception e) {
e.printStackTrace();
}
return "";
return ""; //$NON-NLS-1$
}
private String updateAddress(Address address){
String addressString = null;
@ -256,7 +256,7 @@ public class TaskRabbitMapActivity extends MapActivity implements LocationListen
}
private Location geoPointToLocation(GeoPoint geoPoint) {
Location location = new Location("");
Location location = new Location(""); //$NON-NLS-1$
location.setLatitude(((long)geoPoint.getLatitudeE6())/1E6);
location.setLongitude(((long)geoPoint.getLongitudeE6())/1E6);
return location;

@ -16,7 +16,6 @@ import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.timsu.astrid.R;
@ -32,8 +31,6 @@ public class TaskRabbitNameControlSet extends PopupControlSet implements TaskRab
protected final EditText editText;
protected final TextView notesPreview;
private final LinearLayout notesBody;
private final ImageButton pictureButton;
private Bitmap pendingCommentPicture = null;
@ -44,7 +41,6 @@ public class TaskRabbitNameControlSet extends PopupControlSet implements TaskRab
super(activity, viewLayout, displayViewLayout, titleID);
editText = (EditText) getView().findViewById(R.id.notes);
notesPreview = (TextView) getDisplayView().findViewById(R.id.display_row_edit);
notesBody = (LinearLayout) getDisplayView().findViewById(R.id.notes_body);
displayText.setText(activity.getString(titleID));
editText.setMaxLines(Integer.MAX_VALUE);

@ -35,7 +35,6 @@ public class TimerActionControlSet extends TaskEditControlSet {
}
@Override
@SuppressWarnings("hiding")
protected void readFromTaskOnInitialize() {
if (model.getValue(Task.TIMER_START) == 0)
timerActive = false;
@ -51,7 +50,6 @@ public class TimerActionControlSet extends TaskEditControlSet {
}
@Override
@SuppressWarnings("hiding")
protected String writeToModelAfterInitialized(Task task) {
// Nothing to do here
return null;
@ -96,7 +94,7 @@ public class TimerActionControlSet extends TaskEditControlSet {
chronometer.setOnChronometerTickListener(new OnChronometerTickListener() {
public void onChronometerTick(Chronometer cArg) {
long t = SystemClock.elapsedRealtime() - cArg.getBase();
cArg.setText(DateFormat.format("d'd' h:mm", t));
cArg.setText(DateFormat.format("d'd' h:mm", t)); //$NON-NLS-1$
}
});

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 955 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 930 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 435 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 974 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 905 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 505 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 623 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 237 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 214 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 202 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 200 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid
android:color="#60000000" />
</shape>

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:startColor="#ffffffff"
android:centerColor="#ff000000"
android:endColor="#ffffffff"
android:angle="0" />
</shape>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 983 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 655 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 676 B

@ -1,20 +0,0 @@
<?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.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:drawable="@drawable/none" />
</selector>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 832 B

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:startColor="#008d43"
android:endColor="#00522b"/>
</shape>

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save