Merge branch 'feature/refactoring-mvc' into dev
@@ -1,4 +1,6 @@
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'com.neenbedankt.android-apt'
|
||||
apply plugin: 'me.tatarka.retrolambda'
|
||||
|
||||
android {
|
||||
compileSdkVersion 23
|
||||
@@ -13,7 +15,7 @@ android {
|
||||
buildConfigField "String", "databaseFilename", "\"uhabits.db\""
|
||||
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
//testInstrumentationRunnerArgument "size", "small"
|
||||
testInstrumentationRunnerArgument "size", "medium"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
@@ -29,6 +31,22 @@ android {
|
||||
lintOptions {
|
||||
checkReleaseBuilds false
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
targetCompatibility 1.8
|
||||
sourceCompatibility 1.8
|
||||
}
|
||||
|
||||
testOptions {
|
||||
unitTests.all {
|
||||
testLogging {
|
||||
events "passed", "skipped", "failed", "standardOut", "standardError"
|
||||
outputs.upToDateWhen { false }
|
||||
showStandardStreams = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@@ -40,12 +58,29 @@ dependencies {
|
||||
compile 'org.apmem.tools:layouts:1.10@aar'
|
||||
compile 'com.opencsv:opencsv:3.7'
|
||||
compile 'com.michaelpardo:activeandroid:3.1.0-SNAPSHOT'
|
||||
compile 'org.jetbrains:annotations-java5:15.0'
|
||||
|
||||
compile 'com.jakewharton:butterknife:8.0.1'
|
||||
apt 'com.jakewharton:butterknife-compiler:8.0.1'
|
||||
|
||||
compile 'com.google.dagger:dagger:2.2'
|
||||
apt 'com.google.dagger:dagger-compiler:2.2'
|
||||
testApt 'com.google.dagger:dagger-compiler:2.2'
|
||||
androidTestApt 'com.google.dagger:dagger-compiler:2.2'
|
||||
provided 'javax.annotation:jsr250-api:1.0'
|
||||
|
||||
compile project(':libs:drag-sort-listview:library')
|
||||
|
||||
testCompile 'junit:junit:4.12'
|
||||
testCompile 'org.hamcrest:hamcrest-library:1.3'
|
||||
testCompile 'org.mockito:mockito-core:1.10.19'
|
||||
|
||||
androidTestCompile 'com.android.support:support-annotations:23.3.0'
|
||||
androidTestCompile 'com.android.support.test:runner:0.5'
|
||||
androidTestCompile 'com.android.support.test:rules:0.5'
|
||||
androidTestCompile 'org.mockito:mockito-core:1.10.19'
|
||||
androidTestCompile "com.google.dexmaker:dexmaker:1.2"
|
||||
androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.2'
|
||||
|
||||
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.1') {
|
||||
exclude group: 'com.android.support'
|
||||
@@ -60,13 +95,6 @@ dependencies {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
task grantAnimationPermission(type: Exec, dependsOn: 'installDebug') {
|
||||
commandLine "adb shell pm grant org.isoron.uhabits android.permission.SET_ANIMATION_SCALE".split(' ')
|
||||
}
|
||||
|
||||
tasks.whenTaskAdded { task ->
|
||||
if (task.name.startsWith('connected')) {
|
||||
task.dependsOn grantAnimationPermission
|
||||
}
|
||||
retrolambda {
|
||||
defaultMethods true
|
||||
}
|
||||
|
Before Width: | Height: | Size: 8.6 KiB |
|
Before Width: | Height: | Size: 8.5 KiB |
|
Before Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 8.6 KiB |
|
Before Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 8.3 KiB After Width: | Height: | Size: 8.3 KiB |
|
Before Width: | Height: | Size: 6.3 KiB After Width: | Height: | Size: 6.3 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 8.3 KiB After Width: | Height: | Size: 8.3 KiB |
|
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 8.2 KiB After Width: | Height: | Size: 8.2 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 551 B |
|
After Width: | Height: | Size: 505 B |
|
After Width: | Height: | Size: 559 B |
|
After Width: | Height: | Size: 1.4 KiB |
BIN
app/src/androidTest/assets/views/habits/list/HintView/render.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 7.9 KiB |
|
After Width: | Height: | Size: 8.5 KiB |
|
After Width: | Height: | Size: 9.4 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 9.6 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 36 KiB |
BIN
app/src/androidTest/assets/views/widgets/ScoreWidget/render.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
app/src/androidTest/assets/views/widgets/StreakWidget/render.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import dagger.Component;
|
||||
|
||||
@Singleton
|
||||
@Component(modules = {AndroidModule.class})
|
||||
public interface AndroidTestComponent extends BaseComponent
|
||||
{
|
||||
void inject(BaseAndroidTest baseAndroidTest);
|
||||
}
|
||||
|
||||
135
app/src/androidTest/java/org/isoron/uhabits/BaseAndroidTest.java
Normal file
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits;
|
||||
|
||||
import android.appwidget.*;
|
||||
import android.content.*;
|
||||
import android.os.*;
|
||||
import android.support.annotation.*;
|
||||
import android.support.test.*;
|
||||
|
||||
import org.isoron.uhabits.commands.*;
|
||||
import org.isoron.uhabits.models.*;
|
||||
import org.isoron.uhabits.tasks.*;
|
||||
import org.isoron.uhabits.utils.*;
|
||||
import org.junit.*;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
import javax.inject.*;
|
||||
|
||||
import static junit.framework.Assert.*;
|
||||
import static org.hamcrest.MatcherAssert.*;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
public class BaseAndroidTest
|
||||
{
|
||||
// 8:00am, January 25th, 2015 (UTC)
|
||||
public static final long FIXED_LOCAL_TIME = 1422172800000L;
|
||||
|
||||
private static boolean isLooperPrepared;
|
||||
|
||||
protected Context testContext;
|
||||
|
||||
protected Context targetContext;
|
||||
|
||||
@Inject
|
||||
protected Preferences prefs;
|
||||
|
||||
@Inject
|
||||
protected HabitList habitList;
|
||||
|
||||
@Inject
|
||||
protected CommandRunner commandRunner;
|
||||
|
||||
protected AndroidTestComponent androidTestComponent;
|
||||
|
||||
protected HabitFixtures fixtures;
|
||||
|
||||
protected CountDownLatch latch;
|
||||
|
||||
@Before
|
||||
public void setUp()
|
||||
{
|
||||
if (!isLooperPrepared)
|
||||
{
|
||||
Looper.prepare();
|
||||
isLooperPrepared = true;
|
||||
}
|
||||
|
||||
targetContext = InstrumentationRegistry.getTargetContext();
|
||||
testContext = InstrumentationRegistry.getContext();
|
||||
|
||||
InterfaceUtils.setFixedTheme(R.style.AppBaseTheme);
|
||||
DateUtils.setFixedLocalTime(FIXED_LOCAL_TIME);
|
||||
|
||||
androidTestComponent = DaggerAndroidTestComponent.builder().build();
|
||||
HabitsApplication.setComponent(androidTestComponent);
|
||||
androidTestComponent.inject(this);
|
||||
|
||||
fixtures = new HabitFixtures(habitList);
|
||||
|
||||
latch = new CountDownLatch(1);
|
||||
}
|
||||
|
||||
protected void assertWidgetProviderIsInstalled(Class componentClass)
|
||||
{
|
||||
ComponentName provider =
|
||||
new ComponentName(targetContext, componentClass);
|
||||
AppWidgetManager manager = AppWidgetManager.getInstance(targetContext);
|
||||
|
||||
List<ComponentName> installedProviders = new LinkedList<>();
|
||||
for (AppWidgetProviderInfo info : manager.getInstalledProviders())
|
||||
installedProviders.add(info.provider);
|
||||
|
||||
assertThat(installedProviders, hasItems(provider));
|
||||
}
|
||||
|
||||
protected void setTheme(@StyleRes int themeId)
|
||||
{
|
||||
InterfaceUtils.setFixedTheme(themeId);
|
||||
targetContext.setTheme(themeId);
|
||||
}
|
||||
|
||||
protected void sleep(int time)
|
||||
{
|
||||
try
|
||||
{
|
||||
Thread.sleep(time);
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
fail();
|
||||
}
|
||||
}
|
||||
|
||||
protected void waitForAsyncTasks()
|
||||
throws InterruptedException, TimeoutException
|
||||
{
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN)
|
||||
{
|
||||
Thread.sleep(1000);
|
||||
return;
|
||||
}
|
||||
|
||||
BaseTask.waitForTasks(10000);
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.os.Looper;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
|
||||
import org.isoron.uhabits.helpers.DateHelper;
|
||||
import org.isoron.uhabits.helpers.UIHelper;
|
||||
import org.isoron.uhabits.tasks.BaseTask;
|
||||
import org.junit.Before;
|
||||
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
public class BaseTest
|
||||
{
|
||||
protected Context testContext;
|
||||
protected Context targetContext;
|
||||
private static boolean isLooperPrepared;
|
||||
|
||||
public static final long FIXED_LOCAL_TIME = 1422172800000L; // 8:00am, January 25th, 2015 (UTC)
|
||||
|
||||
@Before
|
||||
public void setup()
|
||||
{
|
||||
if(!isLooperPrepared)
|
||||
{
|
||||
Looper.prepare();
|
||||
isLooperPrepared = true;
|
||||
}
|
||||
|
||||
targetContext = InstrumentationRegistry.getTargetContext();
|
||||
testContext = InstrumentationRegistry.getContext();
|
||||
|
||||
UIHelper.setFixedTheme(R.style.AppBaseTheme);
|
||||
DateHelper.setFixedLocalTime(FIXED_LOCAL_TIME);
|
||||
}
|
||||
|
||||
protected void waitForAsyncTasks() throws InterruptedException, TimeoutException
|
||||
{
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN)
|
||||
{
|
||||
Thread.sleep(1000);
|
||||
return;
|
||||
}
|
||||
|
||||
BaseTask.waitForTasks(10000);
|
||||
}
|
||||
}
|
||||
@@ -17,47 +17,46 @@
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.unit.views;
|
||||
package org.isoron.uhabits;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.os.SystemClock;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.graphics.*;
|
||||
import android.os.*;
|
||||
import android.support.annotation.*;
|
||||
import android.view.*;
|
||||
import android.widget.*;
|
||||
|
||||
import org.isoron.uhabits.BaseTest;
|
||||
import org.isoron.uhabits.helpers.DatabaseHelper;
|
||||
import org.isoron.uhabits.helpers.UIHelper;
|
||||
import org.isoron.uhabits.tasks.BaseTask;
|
||||
import org.isoron.uhabits.views.HabitDataView;
|
||||
import org.isoron.uhabits.ui.widgets.*;
|
||||
import org.isoron.uhabits.utils.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.*;
|
||||
|
||||
import static junit.framework.Assert.fail;
|
||||
import static android.view.View.MeasureSpec.*;
|
||||
import static junit.framework.Assert.*;
|
||||
|
||||
public class ViewTest extends BaseTest
|
||||
public class BaseViewTest extends BaseAndroidTest
|
||||
{
|
||||
protected static final double SIMILARITY_CUTOFF = 0.09;
|
||||
protected static final double DEFAULT_SIMILARITY_CUTOFF = 0.09;
|
||||
|
||||
public static final int HISTOGRAM_BIN_SIZE = 8;
|
||||
|
||||
protected void measureView(int width, int height, View view)
|
||||
{
|
||||
int specWidth = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY);
|
||||
int specHeight = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY);
|
||||
private double similarityCutoff;
|
||||
|
||||
view.measure(specWidth, specHeight);
|
||||
view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
|
||||
@Override
|
||||
public void setUp()
|
||||
{
|
||||
super.setUp();
|
||||
similarityCutoff = DEFAULT_SIMILARITY_CUTOFF;
|
||||
}
|
||||
|
||||
protected void assertRenders(View view, String expectedImagePath) throws IOException
|
||||
protected void assertRenders(View view, String expectedImagePath)
|
||||
throws IOException
|
||||
{
|
||||
StringBuilder errorMessage = new StringBuilder();
|
||||
expectedImagePath = getVersionedViewAssetPath(expectedImagePath);
|
||||
|
||||
if (view.isLayoutRequested()) measureView(view, view.getMeasuredWidth(),
|
||||
view.getMeasuredHeight());
|
||||
|
||||
view.setDrawingCacheEnabled(true);
|
||||
view.buildDrawingCache();
|
||||
Bitmap actual = view.getDrawingCache();
|
||||
@@ -65,12 +64,14 @@ public class ViewTest extends BaseTest
|
||||
|
||||
int width = actual.getWidth();
|
||||
int height = actual.getHeight();
|
||||
Bitmap scaledExpected = Bitmap.createScaledBitmap(expected, width, height, true);
|
||||
Bitmap scaledExpected =
|
||||
Bitmap.createScaledBitmap(expected, width, height, true);
|
||||
|
||||
double distance;
|
||||
boolean similarEnough = true;
|
||||
|
||||
if ((distance = compareHistograms(getHistogram(actual), getHistogram(scaledExpected))) > SIMILARITY_CUTOFF)
|
||||
if ((distance = compareHistograms(getHistogram(actual),
|
||||
getHistogram(scaledExpected))) > similarityCutoff)
|
||||
{
|
||||
similarEnough = false;
|
||||
errorMessage.append(String.format(
|
||||
@@ -82,66 +83,94 @@ public class ViewTest extends BaseTest
|
||||
{
|
||||
saveBitmap(expectedImagePath, ".expected", scaledExpected);
|
||||
String path = saveBitmap(expectedImagePath, "", actual);
|
||||
errorMessage.append(String.format("Actual rendered image " + "saved to %s", path));
|
||||
errorMessage.append(
|
||||
String.format("Actual rendered image saved to %s", path));
|
||||
fail(errorMessage.toString());
|
||||
}
|
||||
|
||||
actual.recycle();
|
||||
expected.recycle();
|
||||
scaledExpected.recycle();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
protected FrameLayout convertToView(BaseWidget widget,
|
||||
int width,
|
||||
int height)
|
||||
{
|
||||
widget.setDimensions(
|
||||
new WidgetDimensions(width, height, width, height));
|
||||
FrameLayout view = new FrameLayout(targetContext);
|
||||
RemoteViews remoteViews = widget.getPortraitRemoteViews();
|
||||
view.addView(remoteViews.apply(targetContext, view));
|
||||
measureView(view, width, height);
|
||||
return view;
|
||||
}
|
||||
|
||||
protected int dpToPixels(int dp)
|
||||
{
|
||||
return (int) InterfaceUtils.dpToPixels(targetContext, dp);
|
||||
}
|
||||
|
||||
protected void measureView(View view, int width, int height)
|
||||
{
|
||||
int specWidth = makeMeasureSpec(width, View.MeasureSpec.EXACTLY);
|
||||
int specHeight = makeMeasureSpec(height, View.MeasureSpec.EXACTLY);
|
||||
|
||||
view.measure(specWidth, specHeight);
|
||||
view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
|
||||
}
|
||||
|
||||
protected void setSimilarityCutoff(double similarityCutoff)
|
||||
{
|
||||
this.similarityCutoff = similarityCutoff;
|
||||
}
|
||||
|
||||
protected void skipAnimation(View view)
|
||||
{
|
||||
ViewPropertyAnimator animator = view.animate();
|
||||
animator.setDuration(0);
|
||||
animator.start();
|
||||
}
|
||||
|
||||
protected void tap(GestureDetector.OnGestureListener view, int x, int y)
|
||||
throws InterruptedException
|
||||
{
|
||||
long now = SystemClock.uptimeMillis();
|
||||
MotionEvent e =
|
||||
MotionEvent.obtain(now, now, MotionEvent.ACTION_UP, dpToPixels(x),
|
||||
dpToPixels(y), 0);
|
||||
view.onSingleTapUp(e);
|
||||
e.recycle();
|
||||
}
|
||||
|
||||
private double compareHistograms(int[][] actualHistogram,
|
||||
int[][] expectedHistogram)
|
||||
{
|
||||
long diff = 0;
|
||||
long total = 0;
|
||||
|
||||
for (int i = 0; i < 256 / HISTOGRAM_BIN_SIZE; i++)
|
||||
{
|
||||
diff += Math.abs(actualHistogram[0][i] - expectedHistogram[0][i]);
|
||||
diff += Math.abs(actualHistogram[1][i] - expectedHistogram[1][i]);
|
||||
diff += Math.abs(actualHistogram[2][i] - expectedHistogram[2][i]);
|
||||
diff += Math.abs(actualHistogram[3][i] - expectedHistogram[3][i]);
|
||||
|
||||
total += actualHistogram[0][i];
|
||||
total += actualHistogram[1][i];
|
||||
total += actualHistogram[2][i];
|
||||
total += actualHistogram[3][i];
|
||||
}
|
||||
|
||||
return (double) diff / total / 2;
|
||||
}
|
||||
|
||||
private Bitmap getBitmapFromAssets(String path) throws IOException
|
||||
{
|
||||
InputStream stream = testContext.getAssets().open(path);
|
||||
return BitmapFactory.decodeStream(stream);
|
||||
}
|
||||
|
||||
private String getVersionedViewAssetPath(String path)
|
||||
{
|
||||
String result = null;
|
||||
|
||||
if (android.os.Build.VERSION.SDK_INT >= 21)
|
||||
{
|
||||
try
|
||||
{
|
||||
String vpath = "views-v21/" + path;
|
||||
testContext.getAssets().open(vpath);
|
||||
result = vpath;
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
if(result == null)
|
||||
result = "views/" + path;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private String saveBitmap(String filename, String suffix, Bitmap bitmap)
|
||||
throws IOException
|
||||
{
|
||||
File dir = DatabaseHelper.getSDCardDir("test-screenshots");
|
||||
if(dir == null) dir = DatabaseHelper.getFilesDir("test-screenshots");
|
||||
if(dir == null) throw new RuntimeException("Could not find suitable dir for screenshots");
|
||||
|
||||
filename = filename.replaceAll("\\.png$", suffix + ".png");
|
||||
String absolutePath = String.format("%s/%s", dir.getAbsolutePath(), filename);
|
||||
|
||||
File parent = new File(absolutePath).getParentFile();
|
||||
if(!parent.exists() && !parent.mkdirs())
|
||||
throw new RuntimeException(String.format("Could not create dir: %s",
|
||||
parent.getAbsolutePath()));
|
||||
|
||||
FileOutputStream out = new FileOutputStream(absolutePath);
|
||||
bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
|
||||
|
||||
return absolutePath;
|
||||
}
|
||||
|
||||
private int[][] getHistogram(Bitmap bitmap)
|
||||
{
|
||||
int histogram[][] = new int[4][256 / HISTOGRAM_BIN_SIZE];
|
||||
@@ -168,59 +197,49 @@ public class ViewTest extends BaseTest
|
||||
return histogram;
|
||||
}
|
||||
|
||||
private double compareHistograms(int[][] actualHistogram, int[][] expectedHistogram)
|
||||
private String getVersionedViewAssetPath(String path)
|
||||
{
|
||||
long diff = 0;
|
||||
long total = 0;
|
||||
String result = null;
|
||||
|
||||
for(int i = 0; i < 256 / HISTOGRAM_BIN_SIZE; i ++)
|
||||
if (android.os.Build.VERSION.SDK_INT >= 21)
|
||||
{
|
||||
diff += Math.abs(actualHistogram[0][i] - expectedHistogram[0][i]);
|
||||
diff += Math.abs(actualHistogram[1][i] - expectedHistogram[1][i]);
|
||||
diff += Math.abs(actualHistogram[2][i] - expectedHistogram[2][i]);
|
||||
diff += Math.abs(actualHistogram[3][i] - expectedHistogram[3][i]);
|
||||
|
||||
total += actualHistogram[0][i];
|
||||
total += actualHistogram[1][i];
|
||||
total += actualHistogram[2][i];
|
||||
total += actualHistogram[3][i];
|
||||
}
|
||||
|
||||
return (double) diff / total / 2;
|
||||
}
|
||||
|
||||
protected int dpToPixels(int dp)
|
||||
{
|
||||
return (int) UIHelper.dpToPixels(targetContext, dp);
|
||||
}
|
||||
|
||||
protected void tap(GestureDetector.OnGestureListener view, int x, int y) throws InterruptedException
|
||||
{
|
||||
long now = SystemClock.uptimeMillis();
|
||||
MotionEvent e = MotionEvent.obtain(now, now, MotionEvent.ACTION_UP, dpToPixels(x),
|
||||
dpToPixels(y), 0);
|
||||
view.onSingleTapUp(e);
|
||||
e.recycle();
|
||||
}
|
||||
|
||||
protected void refreshData(final HabitDataView view)
|
||||
{
|
||||
new BaseTask()
|
||||
{
|
||||
@Override
|
||||
protected void doInBackground()
|
||||
{
|
||||
view.refreshData();
|
||||
}
|
||||
}.execute();
|
||||
|
||||
try
|
||||
{
|
||||
waitForAsyncTasks();
|
||||
String vpath = "views-v21/" + path;
|
||||
testContext.getAssets().open(vpath);
|
||||
result = vpath;
|
||||
}
|
||||
catch (Exception e)
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new RuntimeException("Time out");
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
if (result == null) result = "views/" + path;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private String saveBitmap(String filename, String suffix, Bitmap bitmap)
|
||||
throws IOException
|
||||
{
|
||||
File dir = FileUtils.getSDCardDir("test-screenshots");
|
||||
if (dir == null) dir = FileUtils.getFilesDir("test-screenshots");
|
||||
if (dir == null) throw new RuntimeException(
|
||||
"Could not find suitable dir for screenshots");
|
||||
|
||||
filename = filename.replaceAll("\\.png$", suffix + ".png");
|
||||
String absolutePath =
|
||||
String.format("%s/%s", dir.getAbsolutePath(), filename);
|
||||
|
||||
File parent = new File(absolutePath).getParentFile();
|
||||
if (!parent.exists() && !parent.mkdirs()) throw new RuntimeException(
|
||||
String.format("Could not create dir: %s",
|
||||
parent.getAbsolutePath()));
|
||||
|
||||
FileOutputStream out = new FileOutputStream(absolutePath);
|
||||
bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
|
||||
|
||||
return absolutePath;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits;
|
||||
|
||||
import org.isoron.uhabits.models.*;
|
||||
import org.isoron.uhabits.utils.DateUtils;
|
||||
|
||||
public class HabitFixtures
|
||||
{
|
||||
public boolean NON_DAILY_HABIT_CHECKS[] = {
|
||||
true, false, false, true, true, true, false, false, true, true
|
||||
};
|
||||
|
||||
private final HabitList habitList;
|
||||
|
||||
public HabitFixtures(HabitList habitList)
|
||||
{
|
||||
this.habitList = habitList;
|
||||
}
|
||||
|
||||
public Habit createEmptyHabit()
|
||||
{
|
||||
Habit habit = new Habit();
|
||||
habit.setName("Meditate");
|
||||
habit.setDescription("Did you meditate this morning?");
|
||||
habit.setColor(3);
|
||||
habit.setFrequency(Frequency.DAILY);
|
||||
habitList.add(habit);
|
||||
return habit;
|
||||
}
|
||||
|
||||
public Habit createLongHabit()
|
||||
{
|
||||
Habit habit = createEmptyHabit();
|
||||
habit.setFrequency(new Frequency(3, 7));
|
||||
habit.setColor(4);
|
||||
|
||||
long day = DateUtils.millisecondsInOneDay;
|
||||
long today = DateUtils.getStartOfToday();
|
||||
int marks[] = { 0, 1, 3, 5, 7, 8, 9, 10, 12, 14, 15, 17, 19, 20, 26, 27,
|
||||
28, 50, 51, 52, 53, 54, 58, 60, 63, 65, 70, 71, 72, 73, 74, 75, 80,
|
||||
81, 83, 89, 90, 91, 95, 102, 103, 108, 109, 120};
|
||||
|
||||
for (int mark : marks)
|
||||
habit.getRepetitions().toggleTimestamp(today - mark * day);
|
||||
|
||||
return habit;
|
||||
}
|
||||
|
||||
public Habit createShortHabit()
|
||||
{
|
||||
Habit habit = new Habit();
|
||||
habit.setName("Wake up early");
|
||||
habit.setDescription("Did you wake up before 6am?");
|
||||
habit.setFrequency(new Frequency(2, 3));
|
||||
habitList.add(habit);
|
||||
|
||||
long timestamp = DateUtils.getStartOfToday();
|
||||
for (boolean c : NON_DAILY_HABIT_CHECKS)
|
||||
{
|
||||
if (c) habit.getRepetitions().toggleTimestamp(timestamp);
|
||||
timestamp -= DateUtils.millisecondsInOneDay;
|
||||
}
|
||||
|
||||
return habit;
|
||||
}
|
||||
|
||||
public void purgeHabits(HabitList habitList)
|
||||
{
|
||||
for (Habit h : habitList.getAll(true))
|
||||
habitList.remove(h);
|
||||
}
|
||||
}
|
||||
@@ -17,24 +17,24 @@
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.unit;
|
||||
package org.isoron.uhabits;
|
||||
|
||||
import android.os.Build;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import android.os.*;
|
||||
import android.support.test.runner.*;
|
||||
import android.test.suitebuilder.annotation.*;
|
||||
|
||||
import org.isoron.uhabits.HabitsApplication;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.isoron.uhabits.ui.*;
|
||||
import org.junit.*;
|
||||
import org.junit.runner.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.*;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.MatcherAssert.*;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class HabitsApplicationTest
|
||||
public class HabitsApplicationTest extends BaseAndroidTest
|
||||
{
|
||||
@Test
|
||||
public void test_getLogcat() throws IOException
|
||||
@@ -45,7 +45,11 @@ public class HabitsApplicationTest
|
||||
String msg = "LOGCAT TEST";
|
||||
new RuntimeException(msg).printStackTrace();
|
||||
|
||||
String log = HabitsApplication.getLogcat();
|
||||
HabitsApplication app = HabitsApplication.getInstance();
|
||||
assert(app != null);
|
||||
|
||||
BaseSystem system = new BaseSystem(targetContext);
|
||||
String log = system.getLogcat();
|
||||
assertThat(log, containsString(msg));
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.ui;
|
||||
package org.isoron.uhabits.espresso;
|
||||
|
||||
import android.preference.Preference;
|
||||
import android.view.View;
|
||||
@@ -39,7 +39,7 @@ public class HabitMatchers
|
||||
@Override
|
||||
public boolean matchesSafely(Habit habit)
|
||||
{
|
||||
return habit.name.equals(name);
|
||||
return habit.getName().equals(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -51,7 +51,7 @@ public class HabitMatchers
|
||||
@Override
|
||||
public void describeMismatchSafely(Habit habit, Description description)
|
||||
{
|
||||
description.appendText("was ").appendText(habit.name);
|
||||
description.appendText("was ").appendText(habit.getName());
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -17,7 +17,7 @@
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.ui;
|
||||
package org.isoron.uhabits.espresso;
|
||||
|
||||
import android.support.test.espresso.UiController;
|
||||
import android.support.test.espresso.ViewAction;
|
||||
@@ -61,7 +61,7 @@ public class HabitViewActions
|
||||
@Override
|
||||
public void perform(UiController uiController, View view)
|
||||
{
|
||||
if (view.getId() != R.id.llButtons)
|
||||
if (view.getId() != R.id.checkmarkPanel)
|
||||
throw new InvalidParameterException("View must have id llButtons");
|
||||
|
||||
LinearLayout llButtons = (LinearLayout) view;
|
||||
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.espresso;
|
||||
|
||||
import android.support.test.espresso.*;
|
||||
import android.support.test.espresso.contrib.*;
|
||||
|
||||
import org.hamcrest.*;
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.models.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static android.support.test.espresso.Espresso.*;
|
||||
import static android.support.test.espresso.Espresso.pressBack;
|
||||
import static android.support.test.espresso.action.ViewActions.*;
|
||||
import static android.support.test.espresso.assertion.ViewAssertions.*;
|
||||
import static android.support.test.espresso.matcher.RootMatchers.*;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.Visibility.*;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.*;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
public class MainActivityActions
|
||||
{
|
||||
public static String addHabit()
|
||||
{
|
||||
return addHabit(false);
|
||||
}
|
||||
|
||||
public static String addHabit(boolean openDialogs)
|
||||
{
|
||||
String name = "New Habit " + new Random().nextInt(1000000);
|
||||
String description = "Did you perform your new habit today?";
|
||||
String num = "4";
|
||||
String den = "8";
|
||||
|
||||
onView(withId(R.id.action_add)).perform(click());
|
||||
|
||||
typeHabitData(name, description, num, den);
|
||||
|
||||
if (openDialogs)
|
||||
{
|
||||
onView(withId(R.id.buttonPickColor)).perform(click());
|
||||
pressBack();
|
||||
onView(withId(R.id.tvReminderTime)).perform(click());
|
||||
onView(withText("Done")).perform(click());
|
||||
onView(withId(R.id.tvReminderDays)).perform(click());
|
||||
onView(withText("OK")).perform(click());
|
||||
}
|
||||
|
||||
onView(withId(R.id.buttonSave)).perform(click());
|
||||
|
||||
onData(Matchers.allOf(is(instanceOf(Habit.class)),
|
||||
HabitMatchers.withName(name))).onChildView(withId(R.id.label));
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
public static void assertHabitExists(String name)
|
||||
{
|
||||
List<String> names = new LinkedList<>();
|
||||
names.add(name);
|
||||
assertHabitsExist(names);
|
||||
}
|
||||
|
||||
public static void assertHabitsDontExist(List<String> names)
|
||||
{
|
||||
for (String name : names)
|
||||
onView(withId(R.id.listView)).check(matches(Matchers.not(
|
||||
HabitMatchers.containsHabit(HabitMatchers.withName(name)))));
|
||||
}
|
||||
|
||||
public static void assertHabitsExist(List<String> names)
|
||||
{
|
||||
for (String name : names)
|
||||
onData(Matchers.allOf(is(instanceOf(Habit.class)),
|
||||
HabitMatchers.withName(name))).check(matches(isDisplayed()));
|
||||
}
|
||||
|
||||
private static void clickHiddenMenuItem(int stringId)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Try the ActionMode overflow menu first
|
||||
onView(allOf(withContentDescription("More options"), withParent(
|
||||
withParent(withClassName(containsString("Action")))))).perform(
|
||||
click());
|
||||
}
|
||||
catch (Exception e1)
|
||||
{
|
||||
// Try the toolbar overflow menu
|
||||
onView(allOf(withContentDescription("More options"), withParent(
|
||||
withParent(withClassName(containsString("Toolbar")))))).perform(
|
||||
click());
|
||||
}
|
||||
|
||||
onView(withText(stringId)).perform(click());
|
||||
}
|
||||
|
||||
public static void clickMenuItem(int stringId)
|
||||
{
|
||||
try
|
||||
{
|
||||
onView(withText(stringId)).perform(click());
|
||||
}
|
||||
catch (Exception e1)
|
||||
{
|
||||
try
|
||||
{
|
||||
onView(withContentDescription(stringId)).perform(click());
|
||||
}
|
||||
catch (Exception e2)
|
||||
{
|
||||
clickHiddenMenuItem(stringId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void clickSettingsItem(String text)
|
||||
{
|
||||
onView(withClassName(containsString("RecyclerView"))).perform(
|
||||
RecyclerViewActions.actionOnItem(
|
||||
hasDescendant(withText(containsString(text))), click()));
|
||||
}
|
||||
|
||||
public static void deleteHabit(String name)
|
||||
{
|
||||
deleteHabits(Collections.singletonList(name));
|
||||
}
|
||||
|
||||
public static void deleteHabits(List<String> names)
|
||||
{
|
||||
selectHabits(names);
|
||||
clickMenuItem(R.string.delete);
|
||||
onView(withText("OK")).perform(click());
|
||||
assertHabitsDontExist(names);
|
||||
}
|
||||
|
||||
public static void selectHabit(String name)
|
||||
{
|
||||
selectHabits(Collections.singletonList(name));
|
||||
}
|
||||
|
||||
public static void selectHabits(List<String> names)
|
||||
{
|
||||
boolean first = true;
|
||||
for (String name : names)
|
||||
{
|
||||
onData(Matchers.allOf(is(instanceOf(Habit.class)),
|
||||
HabitMatchers.withName(name)))
|
||||
.onChildView(withId(R.id.label))
|
||||
.perform(first ? longClick() : click());
|
||||
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void typeHabitData(String name,
|
||||
String description,
|
||||
String num,
|
||||
String den)
|
||||
{
|
||||
onView(withId(R.id.tvName)).perform(replaceText(name));
|
||||
onView(withId(R.id.tvDescription)).perform(replaceText(description));
|
||||
|
||||
try
|
||||
{
|
||||
onView(allOf(withId(R.id.sFrequency),
|
||||
withEffectiveVisibility(VISIBLE))).perform(click());
|
||||
onData(allOf(instanceOf(String.class), startsWith("Custom")))
|
||||
.inRoot(isPlatformPopup())
|
||||
.perform(click());
|
||||
}
|
||||
catch (NoMatchingViewException e)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
onView(withId(R.id.tvFreqNum)).perform(replaceText(num));
|
||||
onView(withId(R.id.tvFreqDen)).perform(replaceText(den));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,317 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.espresso;
|
||||
|
||||
import android.app.*;
|
||||
import android.content.*;
|
||||
import android.support.test.*;
|
||||
import android.support.test.espresso.*;
|
||||
import android.support.test.espresso.intent.rule.*;
|
||||
import android.support.test.runner.*;
|
||||
import android.test.suitebuilder.annotation.*;
|
||||
|
||||
import org.hamcrest.*;
|
||||
import org.isoron.uhabits.*;
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.models.*;
|
||||
import org.isoron.uhabits.utils.*;
|
||||
import org.junit.*;
|
||||
import org.junit.runner.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static android.support.test.espresso.Espresso.*;
|
||||
import static android.support.test.espresso.Espresso.pressBack;
|
||||
import static android.support.test.espresso.action.ViewActions.*;
|
||||
import static android.support.test.espresso.assertion.ViewAssertions.*;
|
||||
import static android.support.test.espresso.intent.Intents.*;
|
||||
import static android.support.test.espresso.intent.matcher.IntentMatchers.*;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.*;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.isoron.uhabits.espresso.HabitViewActions.*;
|
||||
import static org.isoron.uhabits.espresso.MainActivityActions.*;
|
||||
import static org.isoron.uhabits.espresso.ShowHabitActivityActions.*;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@LargeTest
|
||||
public class MainTest
|
||||
{
|
||||
private SystemHelper sys;
|
||||
|
||||
@Rule
|
||||
public IntentsTestRule<MainActivity> activityRule =
|
||||
new IntentsTestRule<>(MainActivity.class);
|
||||
|
||||
@Before
|
||||
public void setup()
|
||||
{
|
||||
Context context =
|
||||
InstrumentationRegistry.getInstrumentation().getContext();
|
||||
sys = new SystemHelper(context);
|
||||
sys.disableAllAnimations();
|
||||
sys.acquireWakeLock();
|
||||
sys.unlockScreen();
|
||||
|
||||
Instrumentation.ActivityResult okResult =
|
||||
new Instrumentation.ActivityResult(Activity.RESULT_OK,
|
||||
new Intent());
|
||||
|
||||
intending(hasAction(equalTo(Intent.ACTION_SEND))).respondWith(okResult);
|
||||
intending(hasAction(equalTo(Intent.ACTION_SENDTO))).respondWith(
|
||||
okResult);
|
||||
intending(hasAction(equalTo(Intent.ACTION_VIEW))).respondWith(okResult);
|
||||
|
||||
skipTutorial();
|
||||
}
|
||||
|
||||
public void skipTutorial()
|
||||
{
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < 10; i++)
|
||||
onView(allOf(withClassName(endsWith("AppCompatImageButton")),
|
||||
isDisplayed())).perform(click());
|
||||
}
|
||||
catch (NoMatchingViewException e)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown()
|
||||
{
|
||||
sys.releaseWakeLock();
|
||||
}
|
||||
|
||||
/**
|
||||
* User opens menu, clicks about, sees about screen.
|
||||
*/
|
||||
@Test
|
||||
public void testAbout()
|
||||
{
|
||||
clickMenuItem(R.string.about);
|
||||
onView(isRoot()).perform(swipeUp());
|
||||
}
|
||||
|
||||
/**
|
||||
* User creates a habit, toggles a bunch of checkmarks, clicks the habit to
|
||||
* open the statistics screen, scrolls down to some views, then scrolls the
|
||||
* views backwards and forwards in time.
|
||||
*/
|
||||
@Test
|
||||
public void testAddHabitAndViewStats() throws InterruptedException
|
||||
{
|
||||
String name = addHabit(true);
|
||||
|
||||
onData(Matchers.allOf(is(instanceOf(Habit.class)),
|
||||
HabitMatchers.withName(name)))
|
||||
.onChildView(withId(R.id.checkmarkPanel))
|
||||
.perform(toggleAllCheckmarks());
|
||||
|
||||
Thread.sleep(1200);
|
||||
|
||||
onData(Matchers.allOf(is(instanceOf(Habit.class)),
|
||||
HabitMatchers.withName(name)))
|
||||
.onChildView(withId(R.id.label))
|
||||
.perform(click());
|
||||
|
||||
onView(withId(R.id.scoreView)).perform(scrollTo(), swipeRight());
|
||||
|
||||
onView(withId(R.id.frequencyChart)).perform(scrollTo(), swipeRight());
|
||||
}
|
||||
|
||||
/**
|
||||
* User opens the app, clicks the add button, types some bogus information,
|
||||
* tries to save, dialog displays an error.
|
||||
*/
|
||||
@Test
|
||||
public void testAddInvalidHabit()
|
||||
{
|
||||
onView(withId(R.id.action_add)).perform(click());
|
||||
|
||||
typeHabitData("", "", "15", "7");
|
||||
|
||||
onView(withId(R.id.buttonSave)).perform(click());
|
||||
onView(withId(R.id.tvName)).check(matches(isDisplayed()));
|
||||
}
|
||||
|
||||
/**
|
||||
* User opens the app, creates some habits, selects them, archives them,
|
||||
* select 'show archived' on the menu, selects the previously archived
|
||||
* habits and then deletes them.
|
||||
*/
|
||||
@Test
|
||||
public void testArchiveHabits()
|
||||
{
|
||||
List<String> names = new LinkedList<>();
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
names.add(addHabit());
|
||||
|
||||
selectHabits(names);
|
||||
|
||||
clickMenuItem(R.string.archive);
|
||||
assertHabitsDontExist(names);
|
||||
|
||||
clickMenuItem(R.string.show_archived);
|
||||
|
||||
assertHabitsExist(names);
|
||||
selectHabits(names);
|
||||
clickMenuItem(R.string.unarchive);
|
||||
|
||||
clickMenuItem(R.string.show_archived);
|
||||
|
||||
assertHabitsExist(names);
|
||||
deleteHabits(names);
|
||||
}
|
||||
|
||||
/**
|
||||
* User creates a habit, selects the habit, clicks edit button, changes some
|
||||
* information about the habit, click save button, sees changes on the main
|
||||
* window, selects habit again, changes color, then deletes the habit.
|
||||
*/
|
||||
@Test
|
||||
public void testEditHabit()
|
||||
{
|
||||
String name = addHabit();
|
||||
|
||||
onData(Matchers.allOf(is(instanceOf(Habit.class)),
|
||||
HabitMatchers.withName(name)))
|
||||
.onChildView(withId(R.id.label))
|
||||
.perform(longClick());
|
||||
|
||||
clickMenuItem(R.string.edit);
|
||||
|
||||
String modifiedName = "Modified " + new Random().nextInt(10000);
|
||||
typeHabitData(modifiedName, "", "1", "1");
|
||||
|
||||
onView(withId(R.id.buttonSave)).perform(click());
|
||||
|
||||
assertHabitExists(modifiedName);
|
||||
|
||||
selectHabit(modifiedName);
|
||||
clickMenuItem(R.string.color_picker_default_title);
|
||||
pressBack();
|
||||
|
||||
deleteHabit(modifiedName);
|
||||
}
|
||||
|
||||
/**
|
||||
* User creates a habit, opens statistics page, clicks button to edit
|
||||
* history, adds some checkmarks, closes dialog, sees the modified history
|
||||
* calendar.
|
||||
*/
|
||||
@Test
|
||||
public void testEditHistory()
|
||||
{
|
||||
String name = addHabit();
|
||||
|
||||
onData(Matchers.allOf(is(instanceOf(Habit.class)),
|
||||
HabitMatchers.withName(name)))
|
||||
.onChildView(withId(R.id.label))
|
||||
.perform(click());
|
||||
|
||||
openHistoryEditor();
|
||||
onView(withClassName(endsWith("HabitHistoryView"))).perform(
|
||||
clickAtRandomLocations(20));
|
||||
|
||||
pressBack();
|
||||
onView(withId(R.id.historyChart)).perform(scrollTo(), swipeRight(),
|
||||
swipeLeft());
|
||||
}
|
||||
|
||||
/**
|
||||
* User creates a habit, opens settings, clicks export as CSV, is asked what
|
||||
* activity should handle the file.
|
||||
*/
|
||||
@Test
|
||||
public void testExportCSV()
|
||||
{
|
||||
addHabit();
|
||||
clickMenuItem(R.string.settings);
|
||||
clickSettingsItem("Export as CSV");
|
||||
intended(hasAction(Intent.ACTION_SEND));
|
||||
}
|
||||
|
||||
/**
|
||||
* User creates a habit, exports full backup, deletes the habit, restores
|
||||
* backup, sees that the previously created habit has appeared back.
|
||||
*/
|
||||
@Test
|
||||
public void testExportImportDB()
|
||||
{
|
||||
String name = addHabit();
|
||||
|
||||
clickMenuItem(R.string.settings);
|
||||
|
||||
String date =
|
||||
DateUtils.getBackupDateFormat().format(DateUtils.getLocalTime());
|
||||
date = date.substring(0, date.length() - 2);
|
||||
|
||||
clickSettingsItem("Export full backup");
|
||||
intended(hasAction(Intent.ACTION_SEND));
|
||||
|
||||
deleteHabit(name);
|
||||
|
||||
clickMenuItem(R.string.settings);
|
||||
clickSettingsItem("Import data");
|
||||
|
||||
onData(
|
||||
allOf(is(instanceOf(String.class)), startsWith("Backups"))).perform(
|
||||
click());
|
||||
|
||||
onData(
|
||||
allOf(is(instanceOf(String.class)), containsString(date))).perform(
|
||||
click());
|
||||
|
||||
selectHabit(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* User opens the settings and generates a bug report.
|
||||
*/
|
||||
@Test
|
||||
public void testGenerateBugReport()
|
||||
{
|
||||
clickMenuItem(R.string.settings);
|
||||
clickSettingsItem("Generate bug report");
|
||||
intended(hasAction(Intent.ACTION_SEND));
|
||||
}
|
||||
|
||||
/**
|
||||
* User opens menu, clicks Help, sees website.
|
||||
*/
|
||||
@Test
|
||||
public void testHelp()
|
||||
{
|
||||
clickMenuItem(R.string.help);
|
||||
intended(hasAction(Intent.ACTION_VIEW));
|
||||
}
|
||||
|
||||
/**
|
||||
* User opens menu, clicks settings, sees settings screen.
|
||||
*/
|
||||
@Test
|
||||
public void testSettings()
|
||||
{
|
||||
clickMenuItem(R.string.settings);
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.ui;
|
||||
package org.isoron.uhabits.espresso;
|
||||
|
||||
import android.support.test.espresso.matcher.ViewMatchers;
|
||||
|
||||
@@ -31,7 +31,7 @@ public class ShowHabitActivityActions
|
||||
{
|
||||
public static void openHistoryEditor()
|
||||
{
|
||||
onView(ViewMatchers.withId(R.id.btEditHistory))
|
||||
onView(ViewMatchers.withId(R.id.edit))
|
||||
.perform(scrollTo(), click());
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,23 @@
|
||||
package org.isoron.uhabits.ui;
|
||||
/*
|
||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.espresso;
|
||||
|
||||
import android.app.KeyguardManager;
|
||||
import android.content.Context;
|
||||
@@ -17,18 +17,16 @@
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.unit.io;
|
||||
package org.isoron.uhabits.io;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
import org.isoron.uhabits.BaseTest;
|
||||
import org.isoron.uhabits.helpers.DatabaseHelper;
|
||||
import org.isoron.uhabits.io.HabitsCSVExporter;
|
||||
import org.isoron.uhabits.BaseAndroidTest;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.unit.HabitFixtures;
|
||||
import org.isoron.uhabits.utils.FileUtils;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -45,50 +43,27 @@ import static junit.framework.Assert.assertTrue;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class HabitsCSVExporterTest extends BaseTest
|
||||
public class HabitsCSVExporterTest extends BaseAndroidTest
|
||||
{
|
||||
private File baseDir;
|
||||
|
||||
@Before
|
||||
public void setup()
|
||||
public void setUp()
|
||||
{
|
||||
super.setup();
|
||||
super.setUp();
|
||||
|
||||
HabitFixtures.purgeHabits();
|
||||
HabitFixtures.createShortHabit();
|
||||
HabitFixtures.createEmptyHabit();
|
||||
fixtures.purgeHabits(habitList);
|
||||
fixtures.createShortHabit();
|
||||
fixtures.createEmptyHabit();
|
||||
|
||||
Context targetContext = InstrumentationRegistry.getTargetContext();
|
||||
baseDir = targetContext.getCacheDir();
|
||||
}
|
||||
|
||||
private void unzip(File file) throws IOException
|
||||
{
|
||||
ZipFile zip = new ZipFile(file);
|
||||
Enumeration<? extends ZipEntry> e = zip.entries();
|
||||
|
||||
while(e.hasMoreElements())
|
||||
{
|
||||
ZipEntry entry = e.nextElement();
|
||||
InputStream stream = zip.getInputStream(entry);
|
||||
|
||||
String outputFilename = String.format("%s/%s", baseDir.getAbsolutePath(),
|
||||
entry.getName());
|
||||
File outputFile = new File(outputFilename);
|
||||
|
||||
File parent = outputFile.getParentFile();
|
||||
if(parent != null) parent.mkdirs();
|
||||
|
||||
DatabaseHelper.copy(stream, outputFile);
|
||||
}
|
||||
|
||||
zip.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExportCSV() throws IOException
|
||||
{
|
||||
List<Habit> habits = Habit.getAll(true);
|
||||
List<Habit> habits = habitList.getAll(true);
|
||||
|
||||
HabitsCSVExporter exporter = new HabitsCSVExporter(habits, baseDir);
|
||||
String filename = exporter.writeArchive();
|
||||
@@ -105,14 +80,41 @@ public class HabitsCSVExporterTest extends BaseTest
|
||||
assertPathExists("002 Meditate/Scores.csv");
|
||||
}
|
||||
|
||||
private void assertPathExists(String s)
|
||||
{
|
||||
assertAbsolutePathExists(String.format("%s/%s", baseDir.getAbsolutePath(), s));
|
||||
}
|
||||
|
||||
private void assertAbsolutePathExists(String s)
|
||||
{
|
||||
File file = new File(s);
|
||||
assertTrue(String.format("File %s should exist", file.getAbsolutePath()), file.exists());
|
||||
assertTrue(
|
||||
String.format("File %s should exist", file.getAbsolutePath()),
|
||||
file.exists());
|
||||
}
|
||||
|
||||
private void assertPathExists(String s)
|
||||
{
|
||||
assertAbsolutePathExists(
|
||||
String.format("%s/%s", baseDir.getAbsolutePath(), s));
|
||||
}
|
||||
|
||||
private void unzip(File file) throws IOException
|
||||
{
|
||||
ZipFile zip = new ZipFile(file);
|
||||
Enumeration<? extends ZipEntry> e = zip.entries();
|
||||
|
||||
while (e.hasMoreElements())
|
||||
{
|
||||
ZipEntry entry = e.nextElement();
|
||||
InputStream stream = zip.getInputStream(entry);
|
||||
|
||||
String outputFilename =
|
||||
String.format("%s/%s", baseDir.getAbsolutePath(),
|
||||
entry.getName());
|
||||
File outputFile = new File(outputFilename);
|
||||
|
||||
File parent = outputFile.getParentFile();
|
||||
if (parent != null) parent.mkdirs();
|
||||
|
||||
FileUtils.copy(stream, outputFile);
|
||||
}
|
||||
|
||||
zip.close();
|
||||
}
|
||||
}
|
||||
@@ -17,138 +17,57 @@
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.unit.io;
|
||||
package org.isoron.uhabits.io;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import android.content.*;
|
||||
import android.support.test.*;
|
||||
import android.support.test.runner.*;
|
||||
import android.test.suitebuilder.annotation.*;
|
||||
|
||||
import org.isoron.uhabits.BaseTest;
|
||||
import org.isoron.uhabits.helpers.DatabaseHelper;
|
||||
import org.isoron.uhabits.helpers.DateHelper;
|
||||
import org.isoron.uhabits.io.GenericImporter;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.unit.HabitFixtures;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.isoron.uhabits.*;
|
||||
import org.isoron.uhabits.models.*;
|
||||
import org.isoron.uhabits.utils.*;
|
||||
import org.junit.*;
|
||||
import org.junit.runner.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.List;
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class ImportTest extends BaseTest
|
||||
public class ImportTest extends BaseAndroidTest
|
||||
{
|
||||
private File baseDir;
|
||||
|
||||
private Context context;
|
||||
|
||||
@Before
|
||||
public void setup()
|
||||
public void setUp()
|
||||
{
|
||||
super.setup();
|
||||
DateHelper.setFixedLocalTime(null);
|
||||
super.setUp();
|
||||
DateUtils.setFixedLocalTime(null);
|
||||
|
||||
HabitFixtures.purgeHabits();
|
||||
fixtures.purgeHabits(habitList);
|
||||
context = InstrumentationRegistry.getInstrumentation().getContext();
|
||||
baseDir = DatabaseHelper.getFilesDir("Backups");
|
||||
baseDir = FileUtils.getFilesDir("Backups");
|
||||
if (baseDir == null) fail("baseDir should not be null");
|
||||
}
|
||||
|
||||
private void copyAssetToFile(String assetPath, File dst) throws IOException
|
||||
{
|
||||
InputStream in = context.getAssets().open(assetPath);
|
||||
DatabaseHelper.copy(in, dst);
|
||||
}
|
||||
|
||||
private void importFromFile(String assetFilename) throws IOException
|
||||
{
|
||||
File file = new File(String.format("%s/%s", baseDir.getPath(), assetFilename));
|
||||
copyAssetToFile(assetFilename, file);
|
||||
assertTrue(file.exists());
|
||||
assertTrue(file.canRead());
|
||||
|
||||
GenericImporter importer = new GenericImporter();
|
||||
assertThat(importer.canHandle(file), is(true));
|
||||
|
||||
importer.importHabitsFromFile(file);
|
||||
}
|
||||
|
||||
private boolean containsRepetition(Habit h, int year, int month, int day)
|
||||
{
|
||||
GregorianCalendar date = DateHelper.getStartOfTodayCalendar();
|
||||
date.set(year, month - 1, day);
|
||||
return h.repetitions.contains(date.getTimeInMillis());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTickmateDB() throws IOException
|
||||
{
|
||||
importFromFile("tickmate.db");
|
||||
|
||||
List<Habit> habits = Habit.getAll(true);
|
||||
assertThat(habits.size(), equalTo(3));
|
||||
|
||||
Habit h = habits.get(0);
|
||||
assertThat(h.name, equalTo("Vegan"));
|
||||
assertTrue(containsRepetition(h, 2016, 1, 24));
|
||||
assertTrue(containsRepetition(h, 2016, 2, 5));
|
||||
assertTrue(containsRepetition(h, 2016, 3, 18));
|
||||
assertFalse(containsRepetition(h, 2016, 3, 14));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRewireDB() throws IOException
|
||||
{
|
||||
importFromFile("rewire.db");
|
||||
|
||||
List<Habit> habits = Habit.getAll(true);
|
||||
assertThat(habits.size(), equalTo(3));
|
||||
|
||||
Habit habit = habits.get(0);
|
||||
assertThat(habit.name, equalTo("Wake up early"));
|
||||
assertThat(habit.freqNum, equalTo(3));
|
||||
assertThat(habit.freqDen, equalTo(7));
|
||||
assertFalse(habit.hasReminder());
|
||||
assertFalse(containsRepetition(habit, 2015, 12, 31));
|
||||
assertTrue(containsRepetition(habit, 2016, 1, 18));
|
||||
assertTrue(containsRepetition(habit, 2016, 1, 28));
|
||||
assertFalse(containsRepetition(habit, 2016, 3, 10));
|
||||
|
||||
habit = habits.get(1);
|
||||
assertThat(habit.name, equalTo("brush teeth"));
|
||||
assertThat(habit.freqNum, equalTo(3));
|
||||
assertThat(habit.freqDen, equalTo(7));
|
||||
assertThat(habit.reminderHour, equalTo(8));
|
||||
assertThat(habit.reminderMin, equalTo(0));
|
||||
boolean[] reminderDays = {false, true, true, true, true, true, false};
|
||||
assertThat(habit.reminderDays, equalTo(DateHelper.packWeekdayList(reminderDays)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHabitBullCSV() throws IOException
|
||||
{
|
||||
importFromFile("habitbull.csv");
|
||||
|
||||
List<Habit> habits = Habit.getAll(true);
|
||||
List<Habit> habits = habitList.getAll(true);
|
||||
assertThat(habits.size(), equalTo(4));
|
||||
|
||||
Habit habit = habits.get(0);
|
||||
assertThat(habit.name, equalTo("Breed dragons"));
|
||||
assertThat(habit.description, equalTo("with love and fire"));
|
||||
assertThat(habit.freqNum, equalTo(1));
|
||||
assertThat(habit.freqDen, equalTo(1));
|
||||
assertThat(habit.getName(), equalTo("Breed dragons"));
|
||||
assertThat(habit.getDescription(), equalTo("with love and fire"));
|
||||
assertThat(habit.getFrequency(), equalTo(Frequency.DAILY));
|
||||
assertTrue(containsRepetition(habit, 2016, 3, 18));
|
||||
assertTrue(containsRepetition(habit, 2016, 3, 19));
|
||||
assertFalse(containsRepetition(habit, 2016, 3, 20));
|
||||
@@ -159,15 +78,89 @@ public class ImportTest extends BaseTest
|
||||
{
|
||||
importFromFile("loop.db");
|
||||
|
||||
List<Habit> habits = Habit.getAll(true);
|
||||
List<Habit> habits = habitList.getAll(true);
|
||||
assertThat(habits.size(), equalTo(9));
|
||||
|
||||
Habit habit = habits.get(0);
|
||||
assertThat(habit.name, equalTo("Wake up early"));
|
||||
assertThat(habit.freqNum, equalTo(3));
|
||||
assertThat(habit.freqDen, equalTo(7));
|
||||
assertThat(habit.getName(), equalTo("Wake up early"));
|
||||
assertThat(habit.getFrequency(), equalTo(Frequency.THREE_TIMES_PER_WEEK));
|
||||
assertTrue(containsRepetition(habit, 2016, 3, 14));
|
||||
assertTrue(containsRepetition(habit, 2016, 3, 16));
|
||||
assertFalse(containsRepetition(habit, 2016, 3, 17));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRewireDB() throws IOException
|
||||
{
|
||||
importFromFile("rewire.db");
|
||||
|
||||
List<Habit> habits = habitList.getAll(true);
|
||||
assertThat(habits.size(), equalTo(3));
|
||||
|
||||
Habit habit = habits.get(0);
|
||||
assertThat(habit.getName(), equalTo("Wake up early"));
|
||||
assertThat(habit.getFrequency(),
|
||||
equalTo(Frequency.THREE_TIMES_PER_WEEK));
|
||||
assertFalse(habit.hasReminder());
|
||||
assertFalse(containsRepetition(habit, 2015, 12, 31));
|
||||
assertTrue(containsRepetition(habit, 2016, 1, 18));
|
||||
assertTrue(containsRepetition(habit, 2016, 1, 28));
|
||||
assertFalse(containsRepetition(habit, 2016, 3, 10));
|
||||
|
||||
habit = habits.get(1);
|
||||
assertThat(habit.getName(), equalTo("brush teeth"));
|
||||
assertThat(habit.getFrequency(),
|
||||
equalTo(Frequency.THREE_TIMES_PER_WEEK));
|
||||
assertThat(habit.hasReminder(), equalTo(true));
|
||||
|
||||
Reminder reminder = habit.getReminder();
|
||||
assertThat(reminder.getHour(), equalTo(8));
|
||||
assertThat(reminder.getMinute(), equalTo(0));
|
||||
boolean[] reminderDays = { false, true, true, true, true, true, false };
|
||||
assertThat(reminder.getDays(),
|
||||
equalTo(DateUtils.packWeekdayList(reminderDays)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTickmateDB() throws IOException
|
||||
{
|
||||
importFromFile("tickmate.db");
|
||||
|
||||
List<Habit> habits = habitList.getAll(true);
|
||||
assertThat(habits.size(), equalTo(3));
|
||||
|
||||
Habit h = habits.get(0);
|
||||
assertThat(h.getName(), equalTo("Vegan"));
|
||||
assertTrue(containsRepetition(h, 2016, 1, 24));
|
||||
assertTrue(containsRepetition(h, 2016, 2, 5));
|
||||
assertTrue(containsRepetition(h, 2016, 3, 18));
|
||||
assertFalse(containsRepetition(h, 2016, 3, 14));
|
||||
}
|
||||
|
||||
private boolean containsRepetition(Habit h, int year, int month, int day)
|
||||
{
|
||||
GregorianCalendar date = DateUtils.getStartOfTodayCalendar();
|
||||
date.set(year, month - 1, day);
|
||||
return h.getRepetitions().containsTimestamp(date.getTimeInMillis());
|
||||
}
|
||||
|
||||
private void copyAssetToFile(String assetPath, File dst) throws IOException
|
||||
{
|
||||
InputStream in = context.getAssets().open(assetPath);
|
||||
FileUtils.copy(in, dst);
|
||||
}
|
||||
|
||||
private void importFromFile(String assetFilename) throws IOException
|
||||
{
|
||||
File file =
|
||||
new File(String.format("%s/%s", baseDir.getPath(), assetFilename));
|
||||
copyAssetToFile(assetFilename, file);
|
||||
assertTrue(file.exists());
|
||||
assertTrue(file.canRead());
|
||||
|
||||
GenericImporter importer = new GenericImporter();
|
||||
assertThat(importer.canHandle(file), is(true));
|
||||
|
||||
importer.importHabitsFromFile(file);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.models.sqlite;
|
||||
|
||||
import android.support.test.runner.*;
|
||||
import android.test.suitebuilder.annotation.*;
|
||||
|
||||
import com.activeandroid.query.*;
|
||||
|
||||
import org.isoron.uhabits.*;
|
||||
import org.isoron.uhabits.models.*;
|
||||
import org.isoron.uhabits.models.sqlite.records.*;
|
||||
import org.isoron.uhabits.utils.*;
|
||||
import org.junit.*;
|
||||
import org.junit.runner.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.*;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@MediumTest
|
||||
public class SQLiteCheckmarkListTest extends BaseAndroidTest
|
||||
{
|
||||
private Habit habit;
|
||||
|
||||
private CheckmarkList checkmarks;
|
||||
|
||||
private long today;
|
||||
|
||||
private long day;
|
||||
|
||||
@Override
|
||||
public void setUp()
|
||||
{
|
||||
super.setUp();
|
||||
|
||||
habit = fixtures.createLongHabit();
|
||||
checkmarks = habit.getCheckmarks();
|
||||
checkmarks.getToday(); // compute checkmarks
|
||||
|
||||
today = DateUtils.getStartOfToday();
|
||||
day = DateUtils.millisecondsInOneDay;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAdd()
|
||||
{
|
||||
checkmarks.invalidateNewerThan(0);
|
||||
|
||||
List<Checkmark> list = new LinkedList<>();
|
||||
list.add(new Checkmark(0, 0));
|
||||
list.add(new Checkmark(1, 1));
|
||||
list.add(new Checkmark(2, 2));
|
||||
|
||||
checkmarks.add(list);
|
||||
|
||||
List<CheckmarkRecord> records = getAllRecords();
|
||||
assertThat(records.size(), equalTo(3));
|
||||
assertThat(records.get(0).timestamp, equalTo(2L));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetByInterval()
|
||||
{
|
||||
long from = today - 10 * day;
|
||||
long to = today - 3 * day;
|
||||
|
||||
List<Checkmark> list = checkmarks.getByInterval(from, to);
|
||||
assertThat(list.size(), equalTo(8));
|
||||
|
||||
assertThat(list.get(0).getTimestamp(), equalTo(today - 3 * day));
|
||||
assertThat(list.get(3).getTimestamp(), equalTo(today - 6 * day));
|
||||
assertThat(list.get(7).getTimestamp(), equalTo(today - 10 * day));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidateNewerThan()
|
||||
{
|
||||
List<CheckmarkRecord> records = getAllRecords();
|
||||
assertThat(records.size(), equalTo(121));
|
||||
|
||||
checkmarks.invalidateNewerThan(today - 20 * day);
|
||||
|
||||
records = getAllRecords();
|
||||
assertThat(records.size(), equalTo(100));
|
||||
assertThat(records.get(0).timestamp, equalTo(today - 21 * day));
|
||||
}
|
||||
|
||||
private List<CheckmarkRecord> getAllRecords()
|
||||
{
|
||||
return new Select()
|
||||
.from(CheckmarkRecord.class)
|
||||
.where("habit = ?", habit.getId())
|
||||
.orderBy("timestamp desc")
|
||||
.execute();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.models.sqlite;
|
||||
|
||||
import android.support.test.runner.*;
|
||||
import android.test.suitebuilder.annotation.*;
|
||||
|
||||
import com.activeandroid.query.*;
|
||||
|
||||
import org.isoron.uhabits.*;
|
||||
import org.isoron.uhabits.models.*;
|
||||
import org.isoron.uhabits.models.sqlite.records.*;
|
||||
import org.junit.*;
|
||||
import org.junit.rules.*;
|
||||
import org.junit.runner.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static junit.framework.Assert.*;
|
||||
import static org.hamcrest.MatcherAssert.*;
|
||||
import static org.hamcrest.core.IsEqual.*;
|
||||
|
||||
@SuppressWarnings("JavaDoc")
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@MediumTest
|
||||
public class SQLiteHabitListTest extends BaseAndroidTest
|
||||
{
|
||||
@Rule
|
||||
public ExpectedException exception = ExpectedException.none();
|
||||
|
||||
@Override
|
||||
public void setUp()
|
||||
{
|
||||
super.setUp();
|
||||
fixtures.purgeHabits(habitList);
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
Habit h = new Habit();
|
||||
h.setName("habit " + i);
|
||||
h.setId((long) i);
|
||||
if (i % 2 == 0) h.setArchived(true);
|
||||
|
||||
HabitRecord record = new HabitRecord();
|
||||
record.copyFrom(h);
|
||||
record.position = i;
|
||||
record.save(i);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAdd_withDuplicate()
|
||||
{
|
||||
Habit habit = new Habit();
|
||||
habitList.add(habit);
|
||||
exception.expect(IllegalArgumentException.class);
|
||||
habitList.add(habit);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAdd_withId()
|
||||
{
|
||||
Habit habit = new Habit();
|
||||
habit.setName("Hello world with id");
|
||||
habit.setId(12300L);
|
||||
|
||||
habitList.add(habit);
|
||||
assertThat(habit.getId(), equalTo(12300L));
|
||||
|
||||
HabitRecord record = getRecord(12300L);
|
||||
assertNotNull(record);
|
||||
assertThat(record.name, equalTo(habit.getName()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAdd_withoutId()
|
||||
{
|
||||
Habit habit = new Habit();
|
||||
habit.setName("Hello world");
|
||||
assertNull(habit.getId());
|
||||
|
||||
habitList.add(habit);
|
||||
assertNotNull(habit.getId());
|
||||
|
||||
HabitRecord record = getRecord(habit.getId());
|
||||
assertNotNull(record);
|
||||
assertThat(record.name, equalTo(habit.getName()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCountActive()
|
||||
{
|
||||
assertThat(habitList.countActive(), equalTo(5));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCountWithArchived()
|
||||
{
|
||||
assertThat(habitList.countWithArchived(), equalTo(10));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAll_withArchived()
|
||||
{
|
||||
List<Habit> habits = habitList.getAll(true);
|
||||
assertThat(habits.size(), equalTo(10));
|
||||
assertThat(habits.get(3).getName(), equalTo("habit 3"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAll_withoutArchived()
|
||||
{
|
||||
List<Habit> habits = habitList.getAll(false);
|
||||
assertThat(habits.size(), equalTo(5));
|
||||
assertThat(habits.get(3).getName(), equalTo("habit 7"));
|
||||
|
||||
List<Habit> another = habitList.getAll(false);
|
||||
assertThat(habits, equalTo(another));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetById()
|
||||
{
|
||||
Habit h1 = habitList.getById(0);
|
||||
assertNotNull(h1);
|
||||
assertThat(h1.getName(), equalTo("habit 0"));
|
||||
|
||||
Habit h2 = habitList.getById(0);
|
||||
assertNotNull(h2);
|
||||
assertThat(h1, equalTo(h2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetById_withInvalid()
|
||||
{
|
||||
long invalidId = 9183792001L;
|
||||
Habit h1 = habitList.getById(invalidId);
|
||||
assertNull(h1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetByPosition()
|
||||
{
|
||||
Habit h = habitList.getByPosition(5);
|
||||
assertNotNull(h);
|
||||
assertThat(h.getName(), equalTo("habit 5"));
|
||||
|
||||
h = habitList.getByPosition(5000);
|
||||
assertNull(h);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIndexOf()
|
||||
{
|
||||
Habit h1 = habitList.getByPosition(5);
|
||||
assertNotNull(h1);
|
||||
assertThat(habitList.indexOf(h1), equalTo(5));
|
||||
|
||||
Habit h2 = new Habit();
|
||||
assertThat(habitList.indexOf(h2), equalTo(-1));
|
||||
|
||||
h2.setId(1000L);
|
||||
assertThat(habitList.indexOf(h2), equalTo(-1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_reorder()
|
||||
{
|
||||
// Same as HabitListTest.java
|
||||
// TODO: remove duplication
|
||||
|
||||
int operations[][] = {
|
||||
{5, 2}, {3, 7}, {4, 4}, {3, 2}
|
||||
};
|
||||
|
||||
int expectedPosition[][] = {
|
||||
{0, 1, 3, 4, 5, 2, 6, 7, 8, 9},
|
||||
{0, 1, 7, 3, 4, 2, 5, 6, 8, 9},
|
||||
{0, 1, 7, 3, 4, 2, 5, 6, 8, 9},
|
||||
{0, 1, 7, 2, 4, 3, 5, 6, 8, 9},
|
||||
};
|
||||
|
||||
for (int i = 0; i < operations.length; i++)
|
||||
{
|
||||
int from = operations[i][0];
|
||||
int to = operations[i][1];
|
||||
|
||||
Habit fromHabit = habitList.getByPosition(from);
|
||||
Habit toHabit = habitList.getByPosition(to);
|
||||
habitList.reorder(fromHabit, toHabit);
|
||||
|
||||
int actualPositions[] = new int[10];
|
||||
|
||||
for (int j = 0; j < 10; j++)
|
||||
{
|
||||
Habit h = habitList.getById(j);
|
||||
assertNotNull(h);
|
||||
actualPositions[j] = habitList.indexOf(h);
|
||||
}
|
||||
|
||||
assertThat(actualPositions, equalTo(expectedPosition[i]));
|
||||
}
|
||||
}
|
||||
|
||||
private HabitRecord getRecord(long id)
|
||||
{
|
||||
return new Select()
|
||||
.from(HabitRecord.class)
|
||||
.where("id = ?", id)
|
||||
.executeSingle();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.models.sqlite;
|
||||
|
||||
import android.support.annotation.*;
|
||||
import android.support.test.runner.*;
|
||||
import android.test.suitebuilder.annotation.*;
|
||||
|
||||
import com.activeandroid.query.*;
|
||||
|
||||
import org.isoron.uhabits.*;
|
||||
import org.isoron.uhabits.models.*;
|
||||
import org.isoron.uhabits.models.sqlite.records.*;
|
||||
import org.isoron.uhabits.utils.*;
|
||||
import org.junit.*;
|
||||
import org.junit.runner.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.*;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.hamcrest.core.IsNot.not;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@MediumTest
|
||||
public class SQLiteRepetitionListTest extends BaseAndroidTest
|
||||
{
|
||||
private Habit habit;
|
||||
|
||||
private long today;
|
||||
|
||||
private RepetitionList repetitions;
|
||||
|
||||
private long day;
|
||||
|
||||
@Override
|
||||
public void setUp()
|
||||
{
|
||||
super.setUp();
|
||||
|
||||
habit = fixtures.createLongHabit();
|
||||
repetitions = habit.getRepetitions();
|
||||
today = DateUtils.getStartOfToday();
|
||||
day = DateUtils.millisecondsInOneDay;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAdd()
|
||||
{
|
||||
RepetitionRecord record = getByTimestamp(today + day);
|
||||
assertThat(record, is(nullValue()));
|
||||
|
||||
Repetition rep = new Repetition(today + day);
|
||||
habit.getRepetitions().add(rep);
|
||||
|
||||
record = getByTimestamp(today + day);
|
||||
assertThat(record, is(not(nullValue())));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetByInterval()
|
||||
{
|
||||
List<Repetition> reps =
|
||||
repetitions.getByInterval(today - 10 * day, today);
|
||||
|
||||
assertThat(reps.size(), equalTo(8));
|
||||
assertThat(reps.get(0).getTimestamp(), equalTo(today - 10 * day));
|
||||
assertThat(reps.get(4).getTimestamp(), equalTo(today - 5 * day));
|
||||
assertThat(reps.get(5).getTimestamp(), equalTo(today - 3 * day));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetByTimestamp()
|
||||
{
|
||||
Repetition rep = repetitions.getByTimestamp(today);
|
||||
assertThat(rep, is(not(nullValue())));
|
||||
assertThat(rep.getTimestamp(), equalTo(today));
|
||||
|
||||
rep = repetitions.getByTimestamp(today - 2 * day);
|
||||
assertThat(rep, is(nullValue()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetOldest()
|
||||
{
|
||||
Repetition rep = repetitions.getOldest();
|
||||
assertThat(rep, is(not(nullValue())));
|
||||
assertThat(rep.getTimestamp(), equalTo(today - 120 * day));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetOldest_withEmptyHabit()
|
||||
{
|
||||
Habit empty = fixtures.createEmptyHabit();
|
||||
Repetition rep = empty.getRepetitions().getOldest();
|
||||
assertThat(rep, is(nullValue()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemove()
|
||||
{
|
||||
RepetitionRecord record = getByTimestamp(today);
|
||||
assertThat(record, is(not(nullValue())));
|
||||
|
||||
Repetition rep = record.toRepetition();
|
||||
repetitions.remove(rep);
|
||||
|
||||
record = getByTimestamp(today);
|
||||
assertThat(record, is(nullValue()));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private RepetitionRecord getByTimestamp(long timestamp)
|
||||
{
|
||||
return selectByTimestamp(timestamp).executeSingle();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private From selectByTimestamp(long timestamp)
|
||||
{
|
||||
return new Select()
|
||||
.from(RepetitionRecord.class)
|
||||
.where("habit = ?", habit.getId())
|
||||
.and("timestamp = ?", timestamp);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.models.sqlite;
|
||||
|
||||
import android.support.test.runner.*;
|
||||
import android.test.suitebuilder.annotation.*;
|
||||
|
||||
import com.activeandroid.query.*;
|
||||
|
||||
import org.isoron.uhabits.*;
|
||||
import org.isoron.uhabits.models.*;
|
||||
import org.isoron.uhabits.models.sqlite.records.*;
|
||||
import org.isoron.uhabits.utils.*;
|
||||
import org.junit.*;
|
||||
import org.junit.runner.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static junit.framework.Assert.assertNotNull;
|
||||
import static junit.framework.Assert.assertNull;
|
||||
import static org.hamcrest.MatcherAssert.*;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
@SuppressWarnings("JavaDoc")
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@MediumTest
|
||||
public class SQLiteScoreListTest extends BaseAndroidTest
|
||||
{
|
||||
private Habit habit;
|
||||
|
||||
private ScoreList scores;
|
||||
|
||||
private long today;
|
||||
|
||||
private long day;
|
||||
|
||||
@Override
|
||||
public void setUp()
|
||||
{
|
||||
super.setUp();
|
||||
|
||||
habit = fixtures.createLongHabit();
|
||||
scores = habit.getScores();
|
||||
|
||||
today = DateUtils.getStartOfToday();
|
||||
day = DateUtils.millisecondsInOneDay;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAll()
|
||||
{
|
||||
List<Score> list = scores.getAll();
|
||||
assertThat(list.size(), equalTo(121));
|
||||
assertThat(list.get(0).getTimestamp(), equalTo(today));
|
||||
assertThat(list.get(10).getTimestamp(), equalTo(today - 10 * day));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidateNewerThan()
|
||||
{
|
||||
scores.getTodayValue(); // force recompute
|
||||
List<ScoreRecord> records = getAllRecords();
|
||||
assertThat(records.size(), equalTo(121));
|
||||
|
||||
scores.invalidateNewerThan(today - 10 * day);
|
||||
|
||||
records = getAllRecords();
|
||||
assertThat(records.size(), equalTo(110));
|
||||
assertThat(records.get(0).timestamp, equalTo(today - 11 * day));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAdd()
|
||||
{
|
||||
new Delete().from(ScoreRecord.class).execute();
|
||||
|
||||
List<Score> list = new LinkedList<>();
|
||||
list.add(new Score(today, 0));
|
||||
list.add(new Score(today - day, 0));
|
||||
list.add(new Score(today - 2 * day, 0));
|
||||
|
||||
scores.add(list);
|
||||
|
||||
List<ScoreRecord> records = getAllRecords();
|
||||
assertThat(records.size(), equalTo(3));
|
||||
assertThat(records.get(0).timestamp, equalTo(today));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetByTimestamp()
|
||||
{
|
||||
Score s = scores.getByTimestamp(today);
|
||||
assertNotNull(s);
|
||||
assertThat(s.getTimestamp(), equalTo(today));
|
||||
|
||||
s = scores.getByTimestamp(today - 200 * day);
|
||||
assertNull(s);
|
||||
}
|
||||
|
||||
private List<ScoreRecord> getAllRecords()
|
||||
{
|
||||
return new Select()
|
||||
.from(ScoreRecord.class)
|
||||
.where("habit = ?", habit.getId())
|
||||
.orderBy("timestamp desc")
|
||||
.execute();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.tasks;
|
||||
|
||||
import android.support.test.runner.*;
|
||||
import android.test.suitebuilder.annotation.*;
|
||||
|
||||
import org.isoron.uhabits.*;
|
||||
import org.isoron.uhabits.models.*;
|
||||
import org.junit.*;
|
||||
import org.junit.runner.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
import static junit.framework.Assert.*;
|
||||
import static org.hamcrest.MatcherAssert.*;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.hamcrest.core.IsNot.not;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class ExportCSVTaskTest extends BaseAndroidTest
|
||||
{
|
||||
@Before
|
||||
public void setUp()
|
||||
{
|
||||
super.setUp();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExportCSV() throws Throwable
|
||||
{
|
||||
fixtures.createShortHabit();
|
||||
List<Habit> habits = habitList.getAll(true);
|
||||
|
||||
ExportCSVTask task = new ExportCSVTask(habits, null);
|
||||
task.setListener(archiveFilename -> {
|
||||
assertThat(archiveFilename, is(not(nullValue())));
|
||||
|
||||
File f = new File(archiveFilename);
|
||||
assertTrue(f.exists());
|
||||
assertTrue(f.canRead());
|
||||
});
|
||||
|
||||
task.execute();
|
||||
waitForAsyncTasks();
|
||||
}
|
||||
}
|
||||
@@ -17,16 +17,12 @@
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.unit.tasks;
|
||||
package org.isoron.uhabits.tasks;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import org.isoron.uhabits.BaseTest;
|
||||
import org.isoron.uhabits.tasks.ExportDBTask;
|
||||
import org.isoron.uhabits.BaseAndroidTest;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -41,21 +37,18 @@ import static org.hamcrest.core.IsNot.not;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class ExportDBTaskTest extends BaseTest
|
||||
public class ExportDBTaskTest extends BaseAndroidTest
|
||||
{
|
||||
@Before
|
||||
public void setup()
|
||||
public void setUp()
|
||||
{
|
||||
super.setup();
|
||||
super.setUp();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExportCSV() throws Throwable
|
||||
{
|
||||
Context context = InstrumentationRegistry.getContext();
|
||||
|
||||
ProgressBar bar = new ProgressBar(context);
|
||||
ExportDBTask task = new ExportDBTask(bar);
|
||||
ExportDBTask task = new ExportDBTask(null);
|
||||
task.setListener(new ExportDBTask.Listener()
|
||||
{
|
||||
@Override
|
||||
@@ -17,16 +17,14 @@
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.unit.tasks;
|
||||
package org.isoron.uhabits.tasks;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import org.isoron.uhabits.BaseTest;
|
||||
import org.isoron.uhabits.helpers.DatabaseHelper;
|
||||
import org.isoron.uhabits.tasks.ImportDataTask;
|
||||
import org.isoron.uhabits.BaseAndroidTest;
|
||||
import org.isoron.uhabits.utils.FileUtils;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -41,23 +39,23 @@ import static org.junit.Assert.fail;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class ImportDataTaskTest extends BaseTest
|
||||
public class ImportDataTaskTest extends BaseAndroidTest
|
||||
{
|
||||
private File baseDir;
|
||||
|
||||
@Before
|
||||
public void setup()
|
||||
public void setUp()
|
||||
{
|
||||
super.setup();
|
||||
super.setUp();
|
||||
|
||||
baseDir = DatabaseHelper.getFilesDir("Backups");
|
||||
baseDir = FileUtils.getFilesDir("Backups");
|
||||
if(baseDir == null) fail("baseDir should not be null");
|
||||
}
|
||||
|
||||
private void copyAssetToFile(String assetPath, File dst) throws IOException
|
||||
{
|
||||
InputStream in = testContext.getAssets().open(assetPath);
|
||||
DatabaseHelper.copy(in, dst);
|
||||
FileUtils.copy(in, dst);
|
||||
}
|
||||
|
||||
private void assertTaskResult(final int expectedResult, String assetFilename) throws Throwable
|
||||
@@ -67,7 +65,7 @@ public class ImportDataTaskTest extends BaseTest
|
||||
task.setListener(new ImportDataTask.Listener()
|
||||
{
|
||||
@Override
|
||||
public void onImportFinished(int result)
|
||||
public void onImportDataFinished(int result)
|
||||
{
|
||||
assertThat(result, equalTo(expectedResult));
|
||||
}
|
||||
@@ -80,11 +78,9 @@ public class ImportDataTaskTest extends BaseTest
|
||||
@NonNull
|
||||
private ImportDataTask createTask(String assetFilename) throws IOException
|
||||
{
|
||||
ProgressBar bar = new ProgressBar(targetContext);
|
||||
File file = new File(String.format("%s/%s", baseDir.getPath(), assetFilename));
|
||||
copyAssetToFile(assetFilename, file);
|
||||
|
||||
return new ImportDataTask(file, bar);
|
||||
return new ImportDataTask(file, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1,225 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.ui;
|
||||
|
||||
import android.support.test.espresso.NoMatchingViewException;
|
||||
import android.support.test.espresso.contrib.RecyclerViewActions;
|
||||
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
import static android.support.test.espresso.Espresso.onData;
|
||||
import static android.support.test.espresso.Espresso.onView;
|
||||
import static android.support.test.espresso.Espresso.pressBack;
|
||||
import static android.support.test.espresso.action.ViewActions.click;
|
||||
import static android.support.test.espresso.action.ViewActions.longClick;
|
||||
import static android.support.test.espresso.action.ViewActions.replaceText;
|
||||
import static android.support.test.espresso.assertion.ViewAssertions.matches;
|
||||
import static android.support.test.espresso.matcher.RootMatchers.isPlatformPopup;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.Visibility.VISIBLE;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.withClassName;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.withContentDescription;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.withEffectiveVisibility;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.withId;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.withParent;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.withText;
|
||||
import static org.hamcrest.Matchers.allOf;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
import static org.isoron.uhabits.ui.HabitMatchers.containsHabit;
|
||||
import static org.isoron.uhabits.ui.HabitMatchers.withName;
|
||||
|
||||
public class MainActivityActions
|
||||
{
|
||||
public static String addHabit()
|
||||
{
|
||||
return addHabit(false);
|
||||
}
|
||||
|
||||
public static String addHabit(boolean openDialogs)
|
||||
{
|
||||
String name = "New Habit " + new Random().nextInt(1000000);
|
||||
String description = "Did you perform your new habit today?";
|
||||
String num = "4";
|
||||
String den = "8";
|
||||
|
||||
onView(withId(R.id.action_add))
|
||||
.perform(click());
|
||||
|
||||
typeHabitData(name, description, num, den);
|
||||
|
||||
if(openDialogs)
|
||||
{
|
||||
onView(withId(R.id.buttonPickColor))
|
||||
.perform(click());
|
||||
pressBack();
|
||||
onView(withId(R.id.inputReminderTime))
|
||||
.perform(click());
|
||||
onView(withText("Done"))
|
||||
.perform(click());
|
||||
onView(withId(R.id.inputReminderDays))
|
||||
.perform(click());
|
||||
onView(withText("OK"))
|
||||
.perform(click());
|
||||
}
|
||||
|
||||
onView(withId(R.id.buttonSave))
|
||||
.perform(click());
|
||||
|
||||
onData(allOf(is(instanceOf(Habit.class)), withName(name)))
|
||||
.onChildView(withId(R.id.label));
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
public static void typeHabitData(String name, String description, String num, String den)
|
||||
{
|
||||
onView(withId(R.id.input_name))
|
||||
.perform(replaceText(name));
|
||||
onView(withId(R.id.input_description))
|
||||
.perform(replaceText(description));
|
||||
|
||||
try
|
||||
{
|
||||
onView(allOf(withId(R.id.sFrequency), withEffectiveVisibility(VISIBLE)))
|
||||
.perform(click());
|
||||
onData(allOf(instanceOf(String.class), startsWith("Custom")))
|
||||
.inRoot(isPlatformPopup())
|
||||
.perform(click());
|
||||
}
|
||||
catch(NoMatchingViewException e)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
onView(withId(R.id.input_freq_num))
|
||||
.perform(replaceText(num));
|
||||
onView(withId(R.id.input_freq_den))
|
||||
.perform(replaceText(den));
|
||||
}
|
||||
|
||||
public static void selectHabit(String name)
|
||||
{
|
||||
selectHabits(Collections.singletonList(name));
|
||||
}
|
||||
|
||||
public static void selectHabits(List<String> names)
|
||||
{
|
||||
boolean first = true;
|
||||
for(String name : names)
|
||||
{
|
||||
onData(allOf(is(instanceOf(Habit.class)), withName(name)))
|
||||
.onChildView(withId(R.id.label))
|
||||
.perform(first ? longClick() : click());
|
||||
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void assertHabitsDontExist(List<String> names)
|
||||
{
|
||||
for(String name : names)
|
||||
onView(withId(R.id.listView))
|
||||
.check(matches(not(containsHabit(withName(name)))));
|
||||
}
|
||||
|
||||
public static void assertHabitExists(String name)
|
||||
{
|
||||
List<String> names = new LinkedList<>();
|
||||
names.add(name);
|
||||
assertHabitsExist(names);
|
||||
}
|
||||
|
||||
public static void assertHabitsExist(List<String> names)
|
||||
{
|
||||
for(String name : names)
|
||||
onData(allOf(is(instanceOf(Habit.class)), withName(name)))
|
||||
.check(matches(isDisplayed()));
|
||||
}
|
||||
|
||||
public static void deleteHabit(String name)
|
||||
{
|
||||
deleteHabits(Collections.singletonList(name));
|
||||
}
|
||||
|
||||
public static void deleteHabits(List<String> names)
|
||||
{
|
||||
selectHabits(names);
|
||||
clickMenuItem(R.string.delete);
|
||||
onView(withText("OK"))
|
||||
.perform(click());
|
||||
assertHabitsDontExist(names);
|
||||
}
|
||||
|
||||
public static void clickMenuItem(int stringId)
|
||||
{
|
||||
try
|
||||
{
|
||||
onView(withText(stringId)).perform(click());
|
||||
}
|
||||
catch (Exception e1)
|
||||
{
|
||||
try
|
||||
{
|
||||
onView(withContentDescription(stringId)).perform(click());
|
||||
}
|
||||
catch(Exception e2)
|
||||
{
|
||||
clickHiddenMenuItem(stringId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void clickHiddenMenuItem(int stringId)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Try the ActionMode overflow menu first
|
||||
onView(allOf(withContentDescription("More options"), withParent(withParent(
|
||||
withClassName(containsString("Action")))))).perform(click());
|
||||
}
|
||||
catch (Exception e1)
|
||||
{
|
||||
// Try the toolbar overflow menu
|
||||
onView(allOf(withContentDescription("More options"), withParent(withParent(
|
||||
withClassName(containsString("Toolbar")))))).perform(click());
|
||||
}
|
||||
|
||||
onView(withText(stringId)).perform(click());
|
||||
}
|
||||
|
||||
public static void clickSettingsItem(String text)
|
||||
{
|
||||
onView(withClassName(containsString("RecyclerView")))
|
||||
.perform(RecyclerViewActions.actionOnItem(
|
||||
hasDescendant(withText(containsString(text))),
|
||||
click()));
|
||||
}
|
||||
}
|
||||
@@ -1,346 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.ui;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Instrumentation;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.espresso.NoMatchingViewException;
|
||||
import android.support.test.espresso.intent.rule.IntentsTestRule;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.LargeTest;
|
||||
|
||||
import org.isoron.uhabits.MainActivity;
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.helpers.DateHelper;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
import static android.support.test.espresso.Espresso.onData;
|
||||
import static android.support.test.espresso.Espresso.onView;
|
||||
import static android.support.test.espresso.Espresso.pressBack;
|
||||
import static android.support.test.espresso.action.ViewActions.click;
|
||||
import static android.support.test.espresso.action.ViewActions.longClick;
|
||||
import static android.support.test.espresso.action.ViewActions.scrollTo;
|
||||
import static android.support.test.espresso.action.ViewActions.swipeLeft;
|
||||
import static android.support.test.espresso.action.ViewActions.swipeRight;
|
||||
import static android.support.test.espresso.action.ViewActions.swipeUp;
|
||||
import static android.support.test.espresso.assertion.ViewAssertions.matches;
|
||||
import static android.support.test.espresso.intent.Intents.intended;
|
||||
import static android.support.test.espresso.intent.Intents.intending;
|
||||
import static android.support.test.espresso.intent.matcher.IntentMatchers.hasAction;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.isRoot;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.withClassName;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.withId;
|
||||
import static org.hamcrest.Matchers.allOf;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.endsWith;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
import static org.isoron.uhabits.ui.HabitMatchers.withName;
|
||||
import static org.isoron.uhabits.ui.HabitViewActions.clickAtRandomLocations;
|
||||
import static org.isoron.uhabits.ui.HabitViewActions.toggleAllCheckmarks;
|
||||
import static org.isoron.uhabits.ui.MainActivityActions.addHabit;
|
||||
import static org.isoron.uhabits.ui.MainActivityActions.assertHabitExists;
|
||||
import static org.isoron.uhabits.ui.MainActivityActions.assertHabitsDontExist;
|
||||
import static org.isoron.uhabits.ui.MainActivityActions.assertHabitsExist;
|
||||
import static org.isoron.uhabits.ui.MainActivityActions.clickMenuItem;
|
||||
import static org.isoron.uhabits.ui.MainActivityActions.clickSettingsItem;
|
||||
import static org.isoron.uhabits.ui.MainActivityActions.deleteHabit;
|
||||
import static org.isoron.uhabits.ui.MainActivityActions.deleteHabits;
|
||||
import static org.isoron.uhabits.ui.MainActivityActions.selectHabit;
|
||||
import static org.isoron.uhabits.ui.MainActivityActions.selectHabits;
|
||||
import static org.isoron.uhabits.ui.MainActivityActions.typeHabitData;
|
||||
import static org.isoron.uhabits.ui.ShowHabitActivityActions.openHistoryEditor;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@LargeTest
|
||||
public class MainTest
|
||||
{
|
||||
private SystemHelper sys;
|
||||
|
||||
@Rule
|
||||
public IntentsTestRule<MainActivity> activityRule = new IntentsTestRule<>(
|
||||
MainActivity.class);
|
||||
|
||||
private Context targetContext;
|
||||
|
||||
@Before
|
||||
public void setup()
|
||||
{
|
||||
Context context = InstrumentationRegistry.getInstrumentation().getContext();
|
||||
sys = new SystemHelper(context);
|
||||
sys.disableAllAnimations();
|
||||
sys.acquireWakeLock();
|
||||
sys.unlockScreen();
|
||||
|
||||
targetContext = InstrumentationRegistry.getTargetContext();
|
||||
|
||||
Instrumentation.ActivityResult okResult = new Instrumentation.ActivityResult(
|
||||
Activity.RESULT_OK, new Intent());
|
||||
|
||||
intending(hasAction(equalTo(Intent.ACTION_SEND))).respondWith(okResult);
|
||||
intending(hasAction(equalTo(Intent.ACTION_SENDTO))).respondWith(okResult);
|
||||
intending(hasAction(equalTo(Intent.ACTION_VIEW))).respondWith(okResult);
|
||||
|
||||
skipTutorial();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown()
|
||||
{
|
||||
sys.releaseWakeLock();
|
||||
}
|
||||
|
||||
public void skipTutorial()
|
||||
{
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < 10; i++)
|
||||
onView(allOf(withClassName(endsWith("AppCompatImageButton")),
|
||||
isDisplayed())).perform(click());
|
||||
}
|
||||
catch (NoMatchingViewException e)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* User opens the app, creates some habits, selects them, archives them, select 'show archived'
|
||||
* on the menu, selects the previously archived habits and then deletes them.
|
||||
*/
|
||||
@Test
|
||||
public void testArchiveHabits()
|
||||
{
|
||||
List<String> names = new LinkedList<>();
|
||||
|
||||
for(int i = 0; i < 3; i++)
|
||||
names.add(addHabit());
|
||||
|
||||
selectHabits(names);
|
||||
|
||||
clickMenuItem(R.string.archive);
|
||||
assertHabitsDontExist(names);
|
||||
|
||||
clickMenuItem(R.string.show_archived);
|
||||
|
||||
assertHabitsExist(names);
|
||||
selectHabits(names);
|
||||
clickMenuItem(R.string.unarchive);
|
||||
|
||||
clickMenuItem(R.string.show_archived);
|
||||
|
||||
assertHabitsExist(names);
|
||||
deleteHabits(names);
|
||||
}
|
||||
|
||||
/**
|
||||
* User opens the app, clicks the add button, types some bogus information, tries to save,
|
||||
* dialog displays an error.
|
||||
*/
|
||||
@Test
|
||||
public void testAddInvalidHabit()
|
||||
{
|
||||
onView(withId(R.id.action_add))
|
||||
.perform(click());
|
||||
|
||||
typeHabitData("", "", "15", "7");
|
||||
|
||||
onView(withId(R.id.buttonSave)).perform(click());
|
||||
onView(withId(R.id.input_name)).check(matches(isDisplayed()));
|
||||
}
|
||||
|
||||
/**
|
||||
* User creates a habit, toggles a bunch of checkmarks, clicks the habit to open the statistics
|
||||
* screen, scrolls down to some views, then scrolls the views backwards and forwards in time.
|
||||
*/
|
||||
@Test
|
||||
public void testAddHabitAndViewStats() throws InterruptedException
|
||||
{
|
||||
String name = addHabit(true);
|
||||
|
||||
onData(allOf(is(instanceOf(Habit.class)), withName(name)))
|
||||
.onChildView(withId(R.id.llButtons))
|
||||
.perform(toggleAllCheckmarks());
|
||||
|
||||
Thread.sleep(1200);
|
||||
|
||||
onData(allOf(is(instanceOf(Habit.class)), withName(name)))
|
||||
.onChildView(withId(R.id.label))
|
||||
.perform(click());
|
||||
|
||||
onView(withId(R.id.scoreView))
|
||||
.perform(scrollTo(), swipeRight());
|
||||
|
||||
onView(withId(R.id.punchcardView))
|
||||
.perform(scrollTo(), swipeRight());
|
||||
}
|
||||
|
||||
/**
|
||||
* User creates a habit, selects the habit, clicks edit button, changes some information about
|
||||
* the habit, click save button, sees changes on the main window, selects habit again,
|
||||
* changes color, then deletes the habit.
|
||||
*/
|
||||
@Test
|
||||
public void testEditHabit()
|
||||
{
|
||||
String name = addHabit();
|
||||
|
||||
onData(allOf(is(instanceOf(Habit.class)), withName(name)))
|
||||
.onChildView(withId(R.id.label))
|
||||
.perform(longClick());
|
||||
|
||||
clickMenuItem(R.string.edit);
|
||||
|
||||
String modifiedName = "Modified " + new Random().nextInt(10000);
|
||||
typeHabitData(modifiedName, "", "1", "1");
|
||||
|
||||
onView(withId(R.id.buttonSave))
|
||||
.perform(click());
|
||||
|
||||
assertHabitExists(modifiedName);
|
||||
|
||||
selectHabit(modifiedName);
|
||||
clickMenuItem(R.string.color_picker_default_title);
|
||||
pressBack();
|
||||
|
||||
deleteHabit(modifiedName);
|
||||
}
|
||||
|
||||
/**
|
||||
* User creates a habit, opens statistics page, clicks button to edit history, adds some
|
||||
* checkmarks, closes dialog, sees the modified history calendar.
|
||||
*/
|
||||
@Test
|
||||
public void testEditHistory()
|
||||
{
|
||||
String name = addHabit();
|
||||
|
||||
onData(allOf(is(instanceOf(Habit.class)), withName(name)))
|
||||
.onChildView(withId(R.id.label))
|
||||
.perform(click());
|
||||
|
||||
openHistoryEditor();
|
||||
onView(withClassName(endsWith("HabitHistoryView")))
|
||||
.perform(clickAtRandomLocations(20));
|
||||
|
||||
pressBack();
|
||||
onView(withId(R.id.historyView))
|
||||
.perform(scrollTo(), swipeRight(), swipeLeft());
|
||||
}
|
||||
|
||||
/**
|
||||
* User opens menu, clicks settings, sees settings screen.
|
||||
*/
|
||||
@Test
|
||||
public void testSettings()
|
||||
{
|
||||
clickMenuItem(R.string.settings);
|
||||
}
|
||||
|
||||
/**
|
||||
* User opens menu, clicks about, sees about screen.
|
||||
*/
|
||||
@Test
|
||||
public void testAbout()
|
||||
{
|
||||
clickMenuItem(R.string.about);
|
||||
onView(isRoot()).perform(swipeUp());
|
||||
}
|
||||
|
||||
/**
|
||||
* User opens menu, clicks Help, sees website.
|
||||
*/
|
||||
@Test
|
||||
public void testHelp()
|
||||
{
|
||||
clickMenuItem(R.string.help);
|
||||
intended(hasAction(Intent.ACTION_VIEW));
|
||||
}
|
||||
|
||||
/**
|
||||
* User creates a habit, exports full backup, deletes the habit, restores backup, sees that the
|
||||
* previously created habit has appeared back.
|
||||
*/
|
||||
@Test
|
||||
public void testExportImportDB()
|
||||
{
|
||||
String name = addHabit();
|
||||
|
||||
clickMenuItem(R.string.settings);
|
||||
|
||||
String date = DateHelper.getBackupDateFormat().format(DateHelper.getLocalTime());
|
||||
date = date.substring(0, date.length() - 2);
|
||||
|
||||
clickSettingsItem("Export full backup");
|
||||
intended(hasAction(Intent.ACTION_SEND));
|
||||
|
||||
deleteHabit(name);
|
||||
|
||||
clickMenuItem(R.string.settings);
|
||||
clickSettingsItem("Import data");
|
||||
|
||||
onData(allOf(is(instanceOf(String.class)), startsWith("Backups")))
|
||||
.perform(click());
|
||||
|
||||
onData(allOf(is(instanceOf(String.class)), containsString(date)))
|
||||
.perform(click());
|
||||
|
||||
selectHabit(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* User creates a habit, opens settings, clicks export as CSV, is asked what activity should
|
||||
* handle the file.
|
||||
*/
|
||||
@Test
|
||||
public void testExportCSV()
|
||||
{
|
||||
addHabit();
|
||||
clickMenuItem(R.string.settings);
|
||||
clickSettingsItem("Export as CSV");
|
||||
intended(hasAction(Intent.ACTION_SEND));
|
||||
}
|
||||
|
||||
/**
|
||||
* User opens the settings and generates a bug report.
|
||||
*/
|
||||
@Test
|
||||
public void testGenerateBugReport()
|
||||
{
|
||||
clickMenuItem(R.string.settings);
|
||||
clickSettingsItem("Generate bug report");
|
||||
intended(hasAction(Intent.ACTION_SEND));
|
||||
}
|
||||
}
|
||||
@@ -17,56 +17,44 @@
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.unit.views;
|
||||
package org.isoron.uhabits.ui.common.views;
|
||||
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import android.support.test.runner.*;
|
||||
import android.test.suitebuilder.annotation.*;
|
||||
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.unit.HabitFixtures;
|
||||
import org.isoron.uhabits.views.HabitFrequencyView;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.isoron.uhabits.*;
|
||||
import org.isoron.uhabits.models.*;
|
||||
import org.isoron.uhabits.utils.*;
|
||||
import org.junit.*;
|
||||
import org.junit.runner.*;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class HabitFrequencyViewTest extends ViewTest
|
||||
@MediumTest
|
||||
public class FrequencyChartTest extends BaseViewTest
|
||||
{
|
||||
private HabitFrequencyView view;
|
||||
public static final String BASE_PATH = "common/FrequencyChart/";
|
||||
|
||||
private FrequencyChart view;
|
||||
|
||||
@Override
|
||||
@Before
|
||||
public void setup()
|
||||
public void setUp()
|
||||
{
|
||||
super.setup();
|
||||
super.setUp();
|
||||
|
||||
HabitFixtures.purgeHabits();
|
||||
Habit habit = HabitFixtures.createLongHabit();
|
||||
fixtures.purgeHabits(habitList);
|
||||
Habit habit = fixtures.createLongHabit();
|
||||
|
||||
view = new HabitFrequencyView(targetContext);
|
||||
view.setHabit(habit);
|
||||
refreshData(view);
|
||||
measureView(dpToPixels(300), dpToPixels(100), view);
|
||||
view = new FrequencyChart(targetContext);
|
||||
view.setFrequency(habit.getRepetitions().getWeekdayFrequency());
|
||||
view.setColor(ColorUtils.getAndroidTestColor(habit.getColor()));
|
||||
measureView(view, dpToPixels(300), dpToPixels(100));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRender() throws Throwable
|
||||
{
|
||||
assertRenders(view, "HabitFrequencyView/render.png");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRender_withTransparentBackground() throws Throwable
|
||||
{
|
||||
view.setIsBackgroundTransparent(true);
|
||||
assertRenders(view, "HabitFrequencyView/renderTransparent.png");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRender_withDifferentSize() throws Throwable
|
||||
{
|
||||
measureView(dpToPixels(200), dpToPixels(200), view);
|
||||
assertRenders(view, "HabitFrequencyView/renderDifferentSize.png");
|
||||
assertRenders(view, BASE_PATH + "render.png");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -75,6 +63,20 @@ public class HabitFrequencyViewTest extends ViewTest
|
||||
view.onScroll(null, null, -dpToPixels(150), 0);
|
||||
view.invalidate();
|
||||
|
||||
assertRenders(view, "HabitFrequencyView/renderDataOffset.png");
|
||||
assertRenders(view, BASE_PATH + "renderDataOffset.png");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRender_withDifferentSize() throws Throwable
|
||||
{
|
||||
measureView(view, dpToPixels(200), dpToPixels(200));
|
||||
assertRenders(view, BASE_PATH + "renderDifferentSize.png");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRender_withTransparentBackground() throws Throwable
|
||||
{
|
||||
view.setIsBackgroundTransparent(true);
|
||||
assertRenders(view, BASE_PATH + "renderTransparent.png");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.ui.common.views;
|
||||
|
||||
import android.support.test.runner.*;
|
||||
import android.test.suitebuilder.annotation.*;
|
||||
|
||||
import org.isoron.uhabits.*;
|
||||
import org.isoron.uhabits.models.*;
|
||||
import org.isoron.uhabits.utils.*;
|
||||
import org.junit.*;
|
||||
import org.junit.runner.*;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@MediumTest
|
||||
public class HistoryChartTest extends BaseViewTest
|
||||
{
|
||||
private static final String BASE_PATH = "common/HistoryChart/";
|
||||
|
||||
private HistoryChart chart;
|
||||
|
||||
@Override
|
||||
@Before
|
||||
public void setUp()
|
||||
{
|
||||
super.setUp();
|
||||
|
||||
fixtures.purgeHabits(habitList);
|
||||
Habit habit = fixtures.createLongHabit();
|
||||
|
||||
chart = new HistoryChart(targetContext);
|
||||
chart.setCheckmarks(habit.getCheckmarks().getAllValues());
|
||||
chart.setColor(ColorUtils.getAndroidTestColor(habit.getColor()));
|
||||
measureView(chart, dpToPixels(400), dpToPixels(200));
|
||||
}
|
||||
|
||||
// @Test
|
||||
// public void tapDate_atInvalidLocations() throws Throwable
|
||||
// {
|
||||
// int expectedCheckmarkValues[] = habit.getCheckmarks().getAllValues();
|
||||
//
|
||||
// chart.setIsEditable(true);
|
||||
// tap(chart, 118, 13); // header
|
||||
// tap(chart, 336, 60); // tomorrow's square
|
||||
// tap(chart, 370, 60); // right axis
|
||||
// waitForAsyncTasks();
|
||||
//
|
||||
// int actualCheckmarkValues[] = habit.getCheckmarks().getAllValues();
|
||||
// assertThat(actualCheckmarkValues, equalTo(expectedCheckmarkValues));
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// public void tapDate_withEditableView() throws Throwable
|
||||
// {
|
||||
// chart.setIsEditable(true);
|
||||
// tap(chart, 340, 40); // today's square
|
||||
// waitForAsyncTasks();
|
||||
//
|
||||
// long today = DateUtils.getStartOfToday();
|
||||
// assertFalse(habit.getRepetitions().containsTimestamp(today));
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// public void tapDate_withReadOnlyView() throws Throwable
|
||||
// {
|
||||
// chart.setIsEditable(false);
|
||||
// tap(chart, 340, 40); // today's square
|
||||
// waitForAsyncTasks();
|
||||
//
|
||||
// long today = DateUtils.getStartOfToday();
|
||||
// assertTrue(habit.getRepetitions().containsTimestamp(today));
|
||||
// }
|
||||
|
||||
@Test
|
||||
public void testRender() throws Throwable
|
||||
{
|
||||
assertRenders(chart, BASE_PATH + "render.png");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRender_withDataOffset() throws Throwable
|
||||
{
|
||||
chart.onScroll(null, null, -dpToPixels(150), 0);
|
||||
chart.invalidate();
|
||||
|
||||
assertRenders(chart, BASE_PATH + "renderDataOffset.png");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRender_withDifferentSize() throws Throwable
|
||||
{
|
||||
measureView(chart, dpToPixels(200), dpToPixels(200));
|
||||
assertRenders(chart, BASE_PATH + "renderDifferentSize.png");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRender_withTransparentBackground() throws Throwable
|
||||
{
|
||||
chart.setIsBackgroundTransparent(true);
|
||||
assertRenders(chart, BASE_PATH + "renderTransparent.png");
|
||||
}
|
||||
}
|
||||
@@ -17,35 +17,37 @@
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.unit.views;
|
||||
package org.isoron.uhabits.ui.common.views;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import android.graphics.*;
|
||||
import android.support.test.runner.*;
|
||||
import android.test.suitebuilder.annotation.*;
|
||||
|
||||
import org.isoron.uhabits.helpers.ColorHelper;
|
||||
import org.isoron.uhabits.views.RingView;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.isoron.uhabits.*;
|
||||
import org.isoron.uhabits.utils.*;
|
||||
import org.junit.*;
|
||||
import org.junit.runner.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.*;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class RingViewTest extends ViewTest
|
||||
@MediumTest
|
||||
public class RingViewTest extends BaseViewTest
|
||||
{
|
||||
private static final String BASE_PATH = "common/RingView/";
|
||||
|
||||
private RingView view;
|
||||
|
||||
@Override
|
||||
@Before
|
||||
public void setup()
|
||||
public void setUp()
|
||||
{
|
||||
super.setup();
|
||||
super.setUp();
|
||||
|
||||
view = new RingView(targetContext);
|
||||
view.setPercentage(0.6f);
|
||||
view.setText("60%");
|
||||
view.setColor(ColorHelper.CSV_PALETTE[0]);
|
||||
view.setColor(ColorUtils.getAndroidTestColor(0));
|
||||
view.setBackgroundColor(Color.WHITE);
|
||||
view.setThickness(dpToPixels(3));
|
||||
}
|
||||
@@ -53,17 +55,17 @@ public class RingViewTest extends ViewTest
|
||||
@Test
|
||||
public void testRender_base() throws IOException
|
||||
{
|
||||
measureView(dpToPixels(100), dpToPixels(100), view);
|
||||
assertRenders(view, "RingView/render.png");
|
||||
measureView(view, dpToPixels(100), dpToPixels(100));
|
||||
assertRenders(view, BASE_PATH + "render.png");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRender_withDifferentParams() throws IOException
|
||||
{
|
||||
view.setPercentage(0.25f);
|
||||
view.setColor(ColorHelper.CSV_PALETTE[5]);
|
||||
view.setColor(ColorUtils.getAndroidTestColor(5));
|
||||
|
||||
measureView(dpToPixels(200), dpToPixels(200), view);
|
||||
assertRenders(view, "RingView/renderDifferentParams.png");
|
||||
measureView(view, dpToPixels(200), dpToPixels(200));
|
||||
assertRenders(view, BASE_PATH + "renderDifferentParams.png");
|
||||
}
|
||||
}
|
||||
@@ -17,60 +17,50 @@
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.unit.views;
|
||||
package org.isoron.uhabits.ui.common.views;
|
||||
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import android.util.Log;
|
||||
import android.support.test.runner.*;
|
||||
import android.test.suitebuilder.annotation.*;
|
||||
import android.util.*;
|
||||
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.unit.HabitFixtures;
|
||||
import org.isoron.uhabits.views.HabitScoreView;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.isoron.uhabits.*;
|
||||
import org.isoron.uhabits.models.*;
|
||||
import org.isoron.uhabits.utils.*;
|
||||
import org.junit.*;
|
||||
import org.junit.runner.*;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class HabitScoreViewTest extends ViewTest
|
||||
@MediumTest
|
||||
public class ScoreChartTest extends BaseViewTest
|
||||
{
|
||||
private static final String BASE_PATH = "common/ScoreChart/";
|
||||
|
||||
private Habit habit;
|
||||
private HabitScoreView view;
|
||||
|
||||
private ScoreChart view;
|
||||
|
||||
@Override
|
||||
@Before
|
||||
public void setup()
|
||||
public void setUp()
|
||||
{
|
||||
super.setup();
|
||||
super.setUp();
|
||||
|
||||
HabitFixtures.purgeHabits();
|
||||
habit = HabitFixtures.createLongHabit();
|
||||
fixtures.purgeHabits(habitList);
|
||||
habit = fixtures.createLongHabit();
|
||||
|
||||
view = new HabitScoreView(targetContext);
|
||||
view.setHabit(habit);
|
||||
view = new ScoreChart(targetContext);
|
||||
view.setScores(habit.getScores().getAll());
|
||||
view.setColor(ColorUtils.getColor(targetContext, habit.getColor()));
|
||||
view.setBucketSize(7);
|
||||
refreshData(view);
|
||||
measureView(dpToPixels(300), dpToPixels(200), view);
|
||||
measureView(view, dpToPixels(300), dpToPixels(200));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRender() throws Throwable
|
||||
{
|
||||
Log.d("HabitScoreViewTest", String.format("height=%d", dpToPixels(100)));
|
||||
assertRenders(view, "HabitScoreView/render.png");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRender_withTransparentBackground() throws Throwable
|
||||
{
|
||||
view.setIsTransparencyEnabled(true);
|
||||
assertRenders(view, "HabitScoreView/renderTransparent.png");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRender_withDifferentSize() throws Throwable
|
||||
{
|
||||
measureView(dpToPixels(200), dpToPixels(200), view);
|
||||
assertRenders(view, "HabitScoreView/renderDifferentSize.png");
|
||||
Log.d("HabitScoreViewTest",
|
||||
String.format("height=%d", dpToPixels(100)));
|
||||
assertRenders(view, BASE_PATH + "render.png");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -79,26 +69,40 @@ public class HabitScoreViewTest extends ViewTest
|
||||
view.onScroll(null, null, -dpToPixels(150), 0);
|
||||
view.invalidate();
|
||||
|
||||
assertRenders(view, "HabitScoreView/renderDataOffset.png");
|
||||
assertRenders(view, BASE_PATH + "renderDataOffset.png");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRender_withDifferentSize() throws Throwable
|
||||
{
|
||||
measureView(view, dpToPixels(200), dpToPixels(200));
|
||||
assertRenders(view, BASE_PATH + "renderDifferentSize.png");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRender_withMonthlyBucket() throws Throwable
|
||||
{
|
||||
view.setScores(habit.getScores().groupBy(DateUtils.TruncateField.MONTH));
|
||||
view.setBucketSize(30);
|
||||
view.refreshData();
|
||||
view.invalidate();
|
||||
|
||||
assertRenders(view, "HabitScoreView/renderMonthly.png");
|
||||
assertRenders(view, BASE_PATH + "renderMonthly.png");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRender_withTransparentBackground() throws Throwable
|
||||
{
|
||||
view.setIsTransparencyEnabled(true);
|
||||
assertRenders(view, BASE_PATH + "renderTransparent.png");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRender_withYearlyBucket() throws Throwable
|
||||
{
|
||||
view.setScores(habit.getScores().groupBy(DateUtils.TruncateField.YEAR));
|
||||
view.setBucketSize(365);
|
||||
view.refreshData();
|
||||
view.invalidate();
|
||||
|
||||
assertRenders(view, "HabitScoreView/renderYearly.png");
|
||||
assertRenders(view, BASE_PATH + "renderYearly.png");
|
||||
}
|
||||
}
|
||||
@@ -17,58 +17,57 @@
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.unit.views;
|
||||
package org.isoron.uhabits.ui.common.views;
|
||||
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import android.support.test.runner.*;
|
||||
import android.test.suitebuilder.annotation.*;
|
||||
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.unit.HabitFixtures;
|
||||
import org.isoron.uhabits.views.HabitStreakView;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.isoron.uhabits.*;
|
||||
import org.isoron.uhabits.models.*;
|
||||
import org.isoron.uhabits.utils.*;
|
||||
import org.junit.*;
|
||||
import org.junit.runner.*;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class HabitStreakViewTest extends ViewTest
|
||||
@MediumTest
|
||||
public class StreakChartTest extends BaseViewTest
|
||||
{
|
||||
private HabitStreakView view;
|
||||
private static final String BASE_PATH = "common/StreakChart/";
|
||||
|
||||
private StreakChart view;
|
||||
|
||||
@Override
|
||||
@Before
|
||||
public void setup()
|
||||
public void setUp()
|
||||
{
|
||||
super.setup();
|
||||
super.setUp();
|
||||
|
||||
HabitFixtures.purgeHabits();
|
||||
Habit habit = HabitFixtures.createLongHabit();
|
||||
fixtures.purgeHabits(habitList);
|
||||
Habit habit = fixtures.createLongHabit();
|
||||
|
||||
view = new HabitStreakView(targetContext);
|
||||
measureView(dpToPixels(300), dpToPixels(100), view);
|
||||
|
||||
view.setHabit(habit);
|
||||
refreshData(view);
|
||||
view = new StreakChart(targetContext);
|
||||
view.setColor(ColorUtils.getAndroidTestColor(habit.getColor()));
|
||||
view.setStreaks(habit.getStreaks().getBest(5));
|
||||
measureView(view, dpToPixels(300), dpToPixels(100));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRender() throws Throwable
|
||||
{
|
||||
assertRenders(view, "HabitStreakView/render.png");
|
||||
assertRenders(view, BASE_PATH + "render.png");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRender_withSmallSize() throws Throwable
|
||||
{
|
||||
measureView(view, dpToPixels(100), dpToPixels(100));
|
||||
assertRenders(view, BASE_PATH + "renderSmallSize.png");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRender_withTransparentBackground() throws Throwable
|
||||
{
|
||||
view.setIsBackgroundTransparent(true);
|
||||
assertRenders(view, "HabitStreakView/renderTransparent.png");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRender_withSmallSize() throws Throwable
|
||||
{
|
||||
measureView(dpToPixels(100), dpToPixels(100), view);
|
||||
refreshData(view);
|
||||
|
||||
assertRenders(view, "HabitStreakView/renderSmallSize.png");
|
||||
assertRenders(view, BASE_PATH + "renderTransparent.png");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.ui.habits.list.views;
|
||||
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.*;
|
||||
|
||||
import org.isoron.uhabits.models.Checkmark;
|
||||
import org.isoron.uhabits.BaseViewTest;
|
||||
import org.isoron.uhabits.utils.ColorUtils;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@MediumTest
|
||||
public class CheckmarkButtonViewTest extends BaseViewTest
|
||||
{
|
||||
public static final String PATH = "habits/list/CheckmarkButtonView/";
|
||||
|
||||
private CountDownLatch latch;
|
||||
|
||||
private CheckmarkButtonView view;
|
||||
|
||||
@Override
|
||||
@Before
|
||||
public void setUp()
|
||||
{
|
||||
super.setUp();
|
||||
setSimilarityCutoff(0.03f);
|
||||
|
||||
latch = new CountDownLatch(1);
|
||||
view = new CheckmarkButtonView(targetContext);
|
||||
view.setValue(Checkmark.UNCHECKED);
|
||||
view.setColor(ColorUtils.getAndroidTestColor(7));
|
||||
|
||||
measureView(view, dpToPixels(40), dpToPixels(40));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRender_explicitCheck() throws Exception
|
||||
{
|
||||
view.setValue(Checkmark.CHECKED_EXPLICITLY);
|
||||
assertRendersCheckedExplicitly();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRender_implicitCheck() throws Exception
|
||||
{
|
||||
view.setValue(Checkmark.CHECKED_IMPLICITLY);
|
||||
assertRendersCheckedImplicitly();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRender_unchecked() throws Exception
|
||||
{
|
||||
view.setValue(Checkmark.UNCHECKED);
|
||||
assertRendersUnchecked();
|
||||
}
|
||||
|
||||
protected void assertRendersCheckedExplicitly() throws IOException
|
||||
{
|
||||
assertRenders(view, PATH + "render_explicit_check.png");
|
||||
}
|
||||
|
||||
protected void assertRendersCheckedImplicitly() throws IOException
|
||||
{
|
||||
assertRenders(view, PATH + "render_implicit_check.png");
|
||||
}
|
||||
|
||||
protected void assertRendersUnchecked() throws IOException
|
||||
{
|
||||
assertRenders(view, PATH + "render_unchecked.png");
|
||||
}
|
||||
|
||||
// @Test
|
||||
// public void testLongClick() throws Exception
|
||||
// {
|
||||
// setOnToggleListener();
|
||||
// view.performLongClick();
|
||||
// waitForLatch();
|
||||
// assertRendersCheckedExplicitly();
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// public void testClick_withShortToggle_fromUnchecked() throws Exception
|
||||
// {
|
||||
// Preferences.getInstance().setShortToggleEnabled(true);
|
||||
// view.setValue(Checkmark.UNCHECKED);
|
||||
// setOnToggleListenerAndPerformClick();
|
||||
// assertRendersCheckedExplicitly();
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// public void testClick_withShortToggle_fromChecked() throws Exception
|
||||
// {
|
||||
// Preferences.getInstance().setShortToggleEnabled(true);
|
||||
// view.setValue(Checkmark.CHECKED_EXPLICITLY);
|
||||
// setOnToggleListenerAndPerformClick();
|
||||
// assertRendersUnchecked();
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// public void testClick_withShortToggle_withoutListener() throws Exception
|
||||
// {
|
||||
// Preferences.getInstance().setShortToggleEnabled(true);
|
||||
// view.setValue(Checkmark.CHECKED_EXPLICITLY);
|
||||
// view.setController(null);
|
||||
// view.performClick();
|
||||
// assertRendersUnchecked();
|
||||
// }
|
||||
//
|
||||
// protected void setOnToggleListenerAndPerformClick() throws InterruptedException
|
||||
// {
|
||||
// setOnToggleListener();
|
||||
// view.performClick();
|
||||
// waitForLatch();
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// public void testClick_withoutShortToggle() throws Exception
|
||||
// {
|
||||
// Preferences.getInstance().setShortToggleEnabled(false);
|
||||
// setOnInvalidToggleListener();
|
||||
// view.performClick();
|
||||
// waitForLatch();
|
||||
// assertRendersUnchecked();
|
||||
// }
|
||||
|
||||
// protected void setOnInvalidToggleListener()
|
||||
// {
|
||||
// view.setController(new CheckmarkButtonView.Controller()
|
||||
// {
|
||||
// @Override
|
||||
// public void onToggleCheckmark(CheckmarkButtonView view, long timestamp)
|
||||
// {
|
||||
// fail();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onInvalidToggle(CheckmarkButtonView v)
|
||||
// {
|
||||
// assertThat(v, equalTo(view));
|
||||
// latch.countDown();
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
|
||||
// protected void setOnToggleListener()
|
||||
// {
|
||||
// view.setController(new CheckmarkButtonView.Controller()
|
||||
// {
|
||||
// @Override
|
||||
// public void onToggleCheckmark(CheckmarkButtonView v, long t)
|
||||
// {
|
||||
// assertThat(v, equalTo(view));
|
||||
// assertThat(t, equalTo(DateUtils.getStartOfToday()));
|
||||
// latch.countDown();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onInvalidToggle(CheckmarkButtonView view)
|
||||
// {
|
||||
// fail();
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.ui.habits.list.views;
|
||||
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.*;
|
||||
|
||||
import org.isoron.uhabits.models.Checkmark;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.BaseViewTest;
|
||||
import org.isoron.uhabits.utils.ColorUtils;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@MediumTest
|
||||
public class CheckmarkPanelViewTest extends BaseViewTest
|
||||
{
|
||||
public static final String PATH = "habits/list/CheckmarkPanelView/";
|
||||
|
||||
private CountDownLatch latch;
|
||||
private CheckmarkPanelView view;
|
||||
private int checkmarks[];
|
||||
|
||||
@Override
|
||||
@Before
|
||||
public void setUp()
|
||||
{
|
||||
super.setUp();
|
||||
setSimilarityCutoff(0.03f);
|
||||
prefs.setShouldReverseCheckmarks(false);
|
||||
|
||||
Habit habit = new Habit();
|
||||
|
||||
latch = new CountDownLatch(1);
|
||||
checkmarks = new int[]{
|
||||
Checkmark.CHECKED_EXPLICITLY, Checkmark.UNCHECKED,
|
||||
Checkmark.CHECKED_IMPLICITLY, Checkmark.CHECKED_EXPLICITLY};
|
||||
|
||||
view = new CheckmarkPanelView(targetContext);
|
||||
view.setHabit(habit);
|
||||
view.setCheckmarkValues(checkmarks);
|
||||
view.setColor(ColorUtils.getAndroidTestColor(7));
|
||||
|
||||
measureView(view, dpToPixels(200), dpToPixels(200));
|
||||
}
|
||||
|
||||
// protected void waitForLatch() throws InterruptedException
|
||||
// {
|
||||
// assertTrue("Latch timeout", latch.await(1, TimeUnit.SECONDS));
|
||||
// }
|
||||
|
||||
@Test
|
||||
public void testRender() throws Exception
|
||||
{
|
||||
assertRenders(view, PATH + "render.png");
|
||||
}
|
||||
|
||||
// @Test
|
||||
// public void testToggleCheckmark_withLeftToRight() throws Exception
|
||||
// {
|
||||
// setToggleListener();
|
||||
// view.getButton(1).performToggle();
|
||||
// waitForLatch();
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// public void testToggleCheckmark_withReverseCheckmarks() throws Exception
|
||||
// {
|
||||
// prefs.setShouldReverseCheckmarks(true);
|
||||
// view.setCheckmarkValues(checkmarks); // refresh after preference change
|
||||
//
|
||||
// setToggleListener();
|
||||
// view.getButton(2).performToggle();
|
||||
// waitForLatch();
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.ui.habits.list.views;
|
||||
|
||||
import android.support.test.runner.*;
|
||||
import android.test.suitebuilder.annotation.*;
|
||||
|
||||
import org.isoron.uhabits.*;
|
||||
import org.isoron.uhabits.ui.habits.list.model.*;
|
||||
import org.junit.*;
|
||||
import org.junit.runner.*;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.*;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@MediumTest
|
||||
public class HintViewTest extends BaseViewTest
|
||||
{
|
||||
public static final String PATH = "habits/list/HintView/";
|
||||
|
||||
private HintView view;
|
||||
|
||||
private HintList list;
|
||||
|
||||
@Before
|
||||
@Override
|
||||
public void setUp()
|
||||
{
|
||||
super.setUp();
|
||||
|
||||
view = new HintView(targetContext);
|
||||
list = mock(HintList.class);
|
||||
view.setHints(list);
|
||||
measureView(view, 400, 200);
|
||||
|
||||
String text =
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit.";
|
||||
|
||||
when(list.shouldShow()).thenReturn(true);
|
||||
when(list.pop()).thenReturn(text);
|
||||
|
||||
view.showNext();
|
||||
skipAnimation(view);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRender() throws Exception
|
||||
{
|
||||
assertRenders(view, PATH + "render.png");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClick() throws Exception
|
||||
{
|
||||
assertThat(view.getAlpha(), equalTo(1f));
|
||||
view.performClick();
|
||||
skipAnimation(view);
|
||||
assertThat(view.getAlpha(), equalTo(0f));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.ui.widgets;
|
||||
|
||||
import android.support.test.runner.*;
|
||||
import android.test.suitebuilder.annotation.*;
|
||||
import android.widget.*;
|
||||
|
||||
import org.isoron.uhabits.*;
|
||||
import org.isoron.uhabits.models.*;
|
||||
import org.isoron.uhabits.widgets.*;
|
||||
import org.junit.*;
|
||||
import org.junit.runner.*;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.*;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.isoron.uhabits.models.Checkmark.*;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@MediumTest
|
||||
public class CheckmarkWidgetTest extends BaseViewTest
|
||||
{
|
||||
|
||||
private static final String PATH = "widgets/CheckmarkWidgetView/";
|
||||
|
||||
private Habit habit;
|
||||
|
||||
private CheckmarkList checkmarks;
|
||||
|
||||
private FrameLayout view;
|
||||
|
||||
@Override
|
||||
public void setUp()
|
||||
{
|
||||
super.setUp();
|
||||
habit = fixtures.createShortHabit();
|
||||
checkmarks = habit.getCheckmarks();
|
||||
CheckmarkWidget widget = new CheckmarkWidget(targetContext, 0, habit);
|
||||
view = convertToView(widget, 200, 250);
|
||||
|
||||
assertThat(checkmarks.getTodayValue(), equalTo(CHECKED_EXPLICITLY));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClick() throws Exception
|
||||
{
|
||||
Button button = (Button) view.findViewById(R.id.button);
|
||||
assertThat(button, is(not(nullValue())));
|
||||
|
||||
// A better test would be to capture the intent, but it doesn't seem
|
||||
// possible to capture intents sent to BroadcastReceivers.
|
||||
button.performClick();
|
||||
sleep(1000);
|
||||
|
||||
assertThat(checkmarks.getTodayValue(), equalTo(UNCHECKED));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsInstalled()
|
||||
{
|
||||
assertWidgetProviderIsInstalled(CheckmarkWidgetProvider.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRender() throws Exception
|
||||
{
|
||||
assertRenders(view, PATH + "checked.png");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.ui.widgets;
|
||||
|
||||
import android.support.test.runner.*;
|
||||
import android.test.suitebuilder.annotation.*;
|
||||
import android.widget.*;
|
||||
|
||||
import org.isoron.uhabits.*;
|
||||
import org.isoron.uhabits.models.*;
|
||||
import org.isoron.uhabits.widgets.*;
|
||||
import org.junit.*;
|
||||
import org.junit.runner.*;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@MediumTest
|
||||
public class FrequencyWidgetTest extends BaseViewTest
|
||||
{
|
||||
private static final String PATH = "widgets/FrequencyWidget/";
|
||||
|
||||
private Habit habit;
|
||||
|
||||
private FrameLayout view;
|
||||
|
||||
@Override
|
||||
public void setUp()
|
||||
{
|
||||
super.setUp();
|
||||
setTheme(R.style.TransparentWidgetTheme);
|
||||
|
||||
habit = fixtures.createLongHabit();
|
||||
FrequencyWidget widget = new FrequencyWidget(targetContext, 0, habit);
|
||||
view = convertToView(widget, 400, 400);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsInstalled()
|
||||
{
|
||||
assertWidgetProviderIsInstalled(FrequencyWidgetProvider.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRender() throws Exception
|
||||
{
|
||||
assertRenders(view, PATH + "render.png");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.ui.widgets;
|
||||
|
||||
import android.support.test.runner.*;
|
||||
import android.test.suitebuilder.annotation.*;
|
||||
import android.widget.*;
|
||||
|
||||
import org.isoron.uhabits.*;
|
||||
import org.isoron.uhabits.models.*;
|
||||
import org.isoron.uhabits.widgets.*;
|
||||
import org.junit.*;
|
||||
import org.junit.runner.*;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@MediumTest
|
||||
public class HistoryWidgetTest extends BaseViewTest
|
||||
{
|
||||
private static final String PATH = "widgets/HistoryWidget/";
|
||||
|
||||
private Habit habit;
|
||||
|
||||
private FrameLayout view;
|
||||
|
||||
@Override
|
||||
public void setUp()
|
||||
{
|
||||
super.setUp();
|
||||
setTheme(R.style.TransparentWidgetTheme);
|
||||
|
||||
habit = fixtures.createLongHabit();
|
||||
HistoryWidget widget = new HistoryWidget(targetContext, 0, habit);
|
||||
view = convertToView(widget, 400, 400);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsInstalled()
|
||||
{
|
||||
assertWidgetProviderIsInstalled(HistoryWidgetProvider.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRender() throws Exception
|
||||
{
|
||||
assertRenders(view, PATH + "render.png");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.ui.widgets;
|
||||
|
||||
import android.support.test.runner.*;
|
||||
import android.test.suitebuilder.annotation.*;
|
||||
import android.widget.*;
|
||||
|
||||
import org.isoron.uhabits.*;
|
||||
import org.isoron.uhabits.models.*;
|
||||
import org.isoron.uhabits.widgets.*;
|
||||
import org.junit.*;
|
||||
import org.junit.runner.*;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@MediumTest
|
||||
public class ScoreWidgetTest extends BaseViewTest
|
||||
{
|
||||
private static final String PATH = "widgets/ScoreWidget/";
|
||||
|
||||
private Habit habit;
|
||||
|
||||
private FrameLayout view;
|
||||
|
||||
@Override
|
||||
public void setUp()
|
||||
{
|
||||
super.setUp();
|
||||
setTheme(R.style.TransparentWidgetTheme);
|
||||
|
||||
habit = fixtures.createLongHabit();
|
||||
ScoreWidget widget = new ScoreWidget(targetContext, 0, habit);
|
||||
view = convertToView(widget, 400, 400);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsInstalled()
|
||||
{
|
||||
assertWidgetProviderIsInstalled(ScoreWidgetProvider.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRender() throws Exception
|
||||
{
|
||||
assertRenders(view, PATH + "render.png");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.ui.widgets;
|
||||
|
||||
import android.support.test.runner.*;
|
||||
import android.test.suitebuilder.annotation.*;
|
||||
import android.widget.*;
|
||||
|
||||
import org.isoron.uhabits.*;
|
||||
import org.isoron.uhabits.models.*;
|
||||
import org.isoron.uhabits.widgets.*;
|
||||
import org.junit.*;
|
||||
import org.junit.runner.*;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@MediumTest
|
||||
public class StreakWidgetTest extends BaseViewTest
|
||||
{
|
||||
private static final String PATH = "widgets/StreakWidget/";
|
||||
|
||||
private Habit habit;
|
||||
|
||||
private FrameLayout view;
|
||||
|
||||
@Override
|
||||
public void setUp()
|
||||
{
|
||||
super.setUp();
|
||||
setTheme(R.style.TransparentWidgetTheme);
|
||||
|
||||
habit = fixtures.createLongHabit();
|
||||
StreakWidget widget = new StreakWidget(targetContext, 0, habit);
|
||||
view = convertToView(widget, 400, 400);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsInstalled()
|
||||
{
|
||||
assertWidgetProviderIsInstalled(StreakWidgetProvider.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRender() throws Exception
|
||||
{
|
||||
assertRenders(view, PATH + "render.png");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.ui.widgets.views;
|
||||
|
||||
import android.support.test.runner.*;
|
||||
import android.test.suitebuilder.annotation.*;
|
||||
|
||||
import org.isoron.uhabits.*;
|
||||
import org.isoron.uhabits.models.*;
|
||||
import org.isoron.uhabits.utils.*;
|
||||
import org.junit.*;
|
||||
import org.junit.runner.*;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@MediumTest
|
||||
public class CheckmarkWidgetViewTest extends BaseViewTest
|
||||
{
|
||||
private static final String PATH = "widgets/CheckmarkWidgetView/";
|
||||
|
||||
private CheckmarkWidgetView view;
|
||||
|
||||
private Habit habit;
|
||||
|
||||
@Override
|
||||
@Before
|
||||
public void setUp()
|
||||
{
|
||||
super.setUp();
|
||||
InterfaceUtils.setFixedTheme(R.style.TransparentWidgetTheme);
|
||||
|
||||
habit = fixtures.createShortHabit();
|
||||
view = new CheckmarkWidgetView(targetContext);
|
||||
int color = ColorUtils.getAndroidTestColor(habit.getColor());
|
||||
int score = habit.getScores().getTodayValue();
|
||||
float percentage = (float) score / Score.MAX_VALUE;
|
||||
|
||||
view.setActiveColor(color);
|
||||
view.setCheckmarkValue(habit.getCheckmarks().getTodayValue());
|
||||
view.setPercentage(percentage);
|
||||
view.setName(habit.getName());
|
||||
view.refresh();
|
||||
measureView(view, dpToPixels(100), dpToPixels(200));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRender_checked() throws IOException
|
||||
{
|
||||
assertRenders(view, PATH + "checked.png");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRender_implicitlyChecked() throws IOException
|
||||
{
|
||||
view.setCheckmarkValue(Checkmark.CHECKED_IMPLICITLY);
|
||||
view.refresh();
|
||||
assertRenders(view, PATH + "implicitly_checked.png");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRender_largeSize() throws IOException
|
||||
{
|
||||
measureView(view, dpToPixels(300), dpToPixels(300));
|
||||
assertRenders(view, PATH + "large_size.png");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRender_unchecked() throws IOException
|
||||
{
|
||||
view.setCheckmarkValue(Checkmark.UNCHECKED);
|
||||
view.refresh();
|
||||
assertRenders(view, PATH + "unchecked.png");
|
||||
}
|
||||
}
|
||||
@@ -1,168 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.unit;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.Log;
|
||||
|
||||
import org.isoron.uhabits.helpers.DatabaseHelper;
|
||||
import org.isoron.uhabits.helpers.DateHelper;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.tasks.BaseTask;
|
||||
import org.isoron.uhabits.tasks.ExportDBTask;
|
||||
import org.isoron.uhabits.tasks.ImportDataTask;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.util.Random;
|
||||
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
public class HabitFixtures
|
||||
{
|
||||
public static boolean NON_DAILY_HABIT_CHECKS[] = { true, false, false, true, true, true, false,
|
||||
false, true, true };
|
||||
|
||||
public static Habit createShortHabit()
|
||||
{
|
||||
Habit habit = new Habit();
|
||||
habit.name = "Wake up early";
|
||||
habit.description = "Did you wake up before 6am?";
|
||||
habit.freqNum = 2;
|
||||
habit.freqDen = 3;
|
||||
habit.save();
|
||||
|
||||
long timestamp = DateHelper.getStartOfToday();
|
||||
for(boolean c : NON_DAILY_HABIT_CHECKS)
|
||||
{
|
||||
if(c) habit.repetitions.toggle(timestamp);
|
||||
timestamp -= DateHelper.millisecondsInOneDay;
|
||||
}
|
||||
|
||||
return habit;
|
||||
}
|
||||
|
||||
public static Habit createEmptyHabit()
|
||||
{
|
||||
Habit habit = new Habit();
|
||||
habit.name = "Meditate";
|
||||
habit.description = "Did you meditate this morning?";
|
||||
habit.color = 3;
|
||||
habit.freqNum = 1;
|
||||
habit.freqDen = 1;
|
||||
habit.save();
|
||||
return habit;
|
||||
}
|
||||
|
||||
public static Habit createLongHabit()
|
||||
{
|
||||
Habit habit = createEmptyHabit();
|
||||
habit.freqNum = 3;
|
||||
habit.freqDen = 7;
|
||||
habit.color = 4;
|
||||
habit.save();
|
||||
|
||||
long day = DateHelper.millisecondsInOneDay;
|
||||
long today = DateHelper.getStartOfToday();
|
||||
int marks[] = { 0, 1, 3, 5, 7, 8, 9, 10, 12, 14, 15, 17, 19, 20, 26, 27, 28, 50, 51, 52,
|
||||
53, 54, 58, 60, 63, 65, 70, 71, 72, 73, 74, 75, 80, 81, 83, 89, 90, 91, 95,
|
||||
102, 103, 108, 109, 120};
|
||||
|
||||
for(int mark : marks)
|
||||
habit.repetitions.toggle(today - mark * day);
|
||||
|
||||
return habit;
|
||||
}
|
||||
|
||||
public static void generateHugeDataSet() throws Throwable
|
||||
{
|
||||
final int nHabits = 30;
|
||||
final int nYears = 5;
|
||||
|
||||
DatabaseHelper.executeAsTransaction(new DatabaseHelper.Command()
|
||||
{
|
||||
@Override
|
||||
public void execute()
|
||||
{
|
||||
Random rand = new Random();
|
||||
|
||||
for(int i = 0; i < nHabits; i++)
|
||||
{
|
||||
Log.i("HabitFixture", String.format("Creating habit %d / %d", i, nHabits));
|
||||
|
||||
Habit habit = new Habit();
|
||||
habit.name = String.format("Habit %d", i);
|
||||
habit.save();
|
||||
|
||||
long today = DateHelper.getStartOfToday();
|
||||
long day = DateHelper.millisecondsInOneDay;
|
||||
|
||||
|
||||
for(int j = 0; j < 365 * nYears; j++)
|
||||
{
|
||||
if(rand.nextBoolean())
|
||||
habit.repetitions.toggle(today - j * day);
|
||||
}
|
||||
|
||||
habit.scores.getTodayValue();
|
||||
habit.streaks.getAll(1);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ExportDBTask task = new ExportDBTask(null);
|
||||
task.setListener(new ExportDBTask.Listener()
|
||||
{
|
||||
@Override
|
||||
public void onExportDBFinished(@Nullable String filename)
|
||||
{
|
||||
if(filename != null)
|
||||
Log.i("HabitFixture", String.format("Huge data set exported to %s", filename));
|
||||
else
|
||||
Log.i("HabitFixture", "Failed to save database");
|
||||
}
|
||||
});
|
||||
task.execute();
|
||||
|
||||
BaseTask.waitForTasks(30000);
|
||||
}
|
||||
|
||||
public static void loadHugeDataSet(Context testContext) throws Throwable
|
||||
{
|
||||
File baseDir = DatabaseHelper.getFilesDir("Backups");
|
||||
if(baseDir == null) fail("baseDir should not be null");
|
||||
|
||||
File dst = new File(String.format("%s/%s", baseDir.getPath(), "loopHuge.db"));
|
||||
InputStream in = testContext.getAssets().open("fixtures/loopHuge.db");
|
||||
DatabaseHelper.copy(in, dst);
|
||||
|
||||
ImportDataTask task = new ImportDataTask(dst, null);
|
||||
task.execute();
|
||||
|
||||
BaseTask.waitForTasks(30000);
|
||||
}
|
||||
|
||||
public static void purgeHabits()
|
||||
{
|
||||
for(Habit h : Habit.getAll(true))
|
||||
h.cascadeDelete();
|
||||
}
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.unit.commands;
|
||||
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
import org.isoron.uhabits.BaseTest;
|
||||
import org.isoron.uhabits.commands.EditHabitCommand;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.unit.HabitFixtures;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.greaterThan;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class EditHabitCommandTest extends BaseTest
|
||||
{
|
||||
|
||||
private EditHabitCommand command;
|
||||
private Habit habit;
|
||||
private Habit modified;
|
||||
private Long id;
|
||||
|
||||
@Before
|
||||
public void setup()
|
||||
{
|
||||
super.setup();
|
||||
|
||||
habit = HabitFixtures.createShortHabit();
|
||||
habit.name = "original";
|
||||
habit.freqDen = 1;
|
||||
habit.freqNum = 1;
|
||||
habit.save();
|
||||
|
||||
id = habit.getId();
|
||||
|
||||
modified = new Habit(habit);
|
||||
modified.name = "modified";
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecuteUndoRedo()
|
||||
{
|
||||
command = new EditHabitCommand(habit, modified);
|
||||
|
||||
int originalScore = habit.scores.getTodayValue();
|
||||
assertThat(habit.name, equalTo("original"));
|
||||
|
||||
command.execute();
|
||||
refreshHabit();
|
||||
assertThat(habit.name, equalTo("modified"));
|
||||
assertThat(habit.scores.getTodayValue(), equalTo(originalScore));
|
||||
|
||||
command.undo();
|
||||
refreshHabit();
|
||||
assertThat(habit.name, equalTo("original"));
|
||||
assertThat(habit.scores.getTodayValue(), equalTo(originalScore));
|
||||
|
||||
command.execute();
|
||||
refreshHabit();
|
||||
assertThat(habit.name, equalTo("modified"));
|
||||
assertThat(habit.scores.getTodayValue(), equalTo(originalScore));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecuteUndoRedo_withModifiedInterval()
|
||||
{
|
||||
modified.freqNum = 1;
|
||||
modified.freqDen = 7;
|
||||
command = new EditHabitCommand(habit, modified);
|
||||
|
||||
int originalScore = habit.scores.getTodayValue();
|
||||
assertThat(habit.name, equalTo("original"));
|
||||
|
||||
command.execute();
|
||||
refreshHabit();
|
||||
assertThat(habit.name, equalTo("modified"));
|
||||
assertThat(habit.scores.getTodayValue(), greaterThan(originalScore));
|
||||
|
||||
command.undo();
|
||||
refreshHabit();
|
||||
assertThat(habit.name, equalTo("original"));
|
||||
assertThat(habit.scores.getTodayValue(), equalTo(originalScore));
|
||||
|
||||
command.execute();
|
||||
refreshHabit();
|
||||
assertThat(habit.name, equalTo("modified"));
|
||||
assertThat(habit.scores.getTodayValue(), greaterThan(originalScore));
|
||||
}
|
||||
|
||||
private void refreshHabit()
|
||||
{
|
||||
habit = Habit.get(id);
|
||||
assertTrue(habit != null);
|
||||
}
|
||||
}
|
||||
@@ -1,175 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.unit.models;
|
||||
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
import org.isoron.uhabits.BaseTest;
|
||||
import org.isoron.uhabits.helpers.DateHelper;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.unit.HabitFixtures;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.isoron.uhabits.models.Checkmark.CHECKED_EXPLICITLY;
|
||||
import static org.isoron.uhabits.models.Checkmark.CHECKED_IMPLICITLY;
|
||||
import static org.isoron.uhabits.models.Checkmark.UNCHECKED;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class CheckmarkListTest extends BaseTest
|
||||
{
|
||||
Habit nonDailyHabit;
|
||||
private Habit emptyHabit;
|
||||
|
||||
@Before
|
||||
public void setup()
|
||||
{
|
||||
super.setup();
|
||||
|
||||
HabitFixtures.purgeHabits();
|
||||
nonDailyHabit = HabitFixtures.createShortHabit();
|
||||
emptyHabit = HabitFixtures.createEmptyHabit();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown()
|
||||
{
|
||||
DateHelper.setFixedLocalTime(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_getAllValues_withNonDailyHabit()
|
||||
{
|
||||
int[] expectedValues = { CHECKED_EXPLICITLY, UNCHECKED, CHECKED_IMPLICITLY,
|
||||
CHECKED_EXPLICITLY, CHECKED_EXPLICITLY, CHECKED_EXPLICITLY, UNCHECKED,
|
||||
CHECKED_IMPLICITLY, CHECKED_EXPLICITLY, CHECKED_EXPLICITLY };
|
||||
|
||||
int[] actualValues = nonDailyHabit.checkmarks.getAllValues();
|
||||
|
||||
assertThat(actualValues, equalTo(expectedValues));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_getAllValues_withEmptyHabit()
|
||||
{
|
||||
int[] expectedValues = new int[0];
|
||||
int[] actualValues = emptyHabit.checkmarks.getAllValues();
|
||||
|
||||
assertThat(actualValues, equalTo(expectedValues));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_getAllValues_moveForwardInTime()
|
||||
{
|
||||
travelInTime(3);
|
||||
|
||||
int[] expectedValues = { UNCHECKED, UNCHECKED, UNCHECKED, CHECKED_EXPLICITLY, UNCHECKED,
|
||||
CHECKED_IMPLICITLY, CHECKED_EXPLICITLY, CHECKED_EXPLICITLY, CHECKED_EXPLICITLY,
|
||||
UNCHECKED, CHECKED_IMPLICITLY, CHECKED_EXPLICITLY, CHECKED_EXPLICITLY };
|
||||
|
||||
int[] actualValues = nonDailyHabit.checkmarks.getAllValues();
|
||||
|
||||
assertThat(actualValues, equalTo(expectedValues));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_getAllValues_moveBackwardsInTime()
|
||||
{
|
||||
travelInTime(-3);
|
||||
|
||||
int[] expectedValues = { CHECKED_EXPLICITLY, CHECKED_EXPLICITLY, CHECKED_EXPLICITLY,
|
||||
UNCHECKED, CHECKED_IMPLICITLY, CHECKED_EXPLICITLY, CHECKED_EXPLICITLY };
|
||||
|
||||
int[] actualValues = nonDailyHabit.checkmarks.getAllValues();
|
||||
|
||||
assertThat(actualValues, equalTo(expectedValues));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_getValues_withInvalidInterval()
|
||||
{
|
||||
int values[] = nonDailyHabit.checkmarks.getValues(100L, -100L);
|
||||
assertThat(values, equalTo(new int[0]));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_getValues_withValidInterval()
|
||||
{
|
||||
long from = DateHelper.getStartOfToday() - 15 * DateHelper.millisecondsInOneDay;
|
||||
long to = DateHelper.getStartOfToday() - 5 * DateHelper.millisecondsInOneDay;
|
||||
|
||||
int[] expectedValues = { CHECKED_EXPLICITLY, UNCHECKED, CHECKED_IMPLICITLY,
|
||||
CHECKED_EXPLICITLY, CHECKED_EXPLICITLY, UNCHECKED, UNCHECKED, UNCHECKED, UNCHECKED,
|
||||
UNCHECKED, UNCHECKED };
|
||||
|
||||
int[] actualValues = nonDailyHabit.checkmarks.getValues(from, to);
|
||||
|
||||
assertThat(actualValues, equalTo(expectedValues));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_getTodayValue()
|
||||
{
|
||||
travelInTime(-1);
|
||||
assertThat(nonDailyHabit.checkmarks.getTodayValue(), equalTo(UNCHECKED));
|
||||
|
||||
travelInTime(0);
|
||||
assertThat(nonDailyHabit.checkmarks.getTodayValue(), equalTo(CHECKED_EXPLICITLY));
|
||||
|
||||
travelInTime(1);
|
||||
assertThat(nonDailyHabit.checkmarks.getTodayValue(), equalTo(UNCHECKED));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_writeCSV() throws IOException
|
||||
{
|
||||
String expectedCSV =
|
||||
"2015-01-16,2\n" +
|
||||
"2015-01-17,2\n" +
|
||||
"2015-01-18,1\n" +
|
||||
"2015-01-19,0\n" +
|
||||
"2015-01-20,2\n" +
|
||||
"2015-01-21,2\n" +
|
||||
"2015-01-22,2\n" +
|
||||
"2015-01-23,1\n" +
|
||||
"2015-01-24,0\n" +
|
||||
"2015-01-25,2\n";
|
||||
|
||||
StringWriter writer = new StringWriter();
|
||||
nonDailyHabit.checkmarks.writeCSV(writer);
|
||||
|
||||
assertThat(writer.toString(), equalTo(expectedCSV));
|
||||
}
|
||||
|
||||
private void travelInTime(int days)
|
||||
{
|
||||
DateHelper.setFixedLocalTime(FIXED_LOCAL_TIME +
|
||||
days * DateHelper.millisecondsInOneDay);
|
||||
}
|
||||
}
|
||||
@@ -1,378 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.unit.models;
|
||||
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
import org.hamcrest.MatcherAssert;
|
||||
import org.isoron.uhabits.BaseTest;
|
||||
import org.isoron.uhabits.helpers.DateHelper;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.unit.HabitFixtures;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.hamcrest.core.IsNot.not;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class HabitTest extends BaseTest
|
||||
{
|
||||
@Before
|
||||
public void setup()
|
||||
{
|
||||
super.setup();
|
||||
HabitFixtures.purgeHabits();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConstructor_default()
|
||||
{
|
||||
Habit habit = new Habit();
|
||||
assertThat(habit.archived, is(0));
|
||||
assertThat(habit.highlight, is(0));
|
||||
|
||||
assertThat(habit.reminderHour, is(nullValue()));
|
||||
assertThat(habit.reminderMin, is(nullValue()));
|
||||
|
||||
assertThat(habit.reminderDays, is(not(nullValue())));
|
||||
assertThat(habit.streaks, is(not(nullValue())));
|
||||
assertThat(habit.scores, is(not(nullValue())));
|
||||
assertThat(habit.repetitions, is(not(nullValue())));
|
||||
assertThat(habit.checkmarks, is(not(nullValue())));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConstructor_habit()
|
||||
{
|
||||
Habit model = new Habit();
|
||||
model.archived = 1;
|
||||
model.highlight = 1;
|
||||
model.color = 0;
|
||||
model.freqNum = 10;
|
||||
model.freqDen = 20;
|
||||
model.reminderDays = 1;
|
||||
model.reminderHour = 8;
|
||||
model.reminderMin = 30;
|
||||
model.position = 0;
|
||||
|
||||
Habit habit = new Habit(model);
|
||||
assertThat(habit.archived, is(model.archived));
|
||||
assertThat(habit.highlight, is(model.highlight));
|
||||
assertThat(habit.color, is(model.color));
|
||||
assertThat(habit.freqNum, is(model.freqNum));
|
||||
assertThat(habit.freqDen, is(model.freqDen));
|
||||
assertThat(habit.reminderDays, is(model.reminderDays));
|
||||
assertThat(habit.reminderHour, is(model.reminderHour));
|
||||
assertThat(habit.reminderMin, is(model.reminderMin));
|
||||
assertThat(habit.position, is(model.position));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_get_withValidId()
|
||||
{
|
||||
Habit habit = new Habit();
|
||||
habit.save();
|
||||
|
||||
Habit habit2 = Habit.get(habit.getId());
|
||||
assertThat(habit, equalTo(habit2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_get_withInvalidId()
|
||||
{
|
||||
Habit habit = Habit.get(123456L);
|
||||
assertThat(habit, is(nullValue()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_getAll_withoutArchived()
|
||||
{
|
||||
List<Habit> habits = new LinkedList<>();
|
||||
List<Habit> habitsWithArchived = new LinkedList<>();
|
||||
|
||||
for(int i = 0; i < 10; i++)
|
||||
{
|
||||
Habit h = new Habit();
|
||||
|
||||
if(i % 2 == 0)
|
||||
h.archived = 1;
|
||||
else
|
||||
habits.add(h);
|
||||
|
||||
habitsWithArchived.add(h);
|
||||
h.save();
|
||||
}
|
||||
|
||||
assertThat(habits, equalTo(Habit.getAll(false)));
|
||||
assertThat(habitsWithArchived, equalTo(Habit.getAll(true)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_getByPosition()
|
||||
{
|
||||
List<Habit> habits = new LinkedList<>();
|
||||
|
||||
for(int i = 0; i < 10; i++)
|
||||
{
|
||||
Habit h = new Habit();
|
||||
h.save();
|
||||
habits.add(h);
|
||||
}
|
||||
|
||||
for(int i = 0; i < 10; i++)
|
||||
{
|
||||
Habit h = Habit.getByPosition(i);
|
||||
if(h == null) fail();
|
||||
assertThat(h, equalTo(habits.get(i)));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_count()
|
||||
{
|
||||
for(int i = 0; i < 10; i++)
|
||||
{
|
||||
Habit h = new Habit();
|
||||
if(i % 2 == 0) h.archived = 1;
|
||||
h.save();
|
||||
}
|
||||
|
||||
assertThat(Habit.count(), equalTo(5));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void test_countWithArchived()
|
||||
{
|
||||
for(int i = 0; i < 10; i++)
|
||||
{
|
||||
Habit h = new Habit();
|
||||
if(i % 2 == 0) h.archived = 1;
|
||||
h.save();
|
||||
}
|
||||
|
||||
assertThat(Habit.countWithArchived(), equalTo(10));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_updateId()
|
||||
{
|
||||
Habit habit = new Habit();
|
||||
habit.name = "Hello World";
|
||||
habit.save();
|
||||
|
||||
Long oldId = habit.getId();
|
||||
Long newId = 123456L;
|
||||
Habit.updateId(oldId, newId);
|
||||
|
||||
Habit newHabit = Habit.get(newId);
|
||||
if(newHabit == null) fail();
|
||||
assertThat(newHabit, is(not(nullValue())));
|
||||
assertThat(newHabit.name, equalTo(habit.name));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_reorder()
|
||||
{
|
||||
List<Long> ids = new LinkedList<>();
|
||||
|
||||
int n = 10;
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
Habit h = new Habit();
|
||||
h.save();
|
||||
ids.add(h.getId());
|
||||
assertThat(h.position, is(i));
|
||||
}
|
||||
|
||||
int operations[][] = {
|
||||
{5, 2},
|
||||
{3, 7},
|
||||
{4, 4},
|
||||
{3, 2}
|
||||
};
|
||||
|
||||
int expectedPosition[][] = {
|
||||
{0, 1, 3, 4, 5, 2, 6, 7, 8, 9},
|
||||
{0, 1, 7, 3, 4, 2, 5, 6, 8, 9},
|
||||
{0, 1, 7, 3, 4, 2, 5, 6, 8, 9},
|
||||
{0, 1, 7, 2, 4, 3, 5, 6, 8, 9},
|
||||
};
|
||||
|
||||
for(int i = 0; i < operations.length; i++)
|
||||
{
|
||||
int from = operations[i][0];
|
||||
int to = operations[i][1];
|
||||
|
||||
Habit fromHabit = Habit.getByPosition(from);
|
||||
Habit toHabit = Habit.getByPosition(to);
|
||||
Habit.reorder(fromHabit, toHabit);
|
||||
|
||||
int actualPositions[] = new int[n];
|
||||
|
||||
for (int j = 0; j < n; j++)
|
||||
{
|
||||
Habit h = Habit.get(ids.get(j));
|
||||
if (h == null) fail();
|
||||
actualPositions[j] = h.position;
|
||||
}
|
||||
|
||||
assertThat(actualPositions, equalTo(expectedPosition[i]));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_rebuildOrder()
|
||||
{
|
||||
List<Long> ids = new LinkedList<>();
|
||||
int originalPositions[] = { 0, 1, 1, 4, 6, 8, 10, 10, 13};
|
||||
|
||||
for (int p : originalPositions)
|
||||
{
|
||||
Habit h = new Habit();
|
||||
h.position = p;
|
||||
h.save();
|
||||
ids.add(h.getId());
|
||||
}
|
||||
|
||||
Habit.rebuildOrder();
|
||||
|
||||
for (int i = 0; i < originalPositions.length; i++)
|
||||
{
|
||||
Habit h = Habit.get(ids.get(i));
|
||||
if(h == null) fail();
|
||||
assertThat(h.position, is(i));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_getHabitsWithReminder()
|
||||
{
|
||||
List<Habit> habitsWithReminder = new LinkedList<>();
|
||||
|
||||
for(int i = 0; i < 10; i++)
|
||||
{
|
||||
Habit habit = new Habit();
|
||||
if(i % 2 == 0)
|
||||
{
|
||||
habit.reminderDays = DateHelper.ALL_WEEK_DAYS;
|
||||
habit.reminderHour = 8;
|
||||
habit.reminderMin = 30;
|
||||
habitsWithReminder.add(habit);
|
||||
}
|
||||
habit.save();
|
||||
}
|
||||
|
||||
assertThat(habitsWithReminder, equalTo(Habit.getHabitsWithReminder()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_archive_unarchive()
|
||||
{
|
||||
List<Habit> allHabits = new LinkedList<>();
|
||||
List<Habit> archivedHabits = new LinkedList<>();
|
||||
List<Habit> unarchivedHabits = new LinkedList<>();
|
||||
|
||||
for(int i = 0; i < 10; i++)
|
||||
{
|
||||
Habit habit = new Habit();
|
||||
habit.save();
|
||||
allHabits.add(habit);
|
||||
|
||||
if(i % 2 == 0)
|
||||
archivedHabits.add(habit);
|
||||
else
|
||||
unarchivedHabits.add(habit);
|
||||
}
|
||||
|
||||
Habit.archive(archivedHabits);
|
||||
assertThat(Habit.getAll(false), equalTo(unarchivedHabits));
|
||||
assertThat(Habit.getAll(true), equalTo(allHabits));
|
||||
|
||||
Habit.unarchive(archivedHabits);
|
||||
assertThat(Habit.getAll(false), equalTo(allHabits));
|
||||
assertThat(Habit.getAll(true), equalTo(allHabits));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_setColor()
|
||||
{
|
||||
List<Habit> habits = new LinkedList<>();
|
||||
|
||||
for(int i = 0; i < 10; i++)
|
||||
{
|
||||
Habit habit = new Habit();
|
||||
habit.color = i;
|
||||
habit.save();
|
||||
habits.add(habit);
|
||||
}
|
||||
|
||||
int newColor = 100;
|
||||
Habit.setColor(habits, newColor);
|
||||
|
||||
for(Habit h : habits)
|
||||
assertThat(h.color, equalTo(newColor));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_hasReminder_clearReminder()
|
||||
{
|
||||
Habit h = new Habit();
|
||||
assertThat(h.hasReminder(), is(false));
|
||||
|
||||
h.reminderDays = DateHelper.ALL_WEEK_DAYS;
|
||||
h.reminderHour = 8;
|
||||
h.reminderMin = 30;
|
||||
assertThat(h.hasReminder(), is(true));
|
||||
|
||||
h.clearReminder();
|
||||
assertThat(h.hasReminder(), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_writeCSV() throws IOException
|
||||
{
|
||||
HabitFixtures.createEmptyHabit();
|
||||
HabitFixtures.createShortHabit();
|
||||
|
||||
String expectedCSV =
|
||||
"Position,Name,Description,NumRepetitions,Interval,Color\n" +
|
||||
"001,Meditate,Did you meditate this morning?,1,1,#AFB42B\n" +
|
||||
"002,Wake up early,Did you wake up before 6am?,2,3,#00897B\n";
|
||||
|
||||
StringWriter writer = new StringWriter();
|
||||
Habit.writeCSV(Habit.getAll(true), writer);
|
||||
|
||||
MatcherAssert.assertThat(writer.toString(), equalTo(expectedCSV));
|
||||
}
|
||||
}
|
||||
@@ -1,191 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.unit.models;
|
||||
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
import org.isoron.uhabits.BaseTest;
|
||||
import org.isoron.uhabits.helpers.DateHelper;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.models.Repetition;
|
||||
import org.isoron.uhabits.unit.HabitFixtures;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.HashMap;
|
||||
import java.util.Random;
|
||||
|
||||
import static junit.framework.Assert.assertFalse;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class RepetitionListTest extends BaseTest
|
||||
{
|
||||
private Habit habit;
|
||||
private Habit emptyHabit;
|
||||
|
||||
@Before
|
||||
public void setup()
|
||||
{
|
||||
super.setup();
|
||||
|
||||
HabitFixtures.purgeHabits();
|
||||
habit = HabitFixtures.createShortHabit();
|
||||
emptyHabit = HabitFixtures.createEmptyHabit();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown()
|
||||
{
|
||||
DateHelper.setFixedLocalTime(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_contains()
|
||||
{
|
||||
long current = DateHelper.getStartOfToday();
|
||||
|
||||
for(boolean b : HabitFixtures.NON_DAILY_HABIT_CHECKS)
|
||||
{
|
||||
assertThat(habit.repetitions.contains(current), equalTo(b));
|
||||
current -= DateHelper.millisecondsInOneDay;
|
||||
}
|
||||
|
||||
for(int i = 0; i < 3; i++)
|
||||
{
|
||||
assertThat(habit.repetitions.contains(current), equalTo(false));
|
||||
current -= DateHelper.millisecondsInOneDay;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_delete()
|
||||
{
|
||||
long timestamp = DateHelper.getStartOfToday();
|
||||
assertThat(habit.repetitions.contains(timestamp), equalTo(true));
|
||||
|
||||
habit.repetitions.delete(timestamp);
|
||||
assertThat(habit.repetitions.contains(timestamp), equalTo(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_toggle()
|
||||
{
|
||||
long timestamp = DateHelper.getStartOfToday();
|
||||
assertThat(habit.repetitions.contains(timestamp), equalTo(true));
|
||||
|
||||
habit.repetitions.toggle(timestamp);
|
||||
assertThat(habit.repetitions.contains(timestamp), equalTo(false));
|
||||
|
||||
habit.repetitions.toggle(timestamp);
|
||||
assertThat(habit.repetitions.contains(timestamp), equalTo(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_getWeekDayFrequency()
|
||||
{
|
||||
Random random = new Random();
|
||||
Integer weekdayCount[][] = new Integer[12][7];
|
||||
Integer monthCount[] = new Integer[12];
|
||||
|
||||
Arrays.fill(monthCount, 0);
|
||||
for(Integer row[] : weekdayCount)
|
||||
Arrays.fill(row, 0);
|
||||
|
||||
GregorianCalendar day = DateHelper.getStartOfTodayCalendar();
|
||||
|
||||
// Sets the current date to the end of November
|
||||
day.set(2015, 10, 30);
|
||||
DateHelper.setFixedLocalTime(day.getTimeInMillis());
|
||||
|
||||
// Add repetitions randomly from January to December
|
||||
// Leaves the month of March empty, to check that it returns null
|
||||
day.set(2015, 0, 1);
|
||||
for(int i = 0; i < 365; i ++)
|
||||
{
|
||||
if(random.nextBoolean())
|
||||
{
|
||||
int month = day.get(Calendar.MONTH);
|
||||
int week = day.get(Calendar.DAY_OF_WEEK) % 7;
|
||||
|
||||
if(month != 2)
|
||||
{
|
||||
if (month <= 10)
|
||||
{
|
||||
weekdayCount[month][week]++;
|
||||
monthCount[month]++;
|
||||
}
|
||||
emptyHabit.repetitions.toggle(day.getTimeInMillis());
|
||||
}
|
||||
}
|
||||
|
||||
day.add(Calendar.DAY_OF_YEAR, 1);
|
||||
}
|
||||
|
||||
HashMap<Long, Integer[]> freq = emptyHabit.repetitions.getWeekdayFrequency();
|
||||
|
||||
// Repetitions until November should be counted correctly
|
||||
for(int month = 0; month < 11; month++)
|
||||
{
|
||||
day.set(2015, month, 1);
|
||||
Integer actualCount[] = freq.get(day.getTimeInMillis());
|
||||
if(monthCount[month] == 0)
|
||||
assertThat(actualCount, equalTo(null));
|
||||
else
|
||||
assertThat(actualCount, equalTo(weekdayCount[month]));
|
||||
}
|
||||
|
||||
// Repetitions in December should be discarded
|
||||
day.set(2015, 11, 1);
|
||||
assertThat(freq.get(day.getTimeInMillis()), equalTo(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_count()
|
||||
{
|
||||
long to = DateHelper.getStartOfToday();
|
||||
long from = to - 9 * DateHelper.millisecondsInOneDay;
|
||||
assertThat(habit.repetitions.count(from, to), equalTo(6));
|
||||
|
||||
to = DateHelper.getStartOfToday() - DateHelper.millisecondsInOneDay;
|
||||
from = to - 5 * DateHelper.millisecondsInOneDay;
|
||||
assertThat(habit.repetitions.count(from, to), equalTo(3));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_getOldest()
|
||||
{
|
||||
long expectedOldestTimestamp = DateHelper.getStartOfToday() - 9 * DateHelper.millisecondsInOneDay;
|
||||
|
||||
assertThat(habit.repetitions.getOldestTimestamp(), equalTo(expectedOldestTimestamp));
|
||||
|
||||
Repetition oldest = habit.repetitions.getOldest();
|
||||
assertFalse(oldest == null);
|
||||
assertThat(oldest.timestamp, equalTo(expectedOldestTimestamp));
|
||||
}
|
||||
}
|
||||
@@ -1,176 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.unit.models;
|
||||
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
import org.isoron.uhabits.BaseTest;
|
||||
import org.isoron.uhabits.helpers.DatabaseHelper;
|
||||
import org.isoron.uhabits.helpers.DateHelper;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.models.Score;
|
||||
import org.isoron.uhabits.unit.HabitFixtures;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class ScoreListTest extends BaseTest
|
||||
{
|
||||
private Habit habit;
|
||||
|
||||
@Before
|
||||
public void setup()
|
||||
{
|
||||
super.setup();
|
||||
|
||||
HabitFixtures.purgeHabits();
|
||||
habit = HabitFixtures.createEmptyHabit();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown()
|
||||
{
|
||||
DateHelper.setFixedLocalTime(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_invalidateNewerThan()
|
||||
{
|
||||
assertThat(habit.scores.getTodayValue(), equalTo(0));
|
||||
|
||||
toggleRepetitions(0, 2);
|
||||
assertThat(habit.scores.getTodayValue(), equalTo(1948077));
|
||||
|
||||
habit.freqNum = 1;
|
||||
habit.freqDen = 2;
|
||||
habit.scores.invalidateNewerThan(0);
|
||||
|
||||
assertThat(habit.scores.getTodayValue(), equalTo(1974654));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_getTodayStarValue()
|
||||
{
|
||||
assertThat(habit.scores.getTodayStarStatus(), equalTo(Score.EMPTY_STAR));
|
||||
|
||||
int k = 0;
|
||||
while(habit.scores.getTodayValue() < Score.HALF_STAR_CUTOFF) toggleRepetitions(k, ++k);
|
||||
assertThat(habit.scores.getTodayStarStatus(), equalTo(Score.HALF_STAR));
|
||||
|
||||
while(habit.scores.getTodayValue() < Score.FULL_STAR_CUTOFF) toggleRepetitions(k, ++k);
|
||||
assertThat(habit.scores.getTodayStarStatus(), equalTo(Score.FULL_STAR));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_getTodayValue()
|
||||
{
|
||||
toggleRepetitions(0, 20);
|
||||
assertThat(habit.scores.getTodayValue(), equalTo(12629351));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_getValue()
|
||||
{
|
||||
toggleRepetitions(0, 20);
|
||||
|
||||
int expectedValues[] = { 12629351, 12266245, 11883254, 11479288, 11053198, 10603773,
|
||||
10129735, 9629735, 9102352, 8546087, 7959357, 7340494, 6687738, 5999234, 5273023,
|
||||
4507040, 3699107, 2846927, 1948077, 1000000 };
|
||||
|
||||
long current = DateHelper.getStartOfToday();
|
||||
for(int expectedValue : expectedValues)
|
||||
{
|
||||
assertThat(habit.scores.getValue(current), equalTo(expectedValue));
|
||||
current -= DateHelper.millisecondsInOneDay;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_getAllValues_withoutGroups()
|
||||
{
|
||||
toggleRepetitions(0, 20);
|
||||
|
||||
int expectedValues[] = { 12629351, 12266245, 11883254, 11479288, 11053198, 10603773,
|
||||
10129735, 9629735, 9102352, 8546087, 7959357, 7340494, 6687738, 5999234, 5273023,
|
||||
4507040, 3699107, 2846927, 1948077, 1000000 };
|
||||
|
||||
int actualValues[] = habit.scores.getAllValues(1);
|
||||
assertThat(actualValues, equalTo(expectedValues));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_getAllValues_withGroups()
|
||||
{
|
||||
toggleRepetitions(0, 20);
|
||||
|
||||
int expectedValues[] = { 11434978, 7894999, 3212362 };
|
||||
|
||||
int actualValues[] = habit.scores.getAllValues(7);
|
||||
assertThat(actualValues, equalTo(expectedValues));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_writeCSV() throws IOException
|
||||
{
|
||||
HabitFixtures.purgeHabits();
|
||||
Habit habit = HabitFixtures.createShortHabit();
|
||||
|
||||
String expectedCSV =
|
||||
"2015-01-16,0.0519\n" +
|
||||
"2015-01-17,0.1021\n" +
|
||||
"2015-01-18,0.0986\n" +
|
||||
"2015-01-19,0.0952\n" +
|
||||
"2015-01-20,0.1439\n" +
|
||||
"2015-01-21,0.1909\n" +
|
||||
"2015-01-22,0.2364\n" +
|
||||
"2015-01-23,0.2283\n" +
|
||||
"2015-01-24,0.2205\n" +
|
||||
"2015-01-25,0.2649\n";
|
||||
|
||||
StringWriter writer = new StringWriter();
|
||||
habit.scores.writeCSV(writer);
|
||||
|
||||
assertThat(writer.toString(), equalTo(expectedCSV));
|
||||
}
|
||||
|
||||
private void toggleRepetitions(final int from, final int to)
|
||||
{
|
||||
DatabaseHelper.executeAsTransaction(new DatabaseHelper.Command()
|
||||
{
|
||||
@Override
|
||||
public void execute()
|
||||
{
|
||||
long today = DateHelper.getStartOfToday();
|
||||
for (int i = from; i < to; i++)
|
||||
habit.repetitions.toggle(today - i * DateHelper.millisecondsInOneDay);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.unit.tasks;
|
||||
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import org.isoron.uhabits.BaseTest;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.tasks.ExportCSVTask;
|
||||
import org.isoron.uhabits.unit.HabitFixtures;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.hamcrest.core.IsNot.not;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class ExportCSVTaskTest extends BaseTest
|
||||
{
|
||||
@Before
|
||||
public void setup()
|
||||
{
|
||||
super.setup();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExportCSV() throws Throwable
|
||||
{
|
||||
HabitFixtures.createShortHabit();
|
||||
List<Habit> habits = Habit.getAll(true);
|
||||
ProgressBar bar = new ProgressBar(targetContext);
|
||||
|
||||
ExportCSVTask task = new ExportCSVTask(habits, bar);
|
||||
task.setListener(new ExportCSVTask.Listener()
|
||||
{
|
||||
@Override
|
||||
public void onExportCSVFinished(String archiveFilename)
|
||||
{
|
||||
assertThat(archiveFilename, is(not(nullValue())));
|
||||
|
||||
File f = new File(archiveFilename);
|
||||
assertTrue(f.exists());
|
||||
assertTrue(f.canRead());
|
||||
}
|
||||
});
|
||||
|
||||
task.execute();
|
||||
waitForAsyncTasks();
|
||||
}
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.unit.views;
|
||||
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.helpers.DateHelper;
|
||||
import org.isoron.uhabits.helpers.UIHelper;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.unit.HabitFixtures;
|
||||
import org.isoron.uhabits.views.CheckmarkWidgetView;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class CheckmarkWidgetViewTest extends ViewTest
|
||||
{
|
||||
private CheckmarkWidgetView view;
|
||||
private Habit habit;
|
||||
|
||||
@Before
|
||||
public void setup()
|
||||
{
|
||||
super.setup();
|
||||
UIHelper.setFixedTheme(R.style.TransparentWidgetTheme);
|
||||
|
||||
habit = HabitFixtures.createShortHabit();
|
||||
view = new CheckmarkWidgetView(targetContext);
|
||||
view.setHabit(habit);
|
||||
refreshData(view);
|
||||
measureView(dpToPixels(100), dpToPixels(200), view);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRender_checked() throws IOException
|
||||
{
|
||||
assertRenders(view, "CheckmarkView/checked.png");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRender_unchecked() throws IOException
|
||||
{
|
||||
habit.repetitions.toggle(DateHelper.getStartOfToday());
|
||||
view.refreshData();
|
||||
|
||||
assertRenders(view, "CheckmarkView/unchecked.png");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRender_implicitlyChecked() throws IOException
|
||||
{
|
||||
long today = DateHelper.getStartOfToday();
|
||||
long day = DateHelper.millisecondsInOneDay;
|
||||
habit.repetitions.toggle(today);
|
||||
habit.repetitions.toggle(today - day);
|
||||
habit.repetitions.toggle(today - 2 * day);
|
||||
view.refreshData();
|
||||
|
||||
assertRenders(view, "CheckmarkView/implicitly_checked.png");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRender_largeSize() throws IOException
|
||||
{
|
||||
measureView(dpToPixels(300), dpToPixels(300), view);
|
||||
assertRenders(view, "CheckmarkView/large_size.png");
|
||||
}
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.unit.views;
|
||||
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
import org.isoron.uhabits.helpers.DateHelper;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.unit.HabitFixtures;
|
||||
import org.isoron.uhabits.views.HabitHistoryView;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static junit.framework.Assert.assertFalse;
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class HabitHistoryViewTest extends ViewTest
|
||||
{
|
||||
private Habit habit;
|
||||
private HabitHistoryView view;
|
||||
|
||||
@Before
|
||||
public void setup()
|
||||
{
|
||||
super.setup();
|
||||
|
||||
HabitFixtures.purgeHabits();
|
||||
habit = HabitFixtures.createLongHabit();
|
||||
|
||||
view = new HabitHistoryView(targetContext);
|
||||
view.setHabit(habit);
|
||||
measureView(dpToPixels(400), dpToPixels(200), view);
|
||||
refreshData(view);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRender() throws Throwable
|
||||
{
|
||||
assertRenders(view, "HabitHistoryView/render.png");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRender_withTransparentBackground() throws Throwable
|
||||
{
|
||||
view.setIsBackgroundTransparent(true);
|
||||
assertRenders(view, "HabitHistoryView/renderTransparent.png");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRender_withDifferentSize() throws Throwable
|
||||
{
|
||||
measureView(dpToPixels(200), dpToPixels(200), view);
|
||||
assertRenders(view, "HabitHistoryView/renderDifferentSize.png");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRender_withDataOffset() throws Throwable
|
||||
{
|
||||
view.onScroll(null, null, -dpToPixels(150), 0);
|
||||
view.invalidate();
|
||||
|
||||
assertRenders(view, "HabitHistoryView/renderDataOffset.png");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void tapDate_withEditableView() throws Throwable
|
||||
{
|
||||
view.setIsEditable(true);
|
||||
tap(view, 340, 40); // today's square
|
||||
waitForAsyncTasks();
|
||||
|
||||
long today = DateHelper.getStartOfToday();
|
||||
assertFalse(habit.repetitions.contains(today));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void tapDate_atInvalidLocations() throws Throwable
|
||||
{
|
||||
int expectedCheckmarkValues[] = habit.checkmarks.getAllValues();
|
||||
|
||||
view.setIsEditable(true);
|
||||
tap(view, 118, 13); // header
|
||||
tap(view, 336, 60); // tomorrow's square
|
||||
tap(view, 370, 60); // right axis
|
||||
waitForAsyncTasks();
|
||||
|
||||
int actualCheckmarkValues[] = habit.checkmarks.getAllValues();
|
||||
assertThat(actualCheckmarkValues, equalTo(expectedCheckmarkValues));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void tapDate_withReadOnlyView() throws Throwable
|
||||
{
|
||||
view.setIsEditable(false);
|
||||
tap(view, 340, 40); // today's square
|
||||
waitForAsyncTasks();
|
||||
|
||||
long today = DateHelper.getStartOfToday();
|
||||
assertTrue(habit.repetitions.contains(today));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.unit.views;
|
||||
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.helpers.ColorHelper;
|
||||
import org.isoron.uhabits.views.NumberView;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class NumberViewTest extends ViewTest
|
||||
{
|
||||
private NumberView view;
|
||||
|
||||
@Before
|
||||
public void setup()
|
||||
{
|
||||
super.setup();
|
||||
|
||||
view = new NumberView(targetContext);
|
||||
view.setLabel("Hello world");
|
||||
view.setNumber(31);
|
||||
view.setColor(ColorHelper.CSV_PALETTE[0]);
|
||||
measureView(dpToPixels(100), dpToPixels(100), view);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRender_base() throws IOException
|
||||
{
|
||||
assertRenders(view, "NumberView/render.png");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRender_withLongLabel() throws IOException
|
||||
{
|
||||
view.setLabel("The quick brown fox jumps over the lazy fox");
|
||||
|
||||
measureView(dpToPixels(100), dpToPixels(100), view);
|
||||
assertRenders(view, "NumberView/renderLongLabel.png");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRender_withDifferentParams() throws IOException
|
||||
{
|
||||
view.setNumber(500);
|
||||
view.setColor(ColorHelper.CSV_PALETTE[5]);
|
||||
view.setTextSize(targetContext.getResources().getDimension(R.dimen.tinyTextSize));
|
||||
|
||||
measureView(dpToPixels(200), dpToPixels(200), view);
|
||||
assertRenders(view, "NumberView/renderDifferentParams.png");
|
||||
}
|
||||
}
|
||||
@@ -60,28 +60,28 @@
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".ShowHabitActivity"
|
||||
android:name=".ui.habits.show.ShowHabitActivity"
|
||||
android:label="@string/title_activity_show_habit">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value="org.isoron.uhabits.MainActivity"/>
|
||||
android:value=".MainActivity"/>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".SettingsActivity"
|
||||
android:name=".ui.settings.SettingsActivity"
|
||||
android:label="@string/settings">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value="org.isoron.uhabits.MainActivity"/>
|
||||
android:value=".MainActivity"/>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".IntroActivity"
|
||||
android:name=".ui.intro.IntroActivity"
|
||||
android:label=""
|
||||
android:theme="@style/Theme.AppCompat.Light.NoActionBar"/>
|
||||
|
||||
<activity
|
||||
android:name=".widgets.HabitPickerDialog"
|
||||
android:name=".ui.widgets.HabitPickerDialog"
|
||||
android:theme="@style/Theme.AppCompat.Light.Dialog">
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/>
|
||||
@@ -89,7 +89,7 @@
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".AboutActivity"
|
||||
android:name=".ui.about.AboutActivity"
|
||||
android:label="@string/about">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
|
||||
34
app/src/main/java/org/isoron/uhabits/AndroidComponent.java
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import dagger.Component;
|
||||
|
||||
/**
|
||||
* Dependency injection component for classes that are specific to Android.
|
||||
*/
|
||||
@Singleton
|
||||
@Component(modules = {AndroidModule.class})
|
||||
public interface AndroidComponent extends BaseComponent
|
||||
{
|
||||
}
|
||||
|
||||
73
app/src/main/java/org/isoron/uhabits/AndroidModule.java
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits;
|
||||
|
||||
import org.isoron.uhabits.commands.*;
|
||||
import org.isoron.uhabits.models.*;
|
||||
import org.isoron.uhabits.models.sqlite.*;
|
||||
import org.isoron.uhabits.utils.*;
|
||||
|
||||
import javax.inject.*;
|
||||
|
||||
import dagger.*;
|
||||
|
||||
/**
|
||||
* Module that provides dependencies when the application is running on
|
||||
* Android.
|
||||
* <p>
|
||||
* This module is also used for instrumented tests.
|
||||
*/
|
||||
@Module
|
||||
public class AndroidModule
|
||||
{
|
||||
@Provides
|
||||
@Singleton
|
||||
CommandRunner provideCommandRunner()
|
||||
{
|
||||
return new CommandRunner();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
HabitList provideHabitList()
|
||||
{
|
||||
return SQLiteHabitList.getInstance();
|
||||
}
|
||||
|
||||
@Provides
|
||||
ModelFactory provideModelFactory()
|
||||
{
|
||||
return new SQLModelFactory();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
Preferences providePreferences()
|
||||
{
|
||||
return new Preferences();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
WidgetPreferences provideWidgetPreferences()
|
||||
{
|
||||
return new WidgetPreferences();
|
||||
}
|
||||
}
|
||||
@@ -1,204 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits;
|
||||
|
||||
import android.app.backup.BackupManager;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.isoron.uhabits.commands.Command;
|
||||
import org.isoron.uhabits.helpers.ColorHelper;
|
||||
import org.isoron.uhabits.helpers.UIHelper;
|
||||
|
||||
import java.util.LinkedList;
|
||||
|
||||
abstract public class BaseActivity extends AppCompatActivity implements Thread.UncaughtExceptionHandler
|
||||
{
|
||||
private static int MAX_UNDO_LEVEL = 15;
|
||||
|
||||
private LinkedList<Command> undoList;
|
||||
private LinkedList<Command> redoList;
|
||||
private Toast toast;
|
||||
|
||||
Thread.UncaughtExceptionHandler androidExceptionHandler;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
UIHelper.applyCurrentTheme(this);
|
||||
|
||||
androidExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
|
||||
Thread.setDefaultUncaughtExceptionHandler(this);
|
||||
|
||||
undoList = new LinkedList<>();
|
||||
redoList = new LinkedList<>();
|
||||
}
|
||||
|
||||
public void executeCommand(Command command, Long refreshKey)
|
||||
{
|
||||
executeCommand(command, false, refreshKey);
|
||||
}
|
||||
|
||||
protected void undo()
|
||||
{
|
||||
if (undoList.isEmpty())
|
||||
{
|
||||
showToast(R.string.toast_nothing_to_undo);
|
||||
return;
|
||||
}
|
||||
|
||||
Command last = undoList.pop();
|
||||
redoList.push(last);
|
||||
last.undo();
|
||||
showToast(last.getUndoStringId());
|
||||
}
|
||||
|
||||
protected void redo()
|
||||
{
|
||||
if (redoList.isEmpty())
|
||||
{
|
||||
showToast(R.string.toast_nothing_to_redo);
|
||||
return;
|
||||
}
|
||||
Command last = redoList.pop();
|
||||
executeCommand(last, false, null);
|
||||
}
|
||||
|
||||
public void showToast(Integer stringId)
|
||||
{
|
||||
if (stringId == null) return;
|
||||
if (toast == null) toast = Toast.makeText(this, stringId, Toast.LENGTH_SHORT);
|
||||
else toast.setText(stringId);
|
||||
toast.show();
|
||||
}
|
||||
|
||||
public void executeCommand(final Command command, Boolean clearRedoStack, final Long refreshKey)
|
||||
{
|
||||
undoList.push(command);
|
||||
|
||||
if (undoList.size() > MAX_UNDO_LEVEL) undoList.removeLast();
|
||||
if (clearRedoStack) redoList.clear();
|
||||
|
||||
new AsyncTask<Void, Void, Void>()
|
||||
{
|
||||
@Override
|
||||
protected Void doInBackground(Void... params)
|
||||
{
|
||||
command.execute();
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void aVoid)
|
||||
{
|
||||
BaseActivity.this.onPostExecuteCommand(refreshKey);
|
||||
BackupManager.dataChanged("org.isoron.uhabits");
|
||||
}
|
||||
}.execute();
|
||||
|
||||
|
||||
showToast(command.getExecuteStringId());
|
||||
}
|
||||
|
||||
protected void setupSupportActionBar(boolean homeButtonEnabled)
|
||||
{
|
||||
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
|
||||
if(toolbar == null) return;
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
|
||||
toolbar.setElevation(UIHelper.dpToPixels(this, 2));
|
||||
|
||||
setSupportActionBar(toolbar);
|
||||
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
if(actionBar == null) return;
|
||||
|
||||
if(homeButtonEnabled)
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
}
|
||||
|
||||
public void onPostExecuteCommand(Long refreshKey)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uncaughtException(Thread thread, Throwable ex)
|
||||
{
|
||||
try
|
||||
{
|
||||
ex.printStackTrace();
|
||||
HabitsApplication.dumpBugReportToFile();
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
if(androidExceptionHandler != null)
|
||||
androidExceptionHandler.uncaughtException(thread, ex);
|
||||
else
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
protected void setupActionBarColor(int color)
|
||||
{
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
if(actionBar == null) return;
|
||||
|
||||
if (!UIHelper.getStyledBoolean(this, R.attr.useHabitColorAsPrimary)) return;
|
||||
|
||||
ColorDrawable drawable = new ColorDrawable(color);
|
||||
actionBar.setBackgroundDrawable(drawable);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
|
||||
{
|
||||
int darkerColor = ColorHelper.mixColors(color, Color.BLACK, 0.75f);
|
||||
getWindow().setStatusBarColor(darkerColor);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostResume()
|
||||
{
|
||||
super.onPostResume();
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
|
||||
hideFakeToolbarShadow();
|
||||
}
|
||||
|
||||
protected void hideFakeToolbarShadow()
|
||||
{
|
||||
View view = findViewById(R.id.toolbarShadow);
|
||||
if(view != null) view.setVisibility(View.GONE);
|
||||
|
||||
view = findViewById(R.id.headerShadow);
|
||||
if(view != null) view.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
98
app/src/main/java/org/isoron/uhabits/BaseComponent.java
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits;
|
||||
|
||||
import org.isoron.uhabits.commands.*;
|
||||
import org.isoron.uhabits.io.*;
|
||||
import org.isoron.uhabits.models.*;
|
||||
import org.isoron.uhabits.tasks.*;
|
||||
import org.isoron.uhabits.ui.*;
|
||||
import org.isoron.uhabits.ui.habits.edit.*;
|
||||
import org.isoron.uhabits.ui.habits.list.*;
|
||||
import org.isoron.uhabits.ui.habits.list.controllers.*;
|
||||
import org.isoron.uhabits.ui.habits.list.model.*;
|
||||
import org.isoron.uhabits.ui.habits.list.views.*;
|
||||
import org.isoron.uhabits.ui.habits.show.*;
|
||||
import org.isoron.uhabits.ui.widgets.*;
|
||||
import org.isoron.uhabits.widgets.*;
|
||||
|
||||
/**
|
||||
* Base component for dependency injection.
|
||||
*/
|
||||
public interface BaseComponent
|
||||
{
|
||||
void inject(CheckmarkButtonController checkmarkButtonController);
|
||||
|
||||
void inject(ListHabitsController listHabitsController);
|
||||
|
||||
void inject(CheckmarkPanelView checkmarkPanelView);
|
||||
|
||||
void inject(ToggleRepetitionTask toggleRepetitionTask);
|
||||
|
||||
void inject(HabitCardListCache habitCardListCache);
|
||||
|
||||
void inject(HabitBroadcastReceiver habitBroadcastReceiver);
|
||||
|
||||
void inject(ListHabitsSelectionMenu listHabitsSelectionMenu);
|
||||
|
||||
void inject(HintList hintList);
|
||||
|
||||
void inject(HabitCardListAdapter habitCardListAdapter);
|
||||
|
||||
void inject(ArchiveHabitsCommand archiveHabitsCommand);
|
||||
|
||||
void inject(ChangeHabitColorCommand changeHabitColorCommand);
|
||||
|
||||
void inject(UnarchiveHabitsCommand unarchiveHabitsCommand);
|
||||
|
||||
void inject(EditHabitCommand editHabitCommand);
|
||||
|
||||
void inject(CreateHabitCommand createHabitCommand);
|
||||
|
||||
void inject(HabitPickerDialog habitPickerDialog);
|
||||
|
||||
void inject(BaseWidgetProvider baseWidgetProvider);
|
||||
|
||||
void inject(ShowHabitActivity showHabitActivity);
|
||||
|
||||
void inject(DeleteHabitsCommand deleteHabitsCommand);
|
||||
|
||||
void inject(ListHabitsActivity listHabitsActivity);
|
||||
|
||||
void inject(BaseSystem baseSystem);
|
||||
|
||||
void inject(HistoryEditorDialog historyEditorDialog);
|
||||
|
||||
void inject(HabitsApplication application);
|
||||
|
||||
void inject(Habit habit);
|
||||
|
||||
void inject(AbstractImporter abstractImporter);
|
||||
|
||||
void inject(HabitsCSVExporter habitsCSVExporter);
|
||||
|
||||
void inject(BaseDialogFragment baseDialogFragment);
|
||||
|
||||
void inject(ShowHabitController showHabitController);
|
||||
|
||||
void inject(BaseWidget baseWidget);
|
||||
|
||||
void inject(WidgetUpdater widgetManager);
|
||||
}
|
||||
@@ -19,37 +19,62 @@
|
||||
|
||||
package org.isoron.uhabits;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ContentUris;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.support.v4.app.TaskStackBuilder;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
import android.app.*;
|
||||
import android.content.*;
|
||||
import android.graphics.*;
|
||||
import android.net.*;
|
||||
import android.os.*;
|
||||
import android.preference.*;
|
||||
import android.support.v4.app.*;
|
||||
|
||||
import org.isoron.uhabits.helpers.DateHelper;
|
||||
import org.isoron.uhabits.helpers.ReminderHelper;
|
||||
import org.isoron.uhabits.models.Checkmark;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.tasks.BaseTask;
|
||||
import org.isoron.uhabits.commands.*;
|
||||
import org.isoron.uhabits.models.*;
|
||||
import org.isoron.uhabits.tasks.*;
|
||||
import org.isoron.uhabits.utils.*;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.*;
|
||||
|
||||
import javax.inject.*;
|
||||
|
||||
/**
|
||||
* The Android BroadcastReceiver for Loop Habit Tracker.
|
||||
* <p/>
|
||||
* All broadcast messages are received and processed by this class.
|
||||
*/
|
||||
public class HabitBroadcastReceiver extends BroadcastReceiver
|
||||
{
|
||||
public static final String ACTION_CHECK = "org.isoron.uhabits.ACTION_CHECK";
|
||||
public static final String ACTION_DISMISS = "org.isoron.uhabits.ACTION_DISMISS";
|
||||
public static final String ACTION_SHOW_REMINDER = "org.isoron.uhabits.ACTION_SHOW_REMINDER";
|
||||
public static final String ACTION_SNOOZE = "org.isoron.uhabits.ACTION_SNOOZE";
|
||||
|
||||
public static final String ACTION_DISMISS =
|
||||
"org.isoron.uhabits.ACTION_DISMISS";
|
||||
|
||||
public static final String ACTION_SHOW_REMINDER =
|
||||
"org.isoron.uhabits.ACTION_SHOW_REMINDER";
|
||||
|
||||
public static final String ACTION_SNOOZE =
|
||||
"org.isoron.uhabits.ACTION_SNOOZE";
|
||||
|
||||
@Inject
|
||||
HabitList habitList;
|
||||
|
||||
@Inject
|
||||
CommandRunner commandRunner;
|
||||
|
||||
public HabitBroadcastReceiver()
|
||||
{
|
||||
super();
|
||||
HabitsApplication.getComponent().inject(this);
|
||||
}
|
||||
|
||||
public static void dismissNotification(Context context, Habit habit)
|
||||
{
|
||||
NotificationManager notificationManager =
|
||||
(NotificationManager) context.getSystemService(
|
||||
Activity.NOTIFICATION_SERVICE);
|
||||
|
||||
int notificationId = (int) (habit.getId() % Integer.MAX_VALUE);
|
||||
notificationManager.cancel(notificationId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceive(final Context context, Intent intent)
|
||||
@@ -74,81 +99,52 @@ public class HabitBroadcastReceiver extends BroadcastReceiver
|
||||
break;
|
||||
|
||||
case Intent.ACTION_BOOT_COMPLETED:
|
||||
ReminderHelper.createReminderAlarms(context);
|
||||
ReminderUtils.createReminderAlarms(context, habitList);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void createReminderAlarmsDelayed(final Context context)
|
||||
{
|
||||
new Handler().postDelayed(new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
ReminderHelper.createReminderAlarms(context);
|
||||
}
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
private void snoozeHabit(Context context, Intent intent)
|
||||
{
|
||||
Uri data = intent.getData();
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
long delayMinutes = Long.parseLong(prefs.getString("pref_snooze_interval", "15"));
|
||||
|
||||
long habitId = ContentUris.parseId(data);
|
||||
Habit habit = Habit.get(habitId);
|
||||
if(habit != null)
|
||||
ReminderHelper.createReminderAlarm(context, habit,
|
||||
new Date().getTime() + delayMinutes * 60 * 1000);
|
||||
dismissNotification(context, habitId);
|
||||
}
|
||||
|
||||
private void checkHabit(Context context, Intent intent)
|
||||
{
|
||||
Uri data = intent.getData();
|
||||
Long timestamp = intent.getLongExtra("timestamp", DateHelper.getStartOfToday());
|
||||
long today = DateUtils.getStartOfToday();
|
||||
Long timestamp = intent.getLongExtra("timestamp", today);
|
||||
|
||||
long habitId = ContentUris.parseId(data);
|
||||
Habit habit = Habit.get(habitId);
|
||||
Habit habit = habitList.getById(habitId);
|
||||
if (habit != null)
|
||||
habit.repetitions.toggle(timestamp);
|
||||
{
|
||||
ToggleRepetitionCommand command =
|
||||
new ToggleRepetitionCommand(habit, timestamp);
|
||||
commandRunner.execute(command, habitId);
|
||||
}
|
||||
|
||||
dismissNotification(context, habitId);
|
||||
|
||||
sendRefreshBroadcast(context);
|
||||
}
|
||||
|
||||
public static void sendRefreshBroadcast(Context context)
|
||||
private boolean checkWeekday(Intent intent, Habit habit)
|
||||
{
|
||||
LocalBroadcastManager manager = LocalBroadcastManager.getInstance(context);
|
||||
Intent refreshIntent = new Intent(MainActivity.ACTION_REFRESH);
|
||||
manager.sendBroadcast(refreshIntent);
|
||||
if (!habit.hasReminder()) return false;
|
||||
Reminder reminder = habit.getReminder();
|
||||
|
||||
MainActivity.updateWidgets(context);
|
||||
Long timestamp =
|
||||
intent.getLongExtra("timestamp", DateUtils.getStartOfToday());
|
||||
|
||||
boolean reminderDays[] =
|
||||
DateUtils.unpackWeekdayList(reminder.getDays());
|
||||
int weekday = DateUtils.getWeekday(timestamp);
|
||||
|
||||
return reminderDays[weekday];
|
||||
}
|
||||
|
||||
private void dismissAllHabits()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private void dismissNotification(Context context, Long habitId)
|
||||
{
|
||||
NotificationManager notificationManager =
|
||||
(NotificationManager) context.getSystemService(Activity.NOTIFICATION_SERVICE);
|
||||
|
||||
int notificationId = (int) (habitId % Integer.MAX_VALUE);
|
||||
notificationManager.cancel(notificationId);
|
||||
}
|
||||
|
||||
|
||||
private void createNotification(final Context context, final Intent intent)
|
||||
{
|
||||
final Uri data = intent.getData();
|
||||
final Habit habit = Habit.get(ContentUris.parseId(data));
|
||||
final Long timestamp = intent.getLongExtra("timestamp", DateHelper.getStartOfToday());
|
||||
final Long reminderTime = intent.getLongExtra("reminderTime", DateHelper.getStartOfToday());
|
||||
final Habit habit = habitList.getById(ContentUris.parseId(data));
|
||||
final Long timestamp =
|
||||
intent.getLongExtra("timestamp", DateUtils.getStartOfToday());
|
||||
final Long reminderTime =
|
||||
intent.getLongExtra("reminderTime", DateUtils.getStartOfToday());
|
||||
|
||||
if (habit == null) return;
|
||||
|
||||
@@ -159,7 +155,7 @@ public class HabitBroadcastReceiver extends BroadcastReceiver
|
||||
@Override
|
||||
protected void doInBackground()
|
||||
{
|
||||
todayValue = habit.checkmarks.getTodayValue();
|
||||
todayValue = habit.getCheckmarks().getTodayValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -173,14 +169,18 @@ public class HabitBroadcastReceiver extends BroadcastReceiver
|
||||
contentIntent.setData(data);
|
||||
PendingIntent contentPendingIntent =
|
||||
PendingIntent.getActivity(context, 0, contentIntent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
PendingIntent.FLAG_CANCEL_CURRENT);
|
||||
|
||||
PendingIntent dismissPendingIntent = buildDismissIntent(context);
|
||||
PendingIntent checkIntentPending = buildCheckIntent(context,
|
||||
habit, timestamp, 1);
|
||||
PendingIntent snoozeIntentPending = buildSnoozeIntent(context, habit);
|
||||
PendingIntent dismissPendingIntent;
|
||||
dismissPendingIntent =
|
||||
HabitPendingIntents.dismissNotification(context);
|
||||
PendingIntent checkIntentPending =
|
||||
HabitPendingIntents.toggleCheckmark(context, habit,
|
||||
timestamp, 1);
|
||||
PendingIntent snoozeIntentPending =
|
||||
HabitPendingIntents.snoozeNotification(context, habit);
|
||||
|
||||
Uri ringtoneUri = ReminderHelper.getRingtoneUri(context);
|
||||
Uri ringtoneUri = ReminderUtils.getRingtoneUri(context);
|
||||
|
||||
NotificationCompat.WearableExtender wearableExtender =
|
||||
new NotificationCompat.WearableExtender().setBackground(
|
||||
@@ -190,14 +190,16 @@ public class HabitBroadcastReceiver extends BroadcastReceiver
|
||||
Notification notification =
|
||||
new NotificationCompat.Builder(context)
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
.setContentTitle(habit.name)
|
||||
.setContentText(habit.description)
|
||||
.setContentTitle(habit.getName())
|
||||
.setContentText(habit.getDescription())
|
||||
.setContentIntent(contentPendingIntent)
|
||||
.setDeleteIntent(dismissPendingIntent)
|
||||
.addAction(R.drawable.ic_action_check,
|
||||
context.getString(R.string.check), checkIntentPending)
|
||||
context.getString(R.string.check),
|
||||
checkIntentPending)
|
||||
.addAction(R.drawable.ic_action_snooze,
|
||||
context.getString(R.string.snooze), snoozeIntentPending)
|
||||
context.getString(R.string.snooze),
|
||||
snoozeIntentPending)
|
||||
.setSound(ringtoneUri)
|
||||
.extend(wearableExtender)
|
||||
.setWhen(reminderTime)
|
||||
@@ -218,63 +220,39 @@ public class HabitBroadcastReceiver extends BroadcastReceiver
|
||||
}.execute();
|
||||
}
|
||||
|
||||
public static PendingIntent buildSnoozeIntent(Context context, Habit habit)
|
||||
private void createReminderAlarmsDelayed(final Context context)
|
||||
{
|
||||
Uri data = habit.getUri();
|
||||
Intent snoozeIntent = new Intent(context, HabitBroadcastReceiver.class);
|
||||
snoozeIntent.setData(data);
|
||||
snoozeIntent.setAction(ACTION_SNOOZE);
|
||||
return PendingIntent.getBroadcast(context, 0, snoozeIntent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
new Handler().postDelayed(
|
||||
() -> ReminderUtils.createReminderAlarms(context, habitList), 5000);
|
||||
}
|
||||
|
||||
public static PendingIntent buildCheckIntent(Context context, Habit
|
||||
habit, Long timestamp, int requestCode)
|
||||
private void dismissAllHabits()
|
||||
{
|
||||
Uri data = habit.getUri();
|
||||
Intent checkIntent = new Intent(context, HabitBroadcastReceiver.class);
|
||||
checkIntent.setData(data);
|
||||
checkIntent.setAction(ACTION_CHECK);
|
||||
if(timestamp != null) checkIntent.putExtra("timestamp", timestamp);
|
||||
return PendingIntent.getBroadcast(context, requestCode, checkIntent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
|
||||
}
|
||||
|
||||
public static PendingIntent buildDismissIntent(Context context)
|
||||
{
|
||||
Intent deleteIntent = new Intent(context, HabitBroadcastReceiver.class);
|
||||
deleteIntent.setAction(ACTION_DISMISS);
|
||||
return PendingIntent.getBroadcast(context, 0, deleteIntent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
}
|
||||
|
||||
public static PendingIntent buildViewHabitIntent(Context context, Habit habit)
|
||||
{
|
||||
Intent intent = new Intent(context, ShowHabitActivity.class);
|
||||
intent.setData(Uri.parse("content://org.isoron.uhabits/habit/" + habit.getId()));
|
||||
|
||||
return TaskStackBuilder.create(context.getApplicationContext())
|
||||
.addNextIntentWithParentStack(intent)
|
||||
.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
}
|
||||
|
||||
private boolean checkWeekday(Intent intent, Habit habit)
|
||||
{
|
||||
Long timestamp = intent.getLongExtra("timestamp", DateHelper.getStartOfToday());
|
||||
|
||||
boolean reminderDays[] = DateHelper.unpackWeekdayList(habit.reminderDays);
|
||||
int weekday = DateHelper.getWeekday(timestamp);
|
||||
|
||||
return reminderDays[weekday];
|
||||
}
|
||||
|
||||
public static void dismissNotification(Context context, Habit habit)
|
||||
private void dismissNotification(Context context, Long habitId)
|
||||
{
|
||||
NotificationManager notificationManager =
|
||||
(NotificationManager) context.getSystemService(
|
||||
Activity.NOTIFICATION_SERVICE);
|
||||
|
||||
int notificationId = (int) (habit.getId() % Integer.MAX_VALUE);
|
||||
int notificationId = (int) (habitId % Integer.MAX_VALUE);
|
||||
notificationManager.cancel(notificationId);
|
||||
}
|
||||
|
||||
private void snoozeHabit(Context context, Intent intent)
|
||||
{
|
||||
Uri data = intent.getData();
|
||||
SharedPreferences prefs =
|
||||
PreferenceManager.getDefaultSharedPreferences(context);
|
||||
long delayMinutes =
|
||||
Long.parseLong(prefs.getString("pref_snooze_interval", "15"));
|
||||
|
||||
long habitId = ContentUris.parseId(data);
|
||||
Habit habit = habitList.getById(habitId);
|
||||
if (habit != null) ReminderUtils.createReminderAlarm(context, habit,
|
||||
new Date().getTime() + delayMinutes * 60 * 1000);
|
||||
dismissNotification(context, habitId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits;
|
||||
|
||||
import android.app.*;
|
||||
import android.content.*;
|
||||
import android.net.*;
|
||||
|
||||
import org.isoron.uhabits.models.*;
|
||||
import org.isoron.uhabits.ui.habits.show.*;
|
||||
|
||||
public abstract class HabitPendingIntents
|
||||
{
|
||||
|
||||
private static final String BASE_URL =
|
||||
"content://org.isoron.uhabits/habit/";
|
||||
|
||||
public static PendingIntent dismissNotification(Context context)
|
||||
{
|
||||
Intent deleteIntent = new Intent(context, HabitBroadcastReceiver.class);
|
||||
deleteIntent.setAction(HabitBroadcastReceiver.ACTION_DISMISS);
|
||||
return PendingIntent.getBroadcast(context, 0, deleteIntent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
}
|
||||
|
||||
public static PendingIntent snoozeNotification(Context context, Habit habit)
|
||||
{
|
||||
Uri data = habit.getUri();
|
||||
Intent snoozeIntent = new Intent(context, HabitBroadcastReceiver.class);
|
||||
snoozeIntent.setData(data);
|
||||
snoozeIntent.setAction(HabitBroadcastReceiver.ACTION_SNOOZE);
|
||||
return PendingIntent.getBroadcast(context, 0, snoozeIntent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
}
|
||||
|
||||
public static PendingIntent toggleCheckmark(Context context,
|
||||
Habit habit,
|
||||
Long timestamp,
|
||||
int requestCode)
|
||||
{
|
||||
Uri data = habit.getUri();
|
||||
Intent checkIntent = new Intent(context, HabitBroadcastReceiver.class);
|
||||
checkIntent.setData(data);
|
||||
checkIntent.setAction(HabitBroadcastReceiver.ACTION_CHECK);
|
||||
if (timestamp != null) checkIntent.putExtra("timestamp", timestamp);
|
||||
return PendingIntent.getBroadcast(context, requestCode, checkIntent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
}
|
||||
|
||||
public static PendingIntent viewHabit(Context context, Habit habit)
|
||||
{
|
||||
Intent intent = new Intent(context, ShowHabitActivity.class);
|
||||
intent.setData(Uri.parse(BASE_URL + habit.getId()));
|
||||
return android.support.v4.app.TaskStackBuilder
|
||||
.create(context.getApplicationContext())
|
||||
.addNextIntentWithParentStack(intent)
|
||||
.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
}
|
||||
}
|
||||
@@ -19,42 +19,48 @@
|
||||
|
||||
package org.isoron.uhabits;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.os.Environment;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.view.WindowManager;
|
||||
import android.app.*;
|
||||
import android.content.*;
|
||||
import android.support.annotation.*;
|
||||
|
||||
import com.activeandroid.ActiveAndroid;
|
||||
import com.activeandroid.*;
|
||||
|
||||
import org.isoron.uhabits.helpers.DatabaseHelper;
|
||||
import org.isoron.uhabits.helpers.DateHelper;
|
||||
import org.isoron.uhabits.ui.widgets.*;
|
||||
import org.isoron.uhabits.utils.*;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.LinkedList;
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* The Android application for Loop Habit Tracker.
|
||||
*/
|
||||
public class HabitsApplication extends Application
|
||||
{
|
||||
public static final int RESULT_BUG_REPORT = 4;
|
||||
|
||||
public static final int RESULT_EXPORT_CSV = 2;
|
||||
|
||||
public static final int RESULT_EXPORT_DB = 3;
|
||||
|
||||
public static final int RESULT_IMPORT_DATA = 1;
|
||||
|
||||
@Nullable
|
||||
private static HabitsApplication application;
|
||||
|
||||
private static BaseComponent component;
|
||||
|
||||
@Nullable
|
||||
private static Context context;
|
||||
|
||||
public static boolean isTestMode()
|
||||
private static WidgetUpdater widgetManager;
|
||||
|
||||
public static BaseComponent getComponent()
|
||||
{
|
||||
try
|
||||
{
|
||||
if(context != null)
|
||||
context.getClassLoader().loadClass("org.isoron.uhabits.unit.models.HabitTest");
|
||||
return true;
|
||||
return component;
|
||||
}
|
||||
catch (final Exception e)
|
||||
|
||||
public static void setComponent(BaseComponent component)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
HabitsApplication.component = component;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -63,19 +69,55 @@ public class HabitsApplication extends Application
|
||||
return context;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static HabitsApplication getInstance()
|
||||
{
|
||||
return application;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static WidgetUpdater getWidgetManager()
|
||||
{
|
||||
if (widgetManager == null)
|
||||
throw new RuntimeException("widgetManager is null");
|
||||
|
||||
return widgetManager;
|
||||
}
|
||||
|
||||
public static boolean isTestMode()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (context != null) context
|
||||
.getClassLoader()
|
||||
.loadClass("org.isoron.uhabits.BaseAndroidTest");
|
||||
return true;
|
||||
}
|
||||
catch (final Exception e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate()
|
||||
{
|
||||
super.onCreate();
|
||||
HabitsApplication.context = this;
|
||||
HabitsApplication.application = this;
|
||||
component = DaggerAndroidComponent.builder().build();
|
||||
component.inject(this);
|
||||
|
||||
if (isTestMode())
|
||||
{
|
||||
File db = DatabaseHelper.getDatabaseFile();
|
||||
File db = DatabaseUtils.getDatabaseFile();
|
||||
if (db.exists()) db.delete();
|
||||
}
|
||||
|
||||
DatabaseHelper.initializeActiveAndroid();
|
||||
widgetManager = new WidgetUpdater(this);
|
||||
widgetManager.startListening();
|
||||
|
||||
DatabaseUtils.initializeActiveAndroid();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -83,84 +125,7 @@ public class HabitsApplication extends Application
|
||||
{
|
||||
HabitsApplication.context = null;
|
||||
ActiveAndroid.dispose();
|
||||
widgetManager.stopListening();
|
||||
super.onTerminate();
|
||||
}
|
||||
|
||||
public static String getLogcat() throws IOException
|
||||
{
|
||||
int maxNLines = 250;
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
String[] command = new String[] { "logcat", "-d"};
|
||||
Process process = Runtime.getRuntime().exec(command);
|
||||
|
||||
InputStreamReader in = new InputStreamReader(process.getInputStream());
|
||||
BufferedReader bufferedReader = new BufferedReader(in);
|
||||
|
||||
LinkedList<String> log = new LinkedList<>();
|
||||
|
||||
String line;
|
||||
while ((line = bufferedReader.readLine()) != null)
|
||||
{
|
||||
log.addLast(line);
|
||||
if(log.size() > maxNLines) log.removeFirst();
|
||||
}
|
||||
|
||||
for(String l : log)
|
||||
{
|
||||
builder.append(l);
|
||||
builder.append('\n');
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public static String getDeviceInfo()
|
||||
{
|
||||
if(context == null) return "";
|
||||
|
||||
StringBuilder b = new StringBuilder();
|
||||
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
|
||||
|
||||
b.append(String.format("App Version Name: %s\n", BuildConfig.VERSION_NAME));
|
||||
b.append(String.format("App Version Code: %s\n", BuildConfig.VERSION_CODE));
|
||||
b.append(String.format("OS Version: %s (%s)\n", System.getProperty("os.version"),
|
||||
android.os.Build.VERSION.INCREMENTAL));
|
||||
b.append(String.format("OS API Level: %s\n", android.os.Build.VERSION.SDK));
|
||||
b.append(String.format("Device: %s\n", android.os.Build.DEVICE));
|
||||
b.append(String.format("Model (Product): %s (%s)\n", android.os.Build.MODEL,
|
||||
android.os.Build.PRODUCT));
|
||||
b.append(String.format("Manufacturer: %s\n", android.os.Build.MANUFACTURER));
|
||||
b.append(String.format("Other tags: %s\n", android.os.Build.TAGS));
|
||||
b.append(String.format("Screen Width: %s\n", wm.getDefaultDisplay().getWidth()));
|
||||
b.append(String.format("Screen Height: %s\n", wm.getDefaultDisplay().getHeight()));
|
||||
b.append(String.format("SD Card state: %s\n\n", Environment.getExternalStorageState()));
|
||||
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static File dumpBugReportToFile() throws IOException
|
||||
{
|
||||
String date = DateHelper.getBackupDateFormat().format(DateHelper.getLocalTime());
|
||||
|
||||
if(context == null) throw new RuntimeException("application context should not be null");
|
||||
File dir = DatabaseHelper.getFilesDir("Logs");
|
||||
if (dir == null) throw new IOException("log dir should not be null");
|
||||
|
||||
File logFile = new File(String.format("%s/Log %s.txt", dir.getPath(), date));
|
||||
FileWriter output = new FileWriter(logFile);
|
||||
output.write(generateBugReport());
|
||||
output.close();
|
||||
|
||||
return logFile;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static String generateBugReport() throws IOException
|
||||
{
|
||||
String logcat = getLogcat();
|
||||
String deviceInfo = getDeviceInfo();
|
||||
return deviceInfo + "\n" + logcat;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,12 +23,17 @@ import android.app.backup.BackupAgentHelper;
|
||||
import android.app.backup.FileBackupHelper;
|
||||
import android.app.backup.SharedPreferencesBackupHelper;
|
||||
|
||||
/**
|
||||
* An Android BackupAgentHelper customized for this application.
|
||||
*/
|
||||
public class HabitsBackupAgent extends BackupAgentHelper
|
||||
{
|
||||
@Override
|
||||
public void onCreate()
|
||||
{
|
||||
addHelper("preferences", new SharedPreferencesBackupHelper(this, "preferences"));
|
||||
addHelper("database", new FileBackupHelper(this, "../databases/uhabits.db"));
|
||||
addHelper("preferences",
|
||||
new SharedPreferencesBackupHelper(this, "preferences"));
|
||||
addHelper("database",
|
||||
new FileBackupHelper(this, "../databases/uhabits.db"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,328 +19,16 @@
|
||||
|
||||
package org.isoron.uhabits;
|
||||
|
||||
import android.appwidget.AppWidgetManager;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import org.isoron.uhabits.ui.habits.list.ListHabitsActivity;
|
||||
|
||||
import org.isoron.uhabits.fragments.ListHabitsFragment;
|
||||
import org.isoron.uhabits.helpers.DateHelper;
|
||||
import org.isoron.uhabits.helpers.ReminderHelper;
|
||||
import org.isoron.uhabits.helpers.UIHelper;
|
||||
import org.isoron.uhabits.models.Checkmark;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.tasks.BaseTask;
|
||||
import org.isoron.uhabits.widgets.CheckmarkWidgetProvider;
|
||||
import org.isoron.uhabits.widgets.FrequencyWidgetProvider;
|
||||
import org.isoron.uhabits.widgets.HistoryWidgetProvider;
|
||||
import org.isoron.uhabits.widgets.ScoreWidgetProvider;
|
||||
import org.isoron.uhabits.widgets.StreakWidgetProvider;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class MainActivity extends BaseActivity
|
||||
implements ListHabitsFragment.OnHabitClickListener
|
||||
/**
|
||||
* Application that starts upon clicking the launcher icon.
|
||||
*/
|
||||
public class MainActivity extends ListHabitsActivity
|
||||
{
|
||||
private ListHabitsFragment listHabitsFragment;
|
||||
private SharedPreferences prefs;
|
||||
private BroadcastReceiver receiver;
|
||||
private LocalBroadcastManager localBroadcastManager;
|
||||
|
||||
public static final String ACTION_REFRESH = "org.isoron.uhabits.ACTION_REFRESH";
|
||||
|
||||
public static final int RESULT_IMPORT_DATA = 1;
|
||||
public static final int RESULT_EXPORT_CSV = 2;
|
||||
public static final int RESULT_EXPORT_DB = 3;
|
||||
public static final int RESULT_BUG_REPORT = 4;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setContentView(R.layout.list_habits_activity);
|
||||
|
||||
setupSupportActionBar(false);
|
||||
|
||||
prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
listHabitsFragment =
|
||||
(ListHabitsFragment) getSupportFragmentManager().findFragmentById(R.id.fragment1);
|
||||
|
||||
receiver = new Receiver();
|
||||
localBroadcastManager = LocalBroadcastManager.getInstance(this);
|
||||
localBroadcastManager.registerReceiver(receiver, new IntentFilter(ACTION_REFRESH));
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
|
||||
onPreLollipopStartup();
|
||||
|
||||
onStartup();
|
||||
}
|
||||
|
||||
private void onPreLollipopStartup()
|
||||
{
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
if(actionBar == null) return;
|
||||
if(UIHelper.isNightMode()) return;
|
||||
|
||||
int color = getResources().getColor(R.color.grey_900);
|
||||
actionBar.setBackgroundDrawable(new ColorDrawable(color));
|
||||
}
|
||||
|
||||
private void onStartup()
|
||||
{
|
||||
PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
|
||||
UIHelper.incrementLaunchCount(this);
|
||||
UIHelper.updateLastAppVersion(this);
|
||||
showTutorial();
|
||||
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... params)
|
||||
{
|
||||
ReminderHelper.createReminderAlarms(MainActivity.this);
|
||||
updateWidgets(MainActivity.this);
|
||||
return null;
|
||||
}
|
||||
}.execute();
|
||||
|
||||
}
|
||||
|
||||
private void showTutorial()
|
||||
{
|
||||
Boolean firstRun = prefs.getBoolean("pref_first_run", true);
|
||||
|
||||
if (firstRun)
|
||||
{
|
||||
SharedPreferences.Editor editor = prefs.edit();
|
||||
editor.putBoolean("pref_first_run", false);
|
||||
editor.putLong("last_hint_timestamp", DateHelper.getStartOfToday()).apply();
|
||||
editor.apply();
|
||||
|
||||
Intent intent = new Intent(this, IntroActivity.class);
|
||||
this.startActivity(intent);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu)
|
||||
{
|
||||
menu.clear();
|
||||
getMenuInflater().inflate(R.menu.list_habits_menu, menu);
|
||||
|
||||
MenuItem nightModeItem = menu.findItem(R.id.action_night_mode);
|
||||
nightModeItem.setChecked(UIHelper.isNightMode());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item)
|
||||
{
|
||||
switch (item.getItemId())
|
||||
{
|
||||
case R.id.action_night_mode:
|
||||
{
|
||||
if(UIHelper.isNightMode())
|
||||
UIHelper.setCurrentTheme(UIHelper.THEME_LIGHT);
|
||||
else
|
||||
UIHelper.setCurrentTheme(UIHelper.THEME_DARK);
|
||||
|
||||
refreshTheme();
|
||||
return true;
|
||||
}
|
||||
|
||||
case R.id.action_settings:
|
||||
{
|
||||
Intent intent = new Intent(this, SettingsActivity.class);
|
||||
startActivityForResult(intent, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
case R.id.action_about:
|
||||
{
|
||||
Intent intent = new Intent(this, AboutActivity.class);
|
||||
startActivity(intent);
|
||||
return true;
|
||||
}
|
||||
|
||||
case R.id.action_faq:
|
||||
{
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(Intent.ACTION_VIEW);
|
||||
intent.setData(Uri.parse(getString(R.string.helpURL)));
|
||||
startActivity(intent);
|
||||
return true;
|
||||
}
|
||||
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
private void refreshTheme()
|
||||
{
|
||||
new Handler().postDelayed(new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
Intent intent = new Intent(MainActivity.this, MainActivity.class);
|
||||
|
||||
MainActivity.this.finish();
|
||||
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
|
||||
startActivity(intent);
|
||||
|
||||
}
|
||||
}, 500); // Let the menu disappear first
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data)
|
||||
{
|
||||
switch (resultCode)
|
||||
{
|
||||
case RESULT_IMPORT_DATA:
|
||||
listHabitsFragment.showImportDialog();
|
||||
break;
|
||||
|
||||
case RESULT_EXPORT_CSV:
|
||||
listHabitsFragment.exportAllHabits();
|
||||
break;
|
||||
|
||||
case RESULT_EXPORT_DB:
|
||||
listHabitsFragment.exportDB();
|
||||
break;
|
||||
|
||||
case RESULT_BUG_REPORT:
|
||||
generateBugReport();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void generateBugReport()
|
||||
{
|
||||
try
|
||||
{
|
||||
HabitsApplication.dumpBugReportToFile();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
String log = "---------- BUG REPORT BEGINS ----------\n";
|
||||
log += HabitsApplication.generateBugReport();
|
||||
log += "---------- BUG REPORT ENDS ------------\n";
|
||||
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(Intent.ACTION_SEND);
|
||||
intent.setType("message/rfc822");
|
||||
intent.putExtra(Intent.EXTRA_EMAIL, new String[] { "dev@loophabits.org" });
|
||||
intent.putExtra(Intent.EXTRA_SUBJECT, "Bug Report - Loop Habit Tracker");
|
||||
intent.putExtra(Intent.EXTRA_TEXT, log);
|
||||
startActivity(intent);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
showToast(R.string.bug_report_failed);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHabitClicked(Habit habit)
|
||||
{
|
||||
Intent intent = new Intent(this, ShowHabitActivity.class);
|
||||
intent.setData(Uri.parse("content://org.isoron.uhabits/habit/" + habit.getId()));
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPostExecuteCommand(Long refreshKey)
|
||||
{
|
||||
listHabitsFragment.onPostExecuteCommand(refreshKey);
|
||||
|
||||
new BaseTask()
|
||||
{
|
||||
@Override
|
||||
protected void doInBackground()
|
||||
{
|
||||
dismissNotifications(MainActivity.this);
|
||||
updateWidgets(MainActivity.this);
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
private void dismissNotifications(Context context)
|
||||
{
|
||||
for(Habit h : Habit.getHabitsWithReminder())
|
||||
{
|
||||
if(h.checkmarks.getTodayValue() != Checkmark.UNCHECKED)
|
||||
HabitBroadcastReceiver.dismissNotification(context, h);
|
||||
}
|
||||
}
|
||||
|
||||
public static void updateWidgets(Context context)
|
||||
{
|
||||
updateWidgets(context, CheckmarkWidgetProvider.class);
|
||||
updateWidgets(context, HistoryWidgetProvider.class);
|
||||
updateWidgets(context, ScoreWidgetProvider.class);
|
||||
updateWidgets(context, StreakWidgetProvider.class);
|
||||
updateWidgets(context, FrequencyWidgetProvider.class);
|
||||
}
|
||||
|
||||
private static void updateWidgets(Context context, Class providerClass)
|
||||
{
|
||||
ComponentName provider = new ComponentName(context, providerClass);
|
||||
Intent intent = new Intent(context, providerClass);
|
||||
intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
|
||||
int ids[] = AppWidgetManager.getInstance(context).getAppWidgetIds(provider);
|
||||
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids);
|
||||
context.sendBroadcast(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy()
|
||||
{
|
||||
localBroadcastManager.unregisterReceiver(receiver);
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
class Receiver extends BroadcastReceiver
|
||||
{
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent)
|
||||
{
|
||||
listHabitsFragment.onPostExecuteCommand(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
|
||||
@NonNull int[] grantResults)
|
||||
{
|
||||
if (grantResults.length <= 0) return;
|
||||
if (grantResults[0] != PackageManager.PERMISSION_GRANTED) return;
|
||||
|
||||
listHabitsFragment.showImportDialog();
|
||||
}
|
||||
/*
|
||||
* Since changing the main activity on the manifest file causes things to
|
||||
* break we always point the launcher icon to this activity instead, and
|
||||
* redirect to the desired one using inheritance.
|
||||
*/
|
||||
}
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits;
|
||||
|
||||
import android.content.ContentUris;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.ActionBar;
|
||||
|
||||
import org.isoron.uhabits.helpers.ColorHelper;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
|
||||
public class ShowHabitActivity extends BaseActivity
|
||||
{
|
||||
private Habit habit;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
Uri data = getIntent().getData();
|
||||
habit = Habit.get(ContentUris.parseId(data));
|
||||
|
||||
setContentView(R.layout.show_habit_activity);
|
||||
|
||||
setupSupportActionBar(true);
|
||||
setupHabitActionBar();
|
||||
}
|
||||
|
||||
private void setupHabitActionBar()
|
||||
{
|
||||
if(habit == null) return;
|
||||
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
if(actionBar == null) return;
|
||||
|
||||
actionBar.setTitle(habit.name);
|
||||
|
||||
setupActionBarColor(ColorHelper.getColor(this, habit.color));
|
||||
}
|
||||
|
||||
public Habit getHabit()
|
||||
{
|
||||
return habit;
|
||||
}
|
||||
}
|
||||
@@ -19,38 +19,52 @@
|
||||
|
||||
package org.isoron.uhabits.commands;
|
||||
|
||||
import org.isoron.uhabits.HabitsApplication;
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.models.HabitList;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* Command to archive a list of habits.
|
||||
*/
|
||||
public class ArchiveHabitsCommand extends Command
|
||||
{
|
||||
@Inject
|
||||
HabitList habitList;
|
||||
|
||||
private List<Habit> habits;
|
||||
|
||||
public ArchiveHabitsCommand(List<Habit> habits)
|
||||
{
|
||||
HabitsApplication.getComponent().inject(this);
|
||||
this.habits = habits;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute()
|
||||
{
|
||||
Habit.archive(habits);
|
||||
for(Habit h : habits) h.setArchived(true);
|
||||
habitList.update(habits);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void undo()
|
||||
{
|
||||
Habit.unarchive(habits);
|
||||
for(Habit h : habits) h.setArchived(false);
|
||||
habitList.update(habits);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getExecuteStringId()
|
||||
{
|
||||
return R.string.toast_habit_archived;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getUndoStringId()
|
||||
{
|
||||
return R.string.toast_habit_unarchived;
|
||||
|
||||