Refactor ShowHabit fragment; break widgets

pull/145/head
Alinson S. Xavier 9 years ago
parent a11ad6e909
commit efd0d1e051

@ -91,3 +91,7 @@ dependencies {
exclude group: 'com.android.support' exclude group: 'com.android.support'
} }
} }
retrolambda {
defaultMethods true
}

@ -24,7 +24,7 @@ import android.os.*;
import android.view.*; import android.view.*;
import org.isoron.uhabits.tasks.*; import org.isoron.uhabits.tasks.*;
import org.isoron.uhabits.ui.habits.show.views.*; import org.isoron.uhabits.ui.habits.show.views.charts.*;
import org.isoron.uhabits.utils.*; import org.isoron.uhabits.utils.*;
import java.io.*; import java.io.*;
@ -209,7 +209,7 @@ public class BaseViewTest extends BaseAndroidTest
e.recycle(); e.recycle();
} }
protected void refreshData(final HabitDataView view) protected void refreshData(final HabitChart view)
{ {
new BaseTask() new BaseTask()
{ {

@ -135,7 +135,7 @@ public class MainTest
onView(withId(R.id.scoreView)).perform(scrollTo(), swipeRight()); onView(withId(R.id.scoreView)).perform(scrollTo(), swipeRight());
onView(withId(R.id.punchcardView)).perform(scrollTo(), swipeRight()); onView(withId(R.id.frequencyChart)).perform(scrollTo(), swipeRight());
} }
/** /**
@ -234,7 +234,7 @@ public class MainTest
clickAtRandomLocations(20)); clickAtRandomLocations(20));
pressBack(); pressBack();
onView(withId(R.id.historyView)).perform(scrollTo(), swipeRight(), onView(withId(R.id.historyChart)).perform(scrollTo(), swipeRight(),
swipeLeft()); swipeLeft());
} }

@ -31,7 +31,7 @@ public class ShowHabitActivityActions
{ {
public static void openHistoryEditor() public static void openHistoryEditor()
{ {
onView(ViewMatchers.withId(R.id.btEditHistory)) onView(ViewMatchers.withId(R.id.edit))
.perform(scrollTo(), click()); .perform(scrollTo(), click());
} }
} }

@ -22,8 +22,10 @@ package org.isoron.uhabits.ui.habits.show.views;
import android.support.test.runner.AndroidJUnit4; import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest; import android.test.suitebuilder.annotation.SmallTest;
import org.apache.commons.lang3.*;
import org.isoron.uhabits.*; import org.isoron.uhabits.*;
import org.isoron.uhabits.models.Habit; import org.isoron.uhabits.models.Habit;
import org.isoron.uhabits.ui.habits.show.views.charts.*;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@ -43,9 +45,10 @@ public class FrequencyChartTest extends BaseViewTest
Habit habit = fixtures.createLongHabit(); Habit habit = fixtures.createLongHabit();
view = new FrequencyChart(targetContext); view = new FrequencyChart(targetContext);
view.setHabit(habit); throw new NotImplementedException("");
refreshData(view); // view.setHabit(habit);
measureView(dpToPixels(300), dpToPixels(100), view); // refreshData(view);
// measureView(dpToPixels(300), dpToPixels(100), view);
} }
@Test @Test

@ -22,8 +22,10 @@ package org.isoron.uhabits.ui.habits.show.views;
import android.support.test.runner.AndroidJUnit4; import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest; import android.test.suitebuilder.annotation.SmallTest;
import org.apache.commons.lang3.*;
import org.isoron.uhabits.*; import org.isoron.uhabits.*;
import org.isoron.uhabits.models.Habit; import org.isoron.uhabits.models.Habit;
import org.isoron.uhabits.ui.habits.show.views.charts.*;
import org.isoron.uhabits.utils.DateUtils; import org.isoron.uhabits.utils.DateUtils;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@ -36,11 +38,11 @@ import static org.hamcrest.Matchers.equalTo;
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
@SmallTest @SmallTest
public class HabitHistoryViewTest extends BaseViewTest public class HabitHistoryChartTest extends BaseViewTest
{ {
private Habit habit; private Habit habit;
private HistoryView view; private HistoryChart chart;
@Before @Before
public void setUp() public void setUp()
@ -50,10 +52,11 @@ public class HabitHistoryViewTest extends BaseViewTest
fixtures.purgeHabits(habitList); fixtures.purgeHabits(habitList);
habit = fixtures.createLongHabit(); habit = fixtures.createLongHabit();
view = new HistoryView(targetContext); chart = new HistoryChart(targetContext);
view.setHabit(habit); throw new NotImplementedException("");
measureView(dpToPixels(400), dpToPixels(200), view); // chart.setHabit(habit);
refreshData(view); // measureView(dpToPixels(400), dpToPixels(200), chart);
// refreshData(chart);
} }
@Test @Test
@ -61,10 +64,10 @@ public class HabitHistoryViewTest extends BaseViewTest
{ {
int expectedCheckmarkValues[] = habit.getCheckmarks().getAllValues(); int expectedCheckmarkValues[] = habit.getCheckmarks().getAllValues();
view.setIsEditable(true); chart.setIsEditable(true);
tap(view, 118, 13); // header tap(chart, 118, 13); // header
tap(view, 336, 60); // tomorrow's square tap(chart, 336, 60); // tomorrow's square
tap(view, 370, 60); // right axis tap(chart, 370, 60); // right axis
waitForAsyncTasks(); waitForAsyncTasks();
int actualCheckmarkValues[] = habit.getCheckmarks().getAllValues(); int actualCheckmarkValues[] = habit.getCheckmarks().getAllValues();
@ -74,8 +77,8 @@ public class HabitHistoryViewTest extends BaseViewTest
@Test @Test
public void tapDate_withEditableView() throws Throwable public void tapDate_withEditableView() throws Throwable
{ {
view.setIsEditable(true); chart.setIsEditable(true);
tap(view, 340, 40); // today's square tap(chart, 340, 40); // today's square
waitForAsyncTasks(); waitForAsyncTasks();
long today = DateUtils.getStartOfToday(); long today = DateUtils.getStartOfToday();
@ -85,8 +88,8 @@ public class HabitHistoryViewTest extends BaseViewTest
@Test @Test
public void tapDate_withReadOnlyView() throws Throwable public void tapDate_withReadOnlyView() throws Throwable
{ {
view.setIsEditable(false); chart.setIsEditable(false);
tap(view, 340, 40); // today's square tap(chart, 340, 40); // today's square
waitForAsyncTasks(); waitForAsyncTasks();
long today = DateUtils.getStartOfToday(); long today = DateUtils.getStartOfToday();
@ -96,30 +99,29 @@ public class HabitHistoryViewTest extends BaseViewTest
@Test @Test
public void testRender() throws Throwable public void testRender() throws Throwable
{ {
assertRenders(view, "HabitHistoryView/render.png"); assertRenders(chart, "HabitHistoryView/render.png");
} }
@Test @Test
public void testRender_withDataOffset() throws Throwable public void testRender_withDataOffset() throws Throwable
{ {
view.onScroll(null, null, -dpToPixels(150), 0); chart.onScroll(null, null, -dpToPixels(150), 0);
view.invalidate(); chart.invalidate();
assertRenders(view, "HabitHistoryView/renderDataOffset.png"); assertRenders(chart, "HabitHistoryView/renderDataOffset.png");
} }
@Test @Test
public void testRender_withDifferentSize() throws Throwable public void testRender_withDifferentSize() throws Throwable
{ {
measureView(dpToPixels(200), dpToPixels(200), view); measureView(dpToPixels(200), dpToPixels(200), chart);
assertRenders(view, "HabitHistoryView/renderDifferentSize.png"); assertRenders(chart, "HabitHistoryView/renderDifferentSize.png");
} }
@Test @Test
public void testRender_withTransparentBackground() throws Throwable public void testRender_withTransparentBackground() throws Throwable
{ {
view.setIsBackgroundTransparent(true); chart.setIsBackgroundTransparent(true);
assertRenders(view, "HabitHistoryView/renderTransparent.png"); assertRenders(chart, "HabitHistoryView/renderTransparent.png");
} }
} }

@ -25,6 +25,7 @@ import android.util.Log;
import org.isoron.uhabits.*; import org.isoron.uhabits.*;
import org.isoron.uhabits.models.Habit; import org.isoron.uhabits.models.Habit;
import org.isoron.uhabits.ui.habits.show.views.charts.*;
import org.isoron.uhabits.utils.*; import org.isoron.uhabits.utils.*;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;

@ -22,8 +22,10 @@ package org.isoron.uhabits.ui.habits.show.views;
import android.support.test.runner.AndroidJUnit4; import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest; import android.test.suitebuilder.annotation.SmallTest;
import org.apache.commons.lang3.*;
import org.isoron.uhabits.*; import org.isoron.uhabits.*;
import org.isoron.uhabits.models.Habit; import org.isoron.uhabits.models.Habit;
import org.isoron.uhabits.ui.habits.show.views.charts.*;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@ -45,9 +47,10 @@ public class StreakChartTest extends BaseViewTest
view = new StreakChart(targetContext); view = new StreakChart(targetContext);
measureView(dpToPixels(300), dpToPixels(100), view); measureView(dpToPixels(300), dpToPixels(100), view);
throw new NotImplementedException("");
view.setHabit(habit); // view.setHabit(habit);
refreshData(view); // refreshData(view);
} }
@Test @Test
@ -60,7 +63,7 @@ public class StreakChartTest extends BaseViewTest
public void testRender_withSmallSize() throws Throwable public void testRender_withSmallSize() throws Throwable
{ {
measureView(dpToPixels(100), dpToPixels(100), view); measureView(dpToPixels(100), dpToPixels(100), view);
refreshData(view); // refreshData(view);
assertRenders(view, "HabitStreakView/renderSmallSize.png"); assertRenders(view, "HabitStreakView/renderSmallSize.png");
} }

@ -24,6 +24,7 @@ import android.support.annotation.*;
import org.apache.commons.lang3.builder.*; import org.apache.commons.lang3.builder.*;
import org.isoron.uhabits.*; import org.isoron.uhabits.*;
import org.isoron.uhabits.models.memory.*;
import java.util.*; import java.util.*;
@ -82,14 +83,8 @@ public class Habit
*/ */
public Habit(Habit model) public Habit(Habit model)
{ {
HabitsApplication.getComponent().inject(this);
copyFrom(model); copyFrom(model);
buildLists();
checkmarks = factory.buildCheckmarkList(this);
streaks = factory.buildStreakList(this);
scores = factory.buildScoreList(this);
repetitions = factory.buildRepetitionList(this);
} }
/** /**
@ -100,12 +95,19 @@ public class Habit
*/ */
public Habit() public Habit()
{ {
HabitsApplication.getComponent().inject(this);
this.color = 5; this.color = 5;
this.archived = false; this.archived = false;
this.frequency = new Frequency(3, 7); this.frequency = new Frequency(3, 7);
buildLists();
}
private void buildLists()
{
BaseComponent component = HabitsApplication.getComponent();
if(component == null) factory = new MemoryModelFactory();
else component.inject(this);
checkmarks = factory.buildCheckmarkList(this); checkmarks = factory.buildCheckmarkList(this);
streaks = factory.buildStreakList(this); streaks = factory.buildStreakList(this);
scores = factory.buildScoreList(this); scores = factory.buildScoreList(this);

@ -27,12 +27,13 @@ import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatDialogFragment; import android.support.v7.app.AppCompatDialogFragment;
import android.util.DisplayMetrics; import android.util.DisplayMetrics;
import org.apache.commons.lang3.*;
import org.isoron.uhabits.HabitsApplication; import org.isoron.uhabits.HabitsApplication;
import org.isoron.uhabits.R; import org.isoron.uhabits.R;
import org.isoron.uhabits.models.Habit; import org.isoron.uhabits.models.Habit;
import org.isoron.uhabits.models.HabitList; import org.isoron.uhabits.models.HabitList;
import org.isoron.uhabits.tasks.BaseTask; import org.isoron.uhabits.tasks.BaseTask;
import org.isoron.uhabits.ui.habits.show.views.HistoryView; import org.isoron.uhabits.ui.habits.show.views.charts.HistoryChart;
import javax.inject.Inject; import javax.inject.Inject;
@ -43,7 +44,7 @@ public class HistoryEditorDialog extends AppCompatDialogFragment
private Listener listener; private Listener listener;
HistoryView historyView; HistoryChart historyChart;
@Inject @Inject
HabitList habitList; HabitList habitList;
@ -53,7 +54,7 @@ public class HistoryEditorDialog extends AppCompatDialogFragment
{ {
Context context = getActivity(); Context context = getActivity();
HabitsApplication.getComponent().inject(this); HabitsApplication.getComponent().inject(this);
historyView = new HistoryView(context, null); historyChart = new HistoryChart(context, null);
if (savedInstanceState != null) if (savedInstanceState != null)
{ {
@ -63,14 +64,16 @@ public class HistoryEditorDialog extends AppCompatDialogFragment
int padding = int padding =
(int) getResources().getDimension(R.dimen.history_editor_padding); (int) getResources().getDimension(R.dimen.history_editor_padding);
historyView.setPadding(padding, 0, padding, 0);
historyView.setHabit(habit); if(true) throw new NotImplementedException("");
historyView.setIsEditable(true); historyChart.setPadding(padding, 0, padding, 0);
// historyChart.setHabit(habit);
historyChart.setIsEditable(true);
AlertDialog.Builder builder = new AlertDialog.Builder(context); AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder builder
.setTitle(R.string.history) .setTitle(R.string.history)
.setView(historyView) .setView(historyChart)
.setPositiveButton(android.R.string.ok, this); .setPositiveButton(android.R.string.ok, this);
refreshData(); refreshData();
@ -85,7 +88,7 @@ public class HistoryEditorDialog extends AppCompatDialogFragment
@Override @Override
protected void doInBackground() protected void doInBackground()
{ {
historyView.refreshData(); // historyChart.refreshData();
} }
}.execute(); }.execute();
} }
@ -112,8 +115,8 @@ public class HistoryEditorDialog extends AppCompatDialogFragment
public void setHabit(Habit habit) public void setHabit(Habit habit)
{ {
this.habit = habit; // this.habit = habit;
if (historyView != null) historyView.setHabit(habit); // if (historyChart != null) historyChart.setHabit(habit);
} }
@Override @Override

@ -26,35 +26,21 @@ import android.view.*;
import org.isoron.uhabits.*; import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*; import org.isoron.uhabits.models.*;
import org.isoron.uhabits.ui.habits.edit.*; import org.isoron.uhabits.ui.habits.edit.*;
import org.isoron.uhabits.ui.habits.show.views.*; import org.isoron.uhabits.ui.habits.show.views.cards.*;
import java.util.*;
import butterknife.*; import butterknife.*;
public class ShowHabitFragment extends Fragment public class ShowHabitFragment extends Fragment
implements ModelObservable.Listener
{ {
Habit habit; Habit habit;
int activeColor;
int inactiveColor;
private ShowHabitHelper helper;
protected ShowHabitActivity activity; protected ShowHabitActivity activity;
private List<HabitDataView> dataViews; @BindView(R.id.frequencyCard)
FrequencyCard frequencyCard;
@BindView(R.id.historyView)
HistoryView historyView;
@BindView(R.id.punchcardView)
FrequencyChart frequencyChart;
@BindView(R.id.streakChart) @BindView(R.id.streakCard)
StreakChart streakChart; StreakCard streakCard;
@BindView(R.id.subtitleCard) @BindView(R.id.subtitleCard)
SubtitleCard subtitleCard; SubtitleCard subtitleCard;
@ -65,6 +51,15 @@ public class ShowHabitFragment extends Fragment
@BindView(R.id.strengthCard) @BindView(R.id.strengthCard)
ScoreCard scoreCard; ScoreCard scoreCard;
@BindView(R.id.historyCard)
HistoryCard historyCard;
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater)
{
// inflater.inflate(R.menu.show_habit_fragment, menu);
}
@Override @Override
public View onCreateView(LayoutInflater inflater, public View onCreateView(LayoutInflater inflater,
ViewGroup container, ViewGroup container,
@ -74,45 +69,17 @@ public class ShowHabitFragment extends Fragment
ButterKnife.bind(this, view); ButterKnife.bind(this, view);
activity = (ShowHabitActivity) getActivity(); activity = (ShowHabitActivity) getActivity();
helper = new ShowHabitHelper(this);
habit = activity.getHabit(); habit = activity.getHabit();
helper.updateColors();
createDataViews();
helper.updateCardHeaders(view);
setHasOptionsMenu(true);
return view;
}
@OnClick(R.id.btEditHistory)
public void onClickEditHistory()
{
HistoryEditorDialog frag = new HistoryEditorDialog();
frag.setHabit(habit);
frag.show(getFragmentManager(), "historyEditor");
}
private void createDataViews()
{
subtitleCard.setHabit(habit); subtitleCard.setHabit(habit);
overviewCard.setHabit(habit); overviewCard.setHabit(habit);
scoreCard.setHabit(habit); scoreCard.setHabit(habit);
historyCard.setHabit(habit);
streakCard.setHabit(habit);
frequencyCard.setHabit(habit);
dataViews = new LinkedList<>(); setHasOptionsMenu(true);
dataViews.add(historyView); return view;
dataViews.add(frequencyChart);
dataViews.add(streakChart);
for (HabitDataView dataView : dataViews)
dataView.setHabit(habit);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater)
{
// inflater.inflate(R.menu.show_habit_fragment, menu);
} }
@Override @Override
@ -124,6 +91,7 @@ public class ShowHabitFragment extends Fragment
return false; return false;
} }
private boolean showEditHabitDialog() private boolean showEditHabitDialog()
{ {
if (habit == null) return false; if (habit == null) return false;
@ -133,28 +101,4 @@ public class ShowHabitFragment extends Fragment
frag.show(getFragmentManager(), "editHabit"); frag.show(getFragmentManager(), "editHabit");
return true; return true;
} }
@Override
public void onModelChange()
{
activity.runOnUiThread(() -> {
helper.updateColors();
helper.updateCardHeaders(getView());
if (activity != null) activity.setupHabitActionBar();
});
}
@Override
public void onStart()
{
super.onStart();
habit.getObservable().addListener(this);
}
@Override
public void onPause()
{
habit.getObservable().removeListener(this);
super.onPause();
}
} }

@ -1,63 +0,0 @@
/*
* 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.ui.habits.show;
import android.view.*;
import android.widget.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.utils.*;
public class ShowHabitHelper
{
private ShowHabitFragment fragment;
public ShowHabitHelper(ShowHabitFragment fragment)
{
this.fragment = fragment;
}
void updateCardHeaders(View view)
{
updateColor(view, R.id.tvHistory);
updateColor(view, R.id.tvStreaks);
updateColor(view, R.id.tvWeekdayFreq);
updateColor(view, R.id.scoreLabel);
}
void updateColor(View view, int viewId)
{
if (fragment.habit == null || fragment.activity == null) return;
TextView textView = (TextView) view.findViewById(viewId);
int androidColor =
ColorUtils.getColor(fragment.activity, fragment.habit.getColor());
textView.setTextColor(androidColor);
}
void updateColors()
{
fragment.activeColor = ColorUtils.getColor(fragment.getContext(),
fragment.habit.getColor());
fragment.inactiveColor =
InterfaceUtils.getStyledColor(fragment.getContext(),
R.attr.mediumContrastTextColor);
}
}

@ -0,0 +1,92 @@
/*
* 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.ui.habits.show.views.cards;
import android.content.*;
import android.util.*;
import android.widget.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.tasks.*;
import org.isoron.uhabits.ui.habits.show.views.charts.*;
import org.isoron.uhabits.utils.*;
import java.util.*;
import butterknife.*;
public class FrequencyCard extends HabitCard
{
@BindView(R.id.title)
TextView title;
@BindView(R.id.frequencyChart)
FrequencyChart chart;
public FrequencyCard(Context context)
{
super(context);
init();
}
public FrequencyCard(Context context, AttributeSet attrs)
{
super(context, attrs);
init();
}
private void init()
{
inflate(getContext(), R.layout.show_habit_frequency, this);
ButterKnife.bind(this);
if(isInEditMode()) initEditMode();
}
private void initEditMode()
{
int color = ColorUtils.getAndroidTestColor(1);
title.setTextColor(color);
chart.setColor(color);
chart.populateWithRandomData();
}
@Override
protected void refreshData()
{
Habit habit = getHabit();
int color = ColorUtils.getColor(getContext(), habit.getColor());
title.setTextColor(color);
chart.setColor(color);
new BaseTask()
{
@Override
protected void doInBackground()
{
RepetitionList reps = habit.getRepetitions();
HashMap<Long, Integer[]> frequency = reps.getWeekdayFrequency();
chart.setFrequency(frequency);
}
}.execute();
}
}

@ -0,0 +1,102 @@
/*
* 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.ui.habits.show.views.cards;
import android.content.*;
import android.support.annotation.*;
import android.util.*;
import android.widget.*;
import org.isoron.uhabits.models.*;
public abstract class HabitCard extends LinearLayout
implements ModelObservable.Listener
{
@NonNull
private Habit habit;
public HabitCard(Context context)
{
super(context);
init();
}
public HabitCard(Context context, AttributeSet attrs)
{
super(context, attrs);
init();
}
@NonNull
public Habit getHabit()
{
return habit;
}
public void setHabit(@NonNull Habit habit)
{
detachFrom(this.habit);
attachTo(habit);
this.habit = habit;
}
@Override
public void onModelChange()
{
refreshData();
}
@Override
protected void onAttachedToWindow()
{
if(isInEditMode()) return;
super.onAttachedToWindow();
refreshData();
attachTo(habit);
}
@Override
protected void onDetachedFromWindow()
{
detachFrom(habit);
super.onDetachedFromWindow();
}
protected abstract void refreshData();
private void attachTo(Habit habit)
{
habit.getObservable().addListener(this);
habit.getRepetitions().getObservable().addListener(this);
}
private void detachFrom(Habit habit)
{
habit.getRepetitions().getObservable().removeListener(this);
habit.getObservable().removeListener(this);
}
private void init()
{
if(!isInEditMode()) habit = new Habit();
}
}

@ -0,0 +1,99 @@
/*
* 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.ui.habits.show.views.cards;
import android.content.*;
import android.util.*;
import android.widget.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.tasks.*;
import org.isoron.uhabits.ui.habits.show.views.charts.*;
import org.isoron.uhabits.utils.*;
import butterknife.*;
public class HistoryCard extends HabitCard
{
@BindView(R.id.historyChart)
HistoryChart chart;
@BindView(R.id.title)
TextView title;
public HistoryCard(Context context)
{
super(context);
init();
}
public HistoryCard(Context context, AttributeSet attrs)
{
super(context, attrs);
init();
}
@OnClick(R.id.edit)
public void onClickEditButton()
{
Log.d("HistoryCard", "onClickEditButton");
// HistoryEditorDialog frag = new HistoryEditorDialog();
// frag.setHabit(habit);
// frag.show(getContext().getFragmentManager(), "historyEditor");
}
private void init()
{
inflate(getContext(), R.layout.show_habit_history, this);
ButterKnife.bind(this);
if (isInEditMode()) initEditMode();
}
private void initEditMode()
{
int color = ColorUtils.getAndroidTestColor(1);
title.setTextColor(color);
chart.setColor(color);
chart.populateWithRandomData();
}
@Override
protected void refreshData()
{
Habit habit = getHabit();
int color = ColorUtils.getColor(getContext(), habit.getColor());
title.setTextColor(color);
chart.setColor(color);
new BaseTask()
{
@Override
protected void doInBackground()
{
int checkmarks[] = habit.getCheckmarks().getAllValues();
chart.setCheckmarks(checkmarks);
}
}.execute();
}
}

@ -17,7 +17,7 @@
* with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.isoron.uhabits.ui.habits.show.views; package org.isoron.uhabits.ui.habits.show.views.cards;
import android.content.*; import android.content.*;
import android.support.annotation.*; import android.support.annotation.*;
@ -27,16 +27,13 @@ import android.widget.*;
import org.isoron.uhabits.*; import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*; import org.isoron.uhabits.models.*;
import org.isoron.uhabits.tasks.*; import org.isoron.uhabits.tasks.*;
import org.isoron.uhabits.ui.habits.show.views.*;
import org.isoron.uhabits.utils.*; import org.isoron.uhabits.utils.*;
import butterknife.*; import butterknife.*;
public class OverviewCard extends LinearLayout public class OverviewCard extends HabitCard
implements ModelObservable.Listener
{ {
@Nullable
private Habit habit;
@NonNull @NonNull
private Cache cache; private Cache cache;
@ -70,61 +67,19 @@ public class OverviewCard extends LinearLayout
} }
@Override @Override
public void onModelChange() protected void refreshData()
{ {
refreshCache(); Habit habit = getHabit();
}
public void setHabit(@Nullable Habit habit)
{
this.habit = habit;
color = ColorUtils.getColor(getContext(), habit.getColor()); color = ColorUtils.getColor(getContext(), habit.getColor());
} refreshColors();
@Override
protected void onAttachedToWindow()
{
super.onAttachedToWindow();
refreshCache();
if(habit != null) habit.getObservable().addListener(this);
}
@Override
protected void onDetachedFromWindow()
{
if(habit != null) habit.getObservable().removeListener(this);
super.onDetachedFromWindow();
}
private void init()
{
inflate(getContext(), R.layout.show_habit_overview, this);
ButterKnife.bind(this);
cache = new Cache();
if(isInEditMode()) initEditMode();
}
private void initEditMode()
{
color = ColorUtils.getAndroidTestColor(1);
cache.todayScore = Score.MAX_VALUE * 0.6f;
cache.lastMonthScore = Score.MAX_VALUE * 0.42f;
cache.lastYearScore = Score.MAX_VALUE * 0.75f;
updateScore();
}
private void refreshCache()
{
new BaseTask() new BaseTask()
{ {
@Override @Override
protected void doInBackground() protected void doInBackground()
{ {
if(habit == null) return;
ScoreList scores = habit.getScores(); ScoreList scores = habit.getScores();
long today = DateUtils.getStartOfToday(); long today = DateUtils.getStartOfToday();
long lastMonth = today - 30 * DateUtils.millisecondsInOneDay; long lastMonth = today - 30 * DateUtils.millisecondsInOneDay;
long lastYear = today - 365 * DateUtils.millisecondsInOneDay; long lastYear = today - 365 * DateUtils.millisecondsInOneDay;
@ -137,25 +92,54 @@ public class OverviewCard extends LinearLayout
@Override @Override
protected void onPostExecute(Void aVoid) protected void onPostExecute(Void aVoid)
{ {
updateScore(); refreshScore();
super.onPostExecute(aVoid); super.onPostExecute(aVoid);
} }
}.execute(); }.execute();
} }
void updateScore() private String formatPercentageDiff(float percentageDiff)
{ {
float todayPercentage = cache.todayScore / Score.MAX_VALUE; return String.format("%s%.0f%%", (percentageDiff >= 0 ? "+" : "\u2212"),
float monthDiff = todayPercentage - (cache.lastMonthScore / Score.MAX_VALUE); Math.abs(percentageDiff) * 100);
float yearDiff = todayPercentage - (cache.lastYearScore / Score.MAX_VALUE); }
scoreRing.setColor(color); private void init()
scoreRing.setPercentage(todayPercentage); {
inflate(getContext(), R.layout.show_habit_overview, this);
ButterKnife.bind(this);
cache = new Cache();
scoreLabel.setTextColor(color); if (isInEditMode()) initEditMode();
scoreLabel.setText(String.format("%.0f%%", todayPercentage * 100)); }
private void initEditMode()
{
color = ColorUtils.getAndroidTestColor(1);
cache.todayScore = Score.MAX_VALUE * 0.6f;
cache.lastMonthScore = Score.MAX_VALUE * 0.42f;
cache.lastYearScore = Score.MAX_VALUE * 0.75f;
refreshColors();
refreshScore();
}
private void refreshColors()
{
scoreRing.setColor(color);
scoreLabel.setTextColor(color);
title.setTextColor(color); title.setTextColor(color);
}
private void refreshScore()
{
float todayPercentage = cache.todayScore / Score.MAX_VALUE;
float monthDiff =
todayPercentage - (cache.lastMonthScore / Score.MAX_VALUE);
float yearDiff =
todayPercentage - (cache.lastYearScore / Score.MAX_VALUE);
scoreRing.setPercentage(todayPercentage);
scoreLabel.setText(String.format("%.0f%%", todayPercentage * 100));
monthDiffLabel.setText(formatPercentageDiff(monthDiff)); monthDiffLabel.setText(formatPercentageDiff(monthDiff));
yearDiffLabel.setText(formatPercentageDiff(yearDiff)); yearDiffLabel.setText(formatPercentageDiff(yearDiff));
@ -169,12 +153,6 @@ public class OverviewCard extends LinearLayout
postInvalidate(); postInvalidate();
} }
private String formatPercentageDiff(float percentageDiff)
{
return String.format("%s%.0f%%", (percentageDiff >= 0 ? "+" : "\u2212"),
Math.abs(percentageDiff) * 100);
}
private class Cache private class Cache
{ {
public float todayScore; public float todayScore;

@ -17,7 +17,7 @@
* with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.isoron.uhabits.ui.habits.show.views; package org.isoron.uhabits.ui.habits.show.views.cards;
import android.content.*; import android.content.*;
import android.support.annotation.*; import android.support.annotation.*;
@ -27,14 +27,14 @@ import android.widget.*;
import org.isoron.uhabits.*; import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*; import org.isoron.uhabits.models.*;
import org.isoron.uhabits.tasks.*; import org.isoron.uhabits.tasks.*;
import org.isoron.uhabits.ui.habits.show.views.charts.*;
import org.isoron.uhabits.utils.*; import org.isoron.uhabits.utils.*;
import java.util.*; import java.util.*;
import butterknife.*; import butterknife.*;
public class ScoreCard extends RelativeLayout public class ScoreCard extends HabitCard
implements ModelObservable.Listener
{ {
public static final int[] BUCKET_SIZES = { 1, 7, 31, 92, 365 }; public static final int[] BUCKET_SIZES = { 1, 7, 31, 92, 365 };
@ -47,11 +47,6 @@ public class ScoreCard extends RelativeLayout
@BindView(R.id.title) @BindView(R.id.title)
TextView title; TextView title;
@Nullable
private Habit habit;
private int color;
private int bucketSize; private int bucketSize;
public ScoreCard(Context context) public ScoreCard(Context context)
@ -70,86 +65,64 @@ public class ScoreCard extends RelativeLayout
public void onItemSelected(int position) public void onItemSelected(int position)
{ {
setBucketSizeFromPosition(position); setBucketSizeFromPosition(position);
refreshData();
} }
@Override @Override
public void onModelChange() protected void refreshData()
{ {
refreshData(); Habit habit = getHabit();
} int color = ColorUtils.getColor(getContext(), habit.getColor());
public void setHabit(@NonNull Habit habit) title.setTextColor(color);
{ chart.setPrimaryColor(color);
this.habit = habit;
color = ColorUtils.getColor(getContext(), habit.getColor());
refreshData();
}
@Override new BaseTask()
protected void onAttachedToWindow()
{
super.onAttachedToWindow();
if (habit != null)
{ {
habit.getObservable().addListener(this); @Override
habit.getScores().getObservable().addListener(this); protected void doInBackground()
} {
List<Score> scores;
if (bucketSize == 1) scores = habit.getScores().getAll();
else scores = habit.getScores().groupBy(getTruncateField());
chart.setScores(scores);
chart.setBucketSize(bucketSize);
}
}.execute();
} }
@Override private int getDefaultSpinnerPosition()
protected void onDetachedFromWindow()
{ {
if (habit != null) if (isInEditMode()) return 0;
{ return InterfaceUtils.getDefaultScoreSpinnerPosition(getContext());
habit.getObservable().removeListener(this);
habit.getScores().getObservable().removeListener(this);
}
super.onDetachedFromWindow();
} }
@NonNull @NonNull
private DateUtils.TruncateField getTruncateField() private DateUtils.TruncateField getTruncateField()
{ {
DateUtils.TruncateField field; if (bucketSize == 7) return DateUtils.TruncateField.WEEK_NUMBER;
if (bucketSize == 31) return DateUtils.TruncateField.MONTH;
if (bucketSize == 92) return DateUtils.TruncateField.QUARTER;
if (bucketSize == 365) return DateUtils.TruncateField.YEAR;
switch (bucketSize) Log.e("ScoreCard",
{ String.format("Unknown bucket size: %d", bucketSize));
case 7:
field = DateUtils.TruncateField.WEEK_NUMBER;
break;
case 365:
field = DateUtils.TruncateField.YEAR;
break;
case 92:
field = DateUtils.TruncateField.QUARTER;
break;
default:
Log.e("ScoreCard",
String.format("Unknown bucket size: %d", bucketSize));
// continue to case 31
case 31:
field = DateUtils.TruncateField.MONTH;
break;
}
return field; return DateUtils.TruncateField.MONTH;
} }
private void init() private void init()
{ {
inflate(getContext(), R.layout.show_habit_strength, this); inflate(getContext(), R.layout.show_habit_score, this);
ButterKnife.bind(this); ButterKnife.bind(this);
int defaultPosition = getDefaultSpinnerPosition(); int defaultPosition = getDefaultSpinnerPosition();
setBucketSizeFromPosition(defaultPosition); setBucketSizeFromPosition(defaultPosition);
spinner.setSelection(defaultPosition); spinner.setSelection(defaultPosition);
if(isInEditMode()) if (isInEditMode())
{ {
spinner.setVisibility(GONE); spinner.setVisibility(GONE);
title.setTextColor(ColorUtils.getAndroidTestColor(1)); title.setTextColor(ColorUtils.getAndroidTestColor(1));
@ -158,41 +131,11 @@ public class ScoreCard extends RelativeLayout
} }
} }
private int getDefaultSpinnerPosition()
{
if(isInEditMode()) return 0;
return InterfaceUtils.getDefaultScoreSpinnerPosition(getContext());
}
private void refreshData()
{
if (habit == null) return;
title.setTextColor(color);
chart.setPrimaryColor(color);
new BaseTask()
{
@Override
protected void doInBackground()
{
List<Score> scores;
if (bucketSize == 1) scores = habit.getScores().getAll();
else scores = habit.getScores().groupBy(getTruncateField());
chart.setScores(scores);
chart.setBucketSize(bucketSize);
}
}.execute();
}
private void setBucketSizeFromPosition(int position) private void setBucketSizeFromPosition(int position)
{ {
if(isInEditMode()) return; if (isInEditMode()) return;
InterfaceUtils.setDefaultScoreSpinnerPosition(getContext(), position); InterfaceUtils.setDefaultScoreSpinnerPosition(getContext(), position);
bucketSize = BUCKET_SIZES[position]; bucketSize = BUCKET_SIZES[position];
refreshData();
} }
} }

@ -0,0 +1,99 @@
/*
* 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.ui.habits.show.views.cards;
import android.content.*;
import android.util.*;
import android.widget.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.tasks.*;
import org.isoron.uhabits.ui.habits.show.views.charts.*;
import org.isoron.uhabits.utils.*;
import java.util.*;
import butterknife.*;
public class StreakCard extends HabitCard
{
@BindView(R.id.title)
TextView title;
@BindView(R.id.streakChart)
StreakChart streakChart;
public StreakCard(Context context)
{
super(context);
init();
}
public StreakCard(Context context, AttributeSet attrs)
{
super(context, attrs);
init();
}
private void init()
{
inflate(getContext(), R.layout.show_habit_streak, this);
ButterKnife.bind(this);
setOrientation(VERTICAL);
if(isInEditMode()) initEditMode();
}
private void initEditMode()
{
int color = ColorUtils.getAndroidTestColor(1);
title.setTextColor(color);
streakChart.setColor(color);
streakChart.populateWithRandomData();
}
@Override
protected void refreshData()
{
Habit habit = getHabit();
int color = ColorUtils.getColor(getContext(), habit.getColor());
title.setTextColor(color);
streakChart.setColor(color);
new BaseTask()
{
public List<Streak> streaks;
@Override
protected void doInBackground()
{
streaks = habit.getStreaks().getBest(10);
}
@Override
protected void onPostExecute(Void aVoid)
{
streakChart.setStreaks(streaks);
super.onPostExecute(aVoid);
}
}.execute();
}
}

@ -17,12 +17,11 @@
* with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.isoron.uhabits.ui.habits.show.views; package org.isoron.uhabits.ui.habits.show.views.cards;
import android.annotation.*; import android.annotation.*;
import android.content.*; import android.content.*;
import android.content.res.*; import android.content.res.*;
import android.support.annotation.*;
import android.util.*; import android.util.*;
import android.view.*; import android.view.*;
import android.widget.*; import android.widget.*;
@ -33,8 +32,7 @@ import org.isoron.uhabits.utils.*;
import butterknife.*; import butterknife.*;
public class SubtitleCard extends LinearLayout public class SubtitleCard extends HabitCard
implements ModelObservable.Listener
{ {
@BindView(R.id.questionLabel) @BindView(R.id.questionLabel)
TextView questionLabel; TextView questionLabel;
@ -45,9 +43,6 @@ public class SubtitleCard extends LinearLayout
@BindView(R.id.reminderLabel) @BindView(R.id.reminderLabel)
TextView reminderLabel; TextView reminderLabel;
@Nullable
private Habit habit;
public SubtitleCard(Context context) public SubtitleCard(Context context)
{ {
super(context); super(context);
@ -60,32 +55,6 @@ public class SubtitleCard extends LinearLayout
init(); init();
} }
@Override
public void onModelChange()
{
refreshData();
}
public void setHabit(@Nullable Habit habit)
{
this.habit = habit;
refreshData();
}
@Override
protected void onAttachedToWindow()
{
super.onAttachedToWindow();
if (habit != null) habit.getObservable().addListener(this);
}
@Override
protected void onDetachedFromWindow()
{
if (habit != null) habit.getObservable().removeListener(this);
super.onDetachedFromWindow();
}
private void init() private void init()
{ {
Context context = getContext(); Context context = getContext();
@ -103,9 +72,10 @@ public class SubtitleCard extends LinearLayout
reminderLabel.setText("08:00"); reminderLabel.setText("08:00");
} }
private void refreshData() @Override
protected void refreshData()
{ {
if (habit == null) return; Habit habit = getHabit();
int color = ColorUtils.getColor(getContext(), habit.getColor()); int color = ColorUtils.getColor(getContext(), habit.getColor());
reminderLabel.setText(getResources().getString(R.string.reminder_off)); reminderLabel.setText(getResources().getString(R.string.reminder_off));
@ -123,7 +93,7 @@ public class SubtitleCard extends LinearLayout
postInvalidate(); postInvalidate();
} }
String toText(Frequency freq) private String toText(Frequency freq)
{ {
Resources resources = getResources(); Resources resources = getResources();
Integer num = freq.getNumerator(); Integer num = freq.getNumerator();

@ -17,29 +17,25 @@
* with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.isoron.uhabits.ui.habits.show.views; package org.isoron.uhabits.ui.habits.show.views.charts;
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.uhabits.*; import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.tasks.*;
import org.isoron.uhabits.utils.*; import org.isoron.uhabits.utils.*;
import java.text.*; import java.text.*;
import java.util.*; import java.util.*;
public class FrequencyChart extends ScrollableChart public class FrequencyChart extends ScrollableChart
implements HabitDataView, ModelObservable.Listener
{ {
private Paint pGrid; private Paint pGrid;
private float em; private float em;
private Habit habit;
private SimpleDateFormat dfMonth; private SimpleDateFormat dfMonth;
private SimpleDateFormat dfYear; private SimpleDateFormat dfYear;
@ -68,6 +64,7 @@ public class FrequencyChart extends ScrollableChart
private boolean isBackgroundTransparent; private boolean isBackgroundTransparent;
@NonNull
private HashMap<Long, Integer[]> frequency; private HashMap<Long, Integer[]> frequency;
public FrequencyChart(Context context) public FrequencyChart(Context context)
@ -79,42 +76,30 @@ public class FrequencyChart extends ScrollableChart
public FrequencyChart(Context context, AttributeSet attrs) public FrequencyChart(Context context, AttributeSet attrs)
{ {
super(context, attrs); super(context, attrs);
this.primaryColor = ColorUtils.getColor(getContext(), 7);
this.frequency = new HashMap<>(); this.frequency = new HashMap<>();
init(); init();
} }
@Override public void setColor(int color)
public void onModelChange()
{
refreshData();
}
public void refreshData()
{ {
if (isInEditMode()) generateRandomData(); this.primaryColor = color;
else if (habit != null) initColors();
{
frequency = habit.getRepetitions().getWeekdayFrequency();
createColors();
}
postInvalidate(); postInvalidate();
} }
public void setHabit(Habit habit) public void setFrequency(HashMap<Long, Integer[]> frequency)
{ {
this.habit = habit; this.frequency = frequency;
createColors(); postInvalidate();
} }
public void setIsBackgroundTransparent(boolean isBackgroundTransparent) public void setIsBackgroundTransparent(boolean isBackgroundTransparent)
{ {
this.isBackgroundTransparent = isBackgroundTransparent; this.isBackgroundTransparent = isBackgroundTransparent;
createColors(); initColors();
} }
protected void createPaints() protected void initPaints()
{ {
pText = new Paint(); pText = new Paint();
pText.setAntiAlias(true); pText.setAntiAlias(true);
@ -127,30 +112,6 @@ public class FrequencyChart extends ScrollableChart
pGrid.setAntiAlias(true); pGrid.setAntiAlias(true);
} }
@Override
protected void onAttachedToWindow()
{
super.onAttachedToWindow();
new BaseTask()
{
@Override
protected void doInBackground()
{
refreshData();
}
}.execute();
habit.getObservable().addListener(this);
habit.getCheckmarks().observable.addListener(this);
}
@Override
protected void onDetachedFromWindow()
{
habit.getCheckmarks().observable.removeListener(this);
habit.getObservable().removeListener(this);
super.onDetachedFromWindow();
}
@Override @Override
protected void onDraw(Canvas canvas) protected void onDraw(Canvas canvas)
{ {
@ -214,26 +175,6 @@ public class FrequencyChart extends ScrollableChart
paddingTop = 0; paddingTop = 0;
} }
private void createColors()
{
if (habit != null)
{
this.primaryColor =
ColorUtils.getColor(getContext(), habit.getColor());
}
textColor = InterfaceUtils.getStyledColor(getContext(),
R.attr.mediumContrastTextColor);
gridColor = InterfaceUtils.getStyledColor(getContext(),
R.attr.lowContrastTextColor);
colors = new int[4];
colors[0] = gridColor;
colors[3] = primaryColor;
colors[1] = ColorUtils.mixColors(colors[0], colors[3], 0.66f);
colors[2] = ColorUtils.mixColors(colors[0], colors[3], 0.33f);
}
private void drawColumn(Canvas canvas, RectF rect, GregorianCalendar date) private void drawColumn(Canvas canvas, RectF rect, GregorianCalendar date)
{ {
Integer values[] = frequency.get(date.getTimeInMillis()); Integer values[] = frequency.get(date.getTimeInMillis());
@ -301,24 +242,6 @@ public class FrequencyChart extends ScrollableChart
canvas.drawCircle(rect.centerX(), rect.centerY(), radius, pGraph); canvas.drawCircle(rect.centerX(), rect.centerY(), radius, pGraph);
} }
private void generateRandomData()
{
GregorianCalendar date = DateUtils.getStartOfTodayCalendar();
date.set(Calendar.DAY_OF_MONTH, 1);
Random rand = new Random();
frequency.clear();
for (int i = 0; i < 40; i++)
{
Integer values[] = new Integer[7];
for (int j = 0; j < 7; j++)
values[j] = rand.nextInt(5);
frequency.put(date.getTimeInMillis(), values);
date.add(Calendar.MONTH, -1);
}
}
private float getMaxMonthWidth() private float getMaxMonthWidth()
{ {
float maxMonthWidth = 0; float maxMonthWidth = 0;
@ -336,13 +259,53 @@ public class FrequencyChart extends ScrollableChart
private void init() private void init()
{ {
createPaints(); initPaints();
createColors(); initColors();
initDateFormats();
initRects();
}
private void initColors()
{
textColor = InterfaceUtils.getStyledColor(getContext(),
R.attr.mediumContrastTextColor);
gridColor = InterfaceUtils.getStyledColor(getContext(),
R.attr.lowContrastTextColor);
colors = new int[4];
colors[0] = gridColor;
colors[3] = primaryColor;
colors[1] = ColorUtils.mixColors(colors[0], colors[3], 0.66f);
colors[2] = ColorUtils.mixColors(colors[0], colors[3], 0.33f);
}
private void initDateFormats()
{
dfMonth = DateUtils.getDateFormat("MMM"); dfMonth = DateUtils.getDateFormat("MMM");
dfYear = DateUtils.getDateFormat("yyyy"); dfYear = DateUtils.getDateFormat("yyyy");
}
private void initRects()
{
rect = new RectF(); rect = new RectF();
prevRect = new RectF(); prevRect = new RectF();
} }
public void populateWithRandomData()
{
GregorianCalendar date = DateUtils.getStartOfTodayCalendar();
date.set(Calendar.DAY_OF_MONTH, 1);
Random rand = new Random();
frequency.clear();
for (int i = 0; i < 40; i++)
{
Integer values[] = new Integer[7];
for (int j = 0; j < 7; j++)
values[j] = rand.nextInt(5);
frequency.put(date.getTimeInMillis(), values);
date.add(Calendar.MONTH, -1);
}
}
} }

@ -17,11 +17,11 @@
* with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.isoron.uhabits.ui.habits.show.views; package org.isoron.uhabits.ui.habits.show.views.charts;
import org.isoron.uhabits.models.Habit; import org.isoron.uhabits.models.Habit;
public interface HabitDataView public interface HabitChart
{ {
void setHabit(Habit habit); void setHabit(Habit habit);

@ -17,7 +17,7 @@
* with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.isoron.uhabits.ui.habits.show.views; package org.isoron.uhabits.ui.habits.show.views.charts;
import android.content.*; import android.content.*;
import android.graphics.*; import android.graphics.*;
@ -26,19 +26,13 @@ import android.util.*;
import android.view.*; import android.view.*;
import org.isoron.uhabits.*; import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.tasks.*;
import org.isoron.uhabits.utils.*; import org.isoron.uhabits.utils.*;
import java.text.*; import java.text.*;
import java.util.*; import java.util.*;
public class HistoryView extends ScrollableChart implements HabitDataView, public class HistoryChart extends ScrollableChart
ToggleRepetitionTask.Listener,
ModelObservable.Listener
{ {
private Habit habit;
private int[] checkmarks; private int[] checkmarks;
private Paint pSquareBg, pSquareFg, pTextHeader; private Paint pSquareBg, pSquareFg, pTextHeader;
@ -88,13 +82,13 @@ public class HistoryView extends ScrollableChart implements HabitDataView,
private float headerOverflow = 0; private float headerOverflow = 0;
public HistoryView(Context context) public HistoryChart(Context context)
{ {
super(context); super(context);
init(); init();
} }
public HistoryView(Context context, AttributeSet attrs) public HistoryChart(Context context, AttributeSet attrs)
{ {
super(context, attrs); super(context, attrs);
init(); init();
@ -106,12 +100,6 @@ public class HistoryView extends ScrollableChart implements HabitDataView,
onSingleTapUp(e); onSingleTapUp(e);
} }
@Override
public void onModelChange()
{
refreshData();
}
@Override @Override
public boolean onSingleTapUp(MotionEvent e) public boolean onSingleTapUp(MotionEvent e)
{ {
@ -126,59 +114,48 @@ public class HistoryView extends ScrollableChart implements HabitDataView,
final Long timestamp = positionToTimestamp(x, y); final Long timestamp = positionToTimestamp(x, y);
if (timestamp == null) return false; if (timestamp == null) return false;
ToggleRepetitionTask task = new ToggleRepetitionTask(habit, timestamp); // ToggleRepetitionTask task = new ToggleRepetitionTask(habit, timestamp);
task.setListener(this); // task.setListener(this);
task.execute(); // task.execute();
return true; return true;
} }
@Override public void populateWithRandomData()
public void onToggleRepetitionFinished()
{ {
new BaseTask() Random random = new Random();
{ checkmarks = new int[100];
@Override
protected void doInBackground()
{
refreshData();
}
@Override for (int i = 0; i < 100; i++)
protected void onPostExecute(Void aVoid) if (random.nextFloat() < 0.3) checkmarks[i] = 2;
{
invalidate();
super.onPostExecute(null);
}
}.execute();
}
@Override for (int i = 0; i < 100 - 7; i++)
public void refreshData()
{
if (isInEditMode()) generateRandomData();
else
{ {
if (habit == null) return; int count = 0;
checkmarks = habit.getCheckmarks().getAllValues(); for (int j = 0; j < 7; j++)
createColors(); if (checkmarks[i + j] != 0) count++;
if (count >= 3) checkmarks[i] = Math.max(checkmarks[i], 1);
} }
}
updateDate(); public void setCheckmarks(int[] checkmarks)
{
this.checkmarks = checkmarks;
postInvalidate(); postInvalidate();
} }
@Override public void setColor(int color)
public void setHabit(Habit habit)
{ {
this.habit = habit; this.primaryColor = color;
createColors(); initColors();
postInvalidate();
} }
public void setIsBackgroundTransparent(boolean isBackgroundTransparent) public void setIsBackgroundTransparent(boolean isBackgroundTransparent)
{ {
this.isBackgroundTransparent = isBackgroundTransparent; this.isBackgroundTransparent = isBackgroundTransparent;
createColors(); initColors();
} }
public void setIsEditable(boolean isEditable) public void setIsEditable(boolean isEditable)
@ -186,7 +163,7 @@ public class HistoryView extends ScrollableChart implements HabitDataView,
this.isEditable = isEditable; this.isEditable = isEditable;
} }
protected void createPaints() protected void initPaints()
{ {
pTextHeader = new Paint(); pTextHeader = new Paint();
pTextHeader.setTextAlign(Align.LEFT); pTextHeader.setTextAlign(Align.LEFT);
@ -199,30 +176,6 @@ public class HistoryView extends ScrollableChart implements HabitDataView,
pSquareFg.setTextAlign(Align.CENTER); pSquareFg.setTextAlign(Align.CENTER);
} }
@Override
protected void onAttachedToWindow()
{
super.onAttachedToWindow();
new BaseTask()
{
@Override
protected void doInBackground()
{
refreshData();
}
}.execute();
habit.getObservable().addListener(this);
habit.getCheckmarks().observable.addListener(this);
}
@Override
protected void onDetachedFromWindow()
{
habit.getCheckmarks().observable.removeListener(this);
habit.getObservable().removeListener(this);
super.onDetachedFromWindow();
}
@Override @Override
protected void onDraw(Canvas canvas) protected void onDraw(Canvas canvas)
{ {
@ -290,41 +243,6 @@ public class HistoryView extends ScrollableChart implements HabitDataView,
updateDate(); updateDate();
} }
private void createColors()
{
if (habit != null) this.primaryColor =
ColorUtils.getColor(getContext(), habit.getColor());
if (isBackgroundTransparent)
primaryColor = ColorUtils.setMinValue(primaryColor, 0.75f);
int red = Color.red(primaryColor);
int green = Color.green(primaryColor);
int blue = Color.blue(primaryColor);
if (isBackgroundTransparent)
{
colors = new int[3];
colors[0] = Color.argb(16, 255, 255, 255);
colors[1] = Color.argb(128, red, green, blue);
colors[2] = primaryColor;
textColor = Color.WHITE;
reverseTextColor = Color.WHITE;
}
else
{
colors = new int[3];
colors[0] = InterfaceUtils.getStyledColor(getContext(),
R.attr.lowContrastTextColor);
colors[1] = Color.argb(127, red, green, blue);
colors[2] = primaryColor;
textColor = InterfaceUtils.getStyledColor(getContext(),
R.attr.mediumContrastTextColor);
reverseTextColor = InterfaceUtils.getStyledColor(getContext(),
R.attr.highContrastReverseTextColor);
}
}
private void drawAxis(Canvas canvas, RectF location) private void drawAxis(Canvas canvas, RectF location)
{ {
float verticalOffset = pTextHeader.getFontSpacing() * 0.4f; float verticalOffset = pTextHeader.getFontSpacing() * 0.4f;
@ -398,24 +316,6 @@ public class HistoryView extends ScrollableChart implements HabitDataView,
location.centerY() + squareTextOffset, pSquareFg); location.centerY() + squareTextOffset, pSquareFg);
} }
private void generateRandomData()
{
Random random = new Random();
checkmarks = new int[100];
for (int i = 0; i < 100; i++)
if (random.nextFloat() < 0.3) checkmarks[i] = 2;
for (int i = 0; i < 100 - 7; i++)
{
int count = 0;
for (int j = 0; j < 7; j++)
if (checkmarks[i + j] != 0) count++;
if (count >= 3) checkmarks[i] = Math.max(checkmarks[i], 1);
}
}
private float getWeekdayLabelWidth() private float getWeekdayLabelWidth()
{ {
float width = 0; float width = 0;
@ -428,15 +328,55 @@ public class HistoryView extends ScrollableChart implements HabitDataView,
private void init() private void init()
{ {
createColors();
createPaints();
isEditable = false; isEditable = false;
checkmarks = new int[0]; checkmarks = new int[0];
primaryColor = ColorUtils.getColor(getContext(), 7);
initColors();
initPaints();
initDateFormats();
initRects();
}
private void initColors()
{
if (isBackgroundTransparent)
primaryColor = ColorUtils.setMinValue(primaryColor, 0.75f);
int red = Color.red(primaryColor);
int green = Color.green(primaryColor);
int blue = Color.blue(primaryColor);
if (isBackgroundTransparent)
{
colors = new int[3];
colors[0] = Color.argb(16, 255, 255, 255);
colors[1] = Color.argb(128, red, green, blue);
colors[2] = primaryColor;
textColor = Color.WHITE;
reverseTextColor = Color.WHITE;
}
else
{
colors = new int[3];
colors[0] = InterfaceUtils.getStyledColor(getContext(),
R.attr.lowContrastTextColor);
colors[1] = Color.argb(127, red, green, blue);
colors[2] = primaryColor;
textColor = InterfaceUtils.getStyledColor(getContext(),
R.attr.mediumContrastTextColor);
reverseTextColor = InterfaceUtils.getStyledColor(getContext(),
R.attr.highContrastReverseTextColor);
}
}
private void initDateFormats()
{
dfMonth = DateUtils.getDateFormat("MMM"); dfMonth = DateUtils.getDateFormat("MMM");
dfYear = DateUtils.getDateFormat("yyyy"); dfYear = DateUtils.getDateFormat("yyyy");
}
private void initRects()
{
baseLocation = new RectF(); baseLocation = new RectF();
} }

@ -17,7 +17,7 @@
* with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.isoron.uhabits.ui.habits.show.views; package org.isoron.uhabits.ui.habits.show.views.charts;
import android.content.*; import android.content.*;
import android.graphics.*; import android.graphics.*;
@ -31,6 +31,8 @@ import org.isoron.uhabits.utils.*;
import java.text.*; import java.text.*;
import java.util.*; import java.util.*;
import static org.isoron.uhabits.utils.InterfaceUtils.*;
public class ScoreChart extends ScrollableChart public class ScoreChart extends ScrollableChart
{ {
private static final PorterDuffXfermode XFERMODE_CLEAR = private static final PorterDuffXfermode XFERMODE_CLEAR =
@ -72,6 +74,7 @@ public class ScoreChart extends ScrollableChart
private int primaryColor; private int primaryColor;
@Deprecated
private int bucketSize = 7; private int bucketSize = 7;
private int backgroundColor; private int backgroundColor;
@ -100,6 +103,26 @@ public class ScoreChart extends ScrollableChart
init(); init();
} }
public void populateWithRandomData()
{
Random random = new Random();
scores = new LinkedList<>();
int previous = Score.MAX_VALUE / 2;
long timestamp = DateUtils.getStartOfToday();
long day = DateUtils.millisecondsInOneDay;
for (int i = 1; i < 100; i++)
{
int step = Score.MAX_VALUE / 10;
int current = previous + random.nextInt(step * 2) - step;
current = Math.max(0, Math.min(Score.MAX_VALUE, current));
scores.add(new Score(timestamp, current));
previous = current;
timestamp -= day;
}
}
@Deprecated @Deprecated
public void setBucketSize(int bucketSize) public void setBucketSize(int bucketSize)
{ {
@ -110,7 +133,7 @@ public class ScoreChart extends ScrollableChart
public void setIsTransparencyEnabled(boolean enabled) public void setIsTransparencyEnabled(boolean enabled)
{ {
this.isTransparencyEnabled = enabled; this.isTransparencyEnabled = enabled;
createColors(); initColors();
requestLayout(); requestLayout();
} }
@ -126,19 +149,6 @@ public class ScoreChart extends ScrollableChart
postInvalidate(); postInvalidate();
} }
protected void createPaints()
{
pText = new Paint();
pText.setAntiAlias(true);
pGraph = new Paint();
pGraph.setTextAlign(Paint.Align.CENTER);
pGraph.setAntiAlias(true);
pGrid = new Paint();
pGrid.setAntiAlias(true);
}
@Override @Override
protected void onDraw(Canvas canvas) protected void onDraw(Canvas canvas)
{ {
@ -240,7 +250,7 @@ public class ScoreChart extends ScrollableChart
columnHeight = 8 * baseSize; columnHeight = 8 * baseSize;
float minStrokeWidth = InterfaceUtils.dpToPixels(getContext(), 1); float minStrokeWidth = dpToPixels(getContext(), 1);
pGraph.setTextSize(baseSize * 0.5f); pGraph.setTextSize(baseSize * 0.5f);
pGraph.setStrokeWidth(baseSize * 0.1f); pGraph.setStrokeWidth(baseSize * 0.1f);
pGrid.setStrokeWidth(Math.min(minStrokeWidth, baseSize * 0.05f)); pGrid.setStrokeWidth(Math.min(minStrokeWidth, baseSize * 0.05f));
@ -248,17 +258,6 @@ public class ScoreChart extends ScrollableChart
if (isTransparencyEnabled) initCache(width, height); if (isTransparencyEnabled) initCache(width, height);
} }
private void createColors()
{
primaryColor = Color.BLACK;
textColor = InterfaceUtils.getStyledColor(getContext(),
R.attr.mediumContrastTextColor);
gridColor = InterfaceUtils.getStyledColor(getContext(),
R.attr.lowContrastTextColor);
backgroundColor = InterfaceUtils.getStyledColor(getContext(),
R.attr.cardBackgroundColor);
}
private void drawFooter(Canvas canvas, RectF rect, long currentDate) private void drawFooter(Canvas canvas, RectF rect, long currentDate)
{ {
String yearText = dfYear.format(currentDate); String yearText = dfYear.format(currentDate);
@ -340,7 +339,7 @@ public class ScoreChart extends ScrollableChart
private void drawMarker(Canvas canvas, RectF rect) private void drawMarker(Canvas canvas, RectF rect)
{ {
rect.inset(baseSize * 0.15f, baseSize * 0.15f); 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);
@ -348,33 +347,13 @@ public class ScoreChart extends ScrollableChart
setModeOrColor(pGraph, XFERMODE_SRC, primaryColor); setModeOrColor(pGraph, XFERMODE_SRC, primaryColor);
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_CLEAR, backgroundColor); // setModeOrColor(pGraph, XFERMODE_CLEAR, backgroundColor);
canvas.drawOval(rect, pGraph); // canvas.drawOval(rect, pGraph);
if (isTransparencyEnabled) pGraph.setXfermode(XFERMODE_SRC); if (isTransparencyEnabled) pGraph.setXfermode(XFERMODE_SRC);
} }
public void populateWithRandomData()
{
Random random = new Random();
scores = new LinkedList<>();
int previous = Score.MAX_VALUE / 2;
long timestamp = DateUtils.getStartOfToday();
long day = DateUtils.millisecondsInOneDay;
for (int i = 1; i < 100; i++)
{
int step = Score.MAX_VALUE / 10;
int current = previous + random.nextInt(step * 2) - step;
current = Math.max(0, Math.min(Score.MAX_VALUE, current));
scores.add(new Score(timestamp, current));
previous = current;
timestamp -= day;
}
}
private float getMaxDayWidth() private float getMaxDayWidth()
{ {
float maxDayWidth = 0; float maxDayWidth = 0;
@ -407,15 +386,10 @@ public class ScoreChart extends ScrollableChart
private void init() private void init()
{ {
createPaints(); initPaints();
createColors(); initColors();
initDateFormats();
dfYear = DateUtils.getDateFormat("yyyy"); initRects();
dfMonth = DateUtils.getDateFormat("MMM");
dfDay = DateUtils.getDateFormat("d");
rect = new RectF();
prevRect = new RectF();
} }
private void initCache(int width, int height) private void initCache(int width, int height)
@ -426,6 +400,42 @@ public class ScoreChart extends ScrollableChart
cacheCanvas = new Canvas(drawingCache); cacheCanvas = new Canvas(drawingCache);
} }
private void initColors()
{
Context context = getContext();
primaryColor = Color.BLACK;
textColor = getStyledColor(context, R.attr.mediumContrastTextColor);
gridColor = getStyledColor(context, R.attr.lowContrastTextColor);
backgroundColor = getStyledColor(context, R.attr.cardBackgroundColor);
}
private void initDateFormats()
{
dfYear = DateUtils.getDateFormat("yyyy");
dfMonth = DateUtils.getDateFormat("MMM");
dfDay = DateUtils.getDateFormat("d");
}
private void initPaints()
{
pText = new Paint();
pText.setAntiAlias(true);
pGraph = new Paint();
pGraph.setTextAlign(Paint.Align.CENTER);
pGraph.setAntiAlias(true);
pGrid = new Paint();
pGrid.setAntiAlias(true);
}
private void initRects()
{
rect = new RectF();
prevRect = new RectF();
}
private void setModeOrColor(Paint p, PorterDuffXfermode mode, int color) private void setModeOrColor(Paint p, PorterDuffXfermode mode, int color)
{ {
if (isTransparencyEnabled) p.setXfermode(mode); if (isTransparencyEnabled) p.setXfermode(mode);

@ -17,7 +17,7 @@
* with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.isoron.uhabits.ui.habits.show.views; package org.isoron.uhabits.ui.habits.show.views.charts;
import android.animation.*; import android.animation.*;
import android.content.*; import android.content.*;

@ -17,7 +17,7 @@
* with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.isoron.uhabits.ui.habits.show.views; package org.isoron.uhabits.ui.habits.show.views.charts;
import android.content.*; import android.content.*;
import android.graphics.*; import android.graphics.*;
@ -26,17 +26,13 @@ import android.view.*;
import org.isoron.uhabits.*; import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*; import org.isoron.uhabits.models.*;
import org.isoron.uhabits.tasks.*;
import org.isoron.uhabits.utils.*; import org.isoron.uhabits.utils.*;
import java.text.*; import java.text.*;
import java.util.*; import java.util.*;
public class StreakChart extends View public class StreakChart extends View
implements HabitDataView, ModelObservable.Listener
{ {
private Habit habit;
private Paint paint; private Paint paint;
private long minLength; private long minLength;
@ -67,8 +63,6 @@ public class StreakChart extends View
private boolean shouldShowLabels; private boolean shouldShowLabels;
private int maxStreakCount;
private int textColor; private int textColor;
private int reverseTextColor; private int reverseTextColor;
@ -82,68 +76,44 @@ public class StreakChart extends View
public StreakChart(Context context, AttributeSet attrs) public StreakChart(Context context, AttributeSet attrs)
{ {
super(context, attrs); super(context, attrs);
this.primaryColor = ColorUtils.getColor(getContext(), 7);
init(); init();
} }
@Override
public void onModelChange()
{
refreshData();
}
@Override
public void refreshData()
{
if (habit == null) return;
streaks = habit.getStreaks().getBest(maxStreakCount);
createColors();
updateMaxMin();
postInvalidate();
}
@Override
public void setHabit(Habit habit)
{
this.habit = habit;
createColors();
}
public void setIsBackgroundTransparent(boolean isBackgroundTransparent) public void setIsBackgroundTransparent(boolean isBackgroundTransparent)
{ {
this.isBackgroundTransparent = isBackgroundTransparent; this.isBackgroundTransparent = isBackgroundTransparent;
createColors(); initColors();
} }
protected void createPaints() public void setStreaks(List<Streak> streaks)
{ {
paint = new Paint(); this.streaks = streaks;
paint.setTextAlign(Paint.Align.CENTER); initColors();
paint.setAntiAlias(true); updateMaxMinLengths();
requestLayout();
} }
@Override public void populateWithRandomData()
protected void onAttachedToWindow()
{ {
super.onAttachedToWindow(); long day = DateUtils.millisecondsInOneDay;
new BaseTask() long start = DateUtils.getStartOfToday();
LinkedList<Streak> streaks = new LinkedList<>();
for(int i = 0; i < 10; i++)
{ {
@Override int length = new Random().nextInt(100);
protected void doInBackground() long end = start + length * day;
{ streaks.add(new Streak(start, end));
refreshData(); start = end + day;
} }
}.execute();
habit.getObservable().addListener(this); setStreaks(streaks);
habit.getStreaks().getObservable().addListener(this);
} }
@Override public void setColor(int color)
protected void onDetachedFromWindow()
{ {
habit.getStreaks().getObservable().removeListener(this); this.primaryColor = color;
habit.getObservable().removeListener(this); postInvalidate();
super.onDetachedFromWindow();
} }
@Override @Override
@ -162,11 +132,14 @@ public class StreakChart extends View
} }
@Override @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) protected void onMeasure(int widthSpec, int heightSpec)
{ {
int width = MeasureSpec.getSize(widthMeasureSpec); int width = MeasureSpec.getSize(widthSpec);
int height = MeasureSpec.getSize(heightMeasureSpec); int height = streaks.size() * baseSize;
setMeasuredDimension(width, height);
heightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
widthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
setMeasuredDimension(widthSpec, heightSpec);
} }
@Override @Override
@ -175,7 +148,6 @@ public class StreakChart extends View
int oldWidth, int oldWidth,
int oldHeight) int oldHeight)
{ {
maxStreakCount = height / baseSize;
this.width = width; this.width = width;
float minTextSize = getResources().getDimension(R.dimen.tinyTextSize); float minTextSize = getResources().getDimension(R.dimen.tinyTextSize);
@ -188,28 +160,7 @@ public class StreakChart extends View
em = paint.getFontSpacing(); em = paint.getFontSpacing();
textMargin = 0.5f * em; textMargin = 0.5f * em;
updateMaxMin(); updateMaxMinLengths();
}
private void createColors()
{
if (habit != null) this.primaryColor =
ColorUtils.getColor(getContext(), habit.getColor());
int red = Color.red(primaryColor);
int green = Color.green(primaryColor);
int blue = Color.blue(primaryColor);
colors = new int[4];
colors[3] = primaryColor;
colors[2] = Color.argb(192, red, green, blue);
colors[1] = Color.argb(96, red, green, blue);
colors[0] = InterfaceUtils.getStyledColor(getContext(),
R.attr.lowContrastTextColor);
textColor = InterfaceUtils.getStyledColor(getContext(),
R.attr.mediumContrastTextColor);
reverseTextColor = InterfaceUtils.getStyledColor(getContext(),
R.attr.highContrastReverseTextColor);
} }
private void drawRow(Canvas canvas, Streak streak, RectF rect) private void drawRow(Canvas canvas, Streak streak, RectF rect)
@ -256,18 +207,42 @@ public class StreakChart extends View
private void init() private void init()
{ {
createPaints(); initPaints();
createColors(); initColors();
streaks = Collections.emptyList(); streaks = Collections.emptyList();
dateFormat = DateFormat.getDateInstance(DateFormat.MEDIUM); dateFormat = DateFormat.getDateInstance(DateFormat.MEDIUM);
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT")); dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
rect = new RectF(); rect = new RectF();
maxStreakCount = 10;
baseSize = getResources().getDimensionPixelSize(R.dimen.baseSize); baseSize = getResources().getDimensionPixelSize(R.dimen.baseSize);
} }
private void initColors()
{
int red = Color.red(primaryColor);
int green = Color.green(primaryColor);
int blue = Color.blue(primaryColor);
colors = new int[4];
colors[3] = primaryColor;
colors[2] = Color.argb(192, red, green, blue);
colors[1] = Color.argb(96, red, green, blue);
colors[0] = InterfaceUtils.getStyledColor(getContext(),
R.attr.lowContrastTextColor);
textColor = InterfaceUtils.getStyledColor(getContext(),
R.attr.mediumContrastTextColor);
reverseTextColor = InterfaceUtils.getStyledColor(getContext(),
R.attr.highContrastReverseTextColor);
}
private void initPaints()
{
paint = new Paint();
paint.setTextAlign(Paint.Align.CENTER);
paint.setAntiAlias(true);
}
private int percentageToColor(float percentage) private int percentageToColor(float percentage)
{ {
if (percentage >= 1.0f) return colors[3]; if (percentage >= 1.0f) return colors[3];
@ -276,7 +251,7 @@ public class StreakChart extends View
return colors[0]; return colors[0];
} }
private void updateMaxMin() private void updateMaxMinLengths()
{ {
maxLength = 0; maxLength = 0;
minLength = Long.MAX_VALUE; minLength = Long.MAX_VALUE;

@ -24,7 +24,7 @@ import android.view.*;
import org.isoron.uhabits.*; import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*; import org.isoron.uhabits.models.*;
import org.isoron.uhabits.ui.habits.show.views.*; import org.isoron.uhabits.ui.habits.show.views.charts.*;
import org.isoron.uhabits.widgets.views.*; import org.isoron.uhabits.widgets.views.*;
public class CheckmarkWidgetProvider extends BaseWidgetProvider public class CheckmarkWidgetProvider extends BaseWidgetProvider
@ -65,7 +65,7 @@ public class CheckmarkWidgetProvider extends BaseWidgetProvider
@Override @Override
protected void refreshCustomViewData(View view) protected void refreshCustomViewData(View view)
{ {
((HabitDataView) view).refreshData(); ((HabitChart) view).refreshData();
} }

@ -19,16 +19,14 @@
package org.isoron.uhabits.widgets; package org.isoron.uhabits.widgets;
import android.app.PendingIntent; import android.app.*;
import android.content.Context; import android.content.*;
import android.view.View; import android.view.*;
import org.isoron.uhabits.HabitBroadcastReceiver; import org.apache.commons.lang3.*;
import org.isoron.uhabits.R; import org.isoron.uhabits.*;
import org.isoron.uhabits.models.Habit; import org.isoron.uhabits.models.*;
import org.isoron.uhabits.widgets.views.GraphWidgetView; import org.isoron.uhabits.ui.habits.show.views.charts.*;
import org.isoron.uhabits.ui.habits.show.views.HabitDataView;
import org.isoron.uhabits.ui.habits.show.views.FrequencyChart;
public class FrequencyWidgetProvider extends BaseWidgetProvider public class FrequencyWidgetProvider extends BaseWidgetProvider
{ {
@ -36,15 +34,16 @@ public class FrequencyWidgetProvider extends BaseWidgetProvider
protected View buildCustomView(Context context, Habit habit) protected View buildCustomView(Context context, Habit habit)
{ {
FrequencyChart dataView = new FrequencyChart(context); FrequencyChart dataView = new FrequencyChart(context);
GraphWidgetView view = new GraphWidgetView(context, dataView); throw new NotImplementedException("");
view.setHabit(habit); // GraphWidgetView view = new GraphWidgetView(context, dataView);
return view; // view.setHabit(habit);
// return view;
} }
@Override @Override
protected void refreshCustomViewData(View view) protected void refreshCustomViewData(View view)
{ {
((HabitDataView) view).refreshData(); ((HabitChart) view).refreshData();
} }
@Override @Override

@ -18,32 +18,31 @@
*/ */
package org.isoron.uhabits.widgets; package org.isoron.uhabits.widgets;
import android.app.PendingIntent; import android.app.*;
import android.content.Context; import android.content.*;
import android.view.View; import android.view.*;
import org.isoron.uhabits.HabitBroadcastReceiver; import org.apache.commons.lang3.*;
import org.isoron.uhabits.R; import org.isoron.uhabits.*;
import org.isoron.uhabits.models.Habit; import org.isoron.uhabits.models.*;
import org.isoron.uhabits.widgets.views.GraphWidgetView; import org.isoron.uhabits.ui.habits.show.views.charts.*;
import org.isoron.uhabits.ui.habits.show.views.HabitDataView;
import org.isoron.uhabits.ui.habits.show.views.HistoryView;
public class HistoryWidgetProvider extends BaseWidgetProvider public class HistoryWidgetProvider extends BaseWidgetProvider
{ {
@Override @Override
protected View buildCustomView(Context context, Habit habit) protected View buildCustomView(Context context, Habit habit)
{ {
HistoryView dataView = new HistoryView(context); throw new NotImplementedException("");
GraphWidgetView view = new GraphWidgetView(context, dataView); // HistoryChart dataView = new HistoryChart(context);
view.setHabit(habit); // GraphWidgetView view = new GraphWidgetView(context, dataView);
return view; // view.setHabit(habit);
// return view;
} }
@Override @Override
protected void refreshCustomViewData(View view) protected void refreshCustomViewData(View view)
{ {
((HabitDataView) view).refreshData(); ((HabitChart) view).refreshData();
} }
@Override @Override

@ -25,7 +25,8 @@ import android.view.*;
import org.apache.commons.lang3.*; import org.apache.commons.lang3.*;
import org.isoron.uhabits.*; import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*; import org.isoron.uhabits.models.*;
import org.isoron.uhabits.ui.habits.show.views.*; import org.isoron.uhabits.ui.habits.show.views.cards.*;
import org.isoron.uhabits.ui.habits.show.views.charts.*;
import org.isoron.uhabits.utils.*; import org.isoron.uhabits.utils.*;
public class ScoreWidgetProvider extends BaseWidgetProvider public class ScoreWidgetProvider extends BaseWidgetProvider
@ -50,7 +51,7 @@ public class ScoreWidgetProvider extends BaseWidgetProvider
@Override @Override
protected void refreshCustomViewData(View view) protected void refreshCustomViewData(View view)
{ {
((HabitDataView) view).refreshData(); ((HabitChart) view).refreshData();
} }
@Override @Override

@ -18,16 +18,14 @@
*/ */
package org.isoron.uhabits.widgets; package org.isoron.uhabits.widgets;
import android.app.PendingIntent; import android.app.*;
import android.content.Context; import android.content.*;
import android.view.View; import android.view.*;
import org.isoron.uhabits.HabitBroadcastReceiver; import org.apache.commons.lang3.*;
import org.isoron.uhabits.R; import org.isoron.uhabits.*;
import org.isoron.uhabits.models.Habit; import org.isoron.uhabits.models.*;
import org.isoron.uhabits.widgets.views.GraphWidgetView; import org.isoron.uhabits.ui.habits.show.views.charts.*;
import org.isoron.uhabits.ui.habits.show.views.HabitDataView;
import org.isoron.uhabits.ui.habits.show.views.StreakChart;
public class StreakWidgetProvider extends BaseWidgetProvider public class StreakWidgetProvider extends BaseWidgetProvider
{ {
@ -35,15 +33,16 @@ public class StreakWidgetProvider extends BaseWidgetProvider
protected View buildCustomView(Context context, Habit habit) protected View buildCustomView(Context context, Habit habit)
{ {
StreakChart dataView = new StreakChart(context); StreakChart dataView = new StreakChart(context);
GraphWidgetView view = new GraphWidgetView(context, dataView); throw new NotImplementedException("");
view.setHabit(habit); // GraphWidgetView view = new GraphWidgetView(context, dataView);
return view; // view.setHabit(habit);
// return view;
} }
@Override @Override
protected void refreshCustomViewData(View view) protected void refreshCustomViewData(View view)
{ {
((HabitDataView) view).refreshData(); ((HabitChart) view).refreshData();
} }
@Override @Override

@ -30,13 +30,13 @@ import org.isoron.uhabits.R;
import org.isoron.uhabits.models.Checkmark; import org.isoron.uhabits.models.Checkmark;
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 org.isoron.uhabits.ui.habits.show.views.HabitDataView; import org.isoron.uhabits.ui.habits.show.views.charts.HabitChart;
import org.isoron.uhabits.ui.habits.show.views.RingView; import org.isoron.uhabits.ui.habits.show.views.RingView;
import org.isoron.uhabits.utils.ColorUtils; import org.isoron.uhabits.utils.ColorUtils;
import org.isoron.uhabits.utils.InterfaceUtils; import org.isoron.uhabits.utils.InterfaceUtils;
public class CheckmarkWidgetView extends HabitWidgetView public class CheckmarkWidgetView extends HabitWidgetView
implements HabitDataView implements HabitChart
{ {
private int activeColor; private int activeColor;

@ -27,15 +27,15 @@ import android.widget.TextView;
import org.isoron.uhabits.R; import org.isoron.uhabits.R;
import org.isoron.uhabits.models.Habit; import org.isoron.uhabits.models.Habit;
import org.isoron.uhabits.ui.habits.show.views.HabitDataView; import org.isoron.uhabits.ui.habits.show.views.charts.HabitChart;
public class GraphWidgetView extends HabitWidgetView implements HabitDataView public class GraphWidgetView extends HabitWidgetView implements HabitChart
{ {
private final HabitDataView dataView; private final HabitChart dataView;
private TextView title; private TextView title;
public GraphWidgetView(Context context, HabitDataView dataView) public GraphWidgetView(Context context, HabitChart dataView)
{ {
super(context); super(context);
this.dataView = dataView; this.dataView = dataView;

@ -30,13 +30,13 @@ import android.widget.*;
import org.isoron.uhabits.*; import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*; import org.isoron.uhabits.models.*;
import org.isoron.uhabits.ui.habits.show.views.*; import org.isoron.uhabits.ui.habits.show.views.charts.*;
import org.isoron.uhabits.utils.*; import org.isoron.uhabits.utils.*;
import java.util.*; import java.util.*;
public abstract class HabitWidgetView extends FrameLayout public abstract class HabitWidgetView extends FrameLayout
implements HabitDataView implements HabitChart
{ {
@Nullable @Nullable
protected InsetDrawable background; protected InsetDrawable background;

@ -19,7 +19,6 @@
<ScrollView <ScrollView
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:habit="http://isoron.org/android"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -31,7 +30,7 @@
style="@style/CardList" style="@style/CardList"
android:clipToPadding="false"> android:clipToPadding="false">
<org.isoron.uhabits.ui.habits.show.views.SubtitleCard <org.isoron.uhabits.ui.habits.show.views.cards.SubtitleCard
style="@style/ShowHabit.Subtitle" style="@style/ShowHabit.Subtitle"
android:id="@+id/subtitleCard"/> android:id="@+id/subtitleCard"/>
@ -39,74 +38,29 @@
<!--android:id="@+id/headerShadow"--> <!--android:id="@+id/headerShadow"-->
<!--style="@style/ToolbarShadow"/>--> <!--style="@style/ToolbarShadow"/>-->
<org.isoron.uhabits.ui.habits.show.views.OverviewCard <org.isoron.uhabits.ui.habits.show.views.cards.OverviewCard
android:id="@+id/overviewCard" android:id="@+id/overviewCard"
android:paddingTop="12dp" android:paddingTop="12dp"
style="@style/Card" /> style="@style/Card" />
<org.isoron.uhabits.ui.habits.show.views.ScoreCard <org.isoron.uhabits.ui.habits.show.views.cards.ScoreCard
android:id="@+id/strengthCard" android:id="@+id/strengthCard"
style="@style/Card" style="@style/Card"
android:gravity="center" /> android:gravity="center" />
<LinearLayout <org.isoron.uhabits.ui.habits.show.views.cards.HistoryCard
android:id="@+id/historyCard"
style="@style/Card" style="@style/Card"
android:clipToPadding="false" android:paddingBottom="0dp"
android:orientation="vertical" android:gravity="center" />
android:paddingBottom="0dp">
<TextView
android:id="@+id/tvHistory"
style="@style/CardHeader"
android:text="@string/history"/>
<org.isoron.uhabits.ui.habits.show.views.HistoryView
android:id="@+id/historyView"
android:layout_width="match_parent"
android:layout_height="160dp"/>
<Button
android:id="@+id/btEditHistory"
style="?android:borderlessButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@drawable/ic_action_edit_light"
android:text="@string/edit"
android:textColor="@color/grey_400"
android:textSize="@dimen/smallTextSize"/>
</LinearLayout>
<LinearLayout
style="@style/Card"
android:orientation="vertical">
<TextView
android:id="@+id/tvStreaks"
style="@style/CardHeader"
android:text="@string/best_streaks"/>
<org.isoron.uhabits.ui.habits.show.views.StreakChart
android:id="@+id/streakChart"
android:layout_width="match_parent"
android:layout_height="200dp"/>
</LinearLayout>
<LinearLayout
style="@style/Card"
android:orientation="vertical">
<TextView <org.isoron.uhabits.ui.habits.show.views.cards.StreakCard
android:id="@+id/tvWeekdayFreq" android:id="@+id/streakCard"
style="@style/CardHeader" style="@style/Card"/>
android:text="@string/frequency"/>
<org.isoron.uhabits.ui.habits.show.views.FrequencyChart <org.isoron.uhabits.ui.habits.show.views.cards.FrequencyCard
android:id="@+id/punchcardView" android:id="@+id/frequencyCard"
android:layout_width="match_parent" style="@style/Card"/>
android:layout_height="200dp"/>
</LinearLayout>
</LinearLayout> </LinearLayout>
</ScrollView> </ScrollView>

@ -19,30 +19,13 @@
--> -->
<merge xmlns:android="http://schemas.android.com/apk/res/android"> <merge xmlns:android="http://schemas.android.com/apk/res/android">
<android.support.v7.widget.AppCompatSpinner
android:id="@+id/spinner"
android:layout_width="wrap_content"
android:layout_height="22dp"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:entries="@array/strengthIntervalNames"
android:theme="@style/SmallSpinner"
/>
<TextView <TextView
android:id="@+id/title" android:id="@+id/title"
style="@style/CardHeader" style="@style/CardHeader"
android:layout_alignParentLeft="true" android:text="@string/frequency"/>
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:text="@string/habit_strength"/>
<org.isoron.uhabits.ui.habits.show.views.ScoreChart <org.isoron.uhabits.ui.habits.show.views.charts.FrequencyChart
android:id="@+id/scoreView" android:id="@+id/frequencyChart"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="220dp" android:layout_height="200dp"/>
android:layout_below="@id/title"/>
</merge> </merge>

@ -0,0 +1,48 @@
<?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"
android:clipToPadding="false"
android:orientation="vertical"
android:paddingBottom="0dp">
<TextView
android:id="@+id/title"
style="@style/CardHeader"
android:text="@string/history"/>
<org.isoron.uhabits.ui.habits.show.views.charts.HistoryChart
android:id="@+id/historyChart"
android:layout_width="match_parent"
android:layout_height="160dp"/>
<Button
android:id="@+id/edit"
style="?android:borderlessButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@drawable/ic_action_edit_light"
android:text="@string/edit"
android:textColor="@color/grey_400"
android:textSize="@dimen/smallTextSize"/>
</merge>

@ -1,57 +0,0 @@
<?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/>.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="?windowBackgroundColor"
android:clipToPadding="false"
android:fillViewport="true"
android:orientation="vertical">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
style="@style/Toolbar"
android:background="@color/deep_orange_700"
android:elevation="1dp"
app:popupTheme="?toolbarPopupTheme"
app:title="Meditation"/>
<LinearLayout
style="@style/CardList"
android:clipToPadding="false">
<org.isoron.uhabits.ui.habits.show.views.SubtitleCard
android:id="@+id/subtitleView"
style="@style/ShowHabit.Subtitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<org.isoron.uhabits.ui.habits.show.views.OverviewCard
style="@style/Card"
android:paddingTop="12dp"/>
<org.isoron.uhabits.ui.habits.show.views.ScoreCard
style="@style/Card"
android:paddingTop="12dp"/>
</LinearLayout>
</LinearLayout>

@ -0,0 +1,52 @@
<?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">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.AppCompatSpinner
android:id="@+id/spinner"
android:layout_width="wrap_content"
android:layout_height="22dp"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:entries="@array/strengthIntervalNames"
android:theme="@style/SmallSpinner"
/>
<TextView
android:id="@+id/title"
style="@style/CardHeader"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:text="@string/habit_strength"/>
<org.isoron.uhabits.ui.habits.show.views.charts.ScoreChart
android:id="@+id/scoreView"
android:layout_width="match_parent"
android:layout_height="220dp"
android:layout_below="@id/title"/>
</RelativeLayout>
</merge>

@ -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/best_streaks"/>
<org.isoron.uhabits.ui.habits.show.views.charts.StreakChart
android:id="@+id/streakChart"
android:layout_width="match_parent"
android:layout_height="200dp"/>
</merge>
Loading…
Cancel
Save