diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/OverviewCardTest.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/OverviewCardTest.java deleted file mode 100644 index 9afe0014d..000000000 --- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/OverviewCardTest.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2016 Álinson Santos Xavier - * - * 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 . - */ - -package org.isoron.uhabits.activities.habits.show.views; - -import androidx.test.filters.*; -import androidx.test.runner.*; -import android.view.*; - -import androidx.test.ext.junit.runners.AndroidJUnit4; - -import org.isoron.uhabits.*; -import org.isoron.uhabits.core.models.*; -import org.junit.*; -import org.junit.runner.*; - -@RunWith(AndroidJUnit4.class) -@MediumTest -public class OverviewCardTest extends BaseViewTest -{ - public static final String PATH = "habits/show/OverviewCard/"; - - private OverviewCard view; - - private Habit habit; - - @Before - @Override - public void setUp() - { - super.setUp(); - habit = fixtures.createLongHabit(); - - view = (OverviewCard) LayoutInflater - .from(targetContext) - .inflate(R.layout.show_habit, null) - .findViewById(R.id.overviewCard); - - view.setHabit(habit); - view.refreshData(); - measureView(view, 800, 300); - } - - @Test - public void testRender() throws Exception - { - assertRenders(view, PATH + "render.png"); - } -} diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/OverviewCardTest.kt b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/OverviewCardTest.kt new file mode 100644 index 000000000..dba789cb6 --- /dev/null +++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/OverviewCardTest.kt @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2016 Álinson Santos Xavier + * + * 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 . + */ +package org.isoron.uhabits.activities.habits.show.views + +import android.view.* +import androidx.test.ext.junit.runners.* +import androidx.test.filters.* +import org.isoron.uhabits.* +import org.isoron.uhabits.activities.habits.show.* +import org.isoron.uhabits.core.models.* +import org.junit.* +import org.junit.runner.* + +@RunWith(AndroidJUnit4::class) +@MediumTest +class OverviewCardTest : BaseViewTest() { + val PATH = "habits/show/OverviewCard/" + private lateinit var view: OverviewCard + + @Before + override fun setUp() { + super.setUp() + view = LayoutInflater + .from(targetContext) + .inflate(R.layout.show_habit, null) + .findViewById(R.id.overviewCard) as OverviewCard + view.onData(ShowHabitViewModel( + scoreToday = 0.74f, + scoreMonthDiff = 0.23f, + scoreYearDiff = 0.74f, + totalCount = 44, + color = PaletteColor(7), + )) + measureView(view, 800f, 300f) + } + + @Test + fun testRender() { + assertRenders(view, PATH + "render.png") + } +} \ No newline at end of file diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitPresenter.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitPresenter.kt index 67b97de53..a640ed554 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitPresenter.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitPresenter.kt @@ -21,6 +21,7 @@ package org.isoron.uhabits.activities.habits.show import org.isoron.androidbase.activities.* import org.isoron.uhabits.core.models.* +import org.isoron.uhabits.core.utils.* import javax.inject.* @ActivityScope @@ -30,10 +31,24 @@ class ShowHabitPresenter ) { private val listeners = mutableListOf() - private fun build() = ShowHabitViewModel( - title = habit.name, - isNumerical = habit.isNumerical, - ) + private fun build(): ShowHabitViewModel { + val scores = habit.scores + val today = DateUtils.getTodayWithOffset() + val lastMonth = today.minus(30) + val lastYear = today.minus(365) + val scoreToday = scores.todayValue.toFloat() + val scoreLastMonth = scores.getValue(lastMonth).toFloat() + val scoreLastYear = scores.getValue(lastYear).toFloat() + return ShowHabitViewModel( + title = habit.name, + color = habit.color, + isNumerical = habit.isNumerical, + scoreToday = scoreToday, + scoreMonthDiff = scoreToday - scoreLastMonth, + scoreYearDiff = scoreToday - scoreLastYear, + totalCount = habit.repetitions.totalCount, + ) + } fun addListener(listener: Listener) { listeners.add(listener) diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitRootView.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitRootView.kt index 417aacf73..28cd32010 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitRootView.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitRootView.kt @@ -44,9 +44,10 @@ class ShowHabitRootView addView(binding.root) displayHomeAsUp = true + binding.overviewCard.presenter = presenter + binding.subtitleCard.habit = habit binding.notesCard.habit = habit - binding.overviewCard.habit = habit binding.scoreCard.habit = habit binding.historyCard.habit = habit binding.streakCard.habit = habit diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitViewModel.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitViewModel.kt index b915bcb6a..ba5796e00 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitViewModel.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitViewModel.kt @@ -19,7 +19,14 @@ package org.isoron.uhabits.activities.habits.show +import org.isoron.uhabits.core.models.* + data class ShowHabitViewModel( - val title: String, - val isNumerical: Boolean, + val title: String = "", + val isNumerical: Boolean = false, + val scoreToday: Float = 0f, + val scoreMonthDiff: Float = 0f, + val scoreYearDiff: Float = 0f, + val totalCount: Long = 0L, + val color: PaletteColor = PaletteColor(1), ) \ No newline at end of file diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/OverviewCard.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/OverviewCard.java deleted file mode 100644 index 4a0e6b106..000000000 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/OverviewCard.java +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright (C) 2016 Álinson Santos Xavier - * - * 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 . - */ - -package org.isoron.uhabits.activities.habits.show.views; - -import android.content.*; -import android.util.*; -import android.widget.*; - -import androidx.annotation.NonNull; - -import org.isoron.androidbase.utils.*; -import org.isoron.uhabits.R; -import org.isoron.uhabits.activities.common.views.*; -import org.isoron.uhabits.core.models.*; -import org.isoron.uhabits.core.tasks.*; -import org.isoron.uhabits.core.utils.*; -import org.isoron.uhabits.utils.*; - -import butterknife.*; - -public class OverviewCard extends HabitCard -{ - @NonNull - private Cache cache; - - @BindView(R.id.scoreRing) - RingView scoreRing; - - @BindView(R.id.scoreLabel) - TextView scoreLabel; - - @BindView(R.id.monthDiffLabel) - TextView monthDiffLabel; - - @BindView(R.id.yearDiffLabel) - TextView yearDiffLabel; - - @BindView(R.id.totalCountLabel) - TextView totalCountLabel; - - @BindView(R.id.title) - TextView title; - - private int color; - - public OverviewCard(Context context) - { - super(context); - init(); - } - - public OverviewCard(Context context, AttributeSet attrs) - { - super(context, attrs); - init(); - } - - private String formatPercentageDiff(float percentageDiff) - { - return String.format("%s%.0f%%", (percentageDiff >= 0 ? "+" : "\u2212"), - Math.abs(percentageDiff) * 100); - } - - private void init() - { - inflate(getContext(), R.layout.show_habit_overview, this); - ButterKnife.bind(this); - cache = new Cache(); - if (isInEditMode()) initEditMode(); - } - - private void initEditMode() - { - color = PaletteUtils.getAndroidTestColor(1); - cache.todayScore = 0.6f; - cache.lastMonthScore = 0.42f; - cache.lastYearScore = 0.75f; - refreshColors(); - refreshScore(); - } - - private void refreshColors() - { - scoreRing.setColor(color); - scoreLabel.setTextColor(color); - title.setTextColor(color); - } - - private void refreshScore() - { - float todayPercentage = cache.todayScore; - float monthDiff = todayPercentage - cache.lastMonthScore; - float yearDiff = todayPercentage - cache.lastYearScore; - - scoreRing.setPercentage(todayPercentage); - scoreLabel.setText(String.format("%.0f%%", todayPercentage * 100)); - - monthDiffLabel.setText(formatPercentageDiff(monthDiff)); - yearDiffLabel.setText(formatPercentageDiff(yearDiff)); - totalCountLabel.setText(String.valueOf(cache.totalCount)); - - StyledResources res = new StyledResources(getContext()); - int inactiveColor = res.getColor(R.attr.mediumContrastTextColor); - - monthDiffLabel.setTextColor(monthDiff >= 0 ? color : inactiveColor); - yearDiffLabel.setTextColor(yearDiff >= 0 ? color : inactiveColor); - totalCountLabel.setTextColor(yearDiff >= 0 ? color : inactiveColor); - - postInvalidate(); - } - - private class Cache - { - float todayScore; - - float lastMonthScore; - - float lastYearScore; - - long totalCount; - } - - @Override - protected Task createRefreshTask() - { - return new RefreshTask(); - } - - private class RefreshTask extends CancelableTask - { - @Override - public void doInBackground() - { - if (isCanceled()) return; - Habit habit = getHabit(); - - ScoreList scores = habit.getScores(); - - Timestamp today = DateUtils.getTodayWithOffset(); - Timestamp lastMonth = today.minus(30); - Timestamp lastYear = today.minus(365); - - cache.todayScore = (float) scores.getTodayValue(); - cache.lastMonthScore = (float) scores.getValue(lastMonth); - cache.lastYearScore = (float) scores.getValue(lastYear); - cache.totalCount = habit.getRepetitions().getTotalCount(); - } - - @Override - public void onPostExecute() - { - if (isCanceled()) return; - refreshScore(); - } - - @Override - public void onPreExecute() - { - color = PaletteUtilsKt.toThemedAndroidColor(getHabit().getColor(), getContext()); - refreshColors(); - } - } -} diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/OverviewCard.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/OverviewCard.kt new file mode 100644 index 000000000..e12c777f4 --- /dev/null +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/OverviewCard.kt @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2016 Álinson Santos Xavier + * + * 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 . + */ +package org.isoron.uhabits.activities.habits.show.views + +import android.content.* +import android.util.* +import android.view.* +import android.widget.* +import org.isoron.androidbase.utils.* +import org.isoron.uhabits.* +import org.isoron.uhabits.activities.habits.show.* +import org.isoron.uhabits.databinding.* +import org.isoron.uhabits.utils.* + +class OverviewCard : LinearLayout, ShowHabitPresenter.Listener { + + private val binding = ShowHabitOverviewBinding.inflate(LayoutInflater.from(context), this) + lateinit var presenter: ShowHabitPresenter + + constructor(context: Context) : super(context) { + init() + } + + constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { + init() + } + + private fun init() { + if (isInEditMode) initEditMode() + } + + override fun onAttachedToWindow() { + super.onAttachedToWindow() + presenter.addListener(this) + presenter.requestData(this) + } + + override fun onDetachedFromWindow() { + presenter.removeListener(this) + super.onDetachedFromWindow() + } + + private fun initEditMode() { + onData(ShowHabitViewModel( + scoreToday = 0.6f, + scoreMonthDiff = 0.42f, + scoreYearDiff = 0.75f, + )) + } + + private fun formatPercentageDiff(percentageDiff: Float): String { + return String.format("%s%.0f%%", if (percentageDiff >= 0) "+" else "\u2212", + Math.abs(percentageDiff) * 100) + } + + override fun onData(data: ShowHabitViewModel) { + val androidColor = data.color.toThemedAndroidColor(context) + val res = StyledResources(context) + val inactiveColor = res.getColor(R.attr.mediumContrastTextColor) + binding.monthDiffLabel.setTextColor(if (data.scoreMonthDiff >= 0) androidColor else inactiveColor) + binding.monthDiffLabel.text = formatPercentageDiff(data.scoreMonthDiff) + binding.scoreLabel.setTextColor(androidColor) + binding.scoreLabel.text = String.format("%.0f%%", data.scoreToday * 100) + binding.scoreRing.color = androidColor + binding.scoreRing.percentage = data.scoreToday + binding.title.setTextColor(androidColor) + binding.totalCountLabel.setTextColor(androidColor) + binding.totalCountLabel.text = data.totalCount.toString() + binding.yearDiffLabel.setTextColor(if (data.scoreYearDiff >= 0) androidColor else inactiveColor) + binding.yearDiffLabel.text = formatPercentageDiff(data.scoreYearDiff) + postInvalidate() + } +} \ No newline at end of file diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/utils/AttributeSetUtils.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/utils/AttributeSetUtils.java index 35a6553ac..1faec5e03 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/utils/AttributeSetUtils.java +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/utils/AttributeSetUtils.java @@ -71,9 +71,14 @@ public class AttributeSetUtils @NonNull String name, float defaultValue) { - String number = getAttribute(context, attrs, name, null); - if (number != null) return Float.parseFloat(number); - else return defaultValue; + try + { + String number = getAttribute(context, attrs, name, null); + if (number != null) return Float.parseFloat(number); + else return defaultValue; + } catch(NumberFormatException e) { + return defaultValue; + } } public static int getIntAttribute(@NonNull Context context,