|
|
@ -26,20 +26,23 @@ import android.graphics.Paint;
|
|
|
|
import android.graphics.PorterDuff;
|
|
|
|
import android.graphics.PorterDuff;
|
|
|
|
import android.graphics.PorterDuffXfermode;
|
|
|
|
import android.graphics.PorterDuffXfermode;
|
|
|
|
import android.graphics.RectF;
|
|
|
|
import android.graphics.RectF;
|
|
|
|
|
|
|
|
import android.support.annotation.Nullable;
|
|
|
|
import android.util.AttributeSet;
|
|
|
|
import android.util.AttributeSet;
|
|
|
|
|
|
|
|
|
|
|
|
import org.isoron.helpers.ColorHelper;
|
|
|
|
import org.isoron.helpers.ColorHelper;
|
|
|
|
import org.isoron.helpers.DateHelper;
|
|
|
|
import org.isoron.helpers.DateHelper;
|
|
|
|
|
|
|
|
import org.isoron.uhabits.R;
|
|
|
|
import org.isoron.uhabits.models.Habit;
|
|
|
|
import org.isoron.uhabits.models.Habit;
|
|
|
|
import org.isoron.uhabits.models.Score;
|
|
|
|
import org.isoron.uhabits.models.Score;
|
|
|
|
|
|
|
|
|
|
|
|
import java.text.SimpleDateFormat;
|
|
|
|
import java.text.SimpleDateFormat;
|
|
|
|
|
|
|
|
import java.util.Calendar;
|
|
|
|
|
|
|
|
import java.util.GregorianCalendar;
|
|
|
|
import java.util.Locale;
|
|
|
|
import java.util.Locale;
|
|
|
|
import java.util.Random;
|
|
|
|
import java.util.Random;
|
|
|
|
|
|
|
|
|
|
|
|
public class HabitScoreView extends ScrollableDataView implements HabitDataView
|
|
|
|
public class HabitScoreView extends ScrollableDataView implements HabitDataView
|
|
|
|
{
|
|
|
|
{
|
|
|
|
public static final int BUCKET_SIZE = 7;
|
|
|
|
|
|
|
|
public static final PorterDuffXfermode XFERMODE_CLEAR =
|
|
|
|
public static final PorterDuffXfermode XFERMODE_CLEAR =
|
|
|
|
new PorterDuffXfermode(PorterDuff.Mode.CLEAR);
|
|
|
|
new PorterDuffXfermode(PorterDuff.Mode.CLEAR);
|
|
|
|
public static final PorterDuffXfermode XFERMODE_SRC =
|
|
|
|
public static final PorterDuffXfermode XFERMODE_SRC =
|
|
|
@ -48,8 +51,10 @@ public class HabitScoreView extends ScrollableDataView implements HabitDataView
|
|
|
|
private Paint pGrid;
|
|
|
|
private Paint pGrid;
|
|
|
|
private float em;
|
|
|
|
private float em;
|
|
|
|
private Habit habit;
|
|
|
|
private Habit habit;
|
|
|
|
|
|
|
|
|
|
|
|
private SimpleDateFormat dfMonth;
|
|
|
|
private SimpleDateFormat dfMonth;
|
|
|
|
private SimpleDateFormat dfDay;
|
|
|
|
private SimpleDateFormat dfDay;
|
|
|
|
|
|
|
|
private SimpleDateFormat dfYear;
|
|
|
|
|
|
|
|
|
|
|
|
private Paint pText, pGraph;
|
|
|
|
private Paint pText, pGraph;
|
|
|
|
private RectF rect, prevRect;
|
|
|
|
private RectF rect, prevRect;
|
|
|
@ -62,16 +67,19 @@ public class HabitScoreView extends ScrollableDataView implements HabitDataView
|
|
|
|
|
|
|
|
|
|
|
|
private int textColor;
|
|
|
|
private int textColor;
|
|
|
|
private int dimmedTextColor;
|
|
|
|
private int dimmedTextColor;
|
|
|
|
private int[] colors;
|
|
|
|
|
|
|
|
|
|
|
|
@Nullable
|
|
|
|
private int[] scores;
|
|
|
|
private int[] scores;
|
|
|
|
|
|
|
|
|
|
|
|
private int primaryColor;
|
|
|
|
private int primaryColor;
|
|
|
|
private boolean isBackgroundTransparent;
|
|
|
|
private boolean isBackgroundTransparent;
|
|
|
|
|
|
|
|
private int bucketSize = 7;
|
|
|
|
|
|
|
|
private int footerHeight;
|
|
|
|
|
|
|
|
|
|
|
|
public HabitScoreView(Context context, AttributeSet attrs)
|
|
|
|
public HabitScoreView(Context context, AttributeSet attrs)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
super(context, attrs);
|
|
|
|
super(context, attrs);
|
|
|
|
this.primaryColor = ColorHelper.palette[7];
|
|
|
|
this.primaryColor = ColorHelper.palette[7];
|
|
|
|
this.scores = new int[0];
|
|
|
|
|
|
|
|
init();
|
|
|
|
init();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -85,10 +93,11 @@ public class HabitScoreView extends ScrollableDataView implements HabitDataView
|
|
|
|
|
|
|
|
|
|
|
|
private void init()
|
|
|
|
private void init()
|
|
|
|
{
|
|
|
|
{
|
|
|
|
refreshData();
|
|
|
|
|
|
|
|
createPaints();
|
|
|
|
createPaints();
|
|
|
|
createColors();
|
|
|
|
createColors();
|
|
|
|
|
|
|
|
if(isInEditMode()) refreshData();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
dfYear = new SimpleDateFormat("yyyy", Locale.getDefault());
|
|
|
|
dfMonth = new SimpleDateFormat("MMM", Locale.getDefault());
|
|
|
|
dfMonth = new SimpleDateFormat("MMM", Locale.getDefault());
|
|
|
|
dfDay = new SimpleDateFormat("d", Locale.getDefault());
|
|
|
|
dfDay = new SimpleDateFormat("d", Locale.getDefault());
|
|
|
|
|
|
|
|
|
|
|
@ -114,13 +123,6 @@ public class HabitScoreView extends ScrollableDataView implements HabitDataView
|
|
|
|
textColor = Color.argb(64, 0, 0, 0);
|
|
|
|
textColor = Color.argb(64, 0, 0, 0);
|
|
|
|
dimmedTextColor = Color.argb(16, 0, 0, 0);
|
|
|
|
dimmedTextColor = Color.argb(16, 0, 0, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
colors = new int[4];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
colors[0] = Color.rgb(230, 230, 230);
|
|
|
|
|
|
|
|
colors[3] = primaryColor;
|
|
|
|
|
|
|
|
colors[1] = ColorHelper.mixColors(colors[0], colors[3], 0.66f);
|
|
|
|
|
|
|
|
colors[2] = ColorHelper.mixColors(colors[0], colors[3], 0.33f);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
protected void createPaints()
|
|
|
|
protected void createPaints()
|
|
|
@ -149,19 +151,23 @@ public class HabitScoreView extends ScrollableDataView implements HabitDataView
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if(height < 9) height = 200;
|
|
|
|
if(height < 9) height = 200;
|
|
|
|
|
|
|
|
|
|
|
|
baseSize = height / 9;
|
|
|
|
int maxTextSize = getResources().getDimensionPixelSize(R.dimen.regularTextSize);
|
|
|
|
|
|
|
|
pText.setTextSize(Math.min(height * 0.047f, maxTextSize));
|
|
|
|
|
|
|
|
em = pText.getFontSpacing();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
footerHeight = (int)(3 * em);
|
|
|
|
|
|
|
|
paddingTop = (int) (em);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
baseSize = (height - footerHeight - paddingTop) / 8;
|
|
|
|
setScrollerBucketSize(baseSize);
|
|
|
|
setScrollerBucketSize(baseSize);
|
|
|
|
|
|
|
|
|
|
|
|
columnWidth = baseSize;
|
|
|
|
columnWidth = baseSize;
|
|
|
|
columnHeight = 8 * baseSize;
|
|
|
|
columnHeight = 8 * baseSize;
|
|
|
|
nColumns = width / baseSize;
|
|
|
|
nColumns = width / baseSize;
|
|
|
|
paddingTop = (int) (baseSize * 0.15f);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pText.setTextSize(baseSize * 0.5f);
|
|
|
|
|
|
|
|
pGraph.setTextSize(baseSize * 0.5f);
|
|
|
|
pGraph.setTextSize(baseSize * 0.5f);
|
|
|
|
pGraph.setStrokeWidth(baseSize * 0.1f);
|
|
|
|
pGraph.setStrokeWidth(baseSize * 0.1f);
|
|
|
|
pGrid.setStrokeWidth(baseSize * 0.05f);
|
|
|
|
pGrid.setStrokeWidth(baseSize * 0.05f);
|
|
|
|
em = pText.getFontSpacing();
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void refreshData()
|
|
|
|
public void refreshData()
|
|
|
@ -171,12 +177,17 @@ public class HabitScoreView extends ScrollableDataView implements HabitDataView
|
|
|
|
else
|
|
|
|
else
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (habit == null) return;
|
|
|
|
if (habit == null) return;
|
|
|
|
scores = habit.scores.getAllValues(BUCKET_SIZE);
|
|
|
|
scores = habit.scores.getAllValues(bucketSize);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
invalidate();
|
|
|
|
invalidate();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void setBucketSize(int bucketSize)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
this.bucketSize = bucketSize;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void generateRandomData()
|
|
|
|
private void generateRandomData()
|
|
|
|
{
|
|
|
|
{
|
|
|
|
Random random = new Random();
|
|
|
|
Random random = new Random();
|
|
|
@ -195,37 +206,34 @@ public class HabitScoreView extends ScrollableDataView implements HabitDataView
|
|
|
|
protected void onDraw(Canvas canvas)
|
|
|
|
protected void onDraw(Canvas canvas)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
super.onDraw(canvas);
|
|
|
|
super.onDraw(canvas);
|
|
|
|
|
|
|
|
if (habit == null || scores == null) return;
|
|
|
|
float lineHeight = pText.getFontSpacing();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
rect.set(0, 0, nColumns * columnWidth, columnHeight);
|
|
|
|
rect.set(0, 0, nColumns * columnWidth, columnHeight);
|
|
|
|
rect.offset(0, paddingTop);
|
|
|
|
rect.offset(0, paddingTop);
|
|
|
|
|
|
|
|
|
|
|
|
drawGrid(canvas, rect);
|
|
|
|
drawGrid(canvas, rect);
|
|
|
|
|
|
|
|
|
|
|
|
String previousMonth = "";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pText.setTextAlign(Paint.Align.CENTER);
|
|
|
|
|
|
|
|
pText.setColor(textColor);
|
|
|
|
pText.setColor(textColor);
|
|
|
|
pGraph.setColor(primaryColor);
|
|
|
|
pGraph.setColor(primaryColor);
|
|
|
|
prevRect.setEmpty();
|
|
|
|
prevRect.setEmpty();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
previousMonthText = "";
|
|
|
|
|
|
|
|
previousYearText = "";
|
|
|
|
|
|
|
|
skipYear = 0;
|
|
|
|
|
|
|
|
|
|
|
|
long currentDate = DateHelper.getStartOfToday();
|
|
|
|
long currentDate = DateHelper.getStartOfToday();
|
|
|
|
|
|
|
|
|
|
|
|
for(int k = 0; k < nColumns + getDataOffset() - 1; k++)
|
|
|
|
for(int k = 0; k < nColumns + getDataOffset() - 1; k++)
|
|
|
|
currentDate -= 7 * DateHelper.millisecondsInOneDay;
|
|
|
|
currentDate -= bucketSize * DateHelper.millisecondsInOneDay;
|
|
|
|
|
|
|
|
|
|
|
|
for (int k = 0; k < nColumns; k++)
|
|
|
|
for (int k = 0; k < nColumns; k++)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
String month = dfMonth.format(currentDate);
|
|
|
|
|
|
|
|
String day = dfDay.format(currentDate);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int score = 0;
|
|
|
|
int score = 0;
|
|
|
|
int offset = nColumns - k - 1 + getDataOffset();
|
|
|
|
int offset = nColumns - k - 1 + getDataOffset();
|
|
|
|
if(offset < scores.length) score = scores[offset];
|
|
|
|
if(offset < scores.length) score = scores[offset];
|
|
|
|
|
|
|
|
|
|
|
|
double sRelative = ((double) score) / Score.MAX_VALUE;
|
|
|
|
double relativeScore = ((double) score) / Score.MAX_VALUE;
|
|
|
|
int height = (int) (columnHeight * sRelative);
|
|
|
|
int height = (int) (columnHeight * relativeScore);
|
|
|
|
|
|
|
|
|
|
|
|
rect.set(0, 0, baseSize, baseSize);
|
|
|
|
rect.set(0, 0, baseSize, baseSize);
|
|
|
|
rect.offset(k * columnWidth, paddingTop + columnHeight - height - columnWidth / 2);
|
|
|
|
rect.offset(k * columnWidth, paddingTop + columnHeight - height - columnWidth / 2);
|
|
|
@ -239,20 +247,69 @@ public class HabitScoreView extends ScrollableDataView implements HabitDataView
|
|
|
|
if (k == nColumns - 1) drawMarker(canvas, rect);
|
|
|
|
if (k == nColumns - 1) drawMarker(canvas, rect);
|
|
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
|
|
if (!month.equals(previousMonth))
|
|
|
|
drawFooter(canvas, rect, currentDate);
|
|
|
|
canvas.drawText(month, rect.centerX(), rect.bottom + lineHeight * 1.2f, pText);
|
|
|
|
|
|
|
|
|
|
|
|
currentDate += bucketSize * DateHelper.millisecondsInOneDay;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private int skipYear = 0;
|
|
|
|
|
|
|
|
private String previousYearText;
|
|
|
|
|
|
|
|
private String previousMonthText;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void drawFooter(Canvas canvas, RectF rect, long currentDate)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
String yearText = dfYear.format(currentDate);
|
|
|
|
|
|
|
|
String monthText = dfMonth.format(currentDate);
|
|
|
|
|
|
|
|
String dayText = dfDay.format(currentDate);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
GregorianCalendar calendar = DateHelper.getCalendar(currentDate);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
String text;
|
|
|
|
|
|
|
|
int year = calendar.get(Calendar.YEAR);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
boolean shouldPrintYear = true;
|
|
|
|
|
|
|
|
if(yearText.equals(previousYearText)) shouldPrintYear = false;
|
|
|
|
|
|
|
|
if(bucketSize >= 365 && (year % 2) != 0) shouldPrintYear = false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(skipYear > 0)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
skipYear--;
|
|
|
|
|
|
|
|
shouldPrintYear = false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(shouldPrintYear)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
previousYearText = yearText;
|
|
|
|
|
|
|
|
previousMonthText = "";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pText.setTextAlign(Paint.Align.CENTER);
|
|
|
|
|
|
|
|
canvas.drawText(yearText, rect.centerX(), rect.bottom + em * 2.2f, pText);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
skipYear = 1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(bucketSize < 365)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if(!monthText.equals(previousMonthText))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
previousMonthText = monthText;
|
|
|
|
|
|
|
|
text = monthText;
|
|
|
|
|
|
|
|
}
|
|
|
|
else
|
|
|
|
else
|
|
|
|
canvas.drawText(day, rect.centerX(), rect.bottom + lineHeight * 1.2f, pText);
|
|
|
|
{
|
|
|
|
|
|
|
|
text = dayText;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
previousMonth = month;
|
|
|
|
pText.setTextAlign(Paint.Align.CENTER);
|
|
|
|
currentDate += 7 * DateHelper.millisecondsInOneDay;
|
|
|
|
canvas.drawText(text, rect.centerX(), rect.bottom + em * 1.2f, pText);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void drawGrid(Canvas canvas, RectF rGrid)
|
|
|
|
private void drawGrid(Canvas canvas, RectF rGrid)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
int nRows = 5;
|
|
|
|
int nRows = 5;
|
|
|
|