Merge branch 'hotfix/2.0.3'
10
CHANGELOG.md
@@ -1,5 +1,15 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [2.0.3] - 2021-08-21
|
||||||
|
### Fixed
|
||||||
|
- Improve automatic checkmarks for monthly habits (@iSoron, #947)
|
||||||
|
- Fix small theme issues (@iSoron)
|
||||||
|
- Fix ANR on some Samsung phones (@iSoron, #962)
|
||||||
|
- Fix dates before the year 2000 (@iSoron, #967)
|
||||||
|
- Fix notification adding checkmarks to the wrong day (@hiqua, #969)
|
||||||
|
- Fix crashes in widgets (@hiqua, @iSoron, #907, #966, #965)
|
||||||
|
- Fix crash when moving habits (@hiqua, #968)
|
||||||
|
|
||||||
## [2.0.2] - 2021-05-23
|
## [2.0.2] - 2021-05-23
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|||||||
@@ -37,8 +37,8 @@ android {
|
|||||||
compileSdkVersion(30)
|
compileSdkVersion(30)
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
versionCode(20002)
|
versionCode(20003)
|
||||||
versionName("2.0.2")
|
versionName("2.0.3")
|
||||||
minSdkVersion(23)
|
minSdkVersion(23)
|
||||||
targetSdkVersion(30)
|
targetSdkVersion(30)
|
||||||
applicationId("org.isoron.uhabits")
|
applicationId("org.isoron.uhabits")
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 7.6 KiB |
|
Before Width: | Height: | Size: 8.7 KiB After Width: | Height: | Size: 8.9 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
@@ -23,6 +23,7 @@ import androidx.test.filters.MediumTest
|
|||||||
import org.isoron.uhabits.BaseViewTest
|
import org.isoron.uhabits.BaseViewTest
|
||||||
import org.isoron.uhabits.core.models.Habit
|
import org.isoron.uhabits.core.models.Habit
|
||||||
import org.isoron.uhabits.core.ui.screens.habits.show.views.ScoreCardPresenter.Companion.buildState
|
import org.isoron.uhabits.core.ui.screens.habits.show.views.ScoreCardPresenter.Companion.buildState
|
||||||
|
import org.isoron.uhabits.core.ui.views.LightTheme
|
||||||
import org.isoron.uhabits.utils.toFixedAndroidColor
|
import org.isoron.uhabits.utils.toFixedAndroidColor
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
@@ -39,7 +40,12 @@ class ScoreChartTest : BaseViewTest() {
|
|||||||
super.setUp()
|
super.setUp()
|
||||||
fixtures.purgeHabits(habitList)
|
fixtures.purgeHabits(habitList)
|
||||||
habit = fixtures.createLongHabit()
|
habit = fixtures.createLongHabit()
|
||||||
val state = buildState(habit, prefs.firstWeekdayInt, 0)
|
val state = buildState(
|
||||||
|
habit = habit,
|
||||||
|
firstWeekday = prefs.firstWeekdayInt,
|
||||||
|
spinnerPosition = 0,
|
||||||
|
theme = LightTheme(),
|
||||||
|
)
|
||||||
view = ScoreChart(targetContext).apply {
|
view = ScoreChart(targetContext).apply {
|
||||||
setScores(state.scores)
|
setScores(state.scores)
|
||||||
setColor(state.color.toFixedAndroidColor())
|
setColor(state.color.toFixedAndroidColor())
|
||||||
@@ -72,7 +78,7 @@ class ScoreChartTest : BaseViewTest() {
|
|||||||
@Test
|
@Test
|
||||||
@Throws(Throwable::class)
|
@Throws(Throwable::class)
|
||||||
fun testRender_withMonthlyBucket() {
|
fun testRender_withMonthlyBucket() {
|
||||||
val (scores, bucketSize) = buildState(habit, prefs.firstWeekdayInt, 2)
|
val (scores, bucketSize) = buildState(habit, prefs.firstWeekdayInt, 2, LightTheme())
|
||||||
view.setScores(scores)
|
view.setScores(scores)
|
||||||
view.setBucketSize(bucketSize)
|
view.setBucketSize(bucketSize)
|
||||||
view.invalidate()
|
view.invalidate()
|
||||||
@@ -89,7 +95,7 @@ class ScoreChartTest : BaseViewTest() {
|
|||||||
@Test
|
@Test
|
||||||
@Throws(Throwable::class)
|
@Throws(Throwable::class)
|
||||||
fun testRender_withYearlyBucket() {
|
fun testRender_withYearlyBucket() {
|
||||||
val state = buildState(habit, prefs.firstWeekdayInt, 4)
|
val state = buildState(habit, prefs.firstWeekdayInt, 4, LightTheme())
|
||||||
view.setScores(state.scores)
|
view.setScores(state.scores)
|
||||||
view.setBucketSize(state.bucketSize)
|
view.setBucketSize(state.bucketSize)
|
||||||
view.invalidate()
|
view.invalidate()
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import androidx.test.filters.MediumTest
|
|||||||
import org.isoron.uhabits.BaseViewTest
|
import org.isoron.uhabits.BaseViewTest
|
||||||
import org.isoron.uhabits.R
|
import org.isoron.uhabits.R
|
||||||
import org.isoron.uhabits.core.ui.screens.habits.show.views.FrequencyCardPresenter
|
import org.isoron.uhabits.core.ui.screens.habits.show.views.FrequencyCardPresenter
|
||||||
|
import org.isoron.uhabits.core.ui.views.LightTheme
|
||||||
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,7 +44,13 @@ class FrequencyCardViewTest : BaseViewTest() {
|
|||||||
.from(targetContext)
|
.from(targetContext)
|
||||||
.inflate(R.layout.show_habit, null)
|
.inflate(R.layout.show_habit, null)
|
||||||
.findViewById<View>(R.id.frequencyCard) as FrequencyCardView
|
.findViewById<View>(R.id.frequencyCard) as FrequencyCardView
|
||||||
view.setState(FrequencyCardPresenter.buildState(habit = habit, firstWeekday = 0))
|
view.setState(
|
||||||
|
FrequencyCardPresenter.buildState(
|
||||||
|
habit = habit,
|
||||||
|
firstWeekday = 0,
|
||||||
|
theme = LightTheme(),
|
||||||
|
)
|
||||||
|
)
|
||||||
measureView(view, 800f, 600f)
|
measureView(view, 800f, 600f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import org.isoron.uhabits.BaseViewTest
|
|||||||
import org.isoron.uhabits.R
|
import org.isoron.uhabits.R
|
||||||
import org.isoron.uhabits.core.models.PaletteColor
|
import org.isoron.uhabits.core.models.PaletteColor
|
||||||
import org.isoron.uhabits.core.ui.screens.habits.show.views.OverviewCardState
|
import org.isoron.uhabits.core.ui.screens.habits.show.views.OverviewCardState
|
||||||
|
import org.isoron.uhabits.core.ui.views.LightTheme
|
||||||
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
|
||||||
@@ -50,6 +51,7 @@ class OverviewCardViewTest : BaseViewTest() {
|
|||||||
scoreYearDiff = 0.74f,
|
scoreYearDiff = 0.74f,
|
||||||
totalCount = 44,
|
totalCount = 44,
|
||||||
color = PaletteColor(7),
|
color = PaletteColor(7),
|
||||||
|
theme = LightTheme(),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
measureView(view, 800f, 300f)
|
measureView(view, 800f, 300f)
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import androidx.test.filters.MediumTest
|
|||||||
import org.isoron.uhabits.BaseViewTest
|
import org.isoron.uhabits.BaseViewTest
|
||||||
import org.isoron.uhabits.R
|
import org.isoron.uhabits.R
|
||||||
import org.isoron.uhabits.core.ui.screens.habits.show.views.ScoreCardPresenter.Companion.buildState
|
import org.isoron.uhabits.core.ui.screens.habits.show.views.ScoreCardPresenter.Companion.buildState
|
||||||
|
import org.isoron.uhabits.core.ui.views.LightTheme
|
||||||
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,7 +44,14 @@ class ScoreCardViewTest : BaseViewTest() {
|
|||||||
.from(targetContext)
|
.from(targetContext)
|
||||||
.inflate(R.layout.show_habit, null)
|
.inflate(R.layout.show_habit, null)
|
||||||
.findViewById<View>(R.id.scoreCard) as ScoreCardView
|
.findViewById<View>(R.id.scoreCard) as ScoreCardView
|
||||||
view.setState(buildState(habit = habit, firstWeekday = 0, spinnerPosition = 0))
|
view.setState(
|
||||||
|
buildState(
|
||||||
|
habit = habit,
|
||||||
|
firstWeekday = 0,
|
||||||
|
spinnerPosition = 0,
|
||||||
|
theme = LightTheme(),
|
||||||
|
)
|
||||||
|
)
|
||||||
measureView(view, 800f, 600f)
|
measureView(view, 800f, 600f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import androidx.test.filters.MediumTest
|
|||||||
import org.isoron.uhabits.BaseViewTest
|
import org.isoron.uhabits.BaseViewTest
|
||||||
import org.isoron.uhabits.R
|
import org.isoron.uhabits.R
|
||||||
import org.isoron.uhabits.core.ui.screens.habits.show.views.StreakCardState
|
import org.isoron.uhabits.core.ui.screens.habits.show.views.StreakCardState
|
||||||
|
import org.isoron.uhabits.core.ui.views.LightTheme
|
||||||
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
|
||||||
@@ -47,6 +48,7 @@ class StreakCardViewTest : BaseViewTest() {
|
|||||||
StreakCardState(
|
StreakCardState(
|
||||||
bestStreaks = habit.streaks.getBest(10),
|
bestStreaks = habit.streaks.getBest(10),
|
||||||
color = habit.color,
|
color = habit.color,
|
||||||
|
theme = LightTheme(),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
measureView(view, 800f, 600f)
|
measureView(view, 800f, 600f)
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import org.isoron.uhabits.core.models.PaletteColor
|
|||||||
import org.isoron.uhabits.core.models.Reminder
|
import org.isoron.uhabits.core.models.Reminder
|
||||||
import org.isoron.uhabits.core.models.WeekdayList.Companion.EVERY_DAY
|
import org.isoron.uhabits.core.models.WeekdayList.Companion.EVERY_DAY
|
||||||
import org.isoron.uhabits.core.ui.screens.habits.show.views.SubtitleCardState
|
import org.isoron.uhabits.core.ui.screens.habits.show.views.SubtitleCardState
|
||||||
|
import org.isoron.uhabits.core.ui.views.LightTheme
|
||||||
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
|
||||||
@@ -54,6 +55,7 @@ class SubtitleCardViewTest : BaseViewTest() {
|
|||||||
reminder = Reminder(8, 30, EVERY_DAY),
|
reminder = Reminder(8, 30, EVERY_DAY),
|
||||||
unit = "",
|
unit = "",
|
||||||
targetValue = 0.0,
|
targetValue = 0.0,
|
||||||
|
theme = LightTheme(),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
measureView(view, 800f, 200f)
|
measureView(view, 800f, 200f)
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ class AndroidCanvas : Canvas {
|
|||||||
var innerDensity = 1.0
|
var innerDensity = 1.0
|
||||||
var innerWidth = 0
|
var innerWidth = 0
|
||||||
var innerHeight = 0
|
var innerHeight = 0
|
||||||
|
var mHeight = 15
|
||||||
|
|
||||||
var paint = Paint().apply {
|
var paint = Paint().apply {
|
||||||
isAntiAlias = true
|
isAntiAlias = true
|
||||||
@@ -64,11 +65,10 @@ class AndroidCanvas : Canvas {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun drawText(text: String, x: Double, y: Double) {
|
override fun drawText(text: String, x: Double, y: Double) {
|
||||||
textPaint.getTextBounds(text, 0, text.length, textBounds)
|
|
||||||
innerCanvas.drawText(
|
innerCanvas.drawText(
|
||||||
text,
|
text,
|
||||||
x.toDp(),
|
x.toDp(),
|
||||||
y.toDp() - textBounds.exactCenterY(),
|
y.toDp() + 0.6f * mHeight,
|
||||||
textPaint,
|
textPaint,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -126,10 +126,17 @@ class AndroidCanvas : Canvas {
|
|||||||
Font.BOLD -> Typeface.DEFAULT_BOLD
|
Font.BOLD -> Typeface.DEFAULT_BOLD
|
||||||
Font.FONT_AWESOME -> getFontAwesome(context)
|
Font.FONT_AWESOME -> getFontAwesome(context)
|
||||||
}
|
}
|
||||||
|
updateMHeight()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setFontSize(size: Double) {
|
override fun setFontSize(size: Double) {
|
||||||
textPaint.textSize = size.toDp()
|
textPaint.textSize = size.toDp()
|
||||||
|
updateMHeight()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateMHeight() {
|
||||||
|
textPaint.getTextBounds("m", 0, 1, textBounds)
|
||||||
|
mHeight = textBounds.height()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setStrokeWidth(size: Double) {
|
override fun setStrokeWidth(size: Double) {
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import org.isoron.uhabits.core.preferences.Preferences
|
|||||||
import org.isoron.uhabits.core.ui.ThemeSwitcher
|
import org.isoron.uhabits.core.ui.ThemeSwitcher
|
||||||
import org.isoron.uhabits.core.ui.views.DarkTheme
|
import org.isoron.uhabits.core.ui.views.DarkTheme
|
||||||
import org.isoron.uhabits.core.ui.views.LightTheme
|
import org.isoron.uhabits.core.ui.views.LightTheme
|
||||||
|
import org.isoron.uhabits.core.ui.views.PureBlackTheme
|
||||||
import org.isoron.uhabits.core.ui.views.Theme
|
import org.isoron.uhabits.core.ui.views.Theme
|
||||||
import org.isoron.uhabits.inject.ActivityContext
|
import org.isoron.uhabits.inject.ActivityContext
|
||||||
import org.isoron.uhabits.inject.ActivityScope
|
import org.isoron.uhabits.inject.ActivityScope
|
||||||
@@ -66,7 +67,7 @@ constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun applyPureBlackTheme() {
|
override fun applyPureBlackTheme() {
|
||||||
currentTheme = DarkTheme()
|
currentTheme = PureBlackTheme()
|
||||||
context.setTheme(R.style.AppBaseThemeDark_PureBlack)
|
context.setTheme(R.style.AppBaseThemeDark_PureBlack)
|
||||||
(context as Activity).window.navigationBarColor =
|
(context as Activity).window.navigationBarColor =
|
||||||
ContextCompat.getColor(context, R.color.black)
|
ContextCompat.getColor(context, R.color.black)
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import org.isoron.uhabits.BuildConfig
|
|||||||
import org.isoron.uhabits.R
|
import org.isoron.uhabits.R
|
||||||
import org.isoron.uhabits.core.models.PaletteColor
|
import org.isoron.uhabits.core.models.PaletteColor
|
||||||
import org.isoron.uhabits.databinding.AboutBinding
|
import org.isoron.uhabits.databinding.AboutBinding
|
||||||
|
import org.isoron.uhabits.utils.currentTheme
|
||||||
import org.isoron.uhabits.utils.setupToolbar
|
import org.isoron.uhabits.utils.setupToolbar
|
||||||
|
|
||||||
@SuppressLint("ViewConstructor")
|
@SuppressLint("ViewConstructor")
|
||||||
@@ -41,7 +42,8 @@ class AboutView(
|
|||||||
setupToolbar(
|
setupToolbar(
|
||||||
toolbar = binding.toolbar,
|
toolbar = binding.toolbar,
|
||||||
color = PaletteColor(11),
|
color = PaletteColor(11),
|
||||||
title = resources.getString(R.string.about)
|
title = resources.getString(R.string.about),
|
||||||
|
theme = currentTheme(),
|
||||||
)
|
)
|
||||||
val version = resources.getString(R.string.version_n)
|
val version = resources.getString(R.string.version_n)
|
||||||
binding.tvContributors.setOnClickListener { screen.showCodeContributorsWebsite() }
|
binding.tvContributors.setOnClickListener { screen.showCodeContributorsWebsite() }
|
||||||
|
|||||||
@@ -19,20 +19,21 @@
|
|||||||
package org.isoron.uhabits.activities.common.dialogs
|
package org.isoron.uhabits.activities.common.dialogs
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import org.isoron.platform.gui.toInt
|
||||||
import org.isoron.uhabits.R
|
import org.isoron.uhabits.R
|
||||||
import org.isoron.uhabits.core.models.PaletteColor
|
import org.isoron.uhabits.core.models.PaletteColor
|
||||||
|
import org.isoron.uhabits.core.ui.views.Theme
|
||||||
import org.isoron.uhabits.inject.ActivityContext
|
import org.isoron.uhabits.inject.ActivityContext
|
||||||
import org.isoron.uhabits.inject.ActivityScope
|
import org.isoron.uhabits.inject.ActivityScope
|
||||||
import org.isoron.uhabits.utils.StyledResources
|
import org.isoron.uhabits.utils.StyledResources
|
||||||
import org.isoron.uhabits.utils.toThemedAndroidColor
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ActivityScope
|
@ActivityScope
|
||||||
class ColorPickerDialogFactory @Inject constructor(@param:ActivityContext private val context: Context) {
|
class ColorPickerDialogFactory @Inject constructor(@param:ActivityContext private val context: Context) {
|
||||||
fun create(color: PaletteColor): ColorPickerDialog {
|
fun create(color: PaletteColor, theme: Theme): ColorPickerDialog {
|
||||||
val dialog = ColorPickerDialog()
|
val dialog = ColorPickerDialog()
|
||||||
val res = StyledResources(context)
|
val res = StyledResources(context)
|
||||||
val androidColor = color.toThemedAndroidColor(context)
|
val androidColor = theme.color(color).toInt()
|
||||||
dialog.initialize(
|
dialog.initialize(
|
||||||
R.string.color_picker_default_title,
|
R.string.color_picker_default_title,
|
||||||
res.getPalette(),
|
res.getPalette(),
|
||||||
|
|||||||
@@ -160,26 +160,28 @@ class FrequencyPickerDialog(
|
|||||||
|
|
||||||
private fun populateViews() {
|
private fun populateViews() {
|
||||||
uncheckAll()
|
uncheckAll()
|
||||||
if (freqNumerator == 1) {
|
if (freqDenominator == 30 || freqDenominator == 31) {
|
||||||
if (freqDenominator == 1) {
|
contentView.xTimesPerMonthRadioButton.isChecked = true
|
||||||
contentView.everyDayRadioButton.isChecked = true
|
contentView.xTimesPerMonthTextView.setText(freqNumerator.toString())
|
||||||
} else {
|
focus(contentView.xTimesPerMonthTextView)
|
||||||
contentView.everyXDaysRadioButton.isChecked = true
|
|
||||||
contentView.everyXDaysTextView.setText(freqDenominator.toString())
|
|
||||||
focus(contentView.everyXDaysTextView)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (freqDenominator == 7) {
|
if (freqNumerator == 1) {
|
||||||
contentView.xTimesPerWeekRadioButton.isChecked = true
|
if (freqDenominator == 1) {
|
||||||
contentView.xTimesPerWeekTextView.setText(freqNumerator.toString())
|
contentView.everyDayRadioButton.isChecked = true
|
||||||
focus(contentView.xTimesPerWeekTextView)
|
} else {
|
||||||
} else if (freqDenominator == 30 || freqDenominator == 31) {
|
contentView.everyXDaysRadioButton.isChecked = true
|
||||||
contentView.xTimesPerMonthRadioButton.isChecked = true
|
contentView.everyXDaysTextView.setText(freqDenominator.toString())
|
||||||
contentView.xTimesPerMonthTextView.setText(freqNumerator.toString())
|
focus(contentView.everyXDaysTextView)
|
||||||
focus(contentView.xTimesPerMonthTextView)
|
}
|
||||||
} else {
|
} else {
|
||||||
Log.w("FrequencyPickerDialog", "Unknown frequency: $freqNumerator/$freqDenominator")
|
if (freqDenominator == 7) {
|
||||||
contentView.everyDayRadioButton.isChecked = true
|
contentView.xTimesPerWeekRadioButton.isChecked = true
|
||||||
|
contentView.xTimesPerWeekTextView.setText(freqNumerator.toString())
|
||||||
|
focus(contentView.xTimesPerWeekTextView)
|
||||||
|
} else {
|
||||||
|
Log.w("FrequencyPickerDialog", "Unknown frequency: $freqNumerator/$freqDenominator")
|
||||||
|
contentView.everyDayRadioButton.isChecked = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ package org.isoron.uhabits.activities.habits.edit
|
|||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.res.ColorStateList
|
import android.content.res.ColorStateList
|
||||||
|
import android.content.res.Resources
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.text.Html
|
import android.text.Html
|
||||||
@@ -39,6 +40,7 @@ import kotlinx.android.synthetic.main.activity_edit_habit.notesInput
|
|||||||
import kotlinx.android.synthetic.main.activity_edit_habit.questionInput
|
import kotlinx.android.synthetic.main.activity_edit_habit.questionInput
|
||||||
import kotlinx.android.synthetic.main.activity_edit_habit.targetInput
|
import kotlinx.android.synthetic.main.activity_edit_habit.targetInput
|
||||||
import kotlinx.android.synthetic.main.activity_edit_habit.unitInput
|
import kotlinx.android.synthetic.main.activity_edit_habit.unitInput
|
||||||
|
import org.isoron.platform.gui.toInt
|
||||||
import org.isoron.uhabits.HabitsApplication
|
import org.isoron.uhabits.HabitsApplication
|
||||||
import org.isoron.uhabits.R
|
import org.isoron.uhabits.R
|
||||||
import org.isoron.uhabits.activities.AndroidThemeSwitcher
|
import org.isoron.uhabits.activities.AndroidThemeSwitcher
|
||||||
@@ -57,7 +59,16 @@ import org.isoron.uhabits.databinding.ActivityEditHabitBinding
|
|||||||
import org.isoron.uhabits.utils.ColorUtils
|
import org.isoron.uhabits.utils.ColorUtils
|
||||||
import org.isoron.uhabits.utils.formatTime
|
import org.isoron.uhabits.utils.formatTime
|
||||||
import org.isoron.uhabits.utils.toFormattedString
|
import org.isoron.uhabits.utils.toFormattedString
|
||||||
import org.isoron.uhabits.utils.toThemedAndroidColor
|
|
||||||
|
fun formatFrequency(freqNum: Int, freqDen: Int, resources: Resources) = when {
|
||||||
|
freqNum == 1 && (freqDen == 30 || freqDen == 31) -> resources.getString(R.string.every_month)
|
||||||
|
freqDen == 30 || freqDen == 31 -> resources.getString(R.string.x_times_per_month, freqNum)
|
||||||
|
freqNum == 1 && freqDen == 1 -> resources.getString(R.string.every_day)
|
||||||
|
freqNum == 1 && freqDen == 7 -> resources.getString(R.string.every_week)
|
||||||
|
freqNum == 1 && freqDen > 1 -> resources.getString(R.string.every_x_days, freqDen)
|
||||||
|
freqDen == 7 -> resources.getString(R.string.x_times_per_week, freqNum)
|
||||||
|
else -> "$freqNum/$freqDen"
|
||||||
|
}
|
||||||
|
|
||||||
class EditHabitActivity : AppCompatActivity() {
|
class EditHabitActivity : AppCompatActivity() {
|
||||||
|
|
||||||
@@ -137,7 +148,7 @@ class EditHabitActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
val colorPickerDialogFactory = ColorPickerDialogFactory(this)
|
val colorPickerDialogFactory = ColorPickerDialogFactory(this)
|
||||||
binding.colorButton.setOnClickListener {
|
binding.colorButton.setOnClickListener {
|
||||||
val dialog = colorPickerDialogFactory.create(color)
|
val dialog = colorPickerDialogFactory.create(color, themeSwitcher.currentTheme)
|
||||||
dialog.setListener { paletteColor ->
|
dialog.setListener { paletteColor ->
|
||||||
this.color = paletteColor
|
this.color = paletteColor
|
||||||
updateColors()
|
updateColors()
|
||||||
@@ -299,14 +310,7 @@ class EditHabitActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
@SuppressLint("StringFormatMatches")
|
@SuppressLint("StringFormatMatches")
|
||||||
private fun populateFrequency() {
|
private fun populateFrequency() {
|
||||||
binding.booleanFrequencyPicker.text = when {
|
binding.booleanFrequencyPicker.text = formatFrequency(freqNum, freqDen, resources)
|
||||||
freqNum == 1 && freqDen == 1 -> getString(R.string.every_day)
|
|
||||||
freqNum == 1 && freqDen == 7 -> getString(R.string.every_week)
|
|
||||||
freqNum == 1 && freqDen > 1 -> getString(R.string.every_x_days, freqDen)
|
|
||||||
freqDen == 7 -> getString(R.string.x_times_per_week, freqNum)
|
|
||||||
freqDen == 30 || freqDen == 31 -> getString(R.string.x_times_per_month, freqNum)
|
|
||||||
else -> "$freqNum/$freqDen"
|
|
||||||
}
|
|
||||||
binding.numericalFrequencyPicker.text = when (freqDen) {
|
binding.numericalFrequencyPicker.text = when (freqDen) {
|
||||||
1 -> getString(R.string.every_day)
|
1 -> getString(R.string.every_day)
|
||||||
7 -> getString(R.string.every_week)
|
7 -> getString(R.string.every_week)
|
||||||
@@ -316,7 +320,7 @@ class EditHabitActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun updateColors() {
|
private fun updateColors() {
|
||||||
androidColor = color.toThemedAndroidColor(this)
|
androidColor = themeSwitcher.currentTheme.color(color).toInt()
|
||||||
binding.colorButton.backgroundTintList = ColorStateList.valueOf(androidColor)
|
binding.colorButton.backgroundTintList = ColorStateList.valueOf(androidColor)
|
||||||
if (!themeSwitcher.isNightMode) {
|
if (!themeSwitcher.isNightMode) {
|
||||||
val darkerAndroidColor = ColorUtils.mixColors(Color.BLACK, androidColor, 0.15f)
|
val darkerAndroidColor = ColorUtils.mixColors(Color.BLACK, androidColor, 0.15f)
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ import org.isoron.uhabits.utils.addAtBottom
|
|||||||
import org.isoron.uhabits.utils.addAtTop
|
import org.isoron.uhabits.utils.addAtTop
|
||||||
import org.isoron.uhabits.utils.addBelow
|
import org.isoron.uhabits.utils.addBelow
|
||||||
import org.isoron.uhabits.utils.buildToolbar
|
import org.isoron.uhabits.utils.buildToolbar
|
||||||
|
import org.isoron.uhabits.utils.currentTheme
|
||||||
import org.isoron.uhabits.utils.dim
|
import org.isoron.uhabits.utils.dim
|
||||||
import org.isoron.uhabits.utils.dp
|
import org.isoron.uhabits.utils.dp
|
||||||
import org.isoron.uhabits.utils.setupToolbar
|
import org.isoron.uhabits.utils.setupToolbar
|
||||||
@@ -93,6 +94,7 @@ class ListHabitsRootView @Inject constructor(
|
|||||||
title = resources.getString(R.string.main_activity_title),
|
title = resources.getString(R.string.main_activity_title),
|
||||||
color = PaletteColor(17),
|
color = PaletteColor(17),
|
||||||
displayHomeAsUpEnabled = false,
|
displayHomeAsUpEnabled = false,
|
||||||
|
theme = currentTheme(),
|
||||||
)
|
)
|
||||||
addView(rootView, MATCH_PARENT, MATCH_PARENT)
|
addView(rootView, MATCH_PARENT, MATCH_PARENT)
|
||||||
listAdapter.setListView(listView)
|
listAdapter.setListView(listView)
|
||||||
|
|||||||
@@ -217,7 +217,7 @@ class ListHabitsScreen
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun showColorPicker(defaultColor: PaletteColor, callback: OnColorPickedCallback) {
|
override fun showColorPicker(defaultColor: PaletteColor, callback: OnColorPickedCallback) {
|
||||||
val picker = colorPickerFactory.create(defaultColor)
|
val picker = colorPickerFactory.create(defaultColor, themeSwitcher.currentTheme!!)
|
||||||
picker.setListener(callback)
|
picker.setListener(callback)
|
||||||
picker.show(activity.supportFragmentManager, "picker")
|
picker.show(activity.supportFragmentManager, "picker")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
|
|||||||
import android.widget.FrameLayout
|
import android.widget.FrameLayout
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
import org.isoron.platform.gui.toInt
|
||||||
import org.isoron.uhabits.R
|
import org.isoron.uhabits.R
|
||||||
import org.isoron.uhabits.activities.common.views.RingView
|
import org.isoron.uhabits.activities.common.views.RingView
|
||||||
import org.isoron.uhabits.core.models.Habit
|
import org.isoron.uhabits.core.models.Habit
|
||||||
@@ -41,9 +42,9 @@ import org.isoron.uhabits.core.models.Timestamp
|
|||||||
import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsBehavior
|
import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsBehavior
|
||||||
import org.isoron.uhabits.core.utils.DateUtils
|
import org.isoron.uhabits.core.utils.DateUtils
|
||||||
import org.isoron.uhabits.inject.ActivityContext
|
import org.isoron.uhabits.inject.ActivityContext
|
||||||
|
import org.isoron.uhabits.utils.currentTheme
|
||||||
import org.isoron.uhabits.utils.dp
|
import org.isoron.uhabits.utils.dp
|
||||||
import org.isoron.uhabits.utils.sres
|
import org.isoron.uhabits.utils.sres
|
||||||
import org.isoron.uhabits.utils.toThemedAndroidColor
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class HabitCardViewFactory
|
class HabitCardViewFactory
|
||||||
@@ -213,7 +214,7 @@ class HabitCardView(
|
|||||||
fun getActiveColor(habit: Habit): Int {
|
fun getActiveColor(habit: Habit): Int {
|
||||||
return when (habit.isArchived) {
|
return when (habit.isArchived) {
|
||||||
true -> sres.getColor(R.attr.contrast60)
|
true -> sres.getColor(R.attr.contrast60)
|
||||||
false -> habit.color.toThemedAndroidColor(context)
|
false -> currentTheme().color(habit.color).toInt()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,12 @@ class ShowHabitView(context: Context) : FrameLayout(context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun setState(data: ShowHabitState) {
|
fun setState(data: ShowHabitState) {
|
||||||
setupToolbar(binding.toolbar, title = data.title, color = data.color)
|
setupToolbar(
|
||||||
|
binding.toolbar,
|
||||||
|
title = data.title,
|
||||||
|
color = data.color,
|
||||||
|
theme = data.theme,
|
||||||
|
)
|
||||||
binding.subtitleCard.setState(data.subtitle)
|
binding.subtitleCard.setState(data.subtitle)
|
||||||
binding.overviewCard.setState(data.overview)
|
binding.overviewCard.setState(data.overview)
|
||||||
binding.notesCard.setState(data.notes)
|
binding.notesCard.setState(data.notes)
|
||||||
|
|||||||
@@ -24,12 +24,12 @@ import android.view.LayoutInflater
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.AdapterView
|
import android.widget.AdapterView
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
|
import org.isoron.platform.gui.toInt
|
||||||
import org.isoron.platform.time.JavaLocalDateFormatter
|
import org.isoron.platform.time.JavaLocalDateFormatter
|
||||||
import org.isoron.uhabits.core.ui.screens.habits.show.views.BarCardPresenter
|
import org.isoron.uhabits.core.ui.screens.habits.show.views.BarCardPresenter
|
||||||
import org.isoron.uhabits.core.ui.screens.habits.show.views.BarCardState
|
import org.isoron.uhabits.core.ui.screens.habits.show.views.BarCardState
|
||||||
import org.isoron.uhabits.core.ui.views.BarChart
|
import org.isoron.uhabits.core.ui.views.BarChart
|
||||||
import org.isoron.uhabits.databinding.ShowHabitBarBinding
|
import org.isoron.uhabits.databinding.ShowHabitBarBinding
|
||||||
import org.isoron.uhabits.utils.toThemedAndroidColor
|
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
class BarCardView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
|
class BarCardView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
|
||||||
@@ -37,7 +37,7 @@ class BarCardView(context: Context, attrs: AttributeSet) : LinearLayout(context,
|
|||||||
private var binding = ShowHabitBarBinding.inflate(LayoutInflater.from(context), this)
|
private var binding = ShowHabitBarBinding.inflate(LayoutInflater.from(context), this)
|
||||||
|
|
||||||
fun setState(state: BarCardState) {
|
fun setState(state: BarCardState) {
|
||||||
val androidColor = state.color.toThemedAndroidColor(context)
|
val androidColor = state.theme.color(state.color).toInt()
|
||||||
binding.chart.view = BarChart(state.theme, JavaLocalDateFormatter(Locale.US)).apply {
|
binding.chart.view = BarChart(state.theme, JavaLocalDateFormatter(Locale.US)).apply {
|
||||||
series = mutableListOf(state.entries.map { it.value / 1000.0 })
|
series = mutableListOf(state.entries.map { it.value / 1000.0 })
|
||||||
colors = mutableListOf(theme.color(state.color.paletteIndex))
|
colors = mutableListOf(theme.color(state.color.paletteIndex))
|
||||||
|
|||||||
@@ -22,16 +22,16 @@ import android.content.Context
|
|||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
|
import org.isoron.platform.gui.toInt
|
||||||
import org.isoron.uhabits.core.ui.screens.habits.show.views.FrequencyCardState
|
import org.isoron.uhabits.core.ui.screens.habits.show.views.FrequencyCardState
|
||||||
import org.isoron.uhabits.databinding.ShowHabitFrequencyBinding
|
import org.isoron.uhabits.databinding.ShowHabitFrequencyBinding
|
||||||
import org.isoron.uhabits.utils.toThemedAndroidColor
|
|
||||||
|
|
||||||
class FrequencyCardView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
|
class FrequencyCardView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
|
||||||
|
|
||||||
private var binding = ShowHabitFrequencyBinding.inflate(LayoutInflater.from(context), this)
|
private var binding = ShowHabitFrequencyBinding.inflate(LayoutInflater.from(context), this)
|
||||||
|
|
||||||
fun setState(state: FrequencyCardState) {
|
fun setState(state: FrequencyCardState) {
|
||||||
val androidColor = state.color.toThemedAndroidColor(context)
|
val androidColor = state.theme.color(state.color).toInt()
|
||||||
binding.frequencyChart.setFrequency(state.frequency)
|
binding.frequencyChart.setFrequency(state.frequency)
|
||||||
binding.frequencyChart.setFirstWeekday(state.firstWeekday)
|
binding.frequencyChart.setFirstWeekday(state.firstWeekday)
|
||||||
binding.title.setTextColor(androidColor)
|
binding.title.setTextColor(androidColor)
|
||||||
|
|||||||
@@ -22,12 +22,12 @@ import android.content.Context
|
|||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
|
import org.isoron.platform.gui.toInt
|
||||||
import org.isoron.platform.time.JavaLocalDateFormatter
|
import org.isoron.platform.time.JavaLocalDateFormatter
|
||||||
import org.isoron.uhabits.core.ui.screens.habits.show.views.HistoryCardPresenter
|
import org.isoron.uhabits.core.ui.screens.habits.show.views.HistoryCardPresenter
|
||||||
import org.isoron.uhabits.core.ui.screens.habits.show.views.HistoryCardState
|
import org.isoron.uhabits.core.ui.screens.habits.show.views.HistoryCardState
|
||||||
import org.isoron.uhabits.core.ui.views.HistoryChart
|
import org.isoron.uhabits.core.ui.views.HistoryChart
|
||||||
import org.isoron.uhabits.databinding.ShowHabitHistoryBinding
|
import org.isoron.uhabits.databinding.ShowHabitHistoryBinding
|
||||||
import org.isoron.uhabits.utils.toThemedAndroidColor
|
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
class HistoryCardView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
|
class HistoryCardView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
|
||||||
@@ -35,7 +35,7 @@ class HistoryCardView(context: Context, attrs: AttributeSet) : LinearLayout(cont
|
|||||||
private var binding = ShowHabitHistoryBinding.inflate(LayoutInflater.from(context), this)
|
private var binding = ShowHabitHistoryBinding.inflate(LayoutInflater.from(context), this)
|
||||||
|
|
||||||
fun setState(state: HistoryCardState) {
|
fun setState(state: HistoryCardState) {
|
||||||
val androidColor = state.color.toThemedAndroidColor(context)
|
val androidColor = state.theme.color(state.color).toInt()
|
||||||
binding.title.setTextColor(androidColor)
|
binding.title.setTextColor(androidColor)
|
||||||
binding.chart.view = HistoryChart(
|
binding.chart.view = HistoryChart(
|
||||||
today = state.today,
|
today = state.today,
|
||||||
|
|||||||
@@ -22,11 +22,11 @@ import android.content.Context
|
|||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
|
import org.isoron.platform.gui.toInt
|
||||||
import org.isoron.uhabits.R
|
import org.isoron.uhabits.R
|
||||||
import org.isoron.uhabits.core.ui.screens.habits.show.views.OverviewCardState
|
import org.isoron.uhabits.core.ui.screens.habits.show.views.OverviewCardState
|
||||||
import org.isoron.uhabits.databinding.ShowHabitOverviewBinding
|
import org.isoron.uhabits.databinding.ShowHabitOverviewBinding
|
||||||
import org.isoron.uhabits.utils.StyledResources
|
import org.isoron.uhabits.utils.StyledResources
|
||||||
import org.isoron.uhabits.utils.toThemedAndroidColor
|
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
|
|
||||||
class OverviewCardView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
|
class OverviewCardView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
|
||||||
@@ -42,7 +42,7 @@ class OverviewCardView(context: Context, attrs: AttributeSet) : LinearLayout(con
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun setState(state: OverviewCardState) {
|
fun setState(state: OverviewCardState) {
|
||||||
val androidColor = state.color.toThemedAndroidColor(context)
|
val androidColor = state.theme.color(state.color).toInt()
|
||||||
val res = StyledResources(context)
|
val res = StyledResources(context)
|
||||||
val inactiveColor = res.getColor(R.attr.contrast60)
|
val inactiveColor = res.getColor(R.attr.contrast60)
|
||||||
binding.monthDiffLabel.setTextColor(if (state.scoreMonthDiff >= 0) androidColor else inactiveColor)
|
binding.monthDiffLabel.setTextColor(if (state.scoreMonthDiff >= 0) androidColor else inactiveColor)
|
||||||
|
|||||||
@@ -24,16 +24,16 @@ import android.view.LayoutInflater
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.AdapterView
|
import android.widget.AdapterView
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
|
import org.isoron.platform.gui.toInt
|
||||||
import org.isoron.uhabits.core.ui.screens.habits.show.views.ScoreCardPresenter
|
import org.isoron.uhabits.core.ui.screens.habits.show.views.ScoreCardPresenter
|
||||||
import org.isoron.uhabits.core.ui.screens.habits.show.views.ScoreCardState
|
import org.isoron.uhabits.core.ui.screens.habits.show.views.ScoreCardState
|
||||||
import org.isoron.uhabits.databinding.ShowHabitScoreBinding
|
import org.isoron.uhabits.databinding.ShowHabitScoreBinding
|
||||||
import org.isoron.uhabits.utils.toThemedAndroidColor
|
|
||||||
|
|
||||||
class ScoreCardView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
|
class ScoreCardView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
|
||||||
private var binding = ShowHabitScoreBinding.inflate(LayoutInflater.from(context), this)
|
private var binding = ShowHabitScoreBinding.inflate(LayoutInflater.from(context), this)
|
||||||
|
|
||||||
fun setState(state: ScoreCardState) {
|
fun setState(state: ScoreCardState) {
|
||||||
val androidColor = state.color.toThemedAndroidColor(context)
|
val androidColor = state.theme.color(state.color).toInt()
|
||||||
binding.title.setTextColor(androidColor)
|
binding.title.setTextColor(androidColor)
|
||||||
binding.spinner.setSelection(state.spinnerPosition)
|
binding.spinner.setSelection(state.spinnerPosition)
|
||||||
binding.scoreView.setScores(state.scores)
|
binding.scoreView.setScores(state.scores)
|
||||||
|
|||||||
@@ -22,16 +22,16 @@ import android.content.Context
|
|||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
|
import org.isoron.platform.gui.toInt
|
||||||
import org.isoron.uhabits.core.ui.screens.habits.show.views.StreakCardState
|
import org.isoron.uhabits.core.ui.screens.habits.show.views.StreakCardState
|
||||||
import org.isoron.uhabits.databinding.ShowHabitStreakBinding
|
import org.isoron.uhabits.databinding.ShowHabitStreakBinding
|
||||||
import org.isoron.uhabits.utils.toThemedAndroidColor
|
|
||||||
|
|
||||||
class StreakCardView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
|
class StreakCardView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
|
||||||
private val binding = ShowHabitStreakBinding.inflate(LayoutInflater.from(context), this)
|
private val binding = ShowHabitStreakBinding.inflate(LayoutInflater.from(context), this)
|
||||||
fun setState(state: StreakCardState) {
|
fun setState(state: StreakCardState) {
|
||||||
val color = state.color.toThemedAndroidColor(context)
|
val androidColor = state.theme.color(state.color).toInt()
|
||||||
binding.title.setTextColor(color)
|
binding.title.setTextColor(androidColor)
|
||||||
binding.streakChart.setColor(color)
|
binding.streakChart.setColor(androidColor)
|
||||||
binding.streakChart.setStreaks(state.bestStreaks)
|
binding.streakChart.setStreaks(state.bestStreaks)
|
||||||
postInvalidate()
|
postInvalidate()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,19 +20,18 @@ package org.isoron.uhabits.activities.habits.show.views
|
|||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.res.Resources
|
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
|
import org.isoron.platform.gui.toInt
|
||||||
import org.isoron.uhabits.R
|
import org.isoron.uhabits.R
|
||||||
|
import org.isoron.uhabits.activities.habits.edit.formatFrequency
|
||||||
import org.isoron.uhabits.activities.habits.list.views.toShortString
|
import org.isoron.uhabits.activities.habits.list.views.toShortString
|
||||||
import org.isoron.uhabits.core.models.Frequency
|
|
||||||
import org.isoron.uhabits.core.ui.screens.habits.show.views.SubtitleCardState
|
import org.isoron.uhabits.core.ui.screens.habits.show.views.SubtitleCardState
|
||||||
import org.isoron.uhabits.databinding.ShowHabitSubtitleBinding
|
import org.isoron.uhabits.databinding.ShowHabitSubtitleBinding
|
||||||
import org.isoron.uhabits.utils.InterfaceUtils
|
import org.isoron.uhabits.utils.InterfaceUtils
|
||||||
import org.isoron.uhabits.utils.formatTime
|
import org.isoron.uhabits.utils.formatTime
|
||||||
import org.isoron.uhabits.utils.toThemedAndroidColor
|
|
||||||
|
|
||||||
class SubtitleCardView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
|
class SubtitleCardView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
|
||||||
|
|
||||||
@@ -47,9 +46,13 @@ class SubtitleCardView(context: Context, attrs: AttributeSet) : LinearLayout(con
|
|||||||
|
|
||||||
@SuppressLint("SetTextI18n")
|
@SuppressLint("SetTextI18n")
|
||||||
fun setState(state: SubtitleCardState) {
|
fun setState(state: SubtitleCardState) {
|
||||||
val color = state.color.toThemedAndroidColor(context)
|
val color = state.theme.color(state.color).toInt()
|
||||||
val reminder = state.reminder
|
val reminder = state.reminder
|
||||||
binding.frequencyLabel.text = state.frequency.format(resources)
|
binding.frequencyLabel.text = formatFrequency(
|
||||||
|
state.frequency.numerator,
|
||||||
|
state.frequency.denominator,
|
||||||
|
resources,
|
||||||
|
)
|
||||||
binding.questionLabel.setTextColor(color)
|
binding.questionLabel.setTextColor(color)
|
||||||
binding.questionLabel.text = state.question
|
binding.questionLabel.text = state.question
|
||||||
binding.reminderLabel.text = if (reminder != null) {
|
binding.reminderLabel.text = if (reminder != null) {
|
||||||
@@ -72,32 +75,4 @@ class SubtitleCardView(context: Context, attrs: AttributeSet) : LinearLayout(con
|
|||||||
|
|
||||||
postInvalidate()
|
postInvalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("StringFormatMatches")
|
|
||||||
private fun Frequency.format(resources: Resources): String {
|
|
||||||
val num = this.numerator
|
|
||||||
val den = this.denominator
|
|
||||||
if (num == den) {
|
|
||||||
return resources.getString(R.string.every_day)
|
|
||||||
}
|
|
||||||
if (den == 7) {
|
|
||||||
return resources.getString(R.string.x_times_per_week, num)
|
|
||||||
}
|
|
||||||
if (den == 30 || den == 31) {
|
|
||||||
return resources.getString(R.string.x_times_per_month, num)
|
|
||||||
}
|
|
||||||
if (num == 1) {
|
|
||||||
if (den == 7) {
|
|
||||||
return resources.getString(R.string.every_week)
|
|
||||||
}
|
|
||||||
if (den % 7 == 0) {
|
|
||||||
return resources.getString(R.string.every_x_weeks, den / 7)
|
|
||||||
}
|
|
||||||
if (den == 30 || den == 31) {
|
|
||||||
return resources.getString(R.string.every_month)
|
|
||||||
}
|
|
||||||
return resources.getString(R.string.every_x_days, den)
|
|
||||||
}
|
|
||||||
return "$num/$den"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,15 +23,15 @@ import android.content.res.Resources
|
|||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
|
import org.isoron.platform.gui.toInt
|
||||||
import org.isoron.uhabits.R
|
import org.isoron.uhabits.R
|
||||||
import org.isoron.uhabits.core.ui.screens.habits.show.views.TargetCardState
|
import org.isoron.uhabits.core.ui.screens.habits.show.views.TargetCardState
|
||||||
import org.isoron.uhabits.databinding.ShowHabitTargetBinding
|
import org.isoron.uhabits.databinding.ShowHabitTargetBinding
|
||||||
import org.isoron.uhabits.utils.toThemedAndroidColor
|
|
||||||
|
|
||||||
class TargetCardView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
|
class TargetCardView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
|
||||||
private val binding = ShowHabitTargetBinding.inflate(LayoutInflater.from(context), this)
|
private val binding = ShowHabitTargetBinding.inflate(LayoutInflater.from(context), this)
|
||||||
fun setState(state: TargetCardState) {
|
fun setState(state: TargetCardState) {
|
||||||
val androidColor = state.color.toThemedAndroidColor(context)
|
val androidColor = state.theme.color(state.color).toInt()
|
||||||
binding.targetChart.setValues(state.values)
|
binding.targetChart.setValues(state.values)
|
||||||
binding.targetChart.setTargets(state.targets)
|
binding.targetChart.setTargets(state.targets)
|
||||||
binding.targetChart.setLabels(state.intervals.map { intervalToLabel(resources, it) })
|
binding.targetChart.setLabels(state.intervals.map { intervalToLabel(resources, it) })
|
||||||
|
|||||||
@@ -32,13 +32,15 @@ class SettingsActivity : AppCompatActivity() {
|
|||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
val component = (application as HabitsApplication).component
|
val component = (application as HabitsApplication).component
|
||||||
AndroidThemeSwitcher(this, component.preferences).apply()
|
val themeSwitcher = AndroidThemeSwitcher(this, component.preferences)
|
||||||
|
themeSwitcher.apply()
|
||||||
|
|
||||||
val binding = SettingsActivityBinding.inflate(LayoutInflater.from(this))
|
val binding = SettingsActivityBinding.inflate(LayoutInflater.from(this))
|
||||||
binding.root.setupToolbar(
|
binding.root.setupToolbar(
|
||||||
toolbar = binding.toolbar,
|
toolbar = binding.toolbar,
|
||||||
title = resources.getString(R.string.settings),
|
title = resources.getString(R.string.settings),
|
||||||
color = PaletteColor(11),
|
color = PaletteColor(11),
|
||||||
|
theme = themeSwitcher.currentTheme,
|
||||||
)
|
)
|
||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import org.isoron.uhabits.core.models.Habit
|
|||||||
import org.isoron.uhabits.core.models.HabitList
|
import org.isoron.uhabits.core.models.HabitList
|
||||||
import org.isoron.uhabits.core.models.PaletteColor
|
import org.isoron.uhabits.core.models.PaletteColor
|
||||||
import org.isoron.uhabits.databinding.AutomationBinding
|
import org.isoron.uhabits.databinding.AutomationBinding
|
||||||
|
import org.isoron.uhabits.utils.currentTheme
|
||||||
import org.isoron.uhabits.utils.setupToolbar
|
import org.isoron.uhabits.utils.setupToolbar
|
||||||
import java.util.LinkedList
|
import java.util.LinkedList
|
||||||
|
|
||||||
@@ -53,6 +54,7 @@ class EditSettingRootView(
|
|||||||
title = resources.getString(R.string.app_name),
|
title = resources.getString(R.string.app_name),
|
||||||
color = PaletteColor(11),
|
color = PaletteColor(11),
|
||||||
displayHomeAsUpEnabled = false,
|
displayHomeAsUpEnabled = false,
|
||||||
|
theme = currentTheme(),
|
||||||
)
|
)
|
||||||
populateHabitSpinner()
|
populateHabitSpinner()
|
||||||
binding.habitSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
binding.habitSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
||||||
|
|||||||
@@ -63,13 +63,14 @@ class PendingIntentFactory
|
|||||||
FLAG_UPDATE_CURRENT
|
FLAG_UPDATE_CURRENT
|
||||||
)
|
)
|
||||||
|
|
||||||
fun removeRepetition(habit: Habit): PendingIntent =
|
fun removeRepetition(habit: Habit, timestamp: Timestamp?): PendingIntent =
|
||||||
getBroadcast(
|
getBroadcast(
|
||||||
context,
|
context,
|
||||||
3,
|
3,
|
||||||
Intent(context, WidgetReceiver::class.java).apply {
|
Intent(context, WidgetReceiver::class.java).apply {
|
||||||
action = WidgetReceiver.ACTION_REMOVE_REPETITION
|
action = WidgetReceiver.ACTION_REMOVE_REPETITION
|
||||||
data = Uri.parse(habit.uriString)
|
data = Uri.parse(habit.uriString)
|
||||||
|
if (timestamp != null) putExtra("timestamp", timestamp.unixTime)
|
||||||
},
|
},
|
||||||
FLAG_UPDATE_CURRENT
|
FLAG_UPDATE_CURRENT
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ class AndroidNotificationTray
|
|||||||
val removeRepetitionAction = Action(
|
val removeRepetitionAction = Action(
|
||||||
R.drawable.ic_action_cancel,
|
R.drawable.ic_action_cancel,
|
||||||
context.getString(R.string.no),
|
context.getString(R.string.no),
|
||||||
pendingIntents.removeRepetition(habit)
|
pendingIntents.removeRepetition(habit, timestamp)
|
||||||
)
|
)
|
||||||
|
|
||||||
val enterAction = Action(
|
val enterAction = Action(
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import android.widget.AdapterView.OnItemClickListener
|
|||||||
import androidx.fragment.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
import com.android.datetimepicker.time.RadialPickerLayout
|
import com.android.datetimepicker.time.RadialPickerLayout
|
||||||
import com.android.datetimepicker.time.TimePickerDialog
|
import com.android.datetimepicker.time.TimePickerDialog
|
||||||
|
import org.isoron.platform.gui.toInt
|
||||||
import org.isoron.uhabits.HabitsApplication
|
import org.isoron.uhabits.HabitsApplication
|
||||||
import org.isoron.uhabits.R
|
import org.isoron.uhabits.R
|
||||||
import org.isoron.uhabits.activities.AndroidThemeSwitcher
|
import org.isoron.uhabits.activities.AndroidThemeSwitcher
|
||||||
@@ -35,14 +36,13 @@ import org.isoron.uhabits.core.models.Habit
|
|||||||
import org.isoron.uhabits.core.ui.ThemeSwitcher.Companion.THEME_LIGHT
|
import org.isoron.uhabits.core.ui.ThemeSwitcher.Companion.THEME_LIGHT
|
||||||
import org.isoron.uhabits.receivers.ReminderController
|
import org.isoron.uhabits.receivers.ReminderController
|
||||||
import org.isoron.uhabits.utils.SystemUtils
|
import org.isoron.uhabits.utils.SystemUtils
|
||||||
import org.isoron.uhabits.utils.toThemedAndroidColor
|
|
||||||
import java.util.Calendar
|
import java.util.Calendar
|
||||||
|
|
||||||
class SnoozeDelayPickerActivity : FragmentActivity(), OnItemClickListener {
|
class SnoozeDelayPickerActivity : FragmentActivity(), OnItemClickListener {
|
||||||
private var habit: Habit? = null
|
private var habit: Habit? = null
|
||||||
private var reminderController: ReminderController? = null
|
private var reminderController: ReminderController? = null
|
||||||
private var dialog: AlertDialog? = null
|
private var dialog: AlertDialog? = null
|
||||||
private var color: Int = 0
|
private var androidColor: Int = 0
|
||||||
|
|
||||||
override fun onCreate(bundle: Bundle?) {
|
override fun onCreate(bundle: Bundle?) {
|
||||||
super.onCreate(bundle)
|
super.onCreate(bundle)
|
||||||
@@ -63,7 +63,7 @@ class SnoozeDelayPickerActivity : FragmentActivity(), OnItemClickListener {
|
|||||||
habit = appComponent.habitList.getById(ContentUris.parseId(data))
|
habit = appComponent.habitList.getById(ContentUris.parseId(data))
|
||||||
}
|
}
|
||||||
if (habit == null) finish()
|
if (habit == null) finish()
|
||||||
color = habit!!.color.toThemedAndroidColor(this)
|
androidColor = themeSwitcher.currentTheme.color(habit!!.color).toInt()
|
||||||
reminderController = appComponent.reminderController
|
reminderController = appComponent.reminderController
|
||||||
dialog = AlertDialog.Builder(this)
|
dialog = AlertDialog.Builder(this)
|
||||||
.setTitle(R.string.select_snooze_delay)
|
.setTitle(R.string.select_snooze_delay)
|
||||||
@@ -85,7 +85,7 @@ class SnoozeDelayPickerActivity : FragmentActivity(), OnItemClickListener {
|
|||||||
calendar[Calendar.HOUR_OF_DAY],
|
calendar[Calendar.HOUR_OF_DAY],
|
||||||
calendar[Calendar.MINUTE],
|
calendar[Calendar.MINUTE],
|
||||||
DateFormat.is24HourFormat(this),
|
DateFormat.is24HourFormat(this),
|
||||||
color
|
androidColor
|
||||||
)
|
)
|
||||||
dialog.show(supportFragmentManager, "timePicker")
|
dialog.show(supportFragmentManager, "timePicker")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ package org.isoron.uhabits.utils
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.util.Log
|
|
||||||
import org.isoron.uhabits.core.models.PaletteColor
|
import org.isoron.uhabits.core.models.PaletteColor
|
||||||
|
|
||||||
object PaletteUtils {
|
object PaletteUtils {
|
||||||
@@ -29,16 +28,6 @@ object PaletteUtils {
|
|||||||
fun getAndroidTestColor(index: Int) = PaletteColor(index).toFixedAndroidColor()
|
fun getAndroidTestColor(index: Int) = PaletteColor(index).toFixedAndroidColor()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun PaletteColor.toThemedAndroidColor(context: Context): Int {
|
|
||||||
val palette = StyledResources(context).getPalette()
|
|
||||||
return if (paletteIndex in palette.indices) {
|
|
||||||
palette[paletteIndex]
|
|
||||||
} else {
|
|
||||||
Log.w("ColorHelper", "Invalid color: $paletteIndex. Returning default.")
|
|
||||||
palette[0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun PaletteColor.toFixedAndroidColor(): Int {
|
fun PaletteColor.toFixedAndroidColor(): Int {
|
||||||
return intArrayOf(
|
return intArrayOf(
|
||||||
Color.parseColor("#D32F2F"), // 0 red
|
Color.parseColor("#D32F2F"), // 0 red
|
||||||
|
|||||||
@@ -40,8 +40,12 @@ import androidx.appcompat.app.AppCompatActivity
|
|||||||
import androidx.appcompat.widget.Toolbar
|
import androidx.appcompat.widget.Toolbar
|
||||||
import androidx.core.content.FileProvider
|
import androidx.core.content.FileProvider
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
|
import org.isoron.platform.gui.toInt
|
||||||
|
import org.isoron.uhabits.HabitsApplication
|
||||||
import org.isoron.uhabits.R
|
import org.isoron.uhabits.R
|
||||||
|
import org.isoron.uhabits.activities.AndroidThemeSwitcher
|
||||||
import org.isoron.uhabits.core.models.PaletteColor
|
import org.isoron.uhabits.core.models.PaletteColor
|
||||||
|
import org.isoron.uhabits.core.ui.views.Theme
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
fun RelativeLayout.addBelow(
|
fun RelativeLayout.addBelow(
|
||||||
@@ -157,6 +161,7 @@ fun View.setupToolbar(
|
|||||||
toolbar: Toolbar,
|
toolbar: Toolbar,
|
||||||
title: String,
|
title: String,
|
||||||
color: PaletteColor,
|
color: PaletteColor,
|
||||||
|
theme: Theme,
|
||||||
displayHomeAsUpEnabled: Boolean = true,
|
displayHomeAsUpEnabled: Boolean = true,
|
||||||
) {
|
) {
|
||||||
toolbar.elevation = InterfaceUtils.dpToPixels(context, 2f)
|
toolbar.elevation = InterfaceUtils.dpToPixels(context, 2f)
|
||||||
@@ -165,7 +170,7 @@ fun View.setupToolbar(
|
|||||||
val toolbarColor = if (!res.getBoolean(R.attr.useHabitColorAsPrimary)) {
|
val toolbarColor = if (!res.getBoolean(R.attr.useHabitColorAsPrimary)) {
|
||||||
StyledResources(context).getColor(R.attr.colorPrimary)
|
StyledResources(context).getColor(R.attr.colorPrimary)
|
||||||
} else {
|
} else {
|
||||||
color.toThemedAndroidColor(context)
|
theme.color(color).toInt()
|
||||||
}
|
}
|
||||||
val darkerColor = ColorUtils.mixColors(toolbarColor, Color.BLACK, 0.75f)
|
val darkerColor = ColorUtils.mixColors(toolbarColor, Color.BLACK, 0.75f)
|
||||||
toolbar.background = ColorDrawable(toolbarColor)
|
toolbar.background = ColorDrawable(toolbarColor)
|
||||||
@@ -175,6 +180,13 @@ fun View.setupToolbar(
|
|||||||
activity.supportActionBar?.setDisplayHomeAsUpEnabled(displayHomeAsUpEnabled)
|
activity.supportActionBar?.setDisplayHomeAsUpEnabled(displayHomeAsUpEnabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun View.currentTheme(): Theme {
|
||||||
|
val component = (context.applicationContext as HabitsApplication).component
|
||||||
|
val themeSwitcher = AndroidThemeSwitcher(context, component.preferences)
|
||||||
|
themeSwitcher.apply()
|
||||||
|
return themeSwitcher.currentTheme
|
||||||
|
}
|
||||||
|
|
||||||
fun Int.toMeasureSpec(mode: Int) =
|
fun Int.toMeasureSpec(mode: Int) =
|
||||||
View.MeasureSpec.makeMeasureSpec(this, mode)
|
View.MeasureSpec.makeMeasureSpec(this, mode)
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import org.isoron.uhabits.core.commands.CommandRunner
|
|||||||
import org.isoron.uhabits.core.preferences.Preferences
|
import org.isoron.uhabits.core.preferences.Preferences
|
||||||
import org.isoron.uhabits.core.preferences.WidgetPreferences
|
import org.isoron.uhabits.core.preferences.WidgetPreferences
|
||||||
import org.isoron.uhabits.intents.PendingIntentFactory
|
import org.isoron.uhabits.intents.PendingIntentFactory
|
||||||
|
import kotlin.math.max
|
||||||
|
|
||||||
abstract class BaseWidget(val context: Context, val id: Int, val stacked: Boolean) {
|
abstract class BaseWidget(val context: Context, val id: Int, val stacked: Boolean) {
|
||||||
private val widgetPrefs: WidgetPreferences
|
private val widgetPrefs: WidgetPreferences
|
||||||
@@ -103,8 +104,8 @@ abstract class BaseWidget(val context: Context, val id: Int, val stacked: Boolea
|
|||||||
|
|
||||||
private fun getBitmapFromView(view: View): Bitmap {
|
private fun getBitmapFromView(view: View): Bitmap {
|
||||||
view.invalidate()
|
view.invalidate()
|
||||||
val width = view.measuredWidth
|
val width = max(1, view.measuredWidth)
|
||||||
val height = view.measuredHeight
|
val height = max(1, view.measuredHeight)
|
||||||
val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
|
val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
|
||||||
val canvas = Canvas(bitmap)
|
val canvas = Canvas(bitmap)
|
||||||
view.draw(canvas)
|
view.draw(canvas)
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ abstract class BaseWidgetProvider : AppWidgetProvider() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected fun getHabitsFromWidgetId(widgetId: Int): List<Habit> {
|
protected fun getHabitsFromWidgetId(widgetId: Int): List<Habit> {
|
||||||
val selectedIds = widgetPrefs.getHabitIdsFromWidgetId(widgetId)!!
|
val selectedIds = widgetPrefs.getHabitIdsFromWidgetId(widgetId)
|
||||||
val selectedHabits = ArrayList<Habit>(selectedIds.size)
|
val selectedHabits = ArrayList<Habit>(selectedIds.size)
|
||||||
for (id in selectedIds) {
|
for (id in selectedIds) {
|
||||||
val h = habits.getById(id) ?: throw HabitNotFoundException()
|
val h = habits.getById(id) ?: throw HabitNotFoundException()
|
||||||
|
|||||||
@@ -24,10 +24,11 @@ import android.content.Context
|
|||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
|
import org.isoron.platform.gui.toInt
|
||||||
import org.isoron.uhabits.core.models.Entry
|
import org.isoron.uhabits.core.models.Entry
|
||||||
import org.isoron.uhabits.core.models.Habit
|
import org.isoron.uhabits.core.models.Habit
|
||||||
|
import org.isoron.uhabits.core.ui.views.WidgetTheme
|
||||||
import org.isoron.uhabits.core.utils.DateUtils
|
import org.isoron.uhabits.core.utils.DateUtils
|
||||||
import org.isoron.uhabits.utils.toThemedAndroidColor
|
|
||||||
import org.isoron.uhabits.widgets.views.CheckmarkWidgetView
|
import org.isoron.uhabits.widgets.views.CheckmarkWidgetView
|
||||||
|
|
||||||
open class CheckmarkWidget(
|
open class CheckmarkWidget(
|
||||||
@@ -53,7 +54,7 @@ open class CheckmarkWidget(
|
|||||||
(widgetView as CheckmarkWidgetView).apply {
|
(widgetView as CheckmarkWidgetView).apply {
|
||||||
val today = DateUtils.getTodayWithOffset()
|
val today = DateUtils.getTodayWithOffset()
|
||||||
setBackgroundAlpha(preferedBackgroundAlpha)
|
setBackgroundAlpha(preferedBackgroundAlpha)
|
||||||
activeColor = habit.color.toThemedAndroidColor(context)
|
activeColor = WidgetTheme().color(habit.color).toInt()
|
||||||
name = habit.name
|
name = habit.name
|
||||||
entryValue = habit.computedEntries.get(today).value
|
entryValue = habit.computedEntries.get(today).value
|
||||||
if (habit.isNumerical) {
|
if (habit.isNumerical) {
|
||||||
|
|||||||
@@ -22,9 +22,10 @@ package org.isoron.uhabits.widgets
|
|||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import org.isoron.platform.gui.toInt
|
||||||
import org.isoron.uhabits.activities.common.views.FrequencyChart
|
import org.isoron.uhabits.activities.common.views.FrequencyChart
|
||||||
import org.isoron.uhabits.core.models.Habit
|
import org.isoron.uhabits.core.models.Habit
|
||||||
import org.isoron.uhabits.utils.toThemedAndroidColor
|
import org.isoron.uhabits.core.ui.views.WidgetTheme
|
||||||
import org.isoron.uhabits.widgets.views.GraphWidgetView
|
import org.isoron.uhabits.widgets.views.GraphWidgetView
|
||||||
|
|
||||||
class FrequencyWidget(
|
class FrequencyWidget(
|
||||||
@@ -47,7 +48,7 @@ class FrequencyWidget(
|
|||||||
if (preferedBackgroundAlpha >= 255) widgetView.setShadowAlpha(0x4f)
|
if (preferedBackgroundAlpha >= 255) widgetView.setShadowAlpha(0x4f)
|
||||||
(widgetView.dataView as FrequencyChart).apply {
|
(widgetView.dataView as FrequencyChart).apply {
|
||||||
setFirstWeekday(firstWeekday)
|
setFirstWeekday(firstWeekday)
|
||||||
setColor(habit.color.toThemedAndroidColor(context))
|
setColor(WidgetTheme().color(habit.color).toInt())
|
||||||
setFrequency(habit.originalEntries.computeWeekdayFrequency(habit.isNumerical))
|
setFrequency(habit.originalEntries.computeWeekdayFrequency(habit.isNumerical))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,10 +22,11 @@ package org.isoron.uhabits.widgets
|
|||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import org.isoron.platform.gui.toInt
|
||||||
import org.isoron.uhabits.activities.common.views.ScoreChart
|
import org.isoron.uhabits.activities.common.views.ScoreChart
|
||||||
import org.isoron.uhabits.core.models.Habit
|
import org.isoron.uhabits.core.models.Habit
|
||||||
import org.isoron.uhabits.core.ui.screens.habits.show.views.ScoreCardPresenter
|
import org.isoron.uhabits.core.ui.screens.habits.show.views.ScoreCardPresenter
|
||||||
import org.isoron.uhabits.utils.toThemedAndroidColor
|
import org.isoron.uhabits.core.ui.views.WidgetTheme
|
||||||
import org.isoron.uhabits.widgets.views.GraphWidgetView
|
import org.isoron.uhabits.widgets.views.GraphWidgetView
|
||||||
|
|
||||||
class ScoreWidget(
|
class ScoreWidget(
|
||||||
@@ -44,7 +45,8 @@ class ScoreWidget(
|
|||||||
val viewModel = ScoreCardPresenter.buildState(
|
val viewModel = ScoreCardPresenter.buildState(
|
||||||
habit = habit,
|
habit = habit,
|
||||||
firstWeekday = prefs.firstWeekdayInt,
|
firstWeekday = prefs.firstWeekdayInt,
|
||||||
spinnerPosition = prefs.scoreCardSpinnerPosition
|
spinnerPosition = prefs.scoreCardSpinnerPosition,
|
||||||
|
theme = WidgetTheme(),
|
||||||
)
|
)
|
||||||
val widgetView = view as GraphWidgetView
|
val widgetView = view as GraphWidgetView
|
||||||
widgetView.setBackgroundAlpha(preferedBackgroundAlpha)
|
widgetView.setBackgroundAlpha(preferedBackgroundAlpha)
|
||||||
@@ -52,7 +54,7 @@ class ScoreWidget(
|
|||||||
(widgetView.dataView as ScoreChart).apply {
|
(widgetView.dataView as ScoreChart).apply {
|
||||||
setIsTransparencyEnabled(true)
|
setIsTransparencyEnabled(true)
|
||||||
setBucketSize(viewModel.bucketSize)
|
setBucketSize(viewModel.bucketSize)
|
||||||
setColor(habit.color.toThemedAndroidColor(context))
|
setColor(WidgetTheme().color(habit.color).toInt())
|
||||||
setScores(viewModel.scores)
|
setScores(viewModel.scores)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ internal class StackRemoteViewsFactory(private val context: Context, intent: Int
|
|||||||
AppWidgetManager.INVALID_APPWIDGET_ID
|
AppWidgetManager.INVALID_APPWIDGET_ID
|
||||||
)
|
)
|
||||||
private val habitIds: LongArray
|
private val habitIds: LongArray
|
||||||
private val widgetType: StackWidgetType?
|
private val widgetType: StackWidgetType
|
||||||
private var remoteViews = ArrayList<RemoteViews>()
|
private var remoteViews = ArrayList<RemoteViews>()
|
||||||
override fun onCreate() {}
|
override fun onCreate() {}
|
||||||
override fun onDestroy() {}
|
override fun onDestroy() {}
|
||||||
@@ -86,27 +86,27 @@ internal class StackRemoteViewsFactory(private val context: Context, intent: Int
|
|||||||
|
|
||||||
override fun getViewAt(position: Int): RemoteViews? {
|
override fun getViewAt(position: Int): RemoteViews? {
|
||||||
Log.i("StackRemoteViewsFactory", "getViewAt $position")
|
Log.i("StackRemoteViewsFactory", "getViewAt $position")
|
||||||
return if (position < 0 || position > remoteViews.size) null else remoteViews[position]
|
return if (0 <= position && position < remoteViews.size) remoteViews[position] else null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun constructWidget(
|
private fun constructWidget(
|
||||||
habit: Habit,
|
habit: Habit,
|
||||||
prefs: Preferences
|
prefs: Preferences
|
||||||
): BaseWidget {
|
): BaseWidget {
|
||||||
when (widgetType) {
|
return when (widgetType) {
|
||||||
StackWidgetType.CHECKMARK -> return CheckmarkWidget(context, widgetId, habit, true)
|
StackWidgetType.CHECKMARK -> CheckmarkWidget(context, widgetId, habit, true)
|
||||||
StackWidgetType.FREQUENCY -> return FrequencyWidget(
|
StackWidgetType.FREQUENCY -> FrequencyWidget(
|
||||||
context,
|
context,
|
||||||
widgetId,
|
widgetId,
|
||||||
habit,
|
habit,
|
||||||
prefs.firstWeekdayInt,
|
prefs.firstWeekdayInt,
|
||||||
true
|
true
|
||||||
)
|
)
|
||||||
StackWidgetType.SCORE -> return ScoreWidget(context, widgetId, habit, true)
|
StackWidgetType.SCORE -> ScoreWidget(context, widgetId, habit, true)
|
||||||
StackWidgetType.HISTORY -> return HistoryWidget(context, widgetId, habit, true)
|
StackWidgetType.HISTORY -> HistoryWidget(context, widgetId, habit, true)
|
||||||
StackWidgetType.STREAKS -> return StreakWidget(context, widgetId, habit, true)
|
StackWidgetType.STREAKS -> StreakWidget(context, widgetId, habit, true)
|
||||||
|
StackWidgetType.TARGET -> TargetWidget(context, widgetId, habit, true)
|
||||||
}
|
}
|
||||||
throw IllegalStateException()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getLoadingView(): RemoteViews {
|
override fun getLoadingView(): RemoteViews {
|
||||||
@@ -157,6 +157,7 @@ internal class StackRemoteViewsFactory(private val context: Context, intent: Int
|
|||||||
if (widgetTypeValue < 0) throw RuntimeException("invalid widget type")
|
if (widgetTypeValue < 0) throw RuntimeException("invalid widget type")
|
||||||
if (habitIdsStr == null) throw RuntimeException("habitIdsStr is null")
|
if (habitIdsStr == null) throw RuntimeException("habitIdsStr is null")
|
||||||
widgetType = StackWidgetType.getWidgetTypeFromValue(widgetTypeValue)
|
widgetType = StackWidgetType.getWidgetTypeFromValue(widgetTypeValue)
|
||||||
|
?: throw RuntimeException("unknown widget type value: $widgetTypeValue")
|
||||||
habitIds = splitLongs(habitIdsStr)
|
habitIds = splitLongs(habitIdsStr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,61 +19,59 @@
|
|||||||
package org.isoron.uhabits.widgets
|
package org.isoron.uhabits.widgets
|
||||||
|
|
||||||
import org.isoron.uhabits.R
|
import org.isoron.uhabits.R
|
||||||
|
import java.lang.IllegalStateException
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by victoryu on 11/3/17.
|
|
||||||
*/
|
|
||||||
enum class StackWidgetType(val value: Int) {
|
enum class StackWidgetType(val value: Int) {
|
||||||
CHECKMARK(0), FREQUENCY(1), SCORE(2), // habit strength widget
|
CHECKMARK(0), FREQUENCY(1), SCORE(2), // habit strength widget
|
||||||
HISTORY(3), STREAKS(4), TARGET(5);
|
HISTORY(3), STREAKS(4), TARGET(5);
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun getWidgetTypeFromValue(value: Int): StackWidgetType? {
|
fun getWidgetTypeFromValue(value: Int): StackWidgetType? {
|
||||||
return when {
|
return when (value) {
|
||||||
CHECKMARK.value == value -> CHECKMARK
|
CHECKMARK.value -> CHECKMARK
|
||||||
FREQUENCY.value == value -> FREQUENCY
|
FREQUENCY.value -> FREQUENCY
|
||||||
SCORE.value == value -> SCORE
|
SCORE.value -> SCORE
|
||||||
HISTORY.value == value -> HISTORY
|
HISTORY.value -> HISTORY
|
||||||
STREAKS.value == value -> STREAKS
|
STREAKS.value -> STREAKS
|
||||||
TARGET.value == value -> TARGET
|
TARGET.value -> TARGET
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getStackWidgetLayoutId(type: StackWidgetType?): Int {
|
fun getStackWidgetLayoutId(type: StackWidgetType?): Int {
|
||||||
when (type) {
|
return when (type) {
|
||||||
CHECKMARK -> return R.layout.checkmark_stackview_widget
|
CHECKMARK -> R.layout.checkmark_stackview_widget
|
||||||
FREQUENCY -> return R.layout.frequency_stackview_widget
|
FREQUENCY -> R.layout.frequency_stackview_widget
|
||||||
SCORE -> return R.layout.score_stackview_widget
|
SCORE -> R.layout.score_stackview_widget
|
||||||
HISTORY -> return R.layout.history_stackview_widget
|
HISTORY -> R.layout.history_stackview_widget
|
||||||
STREAKS -> return R.layout.streak_stackview_widget
|
STREAKS -> R.layout.streak_stackview_widget
|
||||||
TARGET -> return R.layout.target_stackview_widget
|
TARGET -> R.layout.target_stackview_widget
|
||||||
|
else -> throw IllegalStateException()
|
||||||
}
|
}
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getStackWidgetAdapterViewId(type: StackWidgetType?): Int {
|
fun getStackWidgetAdapterViewId(type: StackWidgetType?): Int {
|
||||||
when (type) {
|
return when (type) {
|
||||||
CHECKMARK -> return R.id.checkmarkStackWidgetView
|
CHECKMARK -> R.id.checkmarkStackWidgetView
|
||||||
FREQUENCY -> return R.id.frequencyStackWidgetView
|
FREQUENCY -> R.id.frequencyStackWidgetView
|
||||||
SCORE -> return R.id.scoreStackWidgetView
|
SCORE -> R.id.scoreStackWidgetView
|
||||||
HISTORY -> return R.id.historyStackWidgetView
|
HISTORY -> R.id.historyStackWidgetView
|
||||||
STREAKS -> return R.id.streakStackWidgetView
|
STREAKS -> R.id.streakStackWidgetView
|
||||||
TARGET -> return R.id.targetStackWidgetView
|
TARGET -> R.id.targetStackWidgetView
|
||||||
|
else -> throw IllegalStateException()
|
||||||
}
|
}
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getStackWidgetEmptyViewId(type: StackWidgetType?): Int {
|
fun getStackWidgetEmptyViewId(type: StackWidgetType?): Int {
|
||||||
when (type) {
|
return when (type) {
|
||||||
CHECKMARK -> return R.id.checkmarkStackWidgetEmptyView
|
CHECKMARK -> R.id.checkmarkStackWidgetEmptyView
|
||||||
FREQUENCY -> return R.id.frequencyStackWidgetEmptyView
|
FREQUENCY -> R.id.frequencyStackWidgetEmptyView
|
||||||
SCORE -> return R.id.scoreStackWidgetEmptyView
|
SCORE -> R.id.scoreStackWidgetEmptyView
|
||||||
HISTORY -> return R.id.historyStackWidgetEmptyView
|
HISTORY -> R.id.historyStackWidgetEmptyView
|
||||||
STREAKS -> return R.id.streakStackWidgetEmptyView
|
STREAKS -> R.id.streakStackWidgetEmptyView
|
||||||
TARGET -> return R.id.targetStackWidgetEmptyView
|
TARGET -> R.id.targetStackWidgetEmptyView
|
||||||
|
else -> throw IllegalStateException()
|
||||||
}
|
}
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,9 +24,10 @@ import android.content.Context
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup.LayoutParams
|
import android.view.ViewGroup.LayoutParams
|
||||||
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
|
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
|
||||||
|
import org.isoron.platform.gui.toInt
|
||||||
import org.isoron.uhabits.activities.common.views.StreakChart
|
import org.isoron.uhabits.activities.common.views.StreakChart
|
||||||
import org.isoron.uhabits.core.models.Habit
|
import org.isoron.uhabits.core.models.Habit
|
||||||
import org.isoron.uhabits.utils.toThemedAndroidColor
|
import org.isoron.uhabits.core.ui.views.WidgetTheme
|
||||||
import org.isoron.uhabits.widgets.views.GraphWidgetView
|
import org.isoron.uhabits.widgets.views.GraphWidgetView
|
||||||
|
|
||||||
class StreakWidget(
|
class StreakWidget(
|
||||||
@@ -46,7 +47,7 @@ class StreakWidget(
|
|||||||
widgetView.setBackgroundAlpha(preferedBackgroundAlpha)
|
widgetView.setBackgroundAlpha(preferedBackgroundAlpha)
|
||||||
if (preferedBackgroundAlpha >= 255) widgetView.setShadowAlpha(0x4f)
|
if (preferedBackgroundAlpha >= 255) widgetView.setShadowAlpha(0x4f)
|
||||||
(widgetView.dataView as StreakChart).apply {
|
(widgetView.dataView as StreakChart).apply {
|
||||||
setColor(habit.color.toThemedAndroidColor(context))
|
setColor(WidgetTheme().color(habit.color).toInt())
|
||||||
setStreaks(habit.streaks.getBest(maxStreakCount))
|
setStreaks(habit.streaks.getBest(maxStreakCount))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,11 +25,12 @@ import android.view.View
|
|||||||
import android.view.ViewGroup.LayoutParams
|
import android.view.ViewGroup.LayoutParams
|
||||||
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
|
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import org.isoron.platform.gui.toInt
|
||||||
import org.isoron.uhabits.activities.common.views.TargetChart
|
import org.isoron.uhabits.activities.common.views.TargetChart
|
||||||
import org.isoron.uhabits.activities.habits.show.views.TargetCardView.Companion.intervalToLabel
|
import org.isoron.uhabits.activities.habits.show.views.TargetCardView.Companion.intervalToLabel
|
||||||
import org.isoron.uhabits.core.models.Habit
|
import org.isoron.uhabits.core.models.Habit
|
||||||
import org.isoron.uhabits.core.ui.screens.habits.show.views.TargetCardPresenter
|
import org.isoron.uhabits.core.ui.screens.habits.show.views.TargetCardPresenter
|
||||||
import org.isoron.uhabits.utils.toThemedAndroidColor
|
import org.isoron.uhabits.core.ui.views.WidgetTheme
|
||||||
import org.isoron.uhabits.widgets.views.GraphWidgetView
|
import org.isoron.uhabits.widgets.views.GraphWidgetView
|
||||||
|
|
||||||
class TargetWidget(
|
class TargetWidget(
|
||||||
@@ -49,8 +50,12 @@ class TargetWidget(
|
|||||||
widgetView.setBackgroundAlpha(preferedBackgroundAlpha)
|
widgetView.setBackgroundAlpha(preferedBackgroundAlpha)
|
||||||
if (preferedBackgroundAlpha >= 255) widgetView.setShadowAlpha(0x4f)
|
if (preferedBackgroundAlpha >= 255) widgetView.setShadowAlpha(0x4f)
|
||||||
val chart = (widgetView.dataView as TargetChart)
|
val chart = (widgetView.dataView as TargetChart)
|
||||||
val data = TargetCardPresenter.buildState(habit, prefs.firstWeekdayInt)
|
val data = TargetCardPresenter.buildState(
|
||||||
chart.setColor(data.color.toThemedAndroidColor(context))
|
habit = habit,
|
||||||
|
firstWeekday = prefs.firstWeekdayInt,
|
||||||
|
theme = WidgetTheme(),
|
||||||
|
)
|
||||||
|
chart.setColor(WidgetTheme().color(habit.color).toInt())
|
||||||
chart.setTargets(data.targets)
|
chart.setTargets(data.targets)
|
||||||
chart.setLabels(data.intervals.map { intervalToLabel(context.resources, it) })
|
chart.setLabels(data.intervals.map { intervalToLabel(context.resources, it) })
|
||||||
chart.setValues(data.values)
|
chart.setValues(data.values)
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ class WidgetUpdater
|
|||||||
val modifiedWidgetIds = when (modifiedHabitId) {
|
val modifiedWidgetIds = when (modifiedHabitId) {
|
||||||
null -> widgetIds.toList()
|
null -> widgetIds.toList()
|
||||||
else -> widgetIds.filter { w ->
|
else -> widgetIds.filter { w ->
|
||||||
widgetPrefs.getHabitIdsFromWidgetId(w)!!.contains(modifiedHabitId)
|
widgetPrefs.getHabitIdsFromWidgetId(w).contains(modifiedHabitId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import android.widget.ListView
|
|||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import org.isoron.uhabits.HabitsApplication
|
import org.isoron.uhabits.HabitsApplication
|
||||||
import org.isoron.uhabits.R
|
import org.isoron.uhabits.R
|
||||||
|
import org.isoron.uhabits.activities.AndroidThemeSwitcher
|
||||||
import org.isoron.uhabits.core.preferences.WidgetPreferences
|
import org.isoron.uhabits.core.preferences.WidgetPreferences
|
||||||
import org.isoron.uhabits.widgets.WidgetUpdater
|
import org.isoron.uhabits.widgets.WidgetUpdater
|
||||||
import java.util.ArrayList
|
import java.util.ArrayList
|
||||||
@@ -58,6 +59,7 @@ open class HabitPickerDialog : Activity() {
|
|||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
val component = (applicationContext as HabitsApplication).component
|
val component = (applicationContext as HabitsApplication).component
|
||||||
|
AndroidThemeSwitcher(this, component.preferences).apply()
|
||||||
val habitList = component.habitList
|
val habitList = component.habitList
|
||||||
widgetPreferences = component.widgetPreferences
|
widgetPreferences = component.widgetPreferences
|
||||||
widgetUpdater = component.widgetUpdater
|
widgetUpdater = component.widgetUpdater
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ import org.isoron.uhabits.utils.PaletteUtils.getAndroidTestColor
|
|||||||
import org.isoron.uhabits.utils.StyledResources
|
import org.isoron.uhabits.utils.StyledResources
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
class CheckmarkWidgetView : HabitWidgetView {
|
class CheckmarkWidgetView : HabitWidgetView {
|
||||||
var activeColor: Int = 0
|
var activeColor: Int = 0
|
||||||
@@ -118,19 +119,25 @@ class CheckmarkWidgetView : HabitWidgetView {
|
|||||||
get() = R.layout.widget_checkmark
|
get() = R.layout.widget_checkmark
|
||||||
|
|
||||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||||
val width = MeasureSpec.getSize(widthMeasureSpec)
|
var width = MeasureSpec.getSize(widthMeasureSpec)
|
||||||
val height = MeasureSpec.getSize(heightMeasureSpec)
|
var height = MeasureSpec.getSize(heightMeasureSpec)
|
||||||
var textSize = 0.15f * height
|
if (height >= width) {
|
||||||
val maxTextSize = getDimension(context, R.dimen.smallerTextSize)
|
height = min(height, (width * 1.5).roundToInt())
|
||||||
textSize = min(textSize, maxTextSize)
|
} else {
|
||||||
|
width = min(width, height)
|
||||||
|
}
|
||||||
|
val textSize = min(0.2f * width, getDimension(context, R.dimen.smallerTextSize))
|
||||||
label.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize)
|
label.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize)
|
||||||
if (isNumerical) {
|
if (isNumerical) {
|
||||||
ring.setTextSize(textSize * 0.75f)
|
ring.setTextSize(textSize * 0.9f)
|
||||||
} else {
|
} else {
|
||||||
ring.setTextSize(textSize)
|
ring.setTextSize(textSize)
|
||||||
}
|
}
|
||||||
ring.setThickness(0.15f * textSize)
|
ring.setThickness(0.03f * width)
|
||||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
super.onMeasure(
|
||||||
|
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
|
||||||
|
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun init() {
|
private fun init() {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
2.0.2:
|
2.0.3:
|
||||||
* Bug fixes
|
* Bug fixes
|
||||||
2.0:
|
2.0:
|
||||||
* Track numeric habits (e.g. how many pages did you read?)
|
* Track numeric habits (e.g. how many pages did you read?)
|
||||||
|
|||||||
@@ -31,19 +31,19 @@
|
|||||||
android:id="@+id/scoreRing"
|
android:id="@+id/scoreRing"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
android:layout_weight="1"
|
android:layout_weight="0.9"
|
||||||
habit:thickness="2"
|
habit:thickness="2"
|
||||||
habit:textSize="16"
|
habit:textSize="16"
|
||||||
habit:enableFontAwesome="true"
|
habit:enableFontAwesome="true"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_marginTop="8dp"
|
||||||
android:layout_marginLeft="12dp"
|
android:layout_marginLeft="4dp"
|
||||||
android:layout_marginRight="12dp"/>
|
android:layout_marginRight="4dp"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/label"
|
android:id="@+id/label"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="0"
|
android:layout_weight="0.1"
|
||||||
android:textSize="12sp"
|
android:textSize="12sp"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/white"
|
||||||
android:layout_marginLeft="6dp"
|
android:layout_marginLeft="6dp"
|
||||||
|
|||||||
@@ -129,6 +129,7 @@
|
|||||||
<item name="selectedBackground">@drawable/selected_box</item>
|
<item name="selectedBackground">@drawable/selected_box</item>
|
||||||
<item name="textColorAlertDialogListItem">@color/grey_100</item>
|
<item name="textColorAlertDialogListItem">@color/grey_100</item>
|
||||||
<item name="windowBackgroundColor">@color/black</item>
|
<item name="windowBackgroundColor">@color/black</item>
|
||||||
|
<item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material.PureBlack</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="BaseDialog" parent="Theme.AppCompat.Light.Dialog">
|
<style name="BaseDialog" parent="Theme.AppCompat.Light.Dialog">
|
||||||
@@ -151,6 +152,11 @@
|
|||||||
<item name="palette">@array/darkPalette</item>
|
<item name="palette">@array/darkPalette</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="PreferenceThemeOverlay.v14.Material.PureBlack">
|
||||||
|
<item name="android:background">@color/black</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
<style name="WidgetTheme" parent="AppBaseThemeDark">
|
<style name="WidgetTheme" parent="AppBaseThemeDark">
|
||||||
<item name="cardBgColor">@color/grey_850</item>
|
<item name="cardBgColor">@color/grey_850</item>
|
||||||
<item name="contrast0">@color/white</item>
|
<item name="contrast0">@color/white</item>
|
||||||
|
|||||||
@@ -72,10 +72,20 @@ data class LocalDate(val daysSince2000: Int) {
|
|||||||
return dayCache
|
return dayCache
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val monthLength: Int
|
||||||
|
get() = when (month) {
|
||||||
|
4, 6, 9, 11 -> 30
|
||||||
|
2 -> if (isLeapYear(year)) 29 else 28
|
||||||
|
else -> 31
|
||||||
|
}
|
||||||
|
|
||||||
private fun updateYearMonthDayCache() {
|
private fun updateYearMonthDayCache() {
|
||||||
var currYear = 2000
|
var currYear = 2000
|
||||||
var currDay = 0
|
var currDay = 0
|
||||||
|
if (daysSince2000 < 0) {
|
||||||
|
currYear -= 400
|
||||||
|
currDay -= 146097
|
||||||
|
}
|
||||||
while (true) {
|
while (true) {
|
||||||
val currYearLength = if (isLeapYear(currYear)) 366 else 365
|
val currYearLength = if (isLeapYear(currYear)) 366 else 365
|
||||||
if (daysSince2000 < currDay + currYearLength) {
|
if (daysSince2000 < currDay + currYearLength) {
|
||||||
@@ -86,10 +96,8 @@ data class LocalDate(val daysSince2000: Int) {
|
|||||||
currDay += currYearLength
|
currDay += currYearLength
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var currMonth = 1
|
var currMonth = 1
|
||||||
val monthOffset = if (isLeapYear(currYear)) leapOffset else nonLeapOffset
|
val monthOffset = if (isLeapYear(currYear)) leapOffset else nonLeapOffset
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (daysSince2000 < currDay + monthOffset[currMonth]) {
|
if (daysSince2000 < currDay + monthOffset[currMonth]) {
|
||||||
monthCache = currMonth
|
monthCache = currMonth
|
||||||
@@ -98,7 +106,6 @@ data class LocalDate(val daysSince2000: Int) {
|
|||||||
currMonth++
|
currMonth++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
currDay += monthOffset[currMonth - 1]
|
currDay += monthOffset[currMonth - 1]
|
||||||
dayCache = daysSince2000 - currDay + 1
|
dayCache = daysSince2000 - currDay + 1
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -248,8 +248,17 @@ open class EntryList {
|
|||||||
for (i in num - 1 until filtered.size) {
|
for (i in num - 1 until filtered.size) {
|
||||||
val (begin, _) = filtered[i]
|
val (begin, _) = filtered[i]
|
||||||
val (center, _) = filtered[i - num + 1]
|
val (center, _) = filtered[i - num + 1]
|
||||||
if (begin.daysUntil(center) < den) {
|
var size = den
|
||||||
val end = begin.plus(den - 1)
|
if (den == 30 || den == 31) {
|
||||||
|
val beginDate = begin.toLocalDate()
|
||||||
|
size = if (beginDate.day == beginDate.monthLength) {
|
||||||
|
beginDate.plus(1).monthLength
|
||||||
|
} else {
|
||||||
|
beginDate.monthLength
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (begin.daysUntil(center) < size) {
|
||||||
|
val end = begin.plus(size - 1)
|
||||||
intervals.add(Interval(begin, center, end))
|
intervals.add(Interval(begin, center, end))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -258,7 +258,7 @@ open class Preferences(private val storage: Storage) {
|
|||||||
putString(key, joinLongs(values))
|
putString(key, joinLongs(values))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getLongArray(key: String, defValue: LongArray): LongArray? {
|
fun getLongArray(key: String, defValue: LongArray): LongArray {
|
||||||
val string = getString(key, "")
|
val string = getString(key, "")
|
||||||
return if (string.isEmpty()) defValue else splitLongs(
|
return if (string.isEmpty()) defValue else splitLongs(
|
||||||
string
|
string
|
||||||
|
|||||||
@@ -27,19 +27,18 @@ class WidgetPreferences @Inject constructor(private val storage: Preferences.Sto
|
|||||||
storage.putLongArray(getHabitIdKey(widgetId), habitIds)
|
storage.putLongArray(getHabitIdKey(widgetId), habitIds)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getHabitIdsFromWidgetId(widgetId: Int): LongArray? {
|
fun getHabitIdsFromWidgetId(widgetId: Int): LongArray {
|
||||||
var habitIds: LongArray?
|
|
||||||
val habitIdKey = getHabitIdKey(widgetId)
|
val habitIdKey = getHabitIdKey(widgetId)
|
||||||
try {
|
return try {
|
||||||
habitIds = storage.getLongArray(habitIdKey, longArrayOf(-1))
|
storage.getLongArray(habitIdKey, longArrayOf())
|
||||||
} catch (e: ClassCastException) {
|
} catch (e: ClassCastException) {
|
||||||
// Up to Loop 1.7.11, this preference was not an array, but a single
|
// Up to Loop 1.7.11, this preference was not an array, but a single
|
||||||
// long. Trying to read the old preference causes a cast exception.
|
// long. Trying to read the old preference causes a cast exception.
|
||||||
habitIds = LongArray(1)
|
when (val habitId = storage.getLong(habitIdKey, -1)) {
|
||||||
habitIds[0] = storage.getLong(habitIdKey, -1)
|
-1L -> longArrayOf()
|
||||||
storage.putLongArray(habitIdKey, habitIds)
|
else -> longArrayOf(habitId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return habitIds
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removeWidget(id: Int) {
|
fun removeWidget(id: Int) {
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import org.isoron.uhabits.core.AppScope
|
|||||||
import org.isoron.uhabits.core.commands.Command
|
import org.isoron.uhabits.core.commands.Command
|
||||||
import org.isoron.uhabits.core.commands.CommandRunner
|
import org.isoron.uhabits.core.commands.CommandRunner
|
||||||
import org.isoron.uhabits.core.commands.CreateRepetitionCommand
|
import org.isoron.uhabits.core.commands.CreateRepetitionCommand
|
||||||
|
import org.isoron.uhabits.core.io.Logging
|
||||||
import org.isoron.uhabits.core.models.Habit
|
import org.isoron.uhabits.core.models.Habit
|
||||||
import org.isoron.uhabits.core.models.HabitList
|
import org.isoron.uhabits.core.models.HabitList
|
||||||
import org.isoron.uhabits.core.models.HabitList.Order
|
import org.isoron.uhabits.core.models.HabitList.Order
|
||||||
@@ -54,8 +55,12 @@ import javax.inject.Inject
|
|||||||
class HabitCardListCache @Inject constructor(
|
class HabitCardListCache @Inject constructor(
|
||||||
private val allHabits: HabitList,
|
private val allHabits: HabitList,
|
||||||
private val commandRunner: CommandRunner,
|
private val commandRunner: CommandRunner,
|
||||||
taskRunner: TaskRunner
|
taskRunner: TaskRunner,
|
||||||
|
logging: Logging,
|
||||||
) : CommandRunner.Listener {
|
) : CommandRunner.Listener {
|
||||||
|
|
||||||
|
private val logger = logging.getLogger("HabitCardListCache")
|
||||||
|
|
||||||
private var checkmarkCount = 0
|
private var checkmarkCount = 0
|
||||||
private var currentFetchTask: Task? = null
|
private var currentFetchTask: Task? = null
|
||||||
private var listener: Listener
|
private var listener: Listener
|
||||||
@@ -316,8 +321,17 @@ class HabitCardListCache @Inject constructor(
|
|||||||
toPosition: Int
|
toPosition: Int
|
||||||
) {
|
) {
|
||||||
data.habits.removeAt(fromPosition)
|
data.habits.removeAt(fromPosition)
|
||||||
data.habits.add(toPosition, habit)
|
|
||||||
listener.onItemMoved(fromPosition, toPosition)
|
// Workaround for https://github.com/iSoron/uhabits/issues/968
|
||||||
|
val checkedToPosition = if (toPosition > data.habits.size) {
|
||||||
|
logger.error("performMove: $toPosition is strictly higher than ${data.habits.size}")
|
||||||
|
data.habits.size
|
||||||
|
} else {
|
||||||
|
toPosition
|
||||||
|
}
|
||||||
|
|
||||||
|
data.habits.add(checkedToPosition, habit)
|
||||||
|
listener.onItemMoved(fromPosition, checkedToPosition)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ data class ShowHabitState(
|
|||||||
val frequency: FrequencyCardState,
|
val frequency: FrequencyCardState,
|
||||||
val history: HistoryCardState,
|
val history: HistoryCardState,
|
||||||
val bar: BarCardState,
|
val bar: BarCardState,
|
||||||
|
val theme: Theme,
|
||||||
)
|
)
|
||||||
|
|
||||||
class ShowHabitPresenter(
|
class ShowHabitPresenter(
|
||||||
@@ -94,11 +95,14 @@ class ShowHabitPresenter(
|
|||||||
title = habit.name,
|
title = habit.name,
|
||||||
color = habit.color,
|
color = habit.color,
|
||||||
isNumerical = habit.isNumerical,
|
isNumerical = habit.isNumerical,
|
||||||
|
theme = theme,
|
||||||
subtitle = SubtitleCardPresenter.buildState(
|
subtitle = SubtitleCardPresenter.buildState(
|
||||||
habit = habit,
|
habit = habit,
|
||||||
|
theme = theme,
|
||||||
),
|
),
|
||||||
overview = OverviewCardPresenter.buildState(
|
overview = OverviewCardPresenter.buildState(
|
||||||
habit = habit,
|
habit = habit,
|
||||||
|
theme = theme,
|
||||||
),
|
),
|
||||||
notes = NotesCardPresenter.buildState(
|
notes = NotesCardPresenter.buildState(
|
||||||
habit = habit,
|
habit = habit,
|
||||||
@@ -106,18 +110,22 @@ class ShowHabitPresenter(
|
|||||||
target = TargetCardPresenter.buildState(
|
target = TargetCardPresenter.buildState(
|
||||||
habit = habit,
|
habit = habit,
|
||||||
firstWeekday = preferences.firstWeekdayInt,
|
firstWeekday = preferences.firstWeekdayInt,
|
||||||
|
theme = theme,
|
||||||
),
|
),
|
||||||
streaks = StreakCartPresenter.buildState(
|
streaks = StreakCartPresenter.buildState(
|
||||||
habit = habit,
|
habit = habit,
|
||||||
|
theme = theme,
|
||||||
),
|
),
|
||||||
scores = ScoreCardPresenter.buildState(
|
scores = ScoreCardPresenter.buildState(
|
||||||
spinnerPosition = preferences.scoreCardSpinnerPosition,
|
spinnerPosition = preferences.scoreCardSpinnerPosition,
|
||||||
habit = habit,
|
habit = habit,
|
||||||
firstWeekday = preferences.firstWeekdayInt,
|
firstWeekday = preferences.firstWeekdayInt,
|
||||||
|
theme = theme,
|
||||||
),
|
),
|
||||||
frequency = FrequencyCardPresenter.buildState(
|
frequency = FrequencyCardPresenter.buildState(
|
||||||
habit = habit,
|
habit = habit,
|
||||||
firstWeekday = preferences.firstWeekdayInt,
|
firstWeekday = preferences.firstWeekdayInt,
|
||||||
|
theme = theme,
|
||||||
),
|
),
|
||||||
history = HistoryCardPresenter.buildState(
|
history = HistoryCardPresenter.buildState(
|
||||||
habit = habit,
|
habit = habit,
|
||||||
|
|||||||
@@ -22,12 +22,14 @@ package org.isoron.uhabits.core.ui.screens.habits.show.views
|
|||||||
import org.isoron.uhabits.core.models.Habit
|
import org.isoron.uhabits.core.models.Habit
|
||||||
import org.isoron.uhabits.core.models.PaletteColor
|
import org.isoron.uhabits.core.models.PaletteColor
|
||||||
import org.isoron.uhabits.core.models.Timestamp
|
import org.isoron.uhabits.core.models.Timestamp
|
||||||
|
import org.isoron.uhabits.core.ui.views.Theme
|
||||||
import java.util.HashMap
|
import java.util.HashMap
|
||||||
|
|
||||||
data class FrequencyCardState(
|
data class FrequencyCardState(
|
||||||
val color: PaletteColor,
|
val color: PaletteColor,
|
||||||
val firstWeekday: Int,
|
val firstWeekday: Int,
|
||||||
val frequency: HashMap<Timestamp, Array<Int>>,
|
val frequency: HashMap<Timestamp, Array<Int>>,
|
||||||
|
val theme: Theme,
|
||||||
)
|
)
|
||||||
|
|
||||||
class FrequencyCardPresenter {
|
class FrequencyCardPresenter {
|
||||||
@@ -35,12 +37,14 @@ class FrequencyCardPresenter {
|
|||||||
fun buildState(
|
fun buildState(
|
||||||
habit: Habit,
|
habit: Habit,
|
||||||
firstWeekday: Int,
|
firstWeekday: Int,
|
||||||
|
theme: Theme
|
||||||
) = FrequencyCardState(
|
) = FrequencyCardState(
|
||||||
color = habit.color,
|
color = habit.color,
|
||||||
frequency = habit.originalEntries.computeWeekdayFrequency(
|
frequency = habit.originalEntries.computeWeekdayFrequency(
|
||||||
isNumerical = habit.isNumerical
|
isNumerical = habit.isNumerical
|
||||||
),
|
),
|
||||||
firstWeekday = firstWeekday,
|
firstWeekday = firstWeekday,
|
||||||
|
theme = theme,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ package org.isoron.uhabits.core.ui.screens.habits.show.views
|
|||||||
import org.isoron.uhabits.core.models.Entry
|
import org.isoron.uhabits.core.models.Entry
|
||||||
import org.isoron.uhabits.core.models.Habit
|
import org.isoron.uhabits.core.models.Habit
|
||||||
import org.isoron.uhabits.core.models.PaletteColor
|
import org.isoron.uhabits.core.models.PaletteColor
|
||||||
|
import org.isoron.uhabits.core.ui.views.Theme
|
||||||
import org.isoron.uhabits.core.utils.DateUtils
|
import org.isoron.uhabits.core.utils.DateUtils
|
||||||
|
|
||||||
data class OverviewCardState(
|
data class OverviewCardState(
|
||||||
@@ -30,11 +31,12 @@ data class OverviewCardState(
|
|||||||
val scoreYearDiff: Float,
|
val scoreYearDiff: Float,
|
||||||
val scoreToday: Float,
|
val scoreToday: Float,
|
||||||
val totalCount: Long,
|
val totalCount: Long,
|
||||||
|
val theme: Theme,
|
||||||
)
|
)
|
||||||
|
|
||||||
class OverviewCardPresenter {
|
class OverviewCardPresenter {
|
||||||
companion object {
|
companion object {
|
||||||
fun buildState(habit: Habit): OverviewCardState {
|
fun buildState(habit: Habit, theme: Theme): OverviewCardState {
|
||||||
val today = DateUtils.getTodayWithOffset()
|
val today = DateUtils.getTodayWithOffset()
|
||||||
val lastMonth = today.minus(30)
|
val lastMonth = today.minus(30)
|
||||||
val lastYear = today.minus(365)
|
val lastYear = today.minus(365)
|
||||||
@@ -52,6 +54,7 @@ class OverviewCardPresenter {
|
|||||||
scoreMonthDiff = scoreToday - scoreLastMonth,
|
scoreMonthDiff = scoreToday - scoreLastMonth,
|
||||||
scoreYearDiff = scoreToday - scoreLastYear,
|
scoreYearDiff = scoreToday - scoreLastYear,
|
||||||
totalCount = totalCount,
|
totalCount = totalCount,
|
||||||
|
theme = theme,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import org.isoron.uhabits.core.models.Habit
|
|||||||
import org.isoron.uhabits.core.models.PaletteColor
|
import org.isoron.uhabits.core.models.PaletteColor
|
||||||
import org.isoron.uhabits.core.models.Score
|
import org.isoron.uhabits.core.models.Score
|
||||||
import org.isoron.uhabits.core.preferences.Preferences
|
import org.isoron.uhabits.core.preferences.Preferences
|
||||||
|
import org.isoron.uhabits.core.ui.views.Theme
|
||||||
import org.isoron.uhabits.core.utils.DateUtils
|
import org.isoron.uhabits.core.utils.DateUtils
|
||||||
|
|
||||||
data class ScoreCardState(
|
data class ScoreCardState(
|
||||||
@@ -30,6 +31,7 @@ data class ScoreCardState(
|
|||||||
val bucketSize: Int,
|
val bucketSize: Int,
|
||||||
val spinnerPosition: Int,
|
val spinnerPosition: Int,
|
||||||
val color: PaletteColor,
|
val color: PaletteColor,
|
||||||
|
val theme: Theme,
|
||||||
)
|
)
|
||||||
|
|
||||||
class ScoreCardPresenter(
|
class ScoreCardPresenter(
|
||||||
@@ -53,6 +55,7 @@ class ScoreCardPresenter(
|
|||||||
habit: Habit,
|
habit: Habit,
|
||||||
firstWeekday: Int,
|
firstWeekday: Int,
|
||||||
spinnerPosition: Int,
|
spinnerPosition: Int,
|
||||||
|
theme: Theme,
|
||||||
): ScoreCardState {
|
): ScoreCardState {
|
||||||
val bucketSize = BUCKET_SIZES[spinnerPosition]
|
val bucketSize = BUCKET_SIZES[spinnerPosition]
|
||||||
val today = DateUtils.getTodayWithOffset()
|
val today = DateUtils.getTodayWithOffset()
|
||||||
@@ -77,6 +80,7 @@ class ScoreCardPresenter(
|
|||||||
scores = scores,
|
scores = scores,
|
||||||
bucketSize = bucketSize,
|
bucketSize = bucketSize,
|
||||||
spinnerPosition = spinnerPosition,
|
spinnerPosition = spinnerPosition,
|
||||||
|
theme = theme,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,18 +22,21 @@ package org.isoron.uhabits.core.ui.screens.habits.show.views
|
|||||||
import org.isoron.uhabits.core.models.Habit
|
import org.isoron.uhabits.core.models.Habit
|
||||||
import org.isoron.uhabits.core.models.PaletteColor
|
import org.isoron.uhabits.core.models.PaletteColor
|
||||||
import org.isoron.uhabits.core.models.Streak
|
import org.isoron.uhabits.core.models.Streak
|
||||||
|
import org.isoron.uhabits.core.ui.views.Theme
|
||||||
|
|
||||||
data class StreakCardState(
|
data class StreakCardState(
|
||||||
val color: PaletteColor,
|
val color: PaletteColor,
|
||||||
val bestStreaks: List<Streak>
|
val bestStreaks: List<Streak>,
|
||||||
|
val theme: Theme,
|
||||||
)
|
)
|
||||||
|
|
||||||
class StreakCartPresenter {
|
class StreakCartPresenter {
|
||||||
companion object {
|
companion object {
|
||||||
fun buildState(habit: Habit): StreakCardState {
|
fun buildState(habit: Habit, theme: Theme): StreakCardState {
|
||||||
return StreakCardState(
|
return StreakCardState(
|
||||||
color = habit.color,
|
color = habit.color,
|
||||||
bestStreaks = habit.streaks.getBest(10),
|
bestStreaks = habit.streaks.getBest(10),
|
||||||
|
theme = theme,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import org.isoron.uhabits.core.models.Frequency
|
|||||||
import org.isoron.uhabits.core.models.Habit
|
import org.isoron.uhabits.core.models.Habit
|
||||||
import org.isoron.uhabits.core.models.PaletteColor
|
import org.isoron.uhabits.core.models.PaletteColor
|
||||||
import org.isoron.uhabits.core.models.Reminder
|
import org.isoron.uhabits.core.models.Reminder
|
||||||
|
import org.isoron.uhabits.core.ui.views.Theme
|
||||||
|
|
||||||
data class SubtitleCardState(
|
data class SubtitleCardState(
|
||||||
val color: PaletteColor,
|
val color: PaletteColor,
|
||||||
@@ -32,12 +33,14 @@ data class SubtitleCardState(
|
|||||||
val reminder: Reminder?,
|
val reminder: Reminder?,
|
||||||
val targetValue: Double,
|
val targetValue: Double,
|
||||||
val unit: String,
|
val unit: String,
|
||||||
|
val theme: Theme,
|
||||||
)
|
)
|
||||||
|
|
||||||
class SubtitleCardPresenter {
|
class SubtitleCardPresenter {
|
||||||
companion object {
|
companion object {
|
||||||
fun buildState(
|
fun buildState(
|
||||||
habit: Habit,
|
habit: Habit,
|
||||||
|
theme: Theme,
|
||||||
): SubtitleCardState = SubtitleCardState(
|
): SubtitleCardState = SubtitleCardState(
|
||||||
color = habit.color,
|
color = habit.color,
|
||||||
frequency = habit.frequency,
|
frequency = habit.frequency,
|
||||||
@@ -46,6 +49,7 @@ class SubtitleCardPresenter {
|
|||||||
reminder = habit.reminder,
|
reminder = habit.reminder,
|
||||||
targetValue = habit.targetValue,
|
targetValue = habit.targetValue,
|
||||||
unit = habit.unit,
|
unit = habit.unit,
|
||||||
|
theme = theme,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ package org.isoron.uhabits.core.ui.screens.habits.show.views
|
|||||||
import org.isoron.uhabits.core.models.Habit
|
import org.isoron.uhabits.core.models.Habit
|
||||||
import org.isoron.uhabits.core.models.PaletteColor
|
import org.isoron.uhabits.core.models.PaletteColor
|
||||||
import org.isoron.uhabits.core.models.groupedSum
|
import org.isoron.uhabits.core.models.groupedSum
|
||||||
|
import org.isoron.uhabits.core.ui.views.Theme
|
||||||
import org.isoron.uhabits.core.utils.DateUtils
|
import org.isoron.uhabits.core.utils.DateUtils
|
||||||
import java.util.ArrayList
|
import java.util.ArrayList
|
||||||
import java.util.Calendar
|
import java.util.Calendar
|
||||||
@@ -31,6 +32,7 @@ data class TargetCardState(
|
|||||||
val values: List<Double> = listOf(),
|
val values: List<Double> = listOf(),
|
||||||
val targets: List<Double> = listOf(),
|
val targets: List<Double> = listOf(),
|
||||||
val intervals: List<Int> = listOf(),
|
val intervals: List<Int> = listOf(),
|
||||||
|
val theme: Theme,
|
||||||
)
|
)
|
||||||
|
|
||||||
class TargetCardPresenter {
|
class TargetCardPresenter {
|
||||||
@@ -38,6 +40,7 @@ class TargetCardPresenter {
|
|||||||
fun buildState(
|
fun buildState(
|
||||||
habit: Habit,
|
habit: Habit,
|
||||||
firstWeekday: Int,
|
firstWeekday: Int,
|
||||||
|
theme: Theme,
|
||||||
): TargetCardState {
|
): TargetCardState {
|
||||||
val today = DateUtils.getTodayWithOffset()
|
val today = DateUtils.getTodayWithOffset()
|
||||||
val oldest = habit.computedEntries.getKnown().lastOrNull()?.timestamp ?: today
|
val oldest = habit.computedEntries.getKnown().lastOrNull()?.timestamp ?: today
|
||||||
@@ -106,6 +109,7 @@ class TargetCardPresenter {
|
|||||||
values = values,
|
values = values,
|
||||||
targets = targets,
|
targets = targets,
|
||||||
intervals = intervals,
|
intervals = intervals,
|
||||||
|
theme = theme,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
package org.isoron.uhabits.core.ui.views
|
package org.isoron.uhabits.core.ui.views
|
||||||
|
|
||||||
import org.isoron.platform.gui.Color
|
import org.isoron.platform.gui.Color
|
||||||
|
import org.isoron.uhabits.core.models.PaletteColor
|
||||||
|
|
||||||
abstract class Theme {
|
abstract class Theme {
|
||||||
open val appBackgroundColor = Color(0xf4f4f4)
|
open val appBackgroundColor = Color(0xf4f4f4)
|
||||||
@@ -35,6 +36,10 @@ abstract class Theme {
|
|||||||
open val toolbarBackgroundColor = Color(0xf4f4f4)
|
open val toolbarBackgroundColor = Color(0xf4f4f4)
|
||||||
open val toolbarColor = Color(0xffffff)
|
open val toolbarColor = Color(0xffffff)
|
||||||
|
|
||||||
|
fun color(paletteColor: PaletteColor): Color {
|
||||||
|
return color(paletteColor.paletteIndex)
|
||||||
|
}
|
||||||
|
|
||||||
open fun color(paletteIndex: Int): Color {
|
open fun color(paletteIndex: Int): Color {
|
||||||
return when (paletteIndex) {
|
return when (paletteIndex) {
|
||||||
0 -> Color(0xD32F2F)
|
0 -> Color(0xD32F2F)
|
||||||
@@ -109,6 +114,12 @@ open class DarkTheme : Theme() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class PureBlackTheme : DarkTheme() {
|
||||||
|
override val appBackgroundColor = Color(0x000000)
|
||||||
|
override val cardBackgroundColor = Color(0x000000)
|
||||||
|
override val lowContrastTextColor = Color(0x212121)
|
||||||
|
}
|
||||||
|
|
||||||
class WidgetTheme : LightTheme() {
|
class WidgetTheme : LightTheme() {
|
||||||
override val cardBackgroundColor = Color.TRANSPARENT
|
override val cardBackgroundColor = Color.TRANSPARENT
|
||||||
override val highContrastTextColor = Color.WHITE
|
override val highContrastTextColor = Color.WHITE
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016-2021 Álinson Santos Xavier <git@axavier.org>
|
||||||
|
*
|
||||||
|
* 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.platform.gui
|
||||||
|
|
||||||
|
import org.isoron.platform.time.LocalDate
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
class DatesTest {
|
||||||
|
@Test
|
||||||
|
fun testDatesBefore2000() {
|
||||||
|
val date = LocalDate(-1)
|
||||||
|
assertEquals(date.day, 31)
|
||||||
|
assertEquals(date.month, 12)
|
||||||
|
assertEquals(date.year, 1999)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -43,7 +43,7 @@ class HabitCardListCacheTest : BaseUnitTest() {
|
|||||||
for (i in 0..9) {
|
for (i in 0..9) {
|
||||||
if (i == 3) habitList.add(fixtures.createLongHabit()) else habitList.add(fixtures.createShortHabit())
|
if (i == 3) habitList.add(fixtures.createLongHabit()) else habitList.add(fixtures.createShortHabit())
|
||||||
}
|
}
|
||||||
cache = HabitCardListCache(habitList, commandRunner, taskRunner)
|
cache = HabitCardListCache(habitList, commandRunner, taskRunner, mock())
|
||||||
cache.setCheckmarkCount(10)
|
cache.setCheckmarkCount(10)
|
||||||
cache.refreshAllHabits()
|
cache.refreshAllHabits()
|
||||||
cache.onAttached()
|
cache.onAttached()
|
||||||
|
|||||||