Applying intellij-java-google-style

pull/645/merge
Alex Baker 6 years ago
parent d1bdd60a9c
commit da10291b04

@ -1,21 +1,22 @@
package org.tasks;
import javax.inject.Inject;
import org.tasks.caldav.CaldavAccountManager;
import org.tasks.preferences.Preferences;
import javax.inject.Inject;
public class FlavorSetup {
private final CaldavAccountManager caldavAccountManager;
private final Preferences preferences;
@Inject
public FlavorSetup(CaldavAccountManager caldavAccountManager, Preferences preferences) {
this.caldavAccountManager = caldavAccountManager;
this.preferences = preferences;
}
private final CaldavAccountManager caldavAccountManager;
private final Preferences preferences;
@Inject
public FlavorSetup(CaldavAccountManager caldavAccountManager, Preferences preferences) {
this.caldavAccountManager = caldavAccountManager;
this.preferences = preferences;
}
public void setup() {
caldavAccountManager.setBackgroundSynchronization(preferences.getBoolean(R.string.p_background_sync, true));
}
public void setup() {
caldavAccountManager
.setBackgroundSynchronization(preferences.getBoolean(R.string.p_background_sync, true));
}
}

@ -1,97 +1,94 @@
package org.tasks.analytics;
import android.content.Context;
import com.google.android.gms.analytics.ExceptionParser;
import com.google.android.gms.analytics.ExceptionReporter;
import com.google.android.gms.analytics.GoogleAnalytics;
import com.google.android.gms.analytics.HitBuilders;
import com.google.android.gms.analytics.StandardExceptionParser;
import com.google.common.base.Strings;
import javax.inject.Inject;
import org.tasks.BuildConfig;
import org.tasks.R;
import org.tasks.injection.ApplicationScope;
import org.tasks.injection.ForApplication;
import javax.inject.Inject;
import timber.log.Timber;
@ApplicationScope
public class Tracker {
private final GoogleAnalytics analytics;
private final com.google.android.gms.analytics.Tracker tracker;
private final ExceptionParser exceptionParser;
private final Context context;
@Inject
public Tracker(@ForApplication Context context) {
this.context = context;
analytics = GoogleAnalytics.getInstance(context);
tracker = analytics.newTracker(R.xml.google_analytics);
tracker.setAppVersion(Integer.toString(BuildConfig.VERSION_CODE));
final StandardExceptionParser standardExceptionParser = new StandardExceptionParser(context, null);
exceptionParser = (thread, throwable) -> {
StringBuilder stack = new StringBuilder()
.append(standardExceptionParser.getDescription(thread, throwable))
.append("\n")
.append(throwable.getClass().getName())
.append("\n");
for (StackTraceElement element : throwable.getStackTrace()) {
stack.append(element.toString())
.append("\n");
}
return stack.toString();
};
ExceptionReporter reporter = new ExceptionReporter(
tracker,
Thread.getDefaultUncaughtExceptionHandler(),
context);
reporter.setExceptionParser(exceptionParser);
Thread.setDefaultUncaughtExceptionHandler(reporter);
}
public void setTrackingEnabled(boolean enabled) {
analytics.setAppOptOut(!enabled);
}
public void reportException(Throwable t) {
reportException(Thread.currentThread(), t);
}
public void reportException(Thread thread, Throwable t) {
Timber.e(t, t.getMessage());
tracker.send(new HitBuilders.ExceptionBuilder()
.setDescription(exceptionParser.getDescription(thread.getName(), t))
.setFatal(false)
.build());
}
public void reportEvent(Tracking.Events event) {
reportEvent(event, null);
}
public void reportEvent(Tracking.Events event, String label) {
reportEvent(event, event.action, label);
}
public void reportEvent(Tracking.Events event, int action, String label) {
reportEvent(event, context.getString(action), label);
}
public void reportEvent(Tracking.Events event, String action, String label) {
reportEvent(event.category, action, label);
}
private void reportEvent(int category, String action, String label) {
HitBuilders.EventBuilder eventBuilder = new HitBuilders.EventBuilder()
.setCategory(context.getString(category))
.setAction(action);
if (!Strings.isNullOrEmpty(label)) {
eventBuilder.setLabel(label);
}
tracker.send(eventBuilder.build());
private final GoogleAnalytics analytics;
private final com.google.android.gms.analytics.Tracker tracker;
private final ExceptionParser exceptionParser;
private final Context context;
@Inject
public Tracker(@ForApplication Context context) {
this.context = context;
analytics = GoogleAnalytics.getInstance(context);
tracker = analytics.newTracker(R.xml.google_analytics);
tracker.setAppVersion(Integer.toString(BuildConfig.VERSION_CODE));
final StandardExceptionParser standardExceptionParser = new StandardExceptionParser(context,
null);
exceptionParser = (thread, throwable) -> {
StringBuilder stack = new StringBuilder()
.append(standardExceptionParser.getDescription(thread, throwable))
.append("\n")
.append(throwable.getClass().getName())
.append("\n");
for (StackTraceElement element : throwable.getStackTrace()) {
stack.append(element.toString())
.append("\n");
}
return stack.toString();
};
ExceptionReporter reporter = new ExceptionReporter(
tracker,
Thread.getDefaultUncaughtExceptionHandler(),
context);
reporter.setExceptionParser(exceptionParser);
Thread.setDefaultUncaughtExceptionHandler(reporter);
}
public void setTrackingEnabled(boolean enabled) {
analytics.setAppOptOut(!enabled);
}
public void reportException(Throwable t) {
reportException(Thread.currentThread(), t);
}
public void reportException(Thread thread, Throwable t) {
Timber.e(t, t.getMessage());
tracker.send(new HitBuilders.ExceptionBuilder()
.setDescription(exceptionParser.getDescription(thread.getName(), t))
.setFatal(false)
.build());
}
public void reportEvent(Tracking.Events event) {
reportEvent(event, null);
}
public void reportEvent(Tracking.Events event, String label) {
reportEvent(event, event.action, label);
}
public void reportEvent(Tracking.Events event, int action, String label) {
reportEvent(event, context.getString(action), label);
}
public void reportEvent(Tracking.Events event, String action, String label) {
reportEvent(event.category, action, label);
}
private void reportEvent(int category, String action, String label) {
HitBuilders.EventBuilder eventBuilder = new HitBuilders.EventBuilder()
.setCategory(context.getString(category))
.setAction(action);
if (!Strings.isNullOrEmpty(label)) {
eventBuilder.setLabel(label);
}
tracker.send(eventBuilder.build());
}
}

@ -2,31 +2,31 @@ package org.tasks.billing;
import android.app.Activity;
import android.content.Intent;
import javax.inject.Inject;
public class PurchaseHelper {
@Inject
public PurchaseHelper() {
}
@Inject
public PurchaseHelper() {
}
public boolean purchase(final Activity activity,
final String sku, final String pref,
final int requestCode, final PurchaseHelperCallback callback) {
callback.purchaseCompleted(false, sku);
return false;
}
public boolean purchase(final Activity activity,
final String sku, final String pref,
final int requestCode, final PurchaseHelperCallback callback) {
callback.purchaseCompleted(false, sku);
return false;
}
public void handleActivityResult(PurchaseHelperCallback callback, int requestCode, int resultCode, Intent data) {
public void handleActivityResult(PurchaseHelperCallback callback, int requestCode, int resultCode,
Intent data) {
}
}
public void disposeIabHelper() {
public void disposeIabHelper() {
}
}
public void consumePurchases() {
public void consumePurchases() {
}
}
}

@ -1,40 +1,39 @@
package org.tasks.gtasks;
import android.app.Activity;
import com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential;
import com.todoroo.astrid.gtasks.auth.GtasksLoginActivity;
import javax.inject.Inject;
public class PlayServices {
@Inject
public PlayServices() {
@Inject
public PlayServices() {
}
}
public boolean isPlayServicesAvailable() {
return false;
}
public boolean isPlayServicesAvailable() {
return false;
}
public boolean refreshAndCheck() {
return false;
}
public boolean refreshAndCheck() {
return false;
}
public void resolve(Activity activity) {
public void resolve(Activity activity) {
}
}
public String getStatus() {
return null;
}
public String getStatus() {
return null;
}
public boolean clearToken(GoogleAccountCredential googleAccountCredential) {
return false;
}
public boolean clearToken(GoogleAccountCredential googleAccountCredential) {
return false;
}
public void getAuthToken(GtasksLoginActivity gtasksLoginActivity, String a, GtasksLoginActivity.AuthResultHandler authResultHandler) {
public void getAuthToken(GtasksLoginActivity gtasksLoginActivity, String a,
GtasksLoginActivity.AuthResultHandler authResultHandler) {
}
}
}

@ -1,28 +1,26 @@
package org.tasks.location;
import org.tasks.data.Location;
import java.util.List;
import javax.inject.Inject;
import org.tasks.data.Location;
@SuppressWarnings("EmptyMethod")
public class GeofenceApi {
@Inject
public GeofenceApi() {
@Inject
public GeofenceApi() {
}
}
public void register(List<Location> activeGeofences) {
public void register(List<Location> activeGeofences) {
}
}
public void cancel(Location geofence) {
public void cancel(Location geofence) {
}
}
public void cancel(List<Location> geofences) {
public void cancel(List<Location> geofences) {
}
}
}

@ -3,16 +3,16 @@ package org.tasks.location;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import org.tasks.data.Location;
import org.tasks.preferences.Preferences;
public class PlacePicker {
public static Intent getIntent(Activity activity) {
return null;
}
public static Location getPlace(Context context, Intent data, Preferences preferences) {
return null;
}
public static Intent getIntent(Activity activity) {
return null;
}
public static Location getPlace(Context context, Intent data, Preferences preferences) {
return null;
}
}

@ -3,26 +3,25 @@
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.andlib.test;
import static android.support.test.InstrumentationRegistry.getTargetContext;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.tasks.R;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import static android.support.test.InstrumentationRegistry.getTargetContext;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.tasks.R;
/**
* Tests translations for consistency with the default values. You must
@ -30,235 +29,252 @@ import static junit.framework.Assert.assertTrue;
* and arrays.
*
* @author Tim Su <tim@todoroo.com>
*
*/
@RunWith(AndroidJUnit4.class)
public class TranslationTests {
public interface Callback<T> {
void apply(T entry);
/**
* Loop through each locale and call runnable
*/
private void forEachLocale(Callback<Resources> callback) {
Locale[] locales = Locale.getAvailableLocales();
for (Locale locale : locales) {
callback.apply(getResourcesForLocale(locale));
}
}
private Resources getResourcesForLocale(Locale locale) {
Resources resources = getTargetContext().getResources();
Configuration configuration = new Configuration(resources.getConfiguration());
configuration.locale = locale;
return new Resources(resources.getAssets(), resources.getDisplayMetrics(), configuration);
}
/**
* Internal test of format string parser
*/
@Test
public void testFormatStringParser() {
String s = "abc";
FormatStringData data = new FormatStringData(s);
assertEquals(s, data.string);
assertEquals(0, data.characters.length);
s = "abc %s def";
data = new FormatStringData(s);
assertEquals(1, data.characters.length);
assertEquals('s', data.characters[0]);
s = "abc %%s def %d";
data = new FormatStringData(s);
assertEquals(2, data.characters.length);
assertEquals('%', data.characters[0]);
assertEquals('d', data.characters[1]);
assertTrue(data.toString(), data.toString().contains("[%"));
assertTrue(data.toString(), data.toString().contains("d]"));
assertTrue(data.toString(), data.toString().contains(s));
assertTrue(data.matches(new FormatStringData("espanol %% und %d si")));
assertFalse(data.matches(new FormatStringData("ingles %d ja %% pon")));
s = "% abc %";
data = new FormatStringData(s);
assertEquals(2, data.characters.length);
assertEquals(' ', data.characters[0]);
assertEquals('\0', data.characters[1]);
}
/**
* Test that the format specifiers in translations match exactly the
* translations in the default text
*/
@Test
public void testFormatStringsMatch() throws Exception {
final Resources resources = getTargetContext().getResources();
final int[] strings = getResourceIds(R.string.class);
final FormatStringData[] formatStrings = new FormatStringData[strings.length];
final StringBuilder failures = new StringBuilder();
for (int i = 0; i < strings.length; i++) {
try {
String string = resources.getString(strings[i]);
formatStrings[i] = new FormatStringData(string);
} catch (Exception e) {
String name = resources.getResourceName(strings[i]);
failures.append(String.format("error opening %s: %s\n",
name, e.getMessage()));
}
}
/**
* Loop through each locale and call runnable
*/
private void forEachLocale(Callback<Resources> callback) {
Locale[] locales = Locale.getAvailableLocales();
for(Locale locale : locales) {
callback.apply(getResourcesForLocale(locale));
forEachLocale(r -> {
Locale locale = r.getConfiguration().locale;
for (int i = 0; i < strings.length; i++) {
try {
switch (strings[i]) {
case R.string.abc_shareactionprovider_share_with_application:
continue;
}
String string = r.getString(strings[i]);
FormatStringData newFS = new FormatStringData(string);
if (!newFS.matches(formatStrings[i])) {
String name = r.getResourceName(strings[i]);
failures.append(String.format("%s (%s): %s != %s\n",
name, locale.toString(), newFS, formatStrings[i]));
}
} catch (Exception e) {
String name = r.getResourceName(strings[i]);
failures.append(String.format("%s: error opening %s: %s\n",
locale.toString(), name, e.getMessage()));
}
}
});
assertTrue(failures.toString(), errorCount(failures) == 0);
}
/**
* check if string contains contains substrings
*/
private void contains(Resources r, int resource, StringBuilder failures, String expected) {
String translation = r.getString(resource);
if (!translation.contains(expected)) {
Locale locale = r.getConfiguration().locale;
String name = r.getResourceName(resource);
failures.append(String.format("%s: %s did not contain: %s\n",
locale.toString(), name, expected));
}
private Resources getResourcesForLocale(Locale locale) {
Resources resources = getTargetContext().getResources();
Configuration configuration = new Configuration(resources.getConfiguration());
configuration.locale = locale;
return new Resources(resources.getAssets(), resources.getDisplayMetrics(), configuration);
}
/**
* Test dollar sign resources
*/
@Test
public void testSpecialStringsMatch() throws Exception {
final StringBuilder failures = new StringBuilder();
forEachLocale(r -> {
contains(r, R.string.CFC_tag_text, failures, "?");
contains(r, R.string.CFC_title_contains_text, failures, "?");
contains(r, R.string.CFC_dueBefore_text, failures, "?");
contains(r, R.string.CFC_tag_contains_text, failures, "?");
contains(r, R.string.CFC_gtasks_list_text, failures, "?");
});
assertEquals(failures.toString(), 0,
failures.toString().replaceAll("[^\n]", "").length());
}
/**
* Count newlines
*/
private int errorCount(StringBuilder failures) {
int count = 0;
int pos = -1;
while (true) {
pos = failures.indexOf("\n", pos + 1);
if (pos == -1) {
return count;
}
count++;
}
}
/**
* @return an array of all string resource id's
*/
private int[] getResourceIds(Class<?> resources) {
Field[] fields = resources.getDeclaredFields();
List<Integer> ids = new ArrayList<>(fields.length);
for (Field field : fields) {
try {
ids.add(field.getInt(null));
} catch (Exception e) {
// not a field we care about
}
}
int[] idsAsIntArray = new int[ids.size()];
for (int i = 0; i < ids.size(); i++) {
idsAsIntArray[i] = ids.get(i);
}
return idsAsIntArray;
}
private static final class FormatStringData {
private static final char[] scratch = new char[10];
/** format characters */
public final char[] characters;
/** the original string */
public final String string;
public FormatStringData(String string) {
this.string = string;
int pos = -1;
int count = 0;
while(true) {
pos = string.indexOf('%', ++pos);
if(pos++ == -1)
break;
if(pos >= string.length())
scratch[count++] = '\0';
else
scratch[count++] = string.charAt(pos);
}
characters = new char[count];
for(int i = 0; i < count; i++) {
characters[i] = scratch[i];
}
}
public interface Callback<T> {
/** test that the characters match */
public boolean matches(FormatStringData other) {
if(characters.length != other.characters.length)
return false;
outer: for(int i = 0; i < characters.length; i++) {
if(Character.isDigit(characters[i])) {
for(int j = 0; j < other.characters.length; j++)
if(characters[i] == other.characters[j])
break outer;
return false;
} else if(characters[i] != other.characters[i])
return false;
void apply(T entry);
}
}
return true;
}
private static final class FormatStringData {
@Override
public String toString() {
StringBuilder value = new StringBuilder("[");
for(int i = 0; i < characters.length; i++) {
value.append(characters[i]);
if(i < characters.length - 1)
value.append(',');
}
value.append("]: '").append(string).append('\'');
return value.toString();
}
}
private static final char[] scratch = new char[10];
/**
* Internal test of format string parser
* format characters
*/
@Test
public void testFormatStringParser() {
String s = "abc";
FormatStringData data = new FormatStringData(s);
assertEquals(s, data.string);
assertEquals(0, data.characters.length);
s = "abc %s def";
data = new FormatStringData(s);
assertEquals(1, data.characters.length);
assertEquals('s', data.characters[0]);
s = "abc %%s def %d";
data = new FormatStringData(s);
assertEquals(2, data.characters.length);
assertEquals('%', data.characters[0]);
assertEquals('d', data.characters[1]);
assertTrue(data.toString(), data.toString().contains("[%"));
assertTrue(data.toString(), data.toString().contains("d]"));
assertTrue(data.toString(), data.toString().contains(s));
assertTrue(data.matches(new FormatStringData("espanol %% und %d si")));
assertFalse(data.matches(new FormatStringData("ingles %d ja %% pon")));
s = "% abc %";
data = new FormatStringData(s);
assertEquals(2, data.characters.length);
assertEquals(' ', data.characters[0]);
assertEquals('\0', data.characters[1]);
}
public final char[] characters;
/**
* Test that the format specifiers in translations match exactly the
* translations in the default text
* the original string
*/
@Test
public void testFormatStringsMatch() throws Exception {
final Resources resources = getTargetContext().getResources();
final int[] strings = getResourceIds(R.string.class);
final FormatStringData[] formatStrings = new FormatStringData[strings.length];
final StringBuilder failures = new StringBuilder();
for(int i = 0; i < strings.length; i++) {
try {
String string = resources.getString(strings[i]);
formatStrings[i] = new FormatStringData(string);
} catch (Exception e) {
String name = resources.getResourceName(strings[i]);
failures.append(String.format("error opening %s: %s\n",
name, e.getMessage()));
}
}
forEachLocale(r -> {
Locale locale = r.getConfiguration().locale;
for(int i = 0; i < strings.length; i++) {
try {
switch(strings[i]) {
case R.string.abc_shareactionprovider_share_with_application:
continue;
}
String string = r.getString(strings[i]);
FormatStringData newFS = new FormatStringData(string);
if(!newFS.matches(formatStrings[i])) {
String name = r.getResourceName(strings[i]);
failures.append(String.format("%s (%s): %s != %s\n",
name, locale.toString(), newFS, formatStrings[i]));
}
} catch (Exception e) {
String name = r.getResourceName(strings[i]);
failures.append(String.format("%s: error opening %s: %s\n",
locale.toString(), name, e.getMessage()));
}
}
});
public final String string;
assertTrue(failures.toString(), errorCount(failures) == 0);
}
public FormatStringData(String string) {
this.string = string;
/**
* check if string contains contains substrings
*/
private void contains(Resources r, int resource, StringBuilder failures, String expected) {
String translation = r.getString(resource);
if(!translation.contains(expected)) {
Locale locale = r.getConfiguration().locale;
String name = r.getResourceName(resource);
failures.append(String.format("%s: %s did not contain: %s\n",
locale.toString(), name, expected));
int pos = -1;
int count = 0;
while (true) {
pos = string.indexOf('%', ++pos);
if (pos++ == -1) {
break;
}
if (pos >= string.length()) {
scratch[count++] = '\0';
} else {
scratch[count++] = string.charAt(pos);
}
}
characters = new char[count];
for (int i = 0; i < count; i++) {
characters[i] = scratch[i];
}
}
/**
* Test dollar sign resources
*/
@Test
public void testSpecialStringsMatch() throws Exception {
final StringBuilder failures = new StringBuilder();
forEachLocale(r -> {
contains(r, R.string.CFC_tag_text, failures, "?");
contains(r, R.string.CFC_title_contains_text, failures, "?");
contains(r, R.string.CFC_dueBefore_text, failures, "?");
contains(r, R.string.CFC_tag_contains_text, failures, "?");
contains(r, R.string.CFC_gtasks_list_text, failures, "?");
});
assertEquals(failures.toString(), 0,
failures.toString().replaceAll("[^\n]", "").length());
}
/**
* Count newlines
* test that the characters match
*/
private int errorCount(StringBuilder failures) {
int count = 0;
int pos = -1;
while(true) {
pos = failures.indexOf("\n", pos + 1);
if(pos == -1)
return count;
count++;
public boolean matches(FormatStringData other) {
if (characters.length != other.characters.length) {
return false;
}
outer:
for (int i = 0; i < characters.length; i++) {
if (Character.isDigit(characters[i])) {
for (int j = 0; j < other.characters.length; j++) {
if (characters[i] == other.characters[j]) {
break outer;
}
}
return false;
} else if (characters[i] != other.characters[i]) {
return false;
}
}
return true;
}
/**
* @return an array of all string resource id's
*/
private int[] getResourceIds(Class<?> resources) {
Field[] fields = resources.getDeclaredFields();
List<Integer> ids = new ArrayList<>(fields.length);
for (Field field : fields) {
try {
ids.add(field.getInt(null));
} catch (Exception e) {
// not a field we care about
}
@Override
public String toString() {
StringBuilder value = new StringBuilder("[");
for (int i = 0; i < characters.length; i++) {
value.append(characters[i]);
if (i < characters.length - 1) {
value.append(',');
}
int[] idsAsIntArray = new int[ids.size()];
for(int i = 0; i < ids.size(); i++)
idsAsIntArray[i] = ids.get(i);
return idsAsIntArray;
}
value.append("]: '").append(string).append('\'');
return value.toString();
}
}
}

@ -3,20 +3,8 @@
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.andlib.utility;
import android.content.res.Configuration;
import android.support.test.runner.AndroidJUnit4;
import android.util.DisplayMetrics;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.tasks.Snippet;
import org.tasks.time.DateTime;
import java.util.Locale;
package com.todoroo.andlib.utility;
import static android.support.test.InstrumentationRegistry.getTargetContext;
import static com.todoroo.andlib.utility.DateUtilities.addCalendarMonthsToUnixtime;
@ -31,273 +19,298 @@ import static org.tasks.Freeze.freezeAt;
import static org.tasks.date.DateTimeUtils.newDate;
import static org.tasks.date.DateTimeUtils.newDateTime;
import android.content.res.Configuration;
import android.support.test.runner.AndroidJUnit4;
import android.util.DisplayMetrics;
import java.util.Locale;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.tasks.Snippet;
import org.tasks.time.DateTime;
@RunWith(AndroidJUnit4.class)
public class DateUtilitiesTest {
private static Locale defaultLocale;
@Before
public void setUp() {
defaultLocale = Locale.getDefault();
setLocale(Locale.US);
}
@After
public void tearDown() {
DateUtilities.is24HourOverride = null;
setLocale(defaultLocale);
}
private void setLocale(Locale locale) {
org.tasks.locale.Locale.setDefault(locale);
Configuration config = new Configuration();
config.locale = locale;
DisplayMetrics metrics = getTargetContext().getResources().getDisplayMetrics();
getTargetContext().getResources().updateConfiguration(config, metrics);
}
public void forEachLocale(Runnable r) {
Locale[] locales = Locale.getAvailableLocales();
for(Locale locale : locales) {
setLocale(locale);
r.run();
}
}
@Test
public void testTimeString() {
forEachLocale(() -> {
DateTime d = newDateTime();
DateUtilities.is24HourOverride = false;
for (int i = 0; i < 24; i++) {
d = d.withHourOfDay(i);
getTimeString(getTargetContext(), d);
}
DateUtilities.is24HourOverride = true;
for (int i = 0; i < 24; i++) {
d = d.withHourOfDay(i);
getTimeString(getTargetContext(), d);
}
});
}
@Test
public void testDateString() {
forEachLocale(() -> {
DateTime d = newDateTime();
for (int i = 0; i < 12; i++) {
d = d.withMonthOfYear(i);
getDateString(d);
}
});
}
@Test
public void testGet24HourTime() {
DateUtilities.is24HourOverride = true;
assertEquals("09:05", getTimeString(null, new DateTime(2014, 1, 4, 9, 5, 36)));
assertEquals("13:00", getTimeString(null, new DateTime(2014, 1, 4, 13, 0, 1)));
}
@Test
public void testGetTime() {
DateUtilities.is24HourOverride = false;
assertEquals("9:05 AM", getTimeString(null, new DateTime(2014, 1, 4, 9, 5, 36)));
assertEquals("1:05 PM", getTimeString(null, new DateTime(2014, 1, 4, 13, 5, 36)));
}
@Test
public void testGetTimeWithNoMinutes() {
DateUtilities.is24HourOverride = false;
assertEquals("1 PM", getTimeString(null, new DateTime(2014, 1, 4, 13, 0, 59))); // derp?
}
@Test
public void testGetDateStringWithYear() {
assertEquals("Jan 4 '14", getDateString(new DateTime(2014, 1, 4, 0, 0, 0)));
}
@Test
public void testGetDateStringHidingYear() {
freezeAt(newDate(2014, 1, 1)).thawAfter(new Snippet() {{
assertEquals("Jan 1", getDateString(newDateTime()));
}});
}
@Test
public void testGetDateStringWithDifferentYear() {
freezeAt(newDate(2013, 12, 31)).thawAfter(new Snippet() {{
assertEquals("Jan 1 '14", getDateString(new DateTime(2014, 1, 1, 0, 0, 0)));
}});
}
@Test
public void testOneMonthFromStartOfDecember() {
DateTime now = new DateTime(2013, 12, 1, 12, 19, 45, 192);
final long expected = new DateTime(2014, 1, 1, 12, 19, 45, 192).getMillis();
freezeAt(now).thawAfter(new Snippet() {{
assertEquals(expected, oneMonthFromNow());
}});
}
@Test
public void testOneMonthFromEndOfDecember() {
DateTime now = new DateTime(2013, 12, 31, 16, 31, 20, 597);
final long expected = new DateTime(2014, 1, 31, 16, 31, 20, 597).getMillis();
freezeAt(now).thawAfter(new Snippet() {{
assertEquals(expected, oneMonthFromNow());
}});
}
@Test
public void testGetSixMonthsFromEndOfDecember() {
final DateTime now = new DateTime(2013, 12, 31, 17, 17, 32, 900);
final long expected = new DateTime(2014, 7, 1, 17, 17, 32, 900).getMillis();
freezeAt(now).thawAfter(new Snippet() {{
assertEquals(expected, addCalendarMonthsToUnixtime(now.getMillis(), 6));
}});
}
@Test
public void testOneMonthFromEndOfJanuary() {
DateTime now = new DateTime(2014, 1, 31, 12, 54, 33, 175);
final long expected = new DateTime(2014, 3, 3, 12, 54, 33, 175).getMillis();
freezeAt(now).thawAfter(new Snippet() {{
assertEquals(expected, oneMonthFromNow());
}});
}
@Test
public void testOneMonthFromEndOfFebruary() {
DateTime now = new DateTime(2014, 2, 28, 9, 19, 7, 990);
final long expected = new DateTime(2014, 3, 28, 9, 19, 7, 990).getMillis();
freezeAt(now).thawAfter(new Snippet() {{
assertEquals(expected, oneMonthFromNow());
}});
}
@Test
public void testShouldGetStartOfDay() {
DateTime now = new DateTime(2014, 1, 3, 10, 41, 41, 520);
assertEquals(
now.startOfDay().getMillis(),
getStartOfDay(now.getMillis()));
}
@Test
public void testGetWeekdayLongString() {
assertEquals("Sunday", getWeekday(newDate(2013, 12, 29)));
assertEquals("Monday", getWeekday(newDate(2013, 12, 30)));
assertEquals("Tuesday", getWeekday(newDate(2013, 12, 31)));
assertEquals("Wednesday", getWeekday(newDate(2014, 1, 1)));
assertEquals("Thursday", getWeekday(newDate(2014, 1, 2)));
assertEquals("Friday", getWeekday(newDate(2014, 1, 3)));
assertEquals("Saturday", getWeekday(newDate(2014, 1, 4)));
}
@Test
public void testGetWeekdayShortString() {
assertEquals("Sun", getWeekdayShort(newDate(2013, 12, 29)));
assertEquals("Mon", getWeekdayShort(newDate(2013, 12, 30)));
assertEquals("Tue", getWeekdayShort(newDate(2013, 12, 31)));
assertEquals("Wed", getWeekdayShort(newDate(2014, 1, 1)));
assertEquals("Thu", getWeekdayShort(newDate(2014, 1, 2)));
assertEquals("Fri", getWeekdayShort(newDate(2014, 1, 3)));
assertEquals("Sat", getWeekdayShort(newDate(2014, 1, 4)));
}
@Test
public void testAddMonthsToTimestamp() {
assertEquals(newDate(2014, 1, 1).getMillis(), addCalendarMonthsToUnixtime(newDate(2013, 12, 1).getMillis(), 1));
assertEquals(newDate(2014, 12, 31).getMillis(), addCalendarMonthsToUnixtime(newDate(2013, 12, 31).getMillis(), 12));
}
@Test
public void testAddMonthsWithLessDays() {
assertEquals(newDate(2014, 3, 3).getMillis(), addCalendarMonthsToUnixtime(newDate(2013, 12, 31).getMillis(), 2));
}
@Test
public void testAddMonthsWithMoreDays() {
assertEquals(newDate(2014, 1, 30).getMillis(), addCalendarMonthsToUnixtime(newDate(2013, 11, 30).getMillis(), 2));
}
@Test
public void usDateNoYear() {
setLocale(Locale.US);
freezeAt(new DateTime(2018, 1, 1)).thawAfter(() ->
assertEquals("Jan 14", DateUtilities.getRelativeDateStringWithTime(getTargetContext(), new DateTime(2018, 1, 14).getMillis())));
}
@Test
public void usDateWithYear() {
setLocale(Locale.US);
freezeAt(new DateTime(2017, 12, 12)).thawAfter(() ->
assertEquals("Jan 14 '18", DateUtilities.getRelativeDateStringWithTime(getTargetContext(), new DateTime(2018, 1, 14).getMillis())));
}
@Test
public void germanDateNoYear() {
setLocale(Locale.GERMAN);
freezeAt(new DateTime(2018, 1, 1)).thawAfter(() ->
assertEquals("14 Jan.", DateUtilities.getRelativeDateStringWithTime(getTargetContext(), new DateTime(2018, 1, 14).getMillis())));
}
@Test
public void germanDateWithYear() {
setLocale(Locale.GERMAN);
freezeAt(new DateTime(2017, 12, 12)).thawAfter(() ->
assertEquals("14 Jan. '18", DateUtilities.getRelativeDateStringWithTime(getTargetContext(), new DateTime(2018, 1, 14).getMillis())));
}
@Test
public void koreanDateNoYear() {
setLocale(Locale.KOREAN);
freezeAt(new DateTime(2018, 1, 1)).thawAfter(() ->
assertEquals("1월 14일", DateUtilities.getRelativeDateStringWithTime(getTargetContext(), new DateTime(2018, 1, 14).getMillis())));
}
@Test
public void koreanDateWithYear() {
setLocale(Locale.KOREAN);
freezeAt(new DateTime(2017, 12, 12)).thawAfter(() ->
assertEquals("18년 1월 14일", DateUtilities.getRelativeDateStringWithTime(getTargetContext(), new DateTime(2018, 1, 14).getMillis())));
}
@Test
public void japaneseDateNoYear() {
setLocale(Locale.JAPANESE);
freezeAt(new DateTime(2018, 1, 1)).thawAfter(() ->
assertEquals("1月 14日", DateUtilities.getRelativeDateStringWithTime(getTargetContext(), new DateTime(2018, 1, 14).getMillis())));
}
@Test
public void japaneseDateWithYear() {
setLocale(Locale.JAPANESE);
freezeAt(new DateTime(2017, 12, 12)).thawAfter(() ->
assertEquals("18年 1月 14日", DateUtilities.getRelativeDateStringWithTime(getTargetContext(), new DateTime(2018, 1, 14).getMillis())));
}
@Test
public void chineseDateNoYear() {
setLocale(Locale.CHINESE);
freezeAt(new DateTime(2018, 1, 1)).thawAfter(() ->
assertEquals("1月 14日", DateUtilities.getRelativeDateStringWithTime(getTargetContext(), new DateTime(2018, 1, 14).getMillis())));
}
@Test
public void chineseDateWithYear() {
setLocale(Locale.CHINESE);
freezeAt(new DateTime(2017, 12, 12)).thawAfter(() ->
assertEquals("18年 1月 14日", DateUtilities.getRelativeDateStringWithTime(getTargetContext(), new DateTime(2018, 1, 14).getMillis())));
}
private static Locale defaultLocale;
@Before
public void setUp() {
defaultLocale = Locale.getDefault();
setLocale(Locale.US);
}
@After
public void tearDown() {
DateUtilities.is24HourOverride = null;
setLocale(defaultLocale);
}
private void setLocale(Locale locale) {
org.tasks.locale.Locale.setDefault(locale);
Configuration config = new Configuration();
config.locale = locale;
DisplayMetrics metrics = getTargetContext().getResources().getDisplayMetrics();
getTargetContext().getResources().updateConfiguration(config, metrics);
}
public void forEachLocale(Runnable r) {
Locale[] locales = Locale.getAvailableLocales();
for (Locale locale : locales) {
setLocale(locale);
r.run();
}
}
@Test
public void testTimeString() {
forEachLocale(() -> {
DateTime d = newDateTime();
DateUtilities.is24HourOverride = false;
for (int i = 0; i < 24; i++) {
d = d.withHourOfDay(i);
getTimeString(getTargetContext(), d);
}
DateUtilities.is24HourOverride = true;
for (int i = 0; i < 24; i++) {
d = d.withHourOfDay(i);
getTimeString(getTargetContext(), d);
}
});
}
@Test
public void testDateString() {
forEachLocale(() -> {
DateTime d = newDateTime();
for (int i = 0; i < 12; i++) {
d = d.withMonthOfYear(i);
getDateString(d);
}
});
}
@Test
public void testGet24HourTime() {
DateUtilities.is24HourOverride = true;
assertEquals("09:05", getTimeString(null, new DateTime(2014, 1, 4, 9, 5, 36)));
assertEquals("13:00", getTimeString(null, new DateTime(2014, 1, 4, 13, 0, 1)));
}
@Test
public void testGetTime() {
DateUtilities.is24HourOverride = false;
assertEquals("9:05 AM", getTimeString(null, new DateTime(2014, 1, 4, 9, 5, 36)));
assertEquals("1:05 PM", getTimeString(null, new DateTime(2014, 1, 4, 13, 5, 36)));
}
@Test
public void testGetTimeWithNoMinutes() {
DateUtilities.is24HourOverride = false;
assertEquals("1 PM", getTimeString(null, new DateTime(2014, 1, 4, 13, 0, 59))); // derp?
}
@Test
public void testGetDateStringWithYear() {
assertEquals("Jan 4 '14", getDateString(new DateTime(2014, 1, 4, 0, 0, 0)));
}
@Test
public void testGetDateStringHidingYear() {
freezeAt(newDate(2014, 1, 1)).thawAfter(new Snippet() {{
assertEquals("Jan 1", getDateString(newDateTime()));
}});
}
@Test
public void testGetDateStringWithDifferentYear() {
freezeAt(newDate(2013, 12, 31)).thawAfter(new Snippet() {{
assertEquals("Jan 1 '14", getDateString(new DateTime(2014, 1, 1, 0, 0, 0)));
}});
}
@Test
public void testOneMonthFromStartOfDecember() {
DateTime now = new DateTime(2013, 12, 1, 12, 19, 45, 192);
final long expected = new DateTime(2014, 1, 1, 12, 19, 45, 192).getMillis();
freezeAt(now).thawAfter(new Snippet() {{
assertEquals(expected, oneMonthFromNow());
}});
}
@Test
public void testOneMonthFromEndOfDecember() {
DateTime now = new DateTime(2013, 12, 31, 16, 31, 20, 597);
final long expected = new DateTime(2014, 1, 31, 16, 31, 20, 597).getMillis();
freezeAt(now).thawAfter(new Snippet() {{
assertEquals(expected, oneMonthFromNow());
}});
}
@Test
public void testGetSixMonthsFromEndOfDecember() {
final DateTime now = new DateTime(2013, 12, 31, 17, 17, 32, 900);
final long expected = new DateTime(2014, 7, 1, 17, 17, 32, 900).getMillis();
freezeAt(now).thawAfter(new Snippet() {{
assertEquals(expected, addCalendarMonthsToUnixtime(now.getMillis(), 6));
}});
}
@Test
public void testOneMonthFromEndOfJanuary() {
DateTime now = new DateTime(2014, 1, 31, 12, 54, 33, 175);
final long expected = new DateTime(2014, 3, 3, 12, 54, 33, 175).getMillis();
freezeAt(now).thawAfter(new Snippet() {{
assertEquals(expected, oneMonthFromNow());
}});
}
@Test
public void testOneMonthFromEndOfFebruary() {
DateTime now = new DateTime(2014, 2, 28, 9, 19, 7, 990);
final long expected = new DateTime(2014, 3, 28, 9, 19, 7, 990).getMillis();
freezeAt(now).thawAfter(new Snippet() {{
assertEquals(expected, oneMonthFromNow());
}});
}
@Test
public void testShouldGetStartOfDay() {
DateTime now = new DateTime(2014, 1, 3, 10, 41, 41, 520);
assertEquals(
now.startOfDay().getMillis(),
getStartOfDay(now.getMillis()));
}
@Test
public void testGetWeekdayLongString() {
assertEquals("Sunday", getWeekday(newDate(2013, 12, 29)));
assertEquals("Monday", getWeekday(newDate(2013, 12, 30)));
assertEquals("Tuesday", getWeekday(newDate(2013, 12, 31)));
assertEquals("Wednesday", getWeekday(newDate(2014, 1, 1)));
assertEquals("Thursday", getWeekday(newDate(2014, 1, 2)));
assertEquals("Friday", getWeekday(newDate(2014, 1, 3)));
assertEquals("Saturday", getWeekday(newDate(2014, 1, 4)));
}
@Test
public void testGetWeekdayShortString() {
assertEquals("Sun", getWeekdayShort(newDate(2013, 12, 29)));
assertEquals("Mon", getWeekdayShort(newDate(2013, 12, 30)));
assertEquals("Tue", getWeekdayShort(newDate(2013, 12, 31)));
assertEquals("Wed", getWeekdayShort(newDate(2014, 1, 1)));
assertEquals("Thu", getWeekdayShort(newDate(2014, 1, 2)));
assertEquals("Fri", getWeekdayShort(newDate(2014, 1, 3)));
assertEquals("Sat", getWeekdayShort(newDate(2014, 1, 4)));
}
@Test
public void testAddMonthsToTimestamp() {
assertEquals(newDate(2014, 1, 1).getMillis(),
addCalendarMonthsToUnixtime(newDate(2013, 12, 1).getMillis(), 1));
assertEquals(newDate(2014, 12, 31).getMillis(),
addCalendarMonthsToUnixtime(newDate(2013, 12, 31).getMillis(), 12));
}
@Test
public void testAddMonthsWithLessDays() {
assertEquals(newDate(2014, 3, 3).getMillis(),
addCalendarMonthsToUnixtime(newDate(2013, 12, 31).getMillis(), 2));
}
@Test
public void testAddMonthsWithMoreDays() {
assertEquals(newDate(2014, 1, 30).getMillis(),
addCalendarMonthsToUnixtime(newDate(2013, 11, 30).getMillis(), 2));
}
@Test
public void usDateNoYear() {
setLocale(Locale.US);
freezeAt(new DateTime(2018, 1, 1)).thawAfter(() ->
assertEquals("Jan 14", DateUtilities.getRelativeDateStringWithTime(getTargetContext(),
new DateTime(2018, 1, 14).getMillis())));
}
@Test
public void usDateWithYear() {
setLocale(Locale.US);
freezeAt(new DateTime(2017, 12, 12)).thawAfter(() ->
assertEquals("Jan 14 '18", DateUtilities.getRelativeDateStringWithTime(getTargetContext(),
new DateTime(2018, 1, 14).getMillis())));
}
@Test
public void germanDateNoYear() {
setLocale(Locale.GERMAN);
freezeAt(new DateTime(2018, 1, 1)).thawAfter(() ->
assertEquals("14 Jan.", DateUtilities.getRelativeDateStringWithTime(getTargetContext(),
new DateTime(2018, 1, 14).getMillis())));
}
@Test
public void germanDateWithYear() {
setLocale(Locale.GERMAN);
freezeAt(new DateTime(2017, 12, 12)).thawAfter(() ->
assertEquals("14 Jan. '18", DateUtilities.getRelativeDateStringWithTime(getTargetContext(),
new DateTime(2018, 1, 14).getMillis())));
}
@Test
public void koreanDateNoYear() {
setLocale(Locale.KOREAN);
freezeAt(new DateTime(2018, 1, 1)).thawAfter(() ->
assertEquals("1월 14일", DateUtilities.getRelativeDateStringWithTime(getTargetContext(),
new DateTime(2018, 1, 14).getMillis())));
}
@Test
public void koreanDateWithYear() {
setLocale(Locale.KOREAN);
freezeAt(new DateTime(2017, 12, 12)).thawAfter(() ->
assertEquals("18년 1월 14일", DateUtilities.getRelativeDateStringWithTime(getTargetContext(),
new DateTime(2018, 1, 14).getMillis())));
}
@Test
public void japaneseDateNoYear() {
setLocale(Locale.JAPANESE);
freezeAt(new DateTime(2018, 1, 1)).thawAfter(() ->
assertEquals("1月 14日", DateUtilities.getRelativeDateStringWithTime(getTargetContext(),
new DateTime(2018, 1, 14).getMillis())));
}
@Test
public void japaneseDateWithYear() {
setLocale(Locale.JAPANESE);
freezeAt(new DateTime(2017, 12, 12)).thawAfter(() ->
assertEquals("18年 1月 14日", DateUtilities.getRelativeDateStringWithTime(getTargetContext(),
new DateTime(2018, 1, 14).getMillis())));
}
@Test
public void chineseDateNoYear() {
setLocale(Locale.CHINESE);
freezeAt(new DateTime(2018, 1, 1)).thawAfter(() ->
assertEquals("1月 14日", DateUtilities.getRelativeDateStringWithTime(getTargetContext(),
new DateTime(2018, 1, 14).getMillis())));
}
@Test
public void chineseDateWithYear() {
setLocale(Locale.CHINESE);
freezeAt(new DateTime(2017, 12, 12)).thawAfter(() ->
assertEquals("18年 1月 14日", DateUtilities.getRelativeDateStringWithTime(getTargetContext(),
new DateTime(2018, 1, 14).getMillis())));
}
}

@ -1,79 +1,77 @@
package com.todoroo.andlib.utility;
import android.support.test.runner.AndroidJUnit4;
import static android.support.test.InstrumentationRegistry.getTargetContext;
import static com.todoroo.andlib.utility.DateUtilities.getRelativeDay;
import static junit.framework.Assert.assertEquals;
import static org.tasks.Freeze.freezeAt;
import static org.tasks.Freeze.thaw;
import android.support.test.runner.AndroidJUnit4;
import java.util.Locale;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.tasks.time.DateTime;
import java.util.Locale;
import static android.support.test.InstrumentationRegistry.getTargetContext;
import static com.todoroo.andlib.utility.DateUtilities.getRelativeDay;
import static junit.framework.Assert.assertEquals;
import static org.tasks.Freeze.freezeAt;
import static org.tasks.Freeze.thaw;
@RunWith(AndroidJUnit4.class)
public class RelativeDayTest {
private static Locale defaultLocale;
private static final DateTime now = new DateTime(2013, 12, 31, 11, 9, 42, 357);
@Before
public void setUp() {
defaultLocale = Locale.getDefault();
Locale.setDefault(Locale.US);
freezeAt(now);
}
@After
public void tearDown() {
Locale.setDefault(defaultLocale);
thaw();
}
@Test
public void testRelativeDayIsToday() {
checkRelativeDay(new DateTime(), "Today", "Today");
}
@Test
public void testRelativeDayIsTomorrow() {
checkRelativeDay(new DateTime().plusDays(1), "Tomorrow", "Tmrw");
}
@Test
public void testRelativeDayIsYesterday() {
checkRelativeDay(new DateTime().minusDays(1), "Yesterday", "Yest");
}
@Test
public void testRelativeDayTwo() {
checkRelativeDay(new DateTime().minusDays(2), "Sunday", "Sun");
checkRelativeDay(new DateTime().plusDays(2), "Thursday", "Thu");
}
@Test
public void testRelativeDaySix() {
checkRelativeDay(new DateTime().minusDays(6), "Wednesday", "Wed");
checkRelativeDay(new DateTime().plusDays(6), "Monday", "Mon");
}
@Test
public void testRelativeDayOneWeek() {
checkRelativeDay(new DateTime().minusDays(7), "Dec 24", "Dec 24");
}
@Test
public void testRelativeDayOneWeekNextYear() {
checkRelativeDay(new DateTime().plusDays(7), "Jan 7 '14", "Jan 7 '14");
}
private void checkRelativeDay(DateTime now, String full, String abbreviated) {
assertEquals(full, getRelativeDay(getTargetContext(), now.getMillis(), false));
assertEquals(abbreviated, getRelativeDay(getTargetContext(), now.getMillis(), true));
}
private static final DateTime now = new DateTime(2013, 12, 31, 11, 9, 42, 357);
private static Locale defaultLocale;
@Before
public void setUp() {
defaultLocale = Locale.getDefault();
Locale.setDefault(Locale.US);
freezeAt(now);
}
@After
public void tearDown() {
Locale.setDefault(defaultLocale);
thaw();
}
@Test
public void testRelativeDayIsToday() {
checkRelativeDay(new DateTime(), "Today", "Today");
}
@Test
public void testRelativeDayIsTomorrow() {
checkRelativeDay(new DateTime().plusDays(1), "Tomorrow", "Tmrw");
}
@Test
public void testRelativeDayIsYesterday() {
checkRelativeDay(new DateTime().minusDays(1), "Yesterday", "Yest");
}
@Test
public void testRelativeDayTwo() {
checkRelativeDay(new DateTime().minusDays(2), "Sunday", "Sun");
checkRelativeDay(new DateTime().plusDays(2), "Thursday", "Thu");
}
@Test
public void testRelativeDaySix() {
checkRelativeDay(new DateTime().minusDays(6), "Wednesday", "Wed");
checkRelativeDay(new DateTime().plusDays(6), "Monday", "Mon");
}
@Test
public void testRelativeDayOneWeek() {
checkRelativeDay(new DateTime().minusDays(7), "Dec 24", "Dec 24");
}
@Test
public void testRelativeDayOneWeekNextYear() {
checkRelativeDay(new DateTime().plusDays(7), "Jan 7 '14", "Jan 7 '14");
}
private void checkRelativeDay(DateTime now, String full, String abbreviated) {
assertEquals(full, getRelativeDay(getTargetContext(), now.getMillis(), false));
assertEquals(abbreviated, getRelativeDay(getTargetContext(), now.getMillis(), true));
}
}

@ -1,10 +1,16 @@
package com.todoroo.astrid.alarms;
import android.support.test.runner.AndroidJUnit4;
import static com.natpryce.makeiteasy.MakeItEasy.with;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.tasks.makers.TaskMaker.REMINDER_LAST;
import static org.tasks.makers.TaskMaker.newTask;
import android.support.test.runner.AndroidJUnit4;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.data.Task;
import javax.inject.Inject;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@ -18,68 +24,59 @@ import org.tasks.jobs.AlarmJob;
import org.tasks.jobs.JobQueue;
import org.tasks.time.DateTime;
import javax.inject.Inject;
import static com.natpryce.makeiteasy.MakeItEasy.with;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.tasks.makers.TaskMaker.REMINDER_LAST;
import static org.tasks.makers.TaskMaker.newTask;
@RunWith(AndroidJUnit4.class)
public class AlarmJobServiceTest extends InjectingTestCase {
@Inject AlarmDao alarmDao;
@Inject TaskDao taskDao;
@Inject AlarmDao alarmDao;
@Inject TaskDao taskDao;
private AlarmService alarmService;
private JobQueue jobs;
private AlarmService alarmService;
private JobQueue jobs;
@Before
public void before() {
jobs = mock(JobQueue.class);
alarmService = new AlarmService(alarmDao, jobs);
}
@Before
public void before() {
jobs = mock(JobQueue.class);
alarmService = new AlarmService(alarmDao, jobs);
}
@After
public void after() {
verifyNoMoreInteractions(jobs);
}
@After
public void after() {
verifyNoMoreInteractions(jobs);
}
@Test
public void scheduleAlarm() {
Task task = newTask();
@Test
public void scheduleAlarm() {
Task task = newTask();
taskDao.createNew(task);
DateTime alarmTime = new DateTime(2017, 9, 24, 19, 57);
taskDao.createNew(task);
DateTime alarmTime = new DateTime(2017, 9, 24, 19, 57);
Alarm alarm = new Alarm(task.getId(), alarmTime.getMillis());
alarm.setId(alarmDao.insert(alarm));
Alarm alarm = new Alarm(task.getId(), alarmTime.getMillis());
alarm.setId(alarmDao.insert(alarm));
alarmService.scheduleAllAlarms();
alarmService.scheduleAllAlarms();
InOrder order = inOrder(jobs);
order.verify(jobs).add(new AlarmJob(alarm));
}
InOrder order = inOrder(jobs);
order.verify(jobs).add(new AlarmJob(alarm));
}
@Test
public void ignoreStaleAlarm() {
DateTime alarmTime = new DateTime(2017, 9, 24, 19, 57);
@Test
public void ignoreStaleAlarm() {
DateTime alarmTime = new DateTime(2017, 9, 24, 19, 57);
Task task = newTask(with(REMINDER_LAST, alarmTime.endOfMinute()));
Task task = newTask(with(REMINDER_LAST, alarmTime.endOfMinute()));
taskDao.createNew(task);
taskDao.createNew(task);
alarmDao.insert(new Alarm(task.getId(), alarmTime.getMillis()));
alarmDao.insert(new Alarm(task.getId(), alarmTime.getMillis()));
alarmService.scheduleAllAlarms();
alarmService.scheduleAllAlarms();
verifyNoMoreInteractions(jobs);
}
verifyNoMoreInteractions(jobs);
}
@Override
protected void inject(TestComponent component) {
component.inject(this);
}
@Override
protected void inject(TestComponent component) {
component.inject(this);
}
}

@ -3,167 +3,163 @@
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.dao;
import android.support.test.runner.AndroidJUnit4;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotSame;
import static junit.framework.Assert.assertNull;
import com.todoroo.andlib.sql.Query;
import android.support.test.runner.AndroidJUnit4;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.dao.TaskDao.TaskCriteria;
import com.todoroo.astrid.data.Task;
import javax.inject.Inject;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.tasks.injection.InjectingTestCase;
import org.tasks.injection.TestComponent;
import javax.inject.Inject;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotSame;
import static junit.framework.Assert.assertNull;
@RunWith(AndroidJUnit4.class)
public class TaskDaoTests extends InjectingTestCase {
@Inject TaskDao taskDao;
/**
* Test basic task creation, fetch, and save
*/
@Test
public void testTaskCreation() {
assertEquals(0, taskDao.getAll().size());
// create task "happy"
Task task = new Task();
task.setTitle("happy");
taskDao.createNew(task);
assertEquals(1, taskDao.getAll().size());
long happyId = task.getId();
assertNotSame(Task.NO_ID, happyId);
task = taskDao.fetch(happyId);
assertEquals("happy", task.getTitle());
// create task "sad"
task = new Task();
task.setTitle("sad");
taskDao.createNew(task);
assertEquals(2, taskDao.getAll().size());
// rename sad to melancholy
long sadId = task.getId();
assertNotSame(Task.NO_ID, sadId);
task.setTitle("melancholy");
taskDao.save(task);
assertEquals(2, taskDao.getAll().size());
// check state
task = taskDao.fetch(happyId);
assertEquals("happy", task.getTitle());
task = taskDao.fetch(sadId);
assertEquals("melancholy", task.getTitle());
}
/**
* Test various task fetch conditions
*/
@Test
public void testTaskConditions() {
// create normal task
Task task = new Task();
task.setTitle("normal");
taskDao.createNew(task);
// create blank task
task = new Task();
task.setTitle("");
taskDao.createNew(task);
// create hidden task
task = new Task();
task.setTitle("hidden");
task.setHideUntil(DateUtilities.now() + 10000);
taskDao.createNew(task);
// create task with deadlines
task = new Task();
task.setTitle("deadlineInFuture");
task.setDueDate(DateUtilities.now() + 10000);
taskDao.createNew(task);
task = new Task();
task.setTitle("deadlineInPast");
task.setDueDate(DateUtilities.now() - 10000);
taskDao.createNew(task);
// create completed task
task = new Task();
task.setTitle("completed");
task.setCompletionDate(DateUtilities.now() - 10000);
taskDao.createNew(task);
// check is active
assertEquals(5, taskDao.getActiveTasks().size());
// check is visible
assertEquals(5, taskDao.getVisibleTasks().size());
}
/**
* Test task deletion
*/
@Test
public void testTDeletion() {
assertEquals(0, taskDao.getAll().size());
// create task "happy"
Task task = new Task();
task.setTitle("happy");
taskDao.createNew(task);
assertEquals(1, taskDao.getAll().size());
// delete
long happyId = task.getId();
assertEquals(1, taskDao.deleteById(happyId));
assertEquals(0, taskDao.getAll().size());
}
/**
* Test save without prior create doesn't work
*/
@Test
public void testSaveWithoutCreate() {
// try to save task "happy"
Task task = new Task();
task.setTitle("happy");
task.setId(1L);
taskDao.save(task);
assertEquals(0, taskDao.getAll().size());
}
/**
* Test passing invalid task indices to various things
*/
@Test
public void testInvalidIndex() {
assertEquals(0, taskDao.getAll().size());
assertNull(taskDao.fetch(1));
assertEquals(0, taskDao.deleteById(1));
// make sure db still works
assertEquals(0, taskDao.getAll().size());
}
@Override
protected void inject(TestComponent component) {
component.inject(this);
}
// TODO check eventing
@Inject TaskDao taskDao;
/**
* Test basic task creation, fetch, and save
*/
@Test
public void testTaskCreation() {
assertEquals(0, taskDao.getAll().size());
// create task "happy"
Task task = new Task();
task.setTitle("happy");
taskDao.createNew(task);
assertEquals(1, taskDao.getAll().size());
long happyId = task.getId();
assertNotSame(Task.NO_ID, happyId);
task = taskDao.fetch(happyId);
assertEquals("happy", task.getTitle());
// create task "sad"
task = new Task();
task.setTitle("sad");
taskDao.createNew(task);
assertEquals(2, taskDao.getAll().size());
// rename sad to melancholy
long sadId = task.getId();
assertNotSame(Task.NO_ID, sadId);
task.setTitle("melancholy");
taskDao.save(task);
assertEquals(2, taskDao.getAll().size());
// check state
task = taskDao.fetch(happyId);
assertEquals("happy", task.getTitle());
task = taskDao.fetch(sadId);
assertEquals("melancholy", task.getTitle());
}
/**
* Test various task fetch conditions
*/
@Test
public void testTaskConditions() {
// create normal task
Task task = new Task();
task.setTitle("normal");
taskDao.createNew(task);
// create blank task
task = new Task();
task.setTitle("");
taskDao.createNew(task);
// create hidden task
task = new Task();
task.setTitle("hidden");
task.setHideUntil(DateUtilities.now() + 10000);
taskDao.createNew(task);
// create task with deadlines
task = new Task();
task.setTitle("deadlineInFuture");
task.setDueDate(DateUtilities.now() + 10000);
taskDao.createNew(task);
task = new Task();
task.setTitle("deadlineInPast");
task.setDueDate(DateUtilities.now() - 10000);
taskDao.createNew(task);
// create completed task
task = new Task();
task.setTitle("completed");
task.setCompletionDate(DateUtilities.now() - 10000);
taskDao.createNew(task);
// check is active
assertEquals(5, taskDao.getActiveTasks().size());
// check is visible
assertEquals(5, taskDao.getVisibleTasks().size());
}
/**
* Test task deletion
*/
@Test
public void testTDeletion() {
assertEquals(0, taskDao.getAll().size());
// create task "happy"
Task task = new Task();
task.setTitle("happy");
taskDao.createNew(task);
assertEquals(1, taskDao.getAll().size());
// delete
long happyId = task.getId();
assertEquals(1, taskDao.deleteById(happyId));
assertEquals(0, taskDao.getAll().size());
}
/**
* Test save without prior create doesn't work
*/
@Test
public void testSaveWithoutCreate() {
// try to save task "happy"
Task task = new Task();
task.setTitle("happy");
task.setId(1L);
taskDao.save(task);
assertEquals(0, taskDao.getAll().size());
}
/**
* Test passing invalid task indices to various things
*/
@Test
public void testInvalidIndex() {
assertEquals(0, taskDao.getAll().size());
assertNull(taskDao.fetch(1));
assertEquals(0, taskDao.deleteById(1));
// make sure db still works
assertEquals(0, taskDao.getAll().size());
}
@Override
protected void inject(TestComponent component) {
component.inject(this);
}
// TODO check eventing
}

@ -1,17 +1,5 @@
package com.todoroo.astrid.data;
import android.support.test.runner.AndroidJUnit4;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.tasks.Snippet;
import org.tasks.time.DateTime;
import java.util.ArrayList;
import java.util.TreeSet;
import static com.todoroo.astrid.data.Task.URGENCY_DAY_AFTER;
import static com.todoroo.astrid.data.Task.URGENCY_IN_TWO_WEEKS;
import static com.todoroo.astrid.data.Task.URGENCY_NEXT_MONTH;
@ -31,247 +19,257 @@ import static org.tasks.Freeze.thaw;
import static org.tasks.date.DateTimeUtils.newDateTime;
import static org.tasks.time.DateTimeUtils.currentTimeMillis;
import android.support.test.runner.AndroidJUnit4;
import java.util.ArrayList;
import java.util.TreeSet;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.tasks.Snippet;
import org.tasks.time.DateTime;
@RunWith(AndroidJUnit4.class)
public class TaskTest {
private static final DateTime now = new DateTime(2013, 12, 31, 16, 10, 53, 452);
private static final DateTime specificDueDate = new DateTime(2014, 3, 17, 9, 54, 27, 959);
@Before
public void setUp() {
freezeAt(now);
}
@After
public void tearDown() {
thaw();
}
@Test
public void testCreateDueDateNoUrgency() {
assertEquals(0, createDueDate(URGENCY_NONE, 1L));
}
@Test
public void testCreateDueDateToday() {
long expected = new DateTime(2013, 12, 31, 12, 0, 0, 0).getMillis();
assertEquals(expected, createDueDate(URGENCY_TODAY, -1L));
}
@Test
public void testCreateDueDateTomorrow() {
long expected = new DateTime(2014, 1, 1, 12, 0, 0, 0).getMillis();
assertEquals(expected, createDueDate(URGENCY_TOMORROW, -1L));
}
@Test
public void testCreateDueDateDayAfter() {
long expected = new DateTime(2014, 1, 2, 12, 0, 0, 0).getMillis();
assertEquals(expected, createDueDate(URGENCY_DAY_AFTER, -1L));
}
@Test
public void testCreateDueDateNextWeek() {
long expected = new DateTime(2014, 1, 7, 12, 0, 0, 0).getMillis();
assertEquals(expected, createDueDate(URGENCY_NEXT_WEEK, -1L));
}
@Test
public void testCreateDueDateInTwoWeeks() {
long expected = new DateTime(2014, 1, 14, 12, 0, 0, 0).getMillis();
assertEquals(expected, createDueDate(URGENCY_IN_TWO_WEEKS, -1L));
}
@Test
public void testCreateDueDateNextMonth() {
long expected = new DateTime(2014, 1, 31, 12, 0, 0, 0).getMillis();
assertEquals(expected, createDueDate(URGENCY_NEXT_MONTH, -1L));
}
@Test
public void testRemoveTimeForSpecificDay() {
long expected = specificDueDate
.withHourOfDay(12)
.withMinuteOfHour(0)
.withSecondOfMinute(0)
.withMillisOfSecond(0)
.getMillis();
assertEquals(expected, createDueDate(URGENCY_SPECIFIC_DAY, specificDueDate.getMillis()));
}
@Test
public void testRemoveSecondsForSpecificTime() {
long expected = specificDueDate
.withSecondOfMinute(1)
.withMillisOfSecond(0)
.getMillis();
assertEquals(expected, createDueDate(URGENCY_SPECIFIC_DAY_TIME, specificDueDate.getMillis()));
}
@Test
public void testTaskHasDueTime() {
Task task = new Task();
task.setDueDate(1388516076000L);
assertTrue(task.hasDueTime());
assertTrue(task.hasDueDate());
}
@Test
public void testTaskHasDueDate() {
Task task = new Task();
task.setDueDate(1388469600000L);
assertFalse(task.hasDueTime());
assertTrue(task.hasDueDate());
}
@Test
public void testDoesHaveDueTime() {
assertTrue(hasDueTime(1388516076000L));
}
@Test
public void testNoDueTime() {
assertFalse(hasDueTime(newDateTime().startOfDay().getMillis()));
assertFalse(hasDueTime(newDateTime().withMillisOfDay(60000).getMillis()));
}
@Test
public void testHasDueTime() {
assertTrue(hasDueTime(newDateTime().withMillisOfDay(1).getMillis()));
assertTrue(hasDueTime(newDateTime().withMillisOfDay(1000).getMillis()));
assertTrue(hasDueTime(newDateTime().withMillisOfDay(59999).getMillis()));
}
@Test
public void testDoesNotHaveDueTime() {
assertFalse(hasDueTime(1388469600000L));
}
@Test
public void testNewTaskIsNotCompleted() {
assertFalse(new Task().isCompleted());
}
@Test
public void testNewTaskNotDeleted() {
assertFalse(new Task().isDeleted());
}
@Test
public void testNewTaskNotHidden() {
assertFalse(new Task().isHidden());
}
@Test
public void testNewTaskDoesNotHaveDueDateOrTime() {
assertFalse(new Task().hasDueDate());
assertFalse(new Task().hasDueTime());
}
@Test
public void testTaskIsCompleted() {
Task task = new Task();
task.setCompletionDate(1L);
assertTrue(task.isCompleted());
}
@Test
public void testTaskIsNotHiddenAtHideUntilTime() {
final long now = currentTimeMillis();
freezeAt(now).thawAfter(new Snippet() {{
Task task = new Task();
task.setHideUntil(now);
assertFalse(task.isHidden());
}});
}
@Test
public void testTaskIsHiddenBeforeHideUntilTime() {
final long now = currentTimeMillis();
freezeAt(now).thawAfter(new Snippet() {{
Task task = new Task();
task.setHideUntil(now + 1);
assertTrue(task.isHidden());
}});
}
@Test
public void testTaskIsDeleted() {
Task task = new Task();
task.setDeletionDate(1L);
assertTrue(task.isDeleted());
}
@Test
public void testTaskWithNoDueDateIsOverdue() {
assertTrue(new Task().isOverdue());
}
@Test
public void testTaskNotOverdueAtDueTime() {
final long now = currentTimeMillis();
freezeAt(now).thawAfter(new Snippet() {{
Task task = new Task();
task.setDueDate(now);
assertFalse(task.isOverdue());
}});
}
@Test
public void testTaskIsOverduePastDueTime() {
final long dueDate = currentTimeMillis();
freezeAt(dueDate + 1).thawAfter(new Snippet() {{
Task task = new Task();
task.setDueDate(dueDate);
assertTrue(task.isOverdue());
}});
}
@Test
public void testTaskNotOverdueBeforeNoonOnDueDate() {
final DateTime dueDate = new DateTime().startOfDay();
freezeAt(dueDate.plusHours(12).minusMillis(1)).thawAfter(new Snippet() {{
Task task = new Task();
task.setDueDate(dueDate.getMillis());
assertFalse(task.hasDueTime());
assertFalse(task.isOverdue());
}});
}
@Test
public void testTaskOverdueAtNoonOnDueDate() {
final DateTime dueDate = new DateTime().startOfDay();
freezeAt(dueDate.plusHours(12)).thawAfter(new Snippet() {{
Task task = new Task();
task.setDueDate(dueDate.getMillis());
assertFalse(task.hasDueTime());
assertFalse(task.isOverdue());
}});
}
@Test
public void testTaskWithNoDueTimeIsOverdue() {
final DateTime dueDate = new DateTime().startOfDay();
freezeAt(dueDate.plusDays(1)).thawAfter(new Snippet() {{
Task task = new Task();
task.setDueDate(dueDate.getMillis());
assertFalse(task.hasDueTime());
assertTrue(task.isOverdue());
}});
}
@Test
public void testSanity() {
assertTrue(Task.IMPORTANCE_DO_OR_DIE < Task.IMPORTANCE_MUST_DO);
assertTrue(Task.IMPORTANCE_MUST_DO < Task.IMPORTANCE_SHOULD_DO);
assertTrue(Task.IMPORTANCE_SHOULD_DO < Task.IMPORTANCE_NONE);
ArrayList<Integer> reminderFlags = new ArrayList<>();
reminderFlags.add(Task.NOTIFY_AFTER_DEADLINE);
reminderFlags.add(Task.NOTIFY_AT_DEADLINE);
reminderFlags.add(Task.NOTIFY_MODE_NONSTOP);
// assert no duplicates
assertEquals(new TreeSet<>(reminderFlags).size(), reminderFlags.size());
}
private static final DateTime now = new DateTime(2013, 12, 31, 16, 10, 53, 452);
private static final DateTime specificDueDate = new DateTime(2014, 3, 17, 9, 54, 27, 959);
@Before
public void setUp() {
freezeAt(now);
}
@After
public void tearDown() {
thaw();
}
@Test
public void testCreateDueDateNoUrgency() {
assertEquals(0, createDueDate(URGENCY_NONE, 1L));
}
@Test
public void testCreateDueDateToday() {
long expected = new DateTime(2013, 12, 31, 12, 0, 0, 0).getMillis();
assertEquals(expected, createDueDate(URGENCY_TODAY, -1L));
}
@Test
public void testCreateDueDateTomorrow() {
long expected = new DateTime(2014, 1, 1, 12, 0, 0, 0).getMillis();
assertEquals(expected, createDueDate(URGENCY_TOMORROW, -1L));
}
@Test
public void testCreateDueDateDayAfter() {
long expected = new DateTime(2014, 1, 2, 12, 0, 0, 0).getMillis();
assertEquals(expected, createDueDate(URGENCY_DAY_AFTER, -1L));
}
@Test
public void testCreateDueDateNextWeek() {
long expected = new DateTime(2014, 1, 7, 12, 0, 0, 0).getMillis();
assertEquals(expected, createDueDate(URGENCY_NEXT_WEEK, -1L));
}
@Test
public void testCreateDueDateInTwoWeeks() {
long expected = new DateTime(2014, 1, 14, 12, 0, 0, 0).getMillis();
assertEquals(expected, createDueDate(URGENCY_IN_TWO_WEEKS, -1L));
}
@Test
public void testCreateDueDateNextMonth() {
long expected = new DateTime(2014, 1, 31, 12, 0, 0, 0).getMillis();
assertEquals(expected, createDueDate(URGENCY_NEXT_MONTH, -1L));
}
@Test
public void testRemoveTimeForSpecificDay() {
long expected = specificDueDate
.withHourOfDay(12)
.withMinuteOfHour(0)
.withSecondOfMinute(0)
.withMillisOfSecond(0)
.getMillis();
assertEquals(expected, createDueDate(URGENCY_SPECIFIC_DAY, specificDueDate.getMillis()));
}
@Test
public void testRemoveSecondsForSpecificTime() {
long expected = specificDueDate
.withSecondOfMinute(1)
.withMillisOfSecond(0)
.getMillis();
assertEquals(expected, createDueDate(URGENCY_SPECIFIC_DAY_TIME, specificDueDate.getMillis()));
}
@Test
public void testTaskHasDueTime() {
Task task = new Task();
task.setDueDate(1388516076000L);
assertTrue(task.hasDueTime());
assertTrue(task.hasDueDate());
}
@Test
public void testTaskHasDueDate() {
Task task = new Task();
task.setDueDate(1388469600000L);
assertFalse(task.hasDueTime());
assertTrue(task.hasDueDate());
}
@Test
public void testDoesHaveDueTime() {
assertTrue(hasDueTime(1388516076000L));
}
@Test
public void testNoDueTime() {
assertFalse(hasDueTime(newDateTime().startOfDay().getMillis()));
assertFalse(hasDueTime(newDateTime().withMillisOfDay(60000).getMillis()));
}
@Test
public void testHasDueTime() {
assertTrue(hasDueTime(newDateTime().withMillisOfDay(1).getMillis()));
assertTrue(hasDueTime(newDateTime().withMillisOfDay(1000).getMillis()));
assertTrue(hasDueTime(newDateTime().withMillisOfDay(59999).getMillis()));
}
@Test
public void testDoesNotHaveDueTime() {
assertFalse(hasDueTime(1388469600000L));
}
@Test
public void testNewTaskIsNotCompleted() {
assertFalse(new Task().isCompleted());
}
@Test
public void testNewTaskNotDeleted() {
assertFalse(new Task().isDeleted());
}
@Test
public void testNewTaskNotHidden() {
assertFalse(new Task().isHidden());
}
@Test
public void testNewTaskDoesNotHaveDueDateOrTime() {
assertFalse(new Task().hasDueDate());
assertFalse(new Task().hasDueTime());
}
@Test
public void testTaskIsCompleted() {
Task task = new Task();
task.setCompletionDate(1L);
assertTrue(task.isCompleted());
}
@Test
public void testTaskIsNotHiddenAtHideUntilTime() {
final long now = currentTimeMillis();
freezeAt(now).thawAfter(new Snippet() {{
Task task = new Task();
task.setHideUntil(now);
assertFalse(task.isHidden());
}});
}
@Test
public void testTaskIsHiddenBeforeHideUntilTime() {
final long now = currentTimeMillis();
freezeAt(now).thawAfter(new Snippet() {{
Task task = new Task();
task.setHideUntil(now + 1);
assertTrue(task.isHidden());
}});
}
@Test
public void testTaskIsDeleted() {
Task task = new Task();
task.setDeletionDate(1L);
assertTrue(task.isDeleted());
}
@Test
public void testTaskWithNoDueDateIsOverdue() {
assertTrue(new Task().isOverdue());
}
@Test
public void testTaskNotOverdueAtDueTime() {
final long now = currentTimeMillis();
freezeAt(now).thawAfter(new Snippet() {{
Task task = new Task();
task.setDueDate(now);
assertFalse(task.isOverdue());
}});
}
@Test
public void testTaskIsOverduePastDueTime() {
final long dueDate = currentTimeMillis();
freezeAt(dueDate + 1).thawAfter(new Snippet() {{
Task task = new Task();
task.setDueDate(dueDate);
assertTrue(task.isOverdue());
}});
}
@Test
public void testTaskNotOverdueBeforeNoonOnDueDate() {
final DateTime dueDate = new DateTime().startOfDay();
freezeAt(dueDate.plusHours(12).minusMillis(1)).thawAfter(new Snippet() {{
Task task = new Task();
task.setDueDate(dueDate.getMillis());
assertFalse(task.hasDueTime());
assertFalse(task.isOverdue());
}});
}
@Test
public void testTaskOverdueAtNoonOnDueDate() {
final DateTime dueDate = new DateTime().startOfDay();
freezeAt(dueDate.plusHours(12)).thawAfter(new Snippet() {{
Task task = new Task();
task.setDueDate(dueDate.getMillis());
assertFalse(task.hasDueTime());
assertFalse(task.isOverdue());
}});
}
@Test
public void testTaskWithNoDueTimeIsOverdue() {
final DateTime dueDate = new DateTime().startOfDay();
freezeAt(dueDate.plusDays(1)).thawAfter(new Snippet() {{
Task task = new Task();
task.setDueDate(dueDate.getMillis());
assertFalse(task.hasDueTime());
assertTrue(task.isOverdue());
}});
}
@Test
public void testSanity() {
assertTrue(Task.IMPORTANCE_DO_OR_DIE < Task.IMPORTANCE_MUST_DO);
assertTrue(Task.IMPORTANCE_MUST_DO < Task.IMPORTANCE_SHOULD_DO);
assertTrue(Task.IMPORTANCE_SHOULD_DO < Task.IMPORTANCE_NONE);
ArrayList<Integer> reminderFlags = new ArrayList<>();
reminderFlags.add(Task.NOTIFY_AFTER_DEADLINE);
reminderFlags.add(Task.NOTIFY_AT_DEADLINE);
reminderFlags.add(Task.NOTIFY_MODE_NONSTOP);
// assert no duplicates
assertEquals(new TreeSet<>(reminderFlags).size(), reminderFlags.size());
}
}

@ -1,46 +1,43 @@
package com.todoroo.astrid.model;
import android.support.test.runner.AndroidJUnit4;
import static junit.framework.Assert.assertEquals;
import static org.tasks.Freeze.freezeClock;
import static org.tasks.time.DateTimeUtils.currentTimeMillis;
import android.support.test.runner.AndroidJUnit4;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.data.Task;
import javax.inject.Inject;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.tasks.Snippet;
import org.tasks.injection.InjectingTestCase;
import org.tasks.injection.TestComponent;
import javax.inject.Inject;
import static junit.framework.Assert.assertEquals;
import static org.tasks.Freeze.freezeClock;
import static org.tasks.time.DateTimeUtils.currentTimeMillis;
@RunWith(AndroidJUnit4.class)
public class TaskTest extends InjectingTestCase {
@Inject TaskDao taskDao;
@Test
public void testSavedTaskHasCreationDate() {
freezeClock().thawAfter(new Snippet() {{
Task task = new Task();
taskDao.createNew(task);
assertEquals(currentTimeMillis(), (long) task.getCreationDate());
}});
}
@Test
public void testReadTaskFromDb() {
Task task = new Task();
taskDao.createNew(task);
final Task fromDb = taskDao.fetch(task.getId());
assertEquals(task, fromDb);
}
@Override
protected void inject(TestComponent component) {
component.inject(this);
}
@Inject TaskDao taskDao;
@Test
public void testSavedTaskHasCreationDate() {
freezeClock().thawAfter(new Snippet() {{
Task task = new Task();
taskDao.createNew(task);
assertEquals(currentTimeMillis(), (long) task.getCreationDate());
}});
}
@Test
public void testReadTaskFromDb() {
Task task = new Task();
taskDao.createNew(task);
final Task fromDb = taskDao.fetch(task.getId());
assertEquals(task, fromDb);
}
@Override
protected void inject(TestComponent component) {
component.inject(this);
}
}

@ -1,29 +1,5 @@
package com.todoroo.astrid.reminders;
import android.support.test.runner.AndroidJUnit4;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.data.Task;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InOrder;
import org.tasks.R;
import org.tasks.Snippet;
import org.tasks.injection.InjectingTestCase;
import org.tasks.injection.TestComponent;
import org.tasks.jobs.JobQueue;
import org.tasks.jobs.Reminder;
import org.tasks.preferences.Preferences;
import org.tasks.reminders.Random;
import org.tasks.time.DateTime;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import static com.natpryce.makeiteasy.MakeItEasy.with;
import static com.todoroo.andlib.utility.DateUtilities.ONE_HOUR;
import static com.todoroo.andlib.utility.DateUtilities.ONE_WEEK;
@ -48,323 +24,354 @@ import static org.tasks.makers.TaskMaker.REMINDER_LAST;
import static org.tasks.makers.TaskMaker.SNOOZE_TIME;
import static org.tasks.makers.TaskMaker.newTask;
import android.support.test.runner.AndroidJUnit4;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.data.Task;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InOrder;
import org.tasks.R;
import org.tasks.Snippet;
import org.tasks.injection.InjectingTestCase;
import org.tasks.injection.TestComponent;
import org.tasks.jobs.JobQueue;
import org.tasks.jobs.Reminder;
import org.tasks.preferences.Preferences;
import org.tasks.reminders.Random;
import org.tasks.time.DateTime;
@RunWith(AndroidJUnit4.class)
public class ReminderServiceTest extends InjectingTestCase {
@Inject Preferences preferences;
@Inject TaskDao taskDao;
private ReminderService service;
private Random random;
private JobQueue jobs;
@Before
public void before() {
jobs = mock(JobQueue.class);
random = mock(Random.class);
when(random.nextFloat()).thenReturn(1.0f);
preferences.reset();
service = new ReminderService(preferences, jobs, random, taskDao);
}
@After
public void after() {
verifyNoMoreInteractions(jobs);
}
@Override
protected void inject(TestComponent component) {
component.inject(this);
}
@Test
public void dontScheduleDueDateReminderWhenFlagNotSet() {
service.scheduleAlarm(newTask(with(ID, 1L), with(DUE_TIME, newDateTime())));
verify(jobs).cancelReminder(1);
}
@Test
public void dontScheduleDueDateReminderWhenTimeNotSet() {
service.scheduleAlarm(newTask(with(ID, 1L), with(REMINDERS, NOTIFY_AT_DEADLINE)));
verify(jobs).cancelReminder(1);
}
@Test
public void schedulePastDueDate() {
Task task = newTask(
with(ID, 1L),
with(DUE_TIME, newDateTime().minusDays(1)),
with(REMINDERS, NOTIFY_AT_DEADLINE));
service.scheduleAlarm(task);
InOrder order = inOrder(jobs);
order.verify(jobs).cancelReminder(1);
order.verify(jobs).add(new Reminder(1, task.getDueDate(), ReminderService.TYPE_DUE));
}
@Test
public void scheduleFutureDueDate() {
Task task = newTask(
with(ID, 1L),
with(DUE_TIME, newDateTime().plusDays(1)),
with(REMINDERS, NOTIFY_AT_DEADLINE));
service.scheduleAlarm(task);
InOrder order = inOrder(jobs);
order.verify(jobs).cancelReminder(1);
order.verify(jobs).add(new Reminder(1, task.getDueDate(), ReminderService.TYPE_DUE));
}
@Test
public void scheduleReminderAtDefaultDueTime() {
DateTime now = newDateTime();
Task task = newTask(
with(ID, 1L),
with(DUE_DATE, now),
with(REMINDERS, NOTIFY_AT_DEADLINE));
service.scheduleAlarm(task);
InOrder order = inOrder(jobs);
order.verify(jobs).cancelReminder(1);
order.verify(jobs).add(new Reminder(1, now.startOfDay().withHourOfDay(18).getMillis(), ReminderService.TYPE_DUE));
}
@Test
public void dontScheduleReminderForCompletedTask() {
Task task = newTask(
with(ID, 1L),
with(DUE_TIME, newDateTime().plusDays(1)),
with(COMPLETION_TIME, newDateTime()),
with(REMINDERS, NOTIFY_AT_DEADLINE));
service.scheduleAlarm(task);
verify(jobs).cancelReminder(1);
}
@Test
public void dontScheduleReminderForDeletedTask() {
Task task = newTask(
with(ID, 1L),
with(DUE_TIME, newDateTime().plusDays(1)),
with(DELETION_TIME, newDateTime()),
with(REMINDERS, NOTIFY_AT_DEADLINE));
service.scheduleAlarm(task);
verify(jobs).cancelReminder(1);
}
@Test
public void dontScheduleDueDateReminderWhenAlreadyReminded() {
DateTime now = newDateTime();
Task task = newTask(
with(ID, 1L),
with(DUE_TIME, now),
with(REMINDER_LAST, now.plusSeconds(1)),
with(REMINDERS, NOTIFY_AT_DEADLINE));
service.scheduleAlarm(task);
verify(jobs).cancelReminder(1);
}
@Test
public void ignoreStaleSnoozeTime() {
Task task = newTask(
with(ID, 1L),
with(DUE_TIME, newDateTime()),
with(SNOOZE_TIME, newDateTime().minusMinutes(5)),
with(REMINDER_LAST, newDateTime().minusMinutes(4)),
with(REMINDERS, NOTIFY_AT_DEADLINE));
service.scheduleAlarm(task);
InOrder order = inOrder(jobs);
order.verify(jobs).cancelReminder(1);
order.verify(jobs).add(new Reminder(1, task.getDueDate(), ReminderService.TYPE_DUE));
}
@Test
public void dontIgnoreMissedSnoozeTime() {
DateTime dueDate = newDateTime();
Task task = newTask(
with(ID, 1L),
with(DUE_TIME, dueDate),
with(SNOOZE_TIME, dueDate.minusMinutes(4)),
with(REMINDER_LAST, dueDate.minusMinutes(5)),
with(REMINDERS, NOTIFY_AT_DEADLINE));
service.scheduleAlarm(task);
InOrder order = inOrder(jobs);
order.verify(jobs).cancelReminder(1);
order.verify(jobs).add(new Reminder(1, task.getReminderSnooze(), ReminderService.TYPE_SNOOZE));
}
@Test
public void scheduleInitialRandomReminder() {
freezeClock().thawAfter(new Snippet() {{
DateTime now = newDateTime();
when(random.nextFloat()).thenReturn(0.3865f);
Task task = newTask(
with(ID, 1L),
with(REMINDER_LAST, (DateTime) null),
with(CREATION_TIME, now.minusDays(1)),
with(RANDOM_REMINDER_PERIOD, ONE_WEEK));
service.scheduleAlarm(task);
InOrder order = inOrder(jobs);
order.verify(jobs).cancelReminder(1);
order.verify(jobs).add(new Reminder(1L, now.minusDays(1).getMillis() + 584206592, ReminderService.TYPE_RANDOM));
}});
}
@Test
public void scheduleNextRandomReminder() {
freezeClock().thawAfter(new Snippet() {{
DateTime now = newDateTime();
when(random.nextFloat()).thenReturn(0.3865f);
Task task = newTask(
with(ID, 1L),
with(REMINDER_LAST, now.minusDays(1)),
with(CREATION_TIME, now.minusDays(30)),
with(RANDOM_REMINDER_PERIOD, ONE_WEEK));
service.scheduleAlarm(task);
InOrder order = inOrder(jobs);
order.verify(jobs).cancelReminder(1);
order.verify(jobs).add(new Reminder(1L, now.minusDays(1).getMillis() + 584206592, ReminderService.TYPE_RANDOM));
}});
}
@Test
public void scheduleOverdueRandomReminder() {
freezeClock().thawAfter(new Snippet() {{
DateTime now = newDateTime();
when(random.nextFloat()).thenReturn(0.3865f);
Task task = newTask(
with(ID, 1L),
with(REMINDER_LAST, now.minusDays(14)),
with(CREATION_TIME, now.minusDays(30)),
with(RANDOM_REMINDER_PERIOD, ONE_WEEK));
service.scheduleAlarm(task);
InOrder order = inOrder(jobs);
order.verify(jobs).cancelReminder(1);
order.verify(jobs).add(new Reminder(1L, now.getMillis() + 10148400, ReminderService.TYPE_RANDOM));
}});
}
@Test
public void scheduleOverdueNoLastReminder() {
Task task = newTask(
with(ID, 1L),
with(DUE_TIME, new DateTime(2017, 9, 22, 15, 30)),
with(REMINDER_LAST, (DateTime) null),
with(REMINDERS, NOTIFY_AFTER_DEADLINE));
service.scheduleAlarm(task);
InOrder order = inOrder(jobs);
order.verify(jobs).cancelReminder(1);
order.verify(jobs).add(new Reminder(1L, new DateTime(2017, 9, 23, 15, 30, 1, 0).getMillis(), ReminderService.TYPE_OVERDUE));
}
@Test
public void scheduleOverduePastLastReminder() {
Task task = newTask(
with(ID, 1L),
with(DUE_TIME, new DateTime(2017, 9, 22, 15, 30)),
with(REMINDER_LAST, new DateTime(2017, 9, 24, 12, 0)),
with(REMINDERS, NOTIFY_AFTER_DEADLINE));
service.scheduleAlarm(task);
InOrder order = inOrder(jobs);
order.verify(jobs).cancelReminder(1);
order.verify(jobs).add(new Reminder(1L, new DateTime(2017, 9, 24, 15, 30, 1, 0).getMillis(), ReminderService.TYPE_OVERDUE));
}
@Test
public void scheduleOverdueBeforeLastReminder() {
Task task = newTask(
with(ID, 1L),
with(DUE_TIME, new DateTime(2017, 9, 22, 12, 30)),
with(REMINDER_LAST, new DateTime(2017, 9, 24, 15, 0)),
with(REMINDERS, NOTIFY_AFTER_DEADLINE));
service.scheduleAlarm(task);
InOrder order = inOrder(jobs);
order.verify(jobs).cancelReminder(1);
order.verify(jobs).add(new Reminder(1L, new DateTime(2017, 9, 25, 12, 30, 1, 0).getMillis(), ReminderService.TYPE_OVERDUE));
}
@Test
public void scheduleOverdueWithNoDueTime() {
preferences.setInt(R.string.p_rmd_time, (int) TimeUnit.HOURS.toMillis(15));
Task task = newTask(
with(ID, 1L),
with(DUE_DATE, new DateTime(2017, 9, 22)),
with(REMINDER_LAST, new DateTime(2017, 9, 23, 12, 17, 59, 999)),
with(REMINDERS, NOTIFY_AFTER_DEADLINE));
service.scheduleAlarm(task);
InOrder order = inOrder(jobs);
order.verify(jobs).cancelReminder(1);
order.verify(jobs).add(new Reminder(1L, new DateTime(2017, 9, 23, 15, 0, 0, 0).getMillis(), ReminderService.TYPE_OVERDUE));
}
@Test
public void scheduleSubsequentOverdueReminder() {
Task task = newTask(
with(ID, 1L),
with(DUE_TIME, new DateTime(2017, 9, 22, 15, 30)),
with(REMINDER_LAST, new DateTime(2017, 9, 23, 15, 30, 59, 999)),
with(REMINDERS, NOTIFY_AFTER_DEADLINE));
service.scheduleAlarm(task);
InOrder order = inOrder(jobs);
order.verify(jobs).cancelReminder(1);
order.verify(jobs).add(new Reminder(1L, new DateTime(2017, 9, 24, 15, 30, 1, 0).getMillis(), ReminderService.TYPE_OVERDUE));
}
@Test
public void scheduleOverdueAfterLastReminder() {
Task task = newTask(
with(ID, 1L),
with(DUE_TIME, new DateTime(2017, 9, 22, 15, 30)),
with(REMINDER_LAST, new DateTime(2017, 9, 23, 12, 17, 59, 999)),
with(REMINDERS, NOTIFY_AFTER_DEADLINE));
service.scheduleAlarm(task);
InOrder order = inOrder(jobs);
order.verify(jobs).cancelReminder(1);
order.verify(jobs).add(new Reminder(1L, new DateTime(2017, 9, 23, 15, 30, 1, 0).getMillis(), ReminderService.TYPE_OVERDUE));
}
@Test
public void snoozeOverridesAll() {
DateTime now = newDateTime();
Task task = newTask(
with(ID, 1L),
with(DUE_TIME, now),
with(SNOOZE_TIME, now.plusMonths(12)),
with(REMINDERS, NOTIFY_AT_DEADLINE | NOTIFY_AFTER_DEADLINE),
with(RANDOM_REMINDER_PERIOD, ONE_HOUR));
service.scheduleAlarm(task);
InOrder order = inOrder(jobs);
order.verify(jobs).cancelReminder(1);
order.verify(jobs).add(new Reminder(1, now.plusMonths(12).getMillis(), ReminderService.TYPE_SNOOZE));
}
@Inject Preferences preferences;
@Inject TaskDao taskDao;
private ReminderService service;
private Random random;
private JobQueue jobs;
@Before
public void before() {
jobs = mock(JobQueue.class);
random = mock(Random.class);
when(random.nextFloat()).thenReturn(1.0f);
preferences.reset();
service = new ReminderService(preferences, jobs, random, taskDao);
}
@After
public void after() {
verifyNoMoreInteractions(jobs);
}
@Override
protected void inject(TestComponent component) {
component.inject(this);
}
@Test
public void dontScheduleDueDateReminderWhenFlagNotSet() {
service.scheduleAlarm(newTask(with(ID, 1L), with(DUE_TIME, newDateTime())));
verify(jobs).cancelReminder(1);
}
@Test
public void dontScheduleDueDateReminderWhenTimeNotSet() {
service.scheduleAlarm(newTask(with(ID, 1L), with(REMINDERS, NOTIFY_AT_DEADLINE)));
verify(jobs).cancelReminder(1);
}
@Test
public void schedulePastDueDate() {
Task task = newTask(
with(ID, 1L),
with(DUE_TIME, newDateTime().minusDays(1)),
with(REMINDERS, NOTIFY_AT_DEADLINE));
service.scheduleAlarm(task);
InOrder order = inOrder(jobs);
order.verify(jobs).cancelReminder(1);
order.verify(jobs).add(new Reminder(1, task.getDueDate(), ReminderService.TYPE_DUE));
}
@Test
public void scheduleFutureDueDate() {
Task task = newTask(
with(ID, 1L),
with(DUE_TIME, newDateTime().plusDays(1)),
with(REMINDERS, NOTIFY_AT_DEADLINE));
service.scheduleAlarm(task);
InOrder order = inOrder(jobs);
order.verify(jobs).cancelReminder(1);
order.verify(jobs).add(new Reminder(1, task.getDueDate(), ReminderService.TYPE_DUE));
}
@Test
public void scheduleReminderAtDefaultDueTime() {
DateTime now = newDateTime();
Task task = newTask(
with(ID, 1L),
with(DUE_DATE, now),
with(REMINDERS, NOTIFY_AT_DEADLINE));
service.scheduleAlarm(task);
InOrder order = inOrder(jobs);
order.verify(jobs).cancelReminder(1);
order.verify(jobs).add(
new Reminder(1, now.startOfDay().withHourOfDay(18).getMillis(), ReminderService.TYPE_DUE));
}
@Test
public void dontScheduleReminderForCompletedTask() {
Task task = newTask(
with(ID, 1L),
with(DUE_TIME, newDateTime().plusDays(1)),
with(COMPLETION_TIME, newDateTime()),
with(REMINDERS, NOTIFY_AT_DEADLINE));
service.scheduleAlarm(task);
verify(jobs).cancelReminder(1);
}
@Test
public void dontScheduleReminderForDeletedTask() {
Task task = newTask(
with(ID, 1L),
with(DUE_TIME, newDateTime().plusDays(1)),
with(DELETION_TIME, newDateTime()),
with(REMINDERS, NOTIFY_AT_DEADLINE));
service.scheduleAlarm(task);
verify(jobs).cancelReminder(1);
}
@Test
public void dontScheduleDueDateReminderWhenAlreadyReminded() {
DateTime now = newDateTime();
Task task = newTask(
with(ID, 1L),
with(DUE_TIME, now),
with(REMINDER_LAST, now.plusSeconds(1)),
with(REMINDERS, NOTIFY_AT_DEADLINE));
service.scheduleAlarm(task);
verify(jobs).cancelReminder(1);
}
@Test
public void ignoreStaleSnoozeTime() {
Task task = newTask(
with(ID, 1L),
with(DUE_TIME, newDateTime()),
with(SNOOZE_TIME, newDateTime().minusMinutes(5)),
with(REMINDER_LAST, newDateTime().minusMinutes(4)),
with(REMINDERS, NOTIFY_AT_DEADLINE));
service.scheduleAlarm(task);
InOrder order = inOrder(jobs);
order.verify(jobs).cancelReminder(1);
order.verify(jobs).add(new Reminder(1, task.getDueDate(), ReminderService.TYPE_DUE));
}
@Test
public void dontIgnoreMissedSnoozeTime() {
DateTime dueDate = newDateTime();
Task task = newTask(
with(ID, 1L),
with(DUE_TIME, dueDate),
with(SNOOZE_TIME, dueDate.minusMinutes(4)),
with(REMINDER_LAST, dueDate.minusMinutes(5)),
with(REMINDERS, NOTIFY_AT_DEADLINE));
service.scheduleAlarm(task);
InOrder order = inOrder(jobs);
order.verify(jobs).cancelReminder(1);
order.verify(jobs).add(new Reminder(1, task.getReminderSnooze(), ReminderService.TYPE_SNOOZE));
}
@Test
public void scheduleInitialRandomReminder() {
freezeClock().thawAfter(new Snippet() {{
DateTime now = newDateTime();
when(random.nextFloat()).thenReturn(0.3865f);
Task task = newTask(
with(ID, 1L),
with(REMINDER_LAST, (DateTime) null),
with(CREATION_TIME, now.minusDays(1)),
with(RANDOM_REMINDER_PERIOD, ONE_WEEK));
service.scheduleAlarm(task);
InOrder order = inOrder(jobs);
order.verify(jobs).cancelReminder(1);
order.verify(jobs).add(
new Reminder(1L, now.minusDays(1).getMillis() + 584206592, ReminderService.TYPE_RANDOM));
}});
}
@Test
public void scheduleNextRandomReminder() {
freezeClock().thawAfter(new Snippet() {{
DateTime now = newDateTime();
when(random.nextFloat()).thenReturn(0.3865f);
Task task = newTask(
with(ID, 1L),
with(REMINDER_LAST, now.minusDays(1)),
with(CREATION_TIME, now.minusDays(30)),
with(RANDOM_REMINDER_PERIOD, ONE_WEEK));
service.scheduleAlarm(task);
InOrder order = inOrder(jobs);
order.verify(jobs).cancelReminder(1);
order.verify(jobs).add(
new Reminder(1L, now.minusDays(1).getMillis() + 584206592, ReminderService.TYPE_RANDOM));
}});
}
@Test
public void scheduleOverdueRandomReminder() {
freezeClock().thawAfter(new Snippet() {{
DateTime now = newDateTime();
when(random.nextFloat()).thenReturn(0.3865f);
Task task = newTask(
with(ID, 1L),
with(REMINDER_LAST, now.minusDays(14)),
with(CREATION_TIME, now.minusDays(30)),
with(RANDOM_REMINDER_PERIOD, ONE_WEEK));
service.scheduleAlarm(task);
InOrder order = inOrder(jobs);
order.verify(jobs).cancelReminder(1);
order.verify(jobs)
.add(new Reminder(1L, now.getMillis() + 10148400, ReminderService.TYPE_RANDOM));
}});
}
@Test
public void scheduleOverdueNoLastReminder() {
Task task = newTask(
with(ID, 1L),
with(DUE_TIME, new DateTime(2017, 9, 22, 15, 30)),
with(REMINDER_LAST, (DateTime) null),
with(REMINDERS, NOTIFY_AFTER_DEADLINE));
service.scheduleAlarm(task);
InOrder order = inOrder(jobs);
order.verify(jobs).cancelReminder(1);
order.verify(jobs).add(new Reminder(1L, new DateTime(2017, 9, 23, 15, 30, 1, 0).getMillis(),
ReminderService.TYPE_OVERDUE));
}
@Test
public void scheduleOverduePastLastReminder() {
Task task = newTask(
with(ID, 1L),
with(DUE_TIME, new DateTime(2017, 9, 22, 15, 30)),
with(REMINDER_LAST, new DateTime(2017, 9, 24, 12, 0)),
with(REMINDERS, NOTIFY_AFTER_DEADLINE));
service.scheduleAlarm(task);
InOrder order = inOrder(jobs);
order.verify(jobs).cancelReminder(1);
order.verify(jobs).add(new Reminder(1L, new DateTime(2017, 9, 24, 15, 30, 1, 0).getMillis(),
ReminderService.TYPE_OVERDUE));
}
@Test
public void scheduleOverdueBeforeLastReminder() {
Task task = newTask(
with(ID, 1L),
with(DUE_TIME, new DateTime(2017, 9, 22, 12, 30)),
with(REMINDER_LAST, new DateTime(2017, 9, 24, 15, 0)),
with(REMINDERS, NOTIFY_AFTER_DEADLINE));
service.scheduleAlarm(task);
InOrder order = inOrder(jobs);
order.verify(jobs).cancelReminder(1);
order.verify(jobs).add(new Reminder(1L, new DateTime(2017, 9, 25, 12, 30, 1, 0).getMillis(),
ReminderService.TYPE_OVERDUE));
}
@Test
public void scheduleOverdueWithNoDueTime() {
preferences.setInt(R.string.p_rmd_time, (int) TimeUnit.HOURS.toMillis(15));
Task task = newTask(
with(ID, 1L),
with(DUE_DATE, new DateTime(2017, 9, 22)),
with(REMINDER_LAST, new DateTime(2017, 9, 23, 12, 17, 59, 999)),
with(REMINDERS, NOTIFY_AFTER_DEADLINE));
service.scheduleAlarm(task);
InOrder order = inOrder(jobs);
order.verify(jobs).cancelReminder(1);
order.verify(jobs).add(new Reminder(1L, new DateTime(2017, 9, 23, 15, 0, 0, 0).getMillis(),
ReminderService.TYPE_OVERDUE));
}
@Test
public void scheduleSubsequentOverdueReminder() {
Task task = newTask(
with(ID, 1L),
with(DUE_TIME, new DateTime(2017, 9, 22, 15, 30)),
with(REMINDER_LAST, new DateTime(2017, 9, 23, 15, 30, 59, 999)),
with(REMINDERS, NOTIFY_AFTER_DEADLINE));
service.scheduleAlarm(task);
InOrder order = inOrder(jobs);
order.verify(jobs).cancelReminder(1);
order.verify(jobs).add(new Reminder(1L, new DateTime(2017, 9, 24, 15, 30, 1, 0).getMillis(),
ReminderService.TYPE_OVERDUE));
}
@Test
public void scheduleOverdueAfterLastReminder() {
Task task = newTask(
with(ID, 1L),
with(DUE_TIME, new DateTime(2017, 9, 22, 15, 30)),
with(REMINDER_LAST, new DateTime(2017, 9, 23, 12, 17, 59, 999)),
with(REMINDERS, NOTIFY_AFTER_DEADLINE));
service.scheduleAlarm(task);
InOrder order = inOrder(jobs);
order.verify(jobs).cancelReminder(1);
order.verify(jobs).add(new Reminder(1L, new DateTime(2017, 9, 23, 15, 30, 1, 0).getMillis(),
ReminderService.TYPE_OVERDUE));
}
@Test
public void snoozeOverridesAll() {
DateTime now = newDateTime();
Task task = newTask(
with(ID, 1L),
with(DUE_TIME, now),
with(SNOOZE_TIME, now.plusMonths(12)),
with(REMINDERS, NOTIFY_AT_DEADLINE | NOTIFY_AFTER_DEADLINE),
with(RANDOM_REMINDER_PERIOD, ONE_HOUR));
service.scheduleAlarm(task);
InOrder order = inOrder(jobs);
order.verify(jobs).cancelReminder(1);
order.verify(jobs)
.add(new Reminder(1, now.plusMonths(12).getMillis(), ReminderService.TYPE_SNOOZE));
}
}

@ -3,268 +3,283 @@
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.repeats;
import android.support.test.runner.AndroidJUnit4;
import static junit.framework.Assert.assertEquals;
import static org.tasks.date.DateTimeUtils.newDateTime;
import android.support.test.runner.AndroidJUnit4;
import com.google.ical.values.Frequency;
import com.google.ical.values.RRule;
import com.google.ical.values.Weekday;
import com.google.ical.values.WeekdayNum;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.data.Task;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Calendar;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.tasks.time.DateTime;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Calendar;
import static junit.framework.Assert.assertEquals;
import static org.tasks.date.DateTimeUtils.newDateTime;
@RunWith(AndroidJUnit4.class)
public class AdvancedRepeatTest {
private static final int PREV_PREV = -2;
private static final int PREV = -1;
private static final int THIS = 1;
private static final int NEXT = 2;
private Task task;
private long nextDueDate;
private RRule rrule;
@Before
public void setUp() {
task = new Task();
task.setCompletionDate(DateUtilities.now());
rrule = new RRule();
}
// --- date with time tests
@Test
public void testDueDateSpecificTime() throws ParseException {
buildRRule(1, Frequency.DAILY);
// test specific day & time
long dayWithTime = Task.createDueDate(Task.URGENCY_SPECIFIC_DAY_TIME, new DateTime(2010, 8, 1, 10, 4, 0).getMillis());
task.setDueDate(dayWithTime);
long nextDayWithTime = dayWithTime + DateUtilities.ONE_DAY;
nextDueDate = RepeatTaskHelper.computeNextDueDate(task, rrule.toIcal(), false);
assertDateTimeEquals(nextDayWithTime, nextDueDate);
}
@Test
public void testCompletionDateSpecificTime() throws ParseException {
buildRRule(1, Frequency.DAILY);
// test specific day & time
long dayWithTime = Task.createDueDate(Task.URGENCY_SPECIFIC_DAY_TIME, new DateTime(2010, 8, 1, 10, 4, 0).getMillis());
task.setDueDate(dayWithTime);
DateTime todayWithTime = newDateTime()
.withHourOfDay(10)
.withMinuteOfHour(4)
.withSecondOfMinute(1);
long nextDayWithTimeLong = todayWithTime.getMillis();
nextDayWithTimeLong += DateUtilities.ONE_DAY;
nextDayWithTimeLong = nextDayWithTimeLong / 1000L * 1000;
nextDueDate = RepeatTaskHelper.computeNextDueDate(task, rrule.toIcal(), true);
assertDateTimeEquals(nextDayWithTimeLong, nextDueDate);
}
// --- due date tests
/** test multiple days per week - DUE DATE */
@Test
public void testDueDateInPastSingleWeekMultiDay() throws Exception {
buildRRule(1, Frequency.WEEKLY, Weekday.MO, Weekday.WE, Weekday.FR);
setTaskDueDate(THIS, Calendar.SUNDAY);
computeNextDueDate(false);
assertDueDate(nextDueDate, THIS, Calendar.MONDAY);
setTaskDueDate(THIS, Calendar.MONDAY);
computeNextDueDate(false);
assertDueDate(nextDueDate, THIS, Calendar.WEDNESDAY);
setTaskDueDate(THIS, Calendar.FRIDAY);
computeNextDueDate(false);
assertDueDate(nextDueDate, THIS, Calendar.MONDAY);
private static final int PREV_PREV = -2;
private static final int PREV = -1;
private static final int THIS = 1;
private static final int NEXT = 2;
private Task task;
private long nextDueDate;
private RRule rrule;
public static void assertDateTimeEquals(long date, long other) {
assertEquals("Expected: " + newDateTime(date) + ", Actual: " + newDateTime(other),
date, other);
}
// --- date with time tests
@Before
public void setUp() {
task = new Task();
task.setCompletionDate(DateUtilities.now());
rrule = new RRule();
}
@Test
public void testDueDateSpecificTime() throws ParseException {
buildRRule(1, Frequency.DAILY);
// test specific day & time
long dayWithTime = Task.createDueDate(Task.URGENCY_SPECIFIC_DAY_TIME,
new DateTime(2010, 8, 1, 10, 4, 0).getMillis());
task.setDueDate(dayWithTime);
long nextDayWithTime = dayWithTime + DateUtilities.ONE_DAY;
nextDueDate = RepeatTaskHelper.computeNextDueDate(task, rrule.toIcal(), false);
assertDateTimeEquals(nextDayWithTime, nextDueDate);
}
// --- due date tests
@Test
public void testCompletionDateSpecificTime() throws ParseException {
buildRRule(1, Frequency.DAILY);
// test specific day & time
long dayWithTime = Task.createDueDate(Task.URGENCY_SPECIFIC_DAY_TIME,
new DateTime(2010, 8, 1, 10, 4, 0).getMillis());
task.setDueDate(dayWithTime);
DateTime todayWithTime = newDateTime()
.withHourOfDay(10)
.withMinuteOfHour(4)
.withSecondOfMinute(1);
long nextDayWithTimeLong = todayWithTime.getMillis();
nextDayWithTimeLong += DateUtilities.ONE_DAY;
nextDayWithTimeLong = nextDayWithTimeLong / 1000L * 1000;
nextDueDate = RepeatTaskHelper.computeNextDueDate(task, rrule.toIcal(), true);
assertDateTimeEquals(nextDayWithTimeLong, nextDueDate);
}
/**
* test multiple days per week - DUE DATE
*/
@Test
public void testDueDateInPastSingleWeekMultiDay() throws Exception {
buildRRule(1, Frequency.WEEKLY, Weekday.MO, Weekday.WE, Weekday.FR);
setTaskDueDate(THIS, Calendar.SUNDAY);
computeNextDueDate(false);
assertDueDate(nextDueDate, THIS, Calendar.MONDAY);
setTaskDueDate(THIS, Calendar.MONDAY);
computeNextDueDate(false);
assertDueDate(nextDueDate, THIS, Calendar.WEDNESDAY);
setTaskDueDate(THIS, Calendar.FRIDAY);
computeNextDueDate(false);
assertDueDate(nextDueDate, THIS, Calendar.MONDAY);
}
/**
* test single day repeats - DUE DATE
*/
@Test
public void testDueDateSingleDay() throws Exception {
buildRRule(1, Frequency.WEEKLY, Weekday.MO);
setTaskDueDate(PREV_PREV, Calendar.MONDAY);
computeNextDueDate(false);
assertDueDate(nextDueDate, NEXT, Calendar.MONDAY);
setTaskDueDate(PREV_PREV, Calendar.FRIDAY);
computeNextDueDate(false);
assertDueDate(nextDueDate, THIS, Calendar.MONDAY);
setTaskDueDate(PREV, Calendar.MONDAY);
computeNextDueDate(false);
assertDueDate(nextDueDate, NEXT, Calendar.MONDAY);
setTaskDueDate(PREV, Calendar.FRIDAY);
computeNextDueDate(false);
assertDueDate(nextDueDate, THIS, Calendar.MONDAY);
setTaskDueDate(THIS, Calendar.SUNDAY);
computeNextDueDate(false);
assertDueDate(nextDueDate, THIS, Calendar.MONDAY);
setTaskDueDate(THIS, Calendar.MONDAY);
computeNextDueDate(false);
assertDueDate(nextDueDate, NEXT, Calendar.MONDAY);
}
/**
* test multiple days per week - DUE DATE
*/
@Test
public void testDueDateSingleWeekMultiDay() throws Exception {
buildRRule(1, Frequency.WEEKLY, Weekday.MO, Weekday.WE, Weekday.FR);
setTaskDueDate(THIS, Calendar.SUNDAY);
computeNextDueDate(false);
assertDueDate(nextDueDate, THIS, Calendar.MONDAY);
setTaskDueDate(THIS, Calendar.MONDAY);
computeNextDueDate(false);
assertDueDate(nextDueDate, THIS, Calendar.WEDNESDAY);
setTaskDueDate(THIS, Calendar.FRIDAY);
computeNextDueDate(false);
assertDueDate(nextDueDate, THIS, Calendar.MONDAY);
}
// --- completion tests
/**
* test multiple days per week, multiple intervals - DUE DATE
*/
@Test
public void testDueDateMultiWeekMultiDay() throws Exception {
buildRRule(2, Frequency.WEEKLY, Weekday.MO, Weekday.WE, Weekday.FR);
setTaskDueDate(THIS, Calendar.SUNDAY);
computeNextDueDate(false);
assertDueDate(nextDueDate, NEXT, Calendar.MONDAY);
setTaskDueDate(THIS, Calendar.MONDAY);
computeNextDueDate(false);
assertDueDate(nextDueDate, THIS, Calendar.WEDNESDAY);
setTaskDueDate(THIS, Calendar.FRIDAY);
computeNextDueDate(false);
assertDueDate(nextDueDate, NEXT, Calendar.MONDAY);
}
/**
* test multiple days per week - COMPLETE DATE
*/
@Test
public void testCompleteDateSingleWeek() throws Exception {
for (Weekday wday : Weekday.values()) {
buildRRule(1, Frequency.WEEKLY, wday);
computeNextDueDate(true);
long expected = getDate(DateUtilities.now() + DateUtilities.ONE_DAY, THIS, wday.javaDayNum);
nextDueDate = Task.createDueDate(Task.URGENCY_SPECIFIC_DAY, nextDueDate);
assertEquals(expected, nextDueDate);
}
/** test single day repeats - DUE DATE */
@Test
public void testDueDateSingleDay() throws Exception {
buildRRule(1, Frequency.WEEKLY, Weekday.MO);
setTaskDueDate(PREV_PREV, Calendar.MONDAY);
computeNextDueDate(false);
assertDueDate(nextDueDate, NEXT, Calendar.MONDAY);
setTaskDueDate(PREV_PREV, Calendar.FRIDAY);
computeNextDueDate(false);
assertDueDate(nextDueDate, THIS, Calendar.MONDAY);
setTaskDueDate(PREV, Calendar.MONDAY);
computeNextDueDate(false);
assertDueDate(nextDueDate, NEXT, Calendar.MONDAY);
setTaskDueDate(PREV, Calendar.FRIDAY);
computeNextDueDate(false);
assertDueDate(nextDueDate, THIS, Calendar.MONDAY);
setTaskDueDate(THIS, Calendar.SUNDAY);
computeNextDueDate(false);
assertDueDate(nextDueDate, THIS, Calendar.MONDAY);
setTaskDueDate(THIS, Calendar.MONDAY);
computeNextDueDate(false);
assertDueDate(nextDueDate, NEXT, Calendar.MONDAY);
}
/** test multiple days per week - DUE DATE */
@Test
public void testDueDateSingleWeekMultiDay() throws Exception {
buildRRule(1, Frequency.WEEKLY, Weekday.MO, Weekday.WE, Weekday.FR);
setTaskDueDate(THIS, Calendar.SUNDAY);
computeNextDueDate(false);
assertDueDate(nextDueDate, THIS, Calendar.MONDAY);
setTaskDueDate(THIS, Calendar.MONDAY);
computeNextDueDate(false);
assertDueDate(nextDueDate, THIS, Calendar.WEDNESDAY);
setTaskDueDate(THIS, Calendar.FRIDAY);
computeNextDueDate(false);
assertDueDate(nextDueDate, THIS, Calendar.MONDAY);
}
/** test multiple days per week, multiple intervals - DUE DATE */
@Test
public void testDueDateMultiWeekMultiDay() throws Exception {
buildRRule(2, Frequency.WEEKLY, Weekday.MO, Weekday.WE, Weekday.FR);
setTaskDueDate(THIS, Calendar.SUNDAY);
computeNextDueDate(false);
assertDueDate(nextDueDate, NEXT, Calendar.MONDAY);
setTaskDueDate(THIS, Calendar.MONDAY);
computeNextDueDate(false);
assertDueDate(nextDueDate, THIS, Calendar.WEDNESDAY);
setTaskDueDate(THIS, Calendar.FRIDAY);
computeNextDueDate(false);
assertDueDate(nextDueDate, NEXT, Calendar.MONDAY);
}
// --- completion tests
/** test multiple days per week - COMPLETE DATE */
@Test
public void testCompleteDateSingleWeek() throws Exception {
for(Weekday wday : Weekday.values()) {
buildRRule(1, Frequency.WEEKLY, wday);
computeNextDueDate(true);
long expected = getDate(DateUtilities.now() + DateUtilities.ONE_DAY, THIS, wday.javaDayNum);
nextDueDate = Task.createDueDate(Task.URGENCY_SPECIFIC_DAY, nextDueDate);
assertEquals(expected, nextDueDate);
for (Weekday wday1 : Weekday.values()) {
for (Weekday wday2 : Weekday.values()) {
if (wday1 == wday2) {
continue;
}
for(Weekday wday1 : Weekday.values()) {
for(Weekday wday2 : Weekday.values()) {
if(wday1 == wday2)
continue;
buildRRule(1, Frequency.WEEKLY, wday1, wday2);
long nextOne = getDate(DateUtilities.now() + DateUtilities.ONE_DAY, THIS, wday1.javaDayNum);
long nextTwo = getDate(DateUtilities.now() + DateUtilities.ONE_DAY, THIS, wday2.javaDayNum);
computeNextDueDate(true);
nextDueDate = Task.createDueDate(Task.URGENCY_SPECIFIC_DAY, nextDueDate);
assertEquals(Math.min(nextOne, nextTwo), nextDueDate);
}
}
buildRRule(1, Frequency.WEEKLY, wday1, wday2);
long nextOne = getDate(DateUtilities.now() + DateUtilities.ONE_DAY, THIS, wday1.javaDayNum);
long nextTwo = getDate(DateUtilities.now() + DateUtilities.ONE_DAY, THIS, wday2.javaDayNum);
computeNextDueDate(true);
nextDueDate = Task.createDueDate(Task.URGENCY_SPECIFIC_DAY, nextDueDate);
assertEquals(Math.min(nextOne, nextTwo), nextDueDate);
}
}
/** test multiple days per week, multiple intervals - COMPLETE DATE */
@Test
public void testCompleteDateMultiWeek() throws Exception {
for(Weekday wday : Weekday.values()) {
buildRRule(2, Frequency.WEEKLY, wday);
computeNextDueDate(true);
long expected = getDate(DateUtilities.now() + DateUtilities.ONE_DAY, NEXT, wday.javaDayNum);
nextDueDate = Task.createDueDate(Task.URGENCY_SPECIFIC_DAY, nextDueDate);
assertEquals(expected, nextDueDate);
}
for(Weekday wday1 : Weekday.values()) {
for(Weekday wday2 : Weekday.values()) {
if(wday1 == wday2)
continue;
buildRRule(2, Frequency.WEEKLY, wday1, wday2);
long nextOne = getDate(DateUtilities.now() + DateUtilities.ONE_DAY, NEXT, wday1.javaDayNum);
long nextTwo = getDate(DateUtilities.now() + DateUtilities.ONE_DAY, NEXT, wday2.javaDayNum);
computeNextDueDate(true);
nextDueDate = Task.createDueDate(Task.URGENCY_SPECIFIC_DAY, nextDueDate);
assertEquals(Math.min(nextOne, nextTwo), nextDueDate);
}
}
}
// --- helpers
/**
* test multiple days per week, multiple intervals - COMPLETE DATE
*/
@Test
public void testCompleteDateMultiWeek() throws Exception {
for (Weekday wday : Weekday.values()) {
buildRRule(2, Frequency.WEEKLY, wday);
computeNextDueDate(true);
long expected = getDate(DateUtilities.now() + DateUtilities.ONE_DAY, NEXT, wday.javaDayNum);
nextDueDate = Task.createDueDate(Task.URGENCY_SPECIFIC_DAY, nextDueDate);
assertEquals(expected, nextDueDate);
}
// --- helpers
private void computeNextDueDate(boolean fromComplete) throws ParseException{
nextDueDate = RepeatTaskHelper.computeNextDueDate(task, rrule.toIcal(), fromComplete);
}
private void buildRRule(int interval, Frequency freq, Weekday... weekdays) {
rrule.setInterval(interval);
rrule.setFreq(freq);
setRRuleDays(rrule, weekdays);
}
private void assertDueDate(long actual, int expectedWhich, int expectedDayOfWeek) {
long expected = getDate(task.getDueDate(), expectedWhich, expectedDayOfWeek);
assertEquals(expected, actual);
}
for (Weekday wday1 : Weekday.values()) {
for (Weekday wday2 : Weekday.values()) {
if (wday1 == wday2) {
continue;
}
public static void assertDateTimeEquals(long date, long other) {
assertEquals("Expected: " + newDateTime(date) + ", Actual: " + newDateTime(other),
date, other);
buildRRule(2, Frequency.WEEKLY, wday1, wday2);
long nextOne = getDate(DateUtilities.now() + DateUtilities.ONE_DAY, NEXT, wday1.javaDayNum);
long nextTwo = getDate(DateUtilities.now() + DateUtilities.ONE_DAY, NEXT, wday2.javaDayNum);
computeNextDueDate(true);
nextDueDate = Task.createDueDate(Task.URGENCY_SPECIFIC_DAY, nextDueDate);
assertEquals(Math.min(nextOne, nextTwo), nextDueDate);
}
}
private void setRRuleDays(RRule rrule, Weekday... weekdays) {
ArrayList<WeekdayNum> days = new ArrayList<>();
for(Weekday wd : weekdays)
days.add(new WeekdayNum(0, wd));
rrule.setByDay(days);
}
private void computeNextDueDate(boolean fromComplete) throws ParseException {
nextDueDate = RepeatTaskHelper.computeNextDueDate(task, rrule.toIcal(), fromComplete);
}
private void buildRRule(int interval, Frequency freq, Weekday... weekdays) {
rrule.setInterval(interval);
rrule.setFreq(freq);
setRRuleDays(rrule, weekdays);
}
private void assertDueDate(long actual, int expectedWhich, int expectedDayOfWeek) {
long expected = getDate(task.getDueDate(), expectedWhich, expectedDayOfWeek);
assertEquals(expected, actual);
}
private void setRRuleDays(RRule rrule, Weekday... weekdays) {
ArrayList<WeekdayNum> days = new ArrayList<>();
for (Weekday wd : weekdays) {
days.add(new WeekdayNum(0, wd));
}
rrule.setByDay(days);
}
private void setTaskDueDate(int which, int day) {
long time = getDate(DateUtilities.now(), which, day);
private void setTaskDueDate(int which, int day) {
long time = getDate(DateUtilities.now(), which, day);
task.setDueDate(time);
}
task.setDueDate(time);
}
private long getDate(long start, int which, int dayOfWeek) {
Calendar c = Calendar.getInstance();
c.setTimeInMillis(start);
int direction = which > 0 ? 1 : -1;
private long getDate(long start, int which, int dayOfWeek) {
Calendar c = Calendar.getInstance();
c.setTimeInMillis(start);
int direction = which > 0 ? 1 : -1;
while(c.get(Calendar.DAY_OF_WEEK) != dayOfWeek) {
c.add(Calendar.DAY_OF_MONTH, direction);
}
c.add(Calendar.DAY_OF_MONTH, (Math.abs(which) - 1) * direction * 7);
return Task.createDueDate(Task.URGENCY_SPECIFIC_DAY, c.getTimeInMillis());
while (c.get(Calendar.DAY_OF_WEEK) != dayOfWeek) {
c.add(Calendar.DAY_OF_MONTH, direction);
}
c.add(Calendar.DAY_OF_MONTH, (Math.abs(which) - 1) * direction * 7);
return Task.createDueDate(Task.URGENCY_SPECIFIC_DAY, c.getTimeInMillis());
}
}

@ -1,259 +1,266 @@
package com.todoroo.astrid.repeats;
import android.support.test.runner.AndroidJUnit4;
import static com.todoroo.astrid.repeats.RepeatTaskHelper.computeNextDueDate;
import static java.util.Arrays.asList;
import static junit.framework.Assert.assertEquals;
import android.support.test.runner.AndroidJUnit4;
import com.google.ical.values.Frequency;
import com.google.ical.values.RRule;
import com.google.ical.values.Weekday;
import com.google.ical.values.WeekdayNum;
import com.todoroo.astrid.data.Task;
import java.text.ParseException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.tasks.time.DateTime;
import java.text.ParseException;
import static com.todoroo.astrid.repeats.RepeatTaskHelper.computeNextDueDate;
import static java.util.Arrays.asList;
import static junit.framework.Assert.assertEquals;
@RunWith(AndroidJUnit4.class)
public class NewRepeatTests {
@Test
public void testRepeatMinutelyFromDueDate() throws ParseException {
DateTime dueDateTime = newDayTime(2016, 8, 26, 12, 30);
Task task = newFromDue(Frequency.MINUTELY, 1, dueDateTime);
assertEquals(newDayTime(2016, 8, 26, 12, 31), calculateNextDueDate(task));
}
@Test
public void testRepeatHourlyFromDueDate() throws ParseException {
DateTime dueDateTime = newDayTime(2016, 8, 26, 12, 30);
Task task = newFromDue(Frequency.HOURLY, 1, dueDateTime);
assertEquals(newDayTime(2016, 8, 26, 13, 30), calculateNextDueDate(task));
}
@Test
public void testRepeatDailyFromDueDate() throws ParseException {
DateTime dueDateTime = newDayTime(2016, 8, 26, 12, 30);
Task task = newFromDue(Frequency.DAILY, 1, dueDateTime);
assertEquals(newDayTime(2016, 8, 27, 12, 30), calculateNextDueDate(task));
}
@Test
public void testRepeatWeeklyFromDueDate() throws ParseException {
DateTime dueDateTime = newDayTime(2016, 8, 28, 1, 34);
Task task = newFromDue(Frequency.WEEKLY, 1, dueDateTime);
assertEquals(newDayTime(2016, 9, 4, 1, 34), calculateNextDueDate(task));
}
@Test
public void testRepeatMonthlyFromDueDate() throws ParseException {
DateTime dueDateTime = newDayTime(2016, 8, 28, 1, 44);
Task task = newFromDue(Frequency.MONTHLY, 1, dueDateTime);
assertEquals(newDayTime(2016, 9, 28, 1, 44), calculateNextDueDate(task));
}
@Test
public void testRepeatYearlyFromDueDate() throws ParseException {
DateTime dueDateTime = newDayTime(2016, 8, 28, 1, 44);
Task task = newFromDue(Frequency.YEARLY, 1, dueDateTime);
assertEquals(newDayTime(2017, 8, 28, 1, 44), calculateNextDueDate(task));
}
/** Tests for repeating from completionDate */
@Test
public void testRepeatMinutelyFromCompleteDateCompleteBefore() throws ParseException {
DateTime dueDateTime = newDayTime(2016, 8, 30, 0, 25);
DateTime completionDateTime = newDayTime(2016, 8, 29, 0, 14);
Task task = newFromCompleted(Frequency.MINUTELY, 1, dueDateTime, completionDateTime);
assertEquals(newDayTime(2016, 8, 29, 0, 15), calculateNextDueDate(task));
}
@Test
public void testRepeatMinutelyFromCompleteDateCompleteAfter() throws ParseException {
DateTime dueDateTime = newDayTime(2016, 8, 28, 0, 4);
DateTime completionDateTime = newDayTime(2016, 8, 29, 0, 14);
Task task = newFromCompleted(Frequency.MINUTELY, 1, dueDateTime, completionDateTime);
assertEquals(newDayTime(2016, 8, 29, 0, 15), calculateNextDueDate(task));
}
@Test
public void testRepeatHourlyFromCompleteDateCompleteBefore() throws ParseException {
DateTime dueDateTime = newDayTime(2016, 8, 30, 0, 25);
DateTime completionDateTime = newDayTime(2016, 8, 29, 0, 14);
Task task = newFromCompleted(Frequency.HOURLY, 1, dueDateTime, completionDateTime);
assertEquals(newDayTime(2016, 8, 29, 1, 14), calculateNextDueDate(task));
}
@Test
public void testRepeatHourlyFromCompleteDateCompleteAfter() throws ParseException {
DateTime dueDateTime = newDayTime(2016, 8, 28, 0, 4);
DateTime completionDateTime = newDayTime(2016, 8, 29, 0, 14);
Task task = newFromCompleted(Frequency.HOURLY, 1, dueDateTime, completionDateTime);
assertEquals(newDayTime(2016, 8, 29, 1, 14), calculateNextDueDate(task));
}
@Test
public void testRepeatDailyFromCompleteDateCompleteBefore() throws ParseException {
DateTime dueDateTime = newDayTime(2016, 8, 30, 0, 25);
DateTime completionDateTime = newDayTime(2016, 8, 29, 0, 14);
Task task = newFromCompleted(Frequency.DAILY, 1, dueDateTime, completionDateTime);
assertEquals(newDayTime(2016, 8, 30, 0, 25), calculateNextDueDate(task));
}
@Test
public void testRepeatDailyFromCompleteDateCompleteAfter() throws ParseException {
DateTime dueDateTime = newDayTime(2016, 8, 28, 0, 4);
DateTime completionDateTime = newDayTime(2016, 8, 29, 0, 14);
Task task = newFromCompleted(Frequency.DAILY, 1, dueDateTime, completionDateTime);
assertEquals(newDayTime(2016, 8, 30, 0, 4), calculateNextDueDate(task));
}
@Test
public void testRepeatWeeklyFromCompleteDateCompleteBefore() throws ParseException {
DateTime dueDateTime = newDayTime(2016, 8, 30, 0, 25);
DateTime completionDateTime = newDayTime(2016, 8, 29, 0, 14);
Task task = newFromCompleted(Frequency.WEEKLY, 1, dueDateTime, completionDateTime);
assertEquals(newDayTime(2016, 9, 5, 0, 25), calculateNextDueDate(task));
}
@Test
public void testRepeatWeeklyFromCompleteDateCompleteAfter() throws ParseException {
DateTime dueDateTime = newDayTime(2016, 8, 28, 0, 4);
DateTime completionDateTime = newDayTime(2016, 8, 29, 0, 14);
Task task = newFromCompleted(Frequency.WEEKLY, 1, dueDateTime, completionDateTime);
assertEquals(newDayTime(2016, 9, 5, 0, 4), calculateNextDueDate(task));
}
@Test
public void testRepeatMonthlyFromCompleteDateCompleteBefore() throws ParseException {
DateTime dueDateTime = newDayTime(2016, 8, 30, 0, 25);
DateTime completionDateTime = newDayTime(2016, 8, 29, 0, 14);
Task task = newFromCompleted(Frequency.MONTHLY, 1, dueDateTime, completionDateTime);
assertEquals(newDayTime(2016, 9, 29, 0, 25), calculateNextDueDate(task));
}
@Test
public void testRepeatMonthlyFromCompleteDateCompleteAfter() throws ParseException {
DateTime dueDateTime = newDayTime(2016, 8, 28, 0, 4);
DateTime completionDateTime = newDayTime(2016, 8, 29, 0, 14);
Task task = newFromCompleted(Frequency.MONTHLY, 1, dueDateTime, completionDateTime);
assertEquals(newDayTime(2016, 9, 29, 0, 4), calculateNextDueDate(task));
}
@Test
public void testRepeatYearlyFromCompleteDateCompleteBefore() throws ParseException {
DateTime dueDateTime = newDayTime(2016, 8, 30, 0, 25);
DateTime completionDateTime = newDayTime(2016, 8, 29, 0, 14);
Task task = newFromCompleted(Frequency.YEARLY, 1, dueDateTime, completionDateTime);
assertEquals(newDayTime(2017, 8, 29, 0, 25), calculateNextDueDate(task));
}
@Test
public void testRepeatYearlyFromCompleteDateCompleteAfter() throws ParseException {
DateTime dueDateTime = newDayTime(2016, 8, 28, 0, 4);
DateTime completionDateTime = newDayTime(2016, 8, 29, 0, 14);
Task task = newFromCompleted(Frequency.YEARLY, 1, dueDateTime, completionDateTime);
assertEquals(newDayTime(2017, 8, 29, 0, 4), calculateNextDueDate(task));
@Test
public void testRepeatMinutelyFromDueDate() throws ParseException {
DateTime dueDateTime = newDayTime(2016, 8, 26, 12, 30);
Task task = newFromDue(Frequency.MINUTELY, 1, dueDateTime);
assertEquals(newDayTime(2016, 8, 26, 12, 31), calculateNextDueDate(task));
}
@Test
public void testRepeatHourlyFromDueDate() throws ParseException {
DateTime dueDateTime = newDayTime(2016, 8, 26, 12, 30);
Task task = newFromDue(Frequency.HOURLY, 1, dueDateTime);
assertEquals(newDayTime(2016, 8, 26, 13, 30), calculateNextDueDate(task));
}
@Test
public void testRepeatDailyFromDueDate() throws ParseException {
DateTime dueDateTime = newDayTime(2016, 8, 26, 12, 30);
Task task = newFromDue(Frequency.DAILY, 1, dueDateTime);
assertEquals(newDayTime(2016, 8, 27, 12, 30), calculateNextDueDate(task));
}
@Test
public void testRepeatWeeklyFromDueDate() throws ParseException {
DateTime dueDateTime = newDayTime(2016, 8, 28, 1, 34);
Task task = newFromDue(Frequency.WEEKLY, 1, dueDateTime);
assertEquals(newDayTime(2016, 9, 4, 1, 34), calculateNextDueDate(task));
}
@Test
public void testRepeatMonthlyFromDueDate() throws ParseException {
DateTime dueDateTime = newDayTime(2016, 8, 28, 1, 44);
Task task = newFromDue(Frequency.MONTHLY, 1, dueDateTime);
assertEquals(newDayTime(2016, 9, 28, 1, 44), calculateNextDueDate(task));
}
@Test
public void testRepeatYearlyFromDueDate() throws ParseException {
DateTime dueDateTime = newDayTime(2016, 8, 28, 1, 44);
Task task = newFromDue(Frequency.YEARLY, 1, dueDateTime);
assertEquals(newDayTime(2017, 8, 28, 1, 44), calculateNextDueDate(task));
}
/**
* Tests for repeating from completionDate
*/
@Test
public void testRepeatMinutelyFromCompleteDateCompleteBefore() throws ParseException {
DateTime dueDateTime = newDayTime(2016, 8, 30, 0, 25);
DateTime completionDateTime = newDayTime(2016, 8, 29, 0, 14);
Task task = newFromCompleted(Frequency.MINUTELY, 1, dueDateTime, completionDateTime);
assertEquals(newDayTime(2016, 8, 29, 0, 15), calculateNextDueDate(task));
}
@Test
public void testRepeatMinutelyFromCompleteDateCompleteAfter() throws ParseException {
DateTime dueDateTime = newDayTime(2016, 8, 28, 0, 4);
DateTime completionDateTime = newDayTime(2016, 8, 29, 0, 14);
Task task = newFromCompleted(Frequency.MINUTELY, 1, dueDateTime, completionDateTime);
assertEquals(newDayTime(2016, 8, 29, 0, 15), calculateNextDueDate(task));
}
@Test
public void testRepeatHourlyFromCompleteDateCompleteBefore() throws ParseException {
DateTime dueDateTime = newDayTime(2016, 8, 30, 0, 25);
DateTime completionDateTime = newDayTime(2016, 8, 29, 0, 14);
Task task = newFromCompleted(Frequency.HOURLY, 1, dueDateTime, completionDateTime);
assertEquals(newDayTime(2016, 8, 29, 1, 14), calculateNextDueDate(task));
}
@Test
public void testRepeatHourlyFromCompleteDateCompleteAfter() throws ParseException {
DateTime dueDateTime = newDayTime(2016, 8, 28, 0, 4);
DateTime completionDateTime = newDayTime(2016, 8, 29, 0, 14);
Task task = newFromCompleted(Frequency.HOURLY, 1, dueDateTime, completionDateTime);
assertEquals(newDayTime(2016, 8, 29, 1, 14), calculateNextDueDate(task));
}
@Test
public void testRepeatDailyFromCompleteDateCompleteBefore() throws ParseException {
DateTime dueDateTime = newDayTime(2016, 8, 30, 0, 25);
DateTime completionDateTime = newDayTime(2016, 8, 29, 0, 14);
Task task = newFromCompleted(Frequency.DAILY, 1, dueDateTime, completionDateTime);
assertEquals(newDayTime(2016, 8, 30, 0, 25), calculateNextDueDate(task));
}
@Test
public void testRepeatDailyFromCompleteDateCompleteAfter() throws ParseException {
DateTime dueDateTime = newDayTime(2016, 8, 28, 0, 4);
DateTime completionDateTime = newDayTime(2016, 8, 29, 0, 14);
Task task = newFromCompleted(Frequency.DAILY, 1, dueDateTime, completionDateTime);
assertEquals(newDayTime(2016, 8, 30, 0, 4), calculateNextDueDate(task));
}
@Test
public void testRepeatWeeklyFromCompleteDateCompleteBefore() throws ParseException {
DateTime dueDateTime = newDayTime(2016, 8, 30, 0, 25);
DateTime completionDateTime = newDayTime(2016, 8, 29, 0, 14);
Task task = newFromCompleted(Frequency.WEEKLY, 1, dueDateTime, completionDateTime);
assertEquals(newDayTime(2016, 9, 5, 0, 25), calculateNextDueDate(task));
}
@Test
public void testRepeatWeeklyFromCompleteDateCompleteAfter() throws ParseException {
DateTime dueDateTime = newDayTime(2016, 8, 28, 0, 4);
DateTime completionDateTime = newDayTime(2016, 8, 29, 0, 14);
Task task = newFromCompleted(Frequency.WEEKLY, 1, dueDateTime, completionDateTime);
assertEquals(newDayTime(2016, 9, 5, 0, 4), calculateNextDueDate(task));
}
@Test
public void testRepeatMonthlyFromCompleteDateCompleteBefore() throws ParseException {
DateTime dueDateTime = newDayTime(2016, 8, 30, 0, 25);
DateTime completionDateTime = newDayTime(2016, 8, 29, 0, 14);
Task task = newFromCompleted(Frequency.MONTHLY, 1, dueDateTime, completionDateTime);
assertEquals(newDayTime(2016, 9, 29, 0, 25), calculateNextDueDate(task));
}
@Test
public void testRepeatMonthlyFromCompleteDateCompleteAfter() throws ParseException {
DateTime dueDateTime = newDayTime(2016, 8, 28, 0, 4);
DateTime completionDateTime = newDayTime(2016, 8, 29, 0, 14);
Task task = newFromCompleted(Frequency.MONTHLY, 1, dueDateTime, completionDateTime);
assertEquals(newDayTime(2016, 9, 29, 0, 4), calculateNextDueDate(task));
}
@Test
public void testRepeatYearlyFromCompleteDateCompleteBefore() throws ParseException {
DateTime dueDateTime = newDayTime(2016, 8, 30, 0, 25);
DateTime completionDateTime = newDayTime(2016, 8, 29, 0, 14);
Task task = newFromCompleted(Frequency.YEARLY, 1, dueDateTime, completionDateTime);
assertEquals(newDayTime(2017, 8, 29, 0, 25), calculateNextDueDate(task));
}
@Test
public void testRepeatYearlyFromCompleteDateCompleteAfter() throws ParseException {
DateTime dueDateTime = newDayTime(2016, 8, 28, 0, 4);
DateTime completionDateTime = newDayTime(2016, 8, 29, 0, 14);
Task task = newFromCompleted(Frequency.YEARLY, 1, dueDateTime, completionDateTime);
assertEquals(newDayTime(2017, 8, 29, 0, 4), calculateNextDueDate(task));
}
@Test
public void testAdvancedRepeatWeeklyFromDueDate() throws ParseException {
DateTime dueDateTime = newDayTime(2016, 8, 29, 0, 25);
Task task = newWeeklyFromDue(1, dueDateTime, new WeekdayNum(0, Weekday.MO),
new WeekdayNum(0, Weekday.WE));
assertEquals(newDayTime(2016, 8, 31, 0, 25), calculateNextDueDate(task));
}
@Test
public void testAdvancedRepeatWeeklyFromCompleteDateCompleteBefore() throws ParseException {
DateTime dueDateTime = newDayTime(2016, 8, 29, 0, 25);
DateTime completionDateTime = newDayTime(2016, 8, 28, 1, 9);
Task task = newWeeklyFromCompleted(1, dueDateTime, completionDateTime,
new WeekdayNum(0, Weekday.MO), new WeekdayNum(0, Weekday.WE));
assertEquals(newDayTime(2016, 8, 29, 0, 25), calculateNextDueDate(task));
}
@Test
public void testAdvancedRepeatWeeklyFromCompleteDateCompleteAfter() throws ParseException {
DateTime dueDateTime = newDayTime(2016, 8, 29, 0, 25);
DateTime completionDateTime = newDayTime(2016, 9, 1, 1, 9);
Task task = newWeeklyFromCompleted(1, dueDateTime, completionDateTime,
new WeekdayNum(0, Weekday.MO), new WeekdayNum(0, Weekday.WE));
assertEquals(newDayTime(2016, 9, 5, 0, 25), calculateNextDueDate(task));
}
private DateTime newDayTime(int year, int month, int day, int hour, int minute) {
return new DateTime(Task.createDueDate(Task.URGENCY_SPECIFIC_DAY_TIME,
new DateTime(year, month, day, hour, minute).getMillis()));
}
private DateTime calculateNextDueDate(Task task) throws ParseException {
return new DateTime(
computeNextDueDate(task, task.sanitizedRecurrence(), task.repeatAfterCompletion()));
}
private Task newFromDue(Frequency frequency, int interval, DateTime dueDateTime) {
return new Task() {{
setRecurrence(getRecurrenceRule(frequency, interval, false));
setDueDate(dueDateTime.getMillis());
}};
}
private Task newWeeklyFromDue(int interval, DateTime dueDateTime, WeekdayNum... weekdays) {
return new Task() {{
setRecurrence(getRecurrenceRule(Frequency.WEEKLY, interval, false, weekdays));
setDueDate(dueDateTime.getMillis());
}};
}
private Task newFromCompleted(Frequency frequency, int interval, DateTime dueDateTime,
DateTime completionDate) {
return new Task() {{
setRecurrence(getRecurrenceRule(frequency, interval, true));
setDueDate(dueDateTime.getMillis());
setCompletionDate(completionDate.getMillis());
}};
}
private Task newWeeklyFromCompleted(int interval, DateTime dueDateTime, DateTime completionDate,
WeekdayNum... weekdays) {
return new Task() {{
setRecurrence(getRecurrenceRule(Frequency.WEEKLY, interval, true, weekdays));
setDueDate(dueDateTime.getMillis());
setCompletionDate(completionDate.getMillis());
}};
}
private String getRecurrenceRule(Frequency frequency, int interval, boolean fromCompletion,
WeekdayNum... weekdays) {
RRule rrule = new RRule();
rrule.setFreq(frequency);
rrule.setInterval(interval);
if (weekdays != null) {
rrule.setByDay(asList(weekdays));
}
@Test
public void testAdvancedRepeatWeeklyFromDueDate() throws ParseException {
DateTime dueDateTime = newDayTime(2016, 8, 29, 0, 25);
Task task = newWeeklyFromDue(1, dueDateTime, new WeekdayNum(0, Weekday.MO), new WeekdayNum(0, Weekday.WE));
assertEquals(newDayTime(2016, 8, 31, 0, 25), calculateNextDueDate(task));
}
@Test
public void testAdvancedRepeatWeeklyFromCompleteDateCompleteBefore() throws ParseException {
DateTime dueDateTime = newDayTime(2016, 8, 29, 0, 25);
DateTime completionDateTime = newDayTime(2016, 8, 28, 1, 9);
Task task = newWeeklyFromCompleted(1, dueDateTime, completionDateTime, new WeekdayNum(0, Weekday.MO), new WeekdayNum(0, Weekday.WE));
assertEquals(newDayTime(2016, 8, 29, 0, 25), calculateNextDueDate(task));
}
@Test
public void testAdvancedRepeatWeeklyFromCompleteDateCompleteAfter() throws ParseException {
DateTime dueDateTime = newDayTime(2016, 8, 29, 0, 25);
DateTime completionDateTime = newDayTime(2016, 9, 1, 1, 9);
Task task = newWeeklyFromCompleted(1, dueDateTime, completionDateTime, new WeekdayNum(0, Weekday.MO), new WeekdayNum(0, Weekday.WE));
assertEquals(newDayTime(2016, 9, 5, 0, 25), calculateNextDueDate(task));
}
private DateTime newDayTime(int year, int month, int day, int hour, int minute) {
return new DateTime(Task.createDueDate(Task.URGENCY_SPECIFIC_DAY_TIME, new DateTime(year, month, day, hour, minute).getMillis()));
}
private DateTime calculateNextDueDate(Task task) throws ParseException {
return new DateTime(computeNextDueDate(task, task.sanitizedRecurrence(), task.repeatAfterCompletion()));
}
private Task newFromDue(Frequency frequency, int interval, DateTime dueDateTime) {
return new Task() {{
setRecurrence(getRecurrenceRule(frequency, interval, false));
setDueDate(dueDateTime.getMillis());
}};
}
private Task newWeeklyFromDue(int interval, DateTime dueDateTime, WeekdayNum... weekdays) {
return new Task() {{
setRecurrence(getRecurrenceRule(Frequency.WEEKLY, interval, false, weekdays));
setDueDate(dueDateTime.getMillis());
}};
}
private Task newFromCompleted(Frequency frequency, int interval, DateTime dueDateTime, DateTime completionDate) {
return new Task() {{
setRecurrence(getRecurrenceRule(frequency, interval, true));
setDueDate(dueDateTime.getMillis());
setCompletionDate(completionDate.getMillis());
}};
}
private Task newWeeklyFromCompleted(int interval, DateTime dueDateTime, DateTime completionDate, WeekdayNum... weekdays) {
return new Task() {{
setRecurrence(getRecurrenceRule(Frequency.WEEKLY, interval, true, weekdays));
setDueDate(dueDateTime.getMillis());
setCompletionDate(completionDate.getMillis());
}};
}
private String getRecurrenceRule(Frequency frequency, int interval, boolean fromCompletion, WeekdayNum... weekdays) {
RRule rrule = new RRule();
rrule.setFreq(frequency);
rrule.setInterval(interval);
if (weekdays != null) {
rrule.setByDay(asList(weekdays));
}
String result = rrule.toIcal();
if (fromCompletion) {
result += ";FROM=COMPLETION";
}
return result;
String result = rrule.toIcal();
if (fromCompletion) {
result += ";FROM=COMPLETION";
}
return result;
}
}

@ -1,14 +1,26 @@
package com.todoroo.astrid.repeats;
import static com.natpryce.makeiteasy.MakeItEasy.with;
import static junit.framework.Assert.assertEquals;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.tasks.makers.TaskMaker.AFTER_COMPLETE;
import static org.tasks.makers.TaskMaker.COMPLETION_TIME;
import static org.tasks.makers.TaskMaker.DUE_TIME;
import static org.tasks.makers.TaskMaker.ID;
import static org.tasks.makers.TaskMaker.RRULE;
import static org.tasks.makers.TaskMaker.newTask;
import android.annotation.SuppressLint;
import android.support.test.runner.AndroidJUnit4;
import com.google.ical.values.RRule;
import com.todoroo.astrid.alarms.AlarmService;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.gcal.GCalHelper;
import java.text.ParseException;
import javax.inject.Inject;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@ -19,188 +31,173 @@ import org.tasks.injection.InjectingTestCase;
import org.tasks.injection.TestComponent;
import org.tasks.time.DateTime;
import java.text.ParseException;
import javax.inject.Inject;
import static com.natpryce.makeiteasy.MakeItEasy.with;
import static junit.framework.Assert.assertEquals;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.tasks.makers.TaskMaker.AFTER_COMPLETE;
import static org.tasks.makers.TaskMaker.COMPLETION_TIME;
import static org.tasks.makers.TaskMaker.DUE_TIME;
import static org.tasks.makers.TaskMaker.ID;
import static org.tasks.makers.TaskMaker.RRULE;
import static org.tasks.makers.TaskMaker.newTask;
@SuppressLint("NewApi")
@RunWith(AndroidJUnit4.class)
public class RepeatTaskHelperTest extends InjectingTestCase {
@Inject TaskDao taskDao;
private LocalBroadcastManager localBroadcastManager;
private AlarmService alarmService;
private GCalHelper gCalHelper;
private RepeatTaskHelper helper;
private InOrder mocks;
@Before
public void before() {
alarmService = mock(AlarmService.class);
gCalHelper = mock(GCalHelper.class);
localBroadcastManager = mock(LocalBroadcastManager.class);
mocks = inOrder(alarmService, gCalHelper, localBroadcastManager);
helper = new RepeatTaskHelper(gCalHelper, alarmService, taskDao, localBroadcastManager);
}
@After
public void after() {
verifyNoMoreInteractions(localBroadcastManager, gCalHelper, alarmService);
}
@Test
public void noRepeat() {
helper.handleRepeat(newTask(with(DUE_TIME, new DateTime(2017, 10, 4, 13, 30))));
}
@Test
public void testMinutelyRepeat() throws ParseException {
Task task = newTask(with(ID, 1L),
with(DUE_TIME, new DateTime(2017, 10, 4, 13, 30)),
with(RRULE, new RRule("RRULE:FREQ=MINUTELY;INTERVAL=30")));
repeatAndVerify(task,
new DateTime(2017, 10, 4, 13, 30, 1),
new DateTime(2017, 10, 4, 14, 0, 1));
}
@Test
public void testMinutelyRepeatAfterCompletion() throws ParseException {
Task task = newTask(with(ID, 1L),
with(DUE_TIME, new DateTime(2017, 10, 4, 13, 30)),
with(COMPLETION_TIME, new DateTime(2017, 10, 4, 13, 17, 45, 340)),
with(RRULE, new RRule("RRULE:FREQ=MINUTELY;INTERVAL=30")),
with(AFTER_COMPLETE, true));
repeatAndVerify(task,
new DateTime(2017, 10, 4, 13, 30, 1),
new DateTime(2017, 10, 4, 13, 47, 1));
}
@Test
public void testMinutelyDecrementCount() throws ParseException {
Task task = newTask(with(ID, 1L),
with(DUE_TIME, new DateTime(2017, 10, 4, 13, 30)),
with(RRULE, new RRule("RRULE:FREQ=MINUTELY;COUNT=2;INTERVAL=30")));
repeatAndVerify(task,
new DateTime(2017, 10, 4, 13, 30, 1),
new DateTime(2017, 10, 4, 14, 0, 1));
assertEquals(1, new RRule(task.getRecurrenceWithoutFrom()).getCount());
}
@Test
public void testMinutelyLastOccurrence() throws ParseException {
Task task = newTask(with(ID, 1L),
with(DUE_TIME, new DateTime(2017, 10, 4, 13, 30)),
with(RRULE, new RRule("RRULE:FREQ=MINUTELY;COUNT=1;INTERVAL=30")));
helper.handleRepeat(task);
}
@Test
public void testHourlyRepeat() throws ParseException {
Task task = newTask(with(ID, 1L),
with(DUE_TIME, new DateTime(2017, 10, 4, 13, 30)),
with(RRULE, new RRule("RRULE:FREQ=HOURLY;INTERVAL=6")));
repeatAndVerify(task,
new DateTime(2017, 10, 4, 13, 30, 1),
new DateTime(2017, 10, 4, 19, 30, 1));
}
@Test
public void testHourlyRepeatAfterCompletion() throws ParseException {
Task task = newTask(with(ID, 1L),
with(DUE_TIME, new DateTime(2017, 10, 4, 13, 30)),
with(COMPLETION_TIME, new DateTime(2017, 10, 4, 13, 17, 45, 340)),
with(RRULE, new RRule("RRULE:FREQ=HOURLY;INTERVAL=6")),
with(AFTER_COMPLETE, true));
repeatAndVerify(task,
new DateTime(2017, 10, 4, 13, 30, 1),
new DateTime(2017, 10, 4, 19, 17, 1));
}
@Test
public void testDailyRepeat() throws ParseException {
Task task = newTask(with(ID, 1L),
with(DUE_TIME, new DateTime(2017, 10, 4, 13, 30)),
with(RRULE, new RRule("RRULE:FREQ=DAILY;INTERVAL=6")));
repeatAndVerify(task,
new DateTime(2017, 10, 4, 13, 30, 1),
new DateTime(2017, 10, 10, 13, 30, 1));
}
@Test
public void testRepeatWeeklyNoDays() throws ParseException {
Task task = newTask(with(ID, 1L),
with(DUE_TIME, new DateTime(2017, 10, 4, 13, 30)),
with(RRULE, new RRule("RRULE:FREQ=WEEKLY;INTERVAL=2")));
repeatAndVerify(task,
new DateTime(2017, 10, 4, 13, 30, 1),
new DateTime(2017, 10, 18, 13, 30, 1));
}
@Test
public void testYearly() throws ParseException {
Task task = newTask(with(ID, 1L),
with(DUE_TIME, new DateTime(2017, 10, 4, 13, 30)),
with(RRULE, new RRule("RRULE:FREQ=YEARLY;INTERVAL=3")));
repeatAndVerify(task,
new DateTime(2017, 10, 4, 13, 30, 1),
new DateTime(2020, 10, 4, 13, 30, 1));
}
@Test
public void testMonthlyRepeat() throws ParseException {
Task task = newTask(with(ID, 1L),
with(DUE_TIME, new DateTime(2017, 10, 4, 13, 30)),
with(RRULE, new RRule("RRULE:FREQ=MONTHLY;INTERVAL=3")));
repeatAndVerify(task,
new DateTime(2017, 10, 4, 13, 30, 1),
new DateTime(2018, 1, 4, 13, 30, 1));
}
@Test
public void testMonthlyRepeatAtEndOfMonth() throws ParseException {
Task task = newTask(with(ID, 1L),
with(DUE_TIME, new DateTime(2017, 1, 31, 13, 30)),
with(RRULE, new RRule("RRULE:FREQ=MONTHLY;INTERVAL=1")));
repeatAndVerify(task,
new DateTime(2017, 1, 31, 13, 30, 1),
new DateTime(2017, 2, 28, 13, 30, 1));
}
private void repeatAndVerify(Task task, DateTime oldDueDate, DateTime newDueDate) {
helper.handleRepeat(task);
mocks.verify(gCalHelper).rescheduleRepeatingTask(task);
mocks.verify(alarmService).rescheduleAlarms(1, oldDueDate.getMillis(), newDueDate.getMillis());
mocks.verify(localBroadcastManager).broadcastRepeat(1, oldDueDate.getMillis(), newDueDate.getMillis());
}
@Override
protected void inject(TestComponent component) {
component.inject(this);
}
@Inject TaskDao taskDao;
private LocalBroadcastManager localBroadcastManager;
private AlarmService alarmService;
private GCalHelper gCalHelper;
private RepeatTaskHelper helper;
private InOrder mocks;
@Before
public void before() {
alarmService = mock(AlarmService.class);
gCalHelper = mock(GCalHelper.class);
localBroadcastManager = mock(LocalBroadcastManager.class);
mocks = inOrder(alarmService, gCalHelper, localBroadcastManager);
helper = new RepeatTaskHelper(gCalHelper, alarmService, taskDao, localBroadcastManager);
}
@After
public void after() {
verifyNoMoreInteractions(localBroadcastManager, gCalHelper, alarmService);
}
@Test
public void noRepeat() {
helper.handleRepeat(newTask(with(DUE_TIME, new DateTime(2017, 10, 4, 13, 30))));
}
@Test
public void testMinutelyRepeat() throws ParseException {
Task task = newTask(with(ID, 1L),
with(DUE_TIME, new DateTime(2017, 10, 4, 13, 30)),
with(RRULE, new RRule("RRULE:FREQ=MINUTELY;INTERVAL=30")));
repeatAndVerify(task,
new DateTime(2017, 10, 4, 13, 30, 1),
new DateTime(2017, 10, 4, 14, 0, 1));
}
@Test
public void testMinutelyRepeatAfterCompletion() throws ParseException {
Task task = newTask(with(ID, 1L),
with(DUE_TIME, new DateTime(2017, 10, 4, 13, 30)),
with(COMPLETION_TIME, new DateTime(2017, 10, 4, 13, 17, 45, 340)),
with(RRULE, new RRule("RRULE:FREQ=MINUTELY;INTERVAL=30")),
with(AFTER_COMPLETE, true));
repeatAndVerify(task,
new DateTime(2017, 10, 4, 13, 30, 1),
new DateTime(2017, 10, 4, 13, 47, 1));
}
@Test
public void testMinutelyDecrementCount() throws ParseException {
Task task = newTask(with(ID, 1L),
with(DUE_TIME, new DateTime(2017, 10, 4, 13, 30)),
with(RRULE, new RRule("RRULE:FREQ=MINUTELY;COUNT=2;INTERVAL=30")));
repeatAndVerify(task,
new DateTime(2017, 10, 4, 13, 30, 1),
new DateTime(2017, 10, 4, 14, 0, 1));
assertEquals(1, new RRule(task.getRecurrenceWithoutFrom()).getCount());
}
@Test
public void testMinutelyLastOccurrence() throws ParseException {
Task task = newTask(with(ID, 1L),
with(DUE_TIME, new DateTime(2017, 10, 4, 13, 30)),
with(RRULE, new RRule("RRULE:FREQ=MINUTELY;COUNT=1;INTERVAL=30")));
helper.handleRepeat(task);
}
@Test
public void testHourlyRepeat() throws ParseException {
Task task = newTask(with(ID, 1L),
with(DUE_TIME, new DateTime(2017, 10, 4, 13, 30)),
with(RRULE, new RRule("RRULE:FREQ=HOURLY;INTERVAL=6")));
repeatAndVerify(task,
new DateTime(2017, 10, 4, 13, 30, 1),
new DateTime(2017, 10, 4, 19, 30, 1));
}
@Test
public void testHourlyRepeatAfterCompletion() throws ParseException {
Task task = newTask(with(ID, 1L),
with(DUE_TIME, new DateTime(2017, 10, 4, 13, 30)),
with(COMPLETION_TIME, new DateTime(2017, 10, 4, 13, 17, 45, 340)),
with(RRULE, new RRule("RRULE:FREQ=HOURLY;INTERVAL=6")),
with(AFTER_COMPLETE, true));
repeatAndVerify(task,
new DateTime(2017, 10, 4, 13, 30, 1),
new DateTime(2017, 10, 4, 19, 17, 1));
}
@Test
public void testDailyRepeat() throws ParseException {
Task task = newTask(with(ID, 1L),
with(DUE_TIME, new DateTime(2017, 10, 4, 13, 30)),
with(RRULE, new RRule("RRULE:FREQ=DAILY;INTERVAL=6")));
repeatAndVerify(task,
new DateTime(2017, 10, 4, 13, 30, 1),
new DateTime(2017, 10, 10, 13, 30, 1));
}
@Test
public void testRepeatWeeklyNoDays() throws ParseException {
Task task = newTask(with(ID, 1L),
with(DUE_TIME, new DateTime(2017, 10, 4, 13, 30)),
with(RRULE, new RRule("RRULE:FREQ=WEEKLY;INTERVAL=2")));
repeatAndVerify(task,
new DateTime(2017, 10, 4, 13, 30, 1),
new DateTime(2017, 10, 18, 13, 30, 1));
}
@Test
public void testYearly() throws ParseException {
Task task = newTask(with(ID, 1L),
with(DUE_TIME, new DateTime(2017, 10, 4, 13, 30)),
with(RRULE, new RRule("RRULE:FREQ=YEARLY;INTERVAL=3")));
repeatAndVerify(task,
new DateTime(2017, 10, 4, 13, 30, 1),
new DateTime(2020, 10, 4, 13, 30, 1));
}
@Test
public void testMonthlyRepeat() throws ParseException {
Task task = newTask(with(ID, 1L),
with(DUE_TIME, new DateTime(2017, 10, 4, 13, 30)),
with(RRULE, new RRule("RRULE:FREQ=MONTHLY;INTERVAL=3")));
repeatAndVerify(task,
new DateTime(2017, 10, 4, 13, 30, 1),
new DateTime(2018, 1, 4, 13, 30, 1));
}
@Test
public void testMonthlyRepeatAtEndOfMonth() throws ParseException {
Task task = newTask(with(ID, 1L),
with(DUE_TIME, new DateTime(2017, 1, 31, 13, 30)),
with(RRULE, new RRule("RRULE:FREQ=MONTHLY;INTERVAL=1")));
repeatAndVerify(task,
new DateTime(2017, 1, 31, 13, 30, 1),
new DateTime(2017, 2, 28, 13, 30, 1));
}
private void repeatAndVerify(Task task, DateTime oldDueDate, DateTime newDueDate) {
helper.handleRepeat(task);
mocks.verify(gCalHelper).rescheduleRepeatingTask(task);
mocks.verify(alarmService).rescheduleAlarms(1, oldDueDate.getMillis(), newDueDate.getMillis());
mocks.verify(localBroadcastManager)
.broadcastRepeat(1, oldDueDate.getMillis(), newDueDate.getMillis());
}
@Override
protected void inject(TestComponent component) {
component.inject(this);
}
}

@ -3,111 +3,107 @@
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.service;
import android.support.test.runner.AndroidJUnit4;
import static junit.framework.Assert.assertEquals;
import android.support.test.runner.AndroidJUnit4;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.tags.TagService;
import com.todoroo.astrid.utility.TitleParser;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.tasks.injection.InjectingTestCase;
import org.tasks.injection.TestComponent;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.inject.Inject;
import static junit.framework.Assert.assertEquals;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.tasks.injection.InjectingTestCase;
import org.tasks.injection.TestComponent;
@RunWith(AndroidJUnit4.class)
public class QuickAddMarkupTest extends InjectingTestCase {
@Inject TagService tagService;
@Override
protected void inject(TestComponent component) {
component.inject(this);
}
@Test
public void testTags() {
whenTitleIs("this #cool");
assertTitleBecomes("this");
assertTagsAre("cool");
whenTitleIs("#cool task");
assertTitleBecomes("task");
assertTagsAre("cool");
whenTitleIs("doggie #nice #cute");
assertTitleBecomes("doggie");
assertTagsAre("nice", "cute");
}
@Test
public void testContexts() {
whenTitleIs("eat @home");
assertTitleBecomes("eat");
assertTagsAre("home");
whenTitleIs("buy oatmeal @store @morning");
assertTitleBecomes("buy oatmeal");
assertTagsAre("store", "morning");
whenTitleIs("look @ me");
assertTitleBecomes("look @ me");
assertTagsAre();
}
@Test
public void testImportances() {
whenTitleIs("eat !1");
assertTitleBecomes("eat");
assertImportanceIs(Task.IMPORTANCE_SHOULD_DO);
whenTitleIs("super cool!");
assertTitleBecomes("super cool!");
whenTitleIs("stay alive !4");
assertTitleBecomes("stay alive");
assertImportanceIs(Task.IMPORTANCE_DO_OR_DIE);
}
@Test
public void testMixed() {
whenTitleIs("eat #food !2");
assertTitleBecomes("eat");
assertTagsAre("food");
assertImportanceIs(Task.IMPORTANCE_MUST_DO);
}
// --- helpers
private Task task;
private final ArrayList<String> tags = new ArrayList<>();
private void assertTagsAre(String... expectedTags) {
List<String> expected = Arrays.asList(expectedTags);
assertEquals(expected.toString(), tags.toString());
}
private void assertTitleBecomes(String title) {
assertEquals(title, task.getTitle());
}
private void whenTitleIs(String title) {
task = new Task();
task.setTitle(title);
tags.clear();
TitleParser.parse(tagService, task, tags);
}
private void assertImportanceIs(int importance) {
assertEquals(importance, (int)task.getImportance());
}
private final ArrayList<String> tags = new ArrayList<>();
@Inject TagService tagService;
private Task task;
@Override
protected void inject(TestComponent component) {
component.inject(this);
}
@Test
public void testTags() {
whenTitleIs("this #cool");
assertTitleBecomes("this");
assertTagsAre("cool");
whenTitleIs("#cool task");
assertTitleBecomes("task");
assertTagsAre("cool");
whenTitleIs("doggie #nice #cute");
assertTitleBecomes("doggie");
assertTagsAre("nice", "cute");
}
@Test
public void testContexts() {
whenTitleIs("eat @home");
assertTitleBecomes("eat");
assertTagsAre("home");
whenTitleIs("buy oatmeal @store @morning");
assertTitleBecomes("buy oatmeal");
assertTagsAre("store", "morning");
whenTitleIs("look @ me");
assertTitleBecomes("look @ me");
assertTagsAre();
}
// --- helpers
@Test
public void testImportances() {
whenTitleIs("eat !1");
assertTitleBecomes("eat");
assertImportanceIs(Task.IMPORTANCE_SHOULD_DO);
whenTitleIs("super cool!");
assertTitleBecomes("super cool!");
whenTitleIs("stay alive !4");
assertTitleBecomes("stay alive");
assertImportanceIs(Task.IMPORTANCE_DO_OR_DIE);
}
@Test
public void testMixed() {
whenTitleIs("eat #food !2");
assertTitleBecomes("eat");
assertTagsAre("food");
assertImportanceIs(Task.IMPORTANCE_MUST_DO);
}
private void assertTagsAre(String... expectedTags) {
List<String> expected = Arrays.asList(expectedTags);
assertEquals(expected.toString(), tags.toString());
}
private void assertTitleBecomes(String title) {
assertEquals(title, task.getTitle());
}
private void whenTitleIs(String title) {
task = new Task();
task.setTitle(title);
tags.clear();
TitleParser.parse(tagService, task, tags);
}
private void assertImportanceIs(int importance) {
assertEquals(importance, (int) task.getImportance());
}
}

@ -1,73 +1,72 @@
package com.todoroo.astrid.subtasks;
import android.support.test.runner.AndroidJUnit4;
import static junit.framework.Assert.assertEquals;
import android.support.test.runner.AndroidJUnit4;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.data.Task;
import org.tasks.data.TaskListMetadata;
import javax.inject.Inject;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.tasks.data.TaskListMetadata;
import org.tasks.injection.TestComponent;
import javax.inject.Inject;
import static junit.framework.Assert.assertEquals;
@RunWith(AndroidJUnit4.class)
public class SubtasksHelperTest extends SubtasksTestCase {
@Inject TaskDao taskDao;
@Override
public void setUp() {
super.setUp();
createTasks();
TaskListMetadata m = new TaskListMetadata();
m.setFilter(TaskListMetadata.FILTER_ID_ALL);
updater.initializeFromSerializedTree(m, filter, SubtasksHelper.convertTreeToRemoteIds(taskDao, DEFAULT_SERIALIZED_TREE));
}
private void createTask(String title, String uuid) {
Task t = new Task();
t.setTitle(title);
t.setUuid(uuid);
taskDao.createNew(t);
private static final String[] EXPECTED_ORDER = {"-1", "1", "2", "3", "4", "5", "6"};
private static String EXPECTED_REMOTE = "[\"-1\", [\"6\", \"4\", [\"3\", \"1\"]], \"2\", \"5\"]"
.replaceAll("\\s", "");
@Inject TaskDao taskDao;
@Override
public void setUp() {
super.setUp();
createTasks();
TaskListMetadata m = new TaskListMetadata();
m.setFilter(TaskListMetadata.FILTER_ID_ALL);
updater.initializeFromSerializedTree(m, filter,
SubtasksHelper.convertTreeToRemoteIds(taskDao, DEFAULT_SERIALIZED_TREE));
}
private void createTask(String title, String uuid) {
Task t = new Task();
t.setTitle(title);
t.setUuid(uuid);
taskDao.createNew(t);
}
private void createTasks() {
createTask("A", "6"); // Local id 1
createTask("B", "4"); // Local id 2
createTask("C", "3"); // Local id 3
createTask("D", "1"); // Local id 4
createTask("E", "2"); // Local id 5
createTask("F", "5"); // Local id 6
}
// Default order: "[-1, [1, 2, [3, 4]], 5, 6]"
@Test
public void testOrderedIdArray() {
String[] ids = SubtasksHelper.getStringIdArray(DEFAULT_SERIALIZED_TREE);
assertEquals(EXPECTED_ORDER.length, ids.length);
for (int i = 0; i < EXPECTED_ORDER.length; i++) {
assertEquals(EXPECTED_ORDER[i], ids[i]);
}
}
private void createTasks() {
createTask("A", "6"); // Local id 1
createTask("B", "4"); // Local id 2
createTask("C", "3"); // Local id 3
createTask("D", "1"); // Local id 4
createTask("E", "2"); // Local id 5
createTask("F", "5"); // Local id 6
}
private static final String[] EXPECTED_ORDER = { "-1", "1", "2", "3", "4", "5", "6" };
@Test
public void testLocalToRemoteIdMapping() {
String mapped = SubtasksHelper.convertTreeToRemoteIds(taskDao, DEFAULT_SERIALIZED_TREE)
.replaceAll("\\s", "");
assertEquals(EXPECTED_REMOTE, mapped);
}
@Test
public void testOrderedIdArray() {
String[] ids = SubtasksHelper.getStringIdArray(DEFAULT_SERIALIZED_TREE);
assertEquals(EXPECTED_ORDER.length, ids.length);
for (int i = 0; i < EXPECTED_ORDER.length; i++) {
assertEquals(EXPECTED_ORDER[i], ids[i]);
}
}
@Override
protected void inject(TestComponent component) {
super.inject(component);
// Default order: "[-1, [1, 2, [3, 4]], 5, 6]"
private static String EXPECTED_REMOTE = "[\"-1\", [\"6\", \"4\", [\"3\", \"1\"]], \"2\", \"5\"]".replaceAll("\\s", "");
@Test
public void testLocalToRemoteIdMapping() {
String mapped = SubtasksHelper.convertTreeToRemoteIds(taskDao, DEFAULT_SERIALIZED_TREE).replaceAll("\\s", "");
assertEquals(EXPECTED_REMOTE, mapped);
}
@Override
protected void inject(TestComponent component) {
super.inject(component);
component.inject(this);
}
component.inject(this);
}
}

@ -1,61 +1,59 @@
package com.todoroo.astrid.subtasks;
import android.support.test.runner.AndroidJUnit4;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.data.Task;
import org.tasks.data.TaskListMetadata;
import javax.inject.Inject;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import javax.inject.Inject;
import org.tasks.data.TaskListMetadata;
@RunWith(AndroidJUnit4.class)
public class SubtasksMovingTest extends SubtasksTestCase {
@Inject TaskDao taskDao;
private Task A, B, C, D, E, F;
// @Override
protected void disabled_setUp() {
super.setUp();
createTasks();
TaskListMetadata m = new TaskListMetadata();
m.setFilter(TaskListMetadata.FILTER_ID_ALL);
updater.initializeFromSerializedTree(m, filter, SubtasksHelper.convertTreeToRemoteIds(taskDao, DEFAULT_SERIALIZED_TREE));
// Assert initial state is correct
expectParentAndPosition(A, null, 0);
expectParentAndPosition(B, A, 0);
expectParentAndPosition(C, A, 1);
expectParentAndPosition(D, C, 0);
expectParentAndPosition(E, null, 1);
expectParentAndPosition(F, null, 2);
}
private void createTasks() {
A = createTask("A");
B = createTask("B");
C = createTask("C");
D = createTask("D");
E = createTask("E");
F = createTask("F");
}
private Task createTask(String title) {
Task task = new Task();
task.setTitle(title);
taskDao.createNew(task);
return task;
}
private void whenTriggerMoveBefore(Task target, Task before) {
String beforeId = (before == null ? "-1" : before.getUuid());
updater.moveTo(null, filter, target.getUuid(), beforeId);
}
@Inject TaskDao taskDao;
private Task A, B, C, D, E, F;
// @Override
protected void disabled_setUp() {
super.setUp();
createTasks();
TaskListMetadata m = new TaskListMetadata();
m.setFilter(TaskListMetadata.FILTER_ID_ALL);
updater.initializeFromSerializedTree(m, filter,
SubtasksHelper.convertTreeToRemoteIds(taskDao, DEFAULT_SERIALIZED_TREE));
// Assert initial state is correct
expectParentAndPosition(A, null, 0);
expectParentAndPosition(B, A, 0);
expectParentAndPosition(C, A, 1);
expectParentAndPosition(D, C, 0);
expectParentAndPosition(E, null, 1);
expectParentAndPosition(F, null, 2);
}
private void createTasks() {
A = createTask("A");
B = createTask("B");
C = createTask("C");
D = createTask("D");
E = createTask("E");
F = createTask("F");
}
private Task createTask(String title) {
Task task = new Task();
task.setTitle(title);
taskDao.createNew(task);
return task;
}
private void whenTriggerMoveBefore(Task target, Task before) {
String beforeId = (before == null ? "-1" : before.getUuid());
updater.moveTo(null, filter, target.getUuid(), beforeId);
}
/* Starting State (see SubtasksTestCase):
*
@ -67,68 +65,68 @@ public class SubtasksMovingTest extends SubtasksTestCase {
* F
*/
@Ignore
@Test
public void testMoveBeforeIntoSelf() { // Should have no effect
whenTriggerMoveBefore(A, B);
expectParentAndPosition(A, null, 0);
expectParentAndPosition(B, A, 0);
expectParentAndPosition(C, A, 1);
expectParentAndPosition(D, C, 0);
expectParentAndPosition(E, null, 1);
expectParentAndPosition(F, null, 2);
}
@Ignore
@Test
public void testMoveIntoDescendant() { // Should have no effect
whenTriggerMoveBefore(A, C);
expectParentAndPosition(A, null, 0);
expectParentAndPosition(B, A, 0);
expectParentAndPosition(C, A, 1);
expectParentAndPosition(D, C, 0);
expectParentAndPosition(E, null, 1);
expectParentAndPosition(F, null, 2);
}
@Ignore
@Test
public void testMoveToEndOfChildren() { // Should have no effect
whenTriggerMoveBefore(A, E);
expectParentAndPosition(A, null, 0);
expectParentAndPosition(B, A, 0);
expectParentAndPosition(C, A, 1);
expectParentAndPosition(D, C, 0);
expectParentAndPosition(E, null, 1);
expectParentAndPosition(F, null, 2);
}
@Ignore
@Test
public void testStandardMove() {
whenTriggerMoveBefore(A, F);
expectParentAndPosition(A, null, 1);
expectParentAndPosition(B, A, 0);
expectParentAndPosition(C, A, 1);
expectParentAndPosition(D, C, 0);
expectParentAndPosition(E, null, 0);
expectParentAndPosition(F, null, 2);
}
@Ignore
@Test
public void testMoveToEndOfList() {
whenTriggerMoveBefore(A, null);
expectParentAndPosition(A, null, 2);
expectParentAndPosition(B, A, 0);
expectParentAndPosition(C, A, 1);
expectParentAndPosition(D, C, 0);
expectParentAndPosition(E, null, 0);
expectParentAndPosition(F, null, 1);
}
@Ignore
@Test
public void testMoveBeforeIntoSelf() { // Should have no effect
whenTriggerMoveBefore(A, B);
expectParentAndPosition(A, null, 0);
expectParentAndPosition(B, A, 0);
expectParentAndPosition(C, A, 1);
expectParentAndPosition(D, C, 0);
expectParentAndPosition(E, null, 1);
expectParentAndPosition(F, null, 2);
}
@Ignore
@Test
public void testMoveIntoDescendant() { // Should have no effect
whenTriggerMoveBefore(A, C);
expectParentAndPosition(A, null, 0);
expectParentAndPosition(B, A, 0);
expectParentAndPosition(C, A, 1);
expectParentAndPosition(D, C, 0);
expectParentAndPosition(E, null, 1);
expectParentAndPosition(F, null, 2);
}
@Ignore
@Test
public void testMoveToEndOfChildren() { // Should have no effect
whenTriggerMoveBefore(A, E);
expectParentAndPosition(A, null, 0);
expectParentAndPosition(B, A, 0);
expectParentAndPosition(C, A, 1);
expectParentAndPosition(D, C, 0);
expectParentAndPosition(E, null, 1);
expectParentAndPosition(F, null, 2);
}
@Ignore
@Test
public void testStandardMove() {
whenTriggerMoveBefore(A, F);
expectParentAndPosition(A, null, 1);
expectParentAndPosition(B, A, 0);
expectParentAndPosition(C, A, 1);
expectParentAndPosition(D, C, 0);
expectParentAndPosition(E, null, 0);
expectParentAndPosition(F, null, 2);
}
@Ignore
@Test
public void testMoveToEndOfList() {
whenTriggerMoveBefore(A, null);
expectParentAndPosition(A, null, 2);
expectParentAndPosition(B, A, 0);
expectParentAndPosition(C, A, 1);
expectParentAndPosition(D, C, 0);
expectParentAndPosition(E, null, 0);
expectParentAndPosition(F, null, 1);
}
}

@ -1,66 +1,63 @@
package com.todoroo.astrid.subtasks;
import static android.support.test.InstrumentationRegistry.getTargetContext;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.core.BuiltInFilterExposer;
import com.todoroo.astrid.dao.TaskDao;
import org.tasks.data.TaskListMetadataDao;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.subtasks.SubtasksFilterUpdater.Node;
import javax.inject.Inject;
import org.tasks.data.TaskListMetadataDao;
import org.tasks.injection.InjectingTestCase;
import org.tasks.injection.TestComponent;
import org.tasks.preferences.Preferences;
import javax.inject.Inject;
import static android.support.test.InstrumentationRegistry.getTargetContext;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
/**
* Contains useful methods common to all subtasks tests
* @author Sam
*
* @author Sam
*/
public class SubtasksTestCase extends InjectingTestCase {
@Inject TaskListMetadataDao taskListMetadataDao;
@Inject TaskDao taskDao;
@Inject Preferences preferences;
protected SubtasksFilterUpdater updater;
protected Filter filter;
/* Starting State:
*
* A
* B
* C
* D
* E
* F
*/
public static final String DEFAULT_SERIALIZED_TREE = "[-1, [1, 2, [3, 4]], 5, 6]".replaceAll("\\s", "");
@Override
public void setUp() {
super.setUp();
filter = BuiltInFilterExposer.getMyTasksFilter(getTargetContext().getResources());
preferences.clear(SubtasksFilterUpdater.ACTIVE_TASKS_ORDER);
updater = new SubtasksFilterUpdater(taskListMetadataDao, taskDao);
}
@Override
protected void inject(TestComponent component) {
component.inject(this);
}
protected void expectParentAndPosition(Task task, Task parent, int positionInParent) {
String parentId = (parent == null ? "-1" : parent.getUuid());
Node n = updater.findNodeForTask(task.getUuid());
assertNotNull("No node found for task " + task.getTitle(), n);
assertEquals("Parent mismatch", parentId, n.parent.uuid);
assertEquals("Position mismatch", positionInParent, n.parent.children.indexOf(n));
}
/* Starting State:
*
* A
* B
* C
* D
* E
* F
*/
public static final String DEFAULT_SERIALIZED_TREE = "[-1, [1, 2, [3, 4]], 5, 6]"
.replaceAll("\\s", "");
protected SubtasksFilterUpdater updater;
protected Filter filter;
@Inject TaskListMetadataDao taskListMetadataDao;
@Inject TaskDao taskDao;
@Inject Preferences preferences;
@Override
public void setUp() {
super.setUp();
filter = BuiltInFilterExposer.getMyTasksFilter(getTargetContext().getResources());
preferences.clear(SubtasksFilterUpdater.ACTIVE_TASKS_ORDER);
updater = new SubtasksFilterUpdater(taskListMetadataDao, taskDao);
}
@Override
protected void inject(TestComponent component) {
component.inject(this);
}
protected void expectParentAndPosition(Task task, Task parent, int positionInParent) {
String parentId = (parent == null ? "-1" : parent.getUuid());
Node n = updater.findNodeForTask(task.getUuid());
assertNotNull("No node found for task " + task.getTitle(), n);
assertEquals("Parent mismatch", parentId, n.parent.uuid);
assertEquals("Position mismatch", positionInParent, n.parent.children.indexOf(n));
}
}

@ -1,50 +1,47 @@
package com.todoroo.astrid.sync;
import org.tasks.data.TagDataDao;
import com.todoroo.astrid.dao.TaskDao;
import org.tasks.data.TagData;
import com.todoroo.astrid.data.Task;
import javax.inject.Inject;
import org.tasks.data.TagData;
import org.tasks.data.TagDataDao;
import org.tasks.injection.InjectingTestCase;
import org.tasks.injection.TestComponent;
import javax.inject.Inject;
public class NewSyncTestCase extends InjectingTestCase {
@Inject TaskDao taskDao;
@Inject TagDataDao tagDataDao;
protected Task createTask(String title) {
Task task = new Task();
task.setTitle(title);
task.setImportance(SYNC_TASK_IMPORTANCE);
public static final String SYNC_TASK_TITLE = "new title";
public static final int SYNC_TASK_IMPORTANCE = Task.IMPORTANCE_MUST_DO;
@Inject TaskDao taskDao;
@Inject TagDataDao tagDataDao;
taskDao.createNew(task);
return task;
}
protected Task createTask(String title) {
Task task = new Task();
task.setTitle(title);
task.setImportance(SYNC_TASK_IMPORTANCE);
public static final String SYNC_TASK_TITLE = "new title";
public static final int SYNC_TASK_IMPORTANCE = Task.IMPORTANCE_MUST_DO;
taskDao.createNew(task);
return task;
}
protected Task createTask() {
return createTask(SYNC_TASK_TITLE);
}
protected Task createTask() {
return createTask(SYNC_TASK_TITLE);
}
protected TagData createTagData(String name) {
TagData tag = new TagData();
tag.setName(name);
protected TagData createTagData(String name) {
TagData tag = new TagData();
tag.setName(name);
tagDataDao.createNew(tag);
return tag;
}
tagDataDao.createNew(tag);
return tag;
}
protected TagData createTagData() {
return createTagData("new tag");
}
protected TagData createTagData() {
return createTagData("new tag");
}
@Override
protected void inject(TestComponent component) {
component.inject(this);
}
@Override
protected void inject(TestComponent component) {
component.inject(this);
}
}

@ -1,28 +1,26 @@
package com.todoroo.astrid.sync;
import android.support.test.runner.AndroidJUnit4;
import static junit.framework.Assert.assertFalse;
import org.tasks.data.TagData;
import android.support.test.runner.AndroidJUnit4;
import com.todoroo.astrid.data.Task;
import org.junit.Test;
import org.junit.runner.RunWith;
import static junit.framework.Assert.assertFalse;
import org.tasks.data.TagData;
@RunWith(AndroidJUnit4.class)
public class SyncModelTest extends NewSyncTestCase {
@Test
public void testCreateTaskMakesUuid() {
Task task = createTask();
assertFalse(Task.NO_UUID.equals(task.getUuid()));
}
@Test
public void testCreateTagMakesUuid() {
TagData tag = createTagData();
assertFalse(Task.NO_UUID.equals(tag.getRemoteId()));
}
@Test
public void testCreateTaskMakesUuid() {
Task task = createTask();
assertFalse(Task.NO_UUID.equals(task.getUuid()));
}
@Test
public void testCreateTagMakesUuid() {
TagData tag = createTagData();
assertFalse(Task.NO_UUID.equals(tag.getRemoteId()));
}
}

@ -1,39 +1,39 @@
package org.tasks;
import static org.tasks.time.DateTimeUtils.currentTimeMillis;
import org.tasks.time.DateTime;
import org.tasks.time.DateTimeUtils;
import static org.tasks.time.DateTimeUtils.currentTimeMillis;
public class Freeze {
public static Freeze freezeClock() {
return freezeAt(currentTimeMillis());
}
public static Freeze freezeAt(DateTime dateTime) {
return freezeAt(dateTime.getMillis());
}
public static Freeze freezeAt(long millis) {
DateTimeUtils.setCurrentMillisFixed(millis);
return new Freeze();
}
public static void thaw() {
DateTimeUtils.setCurrentMillisSystem();
}
@SuppressWarnings("UnusedParameters")
public void thawAfter(Snippet snippet) {
thaw();
}
public void thawAfter(Runnable run) {
try {
run.run();
} finally {
thaw();
}
public static Freeze freezeClock() {
return freezeAt(currentTimeMillis());
}
public static Freeze freezeAt(DateTime dateTime) {
return freezeAt(dateTime.getMillis());
}
public static Freeze freezeAt(long millis) {
DateTimeUtils.setCurrentMillisFixed(millis);
return new Freeze();
}
public static void thaw() {
DateTimeUtils.setCurrentMillisSystem();
}
@SuppressWarnings("UnusedParameters")
public void thawAfter(Snippet snippet) {
thaw();
}
public void thawAfter(Runnable run) {
try {
run.run();
} finally {
thaw();
}
}
}

@ -1,4 +1,5 @@
package org.tasks;
public class Snippet {
}

@ -3,13 +3,14 @@ package org.tasks;
import android.content.Context;
public class TestUtilities {
private static boolean mockitoInitialized;
public static void initializeMockito(Context context) {
if (!mockitoInitialized) {
// for mockito: https://code.google.com/p/dexmaker/issues/detail?id=2
System.setProperty("dexmaker.dexcache", context.getCacheDir().toString());
mockitoInitialized = true;
}
private static boolean mockitoInitialized;
public static void initializeMockito(Context context) {
if (!mockitoInitialized) {
// for mockito: https://code.google.com/p/dexmaker/issues/detail?id=2
System.setProperty("dexmaker.dexcache", context.getCacheDir().toString());
mockitoInitialized = true;
}
}
}

@ -1,42 +1,41 @@
package org.tasks.date;
import android.support.test.runner.AndroidJUnit4;
import static junit.framework.Assert.assertEquals;
import static org.tasks.Freeze.freezeAt;
import static org.tasks.date.DateTimeUtils.newDateUtc;
import static org.tasks.time.DateTimeUtils.currentTimeMillis;
import android.support.test.runner.AndroidJUnit4;
import java.util.TimeZone;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.tasks.Snippet;
import org.tasks.time.DateTime;
import java.util.TimeZone;
import static junit.framework.Assert.assertEquals;
import static org.tasks.Freeze.freezeAt;
import static org.tasks.date.DateTimeUtils.newDateUtc;
import static org.tasks.time.DateTimeUtils.currentTimeMillis;
@RunWith(AndroidJUnit4.class)
public class DateTimeUtilsTest {
private final DateTime now = new DateTime(2014, 1, 1, 15, 17, 53, 0);
@Test
public void testGetCurrentTime() {
freezeAt(now).thawAfter(new Snippet() {{
assertEquals(now.getMillis(), currentTimeMillis());
}});
}
@Test
public void testCreateNewUtcDate() {
DateTime utc = now.toUTC();
DateTime actual = newDateUtc(utc.getYear(), utc.getMonthOfYear(), utc.getDayOfMonth(), utc.getHourOfDay(), utc.getMinuteOfHour(), utc.getSecondOfMinute());
assertEquals(utc.getMillis(), actual.getMillis());
}
@Test
public void testIllegalInstant() {
new DateTime(2015, 7, 24, 0, 0, 0, 0, TimeZone.getTimeZone("Africa/Cairo"));
new DateTime(2015, 10, 18, 0, 0, 0, 0, TimeZone.getTimeZone("America/Sao_Paulo"));
new DateTime(2015, 10, 4, 0, 0, 0, 0, TimeZone.getTimeZone("America/Asuncion"));
}
private final DateTime now = new DateTime(2014, 1, 1, 15, 17, 53, 0);
@Test
public void testGetCurrentTime() {
freezeAt(now).thawAfter(new Snippet() {{
assertEquals(now.getMillis(), currentTimeMillis());
}});
}
@Test
public void testCreateNewUtcDate() {
DateTime utc = now.toUTC();
DateTime actual = newDateUtc(utc.getYear(), utc.getMonthOfYear(), utc.getDayOfMonth(),
utc.getHourOfDay(), utc.getMinuteOfHour(), utc.getSecondOfMinute());
assertEquals(utc.getMillis(), actual.getMillis());
}
@Test
public void testIllegalInstant() {
new DateTime(2015, 7, 24, 0, 0, 0, 0, TimeZone.getTimeZone("Africa/Cairo"));
new DateTime(2015, 10, 18, 0, 0, 0, 0, TimeZone.getTimeZone("America/Sao_Paulo"));
new DateTime(2015, 10, 4, 0, 0, 0, 0, TimeZone.getTimeZone("America/Asuncion"));
}
}

@ -1,23 +1,23 @@
package org.tasks.injection;
import org.junit.Before;
import static android.support.test.InstrumentationRegistry.getTargetContext;
import static org.tasks.TestUtilities.initializeMockito;
import org.junit.Before;
public abstract class InjectingTestCase {
protected TestComponent component;
protected TestComponent component;
@Before
public void setUp() {
initializeMockito(getTargetContext());
@Before
public void setUp() {
initializeMockito(getTargetContext());
component = DaggerTestComponent.builder()
.testModule(new TestModule(getTargetContext()))
.build();
inject(component);
}
component = DaggerTestComponent.builder()
.testModule(new TestModule(getTargetContext()))
.build();
inject(component);
}
protected abstract void inject(TestComponent component);
protected abstract void inject(TestComponent component);
}

@ -2,102 +2,118 @@ package org.tasks.injection;
import android.arch.persistence.room.Room;
import android.content.Context;
import com.todoroo.astrid.dao.Database;
import com.todoroo.astrid.dao.TaskDao;
import dagger.Module;
import dagger.Provides;
import org.tasks.data.AlarmDao;
import org.tasks.data.CaldavDao;
import org.tasks.data.FilterDao;
import org.tasks.data.GoogleTaskDao;
import org.tasks.data.GoogleTaskListDao;
import org.tasks.data.LocationDao;
import org.tasks.data.TagDao;
import org.tasks.data.TagDataDao;
import org.tasks.data.TaskAttachmentDao;
import org.tasks.data.TaskListMetadataDao;
import org.tasks.data.UserActivityDao;
import org.tasks.notifications.NotificationDao;
import org.tasks.preferences.PermissionChecker;
import org.tasks.preferences.PermissivePermissionChecker;
import dagger.Module;
import dagger.Provides;
@Module
public class TestModule {
private Context context;
public TestModule(Context context) {
this.context = context;
}
@Provides
@ApplicationScope
public Database getDatabase() {
return Room.inMemoryDatabaseBuilder(context, Database.class)
.fallbackToDestructiveMigration()
.build();
}
@Provides
public NotificationDao getNotificationDao(Database appDatabase) {
return appDatabase.notificationDao();
}
@Provides
public TagDataDao getTagDataDao(Database database) {
return database.getTagDataDao();
}
@Provides
public UserActivityDao getUserActivityDao(Database database) {
return database.getUserActivityDao();
}
@Provides
public TaskListMetadataDao getTaskListMetadataDao(Database database) {
return database.getTaskListMetadataDao();
}
@Provides
public GoogleTaskListDao getGoogleTaskListDao(Database database) {
return database.getGoogleTaskListDao();
}
@Provides
public AlarmDao getAlarmDao(Database database) {
return database.getAlarmDao();
}
@Provides
public GoogleTaskDao getGoogleTaskDao(Database database) {
return database.getGoogleTaskDao();
}
@Provides
public TagDao getTagDao(Database database) {
return database.getTagDao();
}
@Provides
public LocationDao getLocationDao(Database database) {
return database.getLocationDao();
}
@Provides
public TaskDao getTaskDao(Database database) {
TaskDao taskDao = database.getTaskDao();
taskDao.initialize(context);
return taskDao;
}
@ApplicationScope
@Provides
@ForApplication
public Context getContext() {
return context;
}
@Provides
public PermissionChecker getPermissionChecker() {
return new PermissivePermissionChecker(context);
}
private Context context;
public TestModule(Context context) {
this.context = context;
}
@Provides
@ApplicationScope
public Database getDatabase() {
return Room.inMemoryDatabaseBuilder(context, Database.class)
.fallbackToDestructiveMigration()
.build();
}
@Provides
public NotificationDao getNotificationDao(Database appDatabase) {
return appDatabase.notificationDao();
}
@Provides
public TagDataDao getTagDataDao(Database database) {
return database.getTagDataDao();
}
@Provides
public UserActivityDao getUserActivityDao(Database database) {
return database.getUserActivityDao();
}
@Provides
public TaskListMetadataDao getTaskListMetadataDao(Database database) {
return database.getTaskListMetadataDao();
}
@Provides
public GoogleTaskListDao getGoogleTaskListDao(Database database) {
return database.getGoogleTaskListDao();
}
@Provides
public AlarmDao getAlarmDao(Database database) {
return database.getAlarmDao();
}
@Provides
public GoogleTaskDao getGoogleTaskDao(Database database) {
return database.getGoogleTaskDao();
}
@Provides
public TagDao getTagDao(Database database) {
return database.getTagDao();
}
@Provides
public LocationDao getLocationDao(Database database) {
return database.getLocationDao();
}
@Provides
public TaskDao getTaskDao(Database database) {
TaskDao taskDao = database.getTaskDao();
taskDao.initialize(context);
return taskDao;
}
@Provides
public CaldavDao getCaldavDao(Database database) {
return database.getCaldavDao();
}
@Provides
public FilterDao getFilterDao(Database database) {
return database.getFilterDao();
}
@Provides
public TaskAttachmentDao getTaskAttachmentDao(Database database) {
return database.getTaskAttachmentDao();
}
@ApplicationScope
@Provides
@ForApplication
public Context getContext() {
return context;
}
@Provides
public PermissionChecker getPermissionChecker() {
return new PermissivePermissionChecker(context);
}
}

@ -3,143 +3,147 @@
*
* See the file "LICENSE" for the full license governing this code.
*/
package org.tasks.jobs;
import android.support.test.runner.AndroidJUnit4;
import static android.support.test.InstrumentationRegistry.getTargetContext;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.tasks.date.DateTimeUtils.newDateTime;
import static org.tasks.time.DateTimeUtils.currentTimeMillis;
import android.support.test.runner.AndroidJUnit4;
import com.todoroo.andlib.utility.AndroidUtilities;
import org.tasks.backup.TasksJsonExporter;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.data.Task;
import java.io.File;
import java.io.IOException;
import javax.inject.Inject;
import org.junit.After;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.tasks.R;
import org.tasks.backup.TasksJsonExporter;
import org.tasks.injection.InjectingTestCase;
import org.tasks.injection.TestComponent;
import org.tasks.preferences.Preferences;
import org.tasks.scheduling.AlarmManager;
import java.io.File;
import java.io.IOException;
import javax.inject.Inject;
import static android.support.test.InstrumentationRegistry.getTargetContext;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.tasks.date.DateTimeUtils.newDateTime;
import static org.tasks.time.DateTimeUtils.currentTimeMillis;
@RunWith(AndroidJUnit4.class)
public class BackupServiceTests extends InjectingTestCase {
private static final long BACKUP_WAIT_TIME = 500L;
File temporaryDirectory = null;
@Inject TasksJsonExporter xmlExporter;
@Inject TaskDao taskDao;
@Inject Preferences preferences;
@Override
public void setUp() {
super.setUp();
private static final long BACKUP_WAIT_TIME = 500L;
try {
temporaryDirectory = File.createTempFile("backup", Long.toString(System.nanoTime()));
} catch (IOException e) {
throw new RuntimeException(e);
}
File temporaryDirectory = null;
if (!(temporaryDirectory.delete()))
throw new RuntimeException("Could not delete temp file: " + temporaryDirectory.getAbsolutePath());
if (!(temporaryDirectory.mkdir()))
throw new RuntimeException("Could not create temp directory: " + temporaryDirectory.getAbsolutePath());
@Inject TasksJsonExporter xmlExporter;
@Inject TaskDao taskDao;
@Inject Preferences preferences;
preferences.setString(R.string.p_backup_dir, temporaryDirectory.getAbsolutePath());
@Override
public void setUp() {
super.setUp();
// make a temporary task
Task task = new Task();
task.setTitle("helicopter");
taskDao.createNew(task);
try {
temporaryDirectory = File.createTempFile("backup", Long.toString(System.nanoTime()));
} catch (IOException e) {
throw new RuntimeException(e);
}
@Override
protected void inject(TestComponent component) {
component.inject(this);
if (!(temporaryDirectory.delete())) {
throw new RuntimeException(
"Could not delete temp file: " + temporaryDirectory.getAbsolutePath());
}
if (!(temporaryDirectory.mkdir())) {
throw new RuntimeException(
"Could not create temp directory: " + temporaryDirectory.getAbsolutePath());
}
@After
public void tearDown() {
if (temporaryDirectory != null) {
for (File file : temporaryDirectory.listFiles())
file.delete();
temporaryDirectory.delete();
}
preferences.setString(R.string.p_backup_dir, temporaryDirectory.getAbsolutePath());
// make a temporary task
Task task = new Task();
task.setTitle("helicopter");
taskDao.createNew(task);
}
@Override
protected void inject(TestComponent component) {
component.inject(this);
}
@After
public void tearDown() {
if (temporaryDirectory != null) {
for (File file : temporaryDirectory.listFiles()) {
file.delete();
}
temporaryDirectory.delete();
}
}
@Ignore
@Test
public void testBackup() {
assertEquals(0, temporaryDirectory.list().length);
@Ignore
@Test
public void testBackup() {
assertEquals(0, temporaryDirectory.list().length);
preferences.setLong(TasksJsonExporter.PREF_BACKUP_LAST_DATE, 0);
preferences.setLong(TasksJsonExporter.PREF_BACKUP_LAST_DATE, 0);
// create a backup
BackupJob service = new BackupJob(getTargetContext(), new JobManager(getTargetContext(), mock(AlarmManager.class)), xmlExporter, preferences);
service.startBackup(getTargetContext());
// create a backup
BackupJob service = new BackupJob(getTargetContext(),
new JobManager(getTargetContext(), mock(AlarmManager.class)), xmlExporter, preferences);
service.startBackup(getTargetContext());
AndroidUtilities.sleepDeep(BACKUP_WAIT_TIME);
AndroidUtilities.sleepDeep(BACKUP_WAIT_TIME);
// assert file created
File[] files = temporaryDirectory.listFiles();
assertEquals(1, files.length);
assertTrue(files[0].getName().matches(BackupJob.BACKUP_FILE_NAME_REGEX));
// assert file created
File[] files = temporaryDirectory.listFiles();
assertEquals(1, files.length);
assertTrue(files[0].getName().matches(BackupJob.BACKUP_FILE_NAME_REGEX));
// assert summary updated
assertTrue(preferences.getLong(TasksJsonExporter.PREF_BACKUP_LAST_DATE, 0) > 0);
}
// assert summary updated
assertTrue(preferences.getLong(TasksJsonExporter.PREF_BACKUP_LAST_DATE, 0) > 0);
}
@Test
public void testDeletion() throws IOException {
// create a bunch of backups
assertEquals(0, temporaryDirectory.list().length);
@Test
public void testDeletion() throws IOException {
// create a bunch of backups
assertEquals(0, temporaryDirectory.list().length);
// create some user files
File myFile = new File(temporaryDirectory, "beans");
myFile.createNewFile();
// create some user files
File myFile = new File(temporaryDirectory, "beans");
myFile.createNewFile();
// create some backup files
for (int i = 0; i < 10; i++) {
String name = String.format("auto.%02d%s.xml", i, newDateTime().toString("MMdd-HHmm"));
File tempFile = new File(temporaryDirectory, name);
tempFile.createNewFile();
}
// create some backup files
for (int i = 0; i < 10; i++) {
String name = String.format("auto.%02d%s.xml", i, newDateTime().toString("MMdd-HHmm"));
File tempFile = new File(temporaryDirectory, name);
tempFile.createNewFile();
}
// make one really old
File[] files = temporaryDirectory.listFiles();
files[4].setLastModified(currentTimeMillis() - 20000);
// make one really old
File[] files = temporaryDirectory.listFiles();
files[4].setLastModified(currentTimeMillis() - 20000);
// assert files created
assertEquals(11, files.length);
// assert files created
assertEquals(11, files.length);
// backup
BackupJob service = new BackupJob(getTargetContext(), new JobManager(getTargetContext(), mock(AlarmManager.class)), xmlExporter, preferences);
service.startBackup(getTargetContext());
// backup
BackupJob service = new BackupJob(getTargetContext(),
new JobManager(getTargetContext(), mock(AlarmManager.class)), xmlExporter, preferences);
service.startBackup(getTargetContext());
AndroidUtilities.sleepDeep(BACKUP_WAIT_TIME);
AndroidUtilities.sleepDeep(BACKUP_WAIT_TIME);
// assert the oldest file was deleted
assertTrue(temporaryDirectory.listFiles().length < 11);
assertFalse(files[4].exists());
// assert the oldest file was deleted
assertTrue(temporaryDirectory.listFiles().length < 11);
assertFalse(files[4].exists());
// assert user file still exists
service.startBackup(getTargetContext());
assertTrue(myFile.exists());
}
// assert user file still exists
service.startBackup(getTargetContext());
assertTrue(myFile.exists());
}
}

@ -1,20 +1,5 @@
package org.tasks.jobs;
import android.support.test.runner.AndroidJUnit4;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InOrder;
import org.tasks.Freeze;
import org.tasks.Snippet;
import org.tasks.preferences.Preferences;
import org.tasks.time.DateTime;
import java.util.List;
import java.util.concurrent.TimeUnit;
import static com.google.common.collect.Sets.newHashSet;
import static com.todoroo.astrid.reminders.ReminderService.TYPE_DUE;
import static com.todoroo.astrid.reminders.ReminderService.TYPE_SNOOZE;
@ -30,292 +15,306 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import static org.tasks.time.DateTimeUtils.currentTimeMillis;
import android.support.test.runner.AndroidJUnit4;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InOrder;
import org.tasks.Freeze;
import org.tasks.Snippet;
import org.tasks.preferences.Preferences;
import org.tasks.time.DateTime;
@RunWith(AndroidJUnit4.class)
public class JobQueueTest {
private static final long ONE_MINUTE = TimeUnit.MINUTES.toMillis(1);
private static final String TAG = NotificationJob.TAG;
private JobQueue queue;
private JobManager jobManager;
private Preferences preferences;
@Before
public void before() {
preferences = mock(Preferences.class);
when(preferences.adjustForQuietHours(anyLong())).then(returnsFirstArg());
jobManager = mock(JobManager.class);
queue = new JobQueue(preferences, jobManager);
}
@After
public void after() {
verifyNoMoreInteractions(jobManager);
}
@Test
public void alarmAndReminderSameTimeSameID() {
long now = currentTimeMillis();
queue.add(new Reminder(1, now, TYPE_DUE));
queue.add(new AlarmJob(1, 1, now));
private static final long ONE_MINUTE = TimeUnit.MINUTES.toMillis(1);
private static final String TAG = NotificationJob.TAG;
verify(jobManager).schedule(TAG, now);
private JobQueue queue;
private JobManager jobManager;
private Preferences preferences;
Freeze.freezeAt(now).thawAfter(new Snippet() {{
assertEquals(
newHashSet(new AlarmJob(1, 1, now),
new Reminder(1, now, TYPE_DUE)),
newHashSet(queue.getOverdueJobs()));
}});
}
@Before
public void before() {
preferences = mock(Preferences.class);
when(preferences.adjustForQuietHours(anyLong())).then(returnsFirstArg());
jobManager = mock(JobManager.class);
queue = new JobQueue(preferences, jobManager);
}
@Test
public void removeAlarmLeaveReminder() {
long now = currentTimeMillis();
@After
public void after() {
verifyNoMoreInteractions(jobManager);
}
queue.add(new Reminder(1, now, TYPE_DUE));
queue.add(new AlarmJob(1, 1, now));
@Test
public void alarmAndReminderSameTimeSameID() {
long now = currentTimeMillis();
verify(jobManager).schedule(TAG, now);
queue.add(new Reminder(1, now, TYPE_DUE));
queue.add(new AlarmJob(1, 1, now));
queue.remove(singletonList(new AlarmJob(1, 1, now)));
verify(jobManager).schedule(TAG, now);
Freeze.freezeAt(now).thawAfter(new Snippet() {{
assertEquals(
singletonList(new Reminder(1, now, TYPE_DUE)),
queue.getOverdueJobs());
}});
}
Freeze.freezeAt(now).thawAfter(new Snippet() {{
assertEquals(
newHashSet(new AlarmJob(1, 1, now),
new Reminder(1, now, TYPE_DUE)),
newHashSet(queue.getOverdueJobs()));
}});
}
@Test
public void removeReminderLeaveAlarm() {
long now = currentTimeMillis();
@Test
public void removeAlarmLeaveReminder() {
long now = currentTimeMillis();
queue.add(new Reminder(1, now, TYPE_DUE));
queue.add(new AlarmJob(1, 1, now));
queue.add(new Reminder(1, now, TYPE_DUE));
queue.add(new AlarmJob(1, 1, now));
verify(jobManager).schedule(TAG, now);
verify(jobManager).schedule(TAG, now);
queue.remove(singletonList(new Reminder(1, now, TYPE_DUE)));
queue.remove(singletonList(new AlarmJob(1, 1, now)));
Freeze.freezeAt(now).thawAfter(new Snippet() {{
assertEquals(
singletonList(new AlarmJob(1, 1, now)),
queue.getOverdueJobs());
}});
}
Freeze.freezeAt(now).thawAfter(new Snippet() {{
assertEquals(
singletonList(new Reminder(1, now, TYPE_DUE)),
queue.getOverdueJobs());
}});
}
@Test
public void twoJobsAtSameTime() {
queue.add(new Reminder(1, 1, 0));
queue.add(new Reminder(2, 1, 0));
@Test
public void removeReminderLeaveAlarm() {
long now = currentTimeMillis();
verify(jobManager).schedule(TAG, 1);
queue.add(new Reminder(1, now, TYPE_DUE));
queue.add(new AlarmJob(1, 1, now));
assertEquals(2, queue.size());
}
verify(jobManager).schedule(TAG, now);
@Test
public void rescheduleForFirstJob() {
queue.add(new Reminder(1, 1, 0));
queue.remove(singletonList(new Reminder(1, now, TYPE_DUE)));
verify(jobManager).schedule(TAG, 1);
}
Freeze.freezeAt(now).thawAfter(new Snippet() {{
assertEquals(
singletonList(new AlarmJob(1, 1, now)),
queue.getOverdueJobs());
}});
}
@Test
public void dontRescheduleForLaterJobs() {
queue.add(new Reminder(1, 1, 0));
queue.add(new Reminder(2, 2, 0));
@Test
public void twoJobsAtSameTime() {
queue.add(new Reminder(1, 1, 0));
queue.add(new Reminder(2, 1, 0));
verify(jobManager).schedule(TAG, 1);
}
verify(jobManager).schedule(TAG, 1);
@Test
public void rescheduleForNewerJob() {
queue.add(new Reminder(1, 2, 0));
queue.add(new Reminder(1, 1, 0));
assertEquals(2, queue.size());
}
InOrder order = inOrder(jobManager);
order.verify(jobManager).schedule(TAG, 2);
order.verify(jobManager).schedule(TAG, 1);
}
@Test
public void rescheduleForFirstJob() {
queue.add(new Reminder(1, 1, 0));
@Test
public void rescheduleWhenCancelingOnlyJob() {
queue.add(new Reminder(1, 2, 0));
queue.cancelReminder(1);
verify(jobManager).schedule(TAG, 1);
}
InOrder order = inOrder(jobManager);
order.verify(jobManager).schedule(TAG, 2);
order.verify(jobManager).cancel(TAG);
}
@Test
public void dontRescheduleForLaterJobs() {
queue.add(new Reminder(1, 1, 0));
queue.add(new Reminder(2, 2, 0));
@Test
public void rescheduleWhenCancelingFirstJob() {
queue.add(new Reminder(1, 1, 0));
queue.add(new Reminder(2, 2, 0));
verify(jobManager).schedule(TAG, 1);
}
queue.cancelReminder(1);
@Test
public void rescheduleForNewerJob() {
queue.add(new Reminder(1, 2, 0));
queue.add(new Reminder(1, 1, 0));
InOrder order = inOrder(jobManager);
order.verify(jobManager).schedule(TAG, 1);
order.verify(jobManager).schedule(TAG, 2);
}
InOrder order = inOrder(jobManager);
order.verify(jobManager).schedule(TAG, 2);
order.verify(jobManager).schedule(TAG, 1);
}
@Test
public void dontRescheduleWhenCancelingLaterJob() {
queue.add(new Reminder(1, 1, 0));
queue.add(new Reminder(2, 2, 0));
@Test
public void rescheduleWhenCancelingOnlyJob() {
queue.add(new Reminder(1, 2, 0));
queue.cancelReminder(1);
queue.cancelReminder(2);
InOrder order = inOrder(jobManager);
order.verify(jobManager).schedule(TAG, 2);
order.verify(jobManager).cancel(TAG);
}
verify(jobManager).schedule(TAG, 1);
}
@Test
public void rescheduleWhenCancelingFirstJob() {
queue.add(new Reminder(1, 1, 0));
queue.add(new Reminder(2, 2, 0));
@Test
public void nextScheduledTimeIsZeroWhenQueueIsEmpty() {
when(preferences.adjustForQuietHours(anyLong())).thenReturn(1234L);
queue.cancelReminder(1);
assertEquals(0, queue.nextScheduledTime());
}
InOrder order = inOrder(jobManager);
order.verify(jobManager).schedule(TAG, 1);
order.verify(jobManager).schedule(TAG, 2);
}
@Test
public void adjustNextScheduledTimeForQuietHours() {
when(preferences.adjustForQuietHours(anyLong())).thenReturn(1234L);
queue.add(new Reminder(1, 1, 1));
@Test
public void dontRescheduleWhenCancelingLaterJob() {
queue.add(new Reminder(1, 1, 0));
queue.add(new Reminder(2, 2, 0));
verify(jobManager).schedule(TAG, 1234);
}
queue.cancelReminder(2);
@Test
public void overdueJobsAreReturned() {
long now = currentTimeMillis();
verify(jobManager).schedule(TAG, 1);
}
queue.add(new Reminder(1, now, TYPE_DUE));
queue.add(new Reminder(2, now + ONE_MINUTE, TYPE_DUE));
@Test
public void nextScheduledTimeIsZeroWhenQueueIsEmpty() {
when(preferences.adjustForQuietHours(anyLong())).thenReturn(1234L);
verify(jobManager).schedule(TAG, now);
assertEquals(0, queue.nextScheduledTime());
}
Freeze.freezeAt(now).thawAfter(new Snippet() {{
assertEquals(
singletonList(new Reminder(1, now, TYPE_DUE)),
queue.getOverdueJobs());
}});
}
@Test
public void adjustNextScheduledTimeForQuietHours() {
when(preferences.adjustForQuietHours(anyLong())).thenReturn(1234L);
queue.add(new Reminder(1, 1, 1));
@Test
public void twoOverdueJobsAtSameTimeReturned() {
long now = currentTimeMillis();
verify(jobManager).schedule(TAG, 1234);
}
queue.add(new Reminder(1, now, TYPE_DUE));
queue.add(new Reminder(2, now, TYPE_DUE));
@Test
public void overdueJobsAreReturned() {
long now = currentTimeMillis();
verify(jobManager).schedule(TAG, now);
queue.add(new Reminder(1, now, TYPE_DUE));
queue.add(new Reminder(2, now + ONE_MINUTE, TYPE_DUE));
Freeze.freezeAt(now).thawAfter(new Snippet() {{
assertEquals(
asList(new Reminder(1, now, TYPE_DUE), new Reminder(2, now, TYPE_DUE)),
queue.getOverdueJobs());
}});
}
verify(jobManager).schedule(TAG, now);
@Test
public void twoOverdueJobsAtDifferentTimes() {
long now = currentTimeMillis();
queue.add(new Reminder(1, now, TYPE_DUE));
queue.add(new Reminder(2, now + ONE_MINUTE, TYPE_DUE));
verify(jobManager).schedule(TAG, now);
Freeze.freezeAt(now + 2 * ONE_MINUTE).thawAfter(new Snippet() {{
assertEquals(
asList(new Reminder(1, now, TYPE_DUE), new Reminder(2, now + ONE_MINUTE, TYPE_DUE)),
queue.getOverdueJobs());
}});
}
Freeze.freezeAt(now).thawAfter(new Snippet() {{
assertEquals(
singletonList(new Reminder(1, now, TYPE_DUE)),
queue.getOverdueJobs());
}});
}
@Test
public void overdueJobsAreRemoved() {
long now = currentTimeMillis();
@Test
public void twoOverdueJobsAtSameTimeReturned() {
long now = currentTimeMillis();
queue.add(new Reminder(1, now, TYPE_DUE));
queue.add(new Reminder(2, now + ONE_MINUTE, TYPE_DUE));
verify(jobManager).schedule(TAG, now);
queue.add(new Reminder(1, now, TYPE_DUE));
queue.add(new Reminder(2, now, TYPE_DUE));
Freeze.freezeAt(now).thawAfter(new Snippet() {{
queue.remove(queue.getOverdueJobs());
}});
verify(jobManager).schedule(TAG, now);
assertEquals(
singletonList(new Reminder(2, now + ONE_MINUTE, TYPE_DUE)),
queue.getJobs());
}
Freeze.freezeAt(now).thawAfter(new Snippet() {{
assertEquals(
asList(new Reminder(1, now, TYPE_DUE), new Reminder(2, now, TYPE_DUE)),
queue.getOverdueJobs());
}});
}
@Test
public void twoOverdueJobsAtDifferentTimes() {
long now = currentTimeMillis();
queue.add(new Reminder(1, now, TYPE_DUE));
queue.add(new Reminder(2, now + ONE_MINUTE, TYPE_DUE));
verify(jobManager).schedule(TAG, now);
Freeze.freezeAt(now + 2 * ONE_MINUTE).thawAfter(new Snippet() {{
assertEquals(
asList(new Reminder(1, now, TYPE_DUE), new Reminder(2, now + ONE_MINUTE, TYPE_DUE)),
queue.getOverdueJobs());
}});
}
@Test
public void overdueJobsAreRemoved() {
long now = currentTimeMillis();
queue.add(new Reminder(1, now, TYPE_DUE));
queue.add(new Reminder(2, now + ONE_MINUTE, TYPE_DUE));
verify(jobManager).schedule(TAG, now);
@Test
public void multipleOverduePeriodsLapsed() {
long now = currentTimeMillis();
Freeze.freezeAt(now).thawAfter(new Snippet() {{
queue.remove(queue.getOverdueJobs());
}});
queue.add(new Reminder(1, now, TYPE_DUE));
queue.add(new Reminder(2, now + ONE_MINUTE, TYPE_DUE));
queue.add(new Reminder(3, now + 2 * ONE_MINUTE, TYPE_DUE));
assertEquals(
singletonList(new Reminder(2, now + ONE_MINUTE, TYPE_DUE)),
queue.getJobs());
}
verify(jobManager).schedule(TAG, now);
@Test
public void multipleOverduePeriodsLapsed() {
long now = currentTimeMillis();
Freeze.freezeAt(now + ONE_MINUTE).thawAfter(new Snippet() {{
queue.remove(queue.getOverdueJobs());
}});
queue.add(new Reminder(1, now, TYPE_DUE));
queue.add(new Reminder(2, now + ONE_MINUTE, TYPE_DUE));
queue.add(new Reminder(3, now + 2 * ONE_MINUTE, TYPE_DUE));
assertEquals(
singletonList(new Reminder(3, now + 2 * ONE_MINUTE, TYPE_DUE)),
queue.getJobs());
}
verify(jobManager).schedule(TAG, now);
Freeze.freezeAt(now + ONE_MINUTE).thawAfter(new Snippet() {{
queue.remove(queue.getOverdueJobs());
}});
@Test
public void clearShouldCancelExisting() {
queue.add(new Reminder(1, 1, 0));
assertEquals(
singletonList(new Reminder(3, now + 2 * ONE_MINUTE, TYPE_DUE)),
queue.getJobs());
}
queue.clear();
InOrder order = inOrder(jobManager);
order.verify(jobManager).schedule(TAG, 1);
order.verify(jobManager).cancel(TAG);
assertEquals(0, queue.size());
}
@Test
public void clearShouldCancelExisting() {
queue.add(new Reminder(1, 1, 0));
@Test
public void ignoreInvalidCancel() {
long now = currentTimeMillis();
queue.add(new Reminder(1, now, TYPE_DUE));
queue.cancelReminder(2);
queue.clear();
InOrder order = inOrder(jobManager);
order.verify(jobManager).schedule(TAG, 1);
order.verify(jobManager).cancel(TAG);
assertEquals(0, queue.size());
}
verify(jobManager).schedule(TAG, now);
}
@Test
public void allDuringSameMinuteAreOverdue() {
DateTime now = new DateTime(2017, 9, 3, 0, 14, 6, 455);
DateTime due = new DateTime(2017, 9, 3, 0, 14, 0, 0);
DateTime snooze = new DateTime(2017, 9, 3, 0, 14, 59, 999);
queue.add(new Reminder(1, due.getMillis(), TYPE_DUE));
queue.add(new Reminder(2, snooze.getMillis(), TYPE_SNOOZE));
queue.add(new Reminder(3, due.plusMinutes(1).getMillis(), TYPE_DUE));
verify(jobManager).schedule(TAG, due.getMillis());
Freeze.freezeAt(now).thawAfter(new Snippet() {{
List<? extends JobQueueEntry> overdueJobs = queue.getOverdueJobs();
assertEquals(
asList(new Reminder(1, due.getMillis(), TYPE_DUE), new Reminder(2, snooze.getMillis(), TYPE_SNOOZE)),
overdueJobs);
queue.remove(overdueJobs);
assertEquals(
singletonList(new Reminder(3, due.plusMinutes(1).getMillis(), TYPE_DUE)),
queue.getJobs());
}});
}
@Test
public void ignoreInvalidCancel() {
long now = currentTimeMillis();
queue.add(new Reminder(1, now, TYPE_DUE));
queue.cancelReminder(2);
verify(jobManager).schedule(TAG, now);
}
@Test
public void allDuringSameMinuteAreOverdue() {
DateTime now = new DateTime(2017, 9, 3, 0, 14, 6, 455);
DateTime due = new DateTime(2017, 9, 3, 0, 14, 0, 0);
DateTime snooze = new DateTime(2017, 9, 3, 0, 14, 59, 999);
queue.add(new Reminder(1, due.getMillis(), TYPE_DUE));
queue.add(new Reminder(2, snooze.getMillis(), TYPE_SNOOZE));
queue.add(new Reminder(3, due.plusMinutes(1).getMillis(), TYPE_DUE));
verify(jobManager).schedule(TAG, due.getMillis());
Freeze.freezeAt(now).thawAfter(new Snippet() {{
List<? extends JobQueueEntry> overdueJobs = queue.getOverdueJobs();
assertEquals(
asList(new Reminder(1, due.getMillis(), TYPE_DUE),
new Reminder(2, snooze.getMillis(), TYPE_SNOOZE)),
overdueJobs);
queue.remove(overdueJobs);
assertEquals(
singletonList(new Reminder(3, due.plusMinutes(1).getMillis(), TYPE_DUE)),
queue.getJobs());
}});
}
}

@ -1,34 +1,33 @@
package org.tasks.makers;
import static com.natpryce.makeiteasy.Property.newProperty;
import static org.tasks.makers.Maker.make;
import com.natpryce.makeiteasy.Instantiator;
import com.natpryce.makeiteasy.Property;
import com.natpryce.makeiteasy.PropertyValue;
import org.tasks.data.GoogleTaskList;
import static com.natpryce.makeiteasy.Property.newProperty;
import static org.tasks.makers.Maker.make;
public class GtaskListMaker {
public static final Property<GoogleTaskList, Long> ID = newProperty();
public static final Property<GoogleTaskList, Integer> ORDER = newProperty();
public static final Property<GoogleTaskList, String> REMOTE_ID = newProperty();
public static final Property<GoogleTaskList, Long> LAST_SYNC = newProperty();
public static final Property<GoogleTaskList, String> NAME = newProperty();
public static final Property<GoogleTaskList, Integer> COLOR = newProperty();
public static GoogleTaskList newGtaskList(PropertyValue<? super GoogleTaskList, ?>... properties) {
return make(instantiator, properties);
}
public static final Property<GoogleTaskList, Long> ID = newProperty();
public static final Property<GoogleTaskList, Integer> ORDER = newProperty();
public static final Property<GoogleTaskList, String> REMOTE_ID = newProperty();
public static final Property<GoogleTaskList, Long> LAST_SYNC = newProperty();
public static final Property<GoogleTaskList, String> NAME = newProperty();
public static final Property<GoogleTaskList, Integer> COLOR = newProperty();
private static final Instantiator<GoogleTaskList> instantiator = lookup -> new GoogleTaskList() {{
setDeleted(0L);
setId(lookup.valueOf(GtaskListMaker.ID, 0L));
setRemoteId(lookup.valueOf(REMOTE_ID, "1"));
setTitle(lookup.valueOf(NAME, "Default"));
setRemoteOrder(lookup.valueOf(ORDER, 0));
setLastSync(lookup.valueOf(LAST_SYNC, 0L));
setColor(lookup.valueOf(COLOR, -1));
}};
private static final Instantiator<GoogleTaskList> instantiator = lookup -> new GoogleTaskList() {{
setDeleted(0L);
setId(lookup.valueOf(GtaskListMaker.ID, 0L));
setRemoteId(lookup.valueOf(REMOTE_ID, "1"));
setTitle(lookup.valueOf(NAME, "Default"));
setRemoteOrder(lookup.valueOf(ORDER, 0));
setLastSync(lookup.valueOf(LAST_SYNC, 0L));
setColor(lookup.valueOf(COLOR, -1));
}};
public static GoogleTaskList newGtaskList(
PropertyValue<? super GoogleTaskList, ?>... properties) {
return make(instantiator, properties);
}
}

@ -1,13 +1,15 @@
package org.tasks.makers;
import static com.natpryce.makeiteasy.MakeItEasy.a;
import com.natpryce.makeiteasy.Instantiator;
import com.natpryce.makeiteasy.PropertyValue;
import static com.natpryce.makeiteasy.MakeItEasy.a;
public class Maker {
@SuppressWarnings("unchecked")
public static <T> T make(Instantiator<T> instantiator, PropertyValue<? super T, ?>... properties) {
return com.natpryce.makeiteasy.MakeItEasy.make(a(instantiator, properties));
}
@SuppressWarnings("unchecked")
public static <T> T make(Instantiator<T> instantiator,
PropertyValue<? super T, ?>... properties) {
return com.natpryce.makeiteasy.MakeItEasy.make(a(instantiator, properties));
}
}

@ -1,112 +1,110 @@
package org.tasks.makers;
import static com.natpryce.makeiteasy.Property.newProperty;
import static org.tasks.date.DateTimeUtils.newDateTime;
import static org.tasks.makers.Maker.make;
import com.google.common.base.Strings;
import com.google.ical.values.RRule;
import com.natpryce.makeiteasy.Instantiator;
import com.natpryce.makeiteasy.Property;
import com.natpryce.makeiteasy.PropertyValue;
import com.todoroo.astrid.data.Task;
import org.tasks.time.DateTime;
import static com.natpryce.makeiteasy.Property.newProperty;
import static org.tasks.date.DateTimeUtils.newDateTime;
import static org.tasks.makers.Maker.make;
public class TaskMaker {
public static Property<Task, Long> ID = newProperty();
public static Property<Task, String> TITLE = newProperty();
public static Property<Task, DateTime> DUE_DATE = newProperty();
public static Property<Task, DateTime> DUE_TIME = newProperty();
public static Property<Task, Integer> PRIORITY = newProperty();
public static Property<Task, DateTime> REMINDER_LAST = newProperty();
public static Property<Task, Long> RANDOM_REMINDER_PERIOD = newProperty();
public static Property<Task, Integer> HIDE_TYPE = newProperty();
public static Property<Task, Integer> REMINDERS = newProperty();
public static Property<Task, DateTime> CREATION_TIME = newProperty();
public static Property<Task, DateTime> COMPLETION_TIME = newProperty();
public static Property<Task, DateTime> DELETION_TIME = newProperty();
public static Property<Task, DateTime> SNOOZE_TIME = newProperty();
public static Property<Task, RRule> RRULE = newProperty();
public static Property<Task, Boolean> AFTER_COMPLETE = newProperty();
@SafeVarargs
public static Task newTask(PropertyValue<? super Task, ?>... properties) {
return make(instantiator, properties);
public static Property<Task, Long> ID = newProperty();
public static Property<Task, String> TITLE = newProperty();
public static Property<Task, DateTime> DUE_DATE = newProperty();
public static Property<Task, DateTime> DUE_TIME = newProperty();
public static Property<Task, Integer> PRIORITY = newProperty();
public static Property<Task, DateTime> REMINDER_LAST = newProperty();
public static Property<Task, Long> RANDOM_REMINDER_PERIOD = newProperty();
public static Property<Task, Integer> HIDE_TYPE = newProperty();
public static Property<Task, Integer> REMINDERS = newProperty();
public static Property<Task, DateTime> CREATION_TIME = newProperty();
public static Property<Task, DateTime> COMPLETION_TIME = newProperty();
public static Property<Task, DateTime> DELETION_TIME = newProperty();
public static Property<Task, DateTime> SNOOZE_TIME = newProperty();
public static Property<Task, RRule> RRULE = newProperty();
public static Property<Task, Boolean> AFTER_COMPLETE = newProperty();
private static final Instantiator<Task> instantiator = lookup -> {
Task task = new Task();
String title = lookup.valueOf(TITLE, (String) null);
if (!Strings.isNullOrEmpty(title)) {
task.setTitle(title);
}
private static final Instantiator<Task> instantiator = lookup -> {
Task task = new Task();
String title = lookup.valueOf(TITLE, (String) null);
if (!Strings.isNullOrEmpty(title)) {
task.setTitle(title);
}
long id = lookup.valueOf(ID, Task.NO_ID);
if (id != Task.NO_ID) {
task.setId(id);
}
int priority = lookup.valueOf(PRIORITY, -1);
if (priority >= 0) {
task.setImportance(priority);
}
DateTime dueDate = lookup.valueOf(DUE_DATE, (DateTime) null);
if (dueDate != null) {
task.setDueDate(Task.createDueDate(Task.URGENCY_SPECIFIC_DAY, dueDate.getMillis()));
}
DateTime dueTime = lookup.valueOf(DUE_TIME, (DateTime) null);
if (dueTime != null) {
task.setDueDate(Task.createDueDate(Task.URGENCY_SPECIFIC_DAY_TIME, dueTime.getMillis()));
}
DateTime completionTime = lookup.valueOf(COMPLETION_TIME, (DateTime) null);
if (completionTime != null) {
task.setCompletionDate(completionTime.getMillis());
}
DateTime deletedTime = lookup.valueOf(DELETION_TIME, (DateTime) null);
if (deletedTime != null) {
task.setDeletionDate(deletedTime.getMillis());
}
DateTime snoozeTime = lookup.valueOf(SNOOZE_TIME, (DateTime) null);
if (snoozeTime != null) {
task.setReminderSnooze(snoozeTime.getMillis());
}
int hideType = lookup.valueOf(HIDE_TYPE, -1);
if (hideType >= 0) {
task.setHideUntil(task.createHideUntil(hideType, 0));
}
int reminderFlags = lookup.valueOf(REMINDERS, -1);
if (reminderFlags >= 0) {
task.setReminderFlags(reminderFlags);
}
DateTime reminderLast = lookup.valueOf(REMINDER_LAST, (DateTime) null);
if (reminderLast != null) {
task.setReminderLast(reminderLast.getMillis());
}
long randomReminderPeriod = lookup.valueOf(RANDOM_REMINDER_PERIOD, 0L);
if (randomReminderPeriod > 0) {
task.setReminderPeriod(randomReminderPeriod);
}
RRule rrule = lookup.valueOf(RRULE, (RRule) null);
if (rrule != null) {
task.setRecurrence(rrule, lookup.valueOf(AFTER_COMPLETE, false));
}
DateTime creationTime = lookup.valueOf(CREATION_TIME, newDateTime());
task.setCreationDate(creationTime.getMillis());
return task;
};
long id = lookup.valueOf(ID, Task.NO_ID);
if (id != Task.NO_ID) {
task.setId(id);
}
int priority = lookup.valueOf(PRIORITY, -1);
if (priority >= 0) {
task.setImportance(priority);
}
DateTime dueDate = lookup.valueOf(DUE_DATE, (DateTime) null);
if (dueDate != null) {
task.setDueDate(Task.createDueDate(Task.URGENCY_SPECIFIC_DAY, dueDate.getMillis()));
}
DateTime dueTime = lookup.valueOf(DUE_TIME, (DateTime) null);
if (dueTime != null) {
task.setDueDate(Task.createDueDate(Task.URGENCY_SPECIFIC_DAY_TIME, dueTime.getMillis()));
}
DateTime completionTime = lookup.valueOf(COMPLETION_TIME, (DateTime) null);
if (completionTime != null) {
task.setCompletionDate(completionTime.getMillis());
}
DateTime deletedTime = lookup.valueOf(DELETION_TIME, (DateTime) null);
if (deletedTime != null) {
task.setDeletionDate(deletedTime.getMillis());
}
DateTime snoozeTime = lookup.valueOf(SNOOZE_TIME, (DateTime) null);
if (snoozeTime != null) {
task.setReminderSnooze(snoozeTime.getMillis());
}
int hideType = lookup.valueOf(HIDE_TYPE, -1);
if (hideType >= 0) {
task.setHideUntil(task.createHideUntil(hideType, 0));
}
int reminderFlags = lookup.valueOf(REMINDERS, -1);
if (reminderFlags >= 0) {
task.setReminderFlags(reminderFlags);
}
DateTime reminderLast = lookup.valueOf(REMINDER_LAST, (DateTime) null);
if (reminderLast != null) {
task.setReminderLast(reminderLast.getMillis());
}
long randomReminderPeriod = lookup.valueOf(RANDOM_REMINDER_PERIOD, 0L);
if (randomReminderPeriod > 0) {
task.setReminderPeriod(randomReminderPeriod);
}
RRule rrule = lookup.valueOf(RRULE, (RRule) null);
if (rrule != null) {
task.setRecurrence(rrule, lookup.valueOf(AFTER_COMPLETE, false));
}
DateTime creationTime = lookup.valueOf(CREATION_TIME, newDateTime());
task.setCreationDate(creationTime.getMillis());
return task;
};
@SafeVarargs
public static Task newTask(PropertyValue<? super Task, ?>... properties) {
return make(instantiator, properties);
}
}

@ -1,41 +1,41 @@
package org.tasks.preferences;
import android.content.Context;
import org.tasks.injection.ForApplication;
public class PermissivePermissionChecker extends PermissionChecker {
public PermissivePermissionChecker(@ForApplication Context context) {
super(context);
}
@Override
public boolean canAccessCalendars() {
return true;
}
@Override
public boolean canWriteToExternalStorage() {
return true;
}
@Override
public boolean canAccessAccounts() {
return true;
}
@Override
public boolean canAccessLocation() {
return true;
}
@Override
public boolean canAccessMic() {
return true;
}
@Override
public boolean canAccessMissedCallPermissions() {
return true;
}
public PermissivePermissionChecker(@ForApplication Context context) {
super(context);
}
@Override
public boolean canAccessCalendars() {
return true;
}
@Override
public boolean canWriteToExternalStorage() {
return true;
}
@Override
public boolean canAccessAccounts() {
return true;
}
@Override
public boolean canAccessLocation() {
return true;
}
@Override
public boolean canAccessMic() {
return true;
}
@Override
public boolean canAccessMissedCallPermissions() {
return true;
}
}

@ -1,154 +1,152 @@
package org.tasks.preferences;
import static android.support.test.InstrumentationRegistry.getTargetContext;
import static junit.framework.Assert.assertEquals;
import android.annotation.SuppressLint;
import android.support.test.runner.AndroidJUnit4;
import java.util.concurrent.TimeUnit;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.tasks.R;
import org.tasks.time.DateTime;
import java.util.concurrent.TimeUnit;
import static android.support.test.InstrumentationRegistry.getTargetContext;
import static junit.framework.Assert.assertEquals;
@RunWith(AndroidJUnit4.class)
public class PreferenceTests {
@SuppressLint("NewApi")
private static final int MILLIS_PER_HOUR = (int) TimeUnit.HOURS.toMillis(1);
@SuppressLint("NewApi")
private static final int MILLIS_PER_HOUR = (int) TimeUnit.HOURS.toMillis(1);
private Preferences preferences;
private Preferences preferences;
@Before
public void setUp() {
preferences = new Preferences(getTargetContext(), null);
preferences.clear();
preferences.setBoolean(R.string.p_rmd_enable_quiet, true);
}
@Before
public void setUp() {
preferences = new Preferences(getTargetContext(), null);
preferences.clear();
preferences.setBoolean(R.string.p_rmd_enable_quiet, true);
}
@Test
public void testNotQuietWhenQuietHoursDisabled() {
preferences.setBoolean(R.string.p_rmd_enable_quiet, false);
setQuietHoursStart(22);
setQuietHoursEnd(10);
@Test
public void testNotQuietWhenQuietHoursDisabled() {
preferences.setBoolean(R.string.p_rmd_enable_quiet, false);
setQuietHoursStart(22);
setQuietHoursEnd(10);
long dueDate = new DateTime(2015, 12, 29, 8, 0, 1).getMillis();
long dueDate = new DateTime(2015, 12, 29, 8, 0, 1).getMillis();
assertEquals(dueDate, preferences.adjustForQuietHours(dueDate));
}
assertEquals(dueDate, preferences.adjustForQuietHours(dueDate));
}
@Test
public void testIsQuietAtStartOfQuietHoursNoWrap() {
setQuietHoursStart(18);
setQuietHoursEnd(19);
@Test
public void testIsQuietAtStartOfQuietHoursNoWrap() {
setQuietHoursStart(18);
setQuietHoursEnd(19);
long dueDate = new DateTime(2015, 12, 29, 18, 0, 1).getMillis();
long dueDate = new DateTime(2015, 12, 29, 18, 0, 1).getMillis();
assertEquals(new DateTime(2015, 12, 29, 19, 0).getMillis(),
preferences.adjustForQuietHours(dueDate));
}
assertEquals(new DateTime(2015, 12, 29, 19, 0).getMillis(),
preferences.adjustForQuietHours(dueDate));
}
@Test
public void testIsQuietAtStartOfQuietHoursWrap() {
setQuietHoursStart(22);
setQuietHoursEnd(10);
@Test
public void testIsQuietAtStartOfQuietHoursWrap() {
setQuietHoursStart(22);
setQuietHoursEnd(10);
long dueDate = new DateTime(2015, 12, 29, 22, 0, 1).getMillis();
long dueDate = new DateTime(2015, 12, 29, 22, 0, 1).getMillis();
assertEquals(new DateTime(2015, 12, 30, 10, 0).getMillis(),
preferences.adjustForQuietHours(dueDate));
}
assertEquals(new DateTime(2015, 12, 30, 10, 0).getMillis(),
preferences.adjustForQuietHours(dueDate));
}
@Test
public void testAdjustForQuietHoursNightWrap() {
setQuietHoursStart(22);
setQuietHoursEnd(10);
@Test
public void testAdjustForQuietHoursNightWrap() {
setQuietHoursStart(22);
setQuietHoursEnd(10);
long dueDate = new DateTime(2015, 12, 29, 23, 30).getMillis();
long dueDate = new DateTime(2015, 12, 29, 23, 30).getMillis();
assertEquals(new DateTime(2015, 12, 30, 10, 0).getMillis(),
preferences.adjustForQuietHours(dueDate));
}
assertEquals(new DateTime(2015, 12, 30, 10, 0).getMillis(),
preferences.adjustForQuietHours(dueDate));
}
@Test
public void testAdjustForQuietHoursMorningWrap() {
setQuietHoursStart(22);
setQuietHoursEnd(10);
@Test
public void testAdjustForQuietHoursMorningWrap() {
setQuietHoursStart(22);
setQuietHoursEnd(10);
long dueDate = new DateTime(2015, 12, 30, 7, 15).getMillis();
long dueDate = new DateTime(2015, 12, 30, 7, 15).getMillis();
assertEquals(new DateTime(2015, 12, 30, 10, 0).getMillis(),
preferences.adjustForQuietHours(dueDate));
}
assertEquals(new DateTime(2015, 12, 30, 10, 0).getMillis(),
preferences.adjustForQuietHours(dueDate));
}
@Test
public void testAdjustForQuietHoursWhenStartAndEndAreSame() {
setQuietHoursStart(18);
setQuietHoursEnd(18);
@Test
public void testAdjustForQuietHoursWhenStartAndEndAreSame() {
setQuietHoursStart(18);
setQuietHoursEnd(18);
long dueDate = new DateTime(2015, 12, 29, 18, 0, 0).getMillis();
long dueDate = new DateTime(2015, 12, 29, 18, 0, 0).getMillis();
assertEquals(dueDate, preferences.adjustForQuietHours(dueDate));
}
assertEquals(dueDate, preferences.adjustForQuietHours(dueDate));
}
@Test
public void testIsNotQuietAtEndOfQuietHoursNoWrap() {
setQuietHoursStart(17);
setQuietHoursEnd(18);
@Test
public void testIsNotQuietAtEndOfQuietHoursNoWrap() {
setQuietHoursStart(17);
setQuietHoursEnd(18);
long dueDate = new DateTime(2015, 12, 29, 18, 0).getMillis();
long dueDate = new DateTime(2015, 12, 29, 18, 0).getMillis();
assertEquals(dueDate, preferences.adjustForQuietHours(dueDate));
}
assertEquals(dueDate, preferences.adjustForQuietHours(dueDate));
}
@Test
public void testIsNotQuietAtEndOfQuietHoursWrap() {
setQuietHoursStart(22);
setQuietHoursEnd(10);
@Test
public void testIsNotQuietAtEndOfQuietHoursWrap() {
setQuietHoursStart(22);
setQuietHoursEnd(10);
long dueDate = new DateTime(2015, 12, 29, 10, 0).getMillis();
long dueDate = new DateTime(2015, 12, 29, 10, 0).getMillis();
assertEquals(dueDate, preferences.adjustForQuietHours(dueDate));
}
assertEquals(dueDate, preferences.adjustForQuietHours(dueDate));
}
@Test
public void testIsNotQuietBeforeNoWrap() {
setQuietHoursStart(17);
setQuietHoursEnd(18);
@Test
public void testIsNotQuietBeforeNoWrap() {
setQuietHoursStart(17);
setQuietHoursEnd(18);
long dueDate = new DateTime(2015, 12, 29, 11, 30).getMillis();
long dueDate = new DateTime(2015, 12, 29, 11, 30).getMillis();
assertEquals(dueDate, preferences.adjustForQuietHours(dueDate));
}
assertEquals(dueDate, preferences.adjustForQuietHours(dueDate));
}
@Test
public void testIsNotQuietAfterNoWrap() {
setQuietHoursStart(17);
setQuietHoursEnd(18);
@Test
public void testIsNotQuietAfterNoWrap() {
setQuietHoursStart(17);
setQuietHoursEnd(18);
long dueDate = new DateTime(2015, 12, 29, 22, 15).getMillis();
long dueDate = new DateTime(2015, 12, 29, 22, 15).getMillis();
assertEquals(dueDate, preferences.adjustForQuietHours(dueDate));
}
assertEquals(dueDate, preferences.adjustForQuietHours(dueDate));
}
@Test
public void testIsNotQuietWrap() {
setQuietHoursStart(22);
setQuietHoursEnd(10);
@Test
public void testIsNotQuietWrap() {
setQuietHoursStart(22);
setQuietHoursEnd(10);
long dueDate = new DateTime(2015, 12, 29, 13, 45).getMillis();
long dueDate = new DateTime(2015, 12, 29, 13, 45).getMillis();
assertEquals(dueDate, preferences.adjustForQuietHours(dueDate));
}
assertEquals(dueDate, preferences.adjustForQuietHours(dueDate));
}
private void setQuietHoursStart(int hour) {
preferences.setInt(R.string.p_rmd_quietStart, hour * MILLIS_PER_HOUR);
}
private void setQuietHoursStart(int hour) {
preferences.setInt(R.string.p_rmd_quietStart, hour * MILLIS_PER_HOUR);
}
private void setQuietHoursEnd(int hour) {
preferences.setInt(R.string.p_rmd_quietEnd, hour * MILLIS_PER_HOUR);
}
private void setQuietHoursEnd(int hour) {
preferences.setInt(R.string.p_rmd_quietEnd, hour * MILLIS_PER_HOUR);
}
}

@ -1,57 +1,57 @@
package org.tasks.repeats;
import android.support.test.runner.AndroidJUnit4;
import static android.support.test.InstrumentationRegistry.getTargetContext;
import static junit.framework.Assert.assertEquals;
import android.support.test.runner.AndroidJUnit4;
import com.google.ical.values.RRule;
import java.text.ParseException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.tasks.locale.Locale;
import java.text.ParseException;
import static android.support.test.InstrumentationRegistry.getTargetContext;
import static junit.framework.Assert.assertEquals;
@RunWith(AndroidJUnit4.class)
public class RepeatRuleToStringTest {
@Test
public void weekly() {
assertEquals("Repeats weekly", toString("RRULE:FREQ=WEEKLY;INTERVAL=1"));
}
@Test
public void weeklyPlural() {
assertEquals("Repeats every 2 weeks", toString("RRULE:FREQ=WEEKLY;INTERVAL=2"));
}
@Test
public void weeklyByDay() {
assertEquals("Repeats weekly on Mon, Tue, Wed, Thu, Fri", toString("RRULE:FREQ=WEEKLY;INTERVAL=1;BYDAY=MO,TU,WE,TH,FR"));
}
@Test
public void printDaysInRepeatRuleOrder() {
assertEquals("Repeats weekly on Fri, Thu, Wed, Tue, Mon", toString("RRULE:FREQ=WEEKLY;INTERVAL=1;BYDAY=FR,TH,WE,TU,MO"));
}
@Test
public void useLocaleForDays() {
assertEquals("Wiederhole wöchentlich am Sa., So.", toString("de", "RRULE:FREQ=WEEKLY;INTERVAL=1;BYDAY=SA,SU"));
}
private String toString(String rrule) {
return toString(null, rrule);
}
private String toString(String language, String rrule) {
try {
Locale locale = new Locale(java.util.Locale.getDefault(), language, -1);
return new RepeatRuleToString(locale.createConfigurationContext(getTargetContext()), locale)
.toString(new RRule(rrule));
} catch (ParseException e) {
throw new RuntimeException(e);
}
@Test
public void weekly() {
assertEquals("Repeats weekly", toString("RRULE:FREQ=WEEKLY;INTERVAL=1"));
}
@Test
public void weeklyPlural() {
assertEquals("Repeats every 2 weeks", toString("RRULE:FREQ=WEEKLY;INTERVAL=2"));
}
@Test
public void weeklyByDay() {
assertEquals("Repeats weekly on Mon, Tue, Wed, Thu, Fri",
toString("RRULE:FREQ=WEEKLY;INTERVAL=1;BYDAY=MO,TU,WE,TH,FR"));
}
@Test
public void printDaysInRepeatRuleOrder() {
assertEquals("Repeats weekly on Fri, Thu, Wed, Tue, Mon",
toString("RRULE:FREQ=WEEKLY;INTERVAL=1;BYDAY=FR,TH,WE,TU,MO"));
}
@Test
public void useLocaleForDays() {
assertEquals("Wiederhole wöchentlich am Sa., So.",
toString("de", "RRULE:FREQ=WEEKLY;INTERVAL=1;BYDAY=SA,SU"));
}
private String toString(String rrule) {
return toString(null, rrule);
}
private String toString(String language, String rrule) {
try {
Locale locale = new Locale(java.util.Locale.getDefault(), language, -1);
return new RepeatRuleToString(locale.createConfigurationContext(getTargetContext()), locale)
.toString(new RRule(rrule));
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
}

@ -1,337 +1,356 @@
package org.tasks.time;
import android.support.test.runner.AndroidJUnit4;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import android.support.test.runner.AndroidJUnit4;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.tasks.Freeze;
import org.tasks.Snippet;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
@RunWith(AndroidJUnit4.class)
public class DateTimeTest {
@Test
public void testGetMillisOfDay() {
assertEquals(7248412, new DateTime(2015, 10, 6, 2, 0, 48, 412).getMillisOfDay());
}
@Test
public void testWithMillisOfDay() {
assertEquals(
new DateTime(2015, 10, 6, 2, 0, 48, 412),
new DateTime(2015, 10, 6, 0, 0, 0, 0).withMillisOfDay(7248412));
}
@Test
public void testWithMillisOfDayDuringDST() {
TimeZone def = TimeZone.getDefault();
try {
TimeZone.setDefault(TimeZone.getTimeZone("America/Chicago"));
assertEquals(2, new DateTime(2015, 10, 31, 2, 0, 0).withMillisOfDay((int) TimeUnit.HOURS.toMillis(2)).getHourOfDay());
} finally {
TimeZone.setDefault(def);
}
}
@Test
public void testWithMillisOfDayAfterDST() {
TimeZone def = TimeZone.getDefault();
try {
TimeZone.setDefault(TimeZone.getTimeZone("America/Chicago"));
assertEquals(2, new DateTime(2015, 11, 2, 2, 0, 0).withMillisOfDay((int) TimeUnit.HOURS.toMillis(2)).getHourOfDay());
} finally {
TimeZone.setDefault(def);
}
}
@Test
public void testWithMillisOfDayStartDST() {
TimeZone def = TimeZone.getDefault();
try {
TimeZone.setDefault(TimeZone.getTimeZone("America/Chicago"));
assertEquals(1, new DateTime(2015, 3, 8, 0, 0, 0).withMillisOfDay((int) TimeUnit.HOURS.toMillis(1)).getHourOfDay());
assertEquals(3, new DateTime(2015, 3, 8, 0, 0, 0).withMillisOfDay((int) TimeUnit.HOURS.toMillis(2)).getHourOfDay());
assertEquals(3, new DateTime(2015, 3, 8, 0, 0, 0).withMillisOfDay((int) TimeUnit.HOURS.toMillis(3)).getHourOfDay());
assertEquals(4, new DateTime(2015, 3, 8, 0, 0, 0).withMillisOfDay((int) TimeUnit.HOURS.toMillis(4)).getHourOfDay());
assertEquals(
new DateTime(2015, 3, 8, 0, 0, 0).withMillisOfDay((int) TimeUnit.HOURS.toMillis(2)).getMillis(),
new DateTime(2015, 3, 8, 0, 0, 0).withMillisOfDay((int) TimeUnit.HOURS.toMillis(3)).getMillis());
} finally {
TimeZone.setDefault(def);
}
}
@Test
public void testWithMillisOfDayEndDST() {
TimeZone def = TimeZone.getDefault();
try {
TimeZone.setDefault(TimeZone.getTimeZone("America/Chicago"));
assertEquals(1, new DateTime(2015, 11, 1, 0, 0, 0).withMillisOfDay((int) TimeUnit.HOURS.toMillis(1)).getHourOfDay());
assertEquals(2, new DateTime(2015, 11, 1, 0, 0, 0).withMillisOfDay((int) TimeUnit.HOURS.toMillis(2)).getHourOfDay());
assertEquals(3, new DateTime(2015, 11, 1, 0, 0, 0).withMillisOfDay((int) TimeUnit.HOURS.toMillis(3)).getHourOfDay());
} finally {
TimeZone.setDefault(def);
}
}
@Test
public void testPlusMonths() {
assertEquals(
new DateTime(2015, 11, 6, 2, 0, 48, 412),
new DateTime(2015, 10, 6, 2, 0, 48, 412).plusMonths(1));
}
@Test
public void testPlusMonthsWrapYear() {
assertEquals(
new DateTime(2016, 1, 6, 2, 0, 48, 412),
new DateTime(2015, 10, 6, 2, 0, 48, 412).plusMonths(3));
}
@Test
public void testGetDayOfMonth() {
assertEquals(5, new DateTime(2015, 10, 5, 0, 0, 0).getDayOfMonth());
}
@Test
public void testPlusDays() {
assertEquals(
new DateTime(2015, 10, 6, 2, 0, 48, 412),
new DateTime(2015, 10, 5, 2, 0, 48, 412).plusDays(1));
}
@Test
public void testPlusDaysWrapMonth() {
assertEquals(
new DateTime(2015, 11, 1, 2, 0, 48, 412),
new DateTime(2015, 10, 31, 2, 0, 48, 412).plusDays(1));
}
@Test
public void testMinuteOfHour() {
assertEquals(43, new DateTime(2015, 10, 5, 2, 43, 48).getMinuteOfHour());
}
@Test
public void testIsEndOfMonth() {
assertTrue(new DateTime(2014, 1, 31, 0, 0, 0).isLastDayOfMonth());
assertTrue(new DateTime(2014, 2, 28, 0, 0, 0).isLastDayOfMonth());
assertTrue(new DateTime(2014, 3, 31, 0, 0, 0).isLastDayOfMonth());
assertTrue(new DateTime(2014, 4, 30, 0, 0, 0).isLastDayOfMonth());
assertTrue(new DateTime(2014, 5, 31, 0, 0, 0).isLastDayOfMonth());
assertTrue(new DateTime(2014, 6, 30, 0, 0, 0).isLastDayOfMonth());
assertTrue(new DateTime(2014, 7, 31, 0, 0, 0).isLastDayOfMonth());
assertTrue(new DateTime(2014, 8, 31, 0, 0, 0).isLastDayOfMonth());
assertTrue(new DateTime(2014, 9, 30, 0, 0, 0).isLastDayOfMonth());
assertTrue(new DateTime(2014, 10, 31, 0, 0, 0).isLastDayOfMonth());
assertTrue(new DateTime(2014, 11, 30, 0, 0, 0).isLastDayOfMonth());
assertTrue(new DateTime(2014, 12, 31, 0, 0, 0).isLastDayOfMonth());
}
@Test
public void testNotTheEndOfTheMonth() {
for (int month = 1; month <= 12; month++) {
int lastDay = new DateTime(2014, month, 1, 0, 0, 0, 0).getNumberOfDaysInMonth();
for (int day = 1; day < lastDay; day++) {
assertFalse(new DateTime(2014, month, day, 0, 0, 0).isLastDayOfMonth());
}
}
}
@Test
public void testCheckEndOfMonthDuringLeapYear() {
assertFalse(new DateTime(2016, 2, 28, 0, 0, 0).isLastDayOfMonth());
assertTrue(new DateTime(2016, 2, 29, 0, 0, 0).isLastDayOfMonth());
}
@Test
public void testNumberOfDaysInMonth() {
assertEquals(31, new DateTime(2015, 1, 5, 9, 45, 34).getNumberOfDaysInMonth());
assertEquals(28, new DateTime(2015, 2, 5, 9, 45, 34).getNumberOfDaysInMonth());
assertEquals(31, new DateTime(2015, 3, 5, 9, 45, 34).getNumberOfDaysInMonth());
assertEquals(30, new DateTime(2015, 4, 5, 9, 45, 34).getNumberOfDaysInMonth());
assertEquals(31, new DateTime(2015, 5, 5, 9, 45, 34).getNumberOfDaysInMonth());
assertEquals(30, new DateTime(2015, 6, 5, 9, 45, 34).getNumberOfDaysInMonth());
assertEquals(31, new DateTime(2015, 7, 5, 9, 45, 34).getNumberOfDaysInMonth());
assertEquals(31, new DateTime(2015, 8, 5, 9, 45, 34).getNumberOfDaysInMonth());
assertEquals(30, new DateTime(2015, 9, 5, 9, 45, 34).getNumberOfDaysInMonth());
assertEquals(31, new DateTime(2015, 10, 5, 9, 45, 34).getNumberOfDaysInMonth());
assertEquals(30, new DateTime(2015, 11, 5, 9, 45, 34).getNumberOfDaysInMonth());
assertEquals(31, new DateTime(2015, 12, 5, 9, 45, 34).getNumberOfDaysInMonth());
}
@Test
public void testWithMillisOfSecond() {
assertEquals(
new DateTime(2015, 11, 6, 13, 34, 56, 453),
new DateTime(2015, 11, 6, 13, 34, 56, 0).withMillisOfSecond(453));
}
@Test
public void testWithHourOfDay() {
assertEquals(
new DateTime(2015, 11, 6, 23, 0, 0),
new DateTime(2015, 11, 6, 1, 0, 0).withHourOfDay(23));
}
@Test
public void testWithMinuteOfHour() {
assertEquals(
new DateTime(2015, 11, 6, 23, 13, 0),
new DateTime(2015, 11, 6, 23, 1, 0).withMinuteOfHour(13));
}
@Test
public void testWithSecondOfMinute() {
assertEquals(
new DateTime(2015, 11, 6, 23, 13, 56),
new DateTime(2015, 11, 6, 23, 13, 1).withSecondOfMinute(56));
}
@Test
public void testGetYear() {
assertEquals(2015, new DateTime(2015, 1, 1, 1, 1, 1).getYear());
}
@Test
public void testMinusMinutes() {
assertEquals(
new DateTime(2015, 11, 4, 23, 59, 0),
new DateTime(2015, 11, 5, 0, 1, 0).minusMinutes(2));
}
@Test
public void testIsBefore() {
assertTrue(new DateTime(2015, 11, 4, 23, 59, 0)
.isBefore(new DateTime(2015, 11, 4, 23, 59, 1)));
assertFalse(new DateTime(2015, 11, 4, 23, 59, 0)
.isBefore(new DateTime(2015, 11, 4, 23, 59, 0)));
}
@Test
public void testGetMonthOfYear() {
assertEquals(1, new DateTime(2015, 1, 2, 3, 4, 5).getMonthOfYear());
}
@Test
public void testIsAfter() {
assertTrue(new DateTime(2015, 11, 4, 23, 59, 1)
.isAfter(new DateTime(2015, 11, 4, 23, 59, 0)));
assertFalse(new DateTime(2015, 11, 4, 23, 59, 0)
.isAfter(new DateTime(2015, 11, 4, 23, 59, 0)));
}
@Test
public void testWithYear() {
assertEquals(
new DateTime(2016, 1, 1, 1, 1, 1),
new DateTime(2015, 1, 1, 1, 1, 1).withYear(2016));
}
@Test
public void testWithMonthOfYear() {
assertEquals(
new DateTime(2015, 1, 2, 3, 4, 5),
new DateTime(2015, 2, 2, 3, 4, 5).withMonthOfYear(1));
}
@Test
public void testGetHourOfDay() {
assertEquals(3, new DateTime(2015, 1, 2, 3, 4, 5).getHourOfDay());
}
@Test
public void testWithDayOfMonth() {
assertEquals(
new DateTime(2015, 1, 2, 3, 4, 5),
new DateTime(2015, 1, 1, 3, 4, 5).withDayOfMonth(2));
}
@Test
public void testPlusMinutes() {
assertEquals(
new DateTime(2015, 1, 2, 3, 4, 5),
new DateTime(2015, 1, 2, 2, 59, 5).plusMinutes(5));
}
@Test
public void testPlusHours() {
assertEquals(
new DateTime(2015, 1, 2, 3, 4, 5),
new DateTime(2015, 1, 1, 3, 4, 5).plusHours(24));
}
@Test
public void testPlusWeeks() {
assertEquals(
new DateTime(2015, 1, 2, 3, 4, 5),
new DateTime(2014, 12, 12, 3, 4, 5).plusWeeks(3));
}
@Test
public void testIsBeforeNow() {
Freeze.freezeAt(new DateTime(2015, 10, 6, 16, 15, 27)).thawAfter(new Snippet() {{
assertFalse(new DateTime(2015, 10, 6, 16, 15, 27).isBeforeNow());
assertTrue(new DateTime(2015, 10, 6, 16, 15, 26).isBeforeNow());
}});
}
@Test
public void testMinusMillis() {
assertEquals(
new DateTime(2015, 11, 6, 16, 18, 20, 452),
new DateTime(2015, 11, 6, 16, 18, 21, 374).minusMillis(922));
}
@Test
public void testMinusDays() {
assertEquals(
new DateTime(2015, 11, 6, 16, 19, 16),
new DateTime(2015, 12, 4, 16, 19, 16).minusDays(28));
assertEquals(
new DateTime(2015, 11, 6, 16, 19, 16),
new DateTime(2015, 11, 7, 16, 19, 16).minusDays(1));
}
@Test
public void testGetSecondOfMinute() {
assertEquals(32, new DateTime(2015, 11, 6, 16, 19, 32).getSecondOfMinute());
}
@Test
public void testToUTC() {
TimeZone def = TimeZone.getDefault();
try {
TimeZone.setDefault(TimeZone.getTimeZone("America/Chicago"));
assertEquals(
new DateTime(2015, 10, 6, 14, 45, 15, 0, TimeZone.getTimeZone("GMT")),
new DateTime(2015, 10, 6, 9, 45, 15).toUTC());
} finally {
TimeZone.setDefault(def);
}
}
@Test
public void testStartOfMinute() {
assertEquals(
new DateTime(2017, 9, 3, 0, 51, 0, 0),
new DateTime(2017, 9, 3, 0, 51, 13, 427).startOfMinute());
}
@Test
public void testEndOfMinute() {
assertEquals(
new DateTime(2017, 9, 22, 14, 47, 59, 999),
new DateTime(2017, 9, 22, 14, 47, 14, 453).endOfMinute());
}
@Test
public void testGetMillisOfDay() {
assertEquals(7248412, new DateTime(2015, 10, 6, 2, 0, 48, 412).getMillisOfDay());
}
@Test
public void testWithMillisOfDay() {
assertEquals(
new DateTime(2015, 10, 6, 2, 0, 48, 412),
new DateTime(2015, 10, 6, 0, 0, 0, 0).withMillisOfDay(7248412));
}
@Test
public void testWithMillisOfDayDuringDST() {
TimeZone def = TimeZone.getDefault();
try {
TimeZone.setDefault(TimeZone.getTimeZone("America/Chicago"));
assertEquals(2,
new DateTime(2015, 10, 31, 2, 0, 0).withMillisOfDay((int) TimeUnit.HOURS.toMillis(2))
.getHourOfDay());
} finally {
TimeZone.setDefault(def);
}
}
@Test
public void testWithMillisOfDayAfterDST() {
TimeZone def = TimeZone.getDefault();
try {
TimeZone.setDefault(TimeZone.getTimeZone("America/Chicago"));
assertEquals(2,
new DateTime(2015, 11, 2, 2, 0, 0).withMillisOfDay((int) TimeUnit.HOURS.toMillis(2))
.getHourOfDay());
} finally {
TimeZone.setDefault(def);
}
}
@Test
public void testWithMillisOfDayStartDST() {
TimeZone def = TimeZone.getDefault();
try {
TimeZone.setDefault(TimeZone.getTimeZone("America/Chicago"));
assertEquals(1,
new DateTime(2015, 3, 8, 0, 0, 0).withMillisOfDay((int) TimeUnit.HOURS.toMillis(1))
.getHourOfDay());
assertEquals(3,
new DateTime(2015, 3, 8, 0, 0, 0).withMillisOfDay((int) TimeUnit.HOURS.toMillis(2))
.getHourOfDay());
assertEquals(3,
new DateTime(2015, 3, 8, 0, 0, 0).withMillisOfDay((int) TimeUnit.HOURS.toMillis(3))
.getHourOfDay());
assertEquals(4,
new DateTime(2015, 3, 8, 0, 0, 0).withMillisOfDay((int) TimeUnit.HOURS.toMillis(4))
.getHourOfDay());
assertEquals(
new DateTime(2015, 3, 8, 0, 0, 0).withMillisOfDay((int) TimeUnit.HOURS.toMillis(2))
.getMillis(),
new DateTime(2015, 3, 8, 0, 0, 0).withMillisOfDay((int) TimeUnit.HOURS.toMillis(3))
.getMillis());
} finally {
TimeZone.setDefault(def);
}
}
@Test
public void testWithMillisOfDayEndDST() {
TimeZone def = TimeZone.getDefault();
try {
TimeZone.setDefault(TimeZone.getTimeZone("America/Chicago"));
assertEquals(1,
new DateTime(2015, 11, 1, 0, 0, 0).withMillisOfDay((int) TimeUnit.HOURS.toMillis(1))
.getHourOfDay());
assertEquals(2,
new DateTime(2015, 11, 1, 0, 0, 0).withMillisOfDay((int) TimeUnit.HOURS.toMillis(2))
.getHourOfDay());
assertEquals(3,
new DateTime(2015, 11, 1, 0, 0, 0).withMillisOfDay((int) TimeUnit.HOURS.toMillis(3))
.getHourOfDay());
} finally {
TimeZone.setDefault(def);
}
}
@Test
public void testPlusMonths() {
assertEquals(
new DateTime(2015, 11, 6, 2, 0, 48, 412),
new DateTime(2015, 10, 6, 2, 0, 48, 412).plusMonths(1));
}
@Test
public void testPlusMonthsWrapYear() {
assertEquals(
new DateTime(2016, 1, 6, 2, 0, 48, 412),
new DateTime(2015, 10, 6, 2, 0, 48, 412).plusMonths(3));
}
@Test
public void testGetDayOfMonth() {
assertEquals(5, new DateTime(2015, 10, 5, 0, 0, 0).getDayOfMonth());
}
@Test
public void testPlusDays() {
assertEquals(
new DateTime(2015, 10, 6, 2, 0, 48, 412),
new DateTime(2015, 10, 5, 2, 0, 48, 412).plusDays(1));
}
@Test
public void testPlusDaysWrapMonth() {
assertEquals(
new DateTime(2015, 11, 1, 2, 0, 48, 412),
new DateTime(2015, 10, 31, 2, 0, 48, 412).plusDays(1));
}
@Test
public void testMinuteOfHour() {
assertEquals(43, new DateTime(2015, 10, 5, 2, 43, 48).getMinuteOfHour());
}
@Test
public void testIsEndOfMonth() {
assertTrue(new DateTime(2014, 1, 31, 0, 0, 0).isLastDayOfMonth());
assertTrue(new DateTime(2014, 2, 28, 0, 0, 0).isLastDayOfMonth());
assertTrue(new DateTime(2014, 3, 31, 0, 0, 0).isLastDayOfMonth());
assertTrue(new DateTime(2014, 4, 30, 0, 0, 0).isLastDayOfMonth());
assertTrue(new DateTime(2014, 5, 31, 0, 0, 0).isLastDayOfMonth());
assertTrue(new DateTime(2014, 6, 30, 0, 0, 0).isLastDayOfMonth());
assertTrue(new DateTime(2014, 7, 31, 0, 0, 0).isLastDayOfMonth());
assertTrue(new DateTime(2014, 8, 31, 0, 0, 0).isLastDayOfMonth());
assertTrue(new DateTime(2014, 9, 30, 0, 0, 0).isLastDayOfMonth());
assertTrue(new DateTime(2014, 10, 31, 0, 0, 0).isLastDayOfMonth());
assertTrue(new DateTime(2014, 11, 30, 0, 0, 0).isLastDayOfMonth());
assertTrue(new DateTime(2014, 12, 31, 0, 0, 0).isLastDayOfMonth());
}
@Test
public void testNotTheEndOfTheMonth() {
for (int month = 1; month <= 12; month++) {
int lastDay = new DateTime(2014, month, 1, 0, 0, 0, 0).getNumberOfDaysInMonth();
for (int day = 1; day < lastDay; day++) {
assertFalse(new DateTime(2014, month, day, 0, 0, 0).isLastDayOfMonth());
}
}
}
@Test
public void testCheckEndOfMonthDuringLeapYear() {
assertFalse(new DateTime(2016, 2, 28, 0, 0, 0).isLastDayOfMonth());
assertTrue(new DateTime(2016, 2, 29, 0, 0, 0).isLastDayOfMonth());
}
@Test
public void testNumberOfDaysInMonth() {
assertEquals(31, new DateTime(2015, 1, 5, 9, 45, 34).getNumberOfDaysInMonth());
assertEquals(28, new DateTime(2015, 2, 5, 9, 45, 34).getNumberOfDaysInMonth());
assertEquals(31, new DateTime(2015, 3, 5, 9, 45, 34).getNumberOfDaysInMonth());
assertEquals(30, new DateTime(2015, 4, 5, 9, 45, 34).getNumberOfDaysInMonth());
assertEquals(31, new DateTime(2015, 5, 5, 9, 45, 34).getNumberOfDaysInMonth());
assertEquals(30, new DateTime(2015, 6, 5, 9, 45, 34).getNumberOfDaysInMonth());
assertEquals(31, new DateTime(2015, 7, 5, 9, 45, 34).getNumberOfDaysInMonth());
assertEquals(31, new DateTime(2015, 8, 5, 9, 45, 34).getNumberOfDaysInMonth());
assertEquals(30, new DateTime(2015, 9, 5, 9, 45, 34).getNumberOfDaysInMonth());
assertEquals(31, new DateTime(2015, 10, 5, 9, 45, 34).getNumberOfDaysInMonth());
assertEquals(30, new DateTime(2015, 11, 5, 9, 45, 34).getNumberOfDaysInMonth());
assertEquals(31, new DateTime(2015, 12, 5, 9, 45, 34).getNumberOfDaysInMonth());
}
@Test
public void testWithMillisOfSecond() {
assertEquals(
new DateTime(2015, 11, 6, 13, 34, 56, 453),
new DateTime(2015, 11, 6, 13, 34, 56, 0).withMillisOfSecond(453));
}
@Test
public void testWithHourOfDay() {
assertEquals(
new DateTime(2015, 11, 6, 23, 0, 0),
new DateTime(2015, 11, 6, 1, 0, 0).withHourOfDay(23));
}
@Test
public void testWithMinuteOfHour() {
assertEquals(
new DateTime(2015, 11, 6, 23, 13, 0),
new DateTime(2015, 11, 6, 23, 1, 0).withMinuteOfHour(13));
}
@Test
public void testWithSecondOfMinute() {
assertEquals(
new DateTime(2015, 11, 6, 23, 13, 56),
new DateTime(2015, 11, 6, 23, 13, 1).withSecondOfMinute(56));
}
@Test
public void testGetYear() {
assertEquals(2015, new DateTime(2015, 1, 1, 1, 1, 1).getYear());
}
@Test
public void testMinusMinutes() {
assertEquals(
new DateTime(2015, 11, 4, 23, 59, 0),
new DateTime(2015, 11, 5, 0, 1, 0).minusMinutes(2));
}
@Test
public void testIsBefore() {
assertTrue(new DateTime(2015, 11, 4, 23, 59, 0)
.isBefore(new DateTime(2015, 11, 4, 23, 59, 1)));
assertFalse(new DateTime(2015, 11, 4, 23, 59, 0)
.isBefore(new DateTime(2015, 11, 4, 23, 59, 0)));
}
@Test
public void testGetMonthOfYear() {
assertEquals(1, new DateTime(2015, 1, 2, 3, 4, 5).getMonthOfYear());
}
@Test
public void testIsAfter() {
assertTrue(new DateTime(2015, 11, 4, 23, 59, 1)
.isAfter(new DateTime(2015, 11, 4, 23, 59, 0)));
assertFalse(new DateTime(2015, 11, 4, 23, 59, 0)
.isAfter(new DateTime(2015, 11, 4, 23, 59, 0)));
}
@Test
public void testWithYear() {
assertEquals(
new DateTime(2016, 1, 1, 1, 1, 1),
new DateTime(2015, 1, 1, 1, 1, 1).withYear(2016));
}
@Test
public void testWithMonthOfYear() {
assertEquals(
new DateTime(2015, 1, 2, 3, 4, 5),
new DateTime(2015, 2, 2, 3, 4, 5).withMonthOfYear(1));
}
@Test
public void testGetHourOfDay() {
assertEquals(3, new DateTime(2015, 1, 2, 3, 4, 5).getHourOfDay());
}
@Test
public void testWithDayOfMonth() {
assertEquals(
new DateTime(2015, 1, 2, 3, 4, 5),
new DateTime(2015, 1, 1, 3, 4, 5).withDayOfMonth(2));
}
@Test
public void testPlusMinutes() {
assertEquals(
new DateTime(2015, 1, 2, 3, 4, 5),
new DateTime(2015, 1, 2, 2, 59, 5).plusMinutes(5));
}
@Test
public void testPlusHours() {
assertEquals(
new DateTime(2015, 1, 2, 3, 4, 5),
new DateTime(2015, 1, 1, 3, 4, 5).plusHours(24));
}
@Test
public void testPlusWeeks() {
assertEquals(
new DateTime(2015, 1, 2, 3, 4, 5),
new DateTime(2014, 12, 12, 3, 4, 5).plusWeeks(3));
}
@Test
public void testIsBeforeNow() {
Freeze.freezeAt(new DateTime(2015, 10, 6, 16, 15, 27)).thawAfter(new Snippet() {{
assertFalse(new DateTime(2015, 10, 6, 16, 15, 27).isBeforeNow());
assertTrue(new DateTime(2015, 10, 6, 16, 15, 26).isBeforeNow());
}});
}
@Test
public void testMinusMillis() {
assertEquals(
new DateTime(2015, 11, 6, 16, 18, 20, 452),
new DateTime(2015, 11, 6, 16, 18, 21, 374).minusMillis(922));
}
@Test
public void testMinusDays() {
assertEquals(
new DateTime(2015, 11, 6, 16, 19, 16),
new DateTime(2015, 12, 4, 16, 19, 16).minusDays(28));
assertEquals(
new DateTime(2015, 11, 6, 16, 19, 16),
new DateTime(2015, 11, 7, 16, 19, 16).minusDays(1));
}
@Test
public void testGetSecondOfMinute() {
assertEquals(32, new DateTime(2015, 11, 6, 16, 19, 32).getSecondOfMinute());
}
@Test
public void testToUTC() {
TimeZone def = TimeZone.getDefault();
try {
TimeZone.setDefault(TimeZone.getTimeZone("America/Chicago"));
assertEquals(
new DateTime(2015, 10, 6, 14, 45, 15, 0, TimeZone.getTimeZone("GMT")),
new DateTime(2015, 10, 6, 9, 45, 15).toUTC());
} finally {
TimeZone.setDefault(def);
}
}
@Test
public void testStartOfMinute() {
assertEquals(
new DateTime(2017, 9, 3, 0, 51, 0, 0),
new DateTime(2017, 9, 3, 0, 51, 13, 427).startOfMinute());
}
@Test
public void testEndOfMinute() {
assertEquals(
new DateTime(2017, 9, 22, 14, 47, 59, 999),
new DateTime(2017, 9, 22, 14, 47, 14, 453).endOfMinute());
}
}

@ -13,39 +13,39 @@ import com.todoroo.astrid.service.TitleParserTest;
import com.todoroo.astrid.subtasks.SubtasksHelperTest;
import com.todoroo.astrid.subtasks.SubtasksTestCase;
import com.todoroo.astrid.sync.NewSyncTestCase;
import org.tasks.jobs.BackupServiceTests;
import dagger.Component;
import org.tasks.jobs.BackupServiceTests;
@ApplicationScope
@Component(modules = TestModule.class)
public interface TestComponent {
Database getDatabase();
void inject(ReminderServiceTest reminderServiceTest);
Database getDatabase();
void inject(ReminderServiceTest reminderServiceTest);
void inject(TaskTest taskTest);
void inject(TaskTest taskTest);
void inject(TaskDaoTests taskDaoTests);
void inject(TaskDaoTests taskDaoTests);
void inject(MetadataDaoTests metadataDaoTests);
void inject(MetadataDaoTests metadataDaoTests);
void inject(Astrid3ProviderTests astrid3ProviderTests);
void inject(Astrid3ProviderTests astrid3ProviderTests);
void inject(NewSyncTestCase newSyncTestCase);
void inject(NewSyncTestCase newSyncTestCase);
void inject(SubtasksTestCase subtasksTestCase);
void inject(SubtasksTestCase subtasksTestCase);
void inject(SubtasksHelperTest subtasksHelperTest);
void inject(SubtasksHelperTest subtasksHelperTest);
void inject(QuickAddMarkupTest quickAddMarkupTest);
void inject(QuickAddMarkupTest quickAddMarkupTest);
void inject(TitleParserTest titleParserTest);
void inject(TitleParserTest titleParserTest);
void inject(NewRepeatTests newRepeatTests);
void inject(NewRepeatTests newRepeatTests);
void inject(BackupServiceTests backupServiceTests);
void inject(BackupServiceTests backupServiceTests);
NotificationTests.NotificationTestsComponent plus(NotificationTests.NotificationTestsModule notificationTestsModule);
NotificationTests.NotificationTestsComponent plus(
NotificationTests.NotificationTestsModule notificationTestsModule);
}

@ -13,39 +13,39 @@ import com.todoroo.astrid.service.TitleParserTest;
import com.todoroo.astrid.subtasks.SubtasksHelperTest;
import com.todoroo.astrid.subtasks.SubtasksTestCase;
import com.todoroo.astrid.sync.NewSyncTestCase;
import org.tasks.jobs.BackupServiceTests;
import dagger.Component;
import org.tasks.jobs.BackupServiceTests;
@ApplicationScope
@Component(modules = TestModule.class)
public interface TestComponent {
Database getDatabase();
void inject(ReminderServiceTest reminderServiceTest);
Database getDatabase();
void inject(ReminderServiceTest reminderServiceTest);
void inject(TaskTest taskTest);
void inject(TaskTest taskTest);
void inject(TaskDaoTests taskDaoTests);
void inject(TaskDaoTests taskDaoTests);
void inject(MetadataDaoTests metadataDaoTests);
void inject(MetadataDaoTests metadataDaoTests);
void inject(Astrid3ProviderTests astrid3ProviderTests);
void inject(Astrid3ProviderTests astrid3ProviderTests);
void inject(NewSyncTestCase newSyncTestCase);
void inject(NewSyncTestCase newSyncTestCase);
void inject(SubtasksTestCase subtasksTestCase);
void inject(SubtasksTestCase subtasksTestCase);
void inject(SubtasksHelperTest subtasksHelperTest);
void inject(SubtasksHelperTest subtasksHelperTest);
void inject(QuickAddMarkupTest quickAddMarkupTest);
void inject(QuickAddMarkupTest quickAddMarkupTest);
void inject(TitleParserTest titleParserTest);
void inject(TitleParserTest titleParserTest);
void inject(NewRepeatTests newRepeatTests);
void inject(NewRepeatTests newRepeatTests);
void inject(BackupServiceTests backupServiceTests);
void inject(BackupServiceTests backupServiceTests);
NotificationTests.NotificationTestsComponent plus(NotificationTests.NotificationTestsModule notificationTestsModule);
NotificationTests.NotificationTestsComponent plus(
NotificationTests.NotificationTestsModule notificationTestsModule);
}

@ -3,14 +3,19 @@
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.gtasks;
import android.support.test.runner.AndroidJUnit4;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
import android.support.test.runner.AndroidJUnit4;
import com.google.api.services.tasks.model.TaskList;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.data.Task;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.tasks.data.GoogleTask;
@ -19,189 +24,181 @@ import org.tasks.data.GoogleTaskList;
import org.tasks.injection.InjectingTestCase;
import org.tasks.injection.TestComponent;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
@SuppressWarnings("nls")
@RunWith(AndroidJUnit4.class)
public class GtasksIndentActionTest extends InjectingTestCase {
@Inject GtasksListService gtasksListService;
@Inject GtasksTaskListUpdater gtasksTaskListUpdater;
@Inject TaskDao taskDao;
@Inject GoogleTaskDao googleTaskDao;
@Inject GtasksListService gtasksListService;
@Inject GtasksTaskListUpdater gtasksTaskListUpdater;
@Inject TaskDao taskDao;
@Inject GoogleTaskDao googleTaskDao;
private Task task;
private GoogleTaskList storeList;
private Task task;
private GoogleTaskList storeList;
@Test
public void testIndentWithoutMetadata() {
givenTask(taskWithoutMetadata());
@Test
public void testIndentWithoutMetadata() {
givenTask(taskWithoutMetadata());
whenIncreaseIndent();
whenIncreaseIndent();
// should not crash
}
// should not crash
}
@Test
public void disabled_testIndentWithMetadataButNoOtherTasks() {
givenTask(taskWithMetadata(0, 0));
@Test
public void disabled_testIndentWithMetadataButNoOtherTasks() {
givenTask(taskWithMetadata(0, 0));
whenIncreaseIndent();
whenIncreaseIndent();
thenExpectIndentationLevel(0);
}
thenExpectIndentationLevel(0);
}
@Test
public void testIndentWithMetadata() {
taskWithMetadata(0, 0);
givenTask(taskWithMetadata(1, 0));
@Test
public void testIndentWithMetadata() {
taskWithMetadata(0, 0);
givenTask(taskWithMetadata(1, 0));
whenIncreaseIndent();
whenIncreaseIndent();
thenExpectIndentationLevel(1);
}
thenExpectIndentationLevel(1);
}
@Test
public void testDeindentWithMetadata() {
givenTask(taskWithMetadata(0, 1));
@Test
public void testDeindentWithMetadata() {
givenTask(taskWithMetadata(0, 1));
whenDecreaseIndent();
whenDecreaseIndent();
thenExpectIndentationLevel(0);
}
thenExpectIndentationLevel(0);
}
@Test
public void testDeindentWithoutMetadata() {
givenTask(taskWithoutMetadata());
@Test
public void testDeindentWithoutMetadata() {
givenTask(taskWithoutMetadata());
whenDecreaseIndent();
whenDecreaseIndent();
// should not crash
}
// should not crash
}
@Test
public void testDeindentWhenAlreadyZero() {
givenTask(taskWithMetadata(0, 0));
@Test
public void testDeindentWhenAlreadyZero() {
givenTask(taskWithMetadata(0, 0));
whenDecreaseIndent();
whenDecreaseIndent();
thenExpectIndentationLevel(0);
}
thenExpectIndentationLevel(0);
}
@Test
public void testIndentWithChildren() {
taskWithMetadata(0, 0);
givenTask(taskWithMetadata(1, 0));
Task child = taskWithMetadata(2, 1);
@Test
public void testIndentWithChildren() {
taskWithMetadata(0, 0);
givenTask(taskWithMetadata(1, 0));
Task child = taskWithMetadata(2, 1);
whenIncreaseIndent();
whenIncreaseIndent();
thenExpectIndentationLevel(1);
thenExpectIndentationLevel(child, 2);
}
thenExpectIndentationLevel(1);
thenExpectIndentationLevel(child, 2);
}
@Test
public void testDeindentWithChildren() {
taskWithMetadata(0, 0);
givenTask(taskWithMetadata(1, 1));
Task child = taskWithMetadata(2, 2);
@Test
public void testDeindentWithChildren() {
taskWithMetadata(0, 0);
givenTask(taskWithMetadata(1, 1));
Task child = taskWithMetadata(2, 2);
whenDecreaseIndent();
whenDecreaseIndent();
thenExpectIndentationLevel(0);
thenExpectIndentationLevel(child, 1);
}
thenExpectIndentationLevel(0);
thenExpectIndentationLevel(child, 1);
}
@Test
public void testIndentWithSiblings() {
taskWithMetadata(0, 0);
givenTask(taskWithMetadata(1, 0));
Task sibling = taskWithMetadata(2, 0);
@Test
public void testIndentWithSiblings() {
taskWithMetadata(0, 0);
givenTask(taskWithMetadata(1, 0));
Task sibling = taskWithMetadata(2, 0);
whenIncreaseIndent();
whenIncreaseIndent();
thenExpectIndentationLevel(1);
thenExpectIndentationLevel(sibling, 0);
}
thenExpectIndentationLevel(1);
thenExpectIndentationLevel(sibling, 0);
}
@Test
public void testIndentWithChildrensChildren() {
taskWithMetadata(0, 0);
givenTask(taskWithMetadata(1, 0));
Task child = taskWithMetadata(2, 1);
Task grandchild = taskWithMetadata(3, 2);
@Test
public void testIndentWithChildrensChildren() {
taskWithMetadata(0, 0);
givenTask(taskWithMetadata(1, 0));
Task child = taskWithMetadata(2, 1);
Task grandchild = taskWithMetadata(3, 2);
whenIncreaseIndent();
whenIncreaseIndent();
thenExpectIndentationLevel(1);
thenExpectIndentationLevel(child, 2);
thenExpectIndentationLevel(grandchild, 3);
}
thenExpectIndentationLevel(1);
thenExpectIndentationLevel(child, 2);
thenExpectIndentationLevel(grandchild, 3);
}
// --- helpers
// --- helpers
private void whenIncreaseIndent() {
gtasksTaskListUpdater.indent(storeList, task.getId(), 1);
}
private void whenIncreaseIndent() {
gtasksTaskListUpdater.indent(storeList, task.getId(), 1);
}
private void whenDecreaseIndent() {
gtasksTaskListUpdater.indent(storeList, task.getId(), -1);
}
private void whenDecreaseIndent() {
gtasksTaskListUpdater.indent(storeList, task.getId(), -1);
}
@Override
public void setUp() {
super.setUp();
List<TaskList> items = new ArrayList<>();
TaskList list = new TaskList();
list.setId("list");
list.setTitle("Test Tasks");
items.add(list);
gtasksListService.updateLists(items);
storeList = gtasksListService.getLists().get(0);
}
@Override
protected void inject(TestComponent component) {
component.inject(this);
}
private Task taskWithMetadata(long order, int indentation) {
Task newTask = new Task();
taskDao.createNew(newTask);
GoogleTask metadata = new GoogleTask(newTask.getId(), "list");
metadata.setIndent(indentation);
metadata.setOrder(order);
googleTaskDao.insert(metadata);
return newTask;
}
private void thenExpectIndentationLevel(int expected) {
thenExpectIndentationLevel(task, expected);
}
private void thenExpectIndentationLevel(Task targetTask, int expected) {
GoogleTask metadata = googleTaskDao.getByTaskId(targetTask.getId());
assertNotNull("task has metadata", metadata);
int indentation = metadata.getIndent();
assertTrue("indentation: " + indentation,
indentation == expected);
}
private void givenTask(Task taskToTest) {
task = taskToTest;
}
private Task taskWithoutMetadata() {
Task task = new Task();
taskDao.createNew(task);
return task;
}
@Override
public void setUp() {
super.setUp();
List<TaskList> items = new ArrayList<>();
TaskList list = new TaskList();
list.setId("list");
list.setTitle("Test Tasks");
items.add(list);
gtasksListService.updateLists(items);
storeList = gtasksListService.getLists().get(0);
}
@Override
protected void inject(TestComponent component) {
component.inject(this);
}
private Task taskWithMetadata(long order, int indentation) {
Task newTask = new Task();
taskDao.createNew(newTask);
GoogleTask metadata = new GoogleTask(newTask.getId(), "list");
metadata.setIndent(indentation);
metadata.setOrder(order);
googleTaskDao.insert(metadata);
return newTask;
}
private void thenExpectIndentationLevel(int expected) {
thenExpectIndentationLevel(task, expected);
}
private void thenExpectIndentationLevel(Task targetTask, int expected) {
GoogleTask metadata = googleTaskDao.getByTaskId(targetTask.getId());
assertNotNull("task has metadata", metadata);
int indentation = metadata.getIndent();
assertTrue("indentation: " + indentation,
indentation == expected);
}
private void givenTask(Task taskToTest) {
task = taskToTest;
}
private Task taskWithoutMetadata() {
Task task = new Task();
taskDao.createNew(task);
return task;
}
}

@ -1,12 +1,24 @@
package com.todoroo.astrid.gtasks;
import android.support.test.runner.AndroidJUnit4;
import static com.natpryce.makeiteasy.MakeItEasy.with;
import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNull;
import static org.tasks.makers.GtaskListMaker.ID;
import static org.tasks.makers.GtaskListMaker.LAST_SYNC;
import static org.tasks.makers.GtaskListMaker.NAME;
import static org.tasks.makers.GtaskListMaker.REMOTE_ID;
import static org.tasks.makers.GtaskListMaker.newGtaskList;
import static org.tasks.makers.RemoteGtaskListMaker.newRemoteList;
import static org.tasks.time.DateTimeUtils.currentTimeMillis;
import android.support.test.runner.AndroidJUnit4;
import com.google.api.client.util.DateTime;
import com.google.api.services.tasks.model.TaskList;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.service.TaskDeleter;
import javax.inject.Inject;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.tasks.LocalBroadcastManager;
@ -17,116 +29,102 @@ import org.tasks.injection.InjectingTestCase;
import org.tasks.injection.TestComponent;
import org.tasks.makers.RemoteGtaskListMaker;
import javax.inject.Inject;
import static com.natpryce.makeiteasy.MakeItEasy.with;
import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNull;
import static org.tasks.makers.GtaskListMaker.ID;
import static org.tasks.makers.GtaskListMaker.LAST_SYNC;
import static org.tasks.makers.GtaskListMaker.NAME;
import static org.tasks.makers.GtaskListMaker.REMOTE_ID;
import static org.tasks.makers.GtaskListMaker.newGtaskList;
import static org.tasks.makers.RemoteGtaskListMaker.newRemoteList;
import static org.tasks.time.DateTimeUtils.currentTimeMillis;
@RunWith(AndroidJUnit4.class)
public class GtasksListServiceTest extends InjectingTestCase {
@Inject TaskDeleter taskDeleter;
@Inject LocalBroadcastManager localBroadcastManager;
@Inject GoogleTaskDao googleTaskDao;
@Inject TaskDao taskDao;
@Inject GoogleTaskListDao googleTaskListDao;
private GtasksListService gtasksListService;
@Override
public void setUp() {
super.setUp();
gtasksListService = new GtasksListService(googleTaskListDao, taskDeleter,
localBroadcastManager, googleTaskDao, taskDao);
}
@Override
protected void inject(TestComponent component) {
component.inject(this);
}
@Test
public void testCreateNewList() {
setLists(newRemoteList(
with(RemoteGtaskListMaker.REMOTE_ID, "1"),
with(RemoteGtaskListMaker.NAME, "Default")));
assertEquals(
newGtaskList(
with(ID, 1L),
with(REMOTE_ID, "1"),
with(NAME, "Default")),
googleTaskListDao.getById(1L));
}
@Test
public void testGetListByRemoteId() {
GoogleTaskList list = newGtaskList(with(REMOTE_ID, "1"));
list.setId(googleTaskListDao.insertOrReplace(list));
assertEquals(list, gtasksListService.getList("1"));
}
@Test
public void testGetListReturnsNullWhenNotFound() {
assertNull(gtasksListService.getList("1"));
}
@Test
public void testDeleteMissingList() {
googleTaskListDao.insertOrReplace(newGtaskList(with(ID, 1L), with(REMOTE_ID, "1")));
TaskList taskList = newRemoteList(with(RemoteGtaskListMaker.REMOTE_ID, "2"));
setLists(taskList);
assertEquals(singletonList(newGtaskList(with(ID, 2L), with(REMOTE_ID, "2"))),
googleTaskListDao.getActiveLists());
}
@Test
public void testUpdateListName() {
googleTaskListDao.insertOrReplace(newGtaskList(
with(ID, 1L),
with(REMOTE_ID, "1"),
with(NAME, "oldName")));
setLists(newRemoteList(
with(RemoteGtaskListMaker.REMOTE_ID, "1"),
with(RemoteGtaskListMaker.NAME, "newName")));
assertEquals("newName", googleTaskListDao.getById(1).getTitle());
}
@Test
public void testNewListLastSyncIsZero() {
setLists(new TaskList().setId("1"));
assertEquals(0L, gtasksListService.getList("1").getLastSync());
}
@Test
public void testNewListNeedsUpdate() {
TaskList taskList = new TaskList().setId("1").setTitle("Default").setUpdated(new DateTime(currentTimeMillis()));
setLists(taskList);
assertEquals(
asList(newGtaskList(with(ID, 1L), with(REMOTE_ID, "1"), with(LAST_SYNC, 0L))),
gtasksListService.getListsToUpdate(asList(taskList)));
}
private void setLists(TaskList... list) {
gtasksListService.updateLists(asList(list));
}
@Inject TaskDeleter taskDeleter;
@Inject LocalBroadcastManager localBroadcastManager;
@Inject GoogleTaskDao googleTaskDao;
@Inject TaskDao taskDao;
@Inject GoogleTaskListDao googleTaskListDao;
private GtasksListService gtasksListService;
@Override
public void setUp() {
super.setUp();
gtasksListService = new GtasksListService(googleTaskListDao, taskDeleter,
localBroadcastManager, googleTaskDao, taskDao);
}
@Override
protected void inject(TestComponent component) {
component.inject(this);
}
@Test
public void testCreateNewList() {
setLists(newRemoteList(
with(RemoteGtaskListMaker.REMOTE_ID, "1"),
with(RemoteGtaskListMaker.NAME, "Default")));
assertEquals(
newGtaskList(
with(ID, 1L),
with(REMOTE_ID, "1"),
with(NAME, "Default")),
googleTaskListDao.getById(1L));
}
@Test
public void testGetListByRemoteId() {
GoogleTaskList list = newGtaskList(with(REMOTE_ID, "1"));
list.setId(googleTaskListDao.insertOrReplace(list));
assertEquals(list, gtasksListService.getList("1"));
}
@Test
public void testGetListReturnsNullWhenNotFound() {
assertNull(gtasksListService.getList("1"));
}
@Test
public void testDeleteMissingList() {
googleTaskListDao.insertOrReplace(newGtaskList(with(ID, 1L), with(REMOTE_ID, "1")));
TaskList taskList = newRemoteList(with(RemoteGtaskListMaker.REMOTE_ID, "2"));
setLists(taskList);
assertEquals(singletonList(newGtaskList(with(ID, 2L), with(REMOTE_ID, "2"))),
googleTaskListDao.getActiveLists());
}
@Test
public void testUpdateListName() {
googleTaskListDao.insertOrReplace(newGtaskList(
with(ID, 1L),
with(REMOTE_ID, "1"),
with(NAME, "oldName")));
setLists(newRemoteList(
with(RemoteGtaskListMaker.REMOTE_ID, "1"),
with(RemoteGtaskListMaker.NAME, "newName")));
assertEquals("newName", googleTaskListDao.getById(1).getTitle());
}
@Test
public void testNewListLastSyncIsZero() {
setLists(new TaskList().setId("1"));
assertEquals(0L, gtasksListService.getList("1").getLastSync());
}
@Test
public void testNewListNeedsUpdate() {
TaskList taskList = new TaskList().setId("1").setTitle("Default")
.setUpdated(new DateTime(currentTimeMillis()));
setLists(taskList);
assertEquals(
asList(newGtaskList(with(ID, 1L), with(REMOTE_ID, "1"), with(LAST_SYNC, 0L))),
gtasksListService.getListsToUpdate(asList(taskList)));
}
private void setLists(TaskList... list) {
gtasksListService.updateLists(asList(list));
}
}

@ -3,14 +3,21 @@
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.gtasks;
import static android.support.test.InstrumentationRegistry.getTargetContext;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;
import android.content.Context;
import android.support.test.runner.AndroidJUnit4;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.data.Task;
import dagger.Module;
import dagger.Provides;
import dagger.Subcomponent;
import javax.inject.Inject;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.tasks.data.GoogleTask;
@ -19,120 +26,101 @@ import org.tasks.injection.InjectingTestCase;
import org.tasks.injection.TestComponent;
import org.tasks.preferences.Preferences;
import javax.inject.Inject;
import dagger.Module;
import dagger.Provides;
import dagger.Subcomponent;
import static android.support.test.InstrumentationRegistry.getTargetContext;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;
@SuppressWarnings("nls")
@RunWith(AndroidJUnit4.class)
public class GtasksMetadataServiceTest extends InjectingTestCase {
@Module
public class GtasksMetadataServiceTestModule {
private final GtasksTestPreferenceService service;
@Inject TaskDao taskDao;
@Inject GoogleTaskDao googleTaskDao;
private Task task;
private GoogleTask metadata;
public GtasksMetadataServiceTestModule(Context context) {
service = new GtasksTestPreferenceService(new Preferences(context, null));
}
@Override
protected void inject(TestComponent component) {
component
.plus(new GtasksMetadataServiceTestModule(getTargetContext()))
.inject(this);
}
@Provides
public GtasksTestPreferenceService getGtasksTestPreferenceService() {
return service;
}
@Test
public void testMetadataFound() {
givenTask(taskWithMetadata(null));
@Provides
public GtasksPreferenceService getGtasksPreferenceService() {
return service;
}
}
whenSearchForMetadata();
@Subcomponent(modules = GtasksMetadataServiceTest.GtasksMetadataServiceTestModule.class)
public interface GtasksMetadataServiceTestComponent {
void inject(GtasksMetadataServiceTest gtasksMetadataServiceTest);
}
thenExpectMetadataFound();
}
@Inject GtasksTestPreferenceService preferences;
@Inject TaskDao taskDao;
@Inject GoogleTaskDao googleTaskDao;
@Test
public void testMetadataDoesntExist() {
givenTask(taskWithoutMetadata());
private Task task;
private GoogleTask metadata;
whenSearchForMetadata();
@Override
public void setUp() {
super.setUp();
thenExpectNoMetadataFound();
}
if (preferences.getDefaultList() == null) {
preferences.setDefaultList("list");
}
}
private void thenExpectNoMetadataFound() {
assertNull(metadata);
}
@Override
protected void inject(TestComponent component) {
component
.plus(new GtasksMetadataServiceTestModule(getTargetContext()))
.inject(this);
}
private void thenExpectMetadataFound() {
assertNotNull(metadata);
}
@Test
public void testMetadataFound() {
givenTask(taskWithMetadata(null));
// --- helpers
whenSearchForMetadata();
private void whenSearchForMetadata() {
metadata = googleTaskDao.getByTaskId(task.getId());
}
thenExpectMetadataFound();
private Task taskWithMetadata(String id) {
Task task = new Task();
task.setTitle("cats");
taskDao.createNew(task);
GoogleTask metadata = new GoogleTask(task.getId(), "");
if (id != null) {
metadata.setRemoteId(id);
}
metadata.setTask(task.getId());
googleTaskDao.insert(metadata);
return task;
}
@Test
public void testMetadataDoesntExist() {
givenTask(taskWithoutMetadata());
private void givenTask(Task taskToTest) {
task = taskToTest;
}
whenSearchForMetadata();
private Task taskWithoutMetadata() {
Task task = new Task();
task.setTitle("dogs");
taskDao.createNew(task);
return task;
}
thenExpectNoMetadataFound();
}
@Subcomponent(modules = GtasksMetadataServiceTest.GtasksMetadataServiceTestModule.class)
public interface GtasksMetadataServiceTestComponent {
// --- helpers
void inject(GtasksMetadataServiceTest gtasksMetadataServiceTest);
}
private void thenExpectNoMetadataFound() {
assertNull(metadata);
}
@Module
public class GtasksMetadataServiceTestModule {
private void thenExpectMetadataFound() {
assertNotNull(metadata);
}
private void whenSearchForMetadata() {
metadata = googleTaskDao.getByTaskId(task.getId());
}
private final GtasksTestPreferenceService service;
private Task taskWithMetadata(String id) {
Task task = new Task();
task.setTitle("cats");
taskDao.createNew(task);
GoogleTask metadata = new GoogleTask(task.getId(), "");
if (id != null) {
metadata.setRemoteId(id);
}
metadata.setTask(task.getId());
googleTaskDao.insert(metadata);
return task;
public GtasksMetadataServiceTestModule(Context context) {
service = new GtasksTestPreferenceService(new Preferences(context, null));
}
private void givenTask(Task taskToTest) {
task = taskToTest;
@Provides
public GtasksTestPreferenceService getGtasksTestPreferenceService() {
return service;
}
private Task taskWithoutMetadata() {
Task task = new Task();
task.setTitle("dogs");
taskDao.createNew(task);
return task;
@Provides
public GtasksPreferenceService getGtasksPreferenceService() {
return service;
}
}
}

@ -3,14 +3,19 @@
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.gtasks;
import android.support.test.runner.AndroidJUnit4;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
import android.support.test.runner.AndroidJUnit4;
import com.google.api.services.tasks.model.TaskList;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.data.Task;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@ -20,179 +25,173 @@ import org.tasks.data.GoogleTaskList;
import org.tasks.injection.InjectingTestCase;
import org.tasks.injection.TestComponent;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
@SuppressWarnings("nls")
@RunWith(AndroidJUnit4.class)
public class GtasksTaskListUpdaterTest extends InjectingTestCase {
private static final int VALUE_UNSET = -1;
@Inject GtasksTaskListUpdater gtasksTaskListUpdater;
@Inject GtasksListService gtasksListService;
@Inject TaskDao taskDao;
@Inject GoogleTaskDao googleTaskDao;
@Test
public void testBasicParentComputation() {
Task[] tasks = givenTasksABCDE();
whenCalculatingParentsAndSiblings();
thenExpectParent(tasks[0], null);
thenExpectParent(tasks[1], tasks[0]);
thenExpectParent(tasks[2], tasks[0]);
thenExpectParent(tasks[3], tasks[2]);
thenExpectParent(tasks[4], null);
}
@Test
public void testBasicSiblingComputation() {
Task[] tasks = givenTasksABCDE();
whenCalculatingParentsAndSiblings();
thenExpectSibling(tasks[0], null);
thenExpectSibling(tasks[1], null);
thenExpectSibling(tasks[2], tasks[1]);
thenExpectSibling(tasks[3], null);
thenExpectSibling(tasks[4], tasks[0]);
}
@Ignore
@Test
public void testMetadataParentComputation() {
Task[] tasks = givenTasksABCDE();
thenExpectMetadataParent(tasks[0], null);
thenExpectMetadataParent(tasks[1], tasks[0]);
thenExpectMetadataParent(tasks[2], tasks[0]);
thenExpectMetadataParent(tasks[3], tasks[2]);
thenExpectMetadataParent(tasks[4], null);
}
@Test
public void testMetadataOrderComputation() {
Task[] tasks = givenTasksABCDE();
thenExpectMetadataIndentAndOrder(tasks[0], 0, 0);
thenExpectMetadataIndentAndOrder(tasks[1], 1, 1);
thenExpectMetadataIndentAndOrder(tasks[2], 2, 1);
thenExpectMetadataIndentAndOrder(tasks[3], 3, 2);
thenExpectMetadataIndentAndOrder(tasks[4], 4, 0);
}
@Ignore
@Test
public void testNewTaskOrder() {
givenTasksABCDE();
Task newTask = createTask("F", VALUE_UNSET, 0);
thenExpectMetadataIndentAndOrder(newTask, 5, 0);
private static final int VALUE_UNSET = -1;
@Inject GtasksTaskListUpdater gtasksTaskListUpdater;
@Inject GtasksListService gtasksListService;
@Inject TaskDao taskDao;
@Inject GoogleTaskDao googleTaskDao;
@Test
public void testBasicParentComputation() {
Task[] tasks = givenTasksABCDE();
whenCalculatingParentsAndSiblings();
thenExpectParent(tasks[0], null);
thenExpectParent(tasks[1], tasks[0]);
thenExpectParent(tasks[2], tasks[0]);
thenExpectParent(tasks[3], tasks[2]);
thenExpectParent(tasks[4], null);
}
@Test
public void testBasicSiblingComputation() {
Task[] tasks = givenTasksABCDE();
whenCalculatingParentsAndSiblings();
thenExpectSibling(tasks[0], null);
thenExpectSibling(tasks[1], null);
thenExpectSibling(tasks[2], tasks[1]);
thenExpectSibling(tasks[3], null);
thenExpectSibling(tasks[4], tasks[0]);
}
@Ignore
@Test
public void testMetadataParentComputation() {
Task[] tasks = givenTasksABCDE();
thenExpectMetadataParent(tasks[0], null);
thenExpectMetadataParent(tasks[1], tasks[0]);
thenExpectMetadataParent(tasks[2], tasks[0]);
thenExpectMetadataParent(tasks[3], tasks[2]);
thenExpectMetadataParent(tasks[4], null);
}
@Test
public void testMetadataOrderComputation() {
Task[] tasks = givenTasksABCDE();
thenExpectMetadataIndentAndOrder(tasks[0], 0, 0);
thenExpectMetadataIndentAndOrder(tasks[1], 1, 1);
thenExpectMetadataIndentAndOrder(tasks[2], 2, 1);
thenExpectMetadataIndentAndOrder(tasks[3], 3, 2);
thenExpectMetadataIndentAndOrder(tasks[4], 4, 0);
}
@Ignore
@Test
public void testNewTaskOrder() {
givenTasksABCDE();
Task newTask = createTask("F", VALUE_UNSET, 0);
thenExpectMetadataIndentAndOrder(newTask, 5, 0);
}
// --- helpers
private void thenExpectMetadataIndentAndOrder(Task task, long order, int indent) {
GoogleTask metadata = googleTaskDao.getByTaskId(task.getId());
assertNotNull("metadata was found", metadata);
assertEquals("order", order, metadata.getOrder());
assertEquals("indentation", indent, metadata.getIndent());
}
private void thenExpectMetadataParent(Task task, Task expectedParent) {
GoogleTask metadata = googleTaskDao.getByTaskId(task.getId());
long parent = metadata.getParent();
if (expectedParent == null) {
assertEquals("Task " + task.getTitle() + " parent none", 0, parent);
} else {
assertEquals("Task " + task.getTitle() + " parent " +
expectedParent.getTitle(), expectedParent.getId(), parent);
}
// --- helpers
private void thenExpectMetadataIndentAndOrder(Task task, long order, int indent) {
GoogleTask metadata = googleTaskDao.getByTaskId(task.getId());
assertNotNull("metadata was found", metadata);
assertEquals("order", order, metadata.getOrder());
assertEquals("indentation", indent, metadata.getIndent());
}
private void thenExpectSibling(Task task, Task expectedSibling) {
long sibling = gtasksTaskListUpdater.siblings.get(task.getId());
if (expectedSibling == null) {
assertEquals("Task " + task.getTitle() + " sibling null", 0L, sibling);
} else {
assertEquals("Task " + task.getTitle() + " sibling " +
expectedSibling.getTitle(), expectedSibling.getId(), sibling);
}
private void thenExpectMetadataParent(Task task, Task expectedParent) {
GoogleTask metadata = googleTaskDao.getByTaskId(task.getId());
long parent = metadata.getParent();
if(expectedParent == null)
assertEquals("Task " + task.getTitle() + " parent none", 0, parent);
else
assertEquals("Task " + task.getTitle() + " parent " +
expectedParent.getTitle(), expectedParent.getId(), parent);
}
private void thenExpectParent(Task task, Task expectedParent) {
long parent = gtasksTaskListUpdater.parents.get(task.getId());
if (expectedParent == null) {
assertEquals("Task " + task.getTitle() + " parent null", 0L, parent);
} else {
assertEquals("Task " + task.getTitle() + " parent " +
expectedParent.getTitle(), expectedParent.getId(), parent);
}
private void thenExpectSibling(Task task, Task expectedSibling) {
long sibling = gtasksTaskListUpdater.siblings.get(task.getId());
if(expectedSibling == null)
assertEquals("Task " + task.getTitle() + " sibling null", 0L, sibling);
else
assertEquals("Task " + task.getTitle() + " sibling " +
expectedSibling.getTitle(), expectedSibling.getId(), sibling);
}
@Override
public void setUp() {
super.setUp();
List<TaskList> items = new ArrayList<>();
TaskList list = new TaskList();
list.setId("1");
list.setTitle("Tim's Tasks");
items.add(list);
gtasksListService.updateLists(items);
}
@Override
protected void inject(TestComponent component) {
component.inject(this);
}
private void whenCalculatingParentsAndSiblings() {
createParentSiblingMaps();
}
void createParentSiblingMaps() {
for (GoogleTaskList list : gtasksListService.getLists()) {
gtasksTaskListUpdater.updateParentSiblingMapsFor(list);
}
private void thenExpectParent(Task task, Task expectedParent) {
long parent = gtasksTaskListUpdater.parents.get(task.getId());
if(expectedParent == null)
assertEquals("Task " + task.getTitle() + " parent null", 0L, parent);
else
assertEquals("Task " + task.getTitle() + " parent " +
expectedParent.getTitle(), expectedParent.getId(), parent);
}
/**
* A
* B
* C
* D
* E
*/
private Task[] givenTasksABCDE() {
return new Task[]{
createTask("A", 0, 0),
createTask("B", 1, 1),
createTask("C", 2, 1),
createTask("D", 3, 2),
createTask("E", 4, 0),
};
}
private Task createTask(String title, long order, int indent) {
Task task = new Task();
task.setTitle(title);
taskDao.createNew(task);
GoogleTask metadata = new GoogleTask(task.getId(), "1");
if (order != VALUE_UNSET) {
metadata.setOrder(order);
}
@Override
public void setUp() {
super.setUp();
List<TaskList> items = new ArrayList<>();
TaskList list = new TaskList();
list.setId("1");
list.setTitle("Tim's Tasks");
items.add(list);
gtasksListService.updateLists(items);
}
@Override
protected void inject(TestComponent component) {
component.inject(this);
}
private void whenCalculatingParentsAndSiblings() {
createParentSiblingMaps();
}
void createParentSiblingMaps() {
for(GoogleTaskList list : gtasksListService.getLists()) {
gtasksTaskListUpdater.updateParentSiblingMapsFor(list);
}
}
/**
* A
* B
* C
* D
* E
*/
private Task[] givenTasksABCDE() {
return new Task[] {
createTask("A", 0, 0),
createTask("B", 1, 1),
createTask("C", 2, 1),
createTask("D", 3, 2),
createTask("E", 4, 0),
};
}
private Task createTask(String title, long order, int indent) {
Task task = new Task();
task.setTitle(title);
taskDao.createNew(task);
GoogleTask metadata = new GoogleTask(task.getId(), "1");
if(order != VALUE_UNSET) {
metadata.setOrder(order);
}
if(indent != VALUE_UNSET) {
metadata.setIndent(indent);
}
googleTaskDao.insert(metadata);
return task;
if (indent != VALUE_UNSET) {
metadata.setIndent(indent);
}
googleTaskDao.insert(metadata);
return task;
}
}

@ -3,14 +3,19 @@
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.gtasks;
import android.support.test.runner.AndroidJUnit4;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
import android.support.test.runner.AndroidJUnit4;
import com.google.api.services.tasks.model.TaskList;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.data.Task;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.tasks.data.GoogleTask;
@ -19,27 +24,19 @@ import org.tasks.data.GoogleTaskList;
import org.tasks.injection.InjectingTestCase;
import org.tasks.injection.TestComponent;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
@SuppressWarnings("nls")
@RunWith(AndroidJUnit4.class)
public class GtasksTaskMovingTest extends InjectingTestCase {
private static final int VALUE_UNSET = -1;
private static final int VALUE_UNSET = -1;
@Inject GtasksListService gtasksListService;
@Inject GtasksTaskListUpdater gtasksTaskListUpdater;
@Inject TaskDao taskDao;
@Inject GoogleTaskDao googleTaskDao;
@Inject GtasksListService gtasksListService;
@Inject GtasksTaskListUpdater gtasksTaskListUpdater;
@Inject TaskDao taskDao;
@Inject GoogleTaskDao googleTaskDao;
private Task A, B, C, D, E, F;
private GoogleTaskList list;
private Task A, B, C, D, E, F;
private GoogleTaskList list;
/* Starting State:
*
@ -51,41 +48,41 @@ public class GtasksTaskMovingTest extends InjectingTestCase {
* F
*/
@Test
public void testMoveDownFromListBottom() {
givenTasksABCDEF();
@Test
public void testMoveDownFromListBottom() {
givenTasksABCDEF();
whenTriggerMove(F, null);
whenTriggerMove(F, null);
thenExpectMetadataOrderAndIndent(E, 4, 0);
thenExpectMetadataOrderAndIndent(F, 5, 0);
}
thenExpectMetadataOrderAndIndent(E, 4, 0);
thenExpectMetadataOrderAndIndent(F, 5, 0);
}
@Test
public void testMoveDownToListBottom() {
givenTasksABCDEF();
@Test
public void testMoveDownToListBottom() {
givenTasksABCDEF();
whenTriggerMove(E, null);
whenTriggerMove(E, null);
thenExpectMetadataOrderAndIndent(E, 5, 0);
thenExpectMetadataOrderAndIndent(F, 4, 0);
}
thenExpectMetadataOrderAndIndent(E, 5, 0);
thenExpectMetadataOrderAndIndent(F, 4, 0);
}
@Test
public void testMoveUpSimple() {
givenTasksABCDEF();
@Test
public void testMoveUpSimple() {
givenTasksABCDEF();
whenTriggerMove(F, E);
whenTriggerMove(F, E);
thenExpectMetadataOrderAndIndent(E, 5, 0);
thenExpectMetadataOrderAndIndent(F, 4, 0);
}
thenExpectMetadataOrderAndIndent(E, 5, 0);
thenExpectMetadataOrderAndIndent(F, 4, 0);
}
@Test
public void testMoveUpWithSubtasks() {
givenTasksABCDEF();
@Test
public void testMoveUpWithSubtasks() {
givenTasksABCDEF();
whenTriggerMove(C, B);
whenTriggerMove(C, B);
/*
* A
@ -94,17 +91,17 @@ public class GtasksTaskMovingTest extends InjectingTestCase {
* B
*/
thenExpectMetadataOrderAndIndent(A, 0, 0);
thenExpectMetadataOrderAndIndent(B, 3, 1);
thenExpectMetadataOrderAndIndent(C, 1, 1);
thenExpectMetadataOrderAndIndent(D, 2, 2);
}
thenExpectMetadataOrderAndIndent(A, 0, 0);
thenExpectMetadataOrderAndIndent(B, 3, 1);
thenExpectMetadataOrderAndIndent(C, 1, 1);
thenExpectMetadataOrderAndIndent(D, 2, 2);
}
@Test
public void testMoveDownThroughSubtasks() {
givenTasksABCDEF();
@Test
public void testMoveDownThroughSubtasks() {
givenTasksABCDEF();
whenTriggerMove(B, E);
whenTriggerMove(B, E);
/*
* A
@ -114,17 +111,17 @@ public class GtasksTaskMovingTest extends InjectingTestCase {
* E
*/
thenExpectMetadataOrderAndIndent(A, 0, 0);
thenExpectMetadataOrderAndIndent(B, 3, 0);
thenExpectMetadataOrderAndIndent(C, 1, 1);
thenExpectMetadataOrderAndIndent(D, 2, 2);
}
thenExpectMetadataOrderAndIndent(A, 0, 0);
thenExpectMetadataOrderAndIndent(B, 3, 0);
thenExpectMetadataOrderAndIndent(C, 1, 1);
thenExpectMetadataOrderAndIndent(D, 2, 2);
}
@Test
public void testMoveUpAboveParent() {
givenTasksABCDEF();
@Test
public void testMoveUpAboveParent() {
givenTasksABCDEF();
whenTriggerMove(B, A);
whenTriggerMove(B, A);
/*
* B
@ -135,16 +132,16 @@ public class GtasksTaskMovingTest extends InjectingTestCase {
* F
*/
thenExpectMetadataOrderAndIndent(A, 1, 0);
thenExpectMetadataOrderAndIndent(B, 0, 0);
thenExpectMetadataOrderAndIndent(C, 2, 1);
}
thenExpectMetadataOrderAndIndent(A, 1, 0);
thenExpectMetadataOrderAndIndent(B, 0, 0);
thenExpectMetadataOrderAndIndent(C, 2, 1);
}
@Test
public void testMoveDownWithChildren() {
givenTasksABCDEF();
@Test
public void testMoveDownWithChildren() {
givenTasksABCDEF();
whenTriggerMove(C, F);
whenTriggerMove(C, F);
/*
* A
@ -155,18 +152,18 @@ public class GtasksTaskMovingTest extends InjectingTestCase {
* F
*/
thenExpectMetadataOrderAndIndent(A, 0, 0);
thenExpectMetadataOrderAndIndent(B, 1, 1);
thenExpectMetadataOrderAndIndent(C, 3, 0);
thenExpectMetadataOrderAndIndent(D, 4, 1);
thenExpectMetadataOrderAndIndent(E, 2, 0);
}
thenExpectMetadataOrderAndIndent(A, 0, 0);
thenExpectMetadataOrderAndIndent(B, 1, 1);
thenExpectMetadataOrderAndIndent(C, 3, 0);
thenExpectMetadataOrderAndIndent(D, 4, 1);
thenExpectMetadataOrderAndIndent(E, 2, 0);
}
@Test
public void testMoveDownIndentingTwice() {
givenTasksABCDEF();
@Test
public void testMoveDownIndentingTwice() {
givenTasksABCDEF();
whenTriggerMove(D, F);
whenTriggerMove(D, F);
/*
* A
@ -176,18 +173,18 @@ public class GtasksTaskMovingTest extends InjectingTestCase {
* D
*/
thenExpectMetadataOrderAndIndent(A, 0, 0);
thenExpectMetadataOrderAndIndent(B, 1, 1);
thenExpectMetadataOrderAndIndent(C, 2, 1);
thenExpectMetadataOrderAndIndent(D, 4, 0);
thenExpectMetadataOrderAndIndent(E, 3, 0);
}
thenExpectMetadataOrderAndIndent(A, 0, 0);
thenExpectMetadataOrderAndIndent(B, 1, 1);
thenExpectMetadataOrderAndIndent(C, 2, 1);
thenExpectMetadataOrderAndIndent(D, 4, 0);
thenExpectMetadataOrderAndIndent(E, 3, 0);
}
@Test
public void testMoveUpMultiple() {
givenTasksABCDEF();
@Test
public void testMoveUpMultiple() {
givenTasksABCDEF();
whenTriggerMove(C, A);
whenTriggerMove(C, A);
/*
* C
@ -196,17 +193,17 @@ public class GtasksTaskMovingTest extends InjectingTestCase {
* B
*/
thenExpectMetadataOrderAndIndent(A, 2, 0);
thenExpectMetadataOrderAndIndent(B, 3, 1);
thenExpectMetadataOrderAndIndent(C, 0, 0);
thenExpectMetadataOrderAndIndent(D, 1, 1);
}
thenExpectMetadataOrderAndIndent(A, 2, 0);
thenExpectMetadataOrderAndIndent(B, 3, 1);
thenExpectMetadataOrderAndIndent(C, 0, 0);
thenExpectMetadataOrderAndIndent(D, 1, 1);
}
@Test
public void testMoveUpIntoSublist() {
givenTasksABCDEF();
@Test
public void testMoveUpIntoSublist() {
givenTasksABCDEF();
whenTriggerMove(F, D);
whenTriggerMove(F, D);
/*
* A
@ -216,19 +213,19 @@ public class GtasksTaskMovingTest extends InjectingTestCase {
* D
*/
thenExpectMetadataOrderAndIndent(A, 0, 0);
thenExpectMetadataOrderAndIndent(B, 1, 1);
thenExpectMetadataOrderAndIndent(C, 2, 1);
thenExpectMetadataOrderAndIndent(D, 4, 2);
thenExpectMetadataOrderAndIndent(E, 5, 0);
thenExpectMetadataOrderAndIndent(F, 3, 2);
}
thenExpectMetadataOrderAndIndent(A, 0, 0);
thenExpectMetadataOrderAndIndent(B, 1, 1);
thenExpectMetadataOrderAndIndent(C, 2, 1);
thenExpectMetadataOrderAndIndent(D, 4, 2);
thenExpectMetadataOrderAndIndent(E, 5, 0);
thenExpectMetadataOrderAndIndent(F, 3, 2);
}
@Test
public void testMoveDownMultiple() {
givenTasksABCDEF();
@Test
public void testMoveDownMultiple() {
givenTasksABCDEF();
whenTriggerMove(B, F);
whenTriggerMove(B, F);
/*
* A
@ -238,77 +235,78 @@ public class GtasksTaskMovingTest extends InjectingTestCase {
* B
*/
thenExpectMetadataOrderAndIndent(A, 0, 0);
thenExpectMetadataOrderAndIndent(B, 4, 0);
thenExpectMetadataOrderAndIndent(C, 1, 1);
thenExpectMetadataOrderAndIndent(D, 2, 2);
thenExpectMetadataOrderAndIndent(E, 3, 0);
thenExpectMetadataOrderAndIndent(F, 5, 0);
}
// --- helpers
/** moveTo = null => move to end */
private void whenTriggerMove(Task target, Task moveTo) {
gtasksTaskListUpdater.moveTo(list, target.getId(), moveTo == null ? -1 : moveTo.getId());
}
private void thenExpectMetadataOrderAndIndent(Task task, long order, int indent) {
GoogleTask metadata = googleTaskDao.getByTaskId(task.getId());
assertNotNull("metadata was found", metadata);
assertEquals("order", order, metadata.getOrder());
assertEquals("indentation", indent, metadata.getIndent());
}
@Override
public void setUp() {
super.setUp();
List<TaskList> items = new ArrayList<>();
TaskList taskList = new TaskList();
taskList.setId("1");
taskList.setTitle("Tim's Tasks");
items.add(taskList);
gtasksListService.updateLists(items);
list = gtasksListService.getLists().get(0);
thenExpectMetadataOrderAndIndent(A, 0, 0);
thenExpectMetadataOrderAndIndent(B, 4, 0);
thenExpectMetadataOrderAndIndent(C, 1, 1);
thenExpectMetadataOrderAndIndent(D, 2, 2);
thenExpectMetadataOrderAndIndent(E, 3, 0);
thenExpectMetadataOrderAndIndent(F, 5, 0);
}
// --- helpers
/**
* moveTo = null => move to end
*/
private void whenTriggerMove(Task target, Task moveTo) {
gtasksTaskListUpdater.moveTo(list, target.getId(), moveTo == null ? -1 : moveTo.getId());
}
private void thenExpectMetadataOrderAndIndent(Task task, long order, int indent) {
GoogleTask metadata = googleTaskDao.getByTaskId(task.getId());
assertNotNull("metadata was found", metadata);
assertEquals("order", order, metadata.getOrder());
assertEquals("indentation", indent, metadata.getIndent());
}
@Override
public void setUp() {
super.setUp();
List<TaskList> items = new ArrayList<>();
TaskList taskList = new TaskList();
taskList.setId("1");
taskList.setTitle("Tim's Tasks");
items.add(taskList);
gtasksListService.updateLists(items);
list = gtasksListService.getLists().get(0);
}
@Override
protected void inject(TestComponent component) {
component.inject(this);
}
/**
* A
* B
* C
* D
* E
* F
*/
private void givenTasksABCDEF() {
A = createTask("A", 0, 0);
B = createTask("B", 1, 1);
C = createTask("C", 2, 1);
D = createTask("D", 3, 2);
E = createTask("E", 4, 0);
F = createTask("F", 5, 0);
}
private Task createTask(String title, long order, int indent) {
Task task = new Task();
task.setTitle(title);
taskDao.createNew(task);
GoogleTask metadata = new GoogleTask(task.getId(), "1");
if (order != VALUE_UNSET) {
metadata.setOrder(order);
}
@Override
protected void inject(TestComponent component) {
component.inject(this);
}
/**
* A
* B
* C
* D
* E
* F
*/
private void givenTasksABCDEF() {
A = createTask("A", 0, 0);
B = createTask("B", 1, 1);
C = createTask("C", 2, 1);
D = createTask("D", 3, 2);
E = createTask("E", 4, 0);
F = createTask("F", 5, 0);
}
private Task createTask(String title, long order, int indent) {
Task task = new Task();
task.setTitle(title);
taskDao.createNew(task);
GoogleTask metadata = new GoogleTask(task.getId(), "1");
if(order != VALUE_UNSET) {
metadata.setOrder(order);
}
if(indent != VALUE_UNSET) {
metadata.setIndent(indent);
}
googleTaskDao.insert(metadata);
return task;
if (indent != VALUE_UNSET) {
metadata.setIndent(indent);
}
googleTaskDao.insert(metadata);
return task;
}
}

@ -3,18 +3,19 @@
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.gtasks;
import org.tasks.preferences.Preferences;
public class GtasksTestPreferenceService extends GtasksPreferenceService {
public GtasksTestPreferenceService(Preferences preferences) {
super(preferences);
}
public GtasksTestPreferenceService(Preferences preferences) {
super(preferences);
}
@Override
public long getLastSyncDate() {
return 0L;
}
@Override
public long getLastSyncDate() {
return 0L;
}
}

@ -1,17 +1,5 @@
package com.todoroo.astrid.gtasks.api;
import android.support.test.runner.AndroidJUnit4;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.tasks.time.DateTime;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
import static com.todoroo.astrid.gtasks.api.GtasksApiUtilities.gtasksCompletedTimeToUnixTime;
import static com.todoroo.astrid.gtasks.api.GtasksApiUtilities.gtasksDueTimeToUnixTime;
import static com.todoroo.astrid.gtasks.api.GtasksApiUtilities.unixTimeToGtasksCompletionTime;
@ -19,66 +7,77 @@ import static com.todoroo.astrid.gtasks.api.GtasksApiUtilities.unixTimeToGtasksD
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNull;
import android.support.test.runner.AndroidJUnit4;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.tasks.time.DateTime;
@RunWith(AndroidJUnit4.class)
public class GtasksApiUtilitiesTest {
private static final Locale defaultLocale = Locale.getDefault();
private static final TimeZone defaultDateTimeZone = TimeZone.getDefault();
@Before
public void setUp() {
Locale.setDefault(Locale.US);
TimeZone.setDefault(TimeZone.getTimeZone("America/Chicago"));
}
@After
public void tearDown() {
Locale.setDefault(defaultLocale);
TimeZone.setDefault(defaultDateTimeZone);
}
@Test
public void testConvertUnixToGoogleCompletionTime() {
long now = new DateTime(2014, 1, 8, 8, 53, 20, 109).getMillis();
assertEquals(now, unixTimeToGtasksCompletionTime(now).getValue());
}
@Test
public void testConvertGoogleCompletedTimeToUnixTime() {
long now = new DateTime(2014, 1, 8, 8, 53, 20, 109).getMillis();
com.google.api.client.util.DateTime gtime = new com.google.api.client.util.DateTime(now);
assertEquals(now, gtasksCompletedTimeToUnixTime(gtime));
}
@Test
public void testConvertDueDateTimeToGoogleDueDate() {
DateTime now = new DateTime(2014, 1, 8, 8, 53, 20, 109);
assertEquals(
new DateTime(2014, 1, 8, 0, 0, 0, 0, TimeZone.getTimeZone("GMT")).getMillis(),
unixTimeToGtasksDueDate(now.getMillis()).getValue());
}
@Test
public void testConvertGoogleDueDateToUnixTime() {
com.google.api.client.util.DateTime googleDueDate =
new com.google.api.client.util.DateTime(
new Date(new DateTime(2014, 1, 8, 0, 0, 0, 0).getMillis()), TimeZone.getTimeZone("GMT"));
assertEquals(
new DateTime(2014, 1, 8, 6, 0, 0, 0).getMillis(),
gtasksDueTimeToUnixTime(googleDueDate));
}
@Test
public void testConvertToInvalidGtaskTimes() {
assertNull(unixTimeToGtasksCompletionTime(-1));
assertNull(unixTimeToGtasksDueDate(-1));
}
@Test
public void testConvertFromInvalidGtaskTimes() {
assertEquals(0, gtasksCompletedTimeToUnixTime(null));
assertEquals(0, gtasksDueTimeToUnixTime(null));
}
private static final Locale defaultLocale = Locale.getDefault();
private static final TimeZone defaultDateTimeZone = TimeZone.getDefault();
@Before
public void setUp() {
Locale.setDefault(Locale.US);
TimeZone.setDefault(TimeZone.getTimeZone("America/Chicago"));
}
@After
public void tearDown() {
Locale.setDefault(defaultLocale);
TimeZone.setDefault(defaultDateTimeZone);
}
@Test
public void testConvertUnixToGoogleCompletionTime() {
long now = new DateTime(2014, 1, 8, 8, 53, 20, 109).getMillis();
assertEquals(now, unixTimeToGtasksCompletionTime(now).getValue());
}
@Test
public void testConvertGoogleCompletedTimeToUnixTime() {
long now = new DateTime(2014, 1, 8, 8, 53, 20, 109).getMillis();
com.google.api.client.util.DateTime gtime = new com.google.api.client.util.DateTime(now);
assertEquals(now, gtasksCompletedTimeToUnixTime(gtime));
}
@Test
public void testConvertDueDateTimeToGoogleDueDate() {
DateTime now = new DateTime(2014, 1, 8, 8, 53, 20, 109);
assertEquals(
new DateTime(2014, 1, 8, 0, 0, 0, 0, TimeZone.getTimeZone("GMT")).getMillis(),
unixTimeToGtasksDueDate(now.getMillis()).getValue());
}
@Test
public void testConvertGoogleDueDateToUnixTime() {
com.google.api.client.util.DateTime googleDueDate =
new com.google.api.client.util.DateTime(
new Date(new DateTime(2014, 1, 8, 0, 0, 0, 0).getMillis()),
TimeZone.getTimeZone("GMT"));
assertEquals(
new DateTime(2014, 1, 8, 6, 0, 0, 0).getMillis(),
gtasksDueTimeToUnixTime(googleDueDate));
}
@Test
public void testConvertToInvalidGtaskTimes() {
assertNull(unixTimeToGtasksCompletionTime(-1));
assertNull(unixTimeToGtasksDueDate(-1));
}
@Test
public void testConvertFromInvalidGtaskTimes() {
assertEquals(0, gtasksCompletedTimeToUnixTime(null));
assertEquals(0, gtasksDueTimeToUnixTime(null));
}
}

@ -1,13 +1,5 @@
package org.tasks.gtasks;
import android.support.test.runner.AndroidJUnit4;
import com.todoroo.astrid.data.Task;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.tasks.time.DateTime;
import static com.natpryce.makeiteasy.MakeItEasy.with;
import static com.todoroo.astrid.data.Task.HIDE_UNTIL_DUE;
import static com.todoroo.astrid.data.Task.HIDE_UNTIL_DUE_TIME;
@ -18,93 +10,102 @@ import static org.tasks.makers.TaskMaker.DUE_TIME;
import static org.tasks.makers.TaskMaker.HIDE_TYPE;
import static org.tasks.makers.TaskMaker.newTask;
import android.support.test.runner.AndroidJUnit4;
import com.todoroo.astrid.data.Task;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.tasks.time.DateTime;
@RunWith(AndroidJUnit4.class)
public class GoogleTaskSyncAdapterTest {
@Test
public void testMergeDate() {
Task local = newTask(with(DUE_DATE, new DateTime(2016, 3, 12)));
@Test
public void testMergeDate() {
Task local = newTask(with(DUE_DATE, new DateTime(2016, 3, 12)));
mergeDates(newTask(with(DUE_DATE, new DateTime(2016, 3, 11))).getDueDate(), local);
mergeDates(newTask(with(DUE_DATE, new DateTime(2016, 3, 11))).getDueDate(), local);
assertEquals(
new DateTime(2016, 3, 11, 12, 0).getMillis(),
local.getDueDate().longValue());
}
assertEquals(
new DateTime(2016, 3, 11, 12, 0).getMillis(),
local.getDueDate().longValue());
}
@Test
public void testMergeTime() {
Task local = newTask(with(DUE_TIME, new DateTime(2016, 3, 11, 13, 30)));
@Test
public void testMergeTime() {
Task local = newTask(with(DUE_TIME, new DateTime(2016, 3, 11, 13, 30)));
mergeDates(newTask(with(DUE_DATE, new DateTime(2016, 3, 11))).getDueDate(), local);
mergeDates(newTask(with(DUE_DATE, new DateTime(2016, 3, 11))).getDueDate(), local);
assertEquals(
new DateTime(2016, 3, 11, 13, 30, 1).getMillis(),
local.getDueDate().longValue());
}
assertEquals(
new DateTime(2016, 3, 11, 13, 30, 1).getMillis(),
local.getDueDate().longValue());
}
@Test
public void testDueDateAdjustHideBackwards() {
Task local = newTask(with(DUE_DATE, new DateTime(2016, 3, 12)), with(HIDE_TYPE, HIDE_UNTIL_DUE));
@Test
public void testDueDateAdjustHideBackwards() {
Task local = newTask(with(DUE_DATE, new DateTime(2016, 3, 12)),
with(HIDE_TYPE, HIDE_UNTIL_DUE));
mergeDates(newTask(with(DUE_DATE, new DateTime(2016, 3, 11))).getDueDate(), local);
mergeDates(newTask(with(DUE_DATE, new DateTime(2016, 3, 11))).getDueDate(), local);
assertEquals(
new DateTime(2016, 3, 11).getMillis(),
local.getHideUntil().longValue());
}
assertEquals(
new DateTime(2016, 3, 11).getMillis(),
local.getHideUntil().longValue());
}
@Test
public void testDueDateAdjustHideForwards() {
Task local = newTask(with(DUE_DATE, new DateTime(2016, 3, 12)), with(HIDE_TYPE, HIDE_UNTIL_DUE));
@Test
public void testDueDateAdjustHideForwards() {
Task local = newTask(with(DUE_DATE, new DateTime(2016, 3, 12)),
with(HIDE_TYPE, HIDE_UNTIL_DUE));
mergeDates(newTask(with(DUE_DATE, new DateTime(2016, 3, 14))).getDueDate(), local);
mergeDates(newTask(with(DUE_DATE, new DateTime(2016, 3, 14))).getDueDate(), local);
assertEquals(
new DateTime(2016, 3, 14).getMillis(),
local.getHideUntil().longValue());
}
assertEquals(
new DateTime(2016, 3, 14).getMillis(),
local.getHideUntil().longValue());
}
@Test
public void testDueTimeAdjustHideBackwards() {
Task local = newTask(with(DUE_TIME, new DateTime(2016, 3, 12, 13, 30)),
with(HIDE_TYPE, HIDE_UNTIL_DUE_TIME));
@Test
public void testDueTimeAdjustHideBackwards() {
Task local = newTask(with(DUE_TIME, new DateTime(2016, 3, 12, 13, 30)),
with(HIDE_TYPE, HIDE_UNTIL_DUE_TIME));
mergeDates(newTask(with(DUE_DATE, new DateTime(2016, 3, 11))).getDueDate(), local);
mergeDates(newTask(with(DUE_DATE, new DateTime(2016, 3, 11))).getDueDate(), local);
assertEquals(
new DateTime(2016, 3, 11, 13, 30, 1).getMillis(),
local.getHideUntil().longValue());
}
assertEquals(
new DateTime(2016, 3, 11, 13, 30, 1).getMillis(),
local.getHideUntil().longValue());
}
@Test
public void testDueTimeAdjustTimeForwards() {
Task local = newTask(with(DUE_TIME, new DateTime(2016, 3, 12, 13, 30)),
with(HIDE_TYPE, HIDE_UNTIL_DUE_TIME));
@Test
public void testDueTimeAdjustTimeForwards() {
Task local = newTask(with(DUE_TIME, new DateTime(2016, 3, 12, 13, 30)),
with(HIDE_TYPE, HIDE_UNTIL_DUE_TIME));
mergeDates(newTask(with(DUE_DATE, new DateTime(2016, 3, 14))).getDueDate(), local);
mergeDates(newTask(with(DUE_DATE, new DateTime(2016, 3, 14))).getDueDate(), local);
assertEquals(
new DateTime(2016, 3, 14, 13, 30, 1).getMillis(),
local.getHideUntil().longValue());
}
assertEquals(
new DateTime(2016, 3, 14, 13, 30, 1).getMillis(),
local.getHideUntil().longValue());
}
@Test
public void testDueDateClearHide() {
Task local = newTask(with(DUE_DATE, new DateTime(2016, 3, 12)), with(HIDE_TYPE, HIDE_UNTIL_DUE));
@Test
public void testDueDateClearHide() {
Task local = newTask(with(DUE_DATE, new DateTime(2016, 3, 12)),
with(HIDE_TYPE, HIDE_UNTIL_DUE));
mergeDates(newTask().getDueDate(), local);
mergeDates(newTask().getDueDate(), local);
assertEquals(0, local.getHideUntil().longValue());
}
assertEquals(0, local.getHideUntil().longValue());
}
@Test
public void testDueTimeClearHide() {
Task local = newTask(with(DUE_TIME, new DateTime(2016, 3, 12, 13, 30)),
with(HIDE_TYPE, HIDE_UNTIL_DUE_TIME));
@Test
public void testDueTimeClearHide() {
Task local = newTask(with(DUE_TIME, new DateTime(2016, 3, 12, 13, 30)),
with(HIDE_TYPE, HIDE_UNTIL_DUE_TIME));
mergeDates(newTask().getDueDate(), local);
mergeDates(newTask().getDueDate(), local);
assertEquals(0, local.getHideUntil().longValue());
}
assertEquals(0, local.getHideUntil().longValue());
}
}

@ -15,44 +15,43 @@ import com.todoroo.astrid.service.TitleParserTest;
import com.todoroo.astrid.subtasks.SubtasksHelperTest;
import com.todoroo.astrid.subtasks.SubtasksTestCase;
import com.todoroo.astrid.sync.NewSyncTestCase;
import org.tasks.jobs.BackupServiceTests;
import dagger.Component;
import org.tasks.jobs.BackupServiceTests;
@ApplicationScope
@Component(modules = TestModule.class)
public interface TestComponent {
GtasksMetadataServiceTest.GtasksMetadataServiceTestComponent plus(GtasksMetadataServiceTest.GtasksMetadataServiceTestModule gtasksMetadataServiceTestModule);
GtasksMetadataServiceTest.GtasksMetadataServiceTestComponent plus(
GtasksMetadataServiceTest.GtasksMetadataServiceTestModule gtasksMetadataServiceTestModule);
void inject(GtasksIndentActionTest gtasksIndentActionTest);
void inject(GtasksIndentActionTest gtasksIndentActionTest);
void inject(GtasksTaskMovingTest gtasksTaskMovingTest);
void inject(GtasksTaskMovingTest gtasksTaskMovingTest);
void inject(GtasksListServiceTest gtasksListServiceTest);
void inject(GtasksListServiceTest gtasksListServiceTest);
void inject(GtasksTaskListUpdaterTest gtasksTaskListUpdaterTest);
void inject(GtasksTaskListUpdaterTest gtasksTaskListUpdaterTest);
void inject(ReminderServiceTest reminderServiceTest);
void inject(ReminderServiceTest reminderServiceTest);
void inject(TaskTest taskTest);
void inject(TaskTest taskTest);
void inject(TaskDaoTests taskDaoTests);
void inject(TaskDaoTests taskDaoTests);
void inject(NewSyncTestCase newSyncTestCase);
void inject(NewSyncTestCase newSyncTestCase);
void inject(SubtasksTestCase subtasksTestCase);
void inject(SubtasksTestCase subtasksTestCase);
void inject(SubtasksHelperTest subtasksHelperTest);
void inject(SubtasksHelperTest subtasksHelperTest);
void inject(QuickAddMarkupTest quickAddMarkupTest);
void inject(QuickAddMarkupTest quickAddMarkupTest);
void inject(TitleParserTest titleParserTest);
void inject(TitleParserTest titleParserTest);
void inject(BackupServiceTests backupServiceTests);
void inject(BackupServiceTests backupServiceTests);
void inject(AlarmJobServiceTest alarmServiceTest);
void inject(AlarmJobServiceTest alarmServiceTest);
void inject(RepeatTaskHelperTest repeatTaskHelperTest);
void inject(RepeatTaskHelperTest repeatTaskHelperTest);
}

@ -1,25 +1,25 @@
package org.tasks.makers;
import static com.natpryce.makeiteasy.Property.newProperty;
import static org.tasks.makers.Maker.make;
import static org.tasks.time.DateTimeUtils.currentTimeMillis;
import com.google.api.client.util.DateTime;
import com.google.api.services.tasks.model.TaskList;
import com.natpryce.makeiteasy.Instantiator;
import com.natpryce.makeiteasy.Property;
import com.natpryce.makeiteasy.PropertyValue;
import static com.natpryce.makeiteasy.Property.newProperty;
import static org.tasks.time.DateTimeUtils.currentTimeMillis;
import static org.tasks.makers.Maker.make;
public class RemoteGtaskListMaker {
public static final Property<TaskList, String> REMOTE_ID = newProperty();
public static final Property<TaskList, String> NAME = newProperty();
public static TaskList newRemoteList(PropertyValue<? super TaskList, ?>... properties) {
return make(instantiator, properties);
}
public static final Property<TaskList, String> REMOTE_ID = newProperty();
public static final Property<TaskList, String> NAME = newProperty();
private static final Instantiator<TaskList> instantiator = lookup -> new TaskList()
.setId(lookup.valueOf(REMOTE_ID, "1"))
.setTitle(lookup.valueOf(NAME, "Default"))
.setUpdated(new DateTime(currentTimeMillis()));
private static final Instantiator<TaskList> instantiator = lookup -> new TaskList()
.setId(lookup.valueOf(REMOTE_ID, "1"))
.setTitle(lookup.valueOf(NAME, "Default"))
.setUpdated(new DateTime(currentTimeMillis()));
public static TaskList newRemoteList(PropertyValue<? super TaskList, ?>... properties) {
return make(instantiator, properties);
}
}

@ -3,4 +3,5 @@ package org.tasks;
import android.support.multidex.MultiDexApplication;
public class BaseApplication extends MultiDexApplication {
}

@ -3,50 +3,47 @@ package org.tasks;
import android.app.Application;
import android.content.Context;
import android.os.StrictMode;
import com.facebook.stetho.Stetho;
import com.facebook.stetho.timber.StethoTree;
import com.squareup.leakcanary.LeakCanary;
import javax.inject.Inject;
import org.tasks.injection.ForApplication;
import org.tasks.preferences.Preferences;
import javax.inject.Inject;
import timber.log.Timber;
public class BuildSetup {
private final Context context;
private final Preferences preferences;
@Inject
public BuildSetup(@ForApplication Context context, Preferences preferences) {
this.context = context;
this.preferences = preferences;
private final Context context;
private final Preferences preferences;
@Inject
public BuildSetup(@ForApplication Context context, Preferences preferences) {
this.context = context;
this.preferences = preferences;
}
public boolean setup() {
Timber.plant(new Timber.DebugTree());
Timber.plant(new StethoTree());
Stetho.initializeWithDefaults(context);
Application application = (Application) context.getApplicationContext();
if (LeakCanary.isInAnalyzerProcess(context)) {
return false;
}
public boolean setup() {
Timber.plant(new Timber.DebugTree());
Timber.plant(new StethoTree());
Stetho.initializeWithDefaults(context);
Application application = (Application) context.getApplicationContext();
if (LeakCanary.isInAnalyzerProcess(context)) {
return false;
}
LeakCanary.install(application);
if (preferences.getBoolean(R.string.p_strict_mode, false)) {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork()
.penaltyLog()
.build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects()
.detectLeakedClosableObjects()
.penaltyLog()
.build());
}
return true;
LeakCanary.install(application);
if (preferences.getBoolean(R.string.p_strict_mode, false)) {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork()
.penaltyLog()
.build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects()
.detectLeakedClosableObjects()
.penaltyLog()
.build());
}
return true;
}
}

@ -1,21 +1,22 @@
package org.tasks;
import javax.inject.Inject;
import org.tasks.caldav.CaldavAccountManager;
import org.tasks.preferences.Preferences;
import javax.inject.Inject;
public class FlavorSetup {
private final CaldavAccountManager caldavAccountManager;
private final Preferences preferences;
@Inject
public FlavorSetup(CaldavAccountManager caldavAccountManager, Preferences preferences) {
this.caldavAccountManager = caldavAccountManager;
this.preferences = preferences;
}
private final CaldavAccountManager caldavAccountManager;
private final Preferences preferences;
@Inject
public FlavorSetup(CaldavAccountManager caldavAccountManager, Preferences preferences) {
this.caldavAccountManager = caldavAccountManager;
this.preferences = preferences;
}
public void setup() {
caldavAccountManager.setBackgroundSynchronization(preferences.getBoolean(R.string.p_background_sync, true));
}
public void setup() {
caldavAccountManager
.setBackgroundSynchronization(preferences.getBoolean(R.string.p_background_sync, true));
}
}

@ -1,41 +1,40 @@
package org.tasks.analytics;
import javax.inject.Inject;
import timber.log.Timber;
public class Tracker {
@Inject
public Tracker() {
@Inject
public Tracker() {
}
}
public void setTrackingEnabled(boolean enabled) {
public void setTrackingEnabled(boolean enabled) {
}
}
public void reportException(Throwable t) {
Timber.e(t, t.getMessage());
}
public void reportException(Throwable t) {
Timber.e(t, t.getMessage());
}
public void reportException(Thread thread, Throwable t) {
Timber.e(t, t.getMessage());
}
public void reportException(Thread thread, Throwable t) {
Timber.e(t, t.getMessage());
}
public void reportEvent(Tracking.Events event) {
public void reportEvent(Tracking.Events event) {
}
}
public void reportEvent(Tracking.Events event, String string) {
public void reportEvent(Tracking.Events event, String string) {
}
}
public void reportEvent(Tracking.Events setPreference, int resId, String s) {
public void reportEvent(Tracking.Events setPreference, int resId, String s) {
}
}
public void reportEvent(Tracking.Events category, String action, String label) {
public void reportEvent(Tracking.Events category, String action, String label) {
}
}
}

@ -2,36 +2,36 @@ package org.tasks.billing;
import android.app.Activity;
import android.content.Intent;
import org.tasks.preferences.Preferences;
import javax.inject.Inject;
import org.tasks.preferences.Preferences;
public class PurchaseHelper {
private Preferences preferences;
@Inject
public PurchaseHelper(Preferences preferences) {
this.preferences = preferences;
}
private Preferences preferences;
@Inject
public PurchaseHelper(Preferences preferences) {
this.preferences = preferences;
}
public boolean purchase(final Activity activity,
final String sku, final String pref,
final int requestCode, final PurchaseHelperCallback callback) {
preferences.setBoolean(pref, true);
callback.purchaseCompleted(true, sku);
return true;
}
public boolean purchase(final Activity activity,
final String sku, final String pref,
final int requestCode, final PurchaseHelperCallback callback) {
preferences.setBoolean(pref, true);
callback.purchaseCompleted(true, sku);
return true;
}
public void handleActivityResult(PurchaseHelperCallback callback, int requestCode, int resultCode, Intent data) {
public void handleActivityResult(PurchaseHelperCallback callback, int requestCode, int resultCode,
Intent data) {
}
}
public void disposeIabHelper() {
public void disposeIabHelper() {
}
}
public void consumePurchases() {
public void consumePurchases() {
}
}
}

@ -1,40 +1,39 @@
package org.tasks.gtasks;
import android.app.Activity;
import com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential;
import com.todoroo.astrid.gtasks.auth.GtasksLoginActivity;
import javax.inject.Inject;
public class PlayServices {
@Inject
public PlayServices() {
@Inject
public PlayServices() {
}
}
public boolean isPlayServicesAvailable() {
return false;
}
public boolean isPlayServicesAvailable() {
return false;
}
public boolean refreshAndCheck() {
return false;
}
public boolean refreshAndCheck() {
return false;
}
public void resolve(Activity activity) {
public void resolve(Activity activity) {
}
}
public String getStatus() {
return null;
}
public String getStatus() {
return null;
}
public boolean clearToken(GoogleAccountCredential googleAccountCredential) {
return false;
}
public boolean clearToken(GoogleAccountCredential googleAccountCredential) {
return false;
}
public void getAuthToken(GtasksLoginActivity gtasksLoginActivity, String a, GtasksLoginActivity.AuthResultHandler authResultHandler) {
public void getAuthToken(GtasksLoginActivity gtasksLoginActivity, String a,
GtasksLoginActivity.AuthResultHandler authResultHandler) {
}
}
}

@ -1,28 +1,26 @@
package org.tasks.location;
import org.tasks.data.Location;
import java.util.List;
import javax.inject.Inject;
import org.tasks.data.Location;
@SuppressWarnings("EmptyMethod")
public class GeofenceApi {
@Inject
public GeofenceApi() {
@Inject
public GeofenceApi() {
}
}
public void register(List<Location> activeGeofences) {
public void register(List<Location> activeGeofences) {
}
}
public void cancel(Location geofence) {
public void cancel(Location geofence) {
}
}
public void cancel(List<Location> geofences) {
public void cancel(List<Location> geofences) {
}
}
}

@ -3,16 +3,16 @@ package org.tasks.location;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import org.tasks.data.Location;
import org.tasks.preferences.Preferences;
public class PlacePicker {
public static Intent getIntent(Activity activity) {
return null;
}
public static Location getPlace(Context context, Intent data, Preferences preferences) {
return null;
}
public static Intent getIntent(Activity activity) {
return null;
}
public static Location getPlace(Context context, Intent data, Preferences preferences) {
return null;
}
}

@ -33,28 +33,29 @@ import android.content.Intent;
* has been acquired.</p>
*/
public class IabBroadcastReceiver extends BroadcastReceiver {
/**
* Listener interface for received broadcast messages.
*/
public interface IabBroadcastListener {
void receivedBroadcast();
}
/**
* The Intent action that this Receiver should filter for.
*/
public static final String ACTION = "com.android.vending.billing.PURCHASES_UPDATED";
/**
* The Intent action that this Receiver should filter for.
*/
public static final String ACTION = "com.android.vending.billing.PURCHASES_UPDATED";
private final IabBroadcastListener mListener;
private final IabBroadcastListener mListener;
public IabBroadcastReceiver(IabBroadcastListener listener) {
mListener = listener;
}
public IabBroadcastReceiver(IabBroadcastListener listener) {
mListener = listener;
@Override
public void onReceive(Context context, Intent intent) {
if (mListener != null) {
mListener.receivedBroadcast();
}
}
@Override
public void onReceive(Context context, Intent intent) {
if (mListener != null) {
mListener.receivedBroadcast();
}
}
/**
* Listener interface for received broadcast messages.
*/
public interface IabBroadcastListener {
void receivedBroadcast();
}
}

@ -23,22 +23,30 @@ package com.android.vending.billing;
*/
@SuppressWarnings("ALL")
public class IabException extends Exception {
IabResult mResult;
public IabException(IabResult r) {
this(r, null);
}
public IabException(int response, String message) {
this(new IabResult(response, message));
}
public IabException(IabResult r, Exception cause) {
super(r.getMessage(), cause);
mResult = r;
}
public IabException(int response, String message, Exception cause) {
this(new IabResult(response, message), cause);
}
/** Returns the IAB result (error) that this exception signals. */
public IabResult getResult() { return mResult; }
IabResult mResult;
public IabException(IabResult r) {
this(r, null);
}
public IabException(int response, String message) {
this(new IabResult(response, message));
}
public IabException(IabResult r, Exception cause) {
super(r.getMessage(), cause);
mResult = r;
}
public IabException(int response, String message, Exception cause) {
this(new IabResult(response, message), cause);
}
/**
* Returns the IAB result (error) that this exception signals.
*/
public IabResult getResult() {
return mResult;
}
}

@ -25,22 +25,37 @@ package com.android.vending.billing;
*/
@SuppressWarnings("ALL")
public class IabResult {
int mResponse;
String mMessage;
public IabResult(int response, String message) {
mResponse = response;
if (message == null || message.trim().length() == 0) {
mMessage = IabHelper.getResponseDesc(response);
}
else {
mMessage = message + " (response: " + IabHelper.getResponseDesc(response) + ")";
}
int mResponse;
String mMessage;
public IabResult(int response, String message) {
mResponse = response;
if (message == null || message.trim().length() == 0) {
mMessage = IabHelper.getResponseDesc(response);
} else {
mMessage = message + " (response: " + IabHelper.getResponseDesc(response) + ")";
}
public int getResponse() { return mResponse; }
public String getMessage() { return mMessage; }
public boolean isSuccess() { return mResponse == IabHelper.BILLING_RESPONSE_RESULT_OK; }
public boolean isFailure() { return !isSuccess(); }
public String toString() { return "IabResult: " + getMessage(); }
}
public int getResponse() {
return mResponse;
}
public String getMessage() {
return mMessage;
}
public boolean isSuccess() {
return mResponse == IabHelper.BILLING_RESPONSE_RESULT_OK;
}
public boolean isFailure() {
return !isSuccess();
}
public String toString() {
return "IabResult: " + getMessage();
}
}

@ -26,67 +26,87 @@ import java.util.Map;
*/
@SuppressWarnings("ALL")
public class Inventory {
Map<String,SkuDetails> mSkuMap = new HashMap<String,SkuDetails>();
Map<String,Purchase> mPurchaseMap = new HashMap<String,Purchase>();
Inventory() { }
Map<String, SkuDetails> mSkuMap = new HashMap<String, SkuDetails>();
Map<String, Purchase> mPurchaseMap = new HashMap<String, Purchase>();
/** Returns the listing details for an in-app product. */
public SkuDetails getSkuDetails(String sku) {
return mSkuMap.get(sku);
}
Inventory() {
}
/** Returns purchase information for a given product, or null if there is no purchase. */
public Purchase getPurchase(String sku) {
return mPurchaseMap.get(sku);
}
/**
* Returns the listing details for an in-app product.
*/
public SkuDetails getSkuDetails(String sku) {
return mSkuMap.get(sku);
}
/** Returns whether or not there exists a purchase of the given product. */
public boolean hasPurchase(String sku) {
return mPurchaseMap.containsKey(sku);
}
/**
* Returns purchase information for a given product, or null if there is no purchase.
*/
public Purchase getPurchase(String sku) {
return mPurchaseMap.get(sku);
}
/** Return whether or not details about the given product are available. */
public boolean hasDetails(String sku) {
return mSkuMap.containsKey(sku);
}
/**
* Returns whether or not there exists a purchase of the given product.
*/
public boolean hasPurchase(String sku) {
return mPurchaseMap.containsKey(sku);
}
/**
* Erase a purchase (locally) from the inventory, given its product ID. This just
* modifies the Inventory object locally and has no effect on the server! This is
* useful when you have an existing Inventory object which you know to be up to date,
* and you have just consumed an item successfully, which means that erasing its
* purchase data from the Inventory you already have is quicker than querying for
* a new Inventory.
*/
public void erasePurchase(String sku) {
if (mPurchaseMap.containsKey(sku)) mPurchaseMap.remove(sku);
}
/**
* Return whether or not details about the given product are available.
*/
public boolean hasDetails(String sku) {
return mSkuMap.containsKey(sku);
}
/** Returns a list of all owned product IDs. */
List<String> getAllOwnedSkus() {
return new ArrayList<String>(mPurchaseMap.keySet());
/**
* Erase a purchase (locally) from the inventory, given its product ID. This just
* modifies the Inventory object locally and has no effect on the server! This is
* useful when you have an existing Inventory object which you know to be up to date,
* and you have just consumed an item successfully, which means that erasing its
* purchase data from the Inventory you already have is quicker than querying for
* a new Inventory.
*/
public void erasePurchase(String sku) {
if (mPurchaseMap.containsKey(sku)) {
mPurchaseMap.remove(sku);
}
}
/** Returns a list of all owned product IDs of a given type */
List<String> getAllOwnedSkus(String itemType) {
List<String> result = new ArrayList<String>();
for (Purchase p : mPurchaseMap.values()) {
if (p.getItemType().equals(itemType)) result.add(p.getSku());
}
return result;
}
/**
* Returns a list of all owned product IDs.
*/
List<String> getAllOwnedSkus() {
return new ArrayList<String>(mPurchaseMap.keySet());
}
/** Returns a list of all purchases. */
List<Purchase> getAllPurchases() {
return new ArrayList<Purchase>(mPurchaseMap.values());
/**
* Returns a list of all owned product IDs of a given type
*/
List<String> getAllOwnedSkus(String itemType) {
List<String> result = new ArrayList<String>();
for (Purchase p : mPurchaseMap.values()) {
if (p.getItemType().equals(itemType)) {
result.add(p.getSku());
}
}
return result;
}
void addSkuDetails(SkuDetails d) {
mSkuMap.put(d.getSku(), d);
}
/**
* Returns a list of all purchases.
*/
List<Purchase> getAllPurchases() {
return new ArrayList<Purchase>(mPurchaseMap.values());
}
void addPurchase(Purchase p) {
mPurchaseMap.put(p.getSku(), p);
}
void addSkuDetails(SkuDetails d) {
mSkuMap.put(d.getSku(), d);
}
void addPurchase(Purchase p) {
mPurchaseMap.put(p.getSku(), p);
}
}

@ -23,45 +23,80 @@ import org.json.JSONObject;
*/
@SuppressWarnings("ALL")
public class Purchase {
String mItemType; // ITEM_TYPE_INAPP or ITEM_TYPE_SUBS
String mOrderId;
String mPackageName;
String mSku;
long mPurchaseTime;
int mPurchaseState;
String mDeveloperPayload;
String mToken;
String mOriginalJson;
String mSignature;
boolean mIsAutoRenewing;
public Purchase(String itemType, String jsonPurchaseInfo, String signature) throws JSONException {
mItemType = itemType;
mOriginalJson = jsonPurchaseInfo;
JSONObject o = new JSONObject(mOriginalJson);
mOrderId = o.optString("orderId");
mPackageName = o.optString("packageName");
mSku = o.optString("productId");
mPurchaseTime = o.optLong("purchaseTime");
mPurchaseState = o.optInt("purchaseState");
mDeveloperPayload = o.optString("developerPayload");
mToken = o.optString("token", o.optString("purchaseToken"));
mIsAutoRenewing = o.optBoolean("autoRenewing");
mSignature = signature;
}
public String getItemType() { return mItemType; }
public String getOrderId() { return mOrderId; }
public String getPackageName() { return mPackageName; }
public String getSku() { return mSku; }
public long getPurchaseTime() { return mPurchaseTime; }
public int getPurchaseState() { return mPurchaseState; }
public String getDeveloperPayload() { return mDeveloperPayload; }
public String getToken() { return mToken; }
public String getOriginalJson() { return mOriginalJson; }
public String getSignature() { return mSignature; }
public boolean isAutoRenewing() { return mIsAutoRenewing; }
@Override
public String toString() { return "PurchaseInfo(type:" + mItemType + "):" + mOriginalJson; }
String mItemType; // ITEM_TYPE_INAPP or ITEM_TYPE_SUBS
String mOrderId;
String mPackageName;
String mSku;
long mPurchaseTime;
int mPurchaseState;
String mDeveloperPayload;
String mToken;
String mOriginalJson;
String mSignature;
boolean mIsAutoRenewing;
public Purchase(String itemType, String jsonPurchaseInfo, String signature) throws JSONException {
mItemType = itemType;
mOriginalJson = jsonPurchaseInfo;
JSONObject o = new JSONObject(mOriginalJson);
mOrderId = o.optString("orderId");
mPackageName = o.optString("packageName");
mSku = o.optString("productId");
mPurchaseTime = o.optLong("purchaseTime");
mPurchaseState = o.optInt("purchaseState");
mDeveloperPayload = o.optString("developerPayload");
mToken = o.optString("token", o.optString("purchaseToken"));
mIsAutoRenewing = o.optBoolean("autoRenewing");
mSignature = signature;
}
public String getItemType() {
return mItemType;
}
public String getOrderId() {
return mOrderId;
}
public String getPackageName() {
return mPackageName;
}
public String getSku() {
return mSku;
}
public long getPurchaseTime() {
return mPurchaseTime;
}
public int getPurchaseState() {
return mPurchaseState;
}
public String getDeveloperPayload() {
return mDeveloperPayload;
}
public String getToken() {
return mToken;
}
public String getOriginalJson() {
return mOriginalJson;
}
public String getSignature() {
return mSignature;
}
public boolean isAutoRenewing() {
return mIsAutoRenewing;
}
@Override
public String toString() {
return "PurchaseInfo(type:" + mItemType + "):" + mOriginalJson;
}
}

@ -19,7 +19,6 @@ import android.annotation.SuppressLint;
import android.text.TextUtils;
import android.util.Base64;
import android.util.Log;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
@ -41,84 +40,87 @@ import java.security.spec.X509EncodedKeySpec;
@SuppressWarnings("ALL")
@SuppressLint("all")
public class Security {
private static final String TAG = "IABUtil/Security";
private static final String KEY_FACTORY_ALGORITHM = "RSA";
private static final String SIGNATURE_ALGORITHM = "SHA1withRSA";
private static final String TAG = "IABUtil/Security";
/**
* Verifies that the data was signed with the given signature, and returns
* the verified purchase. The data is in JSON format and signed
* with a private key. The data also contains the {@link PurchaseState}
* and product ID of the purchase.
* @param base64PublicKey the base64-encoded public key to use for verifying.
* @param signedData the signed JSON string (signed, not encrypted)
* @param signature the signature for the data, signed with the private key
*/
public static boolean verifyPurchase(String base64PublicKey, String signedData, String signature) {
if (TextUtils.isEmpty(signedData) || TextUtils.isEmpty(base64PublicKey) ||
TextUtils.isEmpty(signature)) {
Log.e(TAG, "Purchase verification failed: missing data.");
return false;
}
private static final String KEY_FACTORY_ALGORITHM = "RSA";
private static final String SIGNATURE_ALGORITHM = "SHA1withRSA";
PublicKey key = Security.generatePublicKey(base64PublicKey);
return Security.verify(key, signedData, signature);
/**
* Verifies that the data was signed with the given signature, and returns
* the verified purchase. The data is in JSON format and signed
* with a private key. The data also contains the {@link PurchaseState}
* and product ID of the purchase.
*
* @param base64PublicKey the base64-encoded public key to use for verifying.
* @param signedData the signed JSON string (signed, not encrypted)
* @param signature the signature for the data, signed with the private key
*/
public static boolean verifyPurchase(String base64PublicKey, String signedData,
String signature) {
if (TextUtils.isEmpty(signedData) || TextUtils.isEmpty(base64PublicKey) ||
TextUtils.isEmpty(signature)) {
Log.e(TAG, "Purchase verification failed: missing data.");
return false;
}
/**
* Generates a PublicKey instance from a string containing the
* Base64-encoded public key.
*
* @param encodedPublicKey Base64-encoded public key
* @throws IllegalArgumentException if encodedPublicKey is invalid
*/
public static PublicKey generatePublicKey(String encodedPublicKey) {
try {
byte[] decodedKey = Base64.decode(encodedPublicKey, Base64.DEFAULT);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_FACTORY_ALGORITHM);
return keyFactory.generatePublic(new X509EncodedKeySpec(decodedKey));
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
} catch (InvalidKeySpecException e) {
Log.e(TAG, "Invalid key specification.");
throw new IllegalArgumentException(e);
}
PublicKey key = Security.generatePublicKey(base64PublicKey);
return Security.verify(key, signedData, signature);
}
/**
* Generates a PublicKey instance from a string containing the
* Base64-encoded public key.
*
* @param encodedPublicKey Base64-encoded public key
* @throws IllegalArgumentException if encodedPublicKey is invalid
*/
public static PublicKey generatePublicKey(String encodedPublicKey) {
try {
byte[] decodedKey = Base64.decode(encodedPublicKey, Base64.DEFAULT);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_FACTORY_ALGORITHM);
return keyFactory.generatePublic(new X509EncodedKeySpec(decodedKey));
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
} catch (InvalidKeySpecException e) {
Log.e(TAG, "Invalid key specification.");
throw new IllegalArgumentException(e);
}
}
/**
* Verifies that the signature from the server matches the computed
* signature on the data. Returns true if the data is correctly signed.
*
* @param publicKey public key associated with the developer account
* @param signedData signed data from server
* @param signature server signature
* @return true if the data and signature match
*/
public static boolean verify(PublicKey publicKey, String signedData, String signature) {
byte[] signatureBytes;
try {
signatureBytes = Base64.decode(signature, Base64.DEFAULT);
} catch (IllegalArgumentException e) {
Log.e(TAG, "Base64 decoding failed.");
return false;
}
try {
Signature sig = Signature.getInstance(SIGNATURE_ALGORITHM);
sig.initVerify(publicKey);
sig.update(signedData.getBytes());
if (!sig.verify(signatureBytes)) {
Log.e(TAG, "Signature verification failed.");
return false;
}
return true;
} catch (NoSuchAlgorithmException e) {
Log.e(TAG, "NoSuchAlgorithmException.");
} catch (InvalidKeyException e) {
Log.e(TAG, "Invalid key specification.");
} catch (SignatureException e) {
Log.e(TAG, "Signature exception.");
}
/**
* Verifies that the signature from the server matches the computed
* signature on the data. Returns true if the data is correctly signed.
*
* @param publicKey public key associated with the developer account
* @param signedData signed data from server
* @param signature server signature
* @return true if the data and signature match
*/
public static boolean verify(PublicKey publicKey, String signedData, String signature) {
byte[] signatureBytes;
try {
signatureBytes = Base64.decode(signature, Base64.DEFAULT);
} catch (IllegalArgumentException e) {
Log.e(TAG, "Base64 decoding failed.");
return false;
}
try {
Signature sig = Signature.getInstance(SIGNATURE_ALGORITHM);
sig.initVerify(publicKey);
sig.update(signedData.getBytes());
if (!sig.verify(signatureBytes)) {
Log.e(TAG, "Signature verification failed.");
return false;
}
return true;
} catch (NoSuchAlgorithmException e) {
Log.e(TAG, "NoSuchAlgorithmException.");
} catch (InvalidKeyException e) {
Log.e(TAG, "Invalid key specification.");
} catch (SignatureException e) {
Log.e(TAG, "Signature exception.");
}
return false;
}
}

@ -23,43 +23,64 @@ import org.json.JSONObject;
*/
@SuppressWarnings("ALL")
public class SkuDetails {
private final String mItemType;
private final String mSku;
private final String mType;
private final String mPrice;
private final long mPriceAmountMicros;
private final String mPriceCurrencyCode;
private final String mTitle;
private final String mDescription;
private final String mJson;
public SkuDetails(String jsonSkuDetails) throws JSONException {
this(IabHelper.ITEM_TYPE_INAPP, jsonSkuDetails);
}
private final String mItemType;
private final String mSku;
private final String mType;
private final String mPrice;
private final long mPriceAmountMicros;
private final String mPriceCurrencyCode;
private final String mTitle;
private final String mDescription;
private final String mJson;
public SkuDetails(String itemType, String jsonSkuDetails) throws JSONException {
mItemType = itemType;
mJson = jsonSkuDetails;
JSONObject o = new JSONObject(mJson);
mSku = o.optString("productId");
mType = o.optString("type");
mPrice = o.optString("price");
mPriceAmountMicros = o.optLong("price_amount_micros");
mPriceCurrencyCode = o.optString("price_currency_code");
mTitle = o.optString("title");
mDescription = o.optString("description");
}
public SkuDetails(String jsonSkuDetails) throws JSONException {
this(IabHelper.ITEM_TYPE_INAPP, jsonSkuDetails);
}
public String getSku() { return mSku; }
public String getType() { return mType; }
public String getPrice() { return mPrice; }
public long getPriceAmountMicros() { return mPriceAmountMicros; }
public String getPriceCurrencyCode() { return mPriceCurrencyCode; }
public String getTitle() { return mTitle; }
public String getDescription() { return mDescription; }
public SkuDetails(String itemType, String jsonSkuDetails) throws JSONException {
mItemType = itemType;
mJson = jsonSkuDetails;
JSONObject o = new JSONObject(mJson);
mSku = o.optString("productId");
mType = o.optString("type");
mPrice = o.optString("price");
mPriceAmountMicros = o.optLong("price_amount_micros");
mPriceCurrencyCode = o.optString("price_currency_code");
mTitle = o.optString("title");
mDescription = o.optString("description");
}
@Override
public String toString() {
return "SkuDetails:" + mJson;
}
public String getSku() {
return mSku;
}
public String getType() {
return mType;
}
public String getPrice() {
return mPrice;
}
public long getPriceAmountMicros() {
return mPriceAmountMicros;
}
public String getPriceCurrencyCode() {
return mPriceCurrencyCode;
}
public String getTitle() {
return mTitle;
}
public String getDescription() {
return mDescription;
}
@Override
public String toString() {
return "SkuDetails:" + mJson;
}
}

@ -1,42 +1,41 @@
package org.tasks;
import com.todoroo.astrid.gtasks.GtasksPreferenceService;
import javax.inject.Inject;
import org.tasks.billing.InventoryHelper;
import org.tasks.caldav.CaldavAccountManager;
import org.tasks.gtasks.GoogleAccountManager;
import org.tasks.gtasks.PlayServices;
import org.tasks.preferences.Preferences;
import javax.inject.Inject;
public class FlavorSetup {
private final GtasksPreferenceService gtasksPreferenceService;
private final InventoryHelper inventoryHelper;
private final Preferences preferences;
private final PlayServices playServices;
private final GoogleAccountManager googleAccountManager;
private final CaldavAccountManager caldavAccountManager;
private final GtasksPreferenceService gtasksPreferenceService;
private final InventoryHelper inventoryHelper;
private final Preferences preferences;
private final PlayServices playServices;
private final GoogleAccountManager googleAccountManager;
private final CaldavAccountManager caldavAccountManager;
@Inject
public FlavorSetup(GtasksPreferenceService gtasksPreferenceService, InventoryHelper inventoryHelper,
Preferences preferences, PlayServices playServices,
GoogleAccountManager googleAccountManager, CaldavAccountManager caldavAccountManager) {
this.gtasksPreferenceService = gtasksPreferenceService;
this.inventoryHelper = inventoryHelper;
this.preferences = preferences;
this.playServices = playServices;
this.googleAccountManager = googleAccountManager;
this.caldavAccountManager = caldavAccountManager;
}
@Inject
public FlavorSetup(GtasksPreferenceService gtasksPreferenceService,
InventoryHelper inventoryHelper,
Preferences preferences, PlayServices playServices,
GoogleAccountManager googleAccountManager, CaldavAccountManager caldavAccountManager) {
this.gtasksPreferenceService = gtasksPreferenceService;
this.inventoryHelper = inventoryHelper;
this.preferences = preferences;
this.playServices = playServices;
this.googleAccountManager = googleAccountManager;
this.caldavAccountManager = caldavAccountManager;
}
public void setup() {
inventoryHelper.initialize();
gtasksPreferenceService.stopOngoing(); // if sync ongoing flag was set, clear it
boolean backgroundSyncEnabled = preferences.getBoolean(R.string.p_background_sync, true);
googleAccountManager.setBackgroundSynchronization(backgroundSyncEnabled);
caldavAccountManager.setBackgroundSynchronization(backgroundSyncEnabled);
playServices.refresh();
}
public void setup() {
inventoryHelper.initialize();
gtasksPreferenceService.stopOngoing(); // if sync ongoing flag was set, clear it
boolean backgroundSyncEnabled = preferences.getBoolean(R.string.p_background_sync, true);
googleAccountManager.setBackgroundSynchronization(backgroundSyncEnabled);
caldavAccountManager.setBackgroundSynchronization(backgroundSyncEnabled);
playServices.refresh();
}
}

@ -1,7 +1,6 @@
package org.tasks.analytics;
import android.content.Context;
import com.android.vending.billing.IabResult;
import com.google.android.gms.analytics.ExceptionParser;
import com.google.android.gms.analytics.ExceptionReporter;
@ -9,98 +8,96 @@ import com.google.android.gms.analytics.GoogleAnalytics;
import com.google.android.gms.analytics.HitBuilders;
import com.google.android.gms.analytics.StandardExceptionParser;
import com.google.common.base.Strings;
import javax.inject.Inject;
import org.tasks.BuildConfig;
import org.tasks.R;
import org.tasks.injection.ApplicationScope;
import org.tasks.injection.ForApplication;
import javax.inject.Inject;
import timber.log.Timber;
@ApplicationScope
public class Tracker {
private final GoogleAnalytics analytics;
private final com.google.android.gms.analytics.Tracker tracker;
private final ExceptionParser exceptionParser;
private final Context context;
@Inject
public Tracker(@ForApplication Context context) {
this.context = context;
analytics = GoogleAnalytics.getInstance(context);
tracker = analytics.newTracker(R.xml.google_analytics);
tracker.setAppVersion(Integer.toString(BuildConfig.VERSION_CODE));
final StandardExceptionParser standardExceptionParser = new StandardExceptionParser(context, null);
exceptionParser = (thread, throwable) -> {
StringBuilder stack = new StringBuilder()
.append(standardExceptionParser.getDescription(thread, throwable))
.append("\n")
.append(throwable.getClass().getName())
.append("\n");
for (StackTraceElement element : throwable.getStackTrace()) {
stack.append(element.toString())
.append("\n");
}
return stack.toString();
};
ExceptionReporter reporter = new ExceptionReporter(
tracker,
Thread.getDefaultUncaughtExceptionHandler(),
context);
reporter.setExceptionParser(exceptionParser);
Thread.setDefaultUncaughtExceptionHandler(reporter);
}
public void setTrackingEnabled(boolean enabled) {
analytics.setAppOptOut(!enabled);
}
public void reportException(Throwable t) {
reportException(Thread.currentThread(), t);
}
public void reportException(Thread thread, Throwable t) {
Timber.e(t, t.getMessage());
tracker.send(new HitBuilders.ExceptionBuilder()
.setDescription(exceptionParser.getDescription(thread.getName(), t))
.setFatal(false)
.build());
}
public void reportEvent(Tracking.Events event) {
reportEvent(event, null);
}
public void reportEvent(Tracking.Events event, String label) {
reportEvent(event, event.action, label);
}
public void reportEvent(Tracking.Events event, int action, String label) {
reportEvent(event, context.getString(action), label);
}
public void reportEvent(Tracking.Events event, String action, String label) {
reportEvent(event.category, action, label);
}
private void reportEvent(int category, String action, String label) {
HitBuilders.EventBuilder eventBuilder = new HitBuilders.EventBuilder()
.setCategory(context.getString(category))
.setAction(action);
if (!Strings.isNullOrEmpty(label)) {
eventBuilder.setLabel(label);
}
tracker.send(eventBuilder.build());
}
public void reportIabResult(IabResult result, String sku) {
tracker.send(new HitBuilders.EventBuilder()
.setCategory(context.getString(R.string.tracking_category_iab))
.setAction(sku)
.setLabel(result.getMessage())
.build());
private final GoogleAnalytics analytics;
private final com.google.android.gms.analytics.Tracker tracker;
private final ExceptionParser exceptionParser;
private final Context context;
@Inject
public Tracker(@ForApplication Context context) {
this.context = context;
analytics = GoogleAnalytics.getInstance(context);
tracker = analytics.newTracker(R.xml.google_analytics);
tracker.setAppVersion(Integer.toString(BuildConfig.VERSION_CODE));
final StandardExceptionParser standardExceptionParser = new StandardExceptionParser(context,
null);
exceptionParser = (thread, throwable) -> {
StringBuilder stack = new StringBuilder()
.append(standardExceptionParser.getDescription(thread, throwable))
.append("\n")
.append(throwable.getClass().getName())
.append("\n");
for (StackTraceElement element : throwable.getStackTrace()) {
stack.append(element.toString())
.append("\n");
}
return stack.toString();
};
ExceptionReporter reporter = new ExceptionReporter(
tracker,
Thread.getDefaultUncaughtExceptionHandler(),
context);
reporter.setExceptionParser(exceptionParser);
Thread.setDefaultUncaughtExceptionHandler(reporter);
}
public void setTrackingEnabled(boolean enabled) {
analytics.setAppOptOut(!enabled);
}
public void reportException(Throwable t) {
reportException(Thread.currentThread(), t);
}
public void reportException(Thread thread, Throwable t) {
Timber.e(t, t.getMessage());
tracker.send(new HitBuilders.ExceptionBuilder()
.setDescription(exceptionParser.getDescription(thread.getName(), t))
.setFatal(false)
.build());
}
public void reportEvent(Tracking.Events event) {
reportEvent(event, null);
}
public void reportEvent(Tracking.Events event, String label) {
reportEvent(event, event.action, label);
}
public void reportEvent(Tracking.Events event, int action, String label) {
reportEvent(event, context.getString(action), label);
}
public void reportEvent(Tracking.Events event, String action, String label) {
reportEvent(event.category, action, label);
}
private void reportEvent(int category, String action, String label) {
HitBuilders.EventBuilder eventBuilder = new HitBuilders.EventBuilder()
.setCategory(context.getString(category))
.setAction(action);
if (!Strings.isNullOrEmpty(label)) {
eventBuilder.setLabel(label);
}
tracker.send(eventBuilder.build());
}
public void reportIabResult(IabResult result, String sku) {
tracker.send(new HitBuilders.EventBuilder()
.setCategory(context.getString(R.string.tracking_category_iab))
.setAction(sku)
.setLabel(result.getMessage())
.build());
}
}

@ -2,100 +2,96 @@ package org.tasks.billing;
import android.content.Context;
import android.content.IntentFilter;
import com.android.vending.billing.IabBroadcastReceiver;
import com.android.vending.billing.IabHelper;
import com.android.vending.billing.Inventory;
import com.android.vending.billing.Purchase;
import java.util.concurrent.Executor;
import javax.inject.Inject;
import javax.inject.Named;
import org.tasks.LocalBroadcastManager;
import org.tasks.R;
import org.tasks.injection.ApplicationScope;
import org.tasks.injection.ForApplication;
import org.tasks.preferences.Preferences;
import java.util.concurrent.Executor;
import javax.inject.Inject;
import javax.inject.Named;
import timber.log.Timber;
@ApplicationScope
public class InventoryHelper implements IabBroadcastReceiver.IabBroadcastListener {
private final Context context;
private final Preferences preferences;
private final LocalBroadcastManager localBroadcastManager;
private final Executor executor;
private Inventory inventory;
@Inject
public InventoryHelper(@ForApplication Context context, Preferences preferences,
LocalBroadcastManager localBroadcastManager, @Named("iab-executor") Executor executor) {
this.context = context;
this.preferences = preferences;
this.localBroadcastManager = localBroadcastManager;
this.executor = executor;
private final Context context;
private final Preferences preferences;
private final LocalBroadcastManager localBroadcastManager;
private final Executor executor;
private Inventory inventory;
@Inject
public InventoryHelper(@ForApplication Context context, Preferences preferences,
LocalBroadcastManager localBroadcastManager, @Named("iab-executor") Executor executor) {
this.context = context;
this.preferences = preferences;
this.localBroadcastManager = localBroadcastManager;
this.executor = executor;
}
public void initialize() {
context.registerReceiver(new IabBroadcastReceiver(this),
new IntentFilter(IabBroadcastReceiver.ACTION));
refreshInventory();
}
public void refreshInventory() {
final IabHelper helper = new IabHelper(context, context.getString(R.string.gp_key), executor);
helper.startSetup(getSetupListener(helper));
}
private IabHelper.OnIabSetupFinishedListener getSetupListener(final IabHelper helper) {
return result -> {
if (result.isSuccess()) {
helper.queryInventoryAsync(getQueryListener(helper));
} else {
Timber.e("setup failed: %s", result.getMessage());
helper.dispose();
}
};
}
private IabHelper.QueryInventoryFinishedListener getQueryListener(final IabHelper helper) {
return (result, inv) -> {
if (result.isSuccess()) {
inventory = inv;
checkPurchase(R.string.sku_tasker, R.string.p_purchased_tasker);
checkPurchase(R.string.sku_dashclock, R.string.p_purchased_dashclock);
checkPurchase(R.string.sku_themes, R.string.p_purchased_themes);
localBroadcastManager.broadcastRefresh();
} else {
Timber.e("query inventory failed: %s", result.getMessage());
}
helper.dispose();
};
}
@Override
public void receivedBroadcast() {
refreshInventory();
}
private void checkPurchase(int skuRes, final int prefRes) {
final String sku = context.getString(skuRes);
if (inventory.hasPurchase(sku)) {
Timber.d("Found purchase: %s", sku);
preferences.setBoolean(prefRes, true);
} else {
Timber.d("No purchase: %s", sku);
}
}
public void initialize() {
context.registerReceiver(new IabBroadcastReceiver(this), new IntentFilter(IabBroadcastReceiver.ACTION));
refreshInventory();
}
public void erasePurchase(String sku) {
inventory.erasePurchase(sku);
}
public void refreshInventory() {
final IabHelper helper = new IabHelper(context, context.getString(R.string.gp_key), executor);
helper.startSetup(getSetupListener(helper));
}
private IabHelper.OnIabSetupFinishedListener getSetupListener(final IabHelper helper) {
return result -> {
if (result.isSuccess()) {
helper.queryInventoryAsync(getQueryListener(helper));
} else {
Timber.e("setup failed: %s", result.getMessage());
helper.dispose();
}
};
}
private IabHelper.QueryInventoryFinishedListener getQueryListener(final IabHelper helper) {
return (result, inv) -> {
if (result.isSuccess()) {
inventory = inv;
checkPurchase(R.string.sku_tasker, R.string.p_purchased_tasker);
checkPurchase(R.string.sku_dashclock, R.string.p_purchased_dashclock);
checkPurchase(R.string.sku_themes, R.string.p_purchased_themes);
localBroadcastManager.broadcastRefresh();
} else {
Timber.e("query inventory failed: %s", result.getMessage());
}
helper.dispose();
};
}
@Override
public void receivedBroadcast() {
refreshInventory();
}
private void checkPurchase(int skuRes, final int prefRes) {
final String sku = context.getString(skuRes);
if (inventory.hasPurchase(sku)) {
Timber.d("Found purchase: %s", sku);
preferences.setBoolean(prefRes, true);
} else {
Timber.d("No purchase: %s", sku);
}
}
public void erasePurchase(String sku) {
inventory.erasePurchase(sku);
}
public Purchase getPurchase(String sku) {
return inventory.getPurchase(sku);
}
public Purchase getPurchase(String sku) {
return inventory.getPurchase(sku);
}
}

@ -4,12 +4,15 @@ import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
import com.android.vending.billing.IabHelper;
import com.android.vending.billing.IabResult;
import com.android.vending.billing.Purchase;
import com.google.common.base.Strings;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
import javax.inject.Inject;
import javax.inject.Named;
import org.tasks.BuildConfig;
import org.tasks.LocalBroadcastManager;
import org.tasks.R;
@ -17,161 +20,157 @@ import org.tasks.analytics.Tracker;
import org.tasks.injection.ApplicationScope;
import org.tasks.injection.ForApplication;
import org.tasks.preferences.Preferences;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
import javax.inject.Inject;
import javax.inject.Named;
import timber.log.Timber;
@ApplicationScope
public class PurchaseHelper implements IabHelper.OnIabSetupFinishedListener {
private final Context context;
private final Preferences preferences;
private final Tracker tracker;
private final InventoryHelper inventory;
private final Executor executor;
private LocalBroadcastManager localBroadcastManager;
private PurchaseHelperCallback activityResultCallback;
private IabHelper iabHelper;
@Inject
public PurchaseHelper(@ForApplication Context context, Preferences preferences, Tracker tracker,
InventoryHelper inventory, @Named("iab-executor") Executor executor,
LocalBroadcastManager localBroadcastManager) {
this.context = context;
this.preferences = preferences;
this.tracker = tracker;
this.inventory = inventory;
this.executor = executor;
this.localBroadcastManager = localBroadcastManager;
private final Context context;
private final Preferences preferences;
private final Tracker tracker;
private final InventoryHelper inventory;
private final Executor executor;
private LocalBroadcastManager localBroadcastManager;
private PurchaseHelperCallback activityResultCallback;
private IabHelper iabHelper;
@Inject
public PurchaseHelper(@ForApplication Context context, Preferences preferences, Tracker tracker,
InventoryHelper inventory, @Named("iab-executor") Executor executor,
LocalBroadcastManager localBroadcastManager) {
this.context = context;
this.preferences = preferences;
this.tracker = tracker;
this.inventory = inventory;
this.executor = executor;
this.localBroadcastManager = localBroadcastManager;
}
@Override
public void onIabSetupFinished(IabResult result) {
if (result.isFailure()) {
Timber.e("in-app billing setup failed: %s", result.getMessage());
}
@Override
public void onIabSetupFinished(IabResult result) {
if (result.isFailure()) {
Timber.e("in-app billing setup failed: %s", result.getMessage());
}
}
public boolean purchase(final Activity activity, final String sku, final String pref, final int requestCode, final PurchaseHelperCallback callback) {
launchPurchaseFlow(activity, sku, pref, requestCode, callback);
return true;
}
public void consumePurchases() {
if (BuildConfig.DEBUG) {
final List<Purchase> purchases = new ArrayList<>();
final Purchase tasker = inventory.getPurchase(context.getString(R.string.sku_tasker));
final Purchase dashclock = inventory.getPurchase(context.getString(R.string.sku_dashclock));
final Purchase themes = inventory.getPurchase(context.getString(R.string.sku_themes));
if (tasker != null) {
purchases.add(tasker);
}
if (dashclock != null) {
purchases.add(dashclock);
}
if (themes != null) {
purchases.add(themes);
}
final IabHelper iabHelper = new IabHelper(context, context.getString(R.string.gp_key), executor);
iabHelper.enableDebugLogging(true);
iabHelper.startSetup(result -> {
if (result.isSuccess()) {
iabHelper.consumeAsync(purchases, (purchases1, results) -> {
for (int i = 0; i < purchases1.size() ; i++) {
Purchase purchase = purchases1.get(i);
IabResult iabResult = results.get(i);
if (iabResult.isSuccess()) {
if (purchase.equals(tasker)) {
preferences.setBoolean(R.string.p_purchased_tasker, false);
} else if (purchase.equals(dashclock)) {
preferences.setBoolean(R.string.p_purchased_dashclock, false);
} else if (purchase.equals(themes)) {
preferences.setBoolean(R.string.p_purchased_themes, false);
} else {
Timber.e("Unhandled consumption for purchase: %s", purchase);
}
inventory.erasePurchase(purchase.getSku());
Timber.d("Consumed %s", purchase);
} else {
Timber.e("Consume failed: %s, %s", purchase, iabResult);
}
}
iabHelper.dispose();
});
}
public boolean purchase(final Activity activity, final String sku, final String pref,
final int requestCode, final PurchaseHelperCallback callback) {
launchPurchaseFlow(activity, sku, pref, requestCode, callback);
return true;
}
public void consumePurchases() {
if (BuildConfig.DEBUG) {
final List<Purchase> purchases = new ArrayList<>();
final Purchase tasker = inventory.getPurchase(context.getString(R.string.sku_tasker));
final Purchase dashclock = inventory.getPurchase(context.getString(R.string.sku_dashclock));
final Purchase themes = inventory.getPurchase(context.getString(R.string.sku_themes));
if (tasker != null) {
purchases.add(tasker);
}
if (dashclock != null) {
purchases.add(dashclock);
}
if (themes != null) {
purchases.add(themes);
}
final IabHelper iabHelper = new IabHelper(context, context.getString(R.string.gp_key),
executor);
iabHelper.enableDebugLogging(true);
iabHelper.startSetup(result -> {
if (result.isSuccess()) {
iabHelper.consumeAsync(purchases, (purchases1, results) -> {
for (int i = 0; i < purchases1.size(); i++) {
Purchase purchase = purchases1.get(i);
IabResult iabResult = results.get(i);
if (iabResult.isSuccess()) {
if (purchase.equals(tasker)) {
preferences.setBoolean(R.string.p_purchased_tasker, false);
} else if (purchase.equals(dashclock)) {
preferences.setBoolean(R.string.p_purchased_dashclock, false);
} else if (purchase.equals(themes)) {
preferences.setBoolean(R.string.p_purchased_themes, false);
} else {
Timber.e("setup failed: %s", result.getMessage());
iabHelper.dispose();
Timber.e("Unhandled consumption for purchase: %s", purchase);
}
});
inventory.erasePurchase(purchase.getSku());
Timber.d("Consumed %s", purchase);
} else {
Timber.e("Consume failed: %s, %s", purchase, iabResult);
}
}
iabHelper.dispose();
});
} else {
Timber.e("setup failed: %s", result.getMessage());
iabHelper.dispose();
}
});
}
private void launchPurchaseFlow(final Activity activity, final String sku, final String pref, final int requestCode, final PurchaseHelperCallback callback) {
if (iabHelper != null) {
Toast.makeText(activity, R.string.billing_service_busy, Toast.LENGTH_LONG).show();
callback.purchaseCompleted(false, sku);
return;
}
iabHelper = new IabHelper(context, context.getString(R.string.gp_key), executor);
iabHelper.enableDebugLogging(BuildConfig.DEBUG);
Timber.d("%s: startSetup", iabHelper);
iabHelper.startSetup(result -> {
if (result.isSuccess()) {
try {
Timber.d("%s: launchPurchaseFlow for %s", iabHelper, sku);
iabHelper.launchPurchaseFlow(activity, sku, requestCode, (result1, info) -> {
Timber.d(result1.toString());
tracker.reportIabResult(result1, sku);
if (result1.isSuccess()) {
if (!Strings.isNullOrEmpty(pref)) {
preferences.setBoolean(pref, true);
localBroadcastManager.broadcastRefresh();
}
inventory.refreshInventory();
} else if (result1.getResponse() != IabHelper.BILLING_RESPONSE_RESULT_USER_CANCELED &&
result1.getResponse() != IabHelper.IABHELPER_USER_CANCELLED) {
Toast.makeText(activity, result1.getMessage(), Toast.LENGTH_LONG).show();
}
if (activityResultCallback != null) {
activityResultCallback.purchaseCompleted(result1.isSuccess(), sku);
}
disposeIabHelper();
});
} catch (IllegalStateException e) {
tracker.reportException(e);
Toast.makeText(activity, R.string.billing_service_busy, Toast.LENGTH_LONG).show();
callback.purchaseCompleted(false, sku);
disposeIabHelper();
}
} else {
Timber.e(result.toString());
Toast.makeText(activity, result.getMessage(), Toast.LENGTH_LONG).show();
callback.purchaseCompleted(false, sku);
disposeIabHelper();
}
});
}
private void launchPurchaseFlow(final Activity activity, final String sku, final String pref,
final int requestCode, final PurchaseHelperCallback callback) {
if (iabHelper != null) {
Toast.makeText(activity, R.string.billing_service_busy, Toast.LENGTH_LONG).show();
callback.purchaseCompleted(false, sku);
return;
}
public void disposeIabHelper() {
if (iabHelper != null) {
Timber.d("%s: dispose", iabHelper);
iabHelper.dispose();
iabHelper = null;
iabHelper = new IabHelper(context, context.getString(R.string.gp_key), executor);
iabHelper.enableDebugLogging(BuildConfig.DEBUG);
Timber.d("%s: startSetup", iabHelper);
iabHelper.startSetup(result -> {
if (result.isSuccess()) {
try {
Timber.d("%s: launchPurchaseFlow for %s", iabHelper, sku);
iabHelper.launchPurchaseFlow(activity, sku, requestCode, (result1, info) -> {
Timber.d(result1.toString());
tracker.reportIabResult(result1, sku);
if (result1.isSuccess()) {
if (!Strings.isNullOrEmpty(pref)) {
preferences.setBoolean(pref, true);
localBroadcastManager.broadcastRefresh();
}
inventory.refreshInventory();
} else if (result1.getResponse() != IabHelper.BILLING_RESPONSE_RESULT_USER_CANCELED &&
result1.getResponse() != IabHelper.IABHELPER_USER_CANCELLED) {
Toast.makeText(activity, result1.getMessage(), Toast.LENGTH_LONG).show();
}
if (activityResultCallback != null) {
activityResultCallback.purchaseCompleted(result1.isSuccess(), sku);
}
disposeIabHelper();
});
} catch (IllegalStateException e) {
tracker.reportException(e);
Toast.makeText(activity, R.string.billing_service_busy, Toast.LENGTH_LONG).show();
callback.purchaseCompleted(false, sku);
disposeIabHelper();
}
} else {
Timber.e(result.toString());
Toast.makeText(activity, result.getMessage(), Toast.LENGTH_LONG).show();
callback.purchaseCompleted(false, sku);
disposeIabHelper();
}
});
}
public void disposeIabHelper() {
if (iabHelper != null) {
Timber.d("%s: dispose", iabHelper);
iabHelper.dispose();
iabHelper = null;
}
}
public void handleActivityResult(PurchaseHelperCallback callback, int requestCode, int resultCode, Intent data) {
this.activityResultCallback = callback;
public void handleActivityResult(PurchaseHelperCallback callback, int requestCode, int resultCode,
Intent data) {
this.activityResultCallback = callback;
if (iabHelper != null) {
iabHelper.handleActivityResult(requestCode, resultCode, data);
}
if (iabHelper != null) {
iabHelper.handleActivityResult(requestCode, resultCode, data);
}
}
}

@ -4,7 +4,6 @@ import android.accounts.Account;
import android.app.Activity;
import android.content.Context;
import android.widget.Toast;
import com.google.android.gms.auth.GoogleAuthException;
import com.google.android.gms.auth.GoogleAuthUtil;
import com.google.android.gms.auth.UserRecoverableAuthException;
@ -13,103 +12,103 @@ import com.google.android.gms.common.GoogleApiAvailability;
import com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential;
import com.google.api.services.tasks.TasksScopes;
import com.todoroo.astrid.gtasks.auth.GtasksLoginActivity;
import java.io.IOException;
import javax.inject.Inject;
import org.tasks.R;
import org.tasks.injection.ForApplication;
import org.tasks.preferences.Preferences;
import java.io.IOException;
import javax.inject.Inject;
import timber.log.Timber;
public class PlayServices {
private static final int REQUEST_RESOLUTION = 10000;
private final Context context;
private final Preferences preferences;
private final GoogleAccountManager accountManager;
@Inject
public PlayServices(@ForApplication Context context, Preferences preferences,
GoogleAccountManager googleAccountManager) {
this.context = context;
this.preferences = preferences;
this.accountManager = googleAccountManager;
private static final int REQUEST_RESOLUTION = 10000;
private final Context context;
private final Preferences preferences;
private final GoogleAccountManager accountManager;
@Inject
public PlayServices(@ForApplication Context context, Preferences preferences,
GoogleAccountManager googleAccountManager) {
this.context = context;
this.preferences = preferences;
this.accountManager = googleAccountManager;
}
public boolean refreshAndCheck() {
refresh();
return isPlayServicesAvailable();
}
public boolean isPlayServicesAvailable() {
return getResult() == ConnectionResult.SUCCESS;
}
public void refresh() {
GoogleApiAvailability instance = GoogleApiAvailability.getInstance();
int googlePlayServicesAvailable = instance.isGooglePlayServicesAvailable(context);
preferences.setInt(R.string.play_services_available, googlePlayServicesAvailable);
if (googlePlayServicesAvailable == ConnectionResult.SUCCESS) {
preferences.setBoolean(R.string.warned_play_services, false);
}
public boolean refreshAndCheck() {
refresh();
return isPlayServicesAvailable();
Timber.d(getStatus());
}
public void resolve(Activity activity) {
GoogleApiAvailability googleApiAvailability = GoogleApiAvailability.getInstance();
int error = preferences.getInt(R.string.play_services_available, -1);
if (googleApiAvailability.isUserResolvableError(error)) {
googleApiAvailability.getErrorDialog(activity, error, REQUEST_RESOLUTION).show();
} else {
Toast.makeText(activity, R.string.common_google_play_services_notification_ticker,
Toast.LENGTH_LONG).show();
}
public boolean isPlayServicesAvailable() {
return getResult() == ConnectionResult.SUCCESS;
}
public String getStatus() {
return GoogleApiAvailability.getInstance().getErrorString(getResult());
}
private int getResult() {
return preferences.getInt(R.string.play_services_available, -1);
}
public boolean clearToken(GoogleAccountCredential credential) {
try {
String token = credential.getToken();
Timber.d("Invalidating %s", token);
GoogleAuthUtil.clearToken(context, token);
GoogleAuthUtil
.getToken(context, credential.getSelectedAccount(), "oauth2:" + TasksScopes.TASKS, null);
return true;
} catch (GoogleAuthException e) {
Timber.e(e, e.getMessage());
return false;
} catch (IOException e) {
Timber.e(e, e.getMessage());
return true;
}
public void refresh() {
GoogleApiAvailability instance = GoogleApiAvailability.getInstance();
int googlePlayServicesAvailable = instance.isGooglePlayServicesAvailable(context);
preferences.setInt(R.string.play_services_available, googlePlayServicesAvailable);
if (googlePlayServicesAvailable == ConnectionResult.SUCCESS) {
preferences.setBoolean(R.string.warned_play_services, false);
}
Timber.d(getStatus());
}
public void resolve(Activity activity) {
GoogleApiAvailability googleApiAvailability = GoogleApiAvailability.getInstance();
int error = preferences.getInt(R.string.play_services_available, -1);
if (googleApiAvailability.isUserResolvableError(error)) {
googleApiAvailability.getErrorDialog(activity, error, REQUEST_RESOLUTION).show();
} else {
Toast.makeText(activity, R.string.common_google_play_services_notification_ticker, Toast.LENGTH_LONG).show();
}
}
public String getStatus() {
return GoogleApiAvailability.getInstance().getErrorString(getResult());
}
private int getResult() {
return preferences.getInt(R.string.play_services_available, -1);
}
public boolean clearToken(GoogleAccountCredential credential){
}
public void getAuthToken(final Activity activity, final String accountName,
final GtasksLoginActivity.AuthResultHandler handler) {
final Account account = accountManager.getAccount(accountName);
if (account == null) {
handler.authenticationFailed(
activity.getString(R.string.gtasks_error_accountNotFound, accountName));
} else {
new Thread(() -> {
try {
String token = credential.getToken();
Timber.d("Invalidating %s", token);
GoogleAuthUtil.clearToken(context, token);
GoogleAuthUtil.getToken(context, credential.getSelectedAccount(), "oauth2:" + TasksScopes.TASKS, null);
return true;
} catch (GoogleAuthException e) {
Timber.e(e, e.getMessage());
return false;
} catch (IOException e) {
Timber.e(e, e.getMessage());
return true;
}
}
public void getAuthToken(final Activity activity, final String accountName, final GtasksLoginActivity.AuthResultHandler handler) {
final Account account = accountManager.getAccount(accountName);
if (account == null) {
handler.authenticationFailed(activity.getString(R.string.gtasks_error_accountNotFound, accountName));
} else {
new Thread(() -> {
try {
GoogleAuthUtil.getToken(activity, account, "oauth2:" + TasksScopes.TASKS, null);
handler.authenticationSuccessful(accountName);
} catch(UserRecoverableAuthException e) {
Timber.e(e, e.getMessage());
activity.startActivityForResult(e.getIntent(), GtasksLoginActivity.RC_REQUEST_OAUTH);
} catch(GoogleAuthException | IOException e) {
Timber.e(e, e.getMessage());
handler.authenticationFailed(activity.getString(R.string.gtasks_GLA_errorIOAuth));
}
}).start();
GoogleAuthUtil.getToken(activity, account, "oauth2:" + TasksScopes.TASKS, null);
handler.authenticationSuccessful(accountName);
} catch (UserRecoverableAuthException e) {
Timber.e(e, e.getMessage());
activity.startActivityForResult(e.getIntent(), GtasksLoginActivity.RC_REQUEST_OAUTH);
} catch (GoogleAuthException | IOException e) {
Timber.e(e, e.getMessage());
handler.authenticationFailed(activity.getString(R.string.gtasks_GLA_errorIOAuth));
}
}).start();
}
}
}

@ -1,111 +1,110 @@
package org.tasks.location;
import static com.google.android.gms.location.Geofence.NEVER_EXPIRE;
import static com.google.common.collect.Iterables.transform;
import static com.google.common.collect.Lists.newArrayList;
import static java.util.Collections.singletonList;
import android.annotation.SuppressLint;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import com.google.android.gms.common.api.PendingResult;
import com.google.android.gms.common.api.Status;
import com.google.android.gms.location.GeofencingRequest;
import com.google.android.gms.location.LocationServices;
import com.google.common.collect.Lists;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import org.tasks.R;
import org.tasks.data.Location;
import org.tasks.injection.ForApplication;
import org.tasks.preferences.PermissionChecker;
import org.tasks.preferences.Preferences;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import timber.log.Timber;
import static com.google.android.gms.location.Geofence.NEVER_EXPIRE;
import static com.google.common.collect.Iterables.transform;
import static com.google.common.collect.Lists.newArrayList;
import static java.util.Collections.singletonList;
public class GeofenceApi {
private final Context context;
private final Preferences preferences;
private final PermissionChecker permissionChecker;
@Inject
public GeofenceApi(@ForApplication Context context, Preferences preferences, PermissionChecker permissionChecker) {
this.context = context;
this.preferences = preferences;
this.permissionChecker = permissionChecker;
}
public void register(final List<Location> locations) {
if (locations.isEmpty() || !permissionChecker.canAccessLocation()) {
return;
}
newClient(client -> {
@SuppressWarnings("ResourceType")
@SuppressLint("MissingPermission")
PendingResult<Status> result = LocationServices.GeofencingApi.addGeofences(
client,
getRequests(locations),
PendingIntent.getBroadcast(context, 0, new Intent(context, GeofenceTransitionsIntentService.Broadcast.class), PendingIntent.FLAG_UPDATE_CURRENT));
result.setResultCallback(status -> {
if (status.isSuccess()) {
Timber.i("Registered %s", locations);
} else {
Timber.e("Failed to register %s", locations);
}
client.disconnect();
});
});
}
public void cancel(final Location location) {
cancel(singletonList(location));
private final Context context;
private final Preferences preferences;
private final PermissionChecker permissionChecker;
@Inject
public GeofenceApi(@ForApplication Context context, Preferences preferences,
PermissionChecker permissionChecker) {
this.context = context;
this.preferences = preferences;
this.permissionChecker = permissionChecker;
}
public void register(final List<Location> locations) {
if (locations.isEmpty() || !permissionChecker.canAccessLocation()) {
return;
}
public void cancel(final List<Location> locations) {
if (locations.isEmpty() || !permissionChecker.canAccessLocation()) {
return;
newClient(client -> {
@SuppressWarnings("ResourceType")
@SuppressLint("MissingPermission")
PendingResult<Status> result = LocationServices.GeofencingApi.addGeofences(
client,
getRequests(locations),
PendingIntent.getBroadcast(context, 0,
new Intent(context, GeofenceTransitionsIntentService.Broadcast.class),
PendingIntent.FLAG_UPDATE_CURRENT));
result.setResultCallback(status -> {
if (status.isSuccess()) {
Timber.i("Registered %s", locations);
} else {
Timber.e("Failed to register %s", locations);
}
List<String> ids = Lists.transform(locations, geofence -> Long.toString(geofence.getId()));
newClient(client -> LocationServices.GeofencingApi.removeGeofences(client, ids)
.setResultCallback(status -> {
if (status.isSuccess()) {
Timber.i("Removed %s", locations);
} else {
Timber.e("Failed to remove %s", locations);
}
client.disconnect();
});
});
}
client.disconnect();
}));
}
private void newClient(final GoogleApi.GoogleApiClientConnectionHandler handler) {
new GoogleApi(context).connect(handler);
}
public void cancel(final Location location) {
cancel(singletonList(location));
}
private List<com.google.android.gms.location.Geofence> getRequests(List<Location> locations) {
return newArrayList(transform(locations, this::toGoogleGeofence));
public void cancel(final List<Location> locations) {
if (locations.isEmpty() || !permissionChecker.canAccessLocation()) {
return;
}
private com.google.android.gms.location.Geofence toGoogleGeofence(Location location) {
int radius = preferences.getIntegerFromString(R.string.p_geofence_radius, 250);
int responsiveness = (int) TimeUnit.SECONDS.toMillis(preferences.getIntegerFromString(R.string.p_geofence_responsiveness, 60));
return new com.google.android.gms.location.Geofence.Builder()
.setCircularRegion(location.getLatitude(), location.getLongitude(), radius)
.setNotificationResponsiveness(responsiveness)
.setRequestId(Long.toString(location.getId()))
.setTransitionTypes(GeofencingRequest.INITIAL_TRIGGER_ENTER)
.setExpirationDuration(NEVER_EXPIRE)
.build();
}
List<String> ids = Lists.transform(locations, geofence -> Long.toString(geofence.getId()));
newClient(client -> LocationServices.GeofencingApi.removeGeofences(client, ids)
.setResultCallback(status -> {
if (status.isSuccess()) {
Timber.i("Removed %s", locations);
} else {
Timber.e("Failed to remove %s", locations);
}
client.disconnect();
}));
}
private void newClient(final GoogleApi.GoogleApiClientConnectionHandler handler) {
new GoogleApi(context).connect(handler);
}
private List<com.google.android.gms.location.Geofence> getRequests(List<Location> locations) {
return newArrayList(transform(locations, this::toGoogleGeofence));
}
private com.google.android.gms.location.Geofence toGoogleGeofence(Location location) {
int radius = preferences.getIntegerFromString(R.string.p_geofence_radius, 250);
int responsiveness = (int) TimeUnit.SECONDS
.toMillis(preferences.getIntegerFromString(R.string.p_geofence_responsiveness, 60));
return new com.google.android.gms.location.Geofence.Builder()
.setCircularRegion(location.getLatitude(), location.getLongitude(), radius)
.setNotificationResponsiveness(responsiveness)
.setRequestId(Long.toString(location.getId()))
.setTransitionTypes(GeofencingRequest.INITIAL_TRIGGER_ENTER)
.setExpirationDuration(NEVER_EXPIRE)
.build();
}
}

@ -4,70 +4,68 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.support.v4.app.JobIntentService;
import com.google.android.gms.location.GeofencingEvent;
import com.todoroo.astrid.reminders.ReminderService;
import java.util.List;
import javax.inject.Inject;
import org.tasks.Notifier;
import org.tasks.data.Location;
import org.tasks.data.LocationDao;
import org.tasks.injection.InjectingJobIntentService;
import org.tasks.injection.IntentServiceComponent;
import org.tasks.jobs.JobManager;
import java.util.List;
import javax.inject.Inject;
import timber.log.Timber;
public class GeofenceTransitionsIntentService extends InjectingJobIntentService {
public static class Broadcast extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
JobIntentService.enqueueWork(context, GeofenceTransitionsIntentService.class, JobManager.JOB_ID_GEOFENCE_TRANSITION, intent);
}
}
@Inject LocationDao locationDao;
@Inject Notifier notifier;
@Inject LocationDao locationDao;
@Inject Notifier notifier;
@Override
protected void onHandleWork(Intent intent) {
super.onHandleWork(intent);
@Override
protected void onHandleWork(Intent intent) {
super.onHandleWork(intent);
GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
if (geofencingEvent.hasError()) {
Timber.e("geofence error code %s", geofencingEvent.getErrorCode());
return;
}
GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
if (geofencingEvent.hasError()) {
Timber.e("geofence error code %s", geofencingEvent.getErrorCode());
return;
}
int transitionType = geofencingEvent.getGeofenceTransition();
int transitionType = geofencingEvent.getGeofenceTransition();
List<com.google.android.gms.location.Geofence> triggeringGeofences = geofencingEvent.getTriggeringGeofences();
Timber.i("Received geofence transition: %s, %s", transitionType, triggeringGeofences);
if (transitionType == com.google.android.gms.location.Geofence.GEOFENCE_TRANSITION_ENTER) {
for (com.google.android.gms.location.Geofence triggerGeofence : triggeringGeofences) {
triggerNotification(triggerGeofence);
}
} else {
Timber.w("invalid geofence transition type: %s", transitionType);
}
List<com.google.android.gms.location.Geofence> triggeringGeofences = geofencingEvent
.getTriggeringGeofences();
Timber.i("Received geofence transition: %s, %s", transitionType, triggeringGeofences);
if (transitionType == com.google.android.gms.location.Geofence.GEOFENCE_TRANSITION_ENTER) {
for (com.google.android.gms.location.Geofence triggerGeofence : triggeringGeofences) {
triggerNotification(triggerGeofence);
}
} else {
Timber.w("invalid geofence transition type: %s", transitionType);
}
}
@Override
protected void inject(IntentServiceComponent component) {
component.inject(this);
@Override
protected void inject(IntentServiceComponent component) {
component.inject(this);
}
private void triggerNotification(com.google.android.gms.location.Geofence triggeringGeofence) {
String requestId = triggeringGeofence.getRequestId();
try {
Location location = locationDao.getGeofence(Long.parseLong(requestId));
notifier.triggerTaskNotification(location.getTask(), ReminderService.TYPE_ALARM);
} catch (Exception e) {
Timber.e(e, "Error triggering geofence %s: %s", requestId, e.getMessage());
}
}
private void triggerNotification(com.google.android.gms.location.Geofence triggeringGeofence) {
String requestId = triggeringGeofence.getRequestId();
try {
Location location = locationDao.getGeofence(Long.parseLong(requestId));
notifier.triggerTaskNotification(location.getTask(), ReminderService.TYPE_ALARM);
} catch(Exception e) {
Timber.e(e, "Error triggering geofence %s: %s", requestId, e.getMessage());
}
public static class Broadcast extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
JobIntentService.enqueueWork(context, GeofenceTransitionsIntentService.class,
JobManager.JOB_ID_GEOFENCE_TRANSITION, intent);
}
}
}

@ -2,55 +2,54 @@ package org.tasks.location;
import android.content.Context;
import android.os.Bundle;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.location.places.Places;
import org.tasks.injection.ForApplication;
import javax.inject.Inject;
import org.tasks.injection.ForApplication;
import timber.log.Timber;
public class GoogleApi implements GoogleApiClient.ConnectionCallbacks {
private final GoogleApiClient.Builder builder;
private GoogleApiClient googleApiClient;
private GoogleApiClientConnectionHandler googleApiClientConnectionHandler;
public interface GoogleApiClientConnectionHandler {
void onConnect(GoogleApiClient client);
}
@Inject
public GoogleApi(@ForApplication Context context) {
builder = new GoogleApiClient.Builder(context)
.addApi(LocationServices.API)
.addApi(Places.GEO_DATA_API)
.addConnectionCallbacks(this);
}
public void connect(final GoogleApiClientConnectionHandler googleApiClientConnectionHandler) {
connect(googleApiClientConnectionHandler, connectionResult -> Timber.e("onConnectionFailed(%s)", connectionResult));
}
private void connect(final GoogleApiClientConnectionHandler googleApiClientConnectionHandler, GoogleApiClient.OnConnectionFailedListener onConnectionFailedListener) {
this.googleApiClientConnectionHandler = googleApiClientConnectionHandler;
googleApiClient = builder
.addOnConnectionFailedListener(onConnectionFailedListener)
.build();
googleApiClient.connect();
}
@Override
public void onConnected(Bundle bundle) {
Timber.i("onConnected(Bundle)");
googleApiClientConnectionHandler.onConnect(googleApiClient);
}
@Override
public void onConnectionSuspended(int i) {
Timber.i("onConnectionSuspended(%s)", i);
}
private final GoogleApiClient.Builder builder;
private GoogleApiClient googleApiClient;
private GoogleApiClientConnectionHandler googleApiClientConnectionHandler;
@Inject
public GoogleApi(@ForApplication Context context) {
builder = new GoogleApiClient.Builder(context)
.addApi(LocationServices.API)
.addApi(Places.GEO_DATA_API)
.addConnectionCallbacks(this);
}
public void connect(final GoogleApiClientConnectionHandler googleApiClientConnectionHandler) {
connect(googleApiClientConnectionHandler,
connectionResult -> Timber.e("onConnectionFailed(%s)", connectionResult));
}
private void connect(final GoogleApiClientConnectionHandler googleApiClientConnectionHandler,
GoogleApiClient.OnConnectionFailedListener onConnectionFailedListener) {
this.googleApiClientConnectionHandler = googleApiClientConnectionHandler;
googleApiClient = builder
.addOnConnectionFailedListener(onConnectionFailedListener)
.build();
googleApiClient.connect();
}
@Override
public void onConnected(Bundle bundle) {
Timber.i("onConnected(Bundle)");
googleApiClientConnectionHandler.onConnect(googleApiClient);
}
@Override
public void onConnectionSuspended(int i) {
Timber.i("onConnectionSuspended(%s)", i);
}
public interface GoogleApiClientConnectionHandler {
void onConnect(GoogleApiClient client);
}
}

@ -4,43 +4,42 @@ import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
import com.google.android.gms.common.GooglePlayServicesNotAvailableException;
import com.google.android.gms.common.GooglePlayServicesRepairableException;
import com.google.android.gms.location.places.Place;
import com.google.android.gms.maps.model.LatLng;
import org.tasks.R;
import org.tasks.data.Location;
import org.tasks.preferences.Preferences;
import timber.log.Timber;
public class PlacePicker {
public static Intent getIntent(Activity activity) {
com.google.android.gms.location.places.ui.PlacePicker.IntentBuilder builder =
new com.google.android.gms.location.places.ui.PlacePicker.IntentBuilder();
try {
return builder.build(activity);
} catch (GooglePlayServicesRepairableException e) {
Timber.e(e, e.getMessage());
activity.startActivity(e.getIntent());
} catch (GooglePlayServicesNotAvailableException e) {
Timber.e(e, e.getMessage());
Toast.makeText(activity, R.string.common_google_play_services_notification_ticker, Toast.LENGTH_LONG).show();
}
return null;
}
public static Location getPlace(Context context, Intent data, Preferences preferences) {
Place place = com.google.android.gms.location.places.ui.PlacePicker.getPlace(context, data);
LatLng latLng = place.getLatLng();
Location location = new Location();
location.setName(place.getName().toString());
location.setLatitude(latLng.latitude);
location.setLongitude(latLng.longitude);
location.setRadius(preferences.getIntegerFromString(R.string.p_geofence_radius, 250));
Timber.i("Picked %s", location);
return location;
public static Intent getIntent(Activity activity) {
com.google.android.gms.location.places.ui.PlacePicker.IntentBuilder builder =
new com.google.android.gms.location.places.ui.PlacePicker.IntentBuilder();
try {
return builder.build(activity);
} catch (GooglePlayServicesRepairableException e) {
Timber.e(e, e.getMessage());
activity.startActivity(e.getIntent());
} catch (GooglePlayServicesNotAvailableException e) {
Timber.e(e, e.getMessage());
Toast.makeText(activity, R.string.common_google_play_services_notification_ticker,
Toast.LENGTH_LONG).show();
}
return null;
}
public static Location getPlace(Context context, Intent data, Preferences preferences) {
Place place = com.google.android.gms.location.places.ui.PlacePicker.getPlace(context, data);
LatLng latLng = place.getLatLng();
Location location = new Location();
location.setName(place.getName().toString());
location.setLatitude(latLng.latitude);
location.setLongitude(latLng.longitude);
location.setRadius(preferences.getIntegerFromString(R.string.p_geofence_radius, 250));
Timber.i("Picked %s", location);
return location;
}
}

@ -3,6 +3,7 @@
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.mdimension.jchronic;
import com.mdimension.jchronic.handlers.Handler;
@ -15,7 +16,6 @@ import com.mdimension.jchronic.tags.Separator;
import com.mdimension.jchronic.tags.TimeZone;
import com.mdimension.jchronic.utils.Span;
import com.mdimension.jchronic.utils.Token;
import java.util.LinkedList;
import java.util.List;
@ -38,35 +38,35 @@ public class AstridChronic {
* Options are:
*
* [<tt>:context</tt>]
* <tt>:past</tt> or <tt>:future</tt> (defaults to <tt>:future</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.
* 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)
* 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
* 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+)
* +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.
* 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))
* 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.
* 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.
*/
private static Span parse(String text, Options options) {
// store now for later =)
@ -79,9 +79,9 @@ public class AstridChronic {
List<Token> tokens = AstridChronic.baseTokenize(normalizedText);
try {
tokens = Repeater.scan(tokens, options);
tokens = Repeater.scan(tokens, options);
} catch (Throwable e) {
throw new RuntimeException("Failed to scan tokens.", e);
throw new RuntimeException("Failed to scan tokens.", e);
}
tokens = Grabber.scan(tokens, options);
@ -135,7 +135,8 @@ public class AstridChronic {
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("\\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");
@ -174,10 +175,9 @@ public class AstridChronic {
long guessValue;
if (span.getWidth() > 1) {
guessValue = span.getBegin() + (span.getWidth() / 2);
}
else {
} else {
guessValue = span.getBegin();
}
return new Span(guessValue, guessValue);
return new Span(guessValue, guessValue);
}
}

@ -3,10 +3,10 @@
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.andlib.data;
import android.text.TextUtils;
import com.todoroo.andlib.sql.Field;
/**
@ -16,140 +16,143 @@ import com.todoroo.andlib.sql.Field;
* value is null, it may be of an incorrect type, in which case an exception is
* thrown, or the correct type, in which case the value is returned.
*
* @param <TYPE> a database supported type, such as String or Integer
* @author Tim Su <tim@todoroo.com>
*
* @param <TYPE>
* a database supported type, such as String or Integer
*/
public abstract class Property<TYPE> extends Field implements Cloneable {
// --- implementation
/** The database table name this property */
public final Table table;
/** The database column name for this property */
public final String name;
/**
* Create a property by table and column name. Uses the default property
* expression which is derived from default table name
*/
Property(Table table, String columnName) {
this(table, columnName, (table == null) ? (columnName) : (table.name() + "." + columnName));
// --- implementation
/**
* The database table name this property
*/
public final Table table;
/**
* The database column name for this property
*/
public final String name;
/**
* Create a property by table and column name. Uses the default property
* expression which is derived from default table name
*/
Property(Table table, String columnName) {
this(table, columnName, (table == null) ? (columnName) : (table.name() + "." + columnName));
}
/**
* Create a property by table and column name, manually specifying an
* expression to use in SQL
*/
Property(Table table, String columnName, String expression) {
super(expression);
this.table = table;
this.name = columnName;
}
/**
* Return a clone of this property
*/
@Override
public Property<TYPE> clone() {
try {
return (Property<TYPE>) super.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
/**
* Create a property by table and column name, manually specifying an
* expression to use in SQL
*/
Property(Table table, String columnName, String expression) {
super(expression);
this.table = table;
this.name = columnName;
}
/**
* Return a clone of this property
*/
Property<TYPE> cloneAs(String tableAlias, String columnAlias) {
Table aliasedTable = this.table;
if (!TextUtils.isEmpty(tableAlias)) {
aliasedTable = table.as(tableAlias);
}
/**
* Return a clone of this property
*/
@Override
public Property<TYPE> clone() {
try {
return (Property<TYPE>) super.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
try {
Property<TYPE> newInstance = this.getClass().getConstructor(Table.class, String.class)
.newInstance(aliasedTable, this.name);
if (!TextUtils.isEmpty(columnAlias)) {
return (Property<TYPE>) newInstance.as(columnAlias);
}
return newInstance;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* Return a clone of this property
*/
Property<TYPE> cloneAs(String tableAlias, String columnAlias) {
Table aliasedTable = this.table;
if (!TextUtils.isEmpty(tableAlias)) {
aliasedTable = table.as(tableAlias);
}
try {
Property<TYPE> newInstance = this.getClass().getConstructor(Table.class, String.class).newInstance(aliasedTable, this.name);
if(!TextUtils.isEmpty(columnAlias)) {
return (Property<TYPE>) newInstance.as(columnAlias);
}
return newInstance;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
// --- children
// --- children
public String getColumnName() {
if (hasAlias()) {
return alias;
}
return name;
}
/**
* Integer property type. See {@link Property}
*
* @author Tim Su <tim@todoroo.com>
*/
public static class IntegerProperty extends Property<Integer> {
public IntegerProperty(Table table, String name) {
super(table, name);
}
/**
* Integer property type. See {@link Property}
*
* @author Tim Su <tim@todoroo.com>
*
*/
public static class IntegerProperty extends Property<Integer> {
IntegerProperty(String name, String expression) {
super(null, name, expression);
}
}
public IntegerProperty(Table table, String name) {
super(table, name);
}
/**
* String property type. See {@link Property}
*
* @author Tim Su <tim@todoroo.com>
*/
public static class StringProperty extends Property<String> {
IntegerProperty(String name, String expression) {
super(null, name, expression);
}
public StringProperty(Table table, String name) {
super(table, name);
}
/**
* String property type. See {@link Property}
*
* @author Tim Su <tim@todoroo.com>
*
*/
public static class StringProperty extends Property<String> {
public StringProperty(Table table, String name) {
super(table, name);
}
@Override
public StringProperty as(String newAlias) {
return (StringProperty) super.as(newAlias);
}
@Override
public StringProperty as(String newAlias) {
return (StringProperty) super.as(newAlias);
}
}
/**
* Long property type. See {@link Property}
*
* @author Tim Su <tim@todoroo.com>
*/
public static class LongProperty extends Property<Long> {
/**
* Long property type. See {@link Property}
*
* @author Tim Su <tim@todoroo.com>
*
*/
public static class LongProperty extends Property<Long> {
public LongProperty(Table table, String name) {
super(table, name);
}
@Override
public LongProperty cloneAs(String tableAlias, String columnAlias) {
return (LongProperty) super.cloneAs(tableAlias, columnAlias);
}
public LongProperty(Table table, String name) {
super(table, name);
}
public String getColumnName() {
if (hasAlias()) {
return alias;
}
return name;
@Override
public LongProperty cloneAs(String tableAlias, String columnAlias) {
return (LongProperty) super.cloneAs(tableAlias, columnAlias);
}
}
// --- pseudo-properties
// --- pseudo-properties
/**
* Runs a SQL function and returns the result as a string
*/
public static class CountProperty extends IntegerProperty {
/** Runs a SQL function and returns the result as a string */
public static class CountProperty extends IntegerProperty {
public CountProperty() {
super("count", "COUNT(1)");
alias = "count";
}
public CountProperty() {
super("count", "COUNT(1)");
alias = "count";
}
}
}

@ -3,6 +3,7 @@
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.andlib.data;
import com.todoroo.andlib.sql.SqlTable;
@ -12,41 +13,41 @@ import com.todoroo.andlib.sql.SqlTable;
* clone the table when it returns.
*
* @author Tim Su <tim@todoroo.com>
*
*/
public final class Table extends SqlTable {
public final String name;
public Table(String name) {
this(name, null);
}
public final String name;
private Table(String name, String alias) {
super(name);
this.name = name;
this.alias = alias;
}
public Table(String name) {
this(name, null);
}
/**
* Create a new join table based on this table, but with an alias
*/
@Override
public Table as(String newAlias) {
return new Table(name, newAlias);
}
private Table(String name, String alias) {
super(name);
this.name = name;
this.alias = alias;
}
/**
* Create a new join table based on this table, but with an alias
*/
@Override
public Table as(String newAlias) {
return new Table(name, newAlias);
}
@Override
public String toString() {
if(hasAlias()) {
return expression + " AS " + alias; //$NON-NLS-1$
}
return expression;
@Override
public String toString() {
if (hasAlias()) {
return expression + " AS " + alias; //$NON-NLS-1$
}
return expression;
}
public String name() {
if(hasAlias()) {
return alias;
}
return name;
public String name() {
if (hasAlias()) {
return alias;
}
return name;
}
}

@ -3,6 +3,7 @@
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.andlib.sql;
import static com.todoroo.andlib.sql.SqlConstants.AND;
@ -13,64 +14,64 @@ import static com.todoroo.andlib.sql.SqlConstants.RIGHT_PARENTHESIS;
import static com.todoroo.andlib.sql.SqlConstants.SPACE;
public abstract class Criterion {
final Operator operator;
public Criterion(Operator operator) {
this.operator = operator;
public static final Criterion all = new Criterion(Operator.exists) {
@Override
protected void populate(StringBuilder sb) {
sb.append(1);
}
};
final Operator operator;
public static final Criterion all = new Criterion(Operator.exists) {
@Override
protected void populate(StringBuilder sb) {
sb.append(1);
}
};
public Criterion(Operator operator) {
this.operator = operator;
}
public static Criterion and(final Criterion criterion, final Criterion... criterions) {
return new Criterion(Operator.and) {
public static Criterion and(final Criterion criterion, final Criterion... criterions) {
return new Criterion(Operator.and) {
@Override
protected void populate(StringBuilder sb) {
sb.append(criterion);
for (Criterion c : criterions) {
sb.append(SPACE).append(AND).append(SPACE).append(c);
}
}
};
}
@Override
protected void populate(StringBuilder sb) {
sb.append(criterion);
for (Criterion c : criterions) {
sb.append(SPACE).append(AND).append(SPACE).append(c);
}
}
};
}
public static Criterion or(final Criterion criterion, final Criterion... criterions) {
return new Criterion(Operator.or) {
public static Criterion or(final Criterion criterion, final Criterion... criterions) {
return new Criterion(Operator.or) {
@Override
protected void populate(StringBuilder sb) {
sb.append(criterion);
for (Criterion c : criterions) {
sb.append(SPACE).append(OR).append(SPACE).append(c.toString());
}
}
};
}
@Override
protected void populate(StringBuilder sb) {
sb.append(criterion);
for (Criterion c : criterions) {
sb.append(SPACE).append(OR).append(SPACE).append(c.toString());
}
}
};
}
public static Criterion not(final Criterion criterion) {
return new Criterion(Operator.not) {
public static Criterion not(final Criterion criterion) {
return new Criterion(Operator.not) {
@Override
protected void populate(StringBuilder sb) {
sb.append(NOT).append(SPACE);
criterion.populate(sb);
}
};
}
@Override
protected void populate(StringBuilder sb) {
sb.append(NOT).append(SPACE);
criterion.populate(sb);
}
};
}
protected abstract void populate(StringBuilder sb);
protected abstract void populate(StringBuilder sb);
@Override
public String toString() {
StringBuilder builder = new StringBuilder(LEFT_PARENTHESIS);
populate(builder);
builder.append(RIGHT_PARENTHESIS);
return builder.toString();
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder(LEFT_PARENTHESIS);
populate(builder);
builder.append(RIGHT_PARENTHESIS);
return builder.toString();
}
}

@ -3,79 +3,82 @@
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.andlib.sql;
import static com.todoroo.andlib.sql.SqlConstants.AS;
import static com.todoroo.andlib.sql.SqlConstants.SPACE;
public abstract class DBObject<T extends DBObject<?>> implements Cloneable {
protected String alias;
protected final String expression;
DBObject(String expression){
this.expression = expression;
}
protected final String expression;
protected String alias;
protected T as(String newAlias) {
try {
T clone = (T) clone();
clone.alias = newAlias;
return clone;
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
DBObject(String expression) {
this.expression = expression;
}
protected boolean hasAlias() {
return alias != null;
protected T as(String newAlias) {
try {
T clone = (T) clone();
clone.alias = newAlias;
return clone;
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
protected boolean hasAlias() {
return alias != null;
}
DBObject<?> dbObject = (DBObject<?>) o;
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
if (alias != null ? !alias.equals(dbObject.alias) : dbObject.alias != null) {
return false;
}
if (expression != null ? !expression.equals(dbObject.expression) : dbObject.expression != null) {
return false;
}
DBObject<?> dbObject = (DBObject<?>) o;
return true;
if (alias != null ? !alias.equals(dbObject.alias) : dbObject.alias != null) {
return false;
}
@Override
public int hashCode() {
int result = alias != null ? alias.hashCode() : 0;
result = 31 * result + (expression != null ? expression.hashCode() : 0);
return result;
if (expression != null ? !expression.equals(dbObject.expression)
: dbObject.expression != null) {
return false;
}
@Override
public String toString() {
if (hasAlias()) {
return alias;
}
return expression;
return true;
}
@Override
public int hashCode() {
int result = alias != null ? alias.hashCode() : 0;
result = 31 * result + (expression != null ? expression.hashCode() : 0);
return result;
}
@Override
public String toString() {
if (hasAlias()) {
return alias;
}
return expression;
}
public final String toStringInSelect() {
StringBuilder sb = new StringBuilder(expression);
if (hasAlias()) {
sb.append(SPACE).append(AS).append(SPACE).append(alias);
} else {
int pos = expression.indexOf('.');
if(pos > 0) {
sb.append(SPACE).append(AS).append(SPACE).append(expression.substring(pos + 1));
}
}
return sb.toString();
public final String toStringInSelect() {
StringBuilder sb = new StringBuilder(expression);
if (hasAlias()) {
sb.append(SPACE).append(AS).append(SPACE).append(alias);
} else {
int pos = expression.indexOf('.');
if (pos > 0) {
sb.append(SPACE).append(AS).append(SPACE).append(expression.substring(pos + 1));
}
}
return sb.toString();
}
}

@ -3,6 +3,7 @@
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.andlib.sql;
import static com.todoroo.andlib.sql.SqlConstants.COMMA;
@ -12,68 +13,70 @@ import static com.todoroo.andlib.sql.SqlConstants.SPACE;
public class Field extends DBObject<Field> {
protected Field(String expression) {
super(expression);
}
protected Field(String expression) {
super(expression);
}
public static Field field(String expression) {
return new Field(expression);
}
public static Field field(String expression) {
return new Field(expression);
}
public Criterion eq(Object value) {
if(value == null) {
return UnaryCriterion.isNull(this);
}
return UnaryCriterion.eq(this, value);
public Criterion eq(Object value) {
if (value == null) {
return UnaryCriterion.isNull(this);
}
return UnaryCriterion.eq(this, value);
}
public Criterion neq(Object value) {
if(value == null) {
return UnaryCriterion.isNotNull(this);
}
return UnaryCriterion.neq(this, value);
public Criterion neq(Object value) {
if (value == null) {
return UnaryCriterion.isNotNull(this);
}
return UnaryCriterion.neq(this, value);
}
public Criterion gt(Object value) {
return UnaryCriterion.gt(this, value);
}
public Criterion gt(Object value) {
return UnaryCriterion.gt(this, value);
}
public Criterion lt(final Object value) {
return UnaryCriterion.lt(this, value);
}
public Criterion lt(final Object value) {
return UnaryCriterion.lt(this, value);
}
public Criterion lte(final Object value) {
return UnaryCriterion.lte(this, value);
}
public Criterion lte(final Object value) {
return UnaryCriterion.lte(this, value);
}
public Criterion like(final String value) {
return UnaryCriterion.like(this, value);
}
public Criterion like(final String value) {
return UnaryCriterion.like(this, value);
}
public <T> Criterion in(final Iterable<T> value) {
final Field field = this;
return new Criterion(Operator.in) {
public <T> Criterion in(final Iterable<T> value) {
final Field field = this;
return new Criterion(Operator.in) {
@Override
protected void populate(StringBuilder sb) {
sb.append(field).append(SPACE).append(Operator.in).append(SPACE).append(LEFT_PARENTHESIS).append(SPACE);
for (T t : value) {
sb.append(t.toString()).append(COMMA);
}
sb.deleteCharAt(sb.length() - 1).append(RIGHT_PARENTHESIS);
}
};
}
@Override
protected void populate(StringBuilder sb) {
sb.append(field).append(SPACE).append(Operator.in).append(SPACE).append(LEFT_PARENTHESIS)
.append(SPACE);
for (T t : value) {
sb.append(t.toString()).append(COMMA);
}
sb.deleteCharAt(sb.length() - 1).append(RIGHT_PARENTHESIS);
}
};
}
public Criterion in(final Query query) {
final Field field = this;
return new Criterion(Operator.in) {
public Criterion in(final Query query) {
final Field field = this;
return new Criterion(Operator.in) {
@Override
protected void populate(StringBuilder sb) {
sb.append(field).append(SPACE).append(Operator.in).append(SPACE).append(LEFT_PARENTHESIS).append(query)
.append(RIGHT_PARENTHESIS);
}
};
}
@Override
protected void populate(StringBuilder sb) {
sb.append(field).append(SPACE).append(Operator.in).append(SPACE).append(LEFT_PARENTHESIS)
.append(query)
.append(RIGHT_PARENTHESIS);
}
};
}
}

@ -3,18 +3,19 @@
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.andlib.sql;
public final class Functions {
public static Field upper(Field title) {
return new Field("UPPER(" + title.toString() + ")");
}
public static Field upper(Field title) {
return new Field("UPPER(" + title.toString() + ")");
}
/**
* @return SQL now (in milliseconds)
*/
public static Field now() {
return new Field("(strftime('%s','now')*1000)");
}
/**
* @return SQL now (in milliseconds)
*/
public static Field now() {
return new Field("(strftime('%s','now')*1000)");
}
}

@ -3,6 +3,7 @@
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.andlib.sql;
import static com.todoroo.andlib.sql.SqlConstants.AND;
@ -11,35 +12,37 @@ import static com.todoroo.andlib.sql.SqlConstants.ON;
import static com.todoroo.andlib.sql.SqlConstants.SPACE;
public class Join {
private final SqlTable joinTable;
private final JoinType joinType;
private final Criterion[] criterions;
private Join(SqlTable table, JoinType joinType, Criterion... criterions) {
joinTable = table;
this.joinType = joinType;
this.criterions = criterions;
}
public static Join inner(SqlTable expression, Criterion... criterions) {
return new Join(expression, JoinType.INNER, criterions);
}
private final SqlTable joinTable;
private final JoinType joinType;
private final Criterion[] criterions;
public static Join left(SqlTable table, Criterion... criterions) {
return new Join(table, JoinType.LEFT, criterions);
}
private Join(SqlTable table, JoinType joinType, Criterion... criterions) {
joinTable = table;
this.joinType = joinType;
this.criterions = criterions;
}
public static Join inner(SqlTable expression, Criterion... criterions) {
return new Join(expression, JoinType.INNER, criterions);
}
public static Join left(SqlTable table, Criterion... criterions) {
return new Join(table, JoinType.LEFT, criterions);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(joinType).append(SPACE).append(JOIN).append(SPACE).append(joinTable).append(SPACE).append(ON).append(SPACE).append("(");
for (int i = 0; i < criterions.length; i++) {
sb.append(criterions[i]);
if (i < criterions.length - 1) {
sb.append(SPACE).append(AND).append(SPACE);
}
}
sb.append(")");
return sb.toString();
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(joinType).append(SPACE).append(JOIN).append(SPACE).append(joinTable).append(SPACE)
.append(ON).append(SPACE).append("(");
for (int i = 0; i < criterions.length; i++) {
sb.append(criterions[i]);
if (i < criterions.length - 1) {
sb.append(SPACE).append(AND).append(SPACE);
}
}
sb.append(")");
return sb.toString();
}
}

@ -3,8 +3,9 @@
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.andlib.sql;
public enum JoinType {
INNER, LEFT
INNER, LEFT
}

@ -3,31 +3,32 @@
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.andlib.sql;
public final class Operator {
private final String operator;
public static final Operator eq = new Operator("=");
static final Operator neq = new Operator("<>");
public static final Operator isNull = new Operator("IS NULL");
static final Operator isNotNull = new Operator("IS NOT NULL");
static final Operator gt = new Operator(">");
static final Operator lt = new Operator("<");
static final Operator lte = new Operator("<=");
public static final Operator and = new Operator("AND");
public static final Operator or = new Operator("OR");
public static final Operator not = new Operator("NOT");
public static final Operator exists = new Operator("EXISTS");
public static final Operator like = new Operator("LIKE");
public static final Operator in = new Operator("IN");
public static final Operator eq = new Operator("=");
public static final Operator isNull = new Operator("IS NULL");
public static final Operator and = new Operator("AND");
public static final Operator or = new Operator("OR");
public static final Operator not = new Operator("NOT");
public static final Operator exists = new Operator("EXISTS");
public static final Operator like = new Operator("LIKE");
public static final Operator in = new Operator("IN");
static final Operator neq = new Operator("<>");
static final Operator isNotNull = new Operator("IS NOT NULL");
static final Operator gt = new Operator(">");
static final Operator lt = new Operator("<");
static final Operator lte = new Operator("<=");
private final String operator;
private Operator(String operator) {
this.operator = operator;
}
private Operator(String operator) {
this.operator = operator;
}
@Override
public String toString() {
return this.operator;
}
@Override
public String toString() {
return this.operator;
}
}

@ -3,59 +3,61 @@
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.andlib.sql;
import static com.todoroo.andlib.sql.SqlConstants.SPACE;
import java.util.ArrayList;
import java.util.List;
import static com.todoroo.andlib.sql.SqlConstants.SPACE;
public class Order {
private final Object expression;
private final List<Order> secondaryExpressions;
private final OrderType orderType;
private Order(Object expression) {
this(expression, OrderType.ASC);
}
private Order(Object expression, OrderType orderType) {
this.expression = expression;
this.orderType = orderType;
this.secondaryExpressions = new ArrayList<>();
}
public static Order asc(Object expression) {
return new Order(expression);
}
public static Order desc(Object expression) {
return new Order(expression, OrderType.DESC);
}
public void addSecondaryExpression(Order secondary) {
secondaryExpressions.add(secondary);
private final Object expression;
private final List<Order> secondaryExpressions;
private final OrderType orderType;
private Order(Object expression) {
this(expression, OrderType.ASC);
}
private Order(Object expression, OrderType orderType) {
this.expression = expression;
this.orderType = orderType;
this.secondaryExpressions = new ArrayList<>();
}
public static Order asc(Object expression) {
return new Order(expression);
}
public static Order desc(Object expression) {
return new Order(expression, OrderType.DESC);
}
public void addSecondaryExpression(Order secondary) {
secondaryExpressions.add(secondary);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(expression.toString())
.append(SPACE)
.append(orderType.toString());
for (Order secondary : secondaryExpressions) {
sb.append(", ").append(secondary.toString()); //$NON-NLS-1$
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(expression.toString())
.append(SPACE)
.append(orderType.toString());
for (Order secondary : secondaryExpressions) {
sb.append(", ").append(secondary.toString()); //$NON-NLS-1$
}
return sb.toString();
}
return sb.toString();
}
public Order reverse() {
if(orderType == OrderType.ASC) {
return new Order(expression, OrderType.DESC);
} else {
return new Order(expression, OrderType.ASC);
}
public Order reverse() {
if (orderType == OrderType.ASC) {
return new Order(expression, OrderType.DESC);
} else {
return new Order(expression, OrderType.ASC);
}
}
}

@ -3,8 +3,9 @@
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.andlib.sql;
public enum OrderType {
DESC, ASC
DESC, ASC
}

@ -3,11 +3,8 @@
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.andlib.sql;
import com.todoroo.astrid.data.Task;
import java.util.ArrayList;
package com.todoroo.andlib.sql;
import static com.todoroo.andlib.sql.SqlConstants.ALL;
import static com.todoroo.andlib.sql.SqlConstants.COMMA;
@ -19,142 +16,147 @@ import static com.todoroo.andlib.sql.SqlConstants.SPACE;
import static com.todoroo.andlib.sql.SqlConstants.WHERE;
import static java.util.Arrays.asList;
public final class Query {
private SqlTable table;
private String queryTemplate = null;
private final ArrayList<Criterion> criterions = new ArrayList<>();
private final ArrayList<Field> fields = new ArrayList<>();
private final ArrayList<Join> joins = new ArrayList<>();
private final ArrayList<Order> orders = new ArrayList<>();
private int limits = -1;
private Query(Field... fields) {
this.fields.addAll(asList(fields));
}
public static Query select() {
return new Query(Task.PROPERTIES);
}
public static Query select(Field... fields) {
return new Query(fields);
}
public Query from(SqlTable fromTable) {
this.table = fromTable;
return this;
}
public Query join(Join... join) {
joins.addAll(asList(join));
return this;
}
public Query where(Criterion criterion) {
criterions.add(criterion);
return this;
}
public Query orderBy(Order... order) {
orders.addAll(asList(order));
return this;
}
public Query limit(int limit) {
limits = limit;
return this;
}
@Override
public boolean equals(Object o) {
return this == o || !(o == null || getClass() != o.getClass()) && this.toString().equals(o.toString());
}
@Override
public int hashCode() {
return toString().hashCode();
}
@Override
public String toString() {
StringBuilder sql = new StringBuilder();
visitSelectClause(sql);
visitFromClause(sql);
visitJoinClause(sql);
if(queryTemplate == null) {
visitWhereClause(sql);
visitOrderByClause(sql);
visitLimitClause(sql);
} else {
if(orders.size() > 0) {
throw new IllegalStateException("Can't have extras AND query template"); //$NON-NLS-1$
}
sql.append(queryTemplate);
}
return sql.toString();
}
private void visitOrderByClause(StringBuilder sql) {
if (orders.isEmpty()) {
return;
}
sql.append(ORDER_BY);
for (Order order : orders) {
sql.append(SPACE).append(order).append(COMMA);
}
sql.deleteCharAt(sql.length() - 1).append(SPACE);
}
private void visitWhereClause(StringBuilder sql) {
if (criterions.isEmpty()) {
return;
}
sql.append(WHERE);
for (Criterion criterion : criterions) {
sql.append(SPACE).append(criterion).append(SPACE);
}
}
private void visitJoinClause(StringBuilder sql) {
for (Join join : joins) {
sql.append(join).append(SPACE);
}
}
private void visitFromClause(StringBuilder sql) {
if (table == null) {
return;
}
sql.append(FROM).append(SPACE).append(table).append(SPACE);
}
private void visitSelectClause(StringBuilder sql) {
sql.append(SELECT).append(SPACE);
if (fields.isEmpty()) {
sql.append(ALL).append(SPACE);
return;
}
for (Field field : fields) {
sql.append(field.toStringInSelect()).append(COMMA);
}
sql.deleteCharAt(sql.length() - 1).append(SPACE);
}
import com.todoroo.astrid.data.Task;
import java.util.ArrayList;
private void visitLimitClause(StringBuilder sql) {
if(limits > -1) {
sql.append(LIMIT).append(SPACE).append(limits).append(SPACE);
}
}
public final class Query {
/**
* Add the SQL query template (comes after the "from")
* @return query
*/
public Query withQueryTemplate(String template) {
queryTemplate = template;
return this;
}
private final ArrayList<Criterion> criterions = new ArrayList<>();
private final ArrayList<Field> fields = new ArrayList<>();
private final ArrayList<Join> joins = new ArrayList<>();
private final ArrayList<Order> orders = new ArrayList<>();
private SqlTable table;
private String queryTemplate = null;
private int limits = -1;
private Query(Field... fields) {
this.fields.addAll(asList(fields));
}
public static Query select() {
return new Query(Task.PROPERTIES);
}
public static Query select(Field... fields) {
return new Query(fields);
}
public Query from(SqlTable fromTable) {
this.table = fromTable;
return this;
}
public Query join(Join... join) {
joins.addAll(asList(join));
return this;
}
public Query where(Criterion criterion) {
criterions.add(criterion);
return this;
}
public Query orderBy(Order... order) {
orders.addAll(asList(order));
return this;
}
public Query limit(int limit) {
limits = limit;
return this;
}
@Override
public boolean equals(Object o) {
return this == o || !(o == null || getClass() != o.getClass()) && this.toString()
.equals(o.toString());
}
@Override
public int hashCode() {
return toString().hashCode();
}
@Override
public String toString() {
StringBuilder sql = new StringBuilder();
visitSelectClause(sql);
visitFromClause(sql);
visitJoinClause(sql);
if (queryTemplate == null) {
visitWhereClause(sql);
visitOrderByClause(sql);
visitLimitClause(sql);
} else {
if (orders.size() > 0) {
throw new IllegalStateException("Can't have extras AND query template"); //$NON-NLS-1$
}
sql.append(queryTemplate);
}
return sql.toString();
}
private void visitOrderByClause(StringBuilder sql) {
if (orders.isEmpty()) {
return;
}
sql.append(ORDER_BY);
for (Order order : orders) {
sql.append(SPACE).append(order).append(COMMA);
}
sql.deleteCharAt(sql.length() - 1).append(SPACE);
}
private void visitWhereClause(StringBuilder sql) {
if (criterions.isEmpty()) {
return;
}
sql.append(WHERE);
for (Criterion criterion : criterions) {
sql.append(SPACE).append(criterion).append(SPACE);
}
}
private void visitJoinClause(StringBuilder sql) {
for (Join join : joins) {
sql.append(join).append(SPACE);
}
}
private void visitFromClause(StringBuilder sql) {
if (table == null) {
return;
}
sql.append(FROM).append(SPACE).append(table).append(SPACE);
}
private void visitSelectClause(StringBuilder sql) {
sql.append(SELECT).append(SPACE);
if (fields.isEmpty()) {
sql.append(ALL).append(SPACE);
return;
}
for (Field field : fields) {
sql.append(field.toStringInSelect()).append(COMMA);
}
sql.deleteCharAt(sql.length() - 1).append(SPACE);
}
private void visitLimitClause(StringBuilder sql) {
if (limits > -1) {
sql.append(LIMIT).append(SPACE).append(limits).append(SPACE);
}
}
/**
* Add the SQL query template (comes after the "from")
*
* @return query
*/
public Query withQueryTemplate(String template) {
queryTemplate = template;
return this;
}
}

@ -3,9 +3,8 @@
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.andlib.sql;
import java.util.ArrayList;
package com.todoroo.andlib.sql;
import static com.todoroo.andlib.sql.SqlConstants.COMMA;
import static com.todoroo.andlib.sql.SqlConstants.LIMIT;
@ -14,76 +13,77 @@ import static com.todoroo.andlib.sql.SqlConstants.SPACE;
import static com.todoroo.andlib.sql.SqlConstants.WHERE;
import static java.util.Arrays.asList;
import java.util.ArrayList;
/**
* Query Template returns a bunch of criteria that allows a query to be
* constructed
*
* @author Tim Su <tim@todoroo.com>
*
*/
public final class QueryTemplate {
private final ArrayList<Criterion> criterions = new ArrayList<>();
private final ArrayList<Join> joins = new ArrayList<>();
private final ArrayList<Order> orders = new ArrayList<>();
private Integer limit = null;
private final ArrayList<Criterion> criterions = new ArrayList<>();
private final ArrayList<Join> joins = new ArrayList<>();
private final ArrayList<Order> orders = new ArrayList<>();
private Integer limit = null;
public QueryTemplate join(Join... join) {
joins.addAll(asList(join));
return this;
}
public QueryTemplate join(Join... join) {
joins.addAll(asList(join));
return this;
}
public QueryTemplate where(Criterion criterion) {
criterions.add(criterion);
return this;
}
public QueryTemplate where(Criterion criterion) {
criterions.add(criterion);
return this;
}
public QueryTemplate orderBy(Order... order) {
orders.addAll(asList(order));
return this;
}
public QueryTemplate orderBy(Order... order) {
orders.addAll(asList(order));
return this;
}
@Override
public String toString() {
StringBuilder sql = new StringBuilder();
visitJoinClause(sql);
visitWhereClause(sql);
visitOrderByClause(sql);
if(limit != null) {
sql.append(LIMIT).append(SPACE).append(limit);
}
return sql.toString();
@Override
public String toString() {
StringBuilder sql = new StringBuilder();
visitJoinClause(sql);
visitWhereClause(sql);
visitOrderByClause(sql);
if (limit != null) {
sql.append(LIMIT).append(SPACE).append(limit);
}
return sql.toString();
}
private void visitOrderByClause(StringBuilder sql) {
if (orders.isEmpty()) {
return;
}
sql.append(ORDER_BY);
for (Order order : orders) {
sql.append(SPACE).append(order).append(COMMA);
}
sql.deleteCharAt(sql.length() - 1).append(SPACE);
private void visitOrderByClause(StringBuilder sql) {
if (orders.isEmpty()) {
return;
}
private void visitWhereClause(StringBuilder sql) {
if (criterions.isEmpty()) {
return;
}
sql.append(WHERE);
for (Criterion criterion : criterions) {
sql.append(SPACE).append(criterion).append(SPACE);
}
sql.append(ORDER_BY);
for (Order order : orders) {
sql.append(SPACE).append(order).append(COMMA);
}
sql.deleteCharAt(sql.length() - 1).append(SPACE);
}
private void visitJoinClause(StringBuilder sql) {
for (Join join : joins) {
sql.append(join).append(SPACE);
}
private void visitWhereClause(StringBuilder sql) {
if (criterions.isEmpty()) {
return;
}
sql.append(WHERE);
for (Criterion criterion : criterions) {
sql.append(SPACE).append(criterion).append(SPACE);
}
}
public QueryTemplate limit(int limitValue) {
this.limit = limitValue;
return this;
private void visitJoinClause(StringBuilder sql) {
for (Join join : joins) {
sql.append(join).append(SPACE);
}
}
public QueryTemplate limit(int limitValue) {
this.limit = limitValue;
return this;
}
}

@ -3,23 +3,25 @@
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.andlib.sql;
public final class SqlConstants {
public static final String SELECT = "SELECT";
public static final String SPACE = " ";
public static final String AS = "AS";
static final String COMMA = ",";
public static final String FROM = "FROM";
public static final String ON = "ON";
static final String JOIN = "JOIN";
public static final String ALL = "*";
static final String LEFT_PARENTHESIS = "(";
static final String RIGHT_PARENTHESIS = ")";
public static final String AND = "AND";
public static final String OR = "OR";
static final String ORDER_BY = "ORDER BY";
public static final String WHERE = "WHERE";
public static final String NOT = "NOT";
public static final String LIMIT = "LIMIT";
public static final String SELECT = "SELECT";
public static final String SPACE = " ";
public static final String AS = "AS";
public static final String FROM = "FROM";
public static final String ON = "ON";
public static final String ALL = "*";
public static final String AND = "AND";
public static final String OR = "OR";
public static final String WHERE = "WHERE";
public static final String NOT = "NOT";
public static final String LIMIT = "LIMIT";
static final String COMMA = ",";
static final String JOIN = "JOIN";
static final String LEFT_PARENTHESIS = "(";
static final String RIGHT_PARENTHESIS = ")";
static final String ORDER_BY = "ORDER BY";
}

@ -3,11 +3,12 @@
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.andlib.sql;
public class SqlTable extends DBObject<SqlTable> {
protected SqlTable(String expression) {
super(expression);
}
protected SqlTable(String expression) {
super(expression);
}
}

@ -3,101 +3,103 @@
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.andlib.sql;
import static com.todoroo.andlib.sql.SqlConstants.SPACE;
public class UnaryCriterion extends Criterion {
private final Field expression;
private final Object value;
private UnaryCriterion(Field expression, Operator operator, Object value) {
super(operator);
this.expression = expression;
this.value = value;
}
@Override
protected void populate(StringBuilder sb) {
beforePopulateOperator(sb);
populateOperator(sb);
afterPopulateOperator(sb);
}
public static Criterion eq(Field expression, Object value) {
return new UnaryCriterion(expression, Operator.eq, value);
}
@SuppressWarnings("WeakerAccess")
void beforePopulateOperator(StringBuilder sb) {
sb.append(expression);
}
@SuppressWarnings("WeakerAccess")
void populateOperator(StringBuilder sb) {
sb.append(operator);
}
@SuppressWarnings("WeakerAccess")
void afterPopulateOperator(StringBuilder sb) {
if(value == null) {
return;
}
if(value instanceof String) {
sb.append("'").append(sanitize((String) value)).append("'");
} else {
sb.append(value);
}
}
/**
* Sanitize the given input for SQL
*/
public static String sanitize(String input) {
return input.replace("'", "''");
}
static Criterion neq(Field field, Object value) {
return new UnaryCriterion(field, Operator.neq, value);
}
static Criterion gt(Field field, Object value) {
return new UnaryCriterion(field, Operator.gt, value);
}
static Criterion lt(Field field, Object value) {
return new UnaryCriterion(field, Operator.lt, value);
}
static Criterion lte(Field field, Object value) {
return new UnaryCriterion(field, Operator.lte, value);
}
public static Criterion isNull(Field field) {
return new UnaryCriterion(field, Operator.isNull, null) {
@Override
protected void populateOperator(StringBuilder sb) {
sb.append(SPACE).append(operator);
}
};
}
static Criterion isNotNull(Field field) {
return new UnaryCriterion(field, Operator.isNotNull, null) {
@Override
protected void populateOperator(StringBuilder sb) {
sb.append(SPACE).append(operator);
}
};
private final Field expression;
private final Object value;
private UnaryCriterion(Field expression, Operator operator, Object value) {
super(operator);
this.expression = expression;
this.value = value;
}
public static Criterion eq(Field expression, Object value) {
return new UnaryCriterion(expression, Operator.eq, value);
}
/**
* Sanitize the given input for SQL
*/
public static String sanitize(String input) {
return input.replace("'", "''");
}
static Criterion neq(Field field, Object value) {
return new UnaryCriterion(field, Operator.neq, value);
}
static Criterion gt(Field field, Object value) {
return new UnaryCriterion(field, Operator.gt, value);
}
static Criterion lt(Field field, Object value) {
return new UnaryCriterion(field, Operator.lt, value);
}
static Criterion lte(Field field, Object value) {
return new UnaryCriterion(field, Operator.lte, value);
}
public static Criterion isNull(Field field) {
return new UnaryCriterion(field, Operator.isNull, null) {
@Override
protected void populateOperator(StringBuilder sb) {
sb.append(SPACE).append(operator);
}
};
}
static Criterion isNotNull(Field field) {
return new UnaryCriterion(field, Operator.isNotNull, null) {
@Override
protected void populateOperator(StringBuilder sb) {
sb.append(SPACE).append(operator);
}
};
}
public static Criterion like(Field field, String value) {
return new UnaryCriterion(field, Operator.like, value) {
@Override
protected void populateOperator(StringBuilder sb) {
sb.append(SPACE).append(operator).append(SPACE);
}
};
}
@Override
protected void populate(StringBuilder sb) {
beforePopulateOperator(sb);
populateOperator(sb);
afterPopulateOperator(sb);
}
@SuppressWarnings("WeakerAccess")
void beforePopulateOperator(StringBuilder sb) {
sb.append(expression);
}
@SuppressWarnings("WeakerAccess")
void populateOperator(StringBuilder sb) {
sb.append(operator);
}
@SuppressWarnings("WeakerAccess")
void afterPopulateOperator(StringBuilder sb) {
if (value == null) {
return;
}
public static Criterion like(Field field, String value) {
return new UnaryCriterion(field, Operator.like, value) {
@Override
protected void populateOperator(StringBuilder sb) {
sb.append(SPACE).append(operator).append(SPACE);
}
};
if (value instanceof String) {
sb.append("'").append(sanitize((String) value)).append("'");
} else {
sb.append(value);
}
}
}

@ -3,6 +3,7 @@
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.andlib.utility;
import android.app.Activity;
@ -13,7 +14,6 @@ import android.util.DisplayMetrics;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.TextView;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
@ -23,257 +23,262 @@ import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import timber.log.Timber;
/**
* Android Utility Classes
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class AndroidUtilities {
public static final String SEPARATOR_ESCAPE = "!PIPE!"; //$NON-NLS-1$
public static final String SERIALIZATION_SEPARATOR = "|"; //$NON-NLS-1$
// --- utility methods
/** Suppress virtual keyboard until user's first tap */
public static void suppressVirtualKeyboard(final TextView editor) {
final int inputType = editor.getInputType();
editor.setInputType(InputType.TYPE_NULL);
editor.setOnTouchListener((v, event) -> {
editor.setInputType(inputType);
editor.setOnTouchListener(null);
return false;
});
}
// --- serialization
/**
* Serializes a content value into a string
*/
public static String mapToSerializedString(Map<String, Object> source) {
StringBuilder result = new StringBuilder();
for(Entry<String, Object> entry : source.entrySet()) {
addSerialized(result, entry.getKey(), entry.getValue());
}
return result.toString();
}
/** add serialized helper */
private static void addSerialized(StringBuilder result,
String key, Object value) {
result.append(key.replace(SERIALIZATION_SEPARATOR, SEPARATOR_ESCAPE)).append(
SERIALIZATION_SEPARATOR);
if(value instanceof Integer) {
result.append('i').append(value);
} else if(value instanceof Double) {
result.append('d').append(value);
} else if(value instanceof Long) {
result.append('l').append(value);
} else if(value instanceof String) {
result.append('s').append(value.toString().replace(SERIALIZATION_SEPARATOR, SEPARATOR_ESCAPE));
} else if (value instanceof Boolean) {
result.append('b').append(value);
} else {
throw new UnsupportedOperationException(value.getClass().toString());
}
result.append(SERIALIZATION_SEPARATOR);
}
public static Map<String, Object> mapFromSerializedString(String string) {
if (string == null) {
return new HashMap<>();
}
Map<String, Object> result = new HashMap<>();
fromSerialized(string, result, (object, key, type, value) -> {
switch (type) {
case 'i':
object.put(key, Integer.parseInt(value));
break;
case 'd':
object.put(key, Double.parseDouble(value));
break;
case 'l':
object.put(key, Long.parseLong(value));
break;
case 's':
object.put(key, value.replace(SEPARATOR_ESCAPE, SERIALIZATION_SEPARATOR));
break;
case 'b':
object.put(key, Boolean.parseBoolean(value));
break;
}
});
return result;
public static final String SEPARATOR_ESCAPE = "!PIPE!"; //$NON-NLS-1$
public static final String SERIALIZATION_SEPARATOR = "|"; //$NON-NLS-1$
// --- utility methods
/**
* Suppress virtual keyboard until user's first tap
*/
public static void suppressVirtualKeyboard(final TextView editor) {
final int inputType = editor.getInputType();
editor.setInputType(InputType.TYPE_NULL);
editor.setOnTouchListener((v, event) -> {
editor.setInputType(inputType);
editor.setOnTouchListener(null);
return false;
});
}
// --- serialization
/**
* Serializes a content value into a string
*/
public static String mapToSerializedString(Map<String, Object> source) {
StringBuilder result = new StringBuilder();
for (Entry<String, Object> entry : source.entrySet()) {
addSerialized(result, entry.getKey(), entry.getValue());
}
public interface SerializedPut<T> {
void put(T object, String key, char type, String value) throws NumberFormatException;
return result.toString();
}
/**
* add serialized helper
*/
private static void addSerialized(StringBuilder result,
String key, Object value) {
result.append(key.replace(SERIALIZATION_SEPARATOR, SEPARATOR_ESCAPE)).append(
SERIALIZATION_SEPARATOR);
if (value instanceof Integer) {
result.append('i').append(value);
} else if (value instanceof Double) {
result.append('d').append(value);
} else if (value instanceof Long) {
result.append('l').append(value);
} else if (value instanceof String) {
result.append('s')
.append(value.toString().replace(SERIALIZATION_SEPARATOR, SEPARATOR_ESCAPE));
} else if (value instanceof Boolean) {
result.append('b').append(value);
} else {
throw new UnsupportedOperationException(value.getClass().toString());
}
result.append(SERIALIZATION_SEPARATOR);
}
private static <T> void fromSerialized(String string, T object, SerializedPut<T> putter) {
String[] pairs = string.split("\\" + SERIALIZATION_SEPARATOR); //$NON-NLS-1$
for(int i = 0; i < pairs.length; i += 2) {
try {
String key = pairs[i].replaceAll(SEPARATOR_ESCAPE, SERIALIZATION_SEPARATOR);
String value = pairs[i+1].substring(1);
try {
putter.put(object, key, pairs[i+1].charAt(0), value);
} catch (NumberFormatException e) {
// failed parse to number
putter.put(object, key, 's', value);
Timber.e(e, e.getMessage());
}
} catch (IndexOutOfBoundsException e) {
Timber.e(e, e.getMessage());
}
}
public static Map<String, Object> mapFromSerializedString(String string) {
if (string == null) {
return new HashMap<>();
}
/**
* Copy a file from one place to another
* @throws Exception
*/
public static void copyFile(File in, File out) throws Exception {
FileInputStream fis = new FileInputStream(in);
FileOutputStream fos = new FileOutputStream(out);
Map<String, Object> result = new HashMap<>();
fromSerialized(string, result, (object, key, type, value) -> {
switch (type) {
case 'i':
object.put(key, Integer.parseInt(value));
break;
case 'd':
object.put(key, Double.parseDouble(value));
break;
case 'l':
object.put(key, Long.parseLong(value));
break;
case 's':
object.put(key, value.replace(SEPARATOR_ESCAPE, SERIALIZATION_SEPARATOR));
break;
case 'b':
object.put(key, Boolean.parseBoolean(value));
break;
}
});
return result;
}
private static <T> void fromSerialized(String string, T object, SerializedPut<T> putter) {
String[] pairs = string.split("\\" + SERIALIZATION_SEPARATOR); //$NON-NLS-1$
for (int i = 0; i < pairs.length; i += 2) {
try {
String key = pairs[i].replaceAll(SEPARATOR_ESCAPE, SERIALIZATION_SEPARATOR);
String value = pairs[i + 1].substring(1);
try {
copyStream(fis, fos);
} finally {
fis.close();
fos.close();
putter.put(object, key, pairs[i + 1].charAt(0), value);
} catch (NumberFormatException e) {
// failed parse to number
putter.put(object, key, 's', value);
Timber.e(e, e.getMessage());
}
} catch (IndexOutOfBoundsException e) {
Timber.e(e, e.getMessage());
}
}
/**
* Copy stream from source to destination
* @throws IOException
*/
private static void copyStream(InputStream source, OutputStream dest) throws IOException {
int bytes;
byte[] buffer;
int BUFFER_SIZE = 1024;
buffer = new byte[BUFFER_SIZE];
while ((bytes = source.read(buffer)) != -1) {
if (bytes == 0) {
bytes = source.read();
if (bytes < 0) {
break;
}
dest.write(bytes);
dest.flush();
continue;
}
dest.write(buffer, 0, bytes);
dest.flush();
}
}
public static int convertDpToPixels(DisplayMetrics displayMetrics, int dp) {
// developer.android.com/guide/practices/screens_support.html#dips-pels
return (int) (dp * displayMetrics.density + 0.5f);
}
public static boolean preLollipop() {
return !atLeastLollipop();
}
public static boolean preJellybean() {
return !atLeastJellybean();
}
public static boolean preOreo() {
return !atLeastOreo();
}
public static boolean atLeastJellybeanMR1() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1;
}
public static boolean atLeastJellybean() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
}
public static boolean atLeastKitKat() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
}
public static boolean atLeastLollipop() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
}
public static boolean atLeastMarshmallow() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
}
/**
* Copy a file from one place to another
*/
public static void copyFile(File in, File out) throws Exception {
FileInputStream fis = new FileInputStream(in);
FileOutputStream fos = new FileOutputStream(out);
try {
copyStream(fis, fos);
} finally {
fis.close();
fos.close();
}
}
/**
* Copy stream from source to destination
*/
private static void copyStream(InputStream source, OutputStream dest) throws IOException {
int bytes;
byte[] buffer;
int BUFFER_SIZE = 1024;
buffer = new byte[BUFFER_SIZE];
while ((bytes = source.read(buffer)) != -1) {
if (bytes == 0) {
bytes = source.read();
if (bytes < 0) {
break;
}
dest.write(bytes);
dest.flush();
continue;
}
public static boolean atLeastNougat() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
dest.write(buffer, 0, bytes);
dest.flush();
}
public static boolean atLeastOreo() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
}
public static int convertDpToPixels(DisplayMetrics displayMetrics, int dp) {
// developer.android.com/guide/practices/screens_support.html#dips-pels
return (int) (dp * displayMetrics.density + 0.5f);
}
public static boolean preLollipop() {
return !atLeastLollipop();
}
public static boolean preJellybean() {
return !atLeastJellybean();
}
public static boolean preOreo() {
return !atLeastOreo();
}
public static boolean atLeastJellybeanMR1() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1;
}
public static boolean atLeastJellybean() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
}
public static boolean atLeastKitKat() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
}
public static boolean atLeastLollipop() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
}
public static boolean atLeastMarshmallow() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
}
public static boolean atLeastNougat() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
}
public static boolean atLeastOreo() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
}
/**
* Sleep, ignoring interruption. Before using this method, think carefully
* about why you are ignoring interruptions.
*/
public static void sleepDeep(long l) {
try {
Thread.sleep(l);
} catch (InterruptedException e) {
// ignore
}
/**
* Sleep, ignoring interruption. Before using this method, think carefully
* about why you are ignoring interruptions.
*/
public static void sleepDeep(long l) {
try {
Thread.sleep(l);
} catch (InterruptedException e) {
// ignore
}
}
/**
* Capitalize the first character
*/
public static String capitalize(String string) {
return string.substring(0, 1).toUpperCase() + string.substring(1);
}
public static void hideKeyboard(Activity activity) {
try {
View currentFocus = activity.getCurrentFocus();
if (currentFocus != null) {
InputMethodManager inputMethodManager = (InputMethodManager) activity
.getSystemService(Context.INPUT_METHOD_SERVICE);
inputMethodManager.hideSoftInputFromWindow(currentFocus.getWindowToken(), 0);
}
} catch (Exception e) {
Timber.e(e, e.getMessage());
}
/**
* Capitalize the first character
*/
public static String capitalize(String string) {
return string.substring(0, 1).toUpperCase() + string.substring(1);
}
/**
* Dismiss the keyboard if it is displayed by any of the listed views
*
* @param views - a list of views that might potentially be displaying the keyboard
*/
public static void hideSoftInputForViews(Context context, View... views) {
InputMethodManager imm = (InputMethodManager) context
.getSystemService(Context.INPUT_METHOD_SERVICE);
for (View v : views) {
imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
}
public static void hideKeyboard(Activity activity) {
try {
View currentFocus = activity.getCurrentFocus();
if (currentFocus != null) {
InputMethodManager inputMethodManager = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE);
inputMethodManager.hideSoftInputFromWindow(currentFocus.getWindowToken(), 0);
}
} catch (Exception e) {
Timber.e(e, e.getMessage());
}
}
/**
* Returns the final word characters after the last '.'
*/
public static String getFileExtension(String file) {
int index = file.lastIndexOf('.');
String extension = "";
if (index > 0) {
extension = file.substring(index + 1);
if (!extension.matches("\\w+")) {
extension = "";
}
}
return extension;
}
/**
* Dismiss the keyboard if it is displayed by any of the listed views
* @param views - a list of views that might potentially be displaying the keyboard
*/
public static void hideSoftInputForViews(Context context, View...views) {
InputMethodManager imm = (InputMethodManager)context.getSystemService(Context.INPUT_METHOD_SERVICE);
for (View v : views) {
imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
}
}
public interface SerializedPut<T> {
/**
* Returns the final word characters after the last '.'
*/
public static String getFileExtension(String file) {
int index = file.lastIndexOf('.');
String extension = "";
if (index > 0) {
extension = file.substring(index + 1);
if (!extension.matches("\\w+")) {
extension = "";
}
}
return extension;
}
void put(T object, String key, char type, String value) throws NumberFormatException;
}
}

@ -3,206 +3,212 @@
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.andlib.utility;
import static org.tasks.date.DateTimeUtils.newDateTime;
import static org.tasks.time.DateTimeUtils.currentTimeMillis;
import android.content.Context;
import android.text.format.DateFormat;
import com.todoroo.astrid.data.Task;
import org.tasks.R;
import org.tasks.locale.Locale;
import org.tasks.time.DateTime;
import static org.tasks.date.DateTimeUtils.newDateTime;
import static org.tasks.time.DateTimeUtils.currentTimeMillis;
public class DateUtilities {
private static final long abbreviationLimit = DateUtilities.ONE_DAY * 6;
/**
* Add the specified amount of months to the given time.<br/>
* The day of month will stay the same.<br/>
*
* @param time the base-time (in milliseconds) to which the amount of months is added
* @param interval the amount of months to be added
* @return the calculated time in milliseconds
*/
public static long addCalendarMonthsToUnixtime(long time, int interval) {
DateTime dt = new DateTime(time);
DateTime result = dt.plusMonths(interval);
// preserving java.util.date behavior
int diff = dt.getDayOfMonth() - result.getDayOfMonth();
if(diff > 0) {
result = result.plusDays(diff);
}
return result.getMillis();
}
/** Returns unixtime for current time */
public static long now() {
return currentTimeMillis();
}
/** Returns unixtime one month from now */
public static long oneMonthFromNow() {
return addCalendarMonthsToUnixtime(currentTimeMillis(), 1);
}
/** Represents a single hour */
public static final long ONE_HOUR = 3600000L;
/** Represents a single day */
public static final long ONE_DAY = 24 * ONE_HOUR;
/** Represents a single week */
public static final long ONE_WEEK = 7 * ONE_DAY;
/** Represents a single minute */
public static final long ONE_MINUTE = 60000L;
private static final String JA = "MMM d\u65E5";
private static final String JA_YEAR = "yy\u5E74 " + JA;
private static final String KO = "MMM d\uC77C";
private static final String KO_YEAR = "yy\uB144 " + KO;
private static final String ZH = "MMM d\u65E5";
private static final String ZH_YEAR = "yy\u5E74 " + ZH;
/**
* Represents a single hour
*/
public static final long ONE_HOUR = 3600000L;
/**
* Represents a single day
*/
public static final long ONE_DAY = 24 * ONE_HOUR;
/**
* Represents a single week
*/
public static final long ONE_WEEK = 7 * ONE_DAY;
/**
* Represents a single minute
*/
public static final long ONE_MINUTE = 60000L;
private static final long abbreviationLimit = DateUtilities.ONE_DAY * 6;
private static final String JA = "MMM d\u65E5";
private static final String JA_YEAR = "yy\u5E74 " + JA;
private static final String KO = "MMM d\uC77C";
private static final String KO_YEAR = "yy\uB144 " + KO;
private static final String ZH = "MMM d\u65E5";
private static final String ZH_YEAR = "yy\u5E74 " + ZH;
static Boolean is24HourOverride = null;
/**
* Add the specified amount of months to the given time.<br/>
* The day of month will stay the same.<br/>
*
* @param time the base-time (in milliseconds) to which the amount of months is added
* @param interval the amount of months to be added
* @return the calculated time in milliseconds
*/
public static long addCalendarMonthsToUnixtime(long time, int interval) {
DateTime dt = new DateTime(time);
DateTime result = dt.plusMonths(interval);
// preserving java.util.date behavior
int diff = dt.getDayOfMonth() - result.getDayOfMonth();
if (diff > 0) {
result = result.plusDays(diff);
}
return result.getMillis();
}
/**
* Returns unixtime for current time
*/
public static long now() {
return currentTimeMillis();
}
/* ======================================================================
* =========================================================== formatters
* ====================================================================== */
static Boolean is24HourOverride = null;
public static boolean is24HourFormat(Context context) {
if(is24HourOverride != null) {
return is24HourOverride;
}
return DateFormat.is24HourFormat(context);
}
public static String getTimeString(Context context, long timestamp) {
return getTimeString(context, newDateTime(timestamp));
}
public static String getTimeString(Context context, DateTime date) {
String value;
if (is24HourFormat(context)) {
value = "HH:mm";
} else if (date.getMinuteOfHour() == 0){
value = "h a";
} else {
value = "h:mm a";
}
return date.toString(value);
}
public static String getLongDateString(DateTime date) {
return getDateString("MMMM", date);
}
/**
* @param date date to format
* @return date, with month, day, and year
*/
public static String getDateString(DateTime date) {
return getDateString("MMM", date);
}
private static String getDateString(String simpleDateFormat, DateTime date) {
boolean includeYear = date.getYear() != newDateTime().getYear();
String format = getFormat(Locale.getInstance(), simpleDateFormat, includeYear);
return date.toString(format);
}
private static String getFormat(Locale locale, String monthFormat, boolean includeYear) {
switch(locale.getLanguage()) {
case "ja":
return includeYear ? JA_YEAR : JA;
case "ko":
return includeYear ? KO_YEAR : KO;
case "zh":
return includeYear ? ZH_YEAR : ZH;
}
switch (locale.getCountry()) {
case "BZ":
case "CA":
case "KE":
case "MN":
case "US":
return includeYear
? monthFormat + " d ''yy"
: monthFormat + " d";
default:
return includeYear
? "d " + monthFormat + " ''yy"
: "d " + monthFormat;
}
}
/**
* @return weekday
*/
public static String getWeekday(DateTime date) {
return date.toString("EEEE");
}
/**
* @return weekday
*/
public static String getWeekdayShort(DateTime date) {
return date.toString("EEE");
}
public static String getLongDateStringWithTime(Context context, long timestamp) {
DateTime date = newDateTime(timestamp);
return getLongDateString(date) + ", " + getTimeString(context, date);
}
public static String getDateStringWithTime(Context context, long timestamp) {
DateTime date = newDateTime(timestamp);
return getDateString(date) + ", " + getTimeString(context, date);
}
public static String getRelativeDateStringWithTime(Context context, long timestamp) {
String string = DateUtilities.getRelativeDay(context, timestamp, false);
if (Task.hasDueTime(timestamp)) {
string = String.format("%s %s", string, //$NON-NLS-1$
DateUtilities.getTimeString(context, timestamp));
}
return string;
}
/**
* @return yesterday, today, tomorrow, or null
*/
public static String getRelativeDay(Context context, long date, boolean abbreviated) {
long today = getStartOfDay(currentTimeMillis());
long input = getStartOfDay(date);
if(today == input) {
return context.getString(R.string.today);
}
if(today + ONE_DAY == input) {
return context.getString(abbreviated ? R.string.tmrw : R.string.tomorrow);
}
if(today == input + ONE_DAY) {
return context.getString(abbreviated ? R.string.yest : R.string.yesterday);
}
if(today + abbreviationLimit >= input && today - abbreviationLimit <= input) {
return abbreviated ? DateUtilities.getWeekdayShort(newDateTime(date)) : DateUtilities.getWeekday(newDateTime(date));
}
return getDateString(newDateTime(date));
}
public static long getStartOfDay(long time) {
return newDateTime(time).startOfDay().getMillis();
}
/**
* Returns unixtime one month from now
*/
public static long oneMonthFromNow() {
return addCalendarMonthsToUnixtime(currentTimeMillis(), 1);
}
public static boolean is24HourFormat(Context context) {
if (is24HourOverride != null) {
return is24HourOverride;
}
return DateFormat.is24HourFormat(context);
}
public static String getTimeString(Context context, long timestamp) {
return getTimeString(context, newDateTime(timestamp));
}
public static String getTimeString(Context context, DateTime date) {
String value;
if (is24HourFormat(context)) {
value = "HH:mm";
} else if (date.getMinuteOfHour() == 0) {
value = "h a";
} else {
value = "h:mm a";
}
return date.toString(value);
}
public static String getLongDateString(DateTime date) {
return getDateString("MMMM", date);
}
/**
* @param date date to format
* @return date, with month, day, and year
*/
public static String getDateString(DateTime date) {
return getDateString("MMM", date);
}
private static String getDateString(String simpleDateFormat, DateTime date) {
boolean includeYear = date.getYear() != newDateTime().getYear();
String format = getFormat(Locale.getInstance(), simpleDateFormat, includeYear);
return date.toString(format);
}
private static String getFormat(Locale locale, String monthFormat, boolean includeYear) {
switch (locale.getLanguage()) {
case "ja":
return includeYear ? JA_YEAR : JA;
case "ko":
return includeYear ? KO_YEAR : KO;
case "zh":
return includeYear ? ZH_YEAR : ZH;
}
switch (locale.getCountry()) {
case "BZ":
case "CA":
case "KE":
case "MN":
case "US":
return includeYear
? monthFormat + " d ''yy"
: monthFormat + " d";
default:
return includeYear
? "d " + monthFormat + " ''yy"
: "d " + monthFormat;
}
}
/**
* @return weekday
*/
public static String getWeekday(DateTime date) {
return date.toString("EEEE");
}
/**
* @return weekday
*/
public static String getWeekdayShort(DateTime date) {
return date.toString("EEE");
}
public static String getLongDateStringWithTime(Context context, long timestamp) {
DateTime date = newDateTime(timestamp);
return getLongDateString(date) + ", " + getTimeString(context, date);
}
public static String getDateStringWithTime(Context context, long timestamp) {
DateTime date = newDateTime(timestamp);
return getDateString(date) + ", " + getTimeString(context, date);
}
public static String getRelativeDateStringWithTime(Context context, long timestamp) {
String string = DateUtilities.getRelativeDay(context, timestamp, false);
if (Task.hasDueTime(timestamp)) {
string = String.format("%s %s", string, //$NON-NLS-1$
DateUtilities.getTimeString(context, timestamp));
}
return string;
}
/**
* @return yesterday, today, tomorrow, or null
*/
public static String getRelativeDay(Context context, long date, boolean abbreviated) {
long today = getStartOfDay(currentTimeMillis());
long input = getStartOfDay(date);
if (today == input) {
return context.getString(R.string.today);
}
if (today + ONE_DAY == input) {
return context.getString(abbreviated ? R.string.tmrw : R.string.tomorrow);
}
if (today == input + ONE_DAY) {
return context.getString(abbreviated ? R.string.yest : R.string.yesterday);
}
if (today + abbreviationLimit >= input && today - abbreviationLimit <= input) {
return abbreviated ? DateUtilities.getWeekdayShort(newDateTime(date))
: DateUtilities.getWeekday(newDateTime(date));
}
return getDateString(newDateTime(date));
}
public static long getStartOfDay(long time) {
return newDateTime(time).startOfDay().getMillis();
}
}

@ -3,30 +3,30 @@
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.andlib.utility;
import android.app.Activity;
import android.app.Dialog;
import timber.log.Timber;
public class DialogUtilities {
/**
* Dismiss a dialog off the UI thread
*/
@Deprecated
public static void dismissDialog(Activity activity, final Dialog dialog) {
if(dialog == null) {
return;
}
activity.runOnUiThread(() -> {
try {
dialog.dismiss();
} catch(Exception e) {
Timber.e(e, e.getMessage());
}
});
/**
* Dismiss a dialog off the UI thread
*/
@Deprecated
public static void dismissDialog(Activity activity, final Dialog dialog) {
if (dialog == null) {
return;
}
activity.runOnUiThread(() -> {
try {
dialog.dismiss();
} catch (Exception e) {
Timber.e(e, e.getMessage());
}
});
}
}

@ -3,8 +3,11 @@
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.activity;
import static java.util.Arrays.asList;
import android.content.Context;
import android.os.Bundle;
import android.support.v4.content.ContextCompat;
@ -12,9 +15,13 @@ import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.view.MenuItem;
import butterknife.BindView;
import butterknife.ButterKnife;
import com.google.common.base.Strings;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.inject.Inject;
import org.tasks.R;
import org.tasks.injection.ActivityComponent;
import org.tasks.injection.ThemedInjectingAppCompatActivity;
@ -22,119 +29,106 @@ import org.tasks.preferences.Preferences;
import org.tasks.preferences.beast.BeastModeRecyclerAdapter;
import org.tasks.ui.MenuColorizer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.inject.Inject;
import butterknife.BindView;
import butterknife.ButterKnife;
import static java.util.Arrays.asList;
public class BeastModePreferences extends ThemedInjectingAppCompatActivity implements Toolbar.OnMenuItemClickListener {
@BindView(R.id.toolbar) Toolbar toolbar;
@BindView(R.id.recycler_view) RecyclerView recyclerView;
private static final String BEAST_MODE_ORDER_PREF = "beast_mode_order_v3"; //$NON-NLS-1$
private static final String BEAST_MODE_PREF_ITEM_SEPARATOR = ";";
private BeastModeRecyclerAdapter adapter;
public class BeastModePreferences extends ThemedInjectingAppCompatActivity implements
Toolbar.OnMenuItemClickListener {
@Inject Preferences preferences;
private static final String BEAST_MODE_ORDER_PREF = "beast_mode_order_v3"; //$NON-NLS-1$
private static final String BEAST_MODE_PREF_ITEM_SEPARATOR = ";";
@BindView(R.id.toolbar) Toolbar toolbar;
@BindView(R.id.recycler_view) RecyclerView recyclerView;
@Inject Preferences preferences;
private BeastModeRecyclerAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.beast_mode_pref_activity);
ButterKnife.bind(this);
toolbar.setNavigationIcon(ContextCompat.getDrawable(this, R.drawable.ic_arrow_back_24dp));
toolbar.setNavigationOnClickListener(v -> finish());
toolbar.inflateMenu(R.menu.beast_mode);
toolbar.setOnMenuItemClickListener(this);
MenuColorizer.colorToolbar(this, toolbar);
adapter = new BeastModeRecyclerAdapter(this, constructOrderedControlList(preferences, this));
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
adapter.applyToRecyclerView(recyclerView);
public static void setDefaultOrder(Preferences preferences, Context context) {
if (preferences.getStringValue(BEAST_MODE_ORDER_PREF) != null) {
return;
}
@Override
public void inject(ActivityComponent component) {
component.inject(this);
ArrayList<String> list = constructOrderedControlList(preferences, context);
StringBuilder newSetting = new StringBuilder();
for (String item : list) {
newSetting.append(item);
newSetting.append(BEAST_MODE_PREF_ITEM_SEPARATOR);
}
@Override
public boolean onMenuItemClick(MenuItem item) {
switch(item.getItemId()) {
case R.id.menu_reset_to_defaults:
String[] prefsArray = getResources().getStringArray(R.array.TEA_control_sets_prefs);
adapter.setItems(asList(prefsArray));
return true;
default:
return super.onOptionsItemSelected(item);
}
preferences.setString(BEAST_MODE_ORDER_PREF, newSetting.toString());
}
public static ArrayList<String> constructOrderedControlList(Preferences preferences,
Context context) {
String order = preferences.getStringValue(BEAST_MODE_ORDER_PREF);
ArrayList<String> list = new ArrayList<>();
String[] itemsArray;
if (order == null) {
itemsArray = context.getResources().getStringArray(R.array.TEA_control_sets_prefs);
} else {
itemsArray = order.split(BEAST_MODE_PREF_ITEM_SEPARATOR);
}
@Override
public void finish() {
List<String> items = adapter.getItems();
StringBuilder newSetting = new StringBuilder();
for (int i = 0; i < items.size(); i++) {
newSetting.append(items.get(i));
newSetting.append(BEAST_MODE_PREF_ITEM_SEPARATOR);
}
String oldValue = preferences.getStringValue(BEAST_MODE_ORDER_PREF);
String newValue = newSetting.toString();
if (Strings.isNullOrEmpty(oldValue) || !oldValue.equals(newValue)) {
preferences.setString(BEAST_MODE_ORDER_PREF, newSetting.toString());
setResult(RESULT_OK);
}
super.finish();
}
Collections.addAll(list, itemsArray);
public static void setDefaultOrder(Preferences preferences, Context context) {
if (preferences.getStringValue(BEAST_MODE_ORDER_PREF) != null) {
return;
}
ArrayList<String> list = constructOrderedControlList(preferences, context);
StringBuilder newSetting = new StringBuilder();
for (String item : list) {
newSetting.append(item);
newSetting.append(BEAST_MODE_PREF_ITEM_SEPARATOR);
}
preferences.setString(BEAST_MODE_ORDER_PREF, newSetting.toString());
if (order == null) {
return list;
}
public static ArrayList<String> constructOrderedControlList(Preferences preferences, Context context) {
String order = preferences.getStringValue(BEAST_MODE_ORDER_PREF);
ArrayList<String> list = new ArrayList<>();
String[] itemsArray;
if (order == null) {
itemsArray = context.getResources().getStringArray(R.array.TEA_control_sets_prefs);
} else {
itemsArray = order.split(BEAST_MODE_PREF_ITEM_SEPARATOR);
}
Collections.addAll(list, itemsArray);
if (order == null) {
return list;
}
itemsArray = context.getResources().getStringArray(R.array.TEA_control_sets_prefs);
for (int i = 0; i < itemsArray.length; i++) {
if (!list.contains(itemsArray[i])) {
list.add(i, itemsArray[i]);
}
}
return list;
itemsArray = context.getResources().getStringArray(R.array.TEA_control_sets_prefs);
for (int i = 0; i < itemsArray.length; i++) {
if (!list.contains(itemsArray[i])) {
list.add(i, itemsArray[i]);
}
}
return list;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.beast_mode_pref_activity);
ButterKnife.bind(this);
toolbar.setNavigationIcon(ContextCompat.getDrawable(this, R.drawable.ic_arrow_back_24dp));
toolbar.setNavigationOnClickListener(v -> finish());
toolbar.inflateMenu(R.menu.beast_mode);
toolbar.setOnMenuItemClickListener(this);
MenuColorizer.colorToolbar(this, toolbar);
adapter = new BeastModeRecyclerAdapter(this, constructOrderedControlList(preferences, this));
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
adapter.applyToRecyclerView(recyclerView);
}
@Override
public void inject(ActivityComponent component) {
component.inject(this);
}
@Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_reset_to_defaults:
String[] prefsArray = getResources().getStringArray(R.array.TEA_control_sets_prefs);
adapter.setItems(asList(prefsArray));
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
public void finish() {
List<String> items = adapter.getItems();
StringBuilder newSetting = new StringBuilder();
for (int i = 0; i < items.size(); i++) {
newSetting.append(items.get(i));
newSetting.append(BEAST_MODE_PREF_ITEM_SEPARATOR);
}
String oldValue = preferences.getStringValue(BEAST_MODE_ORDER_PREF);
String newValue = newSetting.toString();
if (Strings.isNullOrEmpty(oldValue) || !oldValue.equals(newValue)) {
preferences.setString(BEAST_MODE_ORDER_PREF, newSetting.toString());
setResult(RESULT_OK);
}
super.finish();
}
}

@ -1,65 +1,63 @@
/**
* TODO: make this lightweight, don't extend the entire TaskListActivity
*/
package com.todoroo.astrid.activity;
import static org.tasks.intents.TaskIntents.getEditTaskStack;
import android.content.Intent;
import android.os.Bundle;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.service.TaskCreator;
import javax.inject.Inject;
import org.tasks.injection.ActivityComponent;
import org.tasks.injection.InjectingAppCompatActivity;
import javax.inject.Inject;
import static org.tasks.intents.TaskIntents.getEditTaskStack;
/**
* @author joshuagross
*
* Create a new task based on incoming links from the "share" menu
* Create a new task based on incoming links from the "share" menu
*/
public final class ShareLinkActivity extends InjectingAppCompatActivity {
@Inject TaskCreator taskCreator;
@Inject TaskDao taskDao;
@Inject TaskCreator taskCreator;
@Inject TaskDao taskDao;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
readIntent();
}
readIntent();
}
@Override
public void inject(ActivityComponent component) {
component.inject(this);
}
@Override
public void inject(ActivityComponent component) {
component.inject(this);
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
setIntent(intent);
readIntent();
}
readIntent();
}
private void readIntent() {
Intent intent = getIntent();
String subject = intent.getStringExtra(Intent.EXTRA_SUBJECT);
if (subject == null) {
subject = "";
}
private void readIntent() {
Intent intent = getIntent();
String subject = intent.getStringExtra(Intent.EXTRA_SUBJECT);
if (subject == null) {
subject = "";
}
Task task = taskCreator.createWithValues(null, subject);
if (task != null) {
task.setNotes(intent.getStringExtra(Intent.EXTRA_TEXT));
getEditTaskStack(this, null, task).startActivities();
}
finish();
Task task = taskCreator.createWithValues(null, subject);
if (task != null) {
task.setNotes(intent.getStringExtra(Intent.EXTRA_TEXT));
getEditTaskStack(this, null, task).startActivities();
}
finish();
}
}

@ -6,18 +6,18 @@ import android.os.Bundle;
public class TaskEditActivity extends Activity {
private static final String TOKEN_ID = "id";
private static final String TOKEN_ID = "id";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final long taskId = getIntent().getLongExtra(TOKEN_ID, 0);
final long taskId = getIntent().getLongExtra(TOKEN_ID, 0);
Intent intent = new Intent(this, TaskListActivity.class);
intent.putExtra(TaskListActivity.OPEN_TASK, taskId);
startActivity(intent);
Intent intent = new Intent(this, TaskListActivity.class);
intent.putExtra(TaskListActivity.OPEN_TASK, taskId);
startActivity(intent);
finish();
}
finish();
}
}

@ -3,8 +3,11 @@
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.activity;
import static org.tasks.date.DateTimeUtils.newDateTime;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
@ -18,7 +21,8 @@ import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import butterknife.BindView;
import butterknife.ButterKnife;
import com.todoroo.andlib.utility.AndroidUtilities;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.api.Filter;
@ -31,7 +35,8 @@ import com.todoroo.astrid.service.TaskDeleter;
import com.todoroo.astrid.timers.TimerPlugin;
import com.todoroo.astrid.ui.EditTitleControlSet;
import com.todoroo.astrid.utility.Flags;
import java.util.List;
import javax.inject.Inject;
import org.tasks.LocalBroadcastManager;
import org.tasks.R;
import org.tasks.analytics.Tracker;
@ -48,146 +53,164 @@ import org.tasks.ui.MenuColorizer;
import org.tasks.ui.RemoteListFragment;
import org.tasks.ui.TaskEditControlFragment;
import java.util.List;
import javax.inject.Inject;
import butterknife.BindView;
import butterknife.ButterKnife;
import static org.tasks.date.DateTimeUtils.newDateTime;
public final class TaskEditFragment extends InjectingFragment implements Toolbar.OnMenuItemClickListener {
public interface TaskEditFragmentCallbackHandler {
void taskEditFinished();
public final class TaskEditFragment extends InjectingFragment implements
Toolbar.OnMenuItemClickListener {
public static final String TAG_TASKEDIT_FRAGMENT = "taskedit_fragment";
private static final String EXTRA_TASK = "extra_task";
@Inject TaskDao taskDao;
@Inject UserActivityDao userActivityDao;
@Inject TaskDeleter taskDeleter;
@Inject NotificationManager notificationManager;
@Inject DialogBuilder dialogBuilder;
@Inject @ForActivity
Context context;
@Inject TaskEditControlSetFragmentManager taskEditControlSetFragmentManager;
@Inject CommentsController commentsController;
@Inject Preferences preferences;
@Inject Tracker tracker;
@Inject TimerPlugin timerPlugin;
@Inject LocalBroadcastManager localBroadcastManager;
@BindView(R.id.toolbar) Toolbar toolbar;
@BindView(R.id.comments) LinearLayout comments;
@BindView(R.id.control_sets) LinearLayout controlSets;
Task model = null;
private TaskEditFragmentCallbackHandler callback;
public static TaskEditFragment newTaskEditFragment(Task task) {
TaskEditFragment taskEditFragment = new TaskEditFragment();
Bundle arguments = new Bundle();
arguments.putParcelable(EXTRA_TASK, task);
taskEditFragment.setArguments(arguments);
return taskEditFragment;
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
callback = (TaskEditFragmentCallbackHandler) activity;
}
@Override
protected void inject(FragmentComponent component) {
component.inject(this);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_task_edit, container, false);
ButterKnife.bind(this, view);
Bundle arguments = getArguments();
model = arguments.getParcelable(EXTRA_TASK);
final boolean backButtonSavesTask = preferences.backButtonSavesTask();
toolbar.setNavigationIcon(ContextCompat.getDrawable(context,
backButtonSavesTask ? R.drawable.ic_close_24dp : R.drawable.ic_save_24dp));
toolbar.setNavigationOnClickListener(v -> {
if (backButtonSavesTask) {
discardButtonClick();
} else {
save();
}
});
toolbar.inflateMenu(R.menu.menu_task_edit_fragment);
toolbar.setOnMenuItemClickListener(this);
MenuColorizer.colorToolbar(context, toolbar);
if (!model.isNew()) {
notificationManager.cancel(model.getId());
}
public static TaskEditFragment newTaskEditFragment(Task task) {
TaskEditFragment taskEditFragment = new TaskEditFragment();
Bundle arguments = new Bundle();
arguments.putParcelable(EXTRA_TASK, task);
taskEditFragment.setArguments(arguments);
return taskEditFragment;
}
public static final String TAG_TASKEDIT_FRAGMENT = "taskedit_fragment";
private static final String EXTRA_TASK = "extra_task";
@Inject TaskDao taskDao;
@Inject UserActivityDao userActivityDao;
@Inject TaskDeleter taskDeleter;
@Inject NotificationManager notificationManager;
@Inject DialogBuilder dialogBuilder;
@Inject @ForActivity Context context;
@Inject TaskEditControlSetFragmentManager taskEditControlSetFragmentManager;
@Inject CommentsController commentsController;
@Inject Preferences preferences;
@Inject Tracker tracker;
@Inject TimerPlugin timerPlugin;
@Inject LocalBroadcastManager localBroadcastManager;
commentsController.initialize(model, comments);
commentsController.reloadView();
@BindView(R.id.toolbar) Toolbar toolbar;
@BindView(R.id.comments) LinearLayout comments;
@BindView(R.id.control_sets) LinearLayout controlSets;
FragmentManager fragmentManager = getChildFragmentManager();
List<TaskEditControlFragment> taskEditControlFragments = taskEditControlSetFragmentManager
.getOrCreateFragments(fragmentManager, model);
Task model = null;
private TaskEditFragmentCallbackHandler callback;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
callback = (TaskEditFragmentCallbackHandler) activity;
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
for (int i = 0; i < taskEditControlFragments.size(); i++) {
TaskEditControlFragment taskEditControlFragment = taskEditControlFragments.get(i);
String tag = getString(taskEditControlFragment.controlId());
fragmentTransaction
.replace(TaskEditControlSetFragmentManager.TASK_EDIT_CONTROL_FRAGMENT_ROWS[i],
taskEditControlFragment, tag);
}
fragmentTransaction.commit();
@Override
protected void inject(FragmentComponent component) {
component.inject(this);
for (int i = taskEditControlFragments.size() - 2; i > 1; i--) {
controlSets.addView(inflater.inflate(R.layout.task_edit_row_divider, controlSets, false), i);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_task_edit, container, false);
ButterKnife.bind(this, view);
Bundle arguments = getArguments();
model = arguments.getParcelable(EXTRA_TASK);
final boolean backButtonSavesTask = preferences.backButtonSavesTask();
toolbar.setNavigationIcon(ContextCompat.getDrawable(context,
backButtonSavesTask ? R.drawable.ic_close_24dp : R.drawable.ic_save_24dp));
toolbar.setNavigationOnClickListener(v -> {
if (backButtonSavesTask) {
discardButtonClick();
} else {
save();
}
});
toolbar.inflateMenu(R.menu.menu_task_edit_fragment);
toolbar.setOnMenuItemClickListener(this);
MenuColorizer.colorToolbar(context, toolbar);
if (!model.isNew()) {
notificationManager.cancel(model.getId());
}
return view;
}
commentsController.initialize(model, comments);
commentsController.reloadView();
FragmentManager fragmentManager = getChildFragmentManager();
List<TaskEditControlFragment> taskEditControlFragments = taskEditControlSetFragmentManager.getOrCreateFragments(fragmentManager, model);
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
for (int i = 0 ; i < taskEditControlFragments.size() ; i++) {
TaskEditControlFragment taskEditControlFragment = taskEditControlFragments.get(i);
String tag = getString(taskEditControlFragment.controlId());
fragmentTransaction.replace(TaskEditControlSetFragmentManager.TASK_EDIT_CONTROL_FRAGMENT_ROWS[i], taskEditControlFragment, tag);
}
fragmentTransaction.commit();
@Override
public boolean onMenuItemClick(MenuItem item) {
AndroidUtilities.hideKeyboard(getActivity());
for (int i = taskEditControlFragments.size() - 2; i > 1 ; i--) {
controlSets.addView(inflater.inflate(R.layout.task_edit_row_divider, controlSets, false), i);
}
return view;
}
@Override
public boolean onMenuItemClick(MenuItem item) {
AndroidUtilities.hideKeyboard(getActivity());
switch (item.getItemId()) {
case R.id.menu_delete:
deleteButtonClick();
return true;
}
return false;
switch (item.getItemId()) {
case R.id.menu_delete:
deleteButtonClick();
return true;
}
public Task stopTimer() {
timerPlugin.stopTimer(model);
String elapsedTime = DateUtils.formatElapsedTime(model.getElapsedSeconds());
addComment(String.format("%s %s\n%s %s", //$NON-NLS-1$
getString(R.string.TEA_timer_comment_stopped),
DateUtilities.getTimeString(getActivity(), newDateTime()),
getString(R.string.TEA_timer_comment_spent),
elapsedTime), null);
return model;
}
public Task startTimer() {
timerPlugin.startTimer(model);
addComment(String.format("%s %s",
getString(R.string.TEA_timer_comment_started),
DateUtilities.getTimeString(getActivity(), newDateTime())),
null);
return model;
return false;
}
public Task stopTimer() {
timerPlugin.stopTimer(model);
String elapsedTime = DateUtils.formatElapsedTime(model.getElapsedSeconds());
addComment(String.format("%s %s\n%s %s", //$NON-NLS-1$
getString(R.string.TEA_timer_comment_stopped),
DateUtilities.getTimeString(getActivity(), newDateTime()),
getString(R.string.TEA_timer_comment_spent),
elapsedTime), null);
return model;
}
public Task startTimer() {
timerPlugin.startTimer(model);
addComment(String.format("%s %s",
getString(R.string.TEA_timer_comment_started),
DateUtilities.getTimeString(getActivity(), newDateTime())),
null);
return model;
}
/**
* Save task model from values in UI components
*/
public void save() {
List<TaskEditControlFragment> fragments = taskEditControlSetFragmentManager
.getFragmentsInPersistOrder(getChildFragmentManager());
if (hasChanges(fragments)) {
boolean isNewTask = model.isNew();
if (isNewTask) {
taskDao.createNew(model);
}
for (TaskEditControlFragment fragment : fragments) {
fragment.apply(model);
}
taskDao.save(model, null);
if (Flags.checkAndClear(Flags.TAGS_CHANGED)) {
localBroadcastManager.broadcastRefreshList();
}
if (isNewTask) {
((TaskListActivity) getActivity())
.getTaskListFragment()
.onTaskCreated(model.getUuid());
}
callback.taskEditFinished();
} else {
discard();
}
}
/*
* ======================================================================
@ -195,54 +218,39 @@ public final class TaskEditFragment extends InjectingFragment implements Toolbar
* ======================================================================
*/
/** Save task model from values in UI components */
public void save() {
List<TaskEditControlFragment> fragments = taskEditControlSetFragmentManager.getFragmentsInPersistOrder(getChildFragmentManager());
if (hasChanges(fragments)) {
boolean isNewTask = model.isNew();
if (isNewTask) {
taskDao.createNew(model);
}
for (TaskEditControlFragment fragment : fragments) {
fragment.apply(model);
}
taskDao.save(model, null);
if (Flags.checkAndClear(Flags.TAGS_CHANGED)) {
localBroadcastManager.broadcastRefreshList();
}
if (isNewTask) {
((TaskListActivity) getActivity())
.getTaskListFragment()
.onTaskCreated(model.getUuid());
}
callback.taskEditFinished();
} else {
discard();
}
}
private EditTitleControlSet getEditTitleControlSet() {
return getFragment(EditTitleControlSet.TAG);
}
private EditTitleControlSet getEditTitleControlSet() {
return getFragment(EditTitleControlSet.TAG);
}
private RemoteListFragment getRemoteListFragment() {
return getFragment(RemoteListFragment.TAG);
}
private RemoteListFragment getRemoteListFragment() {
return getFragment(RemoteListFragment.TAG);
}
private RepeatControlSet getRepeatControlSet() {
return getFragment(RepeatControlSet.TAG);
}
private RepeatControlSet getRepeatControlSet() {
return getFragment(RepeatControlSet.TAG);
}
private FilesControlSet getFilesControlSet() {
return getFragment(FilesControlSet.TAG);
}
private FilesControlSet getFilesControlSet() {
return getFragment(FilesControlSet.TAG);
}
@SuppressWarnings("unchecked")
private <T extends TaskEditControlFragment> T getFragment(int tag) {
return (T) getChildFragmentManager().findFragmentByTag(getString(tag));
}
@SuppressWarnings("unchecked")
private <T extends TaskEditControlFragment> T getFragment(int tag) {
return (T) getChildFragmentManager().findFragmentByTag(getString(tag));
private boolean hasChanges(List<TaskEditControlFragment> fragments) {
try {
for (TaskEditControlFragment fragment : fragments) {
if (fragment.hasChanges(model)) {
return true;
}
}
} catch (Exception e) {
tracker.reportException(e);
}
return false;
}
/*
* ======================================================================
@ -250,48 +258,40 @@ public final class TaskEditFragment extends InjectingFragment implements Toolbar
* ======================================================================
*/
private boolean hasChanges(List<TaskEditControlFragment> fragments) {
try {
for (TaskEditControlFragment fragment : fragments) {
if (fragment.hasChanges(model)) {
return true;
}
}
} catch(Exception e) {
tracker.reportException(e);
}
return false;
public void discardButtonClick() {
if (hasChanges(
taskEditControlSetFragmentManager.getFragmentsInPersistOrder(getChildFragmentManager()))) {
dialogBuilder.newMessageDialog(R.string.discard_confirmation)
.setPositiveButton(R.string.keep_editing, null)
.setNegativeButton(R.string.discard, (dialog, which) -> discard())
.show();
} else {
discard();
}
}
public void discardButtonClick() {
if (hasChanges(taskEditControlSetFragmentManager.getFragmentsInPersistOrder(getChildFragmentManager()))) {
dialogBuilder.newMessageDialog(R.string.discard_confirmation)
.setPositiveButton(R.string.keep_editing, null)
.setNegativeButton(R.string.discard, (dialog, which) -> discard())
.show();
} else {
discard();
}
public void discard() {
if (model != null && model.isNew()) {
timerPlugin.stopTimer(model);
}
public void discard() {
if (model != null && model.isNew()) {
timerPlugin.stopTimer(model);
}
callback.taskEditFinished();
}
callback.taskEditFinished();
}
private void deleteButtonClick() {
dialogBuilder.newMessageDialog(R.string.DLG_delete_this_task_question)
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
timerPlugin.stopTimer(model);
taskDeleter.markDeleted(model);
callback.taskEditFinished();
})
.setNegativeButton(android.R.string.cancel, null)
.show();
}
private void deleteButtonClick() {
dialogBuilder.newMessageDialog(R.string.DLG_delete_this_task_question)
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
timerPlugin.stopTimer(model);
taskDeleter.markDeleted(model);
callback.taskEditFinished();
})
.setNegativeButton(android.R.string.cancel, null)
.show();
}
public void onPriorityChange(int priority) {
getEditTitleControlSet().setPriority(priority);
}
/*
* ======================================================================
@ -299,34 +299,35 @@ public final class TaskEditFragment extends InjectingFragment implements Toolbar
* ======================================================================
*/
public void onPriorityChange(int priority) {
getEditTitleControlSet().setPriority(priority);
}
public void onRepeatChanged(boolean repeat) {
getEditTitleControlSet().repeatChanged(repeat);
}
public void onRepeatChanged(boolean repeat) {
getEditTitleControlSet().repeatChanged(repeat);
}
public void onRemoteListChanged(Filter list) {
getRemoteListFragment().setList(list);
}
public void onRemoteListChanged(Filter list) {
getRemoteListFragment().setList(list);
public void onDueDateChanged(long dueDate) {
RepeatControlSet repeatControlSet = getRepeatControlSet();
if (repeatControlSet != null) {
repeatControlSet.onDueDateChanged(dueDate);
}
public void onDueDateChanged(long dueDate) {
RepeatControlSet repeatControlSet = getRepeatControlSet();
if (repeatControlSet != null) {
repeatControlSet.onDueDateChanged(dueDate);
}
}
public void addComment(String message, String picture) {
UserActivity userActivity = new UserActivity();
userActivity.setMessage(message);
userActivity.setTargetId(model.getUuid());
userActivity.setCreated(DateUtilities.now());
if (picture != null) {
userActivity.setPicture(picture);
}
userActivityDao.createNew(userActivity);
commentsController.reloadView();
}
public void addComment(String message, String picture) {
UserActivity userActivity = new UserActivity();
userActivity.setMessage(message);
userActivity.setTargetId(model.getUuid());
userActivity.setCreated(DateUtilities.now());
if (picture != null) {
userActivity.setPicture(picture);
}
userActivityDao.createNew(userActivity);
commentsController.reloadView();
}
public interface TaskEditFragmentCallbackHandler {
void taskEditFinished();
}
}

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

Loading…
Cancel
Save