mirror of https://github.com/iSoron/uhabits.git
parent
6ec9d51a1e
commit
a6060f468d
@ -0,0 +1,217 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.activities.common.views;
|
||||
|
||||
import android.content.*;
|
||||
import android.graphics.*;
|
||||
import android.util.*;
|
||||
import android.view.*;
|
||||
|
||||
import org.isoron.androidbase.utils.*;
|
||||
import org.isoron.uhabits.*;
|
||||
import org.isoron.uhabits.activities.habits.list.views.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static android.view.View.MeasureSpec.*;
|
||||
import static org.isoron.androidbase.utils.InterfaceUtils.*;
|
||||
|
||||
public class TargetChart extends View
|
||||
{
|
||||
private Paint paint;
|
||||
private int baseSize;
|
||||
private int primaryColor;
|
||||
private int mediumContrastTextColor;
|
||||
private int highContrastReverseTextColor;
|
||||
private int lowContrastTextColor;
|
||||
private RectF rect = new RectF();
|
||||
private RectF barRect = new RectF();
|
||||
private List<Double> values = Collections.emptyList();
|
||||
private List<String> labels = Collections.emptyList();
|
||||
private List<Double> targets = Collections.emptyList();
|
||||
private float maxLabelSize;
|
||||
private float tinyTextSize;
|
||||
|
||||
public TargetChart(Context context)
|
||||
{
|
||||
super(context);
|
||||
init();
|
||||
}
|
||||
|
||||
public TargetChart(Context context, AttributeSet attrs)
|
||||
{
|
||||
super(context, attrs);
|
||||
init();
|
||||
}
|
||||
|
||||
public void populateWithRandomData()
|
||||
{
|
||||
labels = new ArrayList<>();
|
||||
values = new ArrayList<>();
|
||||
targets = new ArrayList<>();
|
||||
for (int i = 0; i < 5; i++) {
|
||||
double percentage = new Random().nextDouble();
|
||||
targets.add(new Random().nextDouble() * 1000.0);
|
||||
values.add(targets.get(i) * percentage * 1.2);
|
||||
labels.add(String.format(Locale.US, "Label %d", i + 1));
|
||||
}
|
||||
}
|
||||
|
||||
public void setColor(int color)
|
||||
{
|
||||
this.primaryColor = color;
|
||||
postInvalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas)
|
||||
{
|
||||
super.onDraw(canvas);
|
||||
if (labels.size() == 0) return;
|
||||
|
||||
maxLabelSize = 0;
|
||||
for (String label : labels) {
|
||||
paint.setTextSize(tinyTextSize);
|
||||
float len = paint.measureText(label);
|
||||
maxLabelSize = Math.max(maxLabelSize, len);
|
||||
}
|
||||
|
||||
rect.set(0, 0, getWidth(), baseSize);
|
||||
for (int i = 0; i < labels.size(); i++) {
|
||||
drawRow(canvas, i, rect);
|
||||
rect.offset(0, baseSize);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthSpec, int heightSpec)
|
||||
{
|
||||
int width = getSize(widthSpec);
|
||||
int height = labels.size() * baseSize;
|
||||
heightSpec = makeMeasureSpec(height, EXACTLY);
|
||||
widthSpec = makeMeasureSpec(width, EXACTLY);
|
||||
setMeasuredDimension(widthSpec, heightSpec);
|
||||
}
|
||||
|
||||
private void drawRow(Canvas canvas, int row, RectF rect)
|
||||
{
|
||||
float padding = dpToPixels(getContext(), 4);
|
||||
float round = dpToPixels(getContext(), 2);
|
||||
float stop = maxLabelSize + padding * 2;
|
||||
|
||||
paint.setColor(mediumContrastTextColor);
|
||||
|
||||
// Draw label
|
||||
paint.setTextSize(tinyTextSize);
|
||||
paint.setTextAlign(Paint.Align.RIGHT);
|
||||
float yTextAdjust = (paint.descent() + paint.ascent()) / 2.0f;
|
||||
canvas.drawText(labels.get(row),
|
||||
rect.left + stop - padding,
|
||||
rect.centerY() - yTextAdjust,
|
||||
paint);
|
||||
|
||||
// Draw background box
|
||||
paint.setColor(lowContrastTextColor);
|
||||
barRect.set(rect.left + stop + padding,
|
||||
rect.top + baseSize * 0.05f,
|
||||
rect.right - padding,
|
||||
rect.bottom - baseSize * 0.05f);
|
||||
canvas.drawRoundRect(barRect, round, round, paint);
|
||||
|
||||
float percentage = (float) (values.get(row) / targets.get(row));
|
||||
percentage = Math.min(1.0f, percentage);
|
||||
|
||||
// Draw completed box
|
||||
float completedWidth = percentage * barRect.width();
|
||||
if (completedWidth > 0 && completedWidth < 2 * round) {
|
||||
completedWidth = 2 * round;
|
||||
}
|
||||
float remainingWidth = barRect.width() - completedWidth;
|
||||
|
||||
paint.setColor(primaryColor);
|
||||
barRect.set(barRect.left,
|
||||
barRect.top,
|
||||
barRect.left + completedWidth,
|
||||
barRect.bottom);
|
||||
canvas.drawRoundRect(barRect, round, round, paint);
|
||||
|
||||
// Draw values
|
||||
paint.setColor(Color.WHITE);
|
||||
paint.setTextSize(tinyTextSize);
|
||||
paint.setTextAlign(Paint.Align.CENTER);
|
||||
yTextAdjust = (paint.descent() + paint.ascent()) / 2.0f;
|
||||
|
||||
double remaining = targets.get(row) - values.get(row);
|
||||
String completedText = NumberButtonViewKt.toShortString(values.get(row));
|
||||
String remainingText = NumberButtonViewKt.toShortString(remaining);
|
||||
|
||||
if (completedWidth > paint.measureText(completedText) + 2 * padding) {
|
||||
paint.setColor(highContrastReverseTextColor);
|
||||
canvas.drawText(completedText,
|
||||
barRect.centerX(),
|
||||
barRect.centerY() - yTextAdjust,
|
||||
paint);
|
||||
}
|
||||
|
||||
if (remainingWidth > paint.measureText(remainingText) + 2 * padding) {
|
||||
paint.setColor(mediumContrastTextColor);
|
||||
barRect.set(rect.left + stop + padding + completedWidth,
|
||||
barRect.top,
|
||||
rect.right - padding,
|
||||
barRect.bottom);
|
||||
canvas.drawText(remainingText,
|
||||
barRect.centerX(),
|
||||
barRect.centerY() - yTextAdjust,
|
||||
paint);
|
||||
}
|
||||
}
|
||||
|
||||
private void init()
|
||||
{
|
||||
paint = new Paint();
|
||||
paint.setTextAlign(Paint.Align.CENTER);
|
||||
paint.setAntiAlias(true);
|
||||
|
||||
StyledResources res = new StyledResources(getContext());
|
||||
lowContrastTextColor = res.getColor(R.attr.lowContrastTextColor);
|
||||
mediumContrastTextColor = res.getColor(R.attr.mediumContrastTextColor);
|
||||
highContrastReverseTextColor = res.getColor(R.attr.highContrastReverseTextColor);
|
||||
tinyTextSize = getDimension(getContext(), R.dimen.tinyTextSize);
|
||||
baseSize = getResources().getDimensionPixelSize(R.dimen.baseSize);
|
||||
}
|
||||
|
||||
public void setValues(List<Double> values)
|
||||
{
|
||||
this.values = values;
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
public void setLabels(List<String> labels)
|
||||
{
|
||||
this.labels = labels;
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
public void setTargets(List<Double> targets)
|
||||
{
|
||||
this.targets = targets;
|
||||
requestLayout();
|
||||
}
|
||||
}
|
@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* This file is part of Loop Habit Tracker.
|
||||
*
|
||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.activities.habits.show.views;
|
||||
|
||||
import android.content.*;
|
||||
import android.content.res.*;
|
||||
import android.util.*;
|
||||
import android.widget.*;
|
||||
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.*;
|
||||
import org.isoron.uhabits.activities.common.views.*;
|
||||
import org.isoron.uhabits.core.models.*;
|
||||
import org.isoron.uhabits.core.preferences.*;
|
||||
import org.isoron.uhabits.core.tasks.*;
|
||||
import org.isoron.uhabits.core.utils.*;
|
||||
import org.isoron.uhabits.utils.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import butterknife.*;
|
||||
|
||||
public class TargetCard extends HabitCard
|
||||
{
|
||||
@BindView(R.id.title)
|
||||
TextView title;
|
||||
|
||||
@BindView(R.id.targetChart)
|
||||
TargetChart targetChart;
|
||||
|
||||
int firstWeekday = Calendar.SATURDAY;
|
||||
|
||||
public TargetCard(Context context)
|
||||
{
|
||||
super(context);
|
||||
init();
|
||||
}
|
||||
|
||||
public TargetCard(Context context, AttributeSet attrs)
|
||||
{
|
||||
super(context, attrs);
|
||||
init();
|
||||
}
|
||||
|
||||
private void init()
|
||||
{
|
||||
inflate(getContext(), R.layout.show_habit_target, this);
|
||||
ButterKnife.bind(this);
|
||||
setOrientation(VERTICAL);
|
||||
|
||||
Context app = getContext().getApplicationContext();
|
||||
if (app instanceof HabitsApplication) {
|
||||
HabitsApplication habitsApp = (HabitsApplication) app;
|
||||
Preferences prefs = habitsApp.getComponent().getPreferences();
|
||||
firstWeekday = prefs.getFirstWeekday();
|
||||
}
|
||||
|
||||
if (isInEditMode()) initEditMode();
|
||||
}
|
||||
|
||||
private void initEditMode()
|
||||
{
|
||||
int color = PaletteUtils.getAndroidTestColor(1);
|
||||
title.setTextColor(color);
|
||||
targetChart.setColor(color);
|
||||
targetChart.populateWithRandomData();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Task createRefreshTask()
|
||||
{
|
||||
return new RefreshTask();
|
||||
}
|
||||
|
||||
private class RefreshTask extends CancelableTask
|
||||
{
|
||||
double todayValue;
|
||||
double thisWeekValue;
|
||||
double thisMonthValue;
|
||||
double thisQuarterValue;
|
||||
double thisYearValue;
|
||||
|
||||
@Override
|
||||
public void doInBackground()
|
||||
{
|
||||
if (isCanceled()) return;
|
||||
CheckmarkList checkmarks = getHabit().getCheckmarks();
|
||||
todayValue = checkmarks.getTodayValue() / 1e3;
|
||||
thisWeekValue = checkmarks.getThisWeekValue(firstWeekday) / 1e3;
|
||||
thisMonthValue = checkmarks.getThisMonthValue() / 1e3;
|
||||
thisQuarterValue = checkmarks.getThisQuarterValue() / 1e3;
|
||||
thisYearValue = checkmarks.getThisYearValue() / 1e3;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPostExecute()
|
||||
{
|
||||
if (isCanceled()) return;
|
||||
|
||||
Calendar cal = DateUtils.getStartOfTodayCalendar();
|
||||
int daysInMonth = cal.getActualMaximum(Calendar.DAY_OF_MONTH);
|
||||
int daysInQuarter = 91;
|
||||
int daysInYear = cal.getActualMaximum(Calendar.DAY_OF_YEAR);
|
||||
|
||||
int den = getHabit().getFrequency().getDenominator();
|
||||
double dailyTarget = getHabit().getTargetValue() / den;
|
||||
Resources res = getResources();
|
||||
|
||||
ArrayList<Double> values = new ArrayList<>();
|
||||
if (den <= 1) values.add(todayValue);
|
||||
if (den <= 7) values.add(thisWeekValue);
|
||||
values.add(thisMonthValue);
|
||||
values.add(thisQuarterValue);
|
||||
values.add(thisYearValue);
|
||||
targetChart.setValues(values);
|
||||
|
||||
ArrayList<Double> targets = new ArrayList<>();
|
||||
if (den <= 1) targets.add(dailyTarget);
|
||||
if (den <= 7) targets.add(dailyTarget * 7);
|
||||
targets.add(dailyTarget * daysInMonth);
|
||||
targets.add(dailyTarget * daysInQuarter);
|
||||
targets.add(dailyTarget * daysInYear);
|
||||
targetChart.setTargets(targets);
|
||||
|
||||
ArrayList<String> labels = new ArrayList<>();
|
||||
if (den <= 1) labels.add(res.getString(R.string.today));
|
||||
if (den <= 7) labels.add(res.getString(R.string.week));
|
||||
labels.add(res.getString(R.string.month));
|
||||
labels.add(res.getString(R.string.quarter));
|
||||
labels.add(res.getString(R.string.year));
|
||||
targetChart.setLabels(labels);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPreExecute()
|
||||
{
|
||||
int color = PaletteUtils.getColor(getContext(), getHabit().getColor());
|
||||
title.setTextColor(color);
|
||||
targetChart.setColor(color);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||
~
|
||||
~ This file is part of Loop Habit Tracker.
|
||||
~
|
||||
~ Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by the
|
||||
~ Free Software Foundation, either version 3 of the License, or (at your
|
||||
~ option) any later version.
|
||||
~
|
||||
~ Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||
~ WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
~ more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License along
|
||||
~ with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
style="@style/CardHeader"
|
||||
android:text="@string/target"/>
|
||||
|
||||
<org.isoron.uhabits.activities.common.views.TargetChart
|
||||
android:id="@+id/targetChart"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="300dp"/>
|
||||
</merge>
|
Loading…
Reference in new issue