diff --git a/app/src/main/java/org/isoron/uhabits/fragments/ShowHabitFragment.java b/app/src/main/java/org/isoron/uhabits/fragments/ShowHabitFragment.java index 4be27ec21..1a4e38f6c 100644 --- a/app/src/main/java/org/isoron/uhabits/fragments/ShowHabitFragment.java +++ b/app/src/main/java/org/isoron/uhabits/fragments/ShowHabitFragment.java @@ -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; + + @Nullable private Habit habit; - private HabitStreakView streakView; - private HabitScoreView scoreView; - private HabitHistoryView historyView; - private HabitFrequencyView punchcardView; + @Nullable private List 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) + { + + } } diff --git a/app/src/main/java/org/isoron/uhabits/views/HabitScoreView.java b/app/src/main/java/org/isoron/uhabits/views/HabitScoreView.java index 355c6ba08..1e4c974ee 100644 --- a/app/src/main/java/org/isoron/uhabits/views/HabitScoreView.java +++ b/app/src/main/java/org/isoron/uhabits/views/HabitScoreView.java @@ -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); + drawFooter(canvas, rect, currentDate); + + 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 - canvas.drawText(day, rect.centerX(), rect.bottom + lineHeight * 1.2f, pText); + { + text = dayText; + } - previousMonth = month; - currentDate += 7 * DateHelper.millisecondsInOneDay; + 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; diff --git a/app/src/main/java/org/isoron/uhabits/views/RingView.java b/app/src/main/java/org/isoron/uhabits/views/RingView.java index d7c3ba775..2d82fd0f8 100644 --- a/app/src/main/java/org/isoron/uhabits/views/RingView.java +++ b/app/src/main/java/org/isoron/uhabits/views/RingView.java @@ -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); diff --git a/app/src/main/res/layout/show_habit.xml b/app/src/main/res/layout/show_habit.xml index c1e88dba1..4833fa5fc 100644 --- a/app/src/main/res/layout/show_habit.xml +++ b/app/src/main/res/layout/show_habit.xml @@ -49,8 +49,8 @@ android:id="@+id/scoreRing" style="@style/smallDataViewStyle" app:label="@string/habit_strength" - app:maxDiameter="60" - app:textSize="12"/> + app:maxDiameter="70" + app:textSize="@dimen/smallTextSize"/> @@ -60,7 +60,8 @@ style="@style/cardStyle" android:layout_width="match_parent" android:layout_height="wrap_content" - android:gravity="top|start"> + android:gravity="top|start" + android:visibility="gone"> - + + + + android:text="@string/habit_strength" + android:layout_alignParentTop="true" + android:layout_alignParentLeft="true" + android:layout_alignParentStart="true"/> + android:layout_height="220dp" + android:layout_below="@id/tvStrength"/> - + + android:textColor="@color/grey_400" + android:textSize="@dimen/smallTextSize"/> diff --git a/app/src/main/res/values-v21/styles.xml b/app/src/main/res/values-v21/styles.xml index 7e519b875..ae57bd247 100644 --- a/app/src/main/res/values-v21/styles.xml +++ b/app/src/main/res/values-v21/styles.xml @@ -28,6 +28,7 @@ true true true + @style/dialogFormSmallText + +