Allow multiple-habits on strength chart

Now when creating a widget from several habits, if you choose "Habit
strength" as the type, then instead of stacking, they will be displayed
on the same chart.
pull/446/head
John Knox 7 years ago
parent 6a3e430a5e
commit 5d331250e4

@ -21,7 +21,6 @@ package org.isoron.uhabits.activities.common.views;
import android.content.*; import android.content.*;
import android.graphics.*; import android.graphics.*;
import android.support.annotation.*;
import android.util.*; import android.util.*;
import org.isoron.androidbase.utils.*; import org.isoron.androidbase.utils.*;
@ -71,10 +70,7 @@ public class ScoreChart extends ScrollableChart
private int gridColor; private int gridColor;
@Nullable private List<Habit> habits = new ArrayList<>();
private List<Score> scores;
private int primaryColor;
@Deprecated @Deprecated
private int bucketSize = 7; private int bucketSize = 7;
@ -93,6 +89,8 @@ public class ScoreChart extends ScrollableChart
private String previousMonthText; private String previousMonthText;
private boolean drawFooter;
public ScoreChart(Context context) public ScoreChart(Context context)
{ {
super(context); super(context);
@ -108,7 +106,6 @@ public class ScoreChart extends ScrollableChart
public void populateWithRandomData() public void populateWithRandomData()
{ {
Random random = new Random(); Random random = new Random();
scores = new LinkedList<>();
double previous = 0.5f; double previous = 0.5f;
Timestamp timestamp = DateUtils.getToday(); Timestamp timestamp = DateUtils.getToday();
@ -118,7 +115,6 @@ public class ScoreChart extends ScrollableChart
double step = 0.1f; double step = 0.1f;
double current = previous + random.nextDouble() * step * 2 - step; double current = previous + random.nextDouble() * step * 2 - step;
current = Math.max(0, Math.min(1.0f, current)); current = Math.max(0, Math.min(1.0f, current));
scores.add(new Score(timestamp.minus(i), current));
previous = current; previous = current;
} }
} }
@ -130,21 +126,15 @@ public class ScoreChart extends ScrollableChart
postInvalidate(); postInvalidate();
} }
public void setIsTransparencyEnabled(boolean enabled) public void setHabits(List<Habit> habits) {
{ this.habits = habits;
this.isTransparencyEnabled = enabled;
postInvalidate(); postInvalidate();
}
public void setColor(int primaryColor)
{
this.primaryColor = primaryColor;
postInvalidate();
} }
public void setScores(@NonNull List<Score> scores) public void setIsTransparencyEnabled(boolean enabled)
{ {
this.scores = scores; this.isTransparencyEnabled = enabled;
postInvalidate(); postInvalidate();
} }
@ -166,7 +156,7 @@ public class ScoreChart extends ScrollableChart
activeCanvas = canvas; activeCanvas = canvas;
} }
if (scores == null) return; if (habits.isEmpty()) return;
rect.set(0, 0, nColumns * columnWidth, columnHeight); rect.set(0, 0, nColumns * columnWidth, columnHeight);
rect.offset(0, paddingTop); rect.offset(0, paddingTop);
@ -174,42 +164,59 @@ public class ScoreChart extends ScrollableChart
drawGrid(activeCanvas, rect); drawGrid(activeCanvas, rect);
pText.setColor(textColor); pText.setColor(textColor);
pGraph.setColor(primaryColor);
prevRect.setEmpty(); prevRect.setEmpty();
previousMonthText = ""; previousMonthText = "";
previousYearText = ""; previousYearText = "";
skipYear = 0; skipYear = 0;
drawFooter = true;
for (int k = 0; k < nColumns; k++) for (Habit habit: habits)
{ {
int offset = nColumns - k - 1 + getDataOffset();
if (offset >= scores.size()) continue;
double score = scores.get(offset).getValue();
Timestamp timestamp = scores.get(offset).getTimestamp();
int height = (int) (columnHeight * score); pText.setColor(textColor);
pGraph.setColor(color(habit.getColor()));
prevRect.setEmpty();
rect.set(0, 0, baseSize, baseSize); previousMonthText = "";
rect.offset(k * columnWidth + (columnWidth - baseSize) / 2, previousYearText = "";
paddingTop + columnHeight - height - baseSize / 2); skipYear = 0;
if (!prevRect.isEmpty()) for (int k = 0; k < nColumns; k++)
{ {
drawLine(activeCanvas, prevRect, rect); List<Score> scores = habit.getScores().toList();
drawMarker(activeCanvas, prevRect); int offset = nColumns - k - 1 + getDataOffset();
} if (offset >= scores.size()) continue;
double score = scores.get(offset).getValue();
Timestamp timestamp = scores.get(offset).getTimestamp();
int height = (int) (columnHeight * score);
rect.set(0, 0, baseSize, baseSize);
rect.offset(k * columnWidth + (columnWidth - baseSize) / 2,
paddingTop + columnHeight - height - baseSize / 2);
if (!prevRect.isEmpty())
{
drawLine(activeCanvas, prevRect, rect, habit.getColor());
drawMarker(activeCanvas, prevRect, habit.getColor());
}
if (k == nColumns - 1) drawMarker(activeCanvas, rect); if (k == nColumns - 1) drawMarker(activeCanvas, rect, habit.getColor());
prevRect.set(rect); prevRect.set(rect);
rect.set(0, 0, columnWidth, columnHeight); rect.set(0, 0, columnWidth, columnHeight);
rect.offset(k * columnWidth, paddingTop); rect.offset(k * columnWidth, paddingTop);
drawFooter(activeCanvas, rect, timestamp); if (drawFooter) drawFooter(activeCanvas, rect, timestamp);
}
prevRect.setEmpty();
drawFooter = false;
} }
if (activeCanvas != canvas) canvas.drawBitmap(drawingCache, 0, 0, null); if (activeCanvas != canvas) canvas.drawBitmap(drawingCache, 0, 0, null);
} }
@ -328,21 +335,21 @@ public class ScoreChart extends ScrollableChart
canvas.drawLine(rGrid.left, rGrid.top, rGrid.right, rGrid.top, pGrid); canvas.drawLine(rGrid.left, rGrid.top, rGrid.right, rGrid.top, pGrid);
} }
private void drawLine(Canvas canvas, RectF rectFrom, RectF rectTo) private void drawLine(Canvas canvas, RectF rectFrom, RectF rectTo, int colorIndex)
{ {
pGraph.setColor(primaryColor); pGraph.setColor(color(colorIndex));
canvas.drawLine(rectFrom.centerX(), rectFrom.centerY(), canvas.drawLine(rectFrom.centerX(), rectFrom.centerY(),
rectTo.centerX(), rectTo.centerY(), pGraph); rectTo.centerX(), rectTo.centerY(), pGraph);
} }
private void drawMarker(Canvas canvas, RectF rect) private void drawMarker(Canvas canvas, RectF rect, int colorIndex)
{ {
rect.inset(baseSize * 0.225f, baseSize * 0.225f); rect.inset(baseSize * 0.225f, baseSize * 0.225f);
setModeOrColor(pGraph, XFERMODE_CLEAR, backgroundColor); setModeOrColor(pGraph, XFERMODE_CLEAR, backgroundColor);
canvas.drawOval(rect, pGraph); canvas.drawOval(rect, pGraph);
rect.inset(baseSize * 0.1f, baseSize * 0.1f); rect.inset(baseSize * 0.1f, baseSize * 0.1f);
setModeOrColor(pGraph, XFERMODE_SRC, primaryColor); setModeOrColor(pGraph, XFERMODE_SRC, color(colorIndex));
canvas.drawOval(rect, pGraph); canvas.drawOval(rect, pGraph);
// rect.inset(baseSize * 0.1f, baseSize * 0.1f); // rect.inset(baseSize * 0.1f, baseSize * 0.1f);
@ -352,6 +359,10 @@ public class ScoreChart extends ScrollableChart
if (isTransparencyEnabled) pGraph.setXfermode(XFERMODE_SRC); if (isTransparencyEnabled) pGraph.setXfermode(XFERMODE_SRC);
} }
private int color(int colorIndex) {
return PaletteUtils.getColor(getContext(), colorIndex);
}
private float getMaxDayWidth() private float getMaxDayWidth()
{ {
float maxDayWidth = 0; float maxDayWidth = 0;
@ -402,7 +413,6 @@ public class ScoreChart extends ScrollableChart
{ {
StyledResources res = new StyledResources(getContext()); StyledResources res = new StyledResources(getContext());
primaryColor = Color.BLACK;
textColor = res.getColor(R.attr.mediumContrastTextColor); textColor = res.getColor(R.attr.mediumContrastTextColor);
gridColor = res.getColor(R.attr.lowContrastTextColor); gridColor = res.getColor(R.attr.lowContrastTextColor);
backgroundColor = res.getColor(R.attr.cardBackgroundColor); backgroundColor = res.getColor(R.attr.cardBackgroundColor);

@ -128,7 +128,6 @@ public class ScoreCard extends HabitCard
{ {
spinner.setVisibility(GONE); spinner.setVisibility(GONE);
title.setTextColor(PaletteUtils.getAndroidTestColor(1)); title.setTextColor(PaletteUtils.getAndroidTestColor(1));
chart.setColor(PaletteUtils.getAndroidTestColor(1));
chart.populateWithRandomData(); chart.populateWithRandomData();
} }
} }
@ -151,7 +150,9 @@ public class ScoreCard extends HabitCard
if (bucketSize == 1) scores = scoreList.toList(); if (bucketSize == 1) scores = scoreList.toList();
else scores = scoreList.groupBy(getTruncateField(bucketSize)); else scores = scoreList.groupBy(getTruncateField(bucketSize));
chart.setScores(scores); List<Habit> habits = new ArrayList<>();
habits.add(getHabit());
chart.setHabits(habits);
chart.setBucketSize(bucketSize); chart.setBucketSize(bucketSize);
} }
@ -161,7 +162,6 @@ public class ScoreCard extends HabitCard
int color = int color =
PaletteUtils.getColor(getContext(), getHabit().getColor()); PaletteUtils.getColor(getContext(), getHabit().getColor());
title.setTextColor(color); title.setTextColor(color);
chart.setColor(color);
} }
} }
} }

@ -37,7 +37,7 @@ class FrequencyWidget(
override fun refreshData(v: View) { override fun refreshData(v: View) {
val widgetView = v as GraphWidgetView val widgetView = v as GraphWidgetView
widgetView.setTitle(habit.name) widgetView.setHabits(listOf(habit))
(widgetView.dataView as FrequencyChart).apply { (widgetView.dataView as FrequencyChart).apply {
setColor(PaletteUtils.getColor(context, habit.color)) setColor(PaletteUtils.getColor(context, habit.color))
setFrequency(habit.repetitions.weekdayFrequency) setFrequency(habit.repetitions.weekdayFrequency)

@ -47,7 +47,7 @@ class HistoryWidget(
override fun buildView() = override fun buildView() =
GraphWidgetView(context, HistoryChart(context)).apply { GraphWidgetView(context, HistoryChart(context)).apply {
setTitle(habit.name) setHabits(listOf(habit))
} }
override fun getDefaultHeight() = 250 override fun getDefaultHeight() = 250

@ -24,37 +24,31 @@ import android.view.*
import org.isoron.uhabits.activities.common.views.* import org.isoron.uhabits.activities.common.views.*
import org.isoron.uhabits.activities.habits.show.views.* import org.isoron.uhabits.activities.habits.show.views.*
import org.isoron.uhabits.core.models.* import org.isoron.uhabits.core.models.*
import org.isoron.uhabits.utils.*
import org.isoron.uhabits.widgets.views.* import org.isoron.uhabits.widgets.views.*
class ScoreWidget( class ScoreWidget(
context: Context, context: Context,
id: Int, id: Int,
private val habit: Habit private val habits: List<Habit>
) : BaseWidget(context, id) { ) : BaseWidget(context, id) {
override fun getOnClickPendingIntent(context: Context) = override fun getOnClickPendingIntent(context: Context) =
pendingIntentFactory.showHabit(habit) pendingIntentFactory.showHabit(habits.get(0))
override fun refreshData(view: View) { override fun refreshData(view: View) {
val size = ScoreCard.BUCKET_SIZES[prefs.defaultScoreSpinnerPosition] val size = ScoreCard.BUCKET_SIZES[prefs.defaultScoreSpinnerPosition]
val scores = when(size) {
1 -> habit.scores.toList()
else -> habit.scores.groupBy(ScoreCard.getTruncateField(size))
}
val widgetView = view as GraphWidgetView val widgetView = view as GraphWidgetView
(widgetView.dataView as ScoreChart).apply { (widgetView.dataView as ScoreChart).apply {
setIsTransparencyEnabled(true) setIsTransparencyEnabled(true)
setBucketSize(size) setBucketSize(size)
setColor(PaletteUtils.getColor(context, habit.color)) setHabits(habits)
setScores(scores)
} }
} }
override fun buildView() = override fun buildView() =
GraphWidgetView(context, ScoreChart(context)).apply { GraphWidgetView(context, ScoreChart(context)).apply {
setTitle(habit.name) setHabits(habits)
} }
override fun getDefaultHeight() = 300 override fun getDefaultHeight() = 300

@ -23,7 +23,6 @@ import android.content.*
class ScoreWidgetProvider : BaseWidgetProvider() { class ScoreWidgetProvider : BaseWidgetProvider() {
override fun getWidgetFromId(context: Context, id: Int): BaseWidget { override fun getWidgetFromId(context: Context, id: Int): BaseWidget {
val habits = getHabitsFromWidgetId(id) val habits = getHabitsFromWidgetId(id)
if (habits.size == 1) return ScoreWidget(context, id, habits[0]) return ScoreWidget(context, id, habits)
else return StackWidget(context, id, StackWidgetType.SCORE, habits)
} }
} }

@ -95,8 +95,11 @@ class StackRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory
return new CheckmarkWidget(context, widgetId, habit); return new CheckmarkWidget(context, widgetId, habit);
case FREQUENCY: case FREQUENCY:
return new FrequencyWidget(context, widgetId, habit); return new FrequencyWidget(context, widgetId, habit);
case SCORE: case SCORE:{
return new ScoreWidget(context, widgetId, habit); List<Habit> list = new ArrayList<>();
list.add(habit);
return new ScoreWidget(context, widgetId, list);
}
case HISTORY: case HISTORY:
return new HistoryWidget(context, widgetId, habit); return new HistoryWidget(context, widgetId, habit);
case STREAKS: case STREAKS:

@ -47,7 +47,7 @@ class StreakWidget(
override fun buildView(): View { override fun buildView(): View {
return GraphWidgetView(context, StreakChart(context)).apply { return GraphWidgetView(context, StreakChart(context)).apply {
setTitle(habit.name) setHabits(listOf(habit))
layoutParams = LayoutParams(MATCH_PARENT, MATCH_PARENT) layoutParams = LayoutParams(MATCH_PARENT, MATCH_PARENT)
} }
} }

@ -20,18 +20,25 @@
package org.isoron.uhabits.widgets.views; package org.isoron.uhabits.widgets.views;
import android.content.*; import android.content.*;
import android.graphics.Color;
import android.support.annotation.*; import android.support.annotation.*;
import android.view.*; import android.view.*;
import android.widget.*; import android.widget.*;
import org.isoron.uhabits.*; import org.isoron.uhabits.*;
import org.isoron.uhabits.core.models.Habit;
import org.isoron.uhabits.utils.PaletteUtils;
import java.util.List;
public class GraphWidgetView extends HabitWidgetView public class GraphWidgetView extends HabitWidgetView
{ {
private final View dataView; private final View dataView;
private TextView title; private LinearLayout legend;
private List<Habit> habits;
public GraphWidgetView(Context context, View dataView) public GraphWidgetView(Context context, View dataView)
{ {
@ -45,9 +52,18 @@ public class GraphWidgetView extends HabitWidgetView
return dataView; return dataView;
} }
public void setTitle(String text) public void setHabits(List<Habit> habits) {
{ for (Habit habit : habits) {
title.setText(text); TextView t = new TextView(getContext());
if (habits.size() == 1) {
t.setTextColor(Color.WHITE);
} else {
t.setTextColor(PaletteUtils.getColor(getContext(), habit.getColor()));
}
t.setPadding(7, 0, 7, 0);
t.setText(habit.getName());
legend.addView(t);
}
} }
@Override @Override
@ -67,7 +83,6 @@ public class GraphWidgetView extends HabitWidgetView
ViewGroup innerFrame = (ViewGroup) findViewById(R.id.innerFrame); ViewGroup innerFrame = (ViewGroup) findViewById(R.id.innerFrame);
innerFrame.addView(dataView); innerFrame.addView(dataView);
title = (TextView) findViewById(R.id.title); legend = (LinearLayout) findViewById(R.id.legend);
title.setVisibility(VISIBLE);
} }
} }

@ -38,12 +38,12 @@
android:paddingBottom="4dp" android:paddingBottom="4dp"
tools:ignore="UselessParent"> tools:ignore="UselessParent">
<TextView <LinearLayout
android:id="@+id/title" android:id="@+id/legend"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center" android:gravity="center"
android:textColor="@color/white"/> android:orientation="horizontal"/>
</LinearLayout> </LinearLayout>

Loading…
Cancel
Save