Merge branch 'master' into 4.0

Conflicts:
	astrid/AndroidManifest.xml
	astrid/plugin-src/com/todoroo/astrid/actfm/TagViewActivity.java
	astrid/project.properties
	astrid/res/drawable-hdpi/widget_frame.png
	astrid/res/drawable-land-hdpi/widget_frame.png
	astrid/res/drawable-land/widget_frame.png
	astrid/res/drawable/widget_frame.png
	astrid/res/layout/task_list_activity.xml
	astrid/src/com/todoroo/astrid/activity/FilterListActivity.java
	astrid/src/com/todoroo/astrid/activity/TaskListActivity.java
	greendroid/GreenDroid/.classpath
pull/14/head
Tim Su 14 years ago
commit 6f6956dcd8

@ -526,16 +526,55 @@ public class AndroidUtilities {
* @param args arguments
* @return method return value, or null if nothing was called or exception
*/
@SuppressWarnings("nls")
public static Object callApiMethod(int minSdk, Object receiver,
String methodName, Class<?>[] params, Object... args) {
if(getSdkVersion() < minSdk)
return null;
Method method;
return AndroidUtilities.callMethod(receiver.getClass(),
receiver, methodName, params, args);
}
/**
* Call a static method via reflection if API level is at least minSdk
* @param minSdk minimum sdk number (i.e. 8)
* @param className fully qualified class to call method on
* @param methodName method name to call
* @param params method parameter types
* @param args arguments
* @return method return value, or null if nothing was called or exception
*/
@SuppressWarnings("nls")
public static Object callApiStaticMethod(int minSdk, String className,
String methodName, Class<?>[] params, Object... args) {
if(getSdkVersion() < minSdk)
return null;
try {
method = receiver.getClass().getMethod(methodName, params);
return method.invoke(receiver, args);
return AndroidUtilities.callMethod(Class.forName(className),
null, methodName, params, args);
} catch (ClassNotFoundException e) {
getExceptionService().reportError("call-method", e);
return null;
}
}
/**
* Call a method via reflection
* @param class class to call method on
* @param receiver object to call method on (can be null)
* @param methodName method name to call
* @param params method parameter types
* @param args arguments
* @return method return value, or null if nothing was called or exception
*/
@SuppressWarnings("nls")
public static Object callMethod(Class<?> cls, Object receiver,
String methodName, Class<?>[] params, Object... args) {
try {
Method method = cls.getMethod(methodName, params);
Object result = method.invoke(receiver, args);
return result;
} catch (SecurityException e) {
getExceptionService().reportError("call-method", e);
} catch (NoSuchMethodException e) {

@ -171,10 +171,6 @@ public class TaskApiDao extends ContentResolverDao<Task> {
values.size() <= 2)
return true;
if(values.containsKey(Task.ELAPSED_SECONDS.name) &&
values.size() <= 2)
return true;
return false;
}

@ -29,5 +29,7 @@
<classpathentry exported="true" kind="lib" path="libs/gson-1.6.jar"/>
<classpathentry exported="true" kind="lib" path="libs/google-api-services-tasks-v1-1.2.5-beta.jar"/>
<classpathentry kind="lib" path="libs/crittercism_v1_1_3.jar"/>
<classpathentry kind="lib" path="libs/jchronic-0.2.3.jar"/>
<classpathentry kind="lib" path="libs/android-support-v4.jar"/>
<classpathentry kind="output" path="bin/classes"/>
</classpath>

@ -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="3.9.1.1"
android:versionCode="206">
android:versionName="3.9.2"
android:versionCode="207">
<!-- widgets, alarms, and services will break if Astrid is installed on SD card -->
<!-- android:installLocation="internalOnly"> -->
@ -318,12 +318,6 @@
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
<receiver android:name="com.todoroo.astrid.actfm.ActFmSyncActionExposer">
<intent-filter>
<action android:name="com.todoroo.astrid.REQUEST_SYNC_ACTIONS" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
<activity android:name="com.todoroo.astrid.actfm.TagViewWrapperActivity"
android:windowSoftInputMode="stateHidden|adjustResize"
android:theme="@style/Theme">
@ -641,6 +635,9 @@
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
<activity android:name="com.todoroo.astrid.welcome.tutorial.WelcomeWalkthrough"
android:windowSoftInputMode="stateHidden"
android:screenOrientation="portrait"></activity>
</application>

@ -6,7 +6,7 @@
<intAttribute key="ch.zork.quicklaunch.index" value="0"/>
<stringAttribute key="ch.zork.quicklaunch.mode" value="run"/>
<intAttribute key="com.android.ide.eclipse.adt.action" value="0"/>
<stringAttribute key="com.android.ide.eclipse.adt.activity" value="com.todoroo.astrid.activity.FilterListActivity"/>
<stringAttribute key="com.android.ide.eclipse.adt.activity" value="com.todoroo.astrid.activity.TaskEditActivity"/>
<stringAttribute key="com.android.ide.eclipse.adt.commandline" value="-scale 0.7 -partition-size 1024"/>
<intAttribute key="com.android.ide.eclipse.adt.delay" value="0"/>
<booleanAttribute key="com.android.ide.eclipse.adt.nobootanim" value="true"/>

@ -142,7 +142,7 @@
replace="\1 true;" />
</target>
<target name="obfuscate">
<target name="obfuscate" depends="-setup">
<property name="proguard.enabled" value="true" />
<antcall target="-obfuscate" />
</target>

@ -8,15 +8,6 @@
package com.localytics.android;
import android.Manifest.permission;
import android.content.Context;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Build;
import android.telephony.TelephonyManager;
import android.util.Log;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
@ -27,6 +18,15 @@ import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import android.Manifest.permission;
import android.content.Context;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Build;
import android.telephony.TelephonyManager;
import android.util.Log;
/**
* Provides a number of static functions to aid in the collection and formatting of datapoints.
* <p>

@ -1,9 +1,9 @@
package com.localytics.android;
import android.Manifest.permission;
import org.json.JSONArray;
import android.Manifest.permission;
/**
* Set of constants for building JSON objects that get sent to the Localytics web service.
*/

@ -1,5 +1,13 @@
package com.localytics.android;
import java.io.File;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
@ -10,14 +18,6 @@ import android.database.sqlite.SQLiteQueryBuilder;
import android.provider.BaseColumns;
import android.util.Log;
import java.io.File;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* Implements the storage mechanism for the Localytics library. The interface and implementation are similar to a ContentProvider
* but modified to be better suited to a library. The interface is table-oriented, rather than Uri-oriented.

@ -8,23 +8,6 @@
package com.localytics.android;
import android.Manifest.permission;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.CursorJoiner;
import android.os.Build;
import android.os.Build.VERSION;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.Log;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
@ -51,6 +34,23 @@ import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.Manifest.permission;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.CursorJoiner;
import android.os.Build;
import android.os.Build.VERSION;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.Log;
import com.localytics.android.JsonObjects.BlobHeader;
import com.localytics.android.LocalyticsProvider.ApiKeysDbColumns;
import com.localytics.android.LocalyticsProvider.AttributesDbColumns;

@ -0,0 +1,198 @@
package com.mdimension.jchronic;
import java.util.LinkedList;
import java.util.List;
import com.mdimension.jchronic.handlers.Handler;
import com.mdimension.jchronic.numerizer.Numerizer;
import com.mdimension.jchronic.repeaters.Repeater;
import com.mdimension.jchronic.tags.Grabber;
import com.mdimension.jchronic.tags.Ordinal;
import com.mdimension.jchronic.tags.Pointer;
import com.mdimension.jchronic.tags.Scalar;
import com.mdimension.jchronic.tags.Separator;
import com.mdimension.jchronic.tags.TimeZone;
import com.mdimension.jchronic.utils.Span;
import com.mdimension.jchronic.utils.Token;
public class AstridChronic {
public static final String VERSION = "0.2.3";
private AstridChronic() {
// DO NOTHING
}
public static Span parse(String text) {
return AstridChronic.parse(text, new Options());
}
/**
* Parses a string containing a natural language date or time. If the parser
* can find a date or time, either a Time or Chronic::Span will be returned
* (depending on the value of <tt>:guess</tt>). If no date or time can be found,
* +nil+ will be returned.
*
* Options are:
*
* [<tt>:context</tt>]
* <tt>:past</tt> or <tt>:future</tt> (defaults to <tt>:future</tt>)
*
* If your string represents a birthday, you can set <tt>:context</tt> to <tt>:past</tt>
* and if an ambiguous string is given, it will assume it is in the
* past. Specify <tt>:future</tt> or omit to set a future context.
*
* [<tt>:now</tt>]
* Time (defaults to Time.now)
*
* By setting <tt>:now</tt> to a Time, all computations will be based off
* of that time instead of Time.now
*
* [<tt>:guess</tt>]
* +true+ or +false+ (defaults to +true+)
*
* By default, the parser will guess a single point in time for the
* given date or time. If you'd rather have the entire time span returned,
* set <tt>:guess</tt> to +false+ and a Chronic::Span will be returned.
*
* [<tt>:ambiguous_time_range</tt>]
* Integer or <tt>:none</tt> (defaults to <tt>6</tt> (6am-6pm))
*
* If an Integer is given, ambiguous times (like 5:00) will be
* assumed to be within the range of that time in the AM to that time
* in the PM. For example, if you set it to <tt>7</tt>, then the parser will
* look for the time between 7am and 7pm. In the case of 5:00, it would
* assume that means 5:00pm. If <tt>:none</tt> is given, no assumption
* will be made, and the first matching instance of that time will
* be used.
*/
@SuppressWarnings("unchecked")
public static Span parse(String text, Options options) {
// store now for later =)
//_now = options.getNow();
// put the text into a normal format to ease scanning
String normalizedText = AstridChronic.preNormalize(text);
// get base tokens for each word
List<Token> tokens = AstridChronic.baseTokenize(normalizedText);
try {
tokens = Repeater.scan(tokens, options);
} catch (Throwable e) {
throw new RuntimeException("Failed to scan tokens.", e);
}
List<Class> scannerClasses = new LinkedList<Class>();
scannerClasses.add(Grabber.class);
scannerClasses.add(Pointer.class);
scannerClasses.add(Scalar.class);
scannerClasses.add(Ordinal.class);
scannerClasses.add(Separator.class);
scannerClasses.add(TimeZone.class);
for (Class scannerClass : scannerClasses) {
try {
tokens = (List<Token>) scannerClass.getMethod("scan", List.class, Options.class).invoke(null, tokens, options);
}
catch (Throwable e) {
throw new RuntimeException("Failed to scan tokens.", e);
}
}
List<Token> taggedTokens = new LinkedList<Token>();
for (Token token : tokens) {
if (token.isTagged()) {
taggedTokens.add(token);
}
}
tokens = taggedTokens;
if (options.isDebug()) {
System.out.println("Chronic.parse: " + tokens);
}
Span span = Handler.tokensToSpan(tokens, options);
// guess a time within a span if required
if (options.isGuess()) {
span = guess(span);
}
return span;
}
/**
* Clean up the specified input text by stripping unwanted characters,
* converting idioms to their canonical form, converting number words
* to numbers (three => 3), and converting ordinal words to numeric
* ordinals (third => 3rd)
*/
protected static String preNormalize(String text) {
String normalizedText = text.toLowerCase();
normalizedText = Chronic.numericizeNumbers(normalizedText);
normalizedText = normalizedText.replaceAll("['\"\\.]", "");
normalizedText = normalizedText.replaceAll("([/\\-,@])", " $1 ");
normalizedText = normalizedText.replaceAll("\\btoday\\b", "this day");
normalizedText = normalizedText.replaceAll("\\btomm?orr?ow\\b", "next day");
normalizedText = normalizedText.replaceAll("\\byesterday\\b", "last day");
normalizedText = normalizedText.replaceAll("\\bnoon\\b", "12:00");
normalizedText = normalizedText.replaceAll("\\bmidnight\\b", "24:00");
normalizedText = normalizedText.replaceAll("\\bbefore now\\b", "past");
normalizedText = normalizedText.replaceAll("\\bnow\\b", "this second");
normalizedText = normalizedText.replaceAll("\\b(ago|before)\\b", "past");
normalizedText = normalizedText.replaceAll("\\bthis past\\b", "last");
normalizedText = normalizedText.replaceAll("\\bthis last\\b", "last");
normalizedText = normalizedText.replaceAll("\\b(?:in|during) the (morning)\\b", "$1");
normalizedText = normalizedText.replaceAll("\\b(?:in the|during the|at) (afternoon|evening|night)\\b", "$1");
normalizedText = normalizedText.replaceAll("\\btonight\\b", "this night");
normalizedText = normalizedText.replaceAll("(?=\\w)([ap]m|oclock)\\b", " $1");
normalizedText = normalizedText.replaceAll("\\b(hence|after|from)\\b", "future");
normalizedText = AstridChronic.numericizeOrdinals(normalizedText);
return normalizedText;
}
/**
* Convert number words to numbers (three => 3)
*/
protected static String numericizeNumbers(String text) {
return Numerizer.numerize(text);
}
/**
* Convert ordinal words to numeric ordinals (third => 3rd)
*/
protected static String numericizeOrdinals(String text) {
return text;
}
/**
* Split the text on spaces and convert each word into
* a Token
*/
protected static List<Token> baseTokenize(String text) {
String[] words = text.split(" ");
List<Token> tokens = new LinkedList<Token>();
for (String word : words) {
tokens.add(new Token(word));
}
return tokens;
}
/**
* Guess a specific time within the given span
*/
// DIFF: We return Span instead of Date
protected static Span guess(Span span) {
if (span == null) {
return null;
}
long guessValue;
if (span.getWidth() > 1) {
guessValue = span.getBegin() + (span.getWidth() / 2);
}
else {
guessValue = span.getBegin();
}
Span guess = new Span(guessValue, guessValue);
return guess;
}
}

Binary file not shown.

@ -136,6 +136,7 @@ public class ActFmLoginActivity extends FragmentActivity implements AuthListener
ContextManager.setContext(this);
setContentView(getContentViewResource());
if(getTitleResource() != 0)
setTitle(getTitleResource());
if (getSupportActionBar() != null)
@ -146,15 +147,6 @@ public class ActFmLoginActivity extends FragmentActivity implements AuthListener
noSync = getIntent().getBooleanExtra(EXTRA_DO_NOT_SYNC, false);
showToast = getIntent().getBooleanExtra(SHOW_TOAST, true);
facebook = new Facebook(APP_ID);
facebookRunner = new AsyncFacebookRunner(facebook);
errors = (TextView) findViewById(R.id.error);
LoginButton loginButton = (LoginButton) findViewById(R.id.fb_login);
loginButton.init(this, facebook, this, new String[] { "email",
"offline_access", "publish_stream" });
initializeUI();
getWindow().setFormat(PixelFormat.RGBA_8888);
@ -198,6 +190,14 @@ public class ActFmLoginActivity extends FragmentActivity implements AuthListener
}
protected void initializeUI() {
facebook = new Facebook(APP_ID);
facebookRunner = new AsyncFacebookRunner(facebook);
errors = (TextView) findViewById(R.id.error);
LoginButton loginButton = (LoginButton) findViewById(R.id.fb_login);
loginButton.init(this, facebook, this, new String[] { "email",
"offline_access", "publish_stream" });
findViewById(R.id.gg_login).setOnClickListener(googleListener);
Button pwLogin = (Button) findViewById(R.id.pw_login);
pwLogin.setOnClickListener(signUpListener);

@ -1,7 +1,6 @@
package com.todoroo.astrid.actfm;
import android.app.ListActivity;
import android.app.ProgressDialog;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
@ -28,7 +27,6 @@ import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.utility.AndroidUtilities;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.andlib.utility.DialogUtilities;
import com.todoroo.astrid.actfm.ActFmCameraModule.CameraResultCallback;
import com.todoroo.astrid.actfm.ActFmCameraModule.ClearImageCallback;
import com.todoroo.astrid.actfm.sync.ActFmPreferenceService;
@ -38,6 +36,7 @@ import com.todoroo.astrid.dao.UpdateDao;
import com.todoroo.astrid.data.TagData;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.data.Update;
import com.todoroo.astrid.helper.ProgressBarSyncResultCallback;
import com.todoroo.astrid.service.StatisticsConstants;
import com.todoroo.astrid.service.StatisticsService;
import com.todoroo.astrid.service.TagDataService;
@ -141,7 +140,7 @@ public class TagUpdatesActivity extends ListActivity {
});
refreshUpdatesList();
refreshActivity(null); // start a pull in the background
refreshActivity(false); // start a pull in the background
}
private void refreshUpdatesList() {
@ -191,8 +190,7 @@ public class TagUpdatesActivity extends ListActivity {
case MENU_REFRESH_ID: {
final ProgressDialog progressDialog = DialogUtilities.progressDialog(this, getString(R.string.DLG_please_wait));
refreshActivity(progressDialog);
refreshActivity(true);
return true;
}
@ -200,20 +198,25 @@ public class TagUpdatesActivity extends ListActivity {
}
}
private void refreshActivity(final ProgressDialog progressDialog) {
actFmSyncService.fetchUpdatesForTag(tagData, true, new Runnable() {
@Override
public void run() {
runOnUiThread(new Runnable() {
private void refreshActivity(boolean manual) {
final ProgressBarSyncResultCallback callback = new ProgressBarSyncResultCallback(
this, R.id.progressBar, new Runnable() {
@Override
public void run() {
refreshUpdatesList();
if (progressDialog != null)
DialogUtilities.dismissDialog(TagUpdatesActivity.this, progressDialog);
}
});
callback.started();
callback.incrementMax(100);
actFmSyncService.fetchUpdatesForTag(tagData, manual, new Runnable() {
@Override
public void run() {
callback.incrementProgress(50);
callback.finished();
}
});
callback.incrementProgress(50);
}
@SuppressWarnings("nls")

@ -2,14 +2,10 @@ 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.app.ProgressDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@ -19,7 +15,8 @@ import android.support.v4.view.Menu;
import android.support.v4.view.MenuItem;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
@ -51,9 +48,9 @@ import com.todoroo.astrid.core.SortHelper;
import com.todoroo.astrid.dao.TaskDao.TaskCriteria;
import com.todoroo.astrid.data.TagData;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.helper.ProgressBarSyncResultCallback;
import com.todoroo.astrid.service.TagDataService;
import com.todoroo.astrid.tags.TagFilterExposer;
import com.todoroo.astrid.tags.TagService;
import com.todoroo.astrid.tags.TagService.Tag;
import com.todoroo.astrid.welcome.HelpInfoPopover;
@ -72,7 +69,7 @@ public class TagViewActivity extends TaskListActivity {
private static final int REQUEST_CODE_SETTINGS = 0;
public static final String TOKEN_START_ACTIVITY = "startActivity";
public static final String TOKEN_START_ACTIVITY = "startActivity"; //$NON-NLS-1$
private TagData tagData;
@ -211,17 +208,6 @@ public class TagViewActivity extends TaskListActivity {
cursor.close();
}
if(tagData.getValue(TagData.REMOTE_ID) > 0) {
String fetchKey = LAST_FETCH_KEY + tagData.getId();
long lastFetchDate = Preferences.getLong(fetchKey, 0);
if(DateUtilities.now() > lastFetchDate + 300000L) {
refreshData(false, false);
Preferences.setLong(fetchKey, DateUtilities.now());
}
} else {
((TextView)taskListView.findViewById(android.R.id.empty)).setText(R.string.TLA_no_items);
}
setUpMembersGallery();
super.onNewIntent(intent);
@ -253,90 +239,33 @@ public class TagViewActivity extends TaskListActivity {
// --------------------------------------------------------- refresh data
/** refresh the list with latest data from the web */
private void refreshData(final boolean manual, boolean bypassTagShow) {
final boolean noRemoteId = tagData.getValue(TagData.REMOTE_ID) == 0;
final ProgressDialog progressDialog;
if(manual && !noRemoteId)
progressDialog = DialogUtilities.progressDialog(getActivity(), getString(R.string.DLG_please_wait));
else
progressDialog = null;
Thread tagShowThread = new Thread(new Runnable() {
@SuppressWarnings("nls")
@Override
public void run() {
try {
String oldName = tagData.getValue(TagData.NAME);
actFmSyncService.fetchTag(tagData);
Activity activity = getActivity();
if (activity != null) {
DialogUtilities.dismissDialog(activity, progressDialog);
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
if(noRemoteId && tagData.getValue(TagData.REMOTE_ID) > 0)
refreshData(manual, true);
}
});
}
if(!oldName.equals(tagData.getValue(TagData.NAME))) {
TagService.getInstance().rename(oldName,
tagData.getValue(TagData.NAME));
protected void initiateAutomaticSync() {
long lastAutoSync = Preferences.getLong(LAST_FETCH_KEY + tagData.getId(), 0);
if(DateUtilities.now() - lastAutoSync > DateUtilities.ONE_HOUR)
refreshData(false);
}
} catch (IOException e) {
Log.e("tag-view-activity", "error-fetching-task-io", e);
} catch (JSONException e) {
Log.e("tag-view-activity", "error-fetching-task", e);
}
}
});
if(!bypassTagShow)
tagShowThread.start();
if(noRemoteId) {
((TextView)taskListView.findViewById(android.R.id.empty)).setText(R.string.TLA_no_items);
return;
}
/** refresh the list with latest data from the web */
private void refreshData(final boolean manual) {
((TextView)taskListView.findViewById(android.R.id.empty)).setText(R.string.DLG_loading);
setUpMembersGallery();
actFmSyncService.fetchTasksForTag(tagData, manual, new Runnable() {
@Override
public void run() {
final Activity activity = getActivity();
if (activity != null) {
activity.runOnUiThread(new Runnable() {
syncService.synchronizeList(tagData, manual, new ProgressBarSyncResultCallback(this,
R.id.progressBar, new Runnable() {
@Override
public void run() {
setUpMembersGallery();
loadTaskListContent(true);
((TextView)taskListView.findViewById(android.R.id.empty)).setText(R.string.TLA_no_items);
DialogUtilities.dismissDialog(activity, progressDialog);
}
});
}
}
});
}));
Preferences.setLong(LAST_FETCH_KEY + tagData.getId(), DateUtilities.now());
actFmSyncService.fetchUpdatesForTag(tagData, manual, new Runnable() {
@Override
public void run() {
final Activity activity = getActivity();
if (activity != null) {
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
//refreshUpdatesList();
DialogUtilities.dismissDialog(activity, progressDialog);
}
});
}
}
});
final boolean noRemoteId = tagData.getValue(TagData.REMOTE_ID) == 0;
if(noRemoteId && !manual)
((TextView)taskListView.findViewById(android.R.id.empty)).setText(R.string.TLA_no_items);
}
private void setUpMembersGallery() {
@ -483,7 +412,7 @@ public class TagViewActivity extends TaskListActivity {
//refreshUpdatesList();
}
});
refreshData(false, true);
refreshData(false);
NotificationManager nm = new AndroidNotificationManager(ContextManager.getContext());
nm.cancel(tagData.getValue(TagData.REMOTE_ID).intValue());
@ -532,7 +461,7 @@ public class TagViewActivity extends TaskListActivity {
// handle my own menus
switch (item.getItemId()) {
case MENU_REFRESH_ID:
refreshData(true, false);
refreshData(true);
return true;
}

@ -133,15 +133,16 @@ public class ActFmSyncProvider extends SyncProvider<ActFmTaskContainer> {
try {
int serverTime = Preferences.getInt(ActFmPreferenceService.PREF_SERVER_TIME, 0);
ArrayList<ActFmTaskContainer> remoteTasks = new ArrayList<ActFmTaskContainer>();
int newServerTime = fetchRemoteTasks(serverTime, remoteTasks);
// int newServerTime = fetchRemoteTasks(serverTime, remoteTasks);
if (serverTime == 0) { // If we've never synced, we may lose some empty tags
pushUnsavedTagData();
}
fetchRemoteTagData(serverTime);
// fetchRemoteTagData(serverTime);
SyncData<ActFmTaskContainer> syncData = populateSyncData(remoteTasks);
/* SyncData<ActFmTaskContainer> syncData = populateSyncData(remoteTasks);
try {
synchronizeTasks(syncData);
@ -150,7 +151,8 @@ public class ActFmSyncProvider extends SyncProvider<ActFmTaskContainer> {
syncData.localUpdated.close();
}
Preferences.setInt(ActFmPreferenceService.PREF_SERVER_TIME, newServerTime);
Preferences.setInt(ActFmPreferenceService.PREF_SERVER_TIME, newServerTime); */
actFmPreferenceService.recordSuccessfulSync();
syncSuccess = getFinalSyncStatus();
@ -321,6 +323,7 @@ public class ActFmSyncProvider extends SyncProvider<ActFmTaskContainer> {
} else { // Set default reminders for remotely created tasks
TaskDao.setDefaultReminders(task.task);
}
task.task.setValue(Task.LAST_SYNC, DateUtilities.now() + 1000);
actFmDataService.saveTaskAndMetadata(task);
}

@ -395,15 +395,17 @@ public final class ActFmSyncService {
JSONObject result = actFmInvoker.invoke("task_save", params.toArray(new Object[params.size()]));
ArrayList<Metadata> metadata = new ArrayList<Metadata>();
JsonHelper.taskFromJson(result, task, metadata);
Flags.set(Flags.ACTFM_SUPPRESS_SYNC);
taskDao.saveExisting(task);
} catch (JSONException e) {
handleException("task-save-json", e);
} catch (IOException e) {
if (notPermanentError(e))
addFailedPush(new FailedPush(PUSH_TYPE_TASK, task.getId()));
handleException("task-save-io", e);
task.setValue(Task.LAST_SYNC, DateUtilities.now() + 1000L);
}
Flags.set(Flags.ACTFM_SUPPRESS_SYNC);
taskDao.saveExisting(task);
}
/**
@ -647,10 +649,11 @@ public final class ActFmSyncService {
/**
* Fetch all tags
* @param serverTime
* @return new serverTime
*/
public void fetchTags(int serverTime) throws JSONException, IOException {
public int fetchTags(int serverTime) throws JSONException, IOException {
if(!checkForToken())
return;
return 0;
JSONObject result = actFmInvoker.invoke("tag_list",
"token", token, "modified_after", serverTime);
@ -666,6 +669,53 @@ public final class ActFmSyncService {
Long[] remoteIdArray = remoteIds.toArray(new Long[remoteIds.size()]);
tagDataService.deleteWhere(Criterion.not(TagData.REMOTE_ID.in(remoteIdArray)));
}
return result.optInt("time", 0);
}
/**
* Fetch active tasks asynchronously
* @param manual
* @param done
*/
public void fetchActiveTasks(final boolean manual, Runnable done) {
invokeFetchList("task", manual, new ListItemProcessor<Task>() {
@Override
protected void mergeAndSave(JSONArray list, HashMap<Long,Long> locals) throws JSONException {
Task remote = new Task();
ArrayList<Metadata> metadata = new ArrayList<Metadata>();
for(int i = 0; i < list.length(); i++) {
JSONObject item = list.getJSONObject(i);
readIds(locals, item, remote);
JsonHelper.taskFromJson(item, remote, metadata);
if(remote.getValue(Task.USER_ID) == 0) {
if(!remote.isSaved())
StatisticsService.reportEvent(StatisticsConstants.ACTFM_TASK_CREATED);
else if(remote.isCompleted())
StatisticsService.reportEvent(StatisticsConstants.ACTFM_TASK_COMPLETED);
}
if(!remote.isSaved() && remote.hasDueDate() &&
remote.getValue(Task.DUE_DATE) < DateUtilities.now())
remote.setFlag(Task.REMINDER_FLAGS, Task.NOTIFY_AFTER_DEADLINE, false);
Flags.set(Flags.ACTFM_SUPPRESS_SYNC);
taskService.save(remote);
metadataService.synchronizeMetadata(remote.getId(), metadata, MetadataCriteria.withKey(TagService.KEY));
remote.clear();
}
}
@Override
protected HashMap<Long, Long> getLocalModels() {
TodorooCursor<Task> cursor = taskService.query(Query.select(Task.ID,
Task.REMOTE_ID).where(Task.REMOTE_ID.in(remoteIds)).orderBy(
Order.asc(Task.REMOTE_ID)));
return cursorToMap(cursor, taskDao, Task.REMOTE_ID, Task.ID);
}
}, done, "active_tasks");
}
/**

@ -0,0 +1,234 @@
/**
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.actfm.sync;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicInteger;
import org.json.JSONException;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.service.ExceptionService;
import com.todoroo.andlib.sql.Criterion;
import com.todoroo.andlib.sql.Query;
import com.todoroo.andlib.utility.Preferences;
import com.todoroo.astrid.dao.TaskDao.TaskCriteria;
import com.todoroo.astrid.data.TagData;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.service.AstridDependencyInjector;
import com.todoroo.astrid.service.StartupService;
import com.todoroo.astrid.service.SyncV2Service.SyncResultCallback;
import com.todoroo.astrid.service.SyncV2Service.SyncV2Provider;
import com.todoroo.astrid.service.TaskService;
import com.todoroo.astrid.tags.TagService;
/**
* Exposes sync action
*
*/
public class ActFmSyncV2Provider implements SyncV2Provider {
@Autowired ActFmPreferenceService actFmPreferenceService;
@Autowired ActFmSyncService actFmSyncService;
@Autowired ExceptionService exceptionService;
@Autowired TaskService taskService;
static {
AstridDependencyInjector.initialize();
}
public ActFmSyncV2Provider() {
DependencyInjectionService.getInstance().inject(this);
}
@Override
public boolean isActive() {
return actFmPreferenceService.isLoggedIn();
}
private static final String LAST_TAG_FETCH_TIME = "actfm_lastTag"; //$NON-NLS-1$
// --- synchronize active tasks
@Override
public void synchronizeActiveTasks(boolean manual,
final SyncResultCallback callback) {
callback.started();
callback.incrementMax(100);
final AtomicInteger finisher = new AtomicInteger(2);
startTagFetcher(callback, finisher);
startTaskFetcher(manual, callback, finisher);
pushQueued(callback, finisher);
callback.incrementProgress(50);
}
/** fetch changes to tags */
private void startTagFetcher(final SyncResultCallback callback,
final AtomicInteger finisher) {
new Thread(new Runnable() {
@Override
public void run() {
int time = Preferences.getInt(LAST_TAG_FETCH_TIME, 0);
try {
time = actFmSyncService.fetchTags(time);
Preferences.setInt(LAST_TAG_FETCH_TIME, time);
} catch (JSONException e) {
exceptionService.reportError("actfm-sync", e); //$NON-NLS-1$
} catch (IOException e) {
exceptionService.reportError("actfm-sync", e); //$NON-NLS-1$
} finally {
callback.incrementProgress(20);
if(finisher.decrementAndGet() == 0)
callback.finished();
}
}
}).start();
}
/** @return runnable to fetch changes to tags */
private void startTaskFetcher(final boolean manual, final SyncResultCallback callback,
final AtomicInteger finisher) {
actFmSyncService.fetchActiveTasks(manual, new Runnable() {
@Override
public void run() {
callback.incrementProgress(30);
if(finisher.decrementAndGet() == 0)
callback.finished();
}
});
}
private void pushQueued(final SyncResultCallback callback,
final AtomicInteger finisher) {
TodorooCursor<Task> cursor = taskService.query(Query.select(Task.PROPERTIES).
where(Criterion.or(
Criterion.and(TaskCriteria.isActive(),
Task.ID.gt(StartupService.INTRO_TASK_SIZE),
Task.REMOTE_ID.eq(0)),
Criterion.and(Task.REMOTE_ID.gt(0),
Task.MODIFICATION_DATE.gt(Task.LAST_SYNC)))));
try {
callback.incrementMax(cursor.getCount() * 20);
finisher.addAndGet(cursor.getCount());
for(int i = 0; i < cursor.getCount(); i++) {
cursor.moveToNext();
final Task task = new Task(cursor);
new Thread(new Runnable() {
public void run() {
try {
actFmSyncService.pushTaskOnSave(task, task.getMergedValues());
} finally {
callback.incrementProgress(20);
if(finisher.decrementAndGet() == 0)
callback.finished();
}
}
}).start();
}
} finally {
cursor.close();
}
}
// --- synchronize list
@Override
public void synchronizeList(Object list, boolean manual,
final SyncResultCallback callback) {
if(!(list instanceof TagData))
return;
TagData tagData = (TagData) list;
final boolean noRemoteId = tagData.getValue(TagData.REMOTE_ID) == 0;
if(noRemoteId && !manual)
return;
callback.started();
callback.incrementMax(100);
final AtomicInteger finisher = new AtomicInteger(3);
fetchTagData(tagData, noRemoteId, manual, callback, finisher);
if(!noRemoteId) {
fetchTasksForTag(tagData, manual, callback, finisher);
fetchUpdatesForTag(tagData, manual, callback, finisher);
}
callback.incrementProgress(50);
}
private void fetchTagData(final TagData tagData, final boolean noRemoteId,
final boolean manual, final SyncResultCallback callback,
final AtomicInteger finisher) {
new Thread(new Runnable() {
@Override
public void run() {
String oldName = tagData.getValue(TagData.NAME);
try {
actFmSyncService.fetchTag(tagData);
if(noRemoteId) {
fetchTasksForTag(tagData, manual, callback, finisher);
fetchUpdatesForTag(tagData, manual, callback, finisher);
}
if(!oldName.equals(tagData.getValue(TagData.NAME))) {
TagService.getInstance().rename(oldName,
tagData.getValue(TagData.NAME));
}
} catch (IOException e) {
exceptionService.reportError("sync-io", e); //$NON-NLS-1$
} catch (JSONException e) {
exceptionService.reportError("sync-json", e); //$NON-NLS-1$
} finally {
callback.incrementProgress(20);
if(finisher.decrementAndGet() == 0)
callback.finished();
}
}
}).start();
}
private void fetchUpdatesForTag(TagData tagData, boolean manual, final SyncResultCallback callback,
final AtomicInteger finisher) {
actFmSyncService.fetchUpdatesForTag(tagData, manual, new Runnable() {
@Override
public void run() {
callback.incrementProgress(20);
if(finisher.decrementAndGet() == 0)
callback.finished();
}
});
}
private void fetchTasksForTag(TagData tagData, boolean manual, final SyncResultCallback callback,
final AtomicInteger finisher) {
actFmSyncService.fetchTasksForTag(tagData, manual, new Runnable() {
@Override
public void run() {
callback.incrementProgress(30);
if(finisher.decrementAndGet() == 0)
callback.finished();
}
});
}
}

@ -7,10 +7,12 @@ import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.text.TextUtils;
import android.text.format.Time;
import android.util.Log;
import com.timsu.astrid.R;
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.core.PluginServices;
@ -66,8 +68,10 @@ public class GCalHelper {
values.put("title", task.getValue(Task.TITLE));
values.put("description", task.getValue(Task.NOTES));
values.put("hasAlarm", 0);
if (AndroidUtilities.getSdkVersion() < 14) {
values.put("transparency", 0);
values.put("visibility", 0);
}
boolean valuesContainCalendarId = (values.containsKey("calendar_id") &&
!TextUtils.isEmpty(values.getAsString("calendar_id")));
if (!valuesContainCalendarId) {
@ -152,5 +156,16 @@ public class GCalHelper {
values.put("dtend", tzCorrectedDueDateNow);
values.put("allDay", "1");
}
adjustDateForIcs(task, values);
}
private static void adjustDateForIcs(Task task, ContentValues values) {
if (AndroidUtilities.getSdkVersion() >= 14) {
if ("1".equals(values.get("allDay"))) {
values.put("eventTimezone", Time.TIMEZONE_UTC);
} else {
values.put("eventTimezone", TimeZone.getDefault().getID());
}
}
}
}

@ -10,7 +10,6 @@ import java.util.List;
import org.json.JSONObject;
import android.app.ListActivity;
import android.app.ProgressDialog;
import android.content.Context;
import android.graphics.Color;
import android.os.Bundle;
@ -37,7 +36,6 @@ import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.sql.Query;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.andlib.utility.DialogUtilities;
import com.todoroo.andlib.utility.Preferences;
import com.todoroo.astrid.actfm.sync.ActFmPreferenceService;
import com.todoroo.astrid.actfm.sync.ActFmSyncService;
@ -47,9 +45,11 @@ 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.ProgressBarSyncResultCallback;
import com.todoroo.astrid.service.MetadataService;
import com.todoroo.astrid.service.StatisticsConstants;
import com.todoroo.astrid.service.StatisticsService;
import com.todoroo.astrid.service.SyncV2Service.SyncResultCallback;
import com.todoroo.astrid.utility.Flags;
public class EditNoteActivity extends ListActivity {
@ -95,12 +95,12 @@ public class EditNoteActivity extends ListActivity {
findViewById(R.id.add_comment).setVisibility(View.VISIBLE);
if(task.getValue(Task.REMOTE_ID) == 0)
refreshData(true);
refreshData(true, null);
else {
String fetchKey = LAST_FETCH_KEY + task.getId();
long lastFetchDate = Preferences.getLong(fetchKey, 0);
if(DateUtilities.now() > lastFetchDate + 300000L) {
refreshData(false);
refreshData(false, null);
Preferences.setLong(fetchKey, DateUtilities.now());
} else {
loadingText.setText(R.string.ENA_no_comments);
@ -223,21 +223,32 @@ public class EditNoteActivity extends ListActivity {
// --- events
private void refreshData(boolean manual) {
final ProgressDialog progressDialog;
if(manual)
progressDialog = DialogUtilities.progressDialog(this, getString(R.string.DLG_please_wait));
else
progressDialog = null;
private void refreshData(boolean manual, SyncResultCallback existingCallback) {
final SyncResultCallback callback;
if(existingCallback != null)
callback = existingCallback;
else {
callback = new ProgressBarSyncResultCallback(
this, R.id.progressBar, new Runnable() {
@Override
public void run() {
setUpListAdapter();
loadingText.setText(R.string.ENA_no_comments);
loadingText.setVisibility(items.size() == 0 ? View.VISIBLE : View.GONE);
}
});
callback.started();
callback.incrementMax(100);
}
if(task.getValue(Task.REMOTE_ID) == 0) {
// push task if it hasn't been pushed
if(task.getValue(Task.REMOTE_ID) == 0) {
new Thread(new Runnable() {
@Override
public void run() {
actFmSyncService.pushTask(task.getId());
refreshData(false);
DialogUtilities.dismissDialog(EditNoteActivity.this, progressDialog);
refreshData(false, callback);
}
}).start();
return;
@ -246,17 +257,11 @@ public class EditNoteActivity extends ListActivity {
actFmSyncService.fetchUpdatesForTask(task, manual, new Runnable() {
@Override
public void run() {
runOnUiThread(new Runnable() {
@Override
public void run() {
setUpListAdapter();
loadingText.setText(R.string.ENA_no_comments);
loadingText.setVisibility(items.size() == 0 ? View.VISIBLE : View.GONE);
DialogUtilities.dismissDialog(EditNoteActivity.this, progressDialog);
}
});
callback.incrementProgress(50);
callback.finished();
}
});
callback.incrementProgress(50);
}
private void addComment() {
@ -288,7 +293,7 @@ public class EditNoteActivity extends ListActivity {
switch (item.getItemId()) {
case MENU_REFRESH_ID: {
refreshData(true);
refreshData(true, null);
return true;
}

@ -9,6 +9,7 @@
-dontoptimize
-keepattributes SourceFile, SourceDir, LineNumberTable, LocalVariableTable, LocalVariableTypeTable
-keep class com.todoroo.**
-keep class com.mdimension.**
-keepnames class com.google.**
# ignore reflection-based access from google libraries

@ -15,4 +15,4 @@ android.library.reference.2=../actionbarsherlock/library
# Project target.
target=android-14
apk-configurations=
android.library.reference.4=../greendroid/GreenDroid
android.library.reference.4=../viewPagerIndicator/library

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 278 KiB

@ -6,6 +6,15 @@
android:orientation="vertical"
android:background="@drawable/background_gradient">
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="fill_parent"
android:layout_height="5dip"
style="@android:style/Widget.ProgressBar.Horizontal"
android:visibility="gone"
android:layout_weight="1" />
<FrameLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"

@ -24,6 +24,14 @@
style="@style/TextAppearance.TLA_Header"/>
</LinearLayout>
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="fill_parent"
android:layout_height="5dip"
style="@android:style/Widget.ProgressBar.Horizontal"
android:visibility="gone"
android:layout_weight="1" />
<ListView
android:id="@android:id/list"
android:layout_width="fill_parent"

@ -4,8 +4,6 @@
xmlns:astrid="http://schemas.android.com/apk/res/com.timsu.astrid"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingLeft="4dip"
android:paddingRight="4dip"
android:minHeight="40dip"
android:orientation="horizontal">

@ -7,6 +7,14 @@
style="@style/Content"
android:orientation="vertical">
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="fill_parent"
android:layout_height="5dip"
style="@android:style/Widget.ProgressBar.Horizontal"
android:visibility="gone"
android:layout_weight="1" />
<!-- Body goes here -->
<!-- Footer -->

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- xmlns:app="http://schemas.android.com/apk/res/com.viewpagerindicator.sample" -->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<android.support.v4.view.ViewPager
android:id="@+id/pager"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
/>
<com.viewpagerindicator.CirclePageIndicator
android:id="@+id/indicator"
android:padding="15dip"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_alignBottom="@id/pager"
android:background="#00000000"
style="@style/CustomCirclePageIndicator"
/>
<!--
app:radius="10dp"
app:fillColor="#FF888888"
app:strokeColor="#FF000000"
app:strokeWidth="2dp" -->
</RelativeLayout>
</LinearLayout>

@ -0,0 +1,101 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:paddingLeft="4dip"
android:paddingRight="4dip"
android:background="@drawable/welcome_walkthrough_fabric"
android:orientation="vertical">
<TextView
android:id="@+id/logo"
android:layout_width="fill_parent"
android:layout_height="110dip"
android:gravity="center_horizontal"
android:paddingBottom="10dip"
android:layout_marginTop="30dip"
android:text="Connect now\nto get started!"
android:textColor="#444444"
android:textSize="40dip"
android:textStyle="bold"
android:shadowRadius="2"
android:shadowDy="1"
android:shadowColor="@android:color/white" />
<TextView
android:id="@+id/error"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textColor="#ff0000"
android:textSize="16sp"
android:textStyle="bold" />
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="253dp"
android:paddingTop="5dip" >
<TextView
android:id="@+id/login_later"
android:layout_width="fill_parent"
android:layout_height="45dip"
android:gravity="center"
android:layout_alignParentBottom="true"
android:paddingLeft="20dip"
android:paddingRight="20dip"
android:paddingBottom="5dip"
android:textSize="16sp"
android:textColor="#ffffff" />
<Button
android:id="@+id/pw_login"
android:layout_width="fill_parent"
android:layout_height="45dip"
android:layout_above="@id/login_later"
android:paddingLeft="20dip"
android:paddingRight="20dip"
android:layout_marginLeft="15dip"
android:layout_marginRight="15dip"
android:textSize="16sp"
android:text="@string/welcome_login_pw"/>
<com.facebook.android.LoginButton
android:id="@+id/fb_login"
android:layout_width="fill_parent"
android:layout_height="45dip"
android:layout_above="@id/pw_login"
android:paddingLeft="20dip"
android:paddingRight="20dip"
android:layout_marginLeft="15dip"
android:layout_marginRight="15dip"
android:textSize="16sp"
android:drawableLeft="@drawable/facebook"
android:text="@string/actfm_ALA_fb_login" />
<Button
android:id="@+id/gg_login"
android:layout_width="fill_parent"
android:layout_height="45dip"
android:layout_above="@id/fb_login"
android:paddingLeft="20dip"
android:paddingRight="20dip"
android:layout_marginLeft="15dip"
android:layout_marginRight="15dip"
android:textSize="16sp"
android:drawableLeft="@drawable/google"
android:text="@string/actfm_ALA_gg_login" />
<TextView
android:id="@+id/tos"
android:layout_width="fill_parent"
android:layout_height="35dip"
android:layout_above="@id/gg_login"
android:gravity="center"
android:paddingLeft="20dip"
android:paddingRight="20dip"
android:textSize="12.5sp"
android:textColor="#ffffff" />
</RelativeLayout>
</LinearLayout>

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:background="@drawable/welcome_walkthrough_fabric">
<TextView
android:id="@+id/welcome_walkthrough_title"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="27dp"
android:gravity="center_horizontal"
android:text="Hello"
android:textColor="#444444"
android:textSize="40dip"
android:textStyle="bold"
android:shadowRadius="2"
android:shadowDy="1"
android:shadowColor="@android:color/white" />
<ImageView
android:id="@+id/welcome_walkthrough_image"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1"
android:src="@drawable/welcome_walkthrough_1"
android:scaleType="fitCenter"/>
<TextView
android:id="@+id/welcome_walkthrough_body"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="75dp"
android:gravity="center_horizontal"
android:text="Hello"
android:textColor="#444444"
android:textSize="20dip"
android:textStyle="bold"
android:shadowRadius="2"
android:shadowDy="1"
android:shadowColor="@android:color/white" />
</LinearLayout>

@ -185,10 +185,10 @@
share, and delegate with others.</string>
<!-- share login: Sharing Login FB Prompt -->
<string name="actfm_ALA_fb_login">Login with Facebook</string>
<string name="actfm_ALA_fb_login">Connect with Facebook</string>
<!-- share login: Sharing Login GG Prompt -->
<string name="actfm_ALA_gg_login">Login with Google</string>
<string name="actfm_ALA_gg_login">Connect with Google</string>
<!-- share login: Sharing Footer Password Label -->
<string name="actfm_ALA_pw_login">Don\'t use Google or Facebook?</string>

@ -74,11 +74,6 @@
<!-- String formatter for Disable button -->
<string name="WID_disableButton">Disable</string>
<!-- =================================================== Welcome screen == -->
<!-- Label for DateButtons with no value -->
<string name="welcome_show_eula">Accept EULA to get started!</string>
<!-- ============================================================= notes -->
<!-- Note Exposer -->

@ -71,7 +71,7 @@
<string name="welcome_login_pw">Login with Username/Password</string>
<string name="welcome_login_later">Login Later</string>
<string name="welcome_login_later">Connect Later</string>
<string name="welcome_login_confirm_later_title">Why not sign in?</string>

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="welcome_show_eula">Accept EULA to get started!</string>
<string name="welcome_title_1">Welcome to Astrid!</string>
<string name="welcome_title_2">Make lists</string>
<string name="welcome_title_3">Share lists</string>
<string name="welcome_title_4">Divvy up tasks</string>
<string name="welcome_title_5">Provide details</string>
<string name="welcome_title_6">Discover</string>
<string name="welcome_title_7">Login</string>
<string name="welcome_body_1">The perfect personal\nto-do list that works great\nwith friends</string>
<string name="welcome_body_2">Perfect for any list:\nto read, to watch, to buy,\nto visit, to do!</string>
<string name="welcome_body_3">Share lists\nwith friends, housemates,\nor your sweetheart!</string>
<string name="welcome_body_4">Never wonder who\'s\nbringing dessert!</string>
<string name="welcome_body_5">Tap to add notes,\nset reminders,\nand much more!</string>
<string name="welcome_body_6">Additional features,\nproductivity tips, and\nsuggestions from friends</string>
<string name="welcome_body_7">Login</string>
</resources>

@ -283,4 +283,12 @@
<item name="android:textSize">14sp</item>
<item name="android:textColor">@android:color/black</item>
</style>
<style name="CustomCirclePageIndicator">
<item name="fillColor">#FFFFFFFF</item>
<item name="strokeColor">#FF000000</item>
<item name="strokeWidth">2dp</item>
<item name="radius">10dp</item>
<item name="centered">true</item>
<item name="android:background">#FFCCCCCC</item>
</style>
</resources>

@ -267,10 +267,6 @@ public class FilterListActivity extends ExpandableListFragment {
if (((AstridWrapperActivity) getActivity()).isMultipleFragments())
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
// item = menu.add(Menu.NONE, MENU_REFRESH_ID, Menu.NONE,
// R.string.TLA_menu_sync);
// item.setIcon(R.drawable.ic_menu_refresh);
// item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
// item = menu.add(Menu.NONE, MENU_HELP_ID, 1,
// R.string.FLA_menu_help);

@ -75,8 +75,6 @@ import com.todoroo.andlib.utility.Preferences;
import com.todoroo.andlib.widget.GestureService;
import com.todoroo.andlib.widget.GestureService.GestureInterface;
import com.todoroo.astrid.actfm.ActFmLoginActivity;
import com.todoroo.astrid.actfm.sync.ActFmPreferenceService;
import com.todoroo.astrid.actfm.sync.ActFmSyncProvider;
import com.todoroo.astrid.activity.SortSelectionActivity.OnSortSelectedListener;
import com.todoroo.astrid.adapter.TaskAdapter;
import com.todoroo.astrid.adapter.TaskAdapter.OnCompletedTaskListener;
@ -97,6 +95,7 @@ import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.gcal.GCalHelper;
import com.todoroo.astrid.helper.MetadataHelper;
import com.todoroo.astrid.helper.ProgressBarSyncResultCallback;
import com.todoroo.astrid.helper.TaskListContextMenuExtensionLoader;
import com.todoroo.astrid.helper.TaskListContextMenuExtensionLoader.ContextMenuItem;
import com.todoroo.astrid.reminders.ReminderDebugContextActions;
@ -106,6 +105,7 @@ import com.todoroo.astrid.service.MetadataService;
import com.todoroo.astrid.service.StartupService;
import com.todoroo.astrid.service.StatisticsConstants;
import com.todoroo.astrid.service.StatisticsService;
import com.todoroo.astrid.service.SyncV2Service;
import com.todoroo.astrid.service.TagDataService;
import com.todoroo.astrid.service.TaskService;
import com.todoroo.astrid.service.UpgradeService;
@ -115,6 +115,7 @@ import com.todoroo.astrid.utility.Flags;
import com.todoroo.astrid.voice.VoiceInputAssistant;
import com.todoroo.astrid.welcome.HelpInfoPopover;
import com.todoroo.astrid.welcome.WelcomeLogin;
import com.todoroo.astrid.welcome.tutorial.WelcomeWalkthrough;
import com.todoroo.astrid.widget.TasksWidget;
/**
@ -165,8 +166,6 @@ public class TaskListActivity extends ListFragment implements OnScrollListener,
public static final String TOKEN_OVERRIDE_ANIM = "finishAnim"; //$NON-NLS-1$
private static final String LAST_AUTOSYNC_ATTEMPT = "last-autosync"; //$NON-NLS-1$
// --- instance variables
@Autowired ExceptionService exceptionService;
@ -181,7 +180,7 @@ public class TaskListActivity extends ListFragment implements OnScrollListener,
@Autowired UpgradeService upgradeService;
@Autowired ActFmPreferenceService actFmPreferenceService;
@Autowired protected SyncV2Service syncService;
@Autowired TagDataService tagDataService;
@ -562,23 +561,6 @@ public class TaskListActivity extends ListFragment implements OnScrollListener,
sortSort = publicPrefs.getInt(SortHelper.PREF_SORT_SORT, 0);
}
private void initiateAutomaticSync() {
if (!actFmPreferenceService.isLoggedIn()) return;
long lastFetchDate = actFmPreferenceService.getLastSyncDate();
long lastAutosyncAttempt = Preferences.getLong(LAST_AUTOSYNC_ATTEMPT, 0);
long lastTry = Math.max(lastFetchDate, lastAutosyncAttempt);
if(DateUtilities.now() < lastTry + 300000L)
return;
new Thread() {
@Override
public void run() {
Preferences.setLong(LAST_AUTOSYNC_ATTEMPT, DateUtilities.now());
new ActFmSyncProvider().synchronize(getActivity(), false);
}
}.start();
}
// Subclasses can override these to customize extras in quickadd intent
protected Intent getOnClickQuickAddIntent(Task t) {
Intent intent = new Intent(getActivity(), TaskEditWrapperActivity.class);
@ -658,10 +640,10 @@ public class TaskListActivity extends ListFragment implements OnScrollListener,
setUpBackgroundJobs();
if (!Preferences.getBoolean(WelcomeLogin.KEY_SHOWED_WELCOME_LOGIN, false)) {
Intent showWelcomeLogin = new Intent(getActivity(), WelcomeLogin.class);
Preferences.setBoolean(WelcomeLogin.KEY_SHOWED_WELCOME_LOGIN, true);
Intent showWelcomeLogin = new Intent(this, WelcomeWalkthrough.class);
showWelcomeLogin.putExtra(ActFmLoginActivity.SHOW_TOAST, false);
startActivity(showWelcomeLogin);
Preferences.setBoolean(WelcomeLogin.KEY_SHOWED_WELCOME_LOGIN, true);
return;
}
@ -676,10 +658,8 @@ public class TaskListActivity extends ListFragment implements OnScrollListener,
Preferences.setBoolean(R.string.p_showed_lists_help, true);
}
if (filter.title != null && filter.title.equals(getString(R.string.BFE_Active))) {
initiateAutomaticSync();
}
}
@Override
public void onPause() {
@ -803,6 +783,7 @@ public class TaskListActivity extends ListFragment implements OnScrollListener,
super.onActivityResult(requestCode, resultCode, data);
if (!Preferences.getBoolean(R.string.p_showed_add_task_help, false)) {
HelpInfoPopover.showPopover(getActivity(), quickAddBox, R.string.help_popover_add_task, null);
Preferences.setBoolean(R.string.p_showed_add_task_help, true);
@ -1184,8 +1165,31 @@ public class TaskListActivity extends ListFragment implements OnScrollListener,
}
}
private void performSyncAction() {
if (syncActions.size() == 0) {
private static final String PREF_LAST_AUTO_SYNC = "taskListLastAutoSync"; //$NON-NLS-1$
protected void initiateAutomaticSync() {
if (filter.title == null || !filter.title.equals(getString(R.string.BFE_Active)))
return;
long lastAutoSync = Preferences.getLong(PREF_LAST_AUTO_SYNC, 0);
if(DateUtilities.now() - lastAutoSync > DateUtilities.ONE_HOUR) {
performSyncServiceV2Sync(false);
}
}
protected void performSyncServiceV2Sync(boolean manual) {
syncService.synchronizeActiveTasks(manual, new ProgressBarSyncResultCallback(this,
R.id.progressBar, new Runnable() {
@Override
public void run() {
loadTaskListContent(true);
}
}));
Preferences.setLong(PREF_LAST_AUTO_SYNC, DateUtilities.now());
}
protected void performSyncAction() {
if (syncActions.size() == 0 && !syncService.isActive()) {
String desiredCategory = getString(R.string.SyP_label);
// Get a list of all sync plugins and bring user to the prefs pane
@ -1226,32 +1230,20 @@ public class TaskListActivity extends ListFragment implements OnScrollListener,
showSyncOptionMenu(actions, listener);
}
else if(syncActions.size() == 1) {
SyncAction syncAction = syncActions.iterator().next();
else {
performSyncServiceV2Sync(true);
if(syncActions.size() > 0) {
for(SyncAction syncAction : syncActions) {
try {
syncAction.intent.send();
Toast.makeText(getActivity(), R.string.SyP_progress_toast,
Toast.LENGTH_LONG).show();
} catch (CanceledException e) {
//
}
} else {
// We have >1 sync actions, pop up a dialogue so the user can
// select just one of them (only sync one at a time)
final SyncAction[] actions = syncActions.toArray(new SyncAction[syncActions.size()]);
DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface click, int which) {
try {
actions[which].intent.send();
Toast.makeText(getActivity(), R.string.SyP_progress_toast,
Toast.LENGTH_LONG).show();
} catch (CanceledException e) {
//
}
Toast.makeText(TaskListActivity.this, R.string.SyP_progress_toast,
Toast.LENGTH_LONG).show();
}
};
showSyncOptionMenu(actions, listener);
}
}

@ -0,0 +1,96 @@
package com.todoroo.astrid.helper;
import java.util.concurrent.atomic.AtomicInteger;
import android.app.Activity;
import android.view.View;
import android.view.animation.AlphaAnimation;
import android.widget.ProgressBar;
import com.todoroo.andlib.utility.AndroidUtilities;
import com.todoroo.astrid.service.SyncV2Service.SyncResultCallback;
public class ProgressBarSyncResultCallback implements SyncResultCallback {
private final ProgressBar progressBar;
private final Activity activity;
private final Runnable onFinished;
private final AtomicInteger providers = new AtomicInteger(0);
public ProgressBarSyncResultCallback(Activity activity,
int progressBarId, Runnable onFinished) {
this.progressBar = (ProgressBar) activity.findViewById(progressBarId);
this.activity = activity;
this.onFinished = onFinished;
progressBar.setProgress(0);
progressBar.setMax(0);
}
@Override
public void finished() {
if(providers.decrementAndGet() == 0) {
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
progressBar.setMax(100);
progressBar.setProgress(100);
AlphaAnimation animation = new AlphaAnimation(1, 0);
animation.setFillAfter(true);
animation.setDuration(1000L);
progressBar.startAnimation(animation);
onFinished.run();
}
});
new Thread() {
@Override
public void run() {
AndroidUtilities.sleepDeep(1000);
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
progressBar.setVisibility(View.GONE);
}
});
}
}.start();
}
}
@Override
public void incrementMax(final int incrementBy) {
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
progressBar.setMax(progressBar.getMax() + incrementBy);
}
});
}
@Override
public void incrementProgress(final int incrementBy) {
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
progressBar.incrementProgressBy(incrementBy);
}
});
}
@Override
public void started() {
if(providers.incrementAndGet() == 1) {
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
progressBar.setVisibility(View.VISIBLE);
AlphaAnimation animation = new AlphaAnimation(0, 1);
animation.setFillAfter(true);
animation.setDuration(1000L);
progressBar.startAnimation(animation);
}
});
}
}
}

@ -73,6 +73,7 @@ public class AstridDependencyInjector extends AbstractDependencyInjector {
injectables.put("tagDataService", TagDataService.class);
injectables.put("upgradeService", UpgradeService.class);
injectables.put("addOnService", AddOnService.class);
injectables.put("syncService", SyncV2Service.class);
// com.timsu.astrid.data
injectables.put("tasksTable", "tasks");

@ -178,6 +178,7 @@ public class StartupService {
upgradeService.performSecondaryUpgrade(context);
// perform startup activities in a background thread
final int finalLatestVersion = latestSetVersion;
new Thread(new Runnable() {
public void run() {
// start widget updating alarm
@ -206,6 +207,7 @@ public class StartupService {
gtasksSyncOnSaveService.initialize();
// get and display update messages
if (finalLatestVersion != 0)
new UpdateMessageService().processUpdates(context);
// Check for feature flips

@ -0,0 +1,93 @@
package com.todoroo.astrid.service;
import com.todoroo.astrid.actfm.sync.ActFmSyncV2Provider;
/**
* SyncV2Service is a simplified synchronization interface for supporting
* next-generation sync interfaces such as Google Tasks and Astrid.com
*
* @author Tim Su <tim@astrid.com>
*
*/
public class SyncV2Service {
public interface SyncResultCallback {
/**
* Increment max sync progress
* @param incrementBy
*/
public void incrementMax(int incrementBy);
/**
* Increment current sync progress
* @param incrementBy
*/
public void incrementProgress(int incrementBy);
/**
* Provider started sync
*/
public void started();
/**
* Provider finished sync
*/
public void finished();
}
public interface SyncV2Provider {
public boolean isActive();
public void synchronizeActiveTasks(boolean manual, SyncResultCallback callback);
public void synchronizeList(Object list, boolean manual, SyncResultCallback callback);
}
/*
* At present, sync provider interactions are handled through code. If
* there is enough interest, the Astrid team could create an interface
* for responding to sync requests through this new API.
*/
private final SyncV2Provider[] providers = new SyncV2Provider[] {
new ActFmSyncV2Provider()
};
/**
* Determine if synchronization is available
*
* @param callback
*/
public boolean isActive() {
for(SyncV2Provider provider : providers) {
if(provider.isActive())
return true;
}
return false;
}
/**
* Initiate synchronization of active tasks
*
* @param manual if manual sync
* @param callback result callback
*/
public void synchronizeActiveTasks(boolean manual, SyncResultCallback callback) {
for(SyncV2Provider provider : providers) {
if(provider.isActive())
provider.synchronizeActiveTasks(manual, callback);
}
}
/**
* Initiate synchronization of task list
*
* @param list list object
* @param manual if manual sync
* @param callback result callback
*/
public void synchronizeList(Object list, boolean manual, SyncResultCallback callback) {
for(SyncV2Provider provider : providers) {
if(provider.isActive())
provider.synchronizeList(list, manual, callback);
}
}
}

@ -1,8 +1,6 @@
package com.todoroo.astrid.service;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.weloveastrid.rmilk.data.MilkTaskFields;
@ -12,6 +10,7 @@ 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.service.ExceptionService;
import com.todoroo.andlib.sql.Criterion;
import com.todoroo.andlib.sql.Functions;
import com.todoroo.andlib.sql.Query;
@ -27,9 +26,10 @@ import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.gcal.GCalHelper;
import com.todoroo.astrid.gtasks.GtasksMetadata;
import com.todoroo.astrid.opencrx.OpencrxCoreUtils;
import com.todoroo.astrid.producteev.ProducteevUtilities;
import com.todoroo.astrid.producteev.sync.ProducteevTask;
import com.todoroo.astrid.tags.TagService;
import com.todoroo.astrid.utility.TitleParser;
/**
* Service layer for {@link Task}-centered activities.
@ -45,6 +45,9 @@ public class TaskService {
@Autowired
private MetadataDao metadataDao;
@Autowired
private ExceptionService exceptionService;
public TaskService() {
DependencyInjectionService.getInstance().inject(this);
}
@ -335,7 +338,11 @@ public class TaskService {
*/
public void quickAdd(Task task) {
ArrayList<String> tags = new ArrayList<String>();
try {
parseQuickAddMarkup(task, tags);
} catch (Throwable e) {
exceptionService.reportError("parse-quick-add", e); //$NON-NLS-1$
}
save(task);
Metadata metadata = new Metadata();
@ -347,40 +354,10 @@ public class TaskService {
}
}
@SuppressWarnings("nls")
public static void parseQuickAddMarkup(Task task, ArrayList<String> tags) {
String title = task.getValue(Task.TITLE);
Pattern tagPattern = Pattern.compile("(\\s|^)#([^\\s]+)");
Pattern contextPattern = Pattern.compile("(\\s|^)(@[^\\s]+)");
Pattern importancePattern = Pattern.compile("(\\s|^)!(\\d)(\\s|$)");
while(true) {
Matcher m = tagPattern.matcher(title);
if(m.find()) {
tags.add(m.group(2));
} else {
m = contextPattern.matcher(title);
if(m.find()) {
tags.add(m.group(2));
} else {
m = importancePattern.matcher(title);
if(m.find()) {
int value = Integer.parseInt(m.group(2));
// not in producteev world: !1 to !4 => importance 3 to 0
int importance = Math.max(Task.IMPORTANCE_MOST, Task.IMPORTANCE_LEAST + 1 - value);
// in the producteev world, !1 to !4 => importance 4 to 1
if(ProducteevUtilities.INSTANCE.isLoggedIn() || OpencrxCoreUtils.INSTANCE.isLoggedIn())
importance++;
task.setValue(Task.IMPORTANCE, importance);
} else
break;
}
new TitleParser(task, tags).parse();
}
title = title.substring(0, m.start()) + title.substring(m.end());
}
task.setValue(Task.TITLE, title.trim());
}
}

@ -40,6 +40,7 @@ import com.todoroo.astrid.utility.AstridPreferences;
public final class UpgradeService {
public static final int V3_9_2 = 207;
public static final int V3_9_1_1 = 206;
public static final int V3_9_1 = 205;
public static final int V3_9_0_2 = 204;
@ -170,6 +171,15 @@ public final class UpgradeService {
Preferences.clear(AstridPreferences.P_UPGRADE_FROM);
StringBuilder changeLog = new StringBuilder();
if (from < V3_9_2) {
newVersionString(changeLog, "3.9.2 (01/13/12)", new String[] {
"Made selecting dates and times easier:",
"New tutorial walkthrough for new users",
"Stomped on a few bugs",
"Feedback welcomed!"
});
}
if (from >= V3_9_1 && from < V3_9_1_1) {
newVersionString(changeLog, "3.9.1.1 (01/06/12)", new String[] {
"Fixed a few bugs:",

@ -0,0 +1,401 @@
package com.todoroo.astrid.utility;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.google.ical.values.Frequency;
import com.google.ical.values.RRule;
import com.mdimension.jchronic.AstridChronic;
import com.mdimension.jchronic.Chronic;
import com.todoroo.astrid.data.Task;
public class TitleParser {
Task task;
ArrayList<String> tags;
public TitleParser(Task task, ArrayList<String> tags){
this.task = task;
this.tags = tags;
}
public void parse(){
repeatHelper(task);
dayHelper(task);
listHelper(task,tags);
priorityHelper(task);
}
private static void listHelper(Task task, ArrayList<String> tags) {
String inputText = task.getValue(Task.TITLE);
Pattern tagPattern = Pattern.compile("(\\s|^)#([^\\s]+)");
Pattern contextPattern = Pattern.compile("(\\s|^)(@[^\\s]+)");
while(true) {
Matcher m = tagPattern.matcher(inputText);
if(m.find()) {
tags.add(m.group(2));
} else {
m = contextPattern.matcher(inputText);
if(m.find()) {
tags.add(m.group(2));
}else{
break;
}
}
inputText = inputText.substring(0, m.start()) + inputText.substring(m.end());
}
task.setValue(Task.TITLE, inputText.trim());
}
//helper method for priorityHelper. converts the string to a Task Importance
@SuppressWarnings("nls")
private static int str_to_priority(String priority_str) {
if (priority_str!=null)
priority_str.toLowerCase().trim();
int priority = Task.IMPORTANCE_DO_OR_DIE;
if ("0".equals(priority_str) || "!0".equals(priority_str) || "least".equals(priority_str) || "lowest".equals(priority_str))
priority = Task.IMPORTANCE_NONE;
if ("!".equals(priority_str) || "!1".equals(priority_str) || "bang".equals(priority_str) || "1".equals(priority_str) || "low".equals(priority_str))
priority = Task.IMPORTANCE_SHOULD_DO;
if ("!!".equals(priority_str) || "!2".equals(priority_str) || "bang bang".equals(priority_str) || "2".equals(priority_str) || "high".equals(priority_str))
priority = Task.IMPORTANCE_MUST_DO;
return priority;
}
//priorityHelper parses the string and sets the Task's importance
private static void priorityHelper(Task task) {
String inputText = task.getValue(Task.TITLE);
String[] importanceStrings = {
"()((^|[^\\w!])!+|(^|[^\\w!])!\\d)($|[^\\w!])",
"()(?i)((\\s?bang){1,})$",
"(?i)(\\spriority\\s?(\\d)$)",
"(?i)(\\sbang\\s?(\\d)$)",
"(?i)()(\\shigh(est)?|\\slow(est)?|\\stop|\\sleast) ?priority$"
};
for (String importanceString:importanceStrings){
Pattern importancePattern= Pattern.compile(importanceString);
while (true){
Matcher m = importancePattern.matcher(inputText);
if(m.find()) {
task.setValue(Task.IMPORTANCE, str_to_priority(m.group(2).trim()));
inputText = inputText.substring(0, m.start()+1) + inputText.substring(m.end());
} else
break;
}
}
task.setValue(Task.TITLE, inputText.trim());
}
//helper for dayHelper. Converts am/pm to an int 0/1.
private static int ampm_to_number(String am_pm_string) {
int time = Calendar.PM;
if (am_pm_string == null){
return time;
}
String text = am_pm_string.toLowerCase().trim();
if (text.equals ("am") || text.equals ("a.m") || text.equals("a"))
time = Calendar.AM;
if (text.equals ("pm") || text.equals ("p.m") || text.equals("p"))
time = Calendar.PM;
return time;
}
//---------------------DATE--------------------------
//Handles setting the task's date.
//Day of week (e.g. Monday, Tuesday,..) is overridden by a set date (e.g. October 23 2013).
//Vague times (e.g. breakfast, night) are overridden by a set time (9 am, at 10, 17:00)
@SuppressWarnings("nls")
private static void dayHelper(Task task ) {
String inputText = task.getValue(Task.TITLE);
Calendar cal = null;
Boolean containsSpecificTime = false;
String[] days_of_week = {
"(?i)\\b(today)\\b",
"(?i)\\b(tomorrow)\\b",
"(?i)\\b(mon(day\\b|\\.))",
"(?i)\\b(tue(sday\\b|\\.))",
"(?i)\\b(wed(nesday\\b|\\.))",
"(?i)\\b(thu(rsday\\b|\\.))",
"(?i)\\b(fri(day\\b|\\.))",
"(?i)\\b(sat(urday\\b|\\.))",
"(?i)\\b(sun(day\\b|\\.))" };
for (String date : days_of_week){
Pattern pattern = Pattern.compile(date);
Matcher m = pattern.matcher(inputText);
if (m.find()) {
Calendar dayCal = AstridChronic.parse(m.group(0)).getBeginCalendar();
cal = dayCal;
//then put it into task
}
}
String[] dates = {
"(?i)\\b(jan(\\.|uary))(\\s(3[0-1]|[0-2]?[0-9])),?( (\\d{4}|\\d{2}))?",
"(?i)\\b(feb(\\.|ruary))(\\s(3[0-1]|[0-2]?[0-9])),?( (\\d{4}|\\d{2}))?",
"(?i)\\b(mar(\\.|ch))(\\s(3[0-1]|[0-2]?[0-9])),?( (\\d{4}|\\d{2}))?",
"(?i)\\b(apr(\\.|il))(\\s(3[0-1]|[0-2]?[0-9])),?( (\\d{4}|\\d{2}))?",
"(?i)\\b(may())(\\s(3[0-1]|[0-2]?[0-9])),?( (\\d{4}|\\d{2}))?",
"(?i)\\b(jun(\\.|e))(\\s(3[0-1]|[0-2]?[0-9])),?( (\\d{4}|\\d{2}))?",
"(?i)\\b(jul(\\.|y))(\\s(3[0-1]|[0-2]?[0-9])),?( (\\d{4}|\\d{2}))?",
"(?i)\\b(aug(\\.|ust))(\\s(3[0-1]|[0-2]?[0-9])),?( (\\d{4}|\\d{2}))?",
"(?i)\\b(sep(\\.|tember))(\\s(3[0-1]|[0-2]?[0-9])),?( (\\d{4}|\\d{2}))?",
"(?i)\\b(oct(\\.|ober))(\\s(3[0-1]|[0-2]?[0-9])),?( (\\d{4}|\\d{2}))?",
"(?i)\\b(nov(\\.|ember))(\\s(3[0-1]|[0-2]?[0-9])),?( (\\d{4}|\\d{2}))?",
"(?i)\\b(dec(\\.|ember))(\\s(3[0-1]|[0-2]?[0-9])),?( (\\d{4}|\\d{2}))?"};
// m.group(1) = "month"
//m.group(4) = "day"
for (String date: dates) {
Pattern pattern = Pattern.compile(date);
Matcher m = pattern.matcher(inputText);
if (m.find()){
Calendar dateCal = Chronic.parse(m.group(1)).getBeginCalendar();
if (m.group(4) != null){
dateCal.set(Calendar.DAY_OF_MONTH, Integer.parseInt(m.group(4)));
}
Calendar today = Calendar.getInstance();
if (m.group(5) != null){
dateCal.set(Calendar.YEAR, Integer.parseInt(m.group(5).trim()));
}
else if (today.get(Calendar.MONTH) - dateCal.get(Calendar.MONTH) > 1){ //if more than a month in the past
dateCal.set(Calendar.YEAR, dateCal.get(Calendar.YEAR)+1);
}
if (cal == null){
cal = dateCal;
}
else{
cal.set(Calendar.DAY_OF_MONTH, dateCal.get(Calendar.DAY_OF_MONTH));
cal.set(Calendar.MONTH,dateCal.get(Calendar.MONTH) );
cal.set(Calendar.YEAR, dateCal.get(Calendar.YEAR));
}
}
}
// for dates in the format MM/DD
Pattern p = Pattern.compile("(?i)\\b(1[0-2]|0?[1-9])(\\/|-)(3[0-1]|[0-2]?[0-9])(\\/|-)?(\\d{4}|\\d{2})?");
Matcher match = p.matcher(inputText);
if (match.find()){
Calendar dCal = Calendar.getInstance();
setCalendarToDefaultTime(dCal);
dCal.set(Calendar.MONTH, Integer.parseInt(match.group(1).trim())-1);
dCal.set(Calendar.DAY_OF_MONTH, Integer.parseInt(match.group(3)));
if (match.group(5) != null && !(match.group(5).trim()).equals(""))
{
String yearString = match.group(5);
if(match.group(5).length()==2)
yearString = "20" + match.group(5);
dCal.set(Calendar.YEAR, Integer.parseInt(yearString));
}
if (cal==null){
cal = dCal;
}
else{
cal.set(Calendar.DAY_OF_MONTH, dCal.get(Calendar.DAY_OF_MONTH));
cal.set(Calendar.MONTH,dCal.get(Calendar.MONTH));
cal.set(Calendar.YEAR, dCal.get(Calendar.YEAR));
}
}
HashMap<String, Integer> dayTimes = new HashMap<String, Integer>();
dayTimes.put("(?i)\\bbreakfast\\b", 8);
dayTimes.put("(?i)\\blunch\\b", 12);
dayTimes.put("(?i)\\bsupper\\b" ,18);
dayTimes.put("(?i)\\bdinner\\b",18);
dayTimes.put("(?i)\\bbrunch\\b", 10);
dayTimes.put("(?i)\\bmorning\\b", 8);
dayTimes.put("(?i)\\bafternoon\\b", 15);
dayTimes.put("(?i)\\bevening\\b" , 19);
dayTimes.put("(?i)\\bnight\\b" , 19);
dayTimes.put("(?i)\\bmidnight\\b" , 0);
dayTimes.put("(?i)\\bnoon\\b" , 12);
for (String dayTime: dayTimes.keySet()){
Pattern pattern = Pattern.compile(dayTime);
Matcher m = pattern.matcher(inputText);
if (m.find()){
containsSpecificTime=true;
int timeHour = dayTimes.get(dayTime);
Calendar dayTimesCal = Calendar.getInstance();
setCalendarToDefaultTime(dayTimesCal);
dayTimesCal.set(Calendar.HOUR, timeHour);
if (cal == null) {
cal = dayTimesCal;
}
else {
setCalendarToDefaultTime(cal);
cal.set(Calendar.HOUR, timeHour);
}
}
}
String[] times = {
//[time] am/pm
"(?i)(\\b)([01]?\\d):?([0-5]\\d)? ?([ap]\\.?m?\\.?)\\b",
//army time
"(?i)\\b(([0-2]?[0-9]):([0-5][0-9]))(\\b)",
//[int] o'clock
"(?i)\\b(([01]?\\d)() ?o'? ?clock) ?([ap]\\.?m\\.?)?\\b",
//at [int]
"(?i)(\\bat) ([01]?\\d)()($|\\D($|\\D))"
//m.group(2) holds the hour
//m.group(3) holds the minutes
//m.group(4) holds am/pm
};
for (String time:times){
Pattern pattern = Pattern.compile(time);
Matcher m = pattern.matcher(inputText);
if (m.find()){
containsSpecificTime = true;
Calendar today = Calendar.getInstance();
Calendar timeCal = Calendar.getInstance();
setCalendarToDefaultTime(timeCal);
timeCal.set(Calendar.HOUR, Integer.parseInt(m.group(2)));
if (m.group(3) != null && !m.group(3).trim().equals(""))
timeCal.set(Calendar.MINUTE, Integer.parseInt(m.group(3)));
else
timeCal.set(Calendar.MINUTE, 0);
if (Integer.parseInt(m.group(2)) <= 12)
timeCal.set(Calendar.AM_PM, ampm_to_number(m.group(4)));
//sets it to the next occurrence of that hour if no am/pm is provided. doesn't include military time
if ( Integer.parseInt(m.group(2))<= 12 && (m.group(4)==null || (m.group(4).trim()).equals(""))) {
while (timeCal.getTime().getTime() < today.getTime().getTime()){
timeCal.set(Calendar.HOUR_OF_DAY, timeCal.get(Calendar.HOUR_OF_DAY)+12);
}
}
else { //if am/pm is provided and the time is in the past, set it to the next day. Military time included.
if (timeCal.get(Calendar.HOUR) !=0 && (timeCal.getTime().getTime() < today.getTime().getTime())) {
timeCal.set(Calendar.DAY_OF_MONTH, timeCal.get(Calendar.DAY_OF_MONTH) + 1);
}
if (timeCal.get(Calendar.HOUR) == 0){
timeCal.set(Calendar.HOUR, 12);
}
}
if (cal == null){
cal = timeCal;
}
else{
cal.set(Calendar.HOUR, timeCal.get(Calendar.HOUR));
cal.set(Calendar.MINUTE,timeCal.get(Calendar.MINUTE) );
cal.set(Calendar.SECOND, timeCal.get(Calendar.SECOND));
cal.set(Calendar.AM_PM, timeCal.get(Calendar.AM_PM));
}
break;
}
}
if( cal!= null ){ //if at least one of the above has been called, write to task. else do nothing.
if (containsSpecificTime) {
task.setValue(Task.DUE_DATE, Task.createDueDate(Task.URGENCY_SPECIFIC_DAY_TIME, cal.getTime().getTime()));
} else {
task.setValue(Task.DUE_DATE, Task.createDueDate(Task.URGENCY_SPECIFIC_DAY, cal.getTime().getTime()));
}
}
}
//---------------------DATE--------------------------
//Parses through the text and sets the frequency of the task.
@SuppressWarnings("nls")
private static void repeatHelper(Task task) {
String inputText = task.getValue(Task.TITLE);
HashMap<String, Frequency> repeatTimes = new HashMap<String, Frequency>();
repeatTimes.put("(?i)\\bevery ?\\w{0,6} days?\\b" , Frequency.DAILY);
repeatTimes.put("(?i)\\bevery ?\\w{0,6} ?nights?\\b" , Frequency.DAILY);
repeatTimes.put("(?i)\\bevery ?\\w{0,6} ?mornings?\\b" , Frequency.DAILY);
repeatTimes.put("(?i)\\bevery ?\\w{0,6} ?evenings?\\b" , Frequency.DAILY);
repeatTimes.put("(?i)\\bevery ?\\w{0,6} ?afternoons?\\b" , Frequency.DAILY);
repeatTimes.put("(?i)\\bevery \\w{0,6} ?weeks?\\b", Frequency.WEEKLY);
repeatTimes.put("(?i)\\bevery \\w{0,6} ?(mon|tues|wednes|thurs|fri|satur|sun)days?\\b", Frequency.WEEKLY);
repeatTimes.put("(?i)\\bevery \\w{0,6} ?months?\\b", Frequency.MONTHLY);
repeatTimes.put("(?i)\\bevery \\w{0,6} ?years?\\b", Frequency.YEARLY);
HashMap<String, Frequency> repeatTimesIntervalOne = new HashMap<String, Frequency>();
//pre-determined intervals of 1
repeatTimesIntervalOne.put( "(?i)\\bdaily\\b" , Frequency.DAILY);
repeatTimesIntervalOne.put( "(?i)\\beveryday\\b" , Frequency.DAILY);
repeatTimesIntervalOne.put( "(?i)\\bweekly\\b" , Frequency.WEEKLY);
repeatTimesIntervalOne.put( "(?i)\\bmonthly\\b" ,Frequency.MONTHLY);
repeatTimesIntervalOne.put( "(?i)\\byearly\\b" , Frequency.YEARLY);
for (String repeatTime:repeatTimes.keySet()){
Pattern pattern = Pattern.compile(repeatTime);
Matcher m = pattern.matcher(inputText);
if (m.find()){
Frequency rtime = repeatTimes.get(repeatTime);
RRule rrule = new RRule();
rrule.setFreq(rtime);
rrule.setInterval(findInterval(inputText));
task.setValue(Task.RECURRENCE, rrule.toIcal());
return;
}
}
for (String repeatTimeIntervalOne:repeatTimesIntervalOne.keySet()){
Pattern pattern = Pattern.compile(repeatTimeIntervalOne);
Matcher m = pattern.matcher(inputText);
if (m.find()) {
Frequency rtime = repeatTimesIntervalOne.get(repeatTimeIntervalOne);
RRule rrule = new RRule();
rrule.setFreq(rtime);
rrule.setInterval(1);
String thing = rrule.toIcal();
task.setValue(Task.RECURRENCE, thing);
return;
}
}
}
//helper method for repeatHelper.
private static int findInterval(String inputText) {
HashMap<String,Integer> words_to_num = new HashMap<String, Integer>();
String[] words = new String[] {
"one", "two", "three", "four", "five", "six",
"seven", "eight", "nine", "ten", "eleven", "twelve"
};
for(int i = 0; i < words.length; i++) {
words_to_num.put(words[i], i+1);
words_to_num.put(Integer.toString(i + 1), i + 1);
}
words_to_num.put("other" , 2);
Pattern pattern = Pattern.compile("(?i)\\bevery (\\w*)\\b");
int interval = 1;
Matcher m = pattern.matcher(inputText);
if (m.find() && m.group(1)!=null){
String interval_str = m.group(1);
if (words_to_num.containsKey(interval_str))
interval = words_to_num.get(interval_str);
else {
try {
interval = Integer.parseInt(interval_str);
} catch (NumberFormatException e) {
// Ah well
}
}
}
return interval;
}
//helper method for DayHelper. Resets the time on the calendar to 00:00:00 am
private static void setCalendarToDefaultTime(Calendar cal){
cal.set(Calendar.HOUR, 0);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.AM_PM, Calendar.AM);
}
}

@ -59,7 +59,7 @@ public class WelcomeLogin extends ActFmLoginActivity implements AuthListener {
setupLoginLater();
}
private SpannableString getLinkStringWithCustomInterval(String base, String linkComponent,
protected SpannableString getLinkStringWithCustomInterval(String base, String linkComponent,
int start, int endOffset, final OnClickListener listener) {
SpannableString link = new SpannableString (String.format("%s %s", //$NON-NLS-1$
base, linkComponent));
@ -78,7 +78,7 @@ public class WelcomeLogin extends ActFmLoginActivity implements AuthListener {
return link;
}
private void setupTermsOfService() {
protected void setupTermsOfService() {
TextView tos = (TextView)findViewById(R.id.tos);
tos.setOnClickListener(showTosListener);
@ -88,12 +88,12 @@ public class WelcomeLogin extends ActFmLoginActivity implements AuthListener {
tos.setText(link);
}
private void setupPWLogin() {
protected void setupPWLogin() {
Button pwLogin = (Button) findViewById(R.id.pw_login);
pwLogin.setOnClickListener(signUpListener);
}
private void setupLoginLater() {
protected void setupLoginLater() {
TextView loginLater = (TextView)findViewById(R.id.login_later);
loginLater.setOnClickListener(loginLaterListener);
String loginLaterBase = getString(R.string.welcome_login_later);
@ -115,14 +115,14 @@ public class WelcomeLogin extends ActFmLoginActivity implements AuthListener {
// --- event handler
private final OnClickListener showTosListener = new OnClickListener() {
protected final OnClickListener showTosListener = new OnClickListener() {
@Override
public void onClick(View v) {
Eula.showEulaBasic(WelcomeLogin.this);
}
};
private final OnClickListener loginLaterListener = new OnClickListener() {
protected final OnClickListener loginLaterListener = new OnClickListener() {
@Override
public void onClick(View arg0) {
String title = getString(R.string.welcome_login_confirm_later_title);

@ -0,0 +1,123 @@
package com.todoroo.astrid.welcome.tutorial;
import android.content.Context;
import android.os.Parcelable;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.TextView;
import com.timsu.astrid.R;
public class ViewPagerAdapter extends PagerAdapter
{
private static int[] images = new int[]
{
R.drawable.welcome_walkthrough_1,
R.drawable.welcome_walkthrough_2,
R.drawable.welcome_walkthrough_3,
R.drawable.welcome_walkthrough_4,
R.drawable.welcome_walkthrough_5,
R.drawable.welcome_walkthrough_6,
R.drawable.welcome_screen
};
private static int[] title = new int[]
{
R.string.welcome_title_1,
R.string.welcome_title_2,
R.string.welcome_title_3,
R.string.welcome_title_4,
R.string.welcome_title_5,
R.string.welcome_title_6,
R.string.welcome_title_7,
};
private static int[] body = new int[]
{
R.string.welcome_body_1,
R.string.welcome_body_2,
R.string.welcome_body_3,
R.string.welcome_body_4,
R.string.welcome_body_5,
R.string.welcome_body_6,
R.string.welcome_body_7,
};
private static int[] layouts = new int[]
{
R.layout.welcome_walkthrough_page,
R.layout.welcome_walkthrough_page,
R.layout.welcome_walkthrough_page,
R.layout.welcome_walkthrough_page,
R.layout.welcome_walkthrough_page,
R.layout.welcome_walkthrough_page,
R.layout.welcome_walkthrough_login_page,
};
private final Context context;
public WelcomeWalkthrough parent;
public ViewPagerAdapter( Context context )
{
this.context = context;
}
@Override
public int getCount()
{
return layouts.length;
}
@Override
public Object instantiateItem( View pager, int position )
{
LayoutInflater inflater = LayoutInflater.from(context);
View pageView = inflater.inflate(layouts[position], null, true);
pageView.setLayoutParams( new ViewGroup.LayoutParams(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT));
if (position != getCount()-1){
ImageView imageView = (ImageView) pageView.findViewById(R.id.welcome_walkthrough_image);
imageView.setImageResource(images[position]);
TextView titleView = (TextView) pageView.findViewById(R.id.welcome_walkthrough_title);
titleView.setText(title[position]);
TextView bodyView = (TextView) pageView.findViewById(R.id.welcome_walkthrough_body);
bodyView.setText(body[position]);
}
((ViewPager)pager).addView( pageView, 0 );
parent.pageScrolled(position, pageView);
return pageView;
}
@Override
public void destroyItem( View pager, int position, Object view )
{
((ViewPager)pager).removeView( (View)view );
}
@Override
public boolean isViewFromObject( View view, Object object )
{
return view.equals( object );
}
@Override
public void finishUpdate( View view ) {}
@Override
public void restoreState( Parcelable p, ClassLoader c ) {}
@Override
public Parcelable saveState() {
return null;
}
@Override
public void startUpdate( View view ) {}
}

@ -0,0 +1,178 @@
package com.todoroo.astrid.welcome.tutorial;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
import android.support.v4.view.ViewPager;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.TextPaint;
import android.text.style.ClickableSpan;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.widget.Button;
import android.widget.TextView;
import com.timsu.astrid.R;
import com.todoroo.andlib.utility.DialogUtilities;
import com.todoroo.astrid.actfm.ActFmLoginActivity;
import com.todoroo.astrid.activity.Eula;
import com.todoroo.astrid.welcome.WelcomeLogin;
import com.viewpagerindicator.CirclePageIndicator;
import com.viewpagerindicator.PageIndicator;
public class WelcomeWalkthrough extends ActFmLoginActivity {
ViewPager mPager;
ViewPagerAdapter mAdapter;
PageIndicator mIndicator;
View currentView;
int currentPage;
@Override
protected void onCreate(Bundle savedInstanceState) {
requestWindowFeature(Window.FEATURE_NO_TITLE);
super.onCreate(savedInstanceState);
//setContentView(R.layout.welcome_walkthrough);
mAdapter = new ViewPagerAdapter(this);
mAdapter.parent = this;
mPager = (ViewPager)findViewById(R.id.pager);
mPager.setAdapter(mAdapter);
mIndicator = (CirclePageIndicator)findViewById(R.id.indicator);
mIndicator.setViewPager(mPager);
}
@Override
protected int getContentViewResource() {
return R.layout.welcome_walkthrough;
}
@Override
protected int getTitleResource() {
return 0;
}
public void pageScrolled(int position, View view){
Log.d(null, "Updated ui");
currentView = view;
currentPage = position;
if (position == mAdapter.getCount()-1) {
initializeUI();
}
}
@Override
protected void initializeUI() {
if(mAdapter == null)
return;
if(currentPage == mAdapter.getCount()-1){
super.initializeUI();
setupTermsOfService();
setupLoginLater();
}
}
protected SpannableString getLinkStringWithCustomInterval(String base, String linkComponent,
int start, int endOffset, final OnClickListener listener) {
SpannableString link = new SpannableString (String.format("%s %s", //$NON-NLS-1$
base, linkComponent));
ClickableSpan linkSpan = new ClickableSpan() {
@Override
public void onClick(View widget) {
listener.onClick(widget);
}
@Override
public void updateDrawState(TextPaint ds) {
ds.setUnderlineText(true);
ds.setColor(Color.rgb(255, 255, 255));
}
};
link.setSpan(linkSpan, start, link.length() + endOffset, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
return link;
}
protected void setupTermsOfService() {
TextView tos = (TextView)currentView.findViewById(R.id.tos);
tos.setOnClickListener(showTosListener);
String tosBase = getString(R.string.welcome_login_tos_base);
String tosLink = getString(R.string.welcome_login_tos_link);
SpannableString link = getLinkStringWithCustomInterval(tosBase, tosLink, tosBase.length() + 2, -1, showTosListener);
tos.setText(link);
}
protected void setupPWLogin() {
Button pwLogin = (Button) findViewById(R.id.pw_login);
pwLogin.setOnClickListener(signUpListener);
}
/*
protected void setupWalkthroughLogin() {
Button loginButton = (Button)currentView.findViewById(R.id.walkthrough_login);
loginButton.setOnClickListener(showWalkthroughLoginListener);
}*/
protected void setupLoginLater() {
TextView loginLater = (TextView)currentView.findViewById(R.id.login_later);
loginLater.setOnClickListener(loginLaterListener);
String loginLaterBase = getString(R.string.welcome_login_later);
SpannableString loginLaterLink = new SpannableString(String.format("%s", loginLaterBase)); //$NON-NLS-1$
ClickableSpan laterSpan = new ClickableSpan() {
@Override
public void onClick(View widget) {
loginLaterListener.onClick(widget);
}
@Override
public void updateDrawState(TextPaint ds) {
ds.setUnderlineText(true);
ds.setColor(Color.rgb(255, 255, 255));
}
};
loginLaterLink.setSpan(laterSpan, 0, loginLaterBase.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
loginLater.setText(loginLaterLink);
}
protected final OnClickListener showTosListener = new OnClickListener() {
@Override
public void onClick(View v) {
Eula.showEulaBasic(WelcomeWalkthrough.this);
}
};
private void showWelcomeLoginActivty() {
Intent showWelcomeLogin = new Intent(this, WelcomeLogin.class);
showWelcomeLogin.putExtra(ActFmLoginActivity.SHOW_TOAST, false);
startActivity(showWelcomeLogin);
}
protected final OnClickListener showWalkthroughLoginListener = new OnClickListener() {
@Override
public void onClick(View v) {
showWelcomeLoginActivty();
}
};
protected final OnClickListener loginLaterListener = new OnClickListener() {
@Override
public void onClick(View arg0) {
String title = getString(R.string.welcome_login_confirm_later_title);
String confirmLater = getString(R.string.welcome_login_confirm_later_dialog);
DialogUtilities.okCancelCustomDialog(WelcomeWalkthrough.this, title, confirmLater,
R.string.welcome_login_confirm_later_ok,
R.string.welcome_login_confirm_later_cancel,
android.R.drawable.ic_dialog_alert,
null, confirmLaterListener);
}
private final DialogInterface.OnClickListener confirmLaterListener = new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
finish();
}
};
};
}

@ -8,6 +8,9 @@
<target name="clean">
<delete dir="release" />
<subant target="clean">
<fileset file="astrid/build.xml" />
</subant>
</target>
<target name="get-version">

@ -3,6 +3,5 @@
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
<classpathentry kind="src" path="gen"/>
<classpathentry kind="output" path="bin/classes"/>
</classpath>

@ -1,4 +1,4 @@
#Tue Mar 08 14:08:11 PST 2011
#Tue Jan 10 16:28:27 PST 2012
cleanup.add_default_serial_version_id=true
cleanup.add_generated_serial_version_id=false
cleanup.add_missing_annotations=true

@ -0,0 +1,509 @@
package com.todoroo.andlib.utility;
import java.util.Calendar;
import java.util.Date;
import com.google.ical.values.Frequency;
import com.google.ical.values.RRule;
import com.timsu.astrid.R;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.service.TaskService;
import com.todoroo.astrid.test.DatabaseTestCase;
public class TitleParserTest extends DatabaseTestCase {
@Override
protected void setUp() throws Exception {
super.setUp();
Preferences.setStringFromInteger(R.string.p_default_urgency_key, 0);
}
/** test that completing a task w/ no regular expressions creates a simple task with no date, no repeat, no lists*/
public void testNoRegexes() throws Exception{
TaskService taskService = new TaskService();
Task task = new Task();
Task nothing = new Task();
task.setValue(Task.TITLE, "Jog");
taskService.quickAdd(task);
assertFalse(task.hasDueTime());
assertFalse(task.hasDueDate());
assertEquals(task.getValue(Task.RECURRENCE), nothing.getValue(Task.RECURRENCE));
}
/** Tests correct date is parsed **/
public void testMonthDate() {
TaskService taskService = new TaskService();
Task task = new Task();
Task nothing = new Task();
String[] titleMonthStrings = {
"Jan.", "January",
"Feb.", "February",
"Mar.", "March",
"Apr.", "April",
"May", "May",
"Jun.", "June",
"Jul.", "July",
"Aug.", "August",
"Sep.", "September",
"Oct.", "October",
"Nov.", "November",
"Dec.", "December"
};
for (int i = 0; i < 23; i++) {
String testTitle = "Jog on " + titleMonthStrings[i] + " 12.";
insertTitleAddTask(testTitle, task, taskService);
Date date = new Date(task.getValue(Task.DUE_DATE));
assertEquals(date.getMonth(), i/2);
assertEquals(date.getDate(), 12);
}
}
public void testMonthSlashDay() {
TaskService taskService = new TaskService();
Task task = new Task();
Task nothing = new Task();
for (int i = 1; i < 13; i++) {
String testTitle = "Jog on " + i + "/12/13";
insertTitleAddTask(testTitle, task, taskService);
Date date = new Date(task.getValue(Task.DUE_DATE));
assertEquals(date.getMonth(), i-1);
assertEquals(date.getDate(), 12);
assertEquals(date.getYear(), 113);
}
}
public void testArmyTime() {
TaskService taskService = new TaskService();
Task task = new Task();
Task nothing = new Task();
String testTitle = "Jog on 23:21.";
insertTitleAddTask(testTitle, task, taskService);
Date date = new Date(task.getValue(Task.DUE_DATE));
assertEquals(date.getMinutes(), 21);
assertEquals(date.getHours(), 23);
}
public void test_AM_PM() {
TaskService taskService = new TaskService();
Task task = new Task();
Task nothing = new Task();
String testTitle = "Jog at 8:33 PM.";
insertTitleAddTask(testTitle, task, taskService);
Date date = new Date(task.getValue(Task.DUE_DATE));
assertEquals(date.getMinutes(), 33);
assertEquals(date.getHours(), 20);
}
public void test_at_hour() {
TaskService taskService = new TaskService();
Task task = new Task();
Task nothing = new Task();
String testTitle = "Jog at 8 PM.";
insertTitleAddTask(testTitle, task, taskService);
Date date = new Date(task.getValue(Task.DUE_DATE));
assertEquals(date.getMinutes(), 0);
assertEquals(date.getHours(), 20);
}
public void test_oclock_AM() {
TaskService taskService = new TaskService();
Task task = new Task();
Task nothing = new Task();
String testTitle = "Jog at 8 o'clock AM.";
insertTitleAddTask(testTitle, task, taskService);
Date date = new Date(task.getValue(Task.DUE_DATE));
assertEquals(date.getMinutes(), 0);
assertEquals(date.getHours(), 8);
}
public void test_several_forms_of_eight() {
TaskService taskService = new TaskService();
Task task = new Task();
Task nothing = new Task();
String[] testTitles = {
"Jog 8 AM",
"Jog 8 o'clock AM",
"at 8:00 AM"
};
for (String testTitle: testTitles) {
insertTitleAddTask(testTitle, task, taskService);
Date date = new Date(task.getValue(Task.DUE_DATE));
assertEquals(date.getMinutes(), 0);
assertEquals(date.getHours(), 8);
}
}
public void test_several_forms_of_1230PM() {
TaskService taskService = new TaskService();
Task task = new Task();
Task nothing = new Task();
String[] testTitles = {
"Jog 12:30 PM",
"at 12:30 PM",
"Do something on 12:30 PM",
"Jog at 12:30 PM Friday"
};
for (String testTitle: testTitles) {
insertTitleAddTask(testTitle, task, taskService);
Date date = new Date(task.getValue(Task.DUE_DATE));
assertEquals(date.getMinutes(), 30);
assertEquals(date.getHours(), 12);
}
}
private void insertTitleAddTask(String title, Task task, TaskService taskService) {
task.setValue(Task.TITLE, title);
taskService.quickAdd(task);
}
// ----------------Days begin----------------//
public void testDays() throws Exception{
Calendar today = Calendar.getInstance();
TaskService taskService = new TaskService();
Task task = new Task();
task.setValue(Task.TITLE, "Jog today");
taskService.quickAdd(task);
Date date = new Date(task.getValue(Task.DUE_DATE));
assertEquals(date.getDay()+1, today.get(Calendar.DAY_OF_WEEK));
//Calendar starts 1-6, date.getDay() starts at 0
task = new Task();
task.setValue(Task.TITLE, "Jog tomorrow");
taskService.quickAdd(task);
date = new Date(task.getValue(Task.DUE_DATE));
assertEquals(date.getDay()+1, today.get(Calendar.DAY_OF_WEEK)+1);
String[] days = {
"sunday",
"monday",
"tuesday",
"wednesday",
"thursday",
"friday",
"saturday",
};
String[] abrevDays = {
"sun.",
"mon.",
"tue.",
"wed.",
"thu.",
"fri.",
"sat."
};
for (int i = 1; i <= 6; i++){
task = new Task();
task.setValue(Task.TITLE, "Jog "+ days[i]);
taskService.quickAdd(task);
date = new Date(task.getValue(Task.DUE_DATE));
assertEquals(date.getDay(), i);
task = new Task();
task.setValue(Task.TITLE, "Jog "+ abrevDays[i]);
taskService.quickAdd(task);
date = new Date(task.getValue(Task.DUE_DATE));
assertEquals(date.getDay(), i);
}
}
//----------------Days end----------------//
//----------------Priority begin----------------//
/** tests all words using priority 0 */
public void testPriority0() throws Exception {
String[] acceptedStrings = {
"priority 0",
"least priority",
"lowest priority",
"bang 0"
};
TaskService taskService = new TaskService();
Task task = new Task();
for (String acceptedString:acceptedStrings){
task = new Task();
task.setValue(Task.TITLE, "Jog " + acceptedString); //test at end of task. should set importance.
taskService.quickAdd(task);
assertEquals((int)task.getValue(Task.IMPORTANCE), (int)Task.IMPORTANCE_LEAST);
}
for (String acceptedString:acceptedStrings){
task = new Task();
task.setValue(Task.TITLE, acceptedString + " jog"); //test at beginning of task. should not set importance.
taskService.quickAdd(task);
assertNotSame((int)task.getValue(Task.IMPORTANCE),(int)Task.IMPORTANCE_LEAST);
}
}
public void testPriority1() throws Exception {
String[] acceptedStringsAtEnd = {
"priority 1",
"low priority",
"bang",
"bang 1"
};
String[] acceptedStringsAnywhere = {
"!1",
"!"
};
TaskService taskService = new TaskService();
Task task = new Task();
for (String acceptedStringAtEnd:acceptedStringsAtEnd){
task = new Task();
task.setValue(Task.TITLE, "Jog " + acceptedStringAtEnd); //test at end of task. should set importance.
taskService.quickAdd(task);
assertEquals((int)task.getValue(Task.IMPORTANCE), (int)Task.IMPORTANCE_SHOULD_DO);
}
for (String acceptedStringAtEnd:acceptedStringsAtEnd){
task = new Task();
task.setValue(Task.TITLE, acceptedStringAtEnd + " jog"); //test at beginning of task. should not set importance.
taskService.quickAdd(task);
assertEquals((int)task.getValue(Task.IMPORTANCE), (int)Task.IMPORTANCE_SHOULD_DO);
}
for (String acceptedStringAnywhere:acceptedStringsAnywhere){
task = new Task();
task.setValue(Task.TITLE, "Jog " + acceptedStringAnywhere); //test at end of task. should set importance.
taskService.quickAdd(task);
assertEquals((int)task.getValue(Task.IMPORTANCE), (int)Task.IMPORTANCE_SHOULD_DO);
task.setValue(Task.TITLE, acceptedStringAnywhere + " jog"); //test at beginning of task. should set importance.
taskService.quickAdd(task);
assertEquals((int)task.getValue(Task.IMPORTANCE), (int)Task.IMPORTANCE_SHOULD_DO);
}
}
public void testPriority2() throws Exception {
String[] acceptedStringsAtEnd = {
"priority 2",
"high priority",
"bang bang",
"bang 2"
};
String[] acceptedStringsAnywhere = {
"!2",
"!!"
};
TaskService taskService = new TaskService();
Task task = new Task();
for (String acceptedStringAtEnd:acceptedStringsAtEnd){
task = new Task();
task.setValue(Task.TITLE, "Jog " + acceptedStringAtEnd); //test at end of task. should set importance.
taskService.quickAdd(task);
assertEquals((int)task.getValue(Task.IMPORTANCE), (int)Task.IMPORTANCE_MUST_DO);
task = new Task();
task.setValue(Task.TITLE, acceptedStringAtEnd + " jog"); //test at beginning of task. should not set importance.
taskService.quickAdd(task);
assertNotSame((int)task.getValue(Task.IMPORTANCE), (int)Task.IMPORTANCE_MUST_DO);
}
for (String acceptedStringAnywhere:acceptedStringsAnywhere){
task = new Task();
task.setValue(Task.TITLE, "Jog " + acceptedStringAnywhere); //test at end of task. should set importance.
taskService.quickAdd(task);
assertEquals((int)task.getValue(Task.IMPORTANCE), (int)Task.IMPORTANCE_MUST_DO);
task.setValue(Task.TITLE, acceptedStringAnywhere + " jog"); //test at beginning of task. should set importance.
taskService.quickAdd(task);
assertEquals((int)task.getValue(Task.IMPORTANCE), (int)Task.IMPORTANCE_MUST_DO);
}
}
public void testPriority3() throws Exception {
String[] acceptedStringsAtEnd = {
"priority 3",
"highest priority",
"bang bang bang",
"bang 3",
"bang bang bang bang bang bang bang"
};
String[] acceptedStringsAnywhere = {
"!3",
"!!!",
"!6",
"!!!!!!!!!!!!!"
};
TaskService taskService = new TaskService();
Task task = new Task();
for (String acceptedStringAtEnd:acceptedStringsAtEnd){
task = new Task();
task.setValue(Task.TITLE, "Jog " + acceptedStringAtEnd); //test at end of task. should set importance.
taskService.quickAdd(task);
assertEquals((int)task.getValue(Task.IMPORTANCE), (int)Task.IMPORTANCE_DO_OR_DIE);
task = new Task();
task.setValue(Task.TITLE, acceptedStringAtEnd + " jog"); //test at beginning of task. should not set importance.
taskService.quickAdd(task);
assertNotSame((int)task.getValue(Task.IMPORTANCE), (int)Task.IMPORTANCE_DO_OR_DIE);
}
for (String acceptedStringAnywhere:acceptedStringsAnywhere){
task = new Task();
task.setValue(Task.TITLE, "Jog " + acceptedStringAnywhere); //test at end of task. should set importance.
taskService.quickAdd(task);
assertEquals((int)task.getValue(Task.IMPORTANCE), (int)Task.IMPORTANCE_DO_OR_DIE);
task.setValue(Task.TITLE, acceptedStringAnywhere + " jog"); //test at beginning of task. should set importance.
taskService.quickAdd(task);
assertEquals((int)task.getValue(Task.IMPORTANCE), (int)Task.IMPORTANCE_DO_OR_DIE);
}
}
//----------------Priority end----------------//
//----------------Repeats begin----------------//
/** test daily repeat from due date, but with no due date set */
public void testDailyWithNoDueDate() throws Exception {
TaskService taskService = new TaskService();
Task task = new Task();
task.setValue(Task.TITLE, "Jog daily");
taskService.quickAdd(task);
RRule rrule = new RRule();
rrule.setFreq(Frequency.DAILY);
rrule.setInterval(1);
assertEquals(task.getValue(Task.RECURRENCE), rrule.toIcal());
assertFalse(task.hasDueTime());
assertFalse(task.hasDueDate());
task.setValue(Task.TITLE, "Jog every day");
taskService.quickAdd(task);
assertEquals(task.getValue(Task.RECURRENCE), rrule.toIcal());
assertFalse(task.hasDueTime());
assertFalse(task.hasDueDate());
for (int i = 1; i <= 12; i++){
task.setValue(Task.TITLE, "Jog every " + i + " days.");
rrule.setInterval(i);
taskService.quickAdd(task);
assertEquals(task.getValue(Task.RECURRENCE), rrule.toIcal());
assertFalse(task.hasDueTime());
assertFalse(task.hasDueDate());
task = new Task();
}
}
/** test weekly repeat from due date, with no due date & time set */
public void testWeeklyWithNoDueDate() throws Exception {
TaskService taskService = new TaskService();
Task task = new Task();
task.setValue(Task.TITLE, "Jog weekly");
taskService.quickAdd(task);
RRule rrule = new RRule();
rrule.setFreq(Frequency.WEEKLY);
rrule.setInterval(1);
assertEquals(task.getValue(Task.RECURRENCE), rrule.toIcal());
assertFalse(task.hasDueTime());
assertFalse(task.hasDueDate());
task.setValue(Task.TITLE, "Jog every week");
taskService.quickAdd(task);
assertEquals(task.getValue(Task.RECURRENCE), rrule.toIcal());
assertFalse(task.hasDueTime());
assertFalse(task.hasDueDate());
for (int i = 1; i <= 12; i++){
task.setValue(Task.TITLE, "Jog every " + i + " weeks");
rrule.setInterval(i);
taskService.quickAdd(task);
assertEquals(task.getValue(Task.RECURRENCE), rrule.toIcal());
assertFalse(task.hasDueTime());
assertFalse(task.hasDueDate());
task = new Task();
}
}
/** test hourly repeat from due date, with no due date but no time */
public void testMonthlyFromNoDueDate() throws Exception {
TaskService taskService = new TaskService();
Task task = new Task();
task.setValue(Task.TITLE, "Jog monthly");
taskService.quickAdd(task);
RRule rrule = new RRule();
rrule.setFreq(Frequency.MONTHLY);
rrule.setInterval(1);
assertEquals(task.getValue(Task.RECURRENCE), rrule.toIcal());
assertFalse(task.hasDueTime());
assertFalse(task.hasDueDate());
task.setValue(Task.TITLE, "Jog every month");
taskService.quickAdd(task);
assertEquals(task.getValue(Task.RECURRENCE), rrule.toIcal());
assertFalse(task.hasDueTime());
assertFalse(task.hasDueDate());
for (int i = 1; i <= 12; i++){
task.setValue(Task.TITLE, "Jog every " + i + " months");
rrule.setInterval(i);
taskService.quickAdd(task);
assertEquals(task.getValue(Task.RECURRENCE), rrule.toIcal());
assertFalse(task.hasDueTime());
assertFalse(task.hasDueDate());
task = new Task();
}
}
public void testDailyFromDueDate() throws Exception { TaskService taskService = new TaskService();
Task task = new Task();
task.setValue(Task.TITLE, "Jog daily starting from today");
taskService.quickAdd(task);
RRule rrule = new RRule();
rrule.setFreq(Frequency.DAILY);
rrule.setInterval(1);
assertEquals(task.getValue(Task.RECURRENCE), rrule.toIcal());
assertTrue(task.hasDueDate());
task.setValue(Task.TITLE, "Jog every day starting from today");
taskService.quickAdd(task);
assertEquals(task.getValue(Task.RECURRENCE), rrule.toIcal());
assertTrue(task.hasDueDate());
for (int i = 1; i <= 12; i++){
task.setValue(Task.TITLE, "Jog every " + i + " days starting from today");
rrule.setInterval(i);
taskService.quickAdd(task);
assertEquals(task.getValue(Task.RECURRENCE), rrule.toIcal());
assertTrue(task.hasDueDate());
task = new Task();
}
}
public void testWeeklyFromDueDate() throws Exception { TaskService taskService = new TaskService();
Task task = new Task();
task.setValue(Task.TITLE, "Jog weekly starting from today");
taskService.quickAdd(task);
RRule rrule = new RRule();
rrule.setFreq(Frequency.WEEKLY);
rrule.setInterval(1);
assertEquals(task.getValue(Task.RECURRENCE), rrule.toIcal());
assertTrue(task.hasDueDate());
task.setValue(Task.TITLE, "Jog every week starting from today");
taskService.quickAdd(task);
assertEquals(task.getValue(Task.RECURRENCE), rrule.toIcal());
assertTrue(task.hasDueDate());
for (int i = 1; i <= 12; i++){
task.setValue(Task.TITLE, "Jog every " + i + " weeks starting from today");
rrule.setInterval(i);
taskService.quickAdd(task);
assertEquals(task.getValue(Task.RECURRENCE), rrule.toIcal());
assertTrue(task.hasDueDate());
task = new Task();
}
}
//----------------Repeats end----------------//
}

@ -0,0 +1,12 @@
#Android generated
bin
gen
#IntelliJ IDEA
.idea
*.iml
#Maven
target
release.properties
pom.xml.*

@ -0,0 +1,88 @@
Change Log
==========
Version 2.2.2 *(2011-12-31)*
----------------------------
* Fix incorrect `R.java` imports in all of the sample activities.
Version 2.2.1 *(2011-12-31)*
----------------------------
* New `setTypeface(Typeface)` and `getTypeface()` methods for title indicator.
(Thanks Dimitri Fedorov)
* Added styled tab indicator sample.
* Support for widths other than those that could be measured exactly.
Version 2.2.0 *(2011-12-13)*
----------------------------
* Default title indicator style is now 'underline'.
* Title indicator now allows specifying an `OnCenterItemClickListener` which
will give you callbacks when the current item title has been clicked.
(Thanks Chris Banes)
Version 2.1.0 *(2011-11-30)*
----------------------------
* Indicators now have a `notifyDataSetChanged` method which should be called
when changes are made to the adapter.
* Fix: Avoid `NullPointerException`s when the `ViewPager` is not immediately
bound to the indicator.
Version 2.0.0 *(2011-11-20)*
----------------------------
* New `TabPageIndicator`! Uses the Ice Cream Sandwich-style action bar tabs
which fill the width of the view when there are only a few tabs or provide
horizontal animated scrolling when there are many.
* Update to link against ACLv4r4. This will now be required in all implementing
applications.
* Allow dragging the title and circle indicators to drag the pager.
* Remove orientation example as the DirectionalViewPager library has not been
updated to ACLv4r4.
Version 1.2.1 *(2011-10-20)*
----------------------------
Maven 3 is now required when building from the command line.
* Update to support ADT 14.
Version 1.2.0 *(2011-10-04)*
----------------------------
* Move to `com.viewpagerindicator` package.
* Move maven group and artifact to `com.viewpagerindicator:library`.
Version 1.1.0 *(2011-10-02)*
----------------------------
* Package changed from `com.jakewharton.android.viewpagerindicator` to
`com.jakewharton.android.view`.
* Add vertical orientation support to the circle indicator.
* Fix: Corrected drawing bug where a single frame would be drawn as if the
pager had completed its scroll when in fact it was still scrolling.
(Thanks SimonVT!)
Version 1.0.1 *(2011-09-15)*
----------------------------
* Fade selected title color to normal text color during the swipe to and from
the center position.
* Fix: Ensure both the indicator and footer line are updated when changing the
footer color via the `setFooterColor` method.
Version 1.0.0 *(2011-08-07)*
----------------------------
Initial release.

@ -0,0 +1,152 @@
Android ViewPagerIndicator
==========================
Port of [Patrik Åkerfeldt][1]'s paging indicators that are compatible with the
ViewPager from the [Android Compatibility Library][2] and
[ActionBarSherlock][3].
Try out the sample application [on the Android Market][10].
![ViewPagerIndicator Sample Screenshots][9]
Usage
=====
*For a working implementation of this project see the `sample/` folder.*
1. Include one of the widgets in your view. This should usually be placed
adjacent to the `ViewPager` it represents.
<com.viewpagerindicator.TitlePageIndicator
android:id="@+id/titles"
android:layout_height="wrap_content"
android:layout_width="fill_parent" />
2. In your `onCreate` method (or `onCreateView` for a fragment), bind the
indicator to the `ViewPager`.
//Set the pager with an adapter
ViewPager pager = (ViewPager)findViewById(R.id.pager);
pager.setAdapter(new TestAdapter(getSupportFragmentManager()));
//Bind the title indicator to the adapter
TitlePageIndicator titleIndicator = (TitlePageIndicator)findViewById(R.id.titles);
titleIndicator.setViewPager(pager);
*Note*: If you are using `TitlePageIndicator` your adapter must implement
`TitleProvider`.
3. *(Optional)* If you use an `OnPageChangeListener` with your view pager
you should set it in the indicator rather than on the pager directly.
//continued from above
titleIndicator.setOnPageChangeListener(mPageChangeListener);
Theming
-------
There are three ways to style the look of the indicators.
1. **Theme XML**. An attribute for each type of indicator is provided in which
you can specify a custom style.
2. **Layout XML**. Through the use of a custom namespace you can include any
desired styles.
3. **Object methods**. Both styles have getters and setters for each style
attribute which can be changed at any point.
Each indicator has a demo which creates the same look using each of these
methods.
Including In Your Project
-------------------------
Android-ViewPagerIndicator is presented as an [Android library project][7]. A
standalone JAR is not possible due to the theming capabilities offered by the
indicator widgets.
You can include this project by [referencing it as a library project][8] in
Eclipse or ant.
If you are a Maven user you can easily include the library by specifying it as
a dependency:
<dependency>
<groupId>com.viewpagerindicator</groupId>
<artifactId>library</artifactId>
<version>2.2.2</version>
<type>apklib</type>
</dependency>
You must also include the following repository:
<repository>
<id>com.jakewharton</id>
<url>http://r.jakewharton.com/maven/release</url>
</repository>
This project depends on the `ViewPager` class which is available in the
[Android Compatibility Library][2] or [ActionBarSherlock][3]. Details for
including one of those libraries is available on their respecitve web sites.
Developed By
============
* Jake Wharton - <jakewharton@gmail.com>
Credits
-------
* [Patrik Åkerfeldt][1] - Author of [ViewFlow][4], a precursor to the ViewPager,
which supports paged views and is the original source of both the title
and circle indicators.
* [Francisco Figueiredo Jr.][5] - Idea and [first implementation][6] for
fragment support via ViewPager.
License
=======
Copyright 2011 Patrik Åkerfeldt
Copyright 2011 Francisco Figueiredo Jr.
Copyright 2011 Jake Wharton
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.
[1]: https://github.com/pakerfeldt
[2]: http://developer.android.com/sdk/compatibility-library.html
[3]: http://actionbarsherlock.com
[4]: https://github.com/pakerfeldt/android-viewflow
[5]: https://github.com/franciscojunior
[6]: https://gist.github.com/1122947
[7]: http://developer.android.com/guide/developing/projects/projects-eclipse.html
[8]: http://developer.android.com/guide/developing/projects/projects-eclipse.html#ReferencingLibraryProject
[9]: https://raw.github.com/JakeWharton/Android-ViewPagerIndicator/master/sample/screens.png
[10]: https://market.android.com/details?id=com.viewpagerindicator.sample

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="gen"/>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
<classpathentry kind="lib" path="eclibs/android-support-v4.jar"/>
<classpathentry kind="output" path="bin/classes"/>
</classpath>

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>viewPagerIndicator</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>com.android.ide.eclipse.adt.ApkBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.viewpagerindicator"
android:versionCode="26"
android:versionName="2.2.2">
<uses-sdk android:minSdkVersion="4" />
</manifest>

@ -0,0 +1,2 @@
# specify alternate libs folder because of conflicting jar
jar.libs.dir=eclibs

@ -0,0 +1,85 @@
<?xml version="1.0" encoding="UTF-8"?>
<project name="library" default="help">
<!-- The local.properties file is created and updated by the 'android' tool.
It contains the path to the SDK. It should *NOT* be checked into
Version Control Systems. -->
<property file="local.properties" />
<!-- The ant.properties file can be created by you. It is only edited by the
'android' tool to add properties to it.
This is the place to change some Ant specific build properties.
Here are some properties you may want to change/update:
source.dir
The name of the source directory. Default is 'src'.
out.dir
The name of the output directory. Default is 'bin'.
For other overridable properties, look at the beginning of the rules
files in the SDK, at tools/ant/build.xml
Properties related to the SDK location or the project target should
be updated using the 'android' tool with the 'update' action.
This file is an integral part of the build system for your
application and should be checked into Version Control Systems.
-->
<property file="ant.properties" />
<!-- The project.properties file is created and updated by the 'android'
tool, as well as ADT.
This contains project specific properties such as project target, and library
dependencies. Lower level build properties are stored in ant.properties
(or in .classpath for Eclipse projects).
This file is an integral part of the build system for your
application and should be checked into Version Control Systems. -->
<loadproperties srcFile="project.properties" />
<!-- quick check on sdk.dir -->
<fail
message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through an env var"
unless="sdk.dir"
/>
<!-- extension targets. Uncomment the ones where you want to do custom work
in between standard targets -->
<!--
<target name="-pre-build">
</target>
<target name="-pre-compile">
</target>
/* This is typically used for code obfuscation.
Compiled code location: ${out.classes.absolute.dir}
If this is not done in place, override ${out.dex.input.absolute.dir} */
<target name="-post-compile">
</target>
-->
<!-- Import the actual build file.
To customize existing targets, there are two options:
- Customize only one target:
- copy/paste the target into this file, *before* the
<import> task.
- customize it to your needs.
- Customize the whole content of build.xml
- copy/paste the content of the rules files (minus the top node)
into this file, replacing the <import> task.
- customize to your needs.
***********************
****** IMPORTANT ******
***********************
In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
in order to avoid having your file be overridden by tools such as "android update project"
-->
<!-- version-tag: 1 -->
<import file="${sdk.dir}/tools/ant/build.xml" />
</project>

@ -0,0 +1,115 @@
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
"-//Puppy Crawl//DTD Check Configuration 1.2//EN"
"http://www.puppycrawl.com/dtds/configuration_1_2.dtd">
<module name="Checker">
<!--module name="NewlineAtEndOfFile"/-->
<module name="FileLength"/>
<module name="FileTabCharacter"/>
<!-- Trailing spaces -->
<module name="RegexpSingleline">
<property name="format" value="\s+$"/>
<property name="message" value="Line has trailing spaces."/>
</module>
<module name="TreeWalker">
<property name="cacheFile" value="${checkstyle.cache.file}"/>
<!-- Checks for Javadoc comments. -->
<!-- See http://checkstyle.sf.net/config_javadoc.html -->
<!--module name="JavadocMethod"/-->
<!--module name="JavadocType"/-->
<!--module name="JavadocVariable"/-->
<!--module name="JavadocStyle"/-->
<!-- Checks for Naming Conventions. -->
<!-- See http://checkstyle.sf.net/config_naming.html -->
<module name="ConstantName"/>
<module name="LocalFinalVariableName"/>
<module name="LocalVariableName"/>
<module name="MemberName"/>
<module name="MethodName"/>
<module name="PackageName"/>
<module name="ParameterName"/>
<module name="StaticVariableName"/>
<module name="TypeName"/>
<!-- Checks for imports -->
<!-- See http://checkstyle.sf.net/config_import.html -->
<module name="AvoidStarImport"/>
<module name="IllegalImport"/> <!-- defaults to sun.* packages -->
<module name="RedundantImport"/>
<module name="UnusedImports"/>
<!-- Checks for Size Violations. -->
<!-- See http://checkstyle.sf.net/config_sizes.html -->
<!--module name="LineLength"/-->
<!--module name="MethodLength"/-->
<!--module name="ParameterNumber"/-->
<!-- Checks for whitespace -->
<!-- See http://checkstyle.sf.net/config_whitespace.html -->
<!--module name="EmptyForIteratorPad"/-->
<!--module name="MethodParamPad"/-->
<!--module name="NoWhitespaceAfter"/-->
<!--module name="NoWhitespaceBefore"/-->
<!--module name="OperatorWrap"/-->
<!--module name="ParenPad"/-->
<!--module name="TypecastParenPad"/-->
<!--module name="WhitespaceAfter"/-->
<!--module name="WhitespaceAround"/-->
<!-- Modifier Checks -->
<!-- See http://checkstyle.sf.net/config_modifiers.html -->
<!--module name="ModifierOrder"/-->
<!--module name="RedundantModifier"/-->
<!-- Checks for blocks. You know, those {}'s -->
<!-- See http://checkstyle.sf.net/config_blocks.html -->
<!--module name="AvoidNestedBlocks"/-->
<!--module name="EmptyBlock"/-->
<!--module name="LeftCurly"/-->
<!--module name="NeedBraces"/-->
<!--module name="RightCurly"/-->
<!-- Checks for common coding problems -->
<!-- See http://checkstyle.sf.net/config_coding.html -->
<!--module name="AvoidInlineConditionals"/-->
<!--module name="DoubleCheckedLocking"/--> <!-- MY FAVOURITE -->
<!--module name="EmptyStatement"/-->
<!--module name="EqualsHashCode"/-->
<!--module name="HiddenField"/-->
<!--module name="IllegalInstantiation"/-->
<!--module name="InnerAssignment"/-->
<!--module name="MagicNumber"/-->
<!--module name="MissingSwitchDefault"/-->
<!--module name="RedundantThrows"/-->
<!--module name="SimplifyBooleanExpression"/-->
<!--module name="SimplifyBooleanReturn"/-->
<!-- Checks for class design -->
<!-- See http://checkstyle.sf.net/config_design.html -->
<!--module name="DesignForExtension"/-->
<!--module name="FinalClass"/-->
<!--module name="HideUtilityClassConstructor"/-->
<!--module name="InterfaceIsType"/-->
<!--module name="VisibilityModifier"/-->
<!-- Miscellaneous other checks. -->
<!-- See http://checkstyle.sf.net/config_misc.html -->
<!--module name="ArrayTypeStyle"/-->
<!--module name="FinalParameters"/-->
<!--module name="TodoComment"/-->
<!--module name="UpperEll"/-->
</module>
</module>

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.viewpagerindicator</groupId>
<artifactId>library</artifactId>
<name>Android-ViewPagerIndicator</name>
<packaging>apklib</packaging>
<parent>
<groupId>com.viewpagerindicator</groupId>
<artifactId>parent</artifactId>
<version>2.2.2</version>
<relativePath>../pom.xml</relativePath>
</parent>
<dependencies>
<dependency>
<groupId>android</groupId>
<artifactId>android</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>android.support</groupId>
<artifactId>compatibility-v4</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<sourceDirectory>src</sourceDirectory>
<plugins>
<plugin>
<groupId>com.jayway.maven.plugins.android.generation2</groupId>
<artifactId>maven-android-plugin</artifactId>
<extensions>true</extensions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<configuration>
<configLocation>${project.basedir}/checkstyle.xml</configLocation>
</configuration>
<executions>
<execution>
<phase>verify</phase>
<goals>
<goal>checkstyle</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

@ -0,0 +1,40 @@
-optimizationpasses 5
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dontpreverify
-verbose
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keep public class com.android.vending.licensing.ILicensingService
-keepclasseswithmembernames class * {
native <methods>;
}
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet);
}
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet, int);
}
-keepclassmembers class * extends android.app.Activity {
public void *(android.view.View);
}
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}

@ -0,0 +1,12 @@
# This file is automatically generated by Android Tools.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file must be checked in Version Control Systems.
#
# To customize properties used by the Ant build system use,
# "ant.properties", and override values to adapt the script to your
# project structure.
android.library=true
# Project target.
target=android-14

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2010 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:state_enabled="false" android:color="@color/vpi__bright_foreground_disabled_holo_dark"/>
<item android:state_window_focused="false" android:color="@color/vpi__bright_foreground_holo_dark"/>
<item android:state_pressed="true" android:color="@color/vpi__bright_foreground_holo_dark"/>
<item android:state_selected="true" android:color="@color/vpi__bright_foreground_holo_dark"/>
<!--item android:state_activated="true" android:color="@color/vpi__bright_foreground_holo_dark"/-->
<item android:color="@color/vpi__bright_foreground_holo_dark"/> <!-- not selected -->
</selector>

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2010 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:state_enabled="false" android:color="@color/vpi__bright_foreground_disabled_holo_light"/>
<item android:state_window_focused="false" android:color="@color/vpi__bright_foreground_holo_light"/>
<item android:state_pressed="true" android:color="@color/vpi__bright_foreground_holo_light"/>
<item android:state_selected="true" android:color="@color/vpi__bright_foreground_holo_light"/>
<!--item android:state_activated="true" android:color="@color/vpi__bright_foreground_holo_light"/-->
<item android:color="@color/vpi__bright_foreground_holo_light"/> <!-- not selected -->
</selector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 B

@ -0,0 +1,34 @@
<?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">
<!-- Non focused states -->
<item android:state_focused="false" android:state_selected="false" android:state_pressed="false" android:drawable="@drawable/vpi__tab_unselected_holo" />
<item android:state_focused="false" android:state_selected="true" android:state_pressed="false" android:drawable="@drawable/vpi__tab_selected_holo" />
<!-- Focused states -->
<item android:state_focused="true" android:state_selected="false" android:state_pressed="false" android:drawable="@drawable/vpi__tab_unselected_focused_holo" />
<item android:state_focused="true" android:state_selected="true" android:state_pressed="false" android:drawable="@drawable/vpi__tab_selected_focused_holo" />
<!-- Pressed -->
<!-- Non focused states -->
<item android:state_focused="false" android:state_selected="false" android:state_pressed="true" android:drawable="@drawable/vpi__tab_unselected_pressed_holo" />
<item android:state_focused="false" android:state_selected="true" android:state_pressed="true" android:drawable="@drawable/vpi__tab_selected_pressed_holo" />
<!-- Focused states -->
<item android:state_focused="true" android:state_selected="false" android:state_pressed="true" android:drawable="@drawable/vpi__tab_unselected_pressed_holo" />
<item android:state_focused="true" android:state_selected="true" android:state_pressed="true" android:drawable="@drawable/vpi__tab_selected_pressed_holo" />
</selector>

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<view
xmlns:android="http://schemas.android.com/apk/res/android"
class="com.viewpagerindicator.TabPageIndicator$TabView"
style="?attr/vpiTabPageIndicatorStyle">
<TextView
android:id="@android:id/text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
style="?attr/vpiTabTextStyle" />
</view>

@ -0,0 +1,84 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2011 Patrik Åkerfeldt
Copyright (C) 2011 Jake Wharton
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.
-->
<resources>
<declare-styleable name="ViewPagerIndicator">
<!-- Style of the circle indicator. -->
<attr name="vpiCirclePageIndicatorStyle" format="reference"/>
<!-- Style of the title indicator. -->
<attr name="vpiTitlePageIndicatorStyle" format="reference"/>
<!-- Style of the tab indicator. -->
<attr name="vpiTabPageIndicatorStyle" format="reference"/>
<!-- Style of the text in a tab. -->
<attr name="vpiTabTextStyle" format="reference"/>
</declare-styleable>
<declare-styleable name="CirclePageIndicator">
<!-- Whether or not the indicators should be centered. -->
<attr name="centered" format="boolean" />
<!-- Color of the filled circle that represents the current page. -->
<attr name="fillColor" format="color" />
<!-- Orientation of the indicator. -->
<attr name="orientation">
<enum name="horizontal" value="0" />
<enum name="vertical" value="1" />
</attr>
<!-- Radius of the circles. This is also the spacing between circles. -->
<attr name="radius" format="dimension" />
<!-- Whether or not the selected indicator snaps to the circles. -->
<attr name="snap" format="boolean" />
<!-- Color of the open circles. -->
<attr name="strokeColor" format="color" />
<!-- Width of the stroke used to draw the circles. -->
<attr name="strokeWidth" format="dimension" />
</declare-styleable>
<declare-styleable name="TitlePageIndicator">
<!-- Screen edge padding. -->
<attr name="clipPadding" format="dimension" />
<!-- Color of the footer line and indicator. -->
<attr name="footerColor" format="color" />
<!-- Height of the footer line. -->
<attr name="footerLineHeight" format="dimension" />
<!-- Style of the indicator. Default is triangle. -->
<attr name="footerIndicatorStyle">
<enum name="none" value="0" />
<enum name="triangle" value="1" />
<enum name="underline" value="2" />
</attr>
<!-- Height of the indicator above the footer line. -->
<attr name="footerIndicatorHeight" format="dimension" />
<!-- Left and right padding of the underline indicator. -->
<attr name="footerIndicatorUnderlinePadding" format="dimension" />
<!-- Padding between the bottom of the title and the footer. -->
<attr name="footerPadding" format="dimension" />
<!-- Color of the selected title. -->
<attr name="selectedColor" format="color" />
<!-- Whether or not the selected item is displayed as bold. -->
<attr name="selectedBold" format="boolean" />
<!-- Color of regular titles. -->
<attr name="textColor" format="color" />
<!-- Size of title text. -->
<attr name="textSize" format="dimension" />
<!-- Padding between titles when bumping into each other. -->
<attr name="titlePadding" format="dimension" />
<!-- Padding between titles and the top of the View. -->
<attr name="topPadding" format="dimension" />
</declare-styleable>
</resources>

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2011 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.
-->
<resources>
<color name="vpi__background_holo_dark">#ff000000</color>
<color name="vpi__background_holo_light">#fff3f3f3</color>
<color name="vpi__bright_foreground_holo_dark">@color/vpi__background_holo_light</color>
<color name="vpi__bright_foreground_holo_light">@color/vpi__background_holo_dark</color>
<color name="vpi__bright_foreground_disabled_holo_dark">#ff4c4c4c</color>
<color name="vpi__bright_foreground_disabled_holo_light">#ffb2b2b2</color>
<color name="vpi__bright_foreground_inverse_holo_dark">@color/vpi__bright_foreground_holo_light</color>
<color name="vpi__bright_foreground_inverse_holo_light">@color/vpi__bright_foreground_holo_dark</color>
</resources>

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

Loading…
Cancel
Save