Allow user to change score view interval

Closes #10
This commit is contained in:
2016-03-20 08:06:12 -04:00
parent e4e8d77acc
commit 7da4ddf91b
9 changed files with 235 additions and 72 deletions

View File

@@ -20,31 +20,36 @@
package org.isoron.uhabits.fragments;
import android.app.Fragment;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.TextView;
import org.isoron.helpers.ColorHelper;
import org.isoron.uhabits.commands.Command;
import org.isoron.helpers.DialogHelper;
import org.isoron.uhabits.HabitBroadcastReceiver;
import org.isoron.uhabits.R;
import org.isoron.uhabits.ShowHabitActivity;
import org.isoron.uhabits.commands.Command;
import org.isoron.uhabits.dialogs.HistoryEditorDialog;
import org.isoron.uhabits.helpers.ReminderHelper;
import org.isoron.uhabits.models.Habit;
import org.isoron.uhabits.models.Score;
import org.isoron.uhabits.views.HabitDataView;
import org.isoron.uhabits.views.HabitHistoryView;
import org.isoron.uhabits.views.HabitFrequencyView;
import org.isoron.uhabits.views.HabitHistoryView;
import org.isoron.uhabits.views.HabitScoreView;
import org.isoron.uhabits.views.HabitStreakView;
import org.isoron.uhabits.views.RepetitionCountView;
@@ -54,17 +59,24 @@ import java.util.LinkedList;
import java.util.List;
public class ShowHabitFragment extends Fragment
implements DialogHelper.OnSavedListener, HistoryEditorDialog.Listener
implements DialogHelper.OnSavedListener, HistoryEditorDialog.Listener,
Spinner.OnItemSelectedListener
{
@Nullable
protected ShowHabitActivity activity;
private Habit habit;
private HabitStreakView streakView;
private HabitScoreView scoreView;
private HabitHistoryView historyView;
private HabitFrequencyView punchcardView;
@Nullable
private Habit habit;
@Nullable
private List<HabitDataView> dataViews;
@Nullable
private HabitScoreView scoreView;
@Nullable
private SharedPreferences prefs;
@Override
public void onStart()
{
@@ -82,10 +94,16 @@ public class ShowHabitFragment extends Fragment
dataViews = new LinkedList<>();
Button btEditHistory = (Button) view.findViewById(R.id.btEditHistory);
streakView = (HabitStreakView) view.findViewById(R.id.streakView);
Spinner sStrengthInterval = (Spinner) view.findViewById(R.id.sStrengthInterval);
scoreView = (HabitScoreView) view.findViewById(R.id.scoreView);
historyView = (HabitHistoryView) view.findViewById(R.id.historyView);
punchcardView = (HabitFrequencyView) view.findViewById(R.id.punchcardView);
prefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
int defaultScoreInterval = prefs.getInt("pref_score_view_interval", 1);
if(defaultScoreInterval > 5 || defaultScoreInterval < 0) defaultScoreInterval = 1;
setScoreBucketSize(defaultScoreInterval);
sStrengthInterval.setSelection(defaultScoreInterval);
sStrengthInterval.setOnItemSelectedListener(this);
dataViews.add((HabitStreakView) view.findViewById(R.id.streakView));
dataViews.add((HabitScoreView) view.findViewById(R.id.scoreView));
@@ -131,6 +149,8 @@ public class ShowHabitFragment extends Fragment
private void updateScoreRing(View view)
{
if(habit == null) return;
RingView scoreRing = (RingView) view.findViewById(R.id.scoreRing);
scoreRing.setColor(habit.color);
scoreRing.setPercentage((float) habit.scores.getTodayValue() / Score.MAX_VALUE);
@@ -138,6 +158,8 @@ public class ShowHabitFragment extends Fragment
private void updateHeaders(View view)
{
if(habit == null | activity == null) return;
if (android.os.Build.VERSION.SDK_INT >= 21)
{
int darkerHabitColor = ColorHelper.mixColors(habit.color, Color.BLACK, 0.75f);
@@ -154,6 +176,8 @@ public class ShowHabitFragment extends Fragment
private void updateColor(View view, int viewId)
{
if(habit == null) return;
TextView textView = (TextView) view.findViewById(viewId);
textView.setTextColor(habit.color);
}
@@ -167,6 +191,8 @@ public class ShowHabitFragment extends Fragment
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
if(habit == null) return false;
switch (item.getItemId())
{
case R.id.action_edit_habit:
@@ -184,6 +210,7 @@ public class ShowHabitFragment extends Fragment
@Override
public void onSaved(Command command, Object savedObject)
{
if(activity == null) return;
Habit h = (Habit) savedObject;
if (h == null) activity.executeCommand(command, null);
@@ -202,7 +229,38 @@ public class ShowHabitFragment extends Fragment
public void refreshData()
{
if(dataViews == null) return;
updateScoreRing(getView());
for(HabitDataView view : dataViews)
view.refreshData();
}
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id)
{
if(parent.getId() == R.id.sStrengthInterval)
setScoreBucketSize(position);
}
private void setScoreBucketSize(int position)
{
int sizes[] = { 1, 7, 31, 92, 365 };
int size = sizes[position];
if(scoreView != null)
{
scoreView.setBucketSize(size);
scoreView.refreshData();
}
if(prefs != null)
prefs.edit().putInt("pref_score_view_interval", position).apply();
}
@Override
public void onNothingSelected(AdapterView<?> parent)
{
}
}

View File

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

View File

@@ -25,6 +25,7 @@ import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.os.Build;
import android.text.Layout;
import android.text.StaticLayout;
import android.text.TextPaint;
@@ -33,6 +34,7 @@ import android.view.View;
import org.isoron.helpers.ColorHelper;
import org.isoron.helpers.DialogHelper;
import org.isoron.uhabits.R;
public class RingView extends View
{
@@ -49,6 +51,7 @@ public class RingView extends View
private float diameter;
private float maxDiameter;
private float textSize;
private int fadedTextColor;
public RingView(Context context, AttributeSet attrs)
@@ -57,10 +60,9 @@ public class RingView extends View
this.label = DialogHelper.getAttribute(context, attrs, "label");
this.maxDiameter = DialogHelper.getFloatAttribute(context, attrs, "maxDiameter");
this.textSize = DialogHelper.getFloatAttribute(context, attrs, "textSize");
this.maxDiameter = DialogHelper.dpToPixels(context, maxDiameter);
this.textSize = DialogHelper.spToPixels(context, textSize);
this.textSize = getResources().getDimension(R.dimen.smallTextSize);
this.color = ColorHelper.palette[7];
this.percentage = 0.75f;
init();
@@ -86,6 +88,8 @@ public class RingView extends View
pRing.setColor(color);
pRing.setTextAlign(Paint.Align.CENTER);
fadedTextColor = getResources().getColor(R.color.fadedTextColor);
rect = new RectF();
}
@@ -122,15 +126,15 @@ public class RingView extends View
rect.offset((width - diameter) / 2, 0);
canvas.drawArc(rect, -90, 360 * percentage, true, pRing);
pRing.setColor(Color.rgb(230, 230, 230));
pRing.setColor(Color.argb(255, 230, 230, 230));
canvas.drawArc(rect, 360 * percentage - 90 + 2, 360 * (1 - percentage) - 4, true, pRing);
pRing.setColor(Color.WHITE);
rect.inset(thickness, thickness);
canvas.drawArc(rect, -90, 360, true, pRing);
pRing.setColor(Color.GRAY);
pRing.setTextSize(diameter * 0.2f);
pRing.setColor(fadedTextColor);
pRing.setTextSize(textSize);
float lineHeight = pRing.getFontSpacing();
canvas.drawText(String.format("%.0f%%", percentage * 100), rect.centerX(),
rect.centerY() + lineHeight / 3, pRing);