diff --git a/app/src/androidTest/assets/views/widgets/CheckmarkWidgetView/checked.png b/app/src/androidTest/assets/views/widgets/CheckmarkWidgetView/checked.png index 69f650a0b..1437a510b 100644 Binary files a/app/src/androidTest/assets/views/widgets/CheckmarkWidgetView/checked.png and b/app/src/androidTest/assets/views/widgets/CheckmarkWidgetView/checked.png differ diff --git a/app/src/androidTest/assets/views/widgets/CheckmarkWidgetView/implicitly_checked.png b/app/src/androidTest/assets/views/widgets/CheckmarkWidgetView/implicitly_checked.png index 992a9e5b2..97fdcbd19 100644 Binary files a/app/src/androidTest/assets/views/widgets/CheckmarkWidgetView/implicitly_checked.png and b/app/src/androidTest/assets/views/widgets/CheckmarkWidgetView/implicitly_checked.png differ diff --git a/app/src/androidTest/assets/views/widgets/CheckmarkWidgetView/large_size.png b/app/src/androidTest/assets/views/widgets/CheckmarkWidgetView/large_size.png index 8aa13d5e2..bcc1fcf9b 100644 Binary files a/app/src/androidTest/assets/views/widgets/CheckmarkWidgetView/large_size.png and b/app/src/androidTest/assets/views/widgets/CheckmarkWidgetView/large_size.png differ diff --git a/app/src/androidTest/assets/views/widgets/CheckmarkWidgetView/unchecked.png b/app/src/androidTest/assets/views/widgets/CheckmarkWidgetView/unchecked.png index 576d369ec..2f64db223 100644 Binary files a/app/src/androidTest/assets/views/widgets/CheckmarkWidgetView/unchecked.png and b/app/src/androidTest/assets/views/widgets/CheckmarkWidgetView/unchecked.png differ diff --git a/app/src/androidTest/java/org/isoron/uhabits/BaseAndroidTest.java b/app/src/androidTest/java/org/isoron/uhabits/BaseAndroidTest.java index 5b94835e2..0123be685 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/BaseAndroidTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/BaseAndroidTest.java @@ -19,21 +19,25 @@ package org.isoron.uhabits; -import android.content.Context; -import android.os.Build; -import android.os.Looper; -import android.support.test.InstrumentationRegistry; +import android.appwidget.*; +import android.content.*; +import android.os.*; +import android.support.test.*; -import org.isoron.uhabits.models.HabitList; -import org.isoron.uhabits.tasks.BaseTask; -import org.isoron.uhabits.utils.DateUtils; -import org.isoron.uhabits.utils.InterfaceUtils; -import org.isoron.uhabits.utils.Preferences; -import org.junit.Before; +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.concurrent.TimeoutException; +import java.util.*; +import java.util.concurrent.*; -import javax.inject.Inject; +import javax.inject.*; + +import static junit.framework.Assert.*; +import static org.hamcrest.MatcherAssert.*; +import static org.hamcrest.Matchers.*; public class BaseAndroidTest { @@ -52,6 +56,9 @@ public class BaseAndroidTest @Inject protected HabitList habitList; + @Inject + protected CommandRunner commandRunner; + protected AndroidTestComponent androidTestComponent; protected HabitFixtures fixtures; @@ -78,6 +85,18 @@ public class BaseAndroidTest fixtures = new HabitFixtures(habitList); } + protected void sleep(int time) + { + try + { + Thread.sleep(time); + } + catch (InterruptedException e) + { + fail(); + } + } + protected void waitForAsyncTasks() throws InterruptedException, TimeoutException { @@ -89,4 +108,19 @@ public class BaseAndroidTest BaseTask.waitForTasks(10000); } + + protected void assertWidgetProviderIsInstalled(ComponentName desiredProvider) + { + AppWidgetManager manager = AppWidgetManager.getInstance(targetContext); + List providerInfoList; + List installedProviders; + + providerInfoList = manager.getInstalledProviders(); + installedProviders = new LinkedList<>(); + + for (AppWidgetProviderInfo info : providerInfoList) + installedProviders.add(info.provider); + + assertThat(installedProviders, hasItems(desiredProvider)); + } } diff --git a/app/src/androidTest/java/org/isoron/uhabits/BaseViewTest.java b/app/src/androidTest/java/org/isoron/uhabits/BaseViewTest.java index 32dac96cb..b7d2d1af5 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/BaseViewTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/BaseViewTest.java @@ -21,10 +21,11 @@ package org.isoron.uhabits; import android.graphics.*; import android.os.*; +import android.support.annotation.*; import android.view.*; +import android.widget.*; -import org.isoron.uhabits.tasks.*; -import org.isoron.uhabits.ui.common.views.*; +import org.isoron.uhabits.ui.widgets.*; import org.isoron.uhabits.utils.*; import java.io.*; @@ -34,7 +35,9 @@ import static junit.framework.Assert.*; public class BaseViewTest extends BaseAndroidTest { protected static final double DEFAULT_SIMILARITY_CUTOFF = 0.09; + public static final int HISTOGRAM_BIN_SIZE = 8; + private double similarityCutoff; @Override @@ -44,21 +47,8 @@ public class BaseViewTest extends BaseAndroidTest similarityCutoff = DEFAULT_SIMILARITY_CUTOFF; } - protected void setSimilarityCutoff(double similarityCutoff) - { - this.similarityCutoff = similarityCutoff; - } - - 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); - - view.measure(specWidth, specHeight); - view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight()); - } - - 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); @@ -70,98 +60,122 @@ public class BaseViewTest extends BaseAndroidTest 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))) > - similarityCutoff) + if ((distance = compareHistograms(getHistogram(actual), + getHistogram(scaledExpected))) > similarityCutoff) { similarEnough = false; errorMessage.append(String.format( - "Rendered image has wrong histogram (distance=%f). ", - distance)); + "Rendered image has wrong histogram (distance=%f). ", + distance)); } - if(!similarEnough) + if (!similarEnough) { 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(); } - private Bitmap getBitmapFromAssets(String path) throws IOException + protected int dpToPixels(int dp) { - InputStream stream = testContext.getAssets().open(path); - return BitmapFactory.decodeStream(stream); + return (int) InterfaceUtils.dpToPixels(targetContext, dp); } - private String getVersionedViewAssetPath(String path) + protected void measureView(View view, int width, int height) { - String result = null; + int specWidth = + View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY); + int specHeight = + View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY); - if (android.os.Build.VERSION.SDK_INT >= 21) - { - try - { - String vpath = "views-v21/" + path; - testContext.getAssets().open(vpath); - result = vpath; - } - catch (IOException e) - { - // ignored - } - } + view.measure(specWidth, specHeight); + view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight()); + } - if(result == null) - result = "views/" + path; + protected void setSimilarityCutoff(double similarityCutoff) + { + this.similarityCutoff = similarityCutoff; + } - return result; + 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 String saveBitmap(String filename, String suffix, Bitmap bitmap) - throws IOException + private double compareHistograms(int[][] actualHistogram, + int[][] expectedHistogram) { - 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"); + long diff = 0; + long total = 0; - filename = filename.replaceAll("\\.png$", suffix + ".png"); - String absolutePath = String.format("%s/%s", dir.getAbsolutePath(), filename); + 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]); - File parent = new File(absolutePath).getParentFile(); - if(!parent.exists() && !parent.mkdirs()) - throw new RuntimeException(String.format("Could not create dir: %s", - parent.getAbsolutePath())); + total += actualHistogram[0][i]; + total += actualHistogram[1][i]; + total += actualHistogram[2][i]; + total += actualHistogram[3][i]; + } - FileOutputStream out = new FileOutputStream(absolutePath); - bitmap.compress(Bitmap.CompressFormat.PNG, 100, out); + return (double) diff / total / 2; + } - return absolutePath; + @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; + } + + private Bitmap getBitmapFromAssets(String path) throws IOException + { + InputStream stream = testContext.getAssets().open(path); + return BitmapFactory.decodeStream(stream); } private int[][] getHistogram(Bitmap bitmap) { int histogram[][] = new int[4][256 / HISTOGRAM_BIN_SIZE]; - for(int x = 0; x < bitmap.getWidth(); x++) + for (int x = 0; x < bitmap.getWidth(); x++) { - for(int y = 0; y < bitmap.getHeight(); y++) + for (int y = 0; y < bitmap.getHeight(); y++) { int color = bitmap.getPixel(x, y); int[] argb = new int[]{ - (color >> 24) & 0xff, //alpha - (color >> 16) & 0xff, //red - (color >> 8) & 0xff, //green - (color ) & 0xff //blue + (color >> 24) & 0xff, //alpha + (color >> 16) & 0xff, //red + (color >> 8) & 0xff, //green + (color) & 0xff //blue }; histogram[0][argb[0] / HISTOGRAM_BIN_SIZE]++; @@ -174,59 +188,49 @@ public class BaseViewTest extends BaseAndroidTest 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]; + try + { + String vpath = "views-v21/" + path; + testContext.getAssets().open(vpath); + result = vpath; + } + catch (IOException e) + { + // ignored + } } - return (double) diff / total / 2; - } + if (result == null) result = "views/" + path; - protected int dpToPixels(int dp) - { - return (int) InterfaceUtils.dpToPixels(targetContext, dp); + return result; } - protected void tap(GestureDetector.OnGestureListener view, int x, int y) throws InterruptedException + private String saveBitmap(String filename, String suffix, Bitmap bitmap) + throws IOException { - long now = SystemClock.uptimeMillis(); - MotionEvent e = MotionEvent.obtain(now, now, MotionEvent.ACTION_UP, dpToPixels(x), - dpToPixels(y), 0); - view.onSingleTapUp(e); - e.recycle(); - } + 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"); - protected void refreshData(final HabitChart view) - { - new BaseTask() - { - @Override - protected void doInBackground() - { - view.refreshData(); - } - }.execute(); + filename = filename.replaceAll("\\.png$", suffix + ".png"); + String absolutePath = + String.format("%s/%s", dir.getAbsolutePath(), filename); - try - { - waitForAsyncTasks(); - } - catch (Exception e) - { - throw new RuntimeException("Time out"); - } + 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; } } diff --git a/app/src/androidTest/java/org/isoron/uhabits/ui/common/views/FrequencyChartTest.java b/app/src/androidTest/java/org/isoron/uhabits/ui/common/views/FrequencyChartTest.java index 2240254f3..2d21a6dd1 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/ui/common/views/FrequencyChartTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/ui/common/views/FrequencyChartTest.java @@ -48,7 +48,7 @@ public class FrequencyChartTest extends BaseViewTest view = new FrequencyChart(targetContext); view.setFrequency(habit.getRepetitions().getWeekdayFrequency()); view.setColor(ColorUtils.getAndroidTestColor(habit.getColor())); - measureView(dpToPixels(300), dpToPixels(100), view); + measureView(view, dpToPixels(300), dpToPixels(100)); } @Test @@ -69,7 +69,7 @@ public class FrequencyChartTest extends BaseViewTest @Test public void testRender_withDifferentSize() throws Throwable { - measureView(dpToPixels(200), dpToPixels(200), view); + measureView(view, dpToPixels(200), dpToPixels(200)); assertRenders(view, BASE_PATH + "renderDifferentSize.png"); } diff --git a/app/src/androidTest/java/org/isoron/uhabits/ui/common/views/HistoryChartTest.java b/app/src/androidTest/java/org/isoron/uhabits/ui/common/views/HistoryChartTest.java index af5b3f3f5..9a6c99640 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/ui/common/views/HistoryChartTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/ui/common/views/HistoryChartTest.java @@ -48,7 +48,7 @@ public class HistoryChartTest extends BaseViewTest chart = new HistoryChart(targetContext); chart.setCheckmarks(habit.getCheckmarks().getAllValues()); chart.setColor(ColorUtils.getAndroidTestColor(habit.getColor())); - measureView(dpToPixels(400), dpToPixels(200), chart); + measureView(chart, dpToPixels(400), dpToPixels(200)); } // @Test @@ -106,7 +106,7 @@ public class HistoryChartTest extends BaseViewTest @Test public void testRender_withDifferentSize() throws Throwable { - measureView(dpToPixels(200), dpToPixels(200), chart); + measureView(chart, dpToPixels(200), dpToPixels(200)); assertRenders(chart, BASE_PATH + "renderDifferentSize.png"); } diff --git a/app/src/androidTest/java/org/isoron/uhabits/ui/common/views/RingViewTest.java b/app/src/androidTest/java/org/isoron/uhabits/ui/common/views/RingViewTest.java index 7512bc107..771e1d17c 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/ui/common/views/RingViewTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/ui/common/views/RingViewTest.java @@ -55,7 +55,7 @@ public class RingViewTest extends BaseViewTest @Test public void testRender_base() throws IOException { - measureView(dpToPixels(100), dpToPixels(100), view); + measureView(view, dpToPixels(100), dpToPixels(100)); assertRenders(view, BASE_PATH + "render.png"); } @@ -65,7 +65,7 @@ public class RingViewTest extends BaseViewTest view.setPercentage(0.25f); view.setColor(ColorUtils.getAndroidTestColor(5)); - measureView(dpToPixels(200), dpToPixels(200), view); + measureView(view, dpToPixels(200), dpToPixels(200)); assertRenders(view, BASE_PATH + "renderDifferentParams.png"); } } diff --git a/app/src/androidTest/java/org/isoron/uhabits/ui/common/views/ScoreChartTest.java b/app/src/androidTest/java/org/isoron/uhabits/ui/common/views/ScoreChartTest.java index 50346aa17..b56bd5704 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/ui/common/views/ScoreChartTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/ui/common/views/ScoreChartTest.java @@ -50,9 +50,9 @@ public class ScoreChartTest extends BaseViewTest view = new ScoreChart(targetContext); view.setScores(habit.getScores().getAll()); - view.setPrimaryColor(ColorUtils.getColor(targetContext, habit.getColor())); + view.setColor(ColorUtils.getColor(targetContext, habit.getColor())); view.setBucketSize(7); - measureView(dpToPixels(300), dpToPixels(200), view); + measureView(view, dpToPixels(300), dpToPixels(200)); } @Test @@ -75,7 +75,7 @@ public class ScoreChartTest extends BaseViewTest @Test public void testRender_withDifferentSize() throws Throwable { - measureView(dpToPixels(200), dpToPixels(200), view); + measureView(view, dpToPixels(200), dpToPixels(200)); assertRenders(view, BASE_PATH + "renderDifferentSize.png"); } diff --git a/app/src/androidTest/java/org/isoron/uhabits/ui/common/views/StreakChartTest.java b/app/src/androidTest/java/org/isoron/uhabits/ui/common/views/StreakChartTest.java index 44f7c7280..3ca002117 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/ui/common/views/StreakChartTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/ui/common/views/StreakChartTest.java @@ -48,7 +48,7 @@ public class StreakChartTest extends BaseViewTest view = new StreakChart(targetContext); view.setColor(ColorUtils.getAndroidTestColor(habit.getColor())); view.setStreaks(habit.getStreaks().getBest(5)); - measureView(dpToPixels(300), dpToPixels(100), view); + measureView(view, dpToPixels(300), dpToPixels(100)); } @Test @@ -60,7 +60,7 @@ public class StreakChartTest extends BaseViewTest @Test public void testRender_withSmallSize() throws Throwable { - measureView(dpToPixels(100), dpToPixels(100), view); + measureView(view, dpToPixels(100), dpToPixels(100)); assertRenders(view, BASE_PATH + "renderSmallSize.png"); } diff --git a/app/src/androidTest/java/org/isoron/uhabits/ui/habits/list/views/CheckmarkButtonViewTest.java b/app/src/androidTest/java/org/isoron/uhabits/ui/habits/list/views/CheckmarkButtonViewTest.java index e1c474d25..b55eefce1 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/ui/habits/list/views/CheckmarkButtonViewTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/ui/habits/list/views/CheckmarkButtonViewTest.java @@ -54,7 +54,7 @@ public class CheckmarkButtonViewTest extends BaseViewTest view.setValue(Checkmark.UNCHECKED); view.setColor(ColorUtils.getAndroidTestColor(7)); - measureView(dpToPixels(40), dpToPixels(40), view); + measureView(view, dpToPixels(40), dpToPixels(40)); } @Test diff --git a/app/src/androidTest/java/org/isoron/uhabits/ui/habits/list/views/CheckmarkPanelViewTest.java b/app/src/androidTest/java/org/isoron/uhabits/ui/habits/list/views/CheckmarkPanelViewTest.java index 684f82746..878dc5c9f 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/ui/habits/list/views/CheckmarkPanelViewTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/ui/habits/list/views/CheckmarkPanelViewTest.java @@ -62,7 +62,7 @@ public class CheckmarkPanelViewTest extends BaseViewTest view.setCheckmarkValues(checkmarks); view.setColor(ColorUtils.getAndroidTestColor(7)); - measureView(dpToPixels(200), dpToPixels(200), view); + measureView(view, dpToPixels(200), dpToPixels(200)); } // protected void waitForLatch() throws InterruptedException diff --git a/app/src/androidTest/java/org/isoron/uhabits/ui/widgets/CheckmarkWidgetTest.java b/app/src/androidTest/java/org/isoron/uhabits/ui/widgets/CheckmarkWidgetTest.java new file mode 100644 index 000000000..20a25bb07 --- /dev/null +++ b/app/src/androidTest/java/org/isoron/uhabits/ui/widgets/CheckmarkWidgetTest.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2016 Álinson Santos Xavier + * + * 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 . + */ + +package org.isoron.uhabits.ui.widgets; + +import android.content.*; +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(CHECKED_IMPLICITLY)); + } + + @Test + public void testIsInstalled() + { + ComponentName provider = + new ComponentName(targetContext, CheckmarkWidgetProvider.class); + + assertWidgetProviderIsInstalled(provider); + } + + @Test + public void testRender() throws Exception + { + assertRenders(view, PATH + "checked.png"); + } +} diff --git a/app/src/androidTest/java/org/isoron/uhabits/widgets/views/CheckmarkWidgetViewTest.java b/app/src/androidTest/java/org/isoron/uhabits/ui/widgets/views/CheckmarkWidgetViewTest.java similarity index 67% rename from app/src/androidTest/java/org/isoron/uhabits/widgets/views/CheckmarkWidgetViewTest.java rename to app/src/androidTest/java/org/isoron/uhabits/ui/widgets/views/CheckmarkWidgetViewTest.java index 9b7fc14d3..fe64291af 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/widgets/views/CheckmarkWidgetViewTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/ui/widgets/views/CheckmarkWidgetViewTest.java @@ -17,20 +17,18 @@ * with this program. If not, see . */ -package org.isoron.uhabits.widgets.views; +package org.isoron.uhabits.ui.widgets.views; -import android.support.test.runner.AndroidJUnit4; +import android.support.test.runner.*; import android.test.suitebuilder.annotation.*; import org.isoron.uhabits.*; -import org.isoron.uhabits.models.Habit; -import org.isoron.uhabits.utils.DateUtils; -import org.isoron.uhabits.utils.InterfaceUtils; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.isoron.uhabits.models.*; +import org.isoron.uhabits.utils.*; +import org.junit.*; +import org.junit.runner.*; -import java.io.IOException; +import java.io.*; @RunWith(AndroidJUnit4.class) @MediumTest @@ -51,9 +49,16 @@ public class CheckmarkWidgetViewTest extends BaseViewTest habit = fixtures.createShortHabit(); view = new CheckmarkWidgetView(targetContext); - view.setHabit(habit); - refreshData(view); - measureView(dpToPixels(100), dpToPixels(200), view); + 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 @@ -65,29 +70,23 @@ public class CheckmarkWidgetViewTest extends BaseViewTest @Test public void testRender_implicitlyChecked() throws IOException { - long today = DateUtils.getStartOfToday(); - long day = DateUtils.millisecondsInOneDay; - habit.getRepetitions().toggleTimestamp(today); - habit.getRepetitions().toggleTimestamp(today - day); - habit.getRepetitions().toggleTimestamp(today - 2 * day); - view.refreshData(); - + view.setCheckmarkValue(Checkmark.CHECKED_IMPLICITLY); + view.refresh(); assertRenders(view, PATH + "implicitly_checked.png"); } @Test public void testRender_largeSize() throws IOException { - measureView(dpToPixels(300), dpToPixels(300), view); + measureView(view, dpToPixels(300), dpToPixels(300)); assertRenders(view, PATH + "large_size.png"); } @Test public void testRender_unchecked() throws IOException { - habit.getRepetitions().toggleTimestamp(DateUtils.getStartOfToday()); - view.refreshData(); - + view.setCheckmarkValue(Checkmark.UNCHECKED); + view.refresh(); assertRenders(view, PATH + "unchecked.png"); } } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index fe419041b..3382151ec 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -81,7 +81,7 @@ android:theme="@style/Theme.AppCompat.Light.NoActionBar"/> @@ -108,53 +108,53 @@ android:resource="@xml/widget_checkmark_info"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/java/org/isoron/uhabits/AndroidModule.java b/app/src/main/java/org/isoron/uhabits/AndroidModule.java index 21f5bb008..3a371d2e8 100644 --- a/app/src/main/java/org/isoron/uhabits/AndroidModule.java +++ b/app/src/main/java/org/isoron/uhabits/AndroidModule.java @@ -63,4 +63,11 @@ public class AndroidModule { return new Preferences(); } + + @Provides + @Singleton + WidgetPreferences provideWidgetPreferences() + { + return new WidgetPreferences(); + } } diff --git a/app/src/main/java/org/isoron/uhabits/BaseComponent.java b/app/src/main/java/org/isoron/uhabits/BaseComponent.java index 28f39fe9a..feb93f511 100644 --- a/app/src/main/java/org/isoron/uhabits/BaseComponent.java +++ b/app/src/main/java/org/isoron/uhabits/BaseComponent.java @@ -30,6 +30,7 @@ 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.*; /** @@ -90,4 +91,8 @@ public interface BaseComponent void inject(BaseDialogFragment baseDialogFragment); void inject(ShowHabitController showHabitController); + + void inject(BaseWidget baseWidget); + + void inject(WidgetUpdater widgetManager); } diff --git a/app/src/main/java/org/isoron/uhabits/HabitBroadcastReceiver.java b/app/src/main/java/org/isoron/uhabits/HabitBroadcastReceiver.java index 5cbdb5bfe..6a0d1f431 100644 --- a/app/src/main/java/org/isoron/uhabits/HabitBroadcastReceiver.java +++ b/app/src/main/java/org/isoron/uhabits/HabitBroadcastReceiver.java @@ -27,14 +27,12 @@ import android.os.*; import android.preference.*; import android.support.v4.app.*; import android.support.v4.app.TaskStackBuilder; -import android.support.v4.content.*; import org.isoron.uhabits.commands.*; import org.isoron.uhabits.models.*; import org.isoron.uhabits.tasks.*; import org.isoron.uhabits.ui.habits.show.*; import org.isoron.uhabits.utils.*; -import org.isoron.uhabits.widgets.*; import java.util.*; @@ -124,16 +122,6 @@ public class HabitBroadcastReceiver extends BroadcastReceiver notificationManager.cancel(notificationId); } - public static void sendRefreshBroadcast(Context context) - { - LocalBroadcastManager manager = - LocalBroadcastManager.getInstance(context); - Intent refreshIntent = new Intent(HabitsApplication.ACTION_REFRESH); - manager.sendBroadcast(refreshIntent); - - WidgetManager.updateWidgets(context); - } - @Override public void onReceive(final Context context, Intent intent) { @@ -178,7 +166,6 @@ public class HabitBroadcastReceiver extends BroadcastReceiver } dismissNotification(context, habitId); - sendRefreshBroadcast(context); } private boolean checkWeekday(Intent intent, Habit habit) diff --git a/app/src/main/java/org/isoron/uhabits/HabitsApplication.java b/app/src/main/java/org/isoron/uhabits/HabitsApplication.java index 72e3c38c3..53fb23d24 100644 --- a/app/src/main/java/org/isoron/uhabits/HabitsApplication.java +++ b/app/src/main/java/org/isoron/uhabits/HabitsApplication.java @@ -25,21 +25,16 @@ import android.support.annotation.*; import com.activeandroid.*; -import org.isoron.uhabits.models.*; +import org.isoron.uhabits.ui.widgets.*; import org.isoron.uhabits.utils.*; import java.io.*; -import javax.inject.*; - /** * The Android application for Loop Habit Tracker. */ public class HabitsApplication extends Application { - public static final String ACTION_REFRESH = - "org.isoron.uhabits.ACTION_REFRESH"; - public static final int RESULT_BUG_REPORT = 4; public static final int RESULT_EXPORT_CSV = 2; @@ -56,19 +51,13 @@ public class HabitsApplication extends Application @Nullable private static Context context; - @Inject - HabitList habitList; + private static WidgetUpdater widgetManager; public static BaseComponent getComponent() { return component; } - public HabitList getHabitList() - { - return habitList; - } - public static void setComponent(BaseComponent component) { HabitsApplication.component = component; @@ -86,6 +75,15 @@ public class HabitsApplication extends Application return application; } + @NonNull + public static WidgetUpdater getWidgetManager() + { + if (widgetManager == null) + throw new RuntimeException("widgetManager is null"); + + return widgetManager; + } + public static boolean isTestMode() { try @@ -108,6 +106,7 @@ public class HabitsApplication extends Application HabitsApplication.context = this; HabitsApplication.application = this; component = DaggerAndroidComponent.builder().build(); + component.inject(this); if (isTestMode()) { @@ -115,7 +114,9 @@ public class HabitsApplication extends Application if (db.exists()) db.delete(); } - component.inject(this); + widgetManager = new WidgetUpdater(this); + widgetManager.startListening(); + DatabaseUtils.initializeActiveAndroid(); } @@ -124,6 +125,7 @@ public class HabitsApplication extends Application { HabitsApplication.context = null; ActiveAndroid.dispose(); + widgetManager.stopListening(); super.onTerminate(); } } diff --git a/app/src/main/java/org/isoron/uhabits/ui/BaseScreen.java b/app/src/main/java/org/isoron/uhabits/ui/BaseScreen.java index 910896284..08b30526b 100644 --- a/app/src/main/java/org/isoron/uhabits/ui/BaseScreen.java +++ b/app/src/main/java/org/isoron/uhabits/ui/BaseScreen.java @@ -160,7 +160,7 @@ public abstract class BaseScreen * * @param rootView the root view for this screen. */ - public void setRootView(@Nullable BaseRootView rootView) + protected void setRootView(@Nullable BaseRootView rootView) { this.rootView = rootView; activity.setContentView(rootView); diff --git a/app/src/main/java/org/isoron/uhabits/ui/BaseSystem.java b/app/src/main/java/org/isoron/uhabits/ui/BaseSystem.java index e1c7e106c..ec39c0270 100644 --- a/app/src/main/java/org/isoron/uhabits/ui/BaseSystem.java +++ b/app/src/main/java/org/isoron/uhabits/ui/BaseSystem.java @@ -28,7 +28,6 @@ import org.isoron.uhabits.*; import org.isoron.uhabits.models.*; import org.isoron.uhabits.tasks.*; import org.isoron.uhabits.utils.*; -import org.isoron.uhabits.widgets.*; import java.io.*; import java.lang.Process; @@ -119,22 +118,6 @@ public class BaseSystem }.execute(); } - /** - * Refreshes all application widgets. - */ - public void updateWidgets() - { - new BaseTask() - { - - @Override - protected void doInBackground() - { - WidgetManager.updateWidgets(context); - } - }.execute(); - } - private String getDeviceInfo() { if (context == null) return "null context\n"; diff --git a/app/src/main/java/org/isoron/uhabits/ui/common/views/ScoreChart.java b/app/src/main/java/org/isoron/uhabits/ui/common/views/ScoreChart.java index 9afad1595..969be04de 100644 --- a/app/src/main/java/org/isoron/uhabits/ui/common/views/ScoreChart.java +++ b/app/src/main/java/org/isoron/uhabits/ui/common/views/ScoreChart.java @@ -137,7 +137,7 @@ public class ScoreChart extends ScrollableChart requestLayout(); } - public void setPrimaryColor(int primaryColor) + public void setColor(int primaryColor) { this.primaryColor = primaryColor; postInvalidate(); diff --git a/app/src/main/java/org/isoron/uhabits/ui/habits/list/ListHabitsActivity.java b/app/src/main/java/org/isoron/uhabits/ui/habits/list/ListHabitsActivity.java index 9ca8d433e..2a93436bb 100644 --- a/app/src/main/java/org/isoron/uhabits/ui/habits/list/ListHabitsActivity.java +++ b/app/src/main/java/org/isoron/uhabits/ui/habits/list/ListHabitsActivity.java @@ -19,14 +19,14 @@ package org.isoron.uhabits.ui.habits.list; -import android.os.Bundle; +import android.os.*; -import org.isoron.uhabits.HabitsApplication; -import org.isoron.uhabits.models.HabitList; -import org.isoron.uhabits.ui.BaseActivity; -import org.isoron.uhabits.ui.BaseSystem; +import org.isoron.uhabits.*; +import org.isoron.uhabits.models.*; +import org.isoron.uhabits.ui.*; +import org.isoron.uhabits.ui.habits.list.model.*; -import javax.inject.Inject; +import javax.inject.*; /** * Activity that allows the user to see and modify the list of habits. @@ -36,19 +36,55 @@ public class ListHabitsActivity extends BaseActivity @Inject HabitList habitList; + private HabitCardListAdapter adapter; + + private ListHabitsRootView rootView; + + private ListHabitsScreen screen; + + private ListHabitsMenu menu; + + private ListHabitsSelectionMenu selectionMenu; + + private ListHabitsController controller; + + private BaseSystem system; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); HabitsApplication.getComponent().inject(this); - BaseSystem system = new BaseSystem(this); - ListHabitsScreen screen = new ListHabitsScreen(this); - ListHabitsController controller = - new ListHabitsController(screen, system, habitList); + int checkmarkCount = ListHabitsRootView.MAX_CHECKMARK_COUNT; + + system = new BaseSystem(this); + adapter = new HabitCardListAdapter(checkmarkCount); + rootView = new ListHabitsRootView(this, adapter); + screen = new ListHabitsScreen(this, rootView); + menu = new ListHabitsMenu(this, screen, adapter); + selectionMenu = new ListHabitsSelectionMenu(screen, adapter); + controller = new ListHabitsController(screen, system, habitList); + + screen.setMenu(menu); + screen.setSelectionMenu(selectionMenu); + rootView.setController(controller, selectionMenu); - screen.setController(controller); setScreen(screen); controller.onStartup(); } + + @Override + protected void onPause() + { + adapter.cancelRefresh(); + super.onPause(); + } + + @Override + protected void onResume() + { + super.onResume(); + adapter.refresh(); + } } diff --git a/app/src/main/java/org/isoron/uhabits/ui/habits/list/ListHabitsController.java b/app/src/main/java/org/isoron/uhabits/ui/habits/list/ListHabitsController.java index 6af715be0..f36dc42f8 100644 --- a/app/src/main/java/org/isoron/uhabits/ui/habits/list/ListHabitsController.java +++ b/app/src/main/java/org/isoron/uhabits/ui/habits/list/ListHabitsController.java @@ -95,7 +95,7 @@ public class ListHabitsController habitList.reorder(from, to); } - public void onImportData(File file) + public void onImportData(@NonNull File file) { ImportDataTask task = new ImportDataTask(file, screen.getProgressBar()); task.setListener(this); @@ -163,7 +163,6 @@ public class ListHabitsController if (prefs.isFirstRun()) onFirstRun(); new Handler().postDelayed(() -> { - system.updateWidgets(); system.scheduleReminders(); }, 1000); } diff --git a/app/src/main/java/org/isoron/uhabits/ui/habits/list/ListHabitsMenu.java b/app/src/main/java/org/isoron/uhabits/ui/habits/list/ListHabitsMenu.java index 8349499f9..b518a33be 100644 --- a/app/src/main/java/org/isoron/uhabits/ui/habits/list/ListHabitsMenu.java +++ b/app/src/main/java/org/isoron/uhabits/ui/habits/list/ListHabitsMenu.java @@ -26,6 +26,7 @@ import android.view.MenuItem; import org.isoron.uhabits.R; import org.isoron.uhabits.ui.BaseActivity; import org.isoron.uhabits.ui.BaseMenu; +import org.isoron.uhabits.ui.habits.list.model.*; import org.isoron.uhabits.utils.InterfaceUtils; public class ListHabitsMenu extends BaseMenu @@ -35,11 +36,15 @@ public class ListHabitsMenu extends BaseMenu private boolean showArchived; + private final HabitCardListAdapter adapter; + public ListHabitsMenu(@NonNull BaseActivity activity, - @NonNull ListHabitsScreen screen) + @NonNull ListHabitsScreen screen, + @NonNull HabitCardListAdapter adapter) { super(activity); this.screen = screen; + this.adapter = adapter; } @Override @@ -79,7 +84,7 @@ public class ListHabitsMenu extends BaseMenu case R.id.action_show_archived: showArchived = !showArchived; - screen.getRootView().setShowArchived(showArchived); + adapter.setShowArchived(showArchived); invalidate(); return true; diff --git a/app/src/main/java/org/isoron/uhabits/ui/habits/list/ListHabitsRootView.java b/app/src/main/java/org/isoron/uhabits/ui/habits/list/ListHabitsRootView.java index 65c3b1b47..d534098f7 100644 --- a/app/src/main/java/org/isoron/uhabits/ui/habits/list/ListHabitsRootView.java +++ b/app/src/main/java/org/isoron/uhabits/ui/habits/list/ListHabitsRootView.java @@ -59,13 +59,27 @@ public class ListHabitsRootView extends BaseRootView @BindView(R.id.hintView) HintView hintView; - @Nullable - private HabitCardListAdapter listAdapter; + @NonNull + private final HabitCardListAdapter listAdapter; - public ListHabitsRootView(@NonNull Context context) + public ListHabitsRootView(@NonNull Context context, + @NonNull HabitCardListAdapter listAdapter) { super(context); - init(); + addView(inflate(getContext(), R.layout.list_habits, null)); + ButterKnife.bind(this); + + this.listAdapter = listAdapter; + listView.setAdapter(listAdapter); + listAdapter.setListView(listView); + + tvStarEmpty.setTypeface(InterfaceUtils.getFontAwesome(getContext())); + initToolbar(); + + String hints[] = + getContext().getResources().getStringArray(R.array.hints); + HintList hintList = new HintList(hints); + hintView.setHints(hintList); } public static int getCheckmarkCount(View v) @@ -84,12 +98,6 @@ public class ListHabitsRootView extends BaseRootView return progressBar; } - public void setShowArchived(boolean showArchived) - { - if (listAdapter == null) return; - listAdapter.setShowArchived(showArchived); - } - @NonNull @Override public Toolbar getToolbar() @@ -103,12 +111,9 @@ public class ListHabitsRootView extends BaseRootView updateEmptyView(); } - public void setController(@Nullable ListHabitsController controller, - @Nullable ListHabitsSelectionMenu menu) + public void setController(@NonNull ListHabitsController controller, + @NonNull ListHabitsSelectionMenu menu) { - listView.setController(null); - if (controller == null || menu == null || listAdapter == null) return; - HabitCardListController listController = new HabitCardListController(listAdapter, listView); @@ -118,49 +123,23 @@ public class ListHabitsRootView extends BaseRootView menu.setListController(listController); } - public void setListAdapter(@NonNull HabitCardListAdapter listAdapter) - { - if (this.listAdapter != null) - listAdapter.getObservable().removeListener(this); - - this.listAdapter = listAdapter; - listView.setAdapter(listAdapter); - listAdapter.setListView(listView); - } - @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); - if (listAdapter != null) listAdapter.getObservable().addListener(this); + listAdapter.getObservable().addListener(this); } @Override protected void onDetachedFromWindow() { - if (listAdapter != null) - listAdapter.getObservable().removeListener(this); + listAdapter.getObservable().removeListener(this); super.onDetachedFromWindow(); } - private void init() - { - addView(inflate(getContext(), R.layout.list_habits, null)); - ButterKnife.bind(this); - - tvStarEmpty.setTypeface(InterfaceUtils.getFontAwesome(getContext())); - initToolbar(); - - String hints[] = - getContext().getResources().getStringArray(R.array.hints); - HintList hintList = new HintList(hints); - hintView.setHints(hintList); - } private void updateEmptyView() { - if (listAdapter == null) return; - llEmpty.setVisibility( listAdapter.getCount() > 0 ? View.GONE : View.VISIBLE); } diff --git a/app/src/main/java/org/isoron/uhabits/ui/habits/list/ListHabitsScreen.java b/app/src/main/java/org/isoron/uhabits/ui/habits/list/ListHabitsScreen.java index 794574b1a..8d22d5eb0 100644 --- a/app/src/main/java/org/isoron/uhabits/ui/habits/list/ListHabitsScreen.java +++ b/app/src/main/java/org/isoron/uhabits/ui/habits/list/ListHabitsScreen.java @@ -32,7 +32,6 @@ import org.isoron.uhabits.models.*; import org.isoron.uhabits.ui.*; import org.isoron.uhabits.ui.about.*; import org.isoron.uhabits.ui.habits.edit.*; -import org.isoron.uhabits.ui.habits.list.model.*; import org.isoron.uhabits.ui.habits.show.*; import org.isoron.uhabits.ui.intro.*; import org.isoron.uhabits.ui.settings.*; @@ -42,37 +41,14 @@ import java.io.*; public class ListHabitsScreen extends BaseScreen { - @Nullable ListHabitsController controller; - @NonNull - private final ListHabitsRootView rootView; - - @NonNull - private final ListHabitsSelectionMenu selectionMenu; - - public ListHabitsScreen(@NonNull BaseActivity activity) + public ListHabitsScreen(@NonNull BaseActivity activity, + ListHabitsRootView rootView) { super(activity); - rootView = new ListHabitsRootView(activity); setRootView(rootView); - - ListHabitsMenu menu = new ListHabitsMenu(activity, this); - selectionMenu = new ListHabitsSelectionMenu(this); - setMenu(menu); - setSelectionMenu(selectionMenu); - - HabitCardListAdapter adapter = - new HabitCardListAdapter(ListHabitsRootView.MAX_CHECKMARK_COUNT); - rootView.setListAdapter(adapter); - selectionMenu.setListAdapter(adapter); - } - - @NonNull - public ListHabitsRootView getRootView() - { - return rootView; } @Override @@ -100,12 +76,6 @@ public class ListHabitsScreen extends BaseScreen } } - public void setController(@Nullable ListHabitsController controller) - { - this.controller = controller; - rootView.setController(controller, selectionMenu); - } - public void showAboutScreen() { Intent intent = new Intent(activity, AboutActivity.class); diff --git a/app/src/main/java/org/isoron/uhabits/ui/habits/list/ListHabitsSelectionMenu.java b/app/src/main/java/org/isoron/uhabits/ui/habits/list/ListHabitsSelectionMenu.java index 540f71e90..07980edca 100644 --- a/app/src/main/java/org/isoron/uhabits/ui/habits/list/ListHabitsSelectionMenu.java +++ b/app/src/main/java/org/isoron/uhabits/ui/habits/list/ListHabitsSelectionMenu.java @@ -42,16 +42,18 @@ public class ListHabitsSelectionMenu extends BaseSelectionMenu @Inject CommandRunner commandRunner; - @Nullable - private HabitCardListAdapter listAdapter; + @NonNull + private final HabitCardListAdapter listAdapter; @Nullable private HabitCardListController listController; - public ListHabitsSelectionMenu(@NonNull ListHabitsScreen screen) + public ListHabitsSelectionMenu(@NonNull ListHabitsScreen screen, + HabitCardListAdapter listAdapter) { this.screen = screen; HabitsApplication.getComponent().inject(this); + this.listAdapter = listAdapter; } @Override @@ -64,8 +66,6 @@ public class ListHabitsSelectionMenu extends BaseSelectionMenu @Override public boolean onItemClicked(@NonNull MenuItem item) { - if (listAdapter == null) return false; - List selected = listAdapter.getSelected(); if (selected.isEmpty()) return false; @@ -104,7 +104,6 @@ public class ListHabitsSelectionMenu extends BaseSelectionMenu @Override public boolean onPrepare(@NonNull Menu menu) { - if (listAdapter == null) return false; List selected = listAdapter.getSelected(); boolean showEdit = (selected.size() == 1); @@ -149,12 +148,6 @@ public class ListHabitsSelectionMenu extends BaseSelectionMenu screen.startSelection(); } - public void setListAdapter(@Nullable HabitCardListAdapter listAdapter) - { - if (listAdapter == null) return; - this.listAdapter = listAdapter; - } - public void setListController(HabitCardListController listController) { this.listController = listController; diff --git a/app/src/main/java/org/isoron/uhabits/ui/habits/list/controllers/CheckmarkButtonController.java b/app/src/main/java/org/isoron/uhabits/ui/habits/list/controllers/CheckmarkButtonController.java index 234d87f94..d5c5dc078 100644 --- a/app/src/main/java/org/isoron/uhabits/ui/habits/list/controllers/CheckmarkButtonController.java +++ b/app/src/main/java/org/isoron/uhabits/ui/habits/list/controllers/CheckmarkButtonController.java @@ -93,6 +93,6 @@ public class CheckmarkButtonController void onInvalidToggle(); - void onToggle(Habit habit, long timestamp); + void onToggle(@NonNull Habit habit, long timestamp); } } diff --git a/app/src/main/java/org/isoron/uhabits/ui/habits/list/controllers/HabitCardController.java b/app/src/main/java/org/isoron/uhabits/ui/habits/list/controllers/HabitCardController.java index f2f2b662b..f62fca8a2 100644 --- a/app/src/main/java/org/isoron/uhabits/ui/habits/list/controllers/HabitCardController.java +++ b/app/src/main/java/org/isoron/uhabits/ui/habits/list/controllers/HabitCardController.java @@ -19,7 +19,7 @@ package org.isoron.uhabits.ui.habits.list.controllers; -import android.support.annotation.Nullable; +import android.support.annotation.*; import org.isoron.uhabits.models.Habit; import org.isoron.uhabits.ui.habits.list.views.HabitCardView; @@ -39,7 +39,7 @@ public class HabitCardController implements HabitCardView.Controller } @Override - public void onToggle(Habit habit, long timestamp) + public void onToggle(@NonNull Habit habit, long timestamp) { if (view != null) view.triggerRipple(timestamp); if (listener != null) listener.onToggle(habit, timestamp); diff --git a/app/src/main/java/org/isoron/uhabits/ui/habits/list/controllers/HabitCardListController.java b/app/src/main/java/org/isoron/uhabits/ui/habits/list/controllers/HabitCardListController.java index 192d92f21..b1137b6ad 100644 --- a/app/src/main/java/org/isoron/uhabits/ui/habits/list/controllers/HabitCardListController.java +++ b/app/src/main/java/org/isoron/uhabits/ui/habits/list/controllers/HabitCardListController.java @@ -147,7 +147,7 @@ public class HabitCardListController implements DragSortListView.DropListener, * @param timestamp the timestamps of the checkmark */ @Override - public void onToggle(Habit habit, long timestamp) + public void onToggle(@NonNull Habit habit, long timestamp) { if (habitListener != null) habitListener.onToggle(habit, timestamp); } @@ -203,7 +203,7 @@ public class HabitCardListController implements DragSortListView.DropListener, * * @param habit the habit clicked */ - void onHabitClick(Habit habit); + void onHabitClick(@NonNull Habit habit); /** * Called when the user wants to change the position of a habit on the @@ -212,7 +212,7 @@ public class HabitCardListController implements DragSortListView.DropListener, * @param from habit to be moved * @param to habit that currently occupies the desired position */ - void onHabitReorder(Habit from, Habit to); + void onHabitReorder(@NonNull Habit from, @NonNull Habit to); } /** diff --git a/app/src/main/java/org/isoron/uhabits/ui/habits/list/model/HabitCardListAdapter.java b/app/src/main/java/org/isoron/uhabits/ui/habits/list/model/HabitCardListAdapter.java index 1d2750316..0f9dab743 100644 --- a/app/src/main/java/org/isoron/uhabits/ui/habits/list/model/HabitCardListAdapter.java +++ b/app/src/main/java/org/isoron/uhabits/ui/habits/list/model/HabitCardListAdapter.java @@ -62,6 +62,11 @@ public class HabitCardListAdapter extends BaseAdapter cache.setCheckmarkCount(checkmarkCount); } + public void cancelRefresh() + { + cache.cancelTasks(); + } + /** * Sets all items as not selected. */ @@ -77,11 +82,6 @@ public class HabitCardListAdapter extends BaseAdapter return cache.getHabitCount(); } - public boolean getIncludeArchived() - { - return cache.getIncludeArchived(); - } - /** * Returns the item that occupies a certain position on the list * @@ -163,6 +163,11 @@ public class HabitCardListAdapter extends BaseAdapter cache.onDetached(); } + public void refresh() + { + cache.refreshAllHabits(true); + } + /** * Changes the order of habits on the adapter. *

diff --git a/app/src/main/java/org/isoron/uhabits/ui/habits/list/model/HabitCardListCache.java b/app/src/main/java/org/isoron/uhabits/ui/habits/list/model/HabitCardListCache.java index 2cc1a108b..65a3fb9ed 100644 --- a/app/src/main/java/org/isoron/uhabits/ui/habits/list/model/HabitCardListCache.java +++ b/app/src/main/java/org/isoron/uhabits/ui/habits/list/model/HabitCardListCache.java @@ -69,6 +69,12 @@ public class HabitCardListCache implements CommandRunner.Listener HabitsApplication.getComponent().inject(this); } + public void cancelTasks() + { + if(currentFetchTask != null) + currentFetchTask.cancel(true); + } + public int[] getCheckmarks(long habitId) { return data.checkmarks.get(habitId); @@ -256,6 +262,7 @@ public class HabitCardListCache implements CommandRunner.Listener newData.copyScoresFrom(data); newData.copyCheckmarksFrom(data); +// sleep(1000); commit(); if (!refreshScoresAndCheckmarks) return; @@ -274,10 +281,23 @@ public class HabitCardListCache implements CommandRunner.Listener newData.checkmarks.put(id, h.getCheckmarks().getValues(dateFrom, dateTo)); +// sleep(1000); publishProgress(current++, newData.habits.size()); } } + private void sleep(int time) + { + try + { + Thread.sleep(time); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + } + @Override protected void onPostExecute(Void aVoid) { diff --git a/app/src/main/java/org/isoron/uhabits/ui/habits/show/ShowHabitActivity.java b/app/src/main/java/org/isoron/uhabits/ui/habits/show/ShowHabitActivity.java index ebff34915..55e95d4e8 100644 --- a/app/src/main/java/org/isoron/uhabits/ui/habits/show/ShowHabitActivity.java +++ b/app/src/main/java/org/isoron/uhabits/ui/habits/show/ShowHabitActivity.java @@ -40,6 +40,14 @@ public class ShowHabitActivity extends BaseActivity @Inject HabitList habitList; + private ShowHabitController controller; + + private ShowHabitRootView rootView; + + private ShowHabitScreen screen; + + private ShowHabitsMenu menu; + @Override protected void onCreate(Bundle savedInstanceState) { @@ -47,15 +55,15 @@ public class ShowHabitActivity extends BaseActivity HabitsApplication.getComponent().inject(this); Habit habit = getHabitFromIntent(); - ShowHabitScreen screen = new ShowHabitScreen(this, habit); - ShowHabitRootView view = new ShowHabitRootView(this, habit); - screen.setRootView(view); - this.setScreen(screen); + rootView = new ShowHabitRootView(this, habit); + screen = new ShowHabitScreen(this, habit, rootView); + setScreen(screen); - ShowHabitsMenu menu = new ShowHabitsMenu(this, screen); - ShowHabitController controller = new ShowHabitController(screen, habit); + menu = new ShowHabitsMenu(this, screen); screen.setMenu(menu); - view.setController(controller); + + controller = new ShowHabitController(screen, habit); + rootView.setController(controller); } @NonNull diff --git a/app/src/main/java/org/isoron/uhabits/ui/habits/show/ShowHabitScreen.java b/app/src/main/java/org/isoron/uhabits/ui/habits/show/ShowHabitScreen.java index 947a95a35..d5f7cf21b 100644 --- a/app/src/main/java/org/isoron/uhabits/ui/habits/show/ShowHabitScreen.java +++ b/app/src/main/java/org/isoron/uhabits/ui/habits/show/ShowHabitScreen.java @@ -31,10 +31,13 @@ public class ShowHabitScreen extends BaseScreen @NonNull private final Habit habit; - public ShowHabitScreen(@NonNull BaseActivity activity, @NonNull Habit habit) + public ShowHabitScreen(@NonNull BaseActivity activity, + @NonNull Habit habit, + ShowHabitRootView view) { super(activity); this.habit = habit; + setRootView(view); } public void showEditHabitDialog() diff --git a/app/src/main/java/org/isoron/uhabits/ui/habits/show/views/HistoryCard.java b/app/src/main/java/org/isoron/uhabits/ui/habits/show/views/HistoryCard.java index 03b663715..c901595ed 100644 --- a/app/src/main/java/org/isoron/uhabits/ui/habits/show/views/HistoryCard.java +++ b/app/src/main/java/org/isoron/uhabits/ui/habits/show/views/HistoryCard.java @@ -58,7 +58,6 @@ public class HistoryCard extends HabitCard @OnClick(R.id.edit) public void onClickEditButton() { - Log.d("HistoryCard", "onClickEditButton"); controller.onEditHistoryButtonClick(); } diff --git a/app/src/main/java/org/isoron/uhabits/ui/habits/show/views/ScoreCard.java b/app/src/main/java/org/isoron/uhabits/ui/habits/show/views/ScoreCard.java index 95ff4b351..ea82cdaff 100644 --- a/app/src/main/java/org/isoron/uhabits/ui/habits/show/views/ScoreCard.java +++ b/app/src/main/java/org/isoron/uhabits/ui/habits/show/views/ScoreCard.java @@ -107,7 +107,7 @@ public class ScoreCard extends HabitCard { spinner.setVisibility(GONE); title.setTextColor(ColorUtils.getAndroidTestColor(1)); - chart.setPrimaryColor(ColorUtils.getAndroidTestColor(1)); + chart.setColor(ColorUtils.getAndroidTestColor(1)); chart.populateWithRandomData(); } } @@ -141,7 +141,7 @@ public class ScoreCard extends HabitCard int color = ColorUtils.getColor(getContext(), getHabit().getColor()); title.setTextColor(color); - chart.setPrimaryColor(color); + chart.setColor(color); } } } diff --git a/app/src/main/java/org/isoron/uhabits/ui/widgets/BaseWidget.java b/app/src/main/java/org/isoron/uhabits/ui/widgets/BaseWidget.java new file mode 100644 index 000000000..830922833 --- /dev/null +++ b/app/src/main/java/org/isoron/uhabits/ui/widgets/BaseWidget.java @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2016 Álinson Santos Xavier + * + * 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 . + */ + +package org.isoron.uhabits.ui.widgets; + +import android.app.*; +import android.content.*; +import android.graphics.*; +import android.support.annotation.*; +import android.view.*; +import android.widget.*; + +import org.isoron.uhabits.*; +import org.isoron.uhabits.utils.*; + +import javax.inject.*; + +import static android.os.Build.VERSION.*; +import static android.os.Build.VERSION_CODES.*; +import static android.view.View.MeasureSpec.*; + +public abstract class BaseWidget +{ + @Inject + WidgetPreferences preferences; + + private final int id; + + @NonNull + private WidgetDimensions dimensions; + + @NonNull + private final Context context; + + public BaseWidget(@NonNull Context context, int id) + { + this.id = id; + this.context = context; + HabitsApplication.getComponent().inject(this); + dimensions = new WidgetDimensions(0, 0, 0, 0); + } + + public void delete() + { + preferences.removeWidget(id); + } + + @NonNull + public Context getContext() + { + return context; + } + + public int getId() + { + return id; + } + + @NonNull + public RemoteViews getLandscapeRemoteViews() + { + return getRemoteViews(dimensions.getLandscapeWidth(), + dimensions.getLandscapeHeight()); + } + + public abstract int getLayoutId(); + + public abstract PendingIntent getOnClickPendingIntent(Context context); + + @NonNull + public RemoteViews getPortraitRemoteViews() + { + return getRemoteViews(dimensions.getPortraitWidth(), + dimensions.getPortraitHeight()); + } + + public abstract void refreshData(View widgetView); + + public void setDimensions(@NonNull WidgetDimensions dimensions) + { + this.dimensions = dimensions; + } + + protected abstract View buildView(); + + protected abstract int getDefaultHeight(); + + protected abstract int getDefaultWidth(); + + protected abstract String getTitle(); + + private void adjustRemoteViewsPadding(RemoteViews remoteViews, + View view, + int width, + int height) + { + int imageWidth = view.getMeasuredWidth(); + int imageHeight = view.getMeasuredHeight(); + int p[] = calculatePadding(width, height, imageWidth, imageHeight); + remoteViews.setViewPadding(R.id.buttonOverlay, p[0], p[1], p[2], p[3]); + } + + private void buildRemoteViews(View view, + RemoteViews remoteViews, + int width, + int height) + { + Bitmap bitmap = getBitmapFromView(view); + remoteViews.setTextViewText(R.id.label, getTitle()); + remoteViews.setImageViewBitmap(R.id.imageView, bitmap); + + if (SDK_INT >= JELLY_BEAN) + adjustRemoteViewsPadding(remoteViews, view, width, height); + + PendingIntent onClickIntent = getOnClickPendingIntent(context); + if (onClickIntent != null) + remoteViews.setOnClickPendingIntent(R.id.button, onClickIntent); + } + + private int[] calculatePadding(int entireWidth, + int entireHeight, + int imageWidth, + int imageHeight) + { + int w = (int) (((float) entireWidth - imageWidth) / 2); + int h = (int) (((float) entireHeight - imageHeight) / 2); + + return new int[]{ w, h, w, h }; + } + + private Bitmap getBitmapFromView(View view) + { + view.invalidate(); + view.setDrawingCacheEnabled(true); + view.buildDrawingCache(true); + return view.getDrawingCache(); + } + + @NonNull + private RemoteViews getRemoteViews(int width, int height) + { + View view = buildView(); + measureView(view, width, height); + + refreshData(view); + + if(view.isLayoutRequested()) + measureView(view, width, height); + + RemoteViews remoteViews = + new RemoteViews(context.getPackageName(), getLayoutId()); + + buildRemoteViews(view, remoteViews, width, height); + + return remoteViews; + } + + private void measureView(View view, int width, int height) + { + LayoutInflater inflater = LayoutInflater.from(context); + View entireView = inflater.inflate(getLayoutId(), null); + + int specWidth = makeMeasureSpec(width, View.MeasureSpec.EXACTLY); + int specHeight = makeMeasureSpec(height, View.MeasureSpec.EXACTLY); + + entireView.measure(specWidth, specHeight); + entireView.layout(0, 0, entireView.getMeasuredWidth(), + entireView.getMeasuredHeight()); + + View imageView = entireView.findViewById(R.id.imageView); + width = imageView.getMeasuredWidth(); + height = imageView.getMeasuredHeight(); + + specWidth = makeMeasureSpec(width, View.MeasureSpec.EXACTLY); + specHeight = makeMeasureSpec(height, View.MeasureSpec.EXACTLY); + + view.measure(specWidth, specHeight); + view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight()); + } +} diff --git a/app/src/main/java/org/isoron/uhabits/ui/widgets/CheckmarkWidget.java b/app/src/main/java/org/isoron/uhabits/ui/widgets/CheckmarkWidget.java new file mode 100644 index 000000000..fa7013b1f --- /dev/null +++ b/app/src/main/java/org/isoron/uhabits/ui/widgets/CheckmarkWidget.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2016 Álinson Santos Xavier + * + * 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 . + */ + +package org.isoron.uhabits.ui.widgets; + +import android.app.*; +import android.content.*; +import android.support.annotation.*; +import android.view.*; + +import org.isoron.uhabits.*; +import org.isoron.uhabits.models.*; +import org.isoron.uhabits.ui.widgets.views.*; +import org.isoron.uhabits.utils.*; + +public class CheckmarkWidget extends BaseWidget +{ + @NonNull + private final Habit habit; + + public CheckmarkWidget(@NonNull Context context, + int widgetId, + @NonNull Habit habit) + { + super(context, widgetId); + this.habit = habit; + } + + @Override + public int getLayoutId() + { + return R.layout.widget_wrapper; + } + + @Override + public PendingIntent getOnClickPendingIntent(Context context) + { + return HabitBroadcastReceiver.buildCheckIntent(context, habit, null); + } + + @Override + public void refreshData(View v) + { + CheckmarkWidgetView view = (CheckmarkWidgetView) v; + int color = ColorUtils.getColor(getContext(), habit.getColor()); + int score = habit.getScores().getTodayValue(); + float percentage = (float) score / Score.MAX_VALUE; + int checkmark = habit.getCheckmarks().getTodayValue(); + + view.setPercentage(percentage); + view.setActiveColor(color); + view.setName(habit.getName()); + view.setCheckmarkValue(checkmark); + view.refresh(); + } + + @Override + protected View buildView() + { + return new CheckmarkWidgetView(getContext()); + } + + @Override + protected int getDefaultHeight() + { + return 125; + } + + @Override + protected int getDefaultWidth() + { + return 125; + } + + @Override + protected String getTitle() + { + return habit.getName(); + } +} diff --git a/app/src/main/java/org/isoron/uhabits/widgets/HabitPickerDialog.java b/app/src/main/java/org/isoron/uhabits/ui/widgets/HabitPickerDialog.java similarity index 56% rename from app/src/main/java/org/isoron/uhabits/widgets/HabitPickerDialog.java rename to app/src/main/java/org/isoron/uhabits/ui/widgets/HabitPickerDialog.java index ffe682065..0126bc2f8 100644 --- a/app/src/main/java/org/isoron/uhabits/widgets/HabitPickerDialog.java +++ b/app/src/main/java/org/isoron/uhabits/ui/widgets/HabitPickerDialog.java @@ -17,38 +17,53 @@ * with this program. If not, see . */ -package org.isoron.uhabits.widgets; - -import android.app.Activity; -import android.appwidget.AppWidgetManager; -import android.content.Intent; -import android.content.SharedPreferences; -import android.os.Bundle; -import android.preference.PreferenceManager; -import android.view.View; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.ListView; - -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.ArrayList; -import java.util.List; - -import javax.inject.Inject; - -public class HabitPickerDialog extends Activity implements AdapterView.OnItemClickListener +package org.isoron.uhabits.ui.widgets; + +import android.app.*; +import android.content.*; +import android.os.*; +import android.view.*; +import android.widget.*; + +import org.isoron.uhabits.*; +import org.isoron.uhabits.models.*; +import org.isoron.uhabits.utils.*; + +import java.util.*; + +import javax.inject.*; + +import static android.appwidget.AppWidgetManager.*; + +public class HabitPickerDialog extends Activity + implements AdapterView.OnItemClickListener { @Inject HabitList habitList; + @Inject + WidgetPreferences preferences; + private Integer widgetId; private ArrayList habitIds; + @Override + public void onItemClick(AdapterView parent, + View view, + int position, + long id) + { + Long habitId = habitIds.get(position); + preferences.addWidget(widgetId, habitId); + HabitsApplication.getWidgetManager().updateWidgets(this); + + Intent resultValue = new Intent(); + resultValue.putExtra(EXTRA_APPWIDGET_ID, widgetId); + setResult(RESULT_OK, resultValue); + finish(); + } + @Override protected void onCreate(Bundle savedInstanceState) { @@ -59,8 +74,8 @@ public class HabitPickerDialog extends Activity implements AdapterView.OnItemCli Intent intent = getIntent(); Bundle extras = intent.getExtras(); - if (extras != null) widgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, - AppWidgetManager.INVALID_APPWIDGET_ID); + if (extras != null) + widgetId = extras.getInt(EXTRA_APPWIDGET_ID, INVALID_APPWIDGET_ID); ListView listView = (ListView) findViewById(R.id.listView); @@ -74,29 +89,11 @@ public class HabitPickerDialog extends Activity implements AdapterView.OnItemCli habitNames.add(h.getName()); } - ArrayAdapter adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, - habitNames); + ArrayAdapter adapter = + new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, + habitNames); listView.setAdapter(adapter); listView.setOnItemClickListener(this); } - @Override - public void onItemClick(AdapterView parent, View view, int position, long id) - { - Long habitId = habitIds.get(position); - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences( - getApplicationContext()); - prefs - .edit() - .putLong(BaseWidgetProvider.getHabitIdKey(widgetId), habitId) - .commit(); - - WidgetManager.updateWidgets(this); - - Intent resultValue = new Intent(); - resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId); - setResult(RESULT_OK, resultValue); - finish(); - } - } diff --git a/app/src/main/java/org/isoron/uhabits/ui/widgets/WidgetDimensions.java b/app/src/main/java/org/isoron/uhabits/ui/widgets/WidgetDimensions.java new file mode 100644 index 000000000..28285e34b --- /dev/null +++ b/app/src/main/java/org/isoron/uhabits/ui/widgets/WidgetDimensions.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2016 Álinson Santos Xavier + * + * 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 . + */ + +package org.isoron.uhabits.ui.widgets; + +public class WidgetDimensions +{ + private final int portraitWidth; + + private final int portraitHeight; + + private final int landscapeWidth; + + private final int landscapeHeight; + + public WidgetDimensions(int portraitWidth, + int portraitHeight, + int landscapeWidth, + int landscapeHeight) + { + this.portraitWidth = portraitWidth; + this.portraitHeight = portraitHeight; + this.landscapeWidth = landscapeWidth; + this.landscapeHeight = landscapeHeight; + } + + public int getLandscapeHeight() + { + return landscapeHeight; + } + + public int getLandscapeWidth() + { + return landscapeWidth; + } + + public int getPortraitHeight() + { + return portraitHeight; + } + + public int getPortraitWidth() + { + return portraitWidth; + } +} \ No newline at end of file diff --git a/app/src/main/java/org/isoron/uhabits/ui/widgets/WidgetUpdater.java b/app/src/main/java/org/isoron/uhabits/ui/widgets/WidgetUpdater.java new file mode 100644 index 000000000..f2a3d3cac --- /dev/null +++ b/app/src/main/java/org/isoron/uhabits/ui/widgets/WidgetUpdater.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2016 Álinson Santos Xavier + * + * 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 . + */ + +package org.isoron.uhabits.ui.widgets; + +import android.appwidget.*; +import android.content.*; +import android.support.annotation.*; + +import org.isoron.uhabits.*; +import org.isoron.uhabits.commands.*; +import org.isoron.uhabits.widgets.*; + +import javax.inject.*; + +/** + * A WidgetUpdater listens to the commands being executed by the application and + * updates the home-screen widgets accordingly. + *

+ * There should be only one instance of this class, created when the application + * starts. To access it, call HabitApplication.getWidgetUpdater(). + */ +public class WidgetUpdater implements CommandRunner.Listener +{ + @Inject + CommandRunner commandRunner; + + @NonNull + private final Context context; + + public WidgetUpdater(@NonNull Context context) + { + this.context = context; + HabitsApplication.getComponent().inject(this); + } + + @Override + public void onCommandExecuted(@NonNull Command command, + @Nullable Long refreshKey) + { + updateWidgets(context); + } + + /** + * Instructs the updater to start listening to commands. If any relevant + * commands are executed after this method is called, the corresponding + * widgets will get updated. + */ + public void startListening() + { + commandRunner.addListener(this); + } + + /** + * Instructs the updater to stop listening to commands. Every command + * executed after this method is called will be ignored by the updater. + */ + public void stopListening() + { + commandRunner.removeListener(this); + } + + 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 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); + } +} diff --git a/app/src/main/java/org/isoron/uhabits/widgets/views/CheckmarkWidgetView.java b/app/src/main/java/org/isoron/uhabits/ui/widgets/views/CheckmarkWidgetView.java similarity index 81% rename from app/src/main/java/org/isoron/uhabits/widgets/views/CheckmarkWidgetView.java rename to app/src/main/java/org/isoron/uhabits/ui/widgets/views/CheckmarkWidgetView.java index b4d8fc6ba..3d3bc3a8c 100644 --- a/app/src/main/java/org/isoron/uhabits/widgets/views/CheckmarkWidgetView.java +++ b/app/src/main/java/org/isoron/uhabits/ui/widgets/views/CheckmarkWidgetView.java @@ -17,26 +17,19 @@ * with this program. If not, see . */ -package org.isoron.uhabits.widgets.views; - -import android.content.Context; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.util.AttributeSet; -import android.util.TypedValue; -import android.widget.TextView; - -import org.isoron.uhabits.R; -import org.isoron.uhabits.models.Checkmark; -import org.isoron.uhabits.models.Habit; -import org.isoron.uhabits.models.Score; -import org.isoron.uhabits.ui.common.views.HabitChart; -import org.isoron.uhabits.ui.common.views.RingView; -import org.isoron.uhabits.utils.ColorUtils; -import org.isoron.uhabits.utils.InterfaceUtils; +package org.isoron.uhabits.ui.widgets.views; + +import android.content.*; +import android.support.annotation.*; +import android.util.*; +import android.widget.*; + +import org.isoron.uhabits.*; +import org.isoron.uhabits.models.*; +import org.isoron.uhabits.ui.common.views.*; +import org.isoron.uhabits.utils.*; public class CheckmarkWidgetView extends HabitWidgetView - implements HabitChart { private int activeColor; @@ -94,6 +87,10 @@ public class CheckmarkWidgetView extends HabitWidgetView R.attr.cardBackgroundColor); foregroundColor = InterfaceUtils.getStyledColor(context, R.attr.mediumContrastTextColor); + + setShadowAlpha(0x00); + rebuildBackground(); + break; case Checkmark.UNCHECKED: @@ -103,6 +100,10 @@ public class CheckmarkWidgetView extends HabitWidgetView R.attr.cardBackgroundColor); foregroundColor = InterfaceUtils.getStyledColor(context, R.attr.mediumContrastTextColor); + + setShadowAlpha(0x00); + rebuildBackground(); + break; } @@ -118,47 +119,31 @@ public class CheckmarkWidgetView extends HabitWidgetView postInvalidate(); } - @Override - public void refreshData() + public void setCheckmarkValue(int checkmarkValue) { - if (habit == null) return; - this.percentage = - (float) habit.getScores().getTodayValue() / Score.MAX_VALUE; - this.checkmarkValue = habit.getCheckmarks().getTodayValue(); - refresh(); + this.checkmarkValue = checkmarkValue; } - @Override - public void setHabit(@NonNull Habit habit) + public void setName(@NonNull String name) { - super.setHabit(habit); - this.name = habit.getName(); - this.activeColor = ColorUtils.getColor(getContext(), habit.getColor()); - refresh(); + this.name = name; } - @Override - @NonNull - protected Integer getInnerLayoutId() + public void setPercentage(float percentage) { - return R.layout.widget_checkmark; + this.percentage = percentage; } - private void init() + public void setActiveColor(int activeColor) { - ring = (RingView) findViewById(R.id.scoreRing); - label = (TextView) findViewById(R.id.label); - - if (ring != null) ring.setIsTransparencyEnabled(true); + this.activeColor = activeColor; + } - if (isInEditMode()) - { - percentage = 0.75f; - name = "Wake up early"; - activeColor = ColorUtils.getAndroidTestColor(6); - checkmarkValue = Checkmark.CHECKED_EXPLICITLY; - refresh(); - } + @Override + @NonNull + protected Integer getInnerLayoutId() + { + return R.layout.widget_checkmark; } @Override @@ -194,4 +179,21 @@ public class CheckmarkWidgetView extends HabitWidgetView super.onMeasure(widthMeasureSpec, heightMeasureSpec); } + + private void init() + { + ring = (RingView) findViewById(R.id.scoreRing); + label = (TextView) findViewById(R.id.label); + + if (ring != null) ring.setIsTransparencyEnabled(true); + + if (isInEditMode()) + { + percentage = 0.75f; + name = "Wake up early"; + activeColor = ColorUtils.getAndroidTestColor(6); + checkmarkValue = Checkmark.CHECKED_EXPLICITLY; + refresh(); + } + } } diff --git a/app/src/main/java/org/isoron/uhabits/widgets/views/GraphWidgetView.java b/app/src/main/java/org/isoron/uhabits/ui/widgets/views/GraphWidgetView.java similarity index 56% rename from app/src/main/java/org/isoron/uhabits/widgets/views/GraphWidgetView.java rename to app/src/main/java/org/isoron/uhabits/ui/widgets/views/GraphWidgetView.java index 96b25dcc8..56ab7b05b 100644 --- a/app/src/main/java/org/isoron/uhabits/widgets/views/GraphWidgetView.java +++ b/app/src/main/java/org/isoron/uhabits/ui/widgets/views/GraphWidgetView.java @@ -17,63 +17,57 @@ * with this program. If not, see . */ -package org.isoron.uhabits.widgets.views; +package org.isoron.uhabits.ui.widgets.views; -import android.content.Context; -import android.support.annotation.NonNull; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; +import android.content.*; +import android.support.annotation.*; +import android.view.*; +import android.widget.*; -import org.isoron.uhabits.R; -import org.isoron.uhabits.models.Habit; -import org.isoron.uhabits.ui.common.views.HabitChart; +import org.isoron.uhabits.*; -public class GraphWidgetView extends HabitWidgetView implements HabitChart +public class GraphWidgetView extends HabitWidgetView { - private final HabitChart dataView; + private final View dataView; + private TextView title; - public GraphWidgetView(Context context, HabitChart dataView) + public GraphWidgetView(Context context, View dataView) { super(context); this.dataView = dataView; init(); } - private void init() + public View getDataView() { - ViewGroup.LayoutParams params = - new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT); - ((View) dataView).setLayoutParams(params); - - ViewGroup innerFrame = (ViewGroup) findViewById(R.id.innerFrame); - innerFrame.addView(((View) dataView)); - - title = (TextView) findViewById(R.id.title); - title.setVisibility(VISIBLE); + return dataView; } - @Override - public void setHabit(@NonNull Habit habit) + public void setTitle(String text) { - super.setHabit(habit); - dataView.setHabit(habit); - title.setText(habit.getName()); + title.setText(text); } @Override - public void refreshData() - { - if(habit == null) return; - dataView.refreshData(); - } - @NonNull protected Integer getInnerLayoutId() { return R.layout.widget_graph; } + + private void init() + { + ViewGroup.LayoutParams params = + new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT); + dataView.setLayoutParams(params); + + ViewGroup innerFrame = (ViewGroup) findViewById(R.id.innerFrame); + innerFrame.addView(dataView); + + title = (TextView) findViewById(R.id.title); + title.setVisibility(VISIBLE); + } } diff --git a/app/src/main/java/org/isoron/uhabits/widgets/views/HabitWidgetView.java b/app/src/main/java/org/isoron/uhabits/ui/widgets/views/HabitWidgetView.java similarity index 92% rename from app/src/main/java/org/isoron/uhabits/widgets/views/HabitWidgetView.java rename to app/src/main/java/org/isoron/uhabits/ui/widgets/views/HabitWidgetView.java index 0c4266ec1..b4e6dafea 100644 --- a/app/src/main/java/org/isoron/uhabits/widgets/views/HabitWidgetView.java +++ b/app/src/main/java/org/isoron/uhabits/ui/widgets/views/HabitWidgetView.java @@ -17,7 +17,7 @@ * with this program. If not, see . */ -package org.isoron.uhabits.widgets.views; +package org.isoron.uhabits.ui.widgets.views; import android.content.*; import android.graphics.*; @@ -29,14 +29,11 @@ import android.view.*; import android.widget.*; import org.isoron.uhabits.*; -import org.isoron.uhabits.models.*; -import org.isoron.uhabits.ui.common.views.*; import org.isoron.uhabits.utils.*; import java.util.*; public abstract class HabitWidgetView extends FrameLayout - implements HabitChart { @Nullable protected InsetDrawable background; @@ -44,9 +41,6 @@ public abstract class HabitWidgetView extends FrameLayout @Nullable protected Paint backgroundPaint; - @Nullable - protected Habit habit; - protected ViewGroup frame; private int shadowAlpha; @@ -63,12 +57,6 @@ public abstract class HabitWidgetView extends FrameLayout init(); } - @Override - public void setHabit(@NonNull Habit habit) - { - this.habit = habit; - } - public void setShadowAlpha(int shadowAlpha) { this.shadowAlpha = shadowAlpha; diff --git a/app/src/main/java/org/isoron/uhabits/widgets/views/package-info.java b/app/src/main/java/org/isoron/uhabits/ui/widgets/views/package-info.java similarity index 95% rename from app/src/main/java/org/isoron/uhabits/widgets/views/package-info.java rename to app/src/main/java/org/isoron/uhabits/ui/widgets/views/package-info.java index 2fbf2799c..7e26dce73 100644 --- a/app/src/main/java/org/isoron/uhabits/widgets/views/package-info.java +++ b/app/src/main/java/org/isoron/uhabits/ui/widgets/views/package-info.java @@ -20,4 +20,4 @@ /** * Provides views that are specific for the home-screen widgets. */ -package org.isoron.uhabits.widgets.views; \ No newline at end of file +package org.isoron.uhabits.ui.widgets.views; \ No newline at end of file diff --git a/app/src/main/java/org/isoron/uhabits/utils/Preferences.java b/app/src/main/java/org/isoron/uhabits/utils/Preferences.java index adb0c6086..73a9e729b 100644 --- a/app/src/main/java/org/isoron/uhabits/utils/Preferences.java +++ b/app/src/main/java/org/isoron/uhabits/utils/Preferences.java @@ -19,13 +19,10 @@ package org.isoron.uhabits.utils; -import android.content.Context; -import android.content.SharedPreferences; -import android.preference.PreferenceManager; +import android.content.*; +import android.preference.*; -import org.isoron.uhabits.BuildConfig; -import org.isoron.uhabits.HabitsApplication; -import org.isoron.uhabits.R; +import org.isoron.uhabits.*; public class Preferences { @@ -103,9 +100,9 @@ public class Preferences public void setShouldReverseCheckmarks(boolean shouldReverse) { prefs - .edit() - .putBoolean("pref_checkmark_reverse_order", shouldReverse) - .apply(); + .edit() + .putBoolean("pref_checkmark_reverse_order", shouldReverse) + .apply(); } public boolean shouldReverseCheckmarks() @@ -127,11 +124,9 @@ public class Preferences public void updateLastHint(int number, long timestamp) { prefs - .edit() - .putInt("last_hint_number", number) - .putLong("last_hint_timestamp", timestamp) - .apply(); + .edit() + .putInt("last_hint_number", number) + .putLong("last_hint_timestamp", timestamp) + .apply(); } - - } diff --git a/app/src/main/java/org/isoron/uhabits/utils/WidgetPreferences.java b/app/src/main/java/org/isoron/uhabits/utils/WidgetPreferences.java new file mode 100644 index 000000000..6fac955b5 --- /dev/null +++ b/app/src/main/java/org/isoron/uhabits/utils/WidgetPreferences.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2016 Álinson Santos Xavier + * + * 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 . + */ + +package org.isoron.uhabits.utils; + +import android.content.*; +import android.preference.*; + +import org.isoron.uhabits.*; + +public class WidgetPreferences +{ + private Context context; + + private SharedPreferences prefs; + + public WidgetPreferences() + { + this.context = HabitsApplication.getContext(); + prefs = PreferenceManager.getDefaultSharedPreferences(context); + } + + public void addWidget(int widgetId, long habitId) + { + prefs + .edit() + .putLong(getHabitIdKey(widgetId), habitId) + .commit(); + } + + public long getHabitIdFromWidgetId(int widgetId) + { + Long habitId = prefs.getLong(getHabitIdKey(widgetId), -1); + if (habitId < 0) throw new RuntimeException("widget not found"); + + return habitId; + } + + public void removeWidget(int id) + { + String habitIdKey = getHabitIdKey(id); + prefs.edit().remove(habitIdKey).apply(); + } + + private String getHabitIdKey(int id) + { + return String.format("widget-%06d-habit", id); + } +} diff --git a/app/src/main/java/org/isoron/uhabits/utils/WidgetUtils.java b/app/src/main/java/org/isoron/uhabits/utils/WidgetUtils.java new file mode 100644 index 000000000..44e8238e7 --- /dev/null +++ b/app/src/main/java/org/isoron/uhabits/utils/WidgetUtils.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2016 Álinson Santos Xavier + * + * 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 . + */ + +package org.isoron.uhabits.utils; + +import android.appwidget.*; +import android.content.*; +import android.os.*; +import android.support.annotation.*; +import android.widget.*; + +import org.isoron.uhabits.ui.widgets.*; + +import static android.os.Build.VERSION.*; +import static android.os.Build.VERSION_CODES.*; + +public abstract class WidgetUtils +{ + @NonNull + public static WidgetDimensions getDimensionsFromOptions( + @NonNull Context context, @NonNull Bundle options) + { + if (SDK_INT < JELLY_BEAN) + throw new AssertionError("method requires jelly-bean"); + + int maxWidth = (int) InterfaceUtils.dpToPixels(context, + options.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH)); + int maxHeight = (int) InterfaceUtils.dpToPixels(context, + options.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT)); + int minWidth = (int) InterfaceUtils.dpToPixels(context, + options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH)); + int minHeight = (int) InterfaceUtils.dpToPixels(context, + options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT)); + + return new WidgetDimensions(minWidth, maxHeight, maxWidth, minHeight); + } + + public static void updateAppWidget(@NonNull AppWidgetManager manager, + @NonNull BaseWidget widget) + { + if (SDK_INT < JELLY_BEAN) + { + RemoteViews portrait = widget.getPortraitRemoteViews(); + manager.updateAppWidget(widget.getId(), portrait); + } + else + { + RemoteViews landscape = widget.getLandscapeRemoteViews(); + RemoteViews portrait = widget.getPortraitRemoteViews(); + RemoteViews views = new RemoteViews(landscape, portrait); + manager.updateAppWidget(widget.getId(), views); + } + } +} diff --git a/app/src/main/java/org/isoron/uhabits/widgets/BaseWidgetProvider.java b/app/src/main/java/org/isoron/uhabits/widgets/BaseWidgetProvider.java index a94809ff4..89d91e2f4 100644 --- a/app/src/main/java/org/isoron/uhabits/widgets/BaseWidgetProvider.java +++ b/app/src/main/java/org/isoron/uhabits/widgets/BaseWidgetProvider.java @@ -19,308 +19,131 @@ package org.isoron.uhabits.widgets; -import android.app.PendingIntent; -import android.appwidget.AppWidgetManager; -import android.appwidget.AppWidgetProvider; -import android.content.Context; -import android.content.SharedPreferences; -import android.graphics.Bitmap; -import android.os.Build; -import android.os.Bundle; -import android.preference.PreferenceManager; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.ImageView; -import android.widget.RemoteViews; -import android.widget.TextView; +import android.appwidget.*; +import android.content.*; +import android.os.*; +import android.support.annotation.*; +import android.widget.*; -import org.isoron.uhabits.HabitsApplication; -import org.isoron.uhabits.R; -import org.isoron.uhabits.models.Habit; -import org.isoron.uhabits.models.HabitList; -import org.isoron.uhabits.tasks.BaseTask; -import org.isoron.uhabits.utils.InterfaceUtils; +import org.isoron.uhabits.*; +import org.isoron.uhabits.models.*; +import org.isoron.uhabits.ui.widgets.*; +import org.isoron.uhabits.utils.*; -import java.io.FileOutputStream; -import java.io.IOException; +import javax.inject.*; -import javax.inject.Inject; +import static android.os.Build.VERSION.*; +import static android.os.Build.VERSION_CODES.*; +import static org.isoron.uhabits.utils.WidgetUtils.*; public abstract class BaseWidgetProvider extends AppWidgetProvider { @Inject HabitList habitList; - private class WidgetDimensions - { - public int portraitWidth, portraitHeight; - public int landscapeWidth, landscapeHeight; - } - - protected abstract int getDefaultHeight(); - - protected abstract int getDefaultWidth(); - - protected abstract PendingIntent getOnClickPendingIntent(Context context, Habit habit); - - protected abstract int getLayoutId(); - - protected abstract View buildCustomView(Context context, Habit habit); - - public static String getHabitIdKey(long widgetId) - { - return String.format("widget-%06d-habit", widgetId); - } - - @Override - public void onDeleted(Context context, int[] appWidgetIds) - { - Context appContext = context.getApplicationContext(); - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(appContext); - - for(Integer id : appWidgetIds) - prefs.edit().remove(getHabitIdKey(id)).apply(); - } + @Inject + WidgetPreferences widgetPrefs; - @Override - public void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager, - int appWidgetId, Bundle newOptions) + public BaseWidgetProvider() { - updateWidget(context, appWidgetManager, appWidgetId, newOptions); + HabitsApplication.getComponent().inject(this); } @Override - public void onUpdate(Context context, AppWidgetManager manager, int[] appWidgetIds) + public void onAppWidgetOptionsChanged(@Nullable Context context, + @Nullable AppWidgetManager manager, + int widgetId, + @Nullable Bundle options) { - for(int id : appWidgetIds) + try { - Bundle options = null; - - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) - options = manager.getAppWidgetOptions(id); + if (context == null) throw new RuntimeException("context is null"); + if (manager == null) throw new RuntimeException("manager is null"); + if (options == null) throw new RuntimeException("options is null"); + context.setTheme(R.style.TransparentWidgetTheme); - updateWidget(context, manager, id, options); + BaseWidget widget = getWidgetFromId(context, widgetId); + WidgetDimensions dims = getDimensionsFromOptions(context, options); + widget.setDimensions(dims); + updateAppWidget(manager, widget); } - } - - private void updateWidget(Context context, AppWidgetManager manager, - int widgetId, Bundle options) - { - HabitsApplication.getComponent().inject(this); - WidgetDimensions dim = getWidgetDimensions(context, options); - - Context appContext = context.getApplicationContext(); - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(appContext); - - Long habitId = prefs.getLong(getHabitIdKey(widgetId), -1L); - if(habitId < 0) return; - - Habit habit = habitList.getById(habitId); - if(habit == null) + catch (RuntimeException e) { drawErrorWidget(context, manager, widgetId); - return; + e.printStackTrace(); } - - new RenderWidgetTask(widgetId, context, habit, dim, manager).execute(); } - private void drawErrorWidget(Context context, AppWidgetManager manager, int widgetId) - { - RemoteViews errorView = new RemoteViews(context.getPackageName(), R.layout.widget_error); - manager.updateAppWidget(widgetId, errorView); - } - - protected abstract void refreshCustomViewData(View widgetView); - - private void savePreview(Context context, int widgetId, Bitmap widgetCache, int width, - int height, String label) + @Override + public void onDeleted(@Nullable Context context, @Nullable int[] ids) { - try - { - LayoutInflater inflater = LayoutInflater.from(context); - View view = inflater.inflate(getLayoutId(), null); - - TextView tvLabel = (TextView) view.findViewById(R.id.label); - if(tvLabel != null) tvLabel.setText(label); - - ImageView iv = (ImageView) view.findViewById(R.id.imageView); - if(iv != null) iv.setImageBitmap(widgetCache); - - view.measure(width, height); - view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight()); - view.setDrawingCacheEnabled(true); - view.buildDrawingCache(); - Bitmap previewCache = view.getDrawingCache(); - - String filename = String.format("%s/%d_%d.png", context.getExternalCacheDir(), widgetId, width); - Log.d("BaseWidgetProvider", String.format("Writing %s", filename)); - FileOutputStream out = new FileOutputStream(filename); + if (context == null) throw new RuntimeException("context is null"); + if (ids == null) throw new RuntimeException("ids is null"); - if(previewCache != null) - previewCache.compress(Bitmap.CompressFormat.PNG, 100, out); - - out.close(); - } - catch (IOException e) + for (int id : ids) { - e.printStackTrace(); + BaseWidget widget = getWidgetFromId(context, id); + widget.delete(); } } - private WidgetDimensions getWidgetDimensions(Context context, Bundle options) + @Override + public void onUpdate(@Nullable Context context, + @Nullable AppWidgetManager manager, + @Nullable int[] widgetIds) { - int maxWidth = getDefaultWidth(); - int minWidth = getDefaultWidth(); - int maxHeight = getDefaultHeight(); - int minHeight = getDefaultHeight(); + if (context == null) throw new RuntimeException("context is null"); + if (manager == null) throw new RuntimeException("manager is null"); + if (widgetIds == null) throw new RuntimeException("widgetIds is null"); + context.setTheme(R.style.TransparentWidgetTheme); - if (options != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) - { - maxWidth = (int) InterfaceUtils.dpToPixels(context, - options.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH)); - maxHeight = (int) InterfaceUtils.dpToPixels(context, - options.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT)); - minWidth = (int) InterfaceUtils.dpToPixels(context, - options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH)); - minHeight = (int) InterfaceUtils.dpToPixels(context, - options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT)); - } - - WidgetDimensions ws = new WidgetDimensions(); - ws.portraitWidth = minWidth; - ws.portraitHeight = maxHeight; - ws.landscapeWidth = maxWidth; - ws.landscapeHeight = minHeight; - return ws; + for (int id : widgetIds) + update(context, manager, id); } - private void measureCustomView(Context context, int w, int h, View customView) + @NonNull + protected Habit getHabitFromWidgetId(int widgetId) { - LayoutInflater inflater = LayoutInflater.from(context); - View entireView = inflater.inflate(getLayoutId(), null); - - int specWidth = View.MeasureSpec.makeMeasureSpec(w, View.MeasureSpec.EXACTLY); - int specHeight = View.MeasureSpec.makeMeasureSpec(h, View.MeasureSpec.EXACTLY); - - entireView.measure(specWidth, specHeight); - entireView.layout(0, 0, entireView.getMeasuredWidth(), entireView.getMeasuredHeight()); + long habitId = widgetPrefs.getHabitIdFromWidgetId(widgetId); + Habit habit = habitList.getById(habitId); + if (habit == null) throw new RuntimeException("habit not found"); + return habit; + } - View imageView = entireView.findViewById(R.id.imageView); - w = imageView.getMeasuredWidth(); - h = imageView.getMeasuredHeight(); + @NonNull + protected abstract BaseWidget getWidgetFromId(@NonNull Context context, + int id); - specWidth = View.MeasureSpec.makeMeasureSpec(w, View.MeasureSpec.EXACTLY); - specHeight = View.MeasureSpec.makeMeasureSpec(h, View.MeasureSpec.EXACTLY); - customView.measure(specWidth, specHeight); - customView.layout(0, 0, customView.getMeasuredWidth(), customView.getMeasuredHeight()); + private void drawErrorWidget(Context context, + AppWidgetManager manager, + int widgetId) + { + RemoteViews errorView = + new RemoteViews(context.getPackageName(), R.layout.widget_error); + manager.updateAppWidget(widgetId, errorView); } - private class RenderWidgetTask extends BaseTask + private void update(@NonNull Context context, + @NonNull AppWidgetManager manager, + int widgetId) { - private final int widgetId; - private final Context context; - private final Habit habit; - private final AppWidgetManager manager; - private RemoteViews portraitRemoteViews, landscapeRemoteViews; - private View portraitWidgetView, landscapeWidgetView; - private WidgetDimensions dim; - - public RenderWidgetTask(int widgetId, Context context, Habit habit, WidgetDimensions ws, - AppWidgetManager manager) - { - this.widgetId = widgetId; - this.context = context; - this.habit = habit; - this.manager = manager; - this.dim = ws; - } - - @Override - protected void onPreExecute() - { - super.onPreExecute(); - context.setTheme(R.style.TransparentWidgetTheme); - - portraitRemoteViews = new RemoteViews(context.getPackageName(), getLayoutId()); - portraitWidgetView = buildCustomView(context, habit); - measureCustomView(context, dim.portraitWidth, dim.portraitHeight, portraitWidgetView); - - landscapeRemoteViews = new RemoteViews(context.getPackageName(), getLayoutId()); - landscapeWidgetView = buildCustomView(context, habit); - measureCustomView(context, dim.landscapeWidth, dim.landscapeHeight, - landscapeWidgetView); - } - - private void updateAppWidget() - { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) - manager.updateAppWidget(widgetId, new RemoteViews(landscapeRemoteViews, - portraitRemoteViews)); - else - manager.updateAppWidget(widgetId, portraitRemoteViews); - } - - @Override - protected void doInBackground() + try { - refreshCustomViewData(portraitWidgetView); - refreshCustomViewData(landscapeWidgetView); - } + BaseWidget widget = getWidgetFromId(context, widgetId); - @Override - protected void onPostExecute(Void aVoid) - { - try - { - buildRemoteViews(portraitWidgetView, portraitRemoteViews, - dim.portraitWidth, dim.portraitHeight); - buildRemoteViews(landscapeWidgetView, landscapeRemoteViews, - dim.landscapeWidth, dim.landscapeHeight); - updateAppWidget(); - } - catch (Exception e) + if (SDK_INT > JELLY_BEAN) { - drawErrorWidget(context, manager, widgetId); - e.printStackTrace(); + Bundle options = manager.getAppWidgetOptions(widgetId); + widget.setDimensions( + getDimensionsFromOptions(context, options)); } - super.onPostExecute(aVoid); + updateAppWidget(manager, widget); } - - private void buildRemoteViews(View widgetView, RemoteViews remoteViews, int width, - int height) + catch (RuntimeException e) { - widgetView.invalidate(); - widgetView.setDrawingCacheEnabled(true); - widgetView.buildDrawingCache(true); - Bitmap drawingCache = widgetView.getDrawingCache(); - remoteViews.setTextViewText(R.id.label, habit.getName()); - remoteViews.setImageViewBitmap(R.id.imageView, drawingCache); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) - { - int imageWidth = widgetView.getMeasuredWidth(); - int imageHeight = widgetView.getMeasuredHeight(); - int p[] = getPadding(width, height, imageWidth, imageHeight); - remoteViews.setViewPadding(R.id.buttonOverlay, p[0], p[1], p[2], p[3]); - } - - //savePreview(context, widgetId, drawingCache, width, height, habit.name); - - PendingIntent onClickIntent = getOnClickPendingIntent(context, habit); - if (onClickIntent != null) remoteViews.setOnClickPendingIntent(R.id.button, - onClickIntent); + drawErrorWidget(context, manager, widgetId); + e.printStackTrace(); } } - - private int[] getPadding(int entireWidth, int entireHeight, int imageWidth, - int imageHeight) - { - int w = (int) (((float) entireWidth - imageWidth) / 2); - int h = (int) (((float) entireHeight - imageHeight) / 2); - - return new int[]{ w, h, w, h }; - } } diff --git a/app/src/main/java/org/isoron/uhabits/widgets/CheckmarkWidgetProvider.java b/app/src/main/java/org/isoron/uhabits/widgets/CheckmarkWidgetProvider.java index 42ddada52..c574d9c2e 100644 --- a/app/src/main/java/org/isoron/uhabits/widgets/CheckmarkWidgetProvider.java +++ b/app/src/main/java/org/isoron/uhabits/widgets/CheckmarkWidgetProvider.java @@ -18,55 +18,19 @@ */ package org.isoron.uhabits.widgets; -import android.app.*; import android.content.*; -import android.view.*; +import android.support.annotation.*; -import org.isoron.uhabits.*; import org.isoron.uhabits.models.*; -import org.isoron.uhabits.ui.common.views.*; -import org.isoron.uhabits.widgets.views.*; +import org.isoron.uhabits.ui.widgets.*; public class CheckmarkWidgetProvider extends BaseWidgetProvider { + @NonNull @Override - protected View buildCustomView(Context context, Habit habit) + protected CheckmarkWidget getWidgetFromId(@NonNull Context context, int id) { - CheckmarkWidgetView view = new CheckmarkWidgetView(context); - view.setHabit(habit); - return view; + Habit habit = getHabitFromWidgetId(id); + return new CheckmarkWidget(context, id, habit); } - - @Override - protected int getDefaultHeight() - { - return 125; - } - - @Override - protected int getDefaultWidth() - { - return 125; - } - - @Override - protected int getLayoutId() - { - return R.layout.widget_wrapper; - } - - @Override - protected PendingIntent getOnClickPendingIntent(Context context, - Habit habit) - { - return HabitBroadcastReceiver.buildCheckIntent(context, habit, null); - } - - @Override - protected void refreshCustomViewData(View view) - { - ((HabitChart) view).refreshData(); - } - - } diff --git a/app/src/main/java/org/isoron/uhabits/widgets/FrequencyWidgetProvider.java b/app/src/main/java/org/isoron/uhabits/widgets/FrequencyWidgetProvider.java index 1eed0d708..3758e1b0f 100644 --- a/app/src/main/java/org/isoron/uhabits/widgets/FrequencyWidgetProvider.java +++ b/app/src/main/java/org/isoron/uhabits/widgets/FrequencyWidgetProvider.java @@ -19,54 +19,73 @@ package org.isoron.uhabits.widgets; -import android.app.*; import android.content.*; -import android.view.*; +import android.support.annotation.*; import org.apache.commons.lang3.*; -import org.isoron.uhabits.*; -import org.isoron.uhabits.models.*; -import org.isoron.uhabits.ui.common.views.*; +import org.isoron.uhabits.ui.widgets.*; public class FrequencyWidgetProvider extends BaseWidgetProvider { + @NonNull @Override - protected View buildCustomView(Context context, Habit habit) + protected BaseWidget getWidgetFromId(@NonNull Context context, int id) { - FrequencyChart dataView = new FrequencyChart(context); throw new NotImplementedException(""); -// GraphWidgetView view = new GraphWidgetView(context, dataView); -// view.setHabit(habit); -// return view; - } - - @Override - protected void refreshCustomViewData(View view) - { - ((HabitChart) view).refreshData(); } - @Override - protected PendingIntent getOnClickPendingIntent(Context context, Habit habit) - { - return HabitBroadcastReceiver.buildViewHabitIntent(context, habit); - } - - @Override - protected int getDefaultHeight() - { - return 200; - } - - @Override - protected int getDefaultWidth() - { - return 200; - } - - @Override - protected int getLayoutId() - { - return R.layout.widget_wrapper; - } +// @NonNull +// @Override +// protected BaseWidget getWidgetFromId(int id) +// { +// throw new NotImplementedException(""); +// } +// +// @Override +// protected View buildCustomView(Context context, Habit habit) +// { +// FrequencyChart chart = new FrequencyChart(context); +// GraphWidgetView view = new GraphWidgetView(context, chart); +// view.setTitle(habit.getName()); +// return view; +// } +// +// @Override +// protected int getDefaultHeight() +// { +// return 200; +// } +// +// @Override +// protected int getDefaultWidth() +// { +// return 200; +// } +// +// @Override +// protected int getLayoutId() +// { +// return R.layout.widget_wrapper; +// } +// +// @Override +// protected PendingIntent getOnClickPendingIntent(Context context, +// Habit habit) +// { +// return HabitBroadcastReceiver.buildViewHabitIntent(context, habit); +// } +// +// @Override +// protected void refreshCustomViewData(Context context, +// View view, +// Habit habit) +// { +// GraphWidgetView widgetView = (GraphWidgetView) view; +// FrequencyChart chart = (FrequencyChart) widgetView.getDataView(); +// +// int color = ColorUtils.getColor(context, habit.getColor()); +// +// chart.setColor(color); +// chart.setFrequency(habit.getRepetitions().getWeekdayFrequency()); +// } } diff --git a/app/src/main/java/org/isoron/uhabits/widgets/HistoryWidgetProvider.java b/app/src/main/java/org/isoron/uhabits/widgets/HistoryWidgetProvider.java index e30a6f71c..a5451a647 100644 --- a/app/src/main/java/org/isoron/uhabits/widgets/HistoryWidgetProvider.java +++ b/app/src/main/java/org/isoron/uhabits/widgets/HistoryWidgetProvider.java @@ -18,54 +18,74 @@ */ package org.isoron.uhabits.widgets; -import android.app.*; import android.content.*; -import android.view.*; +import android.support.annotation.*; import org.apache.commons.lang3.*; -import org.isoron.uhabits.*; -import org.isoron.uhabits.models.*; -import org.isoron.uhabits.ui.common.views.*; +import org.isoron.uhabits.ui.widgets.*; -public class HistoryWidgetProvider extends BaseWidgetProvider +public class HistoryWidgetProvider extends BaseWidgetProvider { + @NonNull @Override - protected View buildCustomView(Context context, Habit habit) + protected BaseWidget getWidgetFromId(@NonNull Context context, int id) { throw new NotImplementedException(""); -// HistoryChart dataView = new HistoryChart(context); -// GraphWidgetView view = new GraphWidgetView(context, dataView); -// view.setHabit(habit); -// return view; - } - - @Override - protected void refreshCustomViewData(View view) - { - ((HabitChart) view).refreshData(); } - @Override - protected PendingIntent getOnClickPendingIntent(Context context, Habit habit) - { - return HabitBroadcastReceiver.buildViewHabitIntent(context, habit); - } - - @Override - protected int getDefaultHeight() - { - return 250; - } - - @Override - protected int getDefaultWidth() - { - return 250; - } - - @Override - protected int getLayoutId() - { - return R.layout.widget_wrapper; - } +// @NonNull +// @Override +// protected BaseWidget getWidgetFromId(int id) +// { +// throw new NotImplementedException(""); +// } +// +// @Override +// protected View buildCustomView(Context context, Habit habit) +// { +// HistoryChart dataView = new HistoryChart(context); +// GraphWidgetView widgetView = new GraphWidgetView(context, dataView); +// widgetView.setTitle(habit.getName()); +// return widgetView; +// } +// +// @Override +// protected int getDefaultHeight() +// { +// return 250; +// } +// +// @Override +// protected int getDefaultWidth() +// { +// return 250; +// } +// +// @Override +// protected int getLayoutId() +// { +// return R.layout.widget_wrapper; +// } +// +// @Override +// protected PendingIntent getOnClickPendingIntent(Context context, +// Habit habit) +// { +// return HabitBroadcastReceiver.buildViewHabitIntent(context, habit); +// } +// +// @Override +// protected void refreshCustomViewData(Context context, +// View view, +// Habit habit) +// { +// GraphWidgetView widgetView = (GraphWidgetView) view; +// HistoryChart chart = (HistoryChart) widgetView.getDataView(); +// +// int color = ColorUtils.getColor(context, habit.getColor()); +// int[] values = habit.getCheckmarks().getAllValues(); +// +// chart.setColor(color); +// chart.setCheckmarks(values); +// } } diff --git a/app/src/main/java/org/isoron/uhabits/widgets/ScoreWidgetProvider.java b/app/src/main/java/org/isoron/uhabits/widgets/ScoreWidgetProvider.java index f01b7be20..0b250d07d 100644 --- a/app/src/main/java/org/isoron/uhabits/widgets/ScoreWidgetProvider.java +++ b/app/src/main/java/org/isoron/uhabits/widgets/ScoreWidgetProvider.java @@ -18,63 +18,73 @@ */ package org.isoron.uhabits.widgets; -import android.app.*; import android.content.*; -import android.view.*; +import android.support.annotation.*; import org.apache.commons.lang3.*; -import org.isoron.uhabits.*; -import org.isoron.uhabits.models.*; -import org.isoron.uhabits.ui.common.views.*; -import org.isoron.uhabits.ui.habits.show.views.*; -import org.isoron.uhabits.utils.*; +import org.isoron.uhabits.ui.widgets.*; public class ScoreWidgetProvider extends BaseWidgetProvider { + @NonNull @Override - protected View buildCustomView(Context context, Habit habit) + protected BaseWidget getWidgetFromId(@NonNull Context context, int id) { - int defaultScoreInterval = InterfaceUtils.getDefaultScoreSpinnerPosition(context); - int size = ScoreCard.BUCKET_SIZES[defaultScoreInterval]; - - ScoreChart dataView = new ScoreChart(context); - dataView.setIsTransparencyEnabled(true); - dataView.setBucketSize(size); - -// GraphWidgetView view = new GraphWidgetView(context, dataView); -// view.setHabit(habit); -// return view; - throw new NotImplementedException(""); } - @Override - protected void refreshCustomViewData(View view) - { - ((HabitChart) view).refreshData(); - } - - @Override - protected PendingIntent getOnClickPendingIntent(Context context, Habit habit) - { - return HabitBroadcastReceiver.buildViewHabitIntent(context, habit); - } - - @Override - protected int getDefaultHeight() - { - return 300; - } - - @Override - protected int getDefaultWidth() - { - return 300; - } - - @Override - protected int getLayoutId() - { - return R.layout.widget_wrapper; - } +// @Override +// protected View buildCustomView(Context context, Habit habit) +// { +// ScoreChart dataView = new ScoreChart(context); +// GraphWidgetView view = new GraphWidgetView(context, dataView); +// view.setTitle(habit.getName()); +// return view; +// } +// +// @Override +// protected int getDefaultHeight() +// { +// return 300; +// } +// +// @Override +// protected int getDefaultWidth() +// { +// return 300; +// } +// +// @Override +// protected int getLayoutId() +// { +// return R.layout.widget_wrapper; +// } +// +// @Override +// protected PendingIntent getOnClickPendingIntent(Context context, +// Habit habit) +// { +// return HabitBroadcastReceiver.buildViewHabitIntent(context, habit); +// } +// +// @Override +// protected void refreshCustomViewData(Context context, +// View view, +// Habit habit) +// { +// int defaultScoreInterval = +// InterfaceUtils.getDefaultScoreSpinnerPosition(context); +// int size = ScoreCard.BUCKET_SIZES[defaultScoreInterval]; +// +// GraphWidgetView widgetView = (GraphWidgetView) view; +// ScoreChart chart = (ScoreChart) widgetView.getDataView(); +// +// int color = ColorUtils.getColor(context, habit.getColor()); +// List scores = habit.getScores().getAll(); +// +// chart.setIsTransparencyEnabled(true); +// chart.setBucketSize(size); +// chart.setColor(color); +// chart.setScores(scores); +// } } diff --git a/app/src/main/java/org/isoron/uhabits/widgets/StreakWidgetProvider.java b/app/src/main/java/org/isoron/uhabits/widgets/StreakWidgetProvider.java index 9b0052f53..71862edef 100644 --- a/app/src/main/java/org/isoron/uhabits/widgets/StreakWidgetProvider.java +++ b/app/src/main/java/org/isoron/uhabits/widgets/StreakWidgetProvider.java @@ -18,54 +18,69 @@ */ package org.isoron.uhabits.widgets; -import android.app.*; import android.content.*; -import android.view.*; +import android.support.annotation.*; import org.apache.commons.lang3.*; -import org.isoron.uhabits.*; -import org.isoron.uhabits.models.*; -import org.isoron.uhabits.ui.common.views.*; +import org.isoron.uhabits.ui.widgets.*; -public class StreakWidgetProvider extends BaseWidgetProvider +public class StreakWidgetProvider extends BaseWidgetProvider { + @NonNull @Override - protected View buildCustomView(Context context, Habit habit) + protected BaseWidget getWidgetFromId(@NonNull Context context, int id) { - StreakChart dataView = new StreakChart(context); throw new NotImplementedException(""); -// GraphWidgetView view = new GraphWidgetView(context, dataView); -// view.setHabit(habit); -// return view; - } - - @Override - protected void refreshCustomViewData(View view) - { - ((HabitChart) view).refreshData(); } - @Override - protected PendingIntent getOnClickPendingIntent(Context context, Habit habit) - { - return HabitBroadcastReceiver.buildViewHabitIntent(context, habit); - } - - @Override - protected int getDefaultHeight() - { - return 200; - } - - @Override - protected int getDefaultWidth() - { - return 200; - } - - @Override - protected int getLayoutId() - { - return R.layout.widget_wrapper; - } +// @Override +// protected View buildCustomView(Context context, Habit habit) +// { +// StreakChart dataView = new StreakChart(context); +// GraphWidgetView view = new GraphWidgetView(context, dataView); +// view.setTitle(habit.getName()); +// return view; +// } +// +// @Override +// protected int getDefaultHeight() +// { +// return 200; +// } +// +// @Override +// protected int getDefaultWidth() +// { +// return 200; +// } +// +// @Override +// protected int getLayoutId() +// { +// return R.layout.widget_wrapper; +// } +// +// @Override +// protected PendingIntent getOnClickPendingIntent(Context context, +// Habit habit) +// { +// return HabitBroadcastReceiver.buildViewHabitIntent(context, habit); +// } +// +// @Override +// protected void refreshCustomViewData(Context context, +// View view, +// Habit habit) +// { +// GraphWidgetView widgetView = (GraphWidgetView) view; +// StreakChart chart = (StreakChart) widgetView.getDataView(); +// +// int color = ColorUtils.getColor(context, habit.getColor()); +// +// // TODO: make this dynamic +// List streaks = habit.getStreaks().getBest(10); +// +// chart.setColor(color); +// chart.setStreaks(streaks); +// } } diff --git a/app/src/main/java/org/isoron/uhabits/widgets/WidgetManager.java b/app/src/main/java/org/isoron/uhabits/widgets/WidgetManager.java deleted file mode 100644 index 69643059c..000000000 --- a/app/src/main/java/org/isoron/uhabits/widgets/WidgetManager.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2016 Álinson Santos Xavier - * - * 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 . - */ - -package org.isoron.uhabits.widgets; - -import android.appwidget.AppWidgetManager; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; - -public class WidgetManager -{ - 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); - } -} diff --git a/app/src/main/res/xml/widget_checkmark_info.xml b/app/src/main/res/xml/widget_checkmark_info.xml index 3474f56bf..729170884 100644 --- a/app/src/main/res/xml/widget_checkmark_info.xml +++ b/app/src/main/res/xml/widget_checkmark_info.xml @@ -25,7 +25,7 @@ android:previewImage="@drawable/widget_preview_checkmark" android:resizeMode="none" android:updatePeriodMillis="3600000" - android:configure="org.isoron.uhabits.widgets.HabitPickerDialog" + android:configure="org.isoron.uhabits.ui.widgets.HabitPickerDialog" android:widgetCategory="home_screen"> diff --git a/app/src/main/res/xml/widget_frequency_info.xml b/app/src/main/res/xml/widget_frequency_info.xml index 32a25906b..2cb07f9a6 100644 --- a/app/src/main/res/xml/widget_frequency_info.xml +++ b/app/src/main/res/xml/widget_frequency_info.xml @@ -27,7 +27,7 @@ android:previewImage="@drawable/widget_preview_frequency" android:resizeMode="vertical|horizontal" android:updatePeriodMillis="3600000" - android:configure="org.isoron.uhabits.widgets.HabitPickerDialog" + android:configure="org.isoron.uhabits.ui.widgets.HabitPickerDialog" android:widgetCategory="home_screen"> \ No newline at end of file diff --git a/app/src/main/res/xml/widget_history_info.xml b/app/src/main/res/xml/widget_history_info.xml index 5b8d501af..392fa0892 100644 --- a/app/src/main/res/xml/widget_history_info.xml +++ b/app/src/main/res/xml/widget_history_info.xml @@ -27,7 +27,7 @@ android:previewImage="@drawable/widget_preview_history" android:resizeMode="vertical|horizontal" android:updatePeriodMillis="3600000" - android:configure="org.isoron.uhabits.widgets.HabitPickerDialog" + android:configure="org.isoron.uhabits.ui.widgets.HabitPickerDialog" android:widgetCategory="home_screen"> \ No newline at end of file diff --git a/app/src/main/res/xml/widget_score_info.xml b/app/src/main/res/xml/widget_score_info.xml index e2be33252..e69902047 100644 --- a/app/src/main/res/xml/widget_score_info.xml +++ b/app/src/main/res/xml/widget_score_info.xml @@ -27,7 +27,7 @@ android:previewImage="@drawable/widget_preview_score" android:resizeMode="vertical|horizontal" android:updatePeriodMillis="3600000" - android:configure="org.isoron.uhabits.widgets.HabitPickerDialog" + android:configure="org.isoron.uhabits.ui.widgets.HabitPickerDialog" android:widgetCategory="home_screen"> \ No newline at end of file diff --git a/app/src/main/res/xml/widget_streak_info.xml b/app/src/main/res/xml/widget_streak_info.xml index 4ca86447f..e201161dd 100644 --- a/app/src/main/res/xml/widget_streak_info.xml +++ b/app/src/main/res/xml/widget_streak_info.xml @@ -27,7 +27,7 @@ android:previewImage="@drawable/widget_preview_streaks" android:resizeMode="vertical|horizontal" android:updatePeriodMillis="3600000" - android:configure="org.isoron.uhabits.widgets.HabitPickerDialog" + android:configure="org.isoron.uhabits.ui.widgets.HabitPickerDialog" android:widgetCategory="home_screen"> \ No newline at end of file diff --git a/app/src/test/java/org/isoron/uhabits/TestModule.java b/app/src/test/java/org/isoron/uhabits/TestModule.java index f49c96318..f93845f89 100644 --- a/app/src/test/java/org/isoron/uhabits/TestModule.java +++ b/app/src/test/java/org/isoron/uhabits/TestModule.java @@ -35,16 +35,15 @@ public class TestModule { @Singleton @Provides - Preferences providePreferences() + CommandRunner provideCommandRunner() { - return mock(Preferences.class); + return mock(CommandRunner.class); } - @Singleton @Provides - CommandRunner provideCommandRunner() + Habit provideHabit() { - return mock(CommandRunner.class); + return mock(Habit.class); } @Singleton @@ -55,15 +54,23 @@ public class TestModule } @Provides - Habit provideHabit() + @Singleton + ModelFactory provideModelFactory() { - return mock(Habit.class); + return new MemoryModelFactory(); + } + + @Singleton + @Provides + Preferences providePreferences() + { + return mock(Preferences.class); } @Provides @Singleton - ModelFactory provideModelFactory() + WidgetPreferences provideWidgetPreferences() { - return new MemoryModelFactory(); + return mock(WidgetPreferences.class); } }