diff --git a/android/uhabits-android/src/main/AndroidManifest.xml b/android/uhabits-android/src/main/AndroidManifest.xml
index 0c298306b..1341f5afe 100644
--- a/android/uhabits-android/src/main/AndroidManifest.xml
+++ b/android/uhabits-android/src/main/AndroidManifest.xml
@@ -107,6 +107,17 @@
android:name="android.appwidget.provider"
android:resource="@xml/widget_checkmark_info"/>
+
+
+
+
+
+
+
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/CurrentStreakWidget.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/CurrentStreakWidget.kt
new file mode 100644
index 000000000..b81236a73
--- /dev/null
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/CurrentStreakWidget.kt
@@ -0,0 +1,58 @@
+/*
+ * 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.content.*
+import android.view.*
+import org.isoron.uhabits.core.models.*
+import org.isoron.uhabits.utils.*
+import org.isoron.uhabits.widgets.views.*
+
+class CurrentStreakWidget(
+ context: Context,
+ widgetId: Int,
+ private val habit: Habit
+) : BaseWidget(context, widgetId) {
+
+ override fun getOnClickPendingIntent(context: Context) =
+ pendingIntentFactory.toggleCheckmark(habit, null)
+
+ override fun refreshData(v: View) {
+ val activeStreak = habit.streaks.activeStreak
+ val numReps = if (activeStreak != null) {
+ habit.repetitions.getByInterval(activeStreak.start, activeStreak.end).size.toString()
+ } else {
+ "0"
+ }
+ (v as CurrentStreakWidgetView).apply {
+ setBackgroundAlpha(preferedBackgroundAlpha)
+ setCurrentStreak(numReps)
+ setPercentage(habit.scores.todayValue.toFloat())
+ setActiveColor(PaletteUtils.getColor(context, habit.color))
+ setName(habit.name)
+ setCheckmarkValue(habit.checkmarks.todayValue)
+ refresh()
+ }
+ }
+
+ override fun buildView() = CurrentStreakWidgetView(context)
+ override fun getDefaultHeight() = 125
+ override fun getDefaultWidth() = 125
+}
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/CurrentStreakWidgetProvider.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/CurrentStreakWidgetProvider.kt
new file mode 100644
index 000000000..b813ff62c
--- /dev/null
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/CurrentStreakWidgetProvider.kt
@@ -0,0 +1,29 @@
+/*
+ * 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.content.*
+
+class CurrentStreakWidgetProvider : BaseWidgetProvider() {
+ override fun getWidgetFromId(context: Context, id: Int): BaseWidget {
+ val habits = getHabitsFromWidgetId(id)
+ if (habits.size == 1) return CurrentStreakWidget(context, id, habits[0])
+ else return StackWidget(context, id, StackWidgetType.CHECKMARK, habits)
+ }
+}
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/StackWidgetService.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/StackWidgetService.java
index 64b4be5a9..a02ca9997 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/StackWidgetService.java
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/StackWidgetService.java
@@ -96,6 +96,8 @@ class StackRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory
{
case CHECKMARK:
return new CheckmarkWidget(context, widgetId, habit);
+ case CURRENTSTREAK:
+ return new CurrentStreakWidget(context, widgetId, habit);
case FREQUENCY:
return new FrequencyWidget(context, widgetId, habit, prefs.getFirstWeekday());
case SCORE:
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/StackWidgetType.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/StackWidgetType.java
index a5aeebb82..7bad462fb 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/StackWidgetType.java
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/StackWidgetType.java
@@ -12,7 +12,8 @@ public enum StackWidgetType {
FREQUENCY(1),
SCORE(2), // habit strength widget
HISTORY(3),
- STREAKS(4);
+ STREAKS(4),
+ CURRENTSTREAK(5);
private int value;
StackWidgetType(int value) {
@@ -26,6 +27,8 @@ public enum StackWidgetType {
public static StackWidgetType getWidgetTypeFromValue(int value) {
if (CHECKMARK.getValue() == value) {
return CHECKMARK;
+ } else if (CURRENTSTREAK.getValue() == value) {
+ return CURRENTSTREAK;
} else if (FREQUENCY.getValue() == value) {
return FREQUENCY;
} else if (SCORE.getValue() == value) {
@@ -42,6 +45,8 @@ public enum StackWidgetType {
switch (type) {
case CHECKMARK:
return R.layout.checkmark_stackview_widget;
+ case CURRENTSTREAK:
+ return R.layout.currentstreak_stackview_widget;
case FREQUENCY:
return R.layout.frequency_stackview_widget;
case SCORE:
@@ -58,6 +63,8 @@ public enum StackWidgetType {
switch (type) {
case CHECKMARK:
return R.id.checkmarkStackWidgetView;
+ case CURRENTSTREAK:
+ return R.id.currentStreakkWidgetEmptyView;
case FREQUENCY:
return R.id.frequencyStackWidgetView;
case SCORE:
@@ -74,6 +81,8 @@ public enum StackWidgetType {
switch (type) {
case CHECKMARK:
return R.id.checkmarkStackWidgetEmptyView;
+ case CURRENTSTREAK:
+ return R.id.currentStreakkWidgetEmptyView;
case FREQUENCY:
return R.id.frequencyStackWidgetEmptyView;
case SCORE:
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/WidgetUpdater.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/WidgetUpdater.kt
index b8293582a..cea167704 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/WidgetUpdater.kt
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/WidgetUpdater.kt
@@ -63,6 +63,7 @@ class WidgetUpdater
fun updateWidgets(modifiedHabitId: Long?) {
taskRunner.execute {
updateWidgets(modifiedHabitId, CheckmarkWidgetProvider::class.java)
+ updateWidgets(modifiedHabitId, CurrentStreakWidgetProvider::class.java)
updateWidgets(modifiedHabitId, HistoryWidgetProvider::class.java)
updateWidgets(modifiedHabitId, ScoreWidgetProvider::class.java)
updateWidgets(modifiedHabitId, StreakWidgetProvider::class.java)
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/views/CheckmarkWidgetView.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/views/CheckmarkWidgetView.java
index f241d5a32..c5a99cdfd 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/views/CheckmarkWidgetView.java
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/views/CheckmarkWidgetView.java
@@ -156,9 +156,9 @@ public class CheckmarkWidgetView extends HabitWidgetView
ring.setVisibility(VISIBLE);
widthMeasureSpec =
- MeasureSpec.makeMeasureSpec((int) w, MeasureSpec.EXACTLY);
+ MeasureSpec.makeMeasureSpec((int) w, MeasureSpec.EXACTLY);
heightMeasureSpec =
- MeasureSpec.makeMeasureSpec((int) h, MeasureSpec.EXACTLY);
+ MeasureSpec.makeMeasureSpec((int) h, MeasureSpec.EXACTLY);
float textSize = 0.15f * h;
float maxTextSize = getDimension(getContext(), R.dimen.smallerTextSize);
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/views/CurrentStreakWidgetView.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/views/CurrentStreakWidgetView.java
new file mode 100644
index 000000000..2dc869830
--- /dev/null
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/views/CurrentStreakWidgetView.java
@@ -0,0 +1,189 @@
+/*
+ * 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.views;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import org.isoron.androidbase.utils.StyledResources;
+import org.isoron.uhabits.R;
+import org.isoron.uhabits.activities.common.views.RingView;
+import org.isoron.uhabits.core.models.Checkmark;
+import org.isoron.uhabits.utils.PaletteUtils;
+
+import static org.isoron.androidbase.utils.InterfaceUtils.getDimension;
+
+public class CurrentStreakWidgetView extends HabitWidgetView
+{
+ private int activeColor;
+
+ private float percentage;
+
+ @Nullable
+ private String name;
+
+ private RingView ring;
+
+ private TextView label;
+
+ private int checkmarkValue;
+ private String currentStreak;
+
+ public CurrentStreakWidgetView(Context context)
+ {
+ super(context);
+ init();
+ }
+
+ public CurrentStreakWidgetView(Context context, AttributeSet attrs)
+ {
+ super(context, attrs);
+ init();
+ }
+
+ public void refresh()
+ {
+ if (backgroundPaint == null || frame == null || ring == null) return;
+
+ StyledResources res = new StyledResources(getContext());
+
+ String text;
+ int bgColor;
+ int fgColor;
+
+ switch (checkmarkValue)
+ {
+ case Checkmark.CHECKED_EXPLICITLY:
+ bgColor = activeColor;
+ fgColor = res.getColor(R.attr.highContrastReverseTextColor);
+ setShadowAlpha(0x4f);
+ backgroundPaint.setColor(bgColor);
+ frame.setBackgroundDrawable(background);
+ break;
+
+ case Checkmark.CHECKED_IMPLICITLY:
+ case Checkmark.UNCHECKED:
+ default:
+ bgColor = res.getColor(R.attr.cardBgColor);
+ fgColor = res.getColor(R.attr.mediumContrastTextColor);
+ setShadowAlpha(0x00);
+ break;
+ }
+ text = currentStreak;
+
+ ring.setPercentage(percentage);
+ ring.setColor(fgColor);
+ ring.setBackgroundColor(bgColor);
+ ring.setText(text);
+
+ label.setText(name);
+ label.setTextColor(fgColor);
+
+ requestLayout();
+ postInvalidate();
+ }
+
+ public void setActiveColor(int activeColor)
+ {
+ this.activeColor = activeColor;
+ }
+ public void setCurrentStreak(String currentStreak)
+ {
+ this.currentStreak = currentStreak;
+ }
+
+ public void setCheckmarkValue(int checkmarkValue)
+ {
+ this.checkmarkValue = checkmarkValue;
+ }
+
+ public void setName(@NonNull String name)
+ {
+ this.name = name;
+ }
+
+ public void setPercentage(float percentage)
+ {
+ this.percentage = percentage;
+ }
+
+ @Override
+ @NonNull
+ protected Integer getInnerLayoutId()
+ {
+ return R.layout.widget_checkmark;
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
+ {
+ int width = MeasureSpec.getSize(widthMeasureSpec);
+ int height = MeasureSpec.getSize(heightMeasureSpec);
+
+ float w = width;
+ float h = width * 1.25f;
+ float scale = Math.min(width / w, height / h);
+
+ w *= scale;
+ h *= scale;
+
+ if (h < getDimension(getContext(), R.dimen.checkmarkWidget_heightBreakpoint))
+ ring.setVisibility(GONE);
+ else
+ ring.setVisibility(VISIBLE);
+
+ widthMeasureSpec =
+ MeasureSpec.makeMeasureSpec((int) w, MeasureSpec.EXACTLY);
+ heightMeasureSpec =
+ MeasureSpec.makeMeasureSpec((int) h, MeasureSpec.EXACTLY);
+
+ float textSize = 0.15f * h;
+ float maxTextSize = getDimension(getContext(), R.dimen.smallerTextSize);
+ textSize = Math.min(textSize, maxTextSize);
+
+ label.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
+ ring.setTextSize(textSize);
+ ring.setThickness(0.15f * textSize);
+
+ 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 = PaletteUtils.getAndroidTestColor(6);
+ checkmarkValue = Checkmark.CHECKED_EXPLICITLY;
+ refresh();
+ }
+ }
+}
diff --git a/android/uhabits-android/src/main/res/layout/currentstreak_stackview_widget.xml b/android/uhabits-android/src/main/res/layout/currentstreak_stackview_widget.xml
new file mode 100644
index 000000000..3f4e07f40
--- /dev/null
+++ b/android/uhabits-android/src/main/res/layout/currentstreak_stackview_widget.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
diff --git a/android/uhabits-android/src/main/res/values/strings.xml b/android/uhabits-android/src/main/res/values/strings.xml
index d2273b13b..39cfc8241 100644
--- a/android/uhabits-android/src/main/res/values/strings.xml
+++ b/android/uhabits-android/src/main/res/values/strings.xml
@@ -174,6 +174,8 @@
Dark theme
Use pure black in dark theme
Replaces gray backgrounds with pure black in dark theme. Reduces battery usage in phones with AMOLED display.
+ Widget numbers instead of checkmarks
+ Replaces the checkmark in the 1x1 habit widget with the number of times you\'ve currently chained the habit.
Interface
Reverse order of days
Show days in reverse order on the main screen.
diff --git a/android/uhabits-android/src/main/res/xml/preferences.xml b/android/uhabits-android/src/main/res/xml/preferences.xml
index 032dda1d7..5c0e7f5bb 100644
--- a/android/uhabits-android/src/main/res/xml/preferences.xml
+++ b/android/uhabits-android/src/main/res/xml/preferences.xml
@@ -59,6 +59,14 @@
android:key="pref_first_weekday"
android:title="@string/first_day_of_the_week"
app:iconSpaceReserved="false" />
+
diff --git a/android/uhabits-android/src/main/res/xml/widget_currentstreak_info.xml b/android/uhabits-android/src/main/res/xml/widget_currentstreak_info.xml
new file mode 100644
index 000000000..4541e5857
--- /dev/null
+++ b/android/uhabits-android/src/main/res/xml/widget_currentstreak_info.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/Streak.java b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/Streak.java
index 4a66bed7a..b75bd147c 100644
--- a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/Streak.java
+++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/Streak.java
@@ -20,6 +20,7 @@
package org.isoron.uhabits.core.models;
import org.apache.commons.lang3.builder.*;
+import org.isoron.uhabits.core.utils.DateUtils;
import static org.isoron.uhabits.core.utils.StringUtils.defaultToStringStyle;
@@ -29,6 +30,8 @@ public final class Streak
private final Timestamp end;
+ //TODO define the "End" of a streak to follow the rules of the given streak?
+ //ex if 2 times in a week, define end at end of that week. or by other rules ofc
public Streak(Timestamp start, Timestamp end)
{
this.start = start;
@@ -53,6 +56,12 @@ public final class Streak
return end;
}
+ public boolean isActive()
+ {
+ Timestamp now = DateUtils.getToday();
+ return (start.daysUntil(now) >= 0) & (end.daysUntil(now) <= 1);
+ }
+
public int getLength()
{
return start.daysUntil(end) + 1;
diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/StreakList.java b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/StreakList.java
index 542ed9962..09cf23e9b 100644
--- a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/StreakList.java
+++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/StreakList.java
@@ -78,6 +78,18 @@ public abstract class StreakList
add(streaks);
}
+ public synchronized Streak getActiveStreak()
+ {
+ List streaks = getAll();
+ if (streaks.size() > 0){
+ Streak s = streaks.get(0);
+ if (s.isActive()){
+ return s;
+ }
+ }
+ return null;
+ }
+
/**
* Converts a list of checkmark values to a list of streaks.
*
diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/preferences/Preferences.java b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/preferences/Preferences.java
index 2cec8f862..44a949172 100644
--- a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/preferences/Preferences.java
+++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/preferences/Preferences.java
@@ -333,6 +333,11 @@ public class Preferences
return Integer.parseInt(storage.getString("pref_widget_opacity", "102"));
}
+ public boolean getWidgetNumberMode()
+ {
+ return storage.getBoolean("pref_widget_numbers", false);
+ }
+
/**
* @return An integer representing the first day of the week. Sunday
* corresponds to 1, Monday to 2, and so on, until Saturday, which is