Compare commits

..

15 Commits

Author SHA1 Message Date
40a4d254f5 Merge branch 'hotfix/2.1.1' 2022-09-24 19:40:04 -05:00
177d01edd9 Fix Tasker plugin; bump version to 2.1.1
Fixes #1503
2022-09-24 19:25:42 -05:00
ec1f0c5356 StackWidget: Remove unused remoteViews; fix invalid check 2022-09-09 06:02:57 -05:00
961fb7618f splitLongs: Handle NumberFormatException 2022-09-09 05:39:15 -05:00
11f726064a StackWidget: Avoid storing RemoteViews reference 2022-09-09 05:39:15 -05:00
abced92a07 Update CHANGELOG 2022-09-08 20:09:47 -05:00
cheer
eeacc5eef8 Support for Android 13 Themed Icons (#1497) 2022-09-08 20:08:12 -05:00
Eduardo Esparza
16c65f19fd fix marker scaling for numerical habits in frequency display (#1489) 2022-09-08 20:08:05 -05:00
fc402fd81b Minor fix to BarChart 2022-09-03 20:26:21 -05:00
f7c6bc716c Fix ListHabitsScreen.showColorPicker 2022-09-03 19:52:13 -05:00
2535347d5a Always update widgets on launch
Helps mitigate situations where the widgets mysteriously fail to render.
The user can always force a refresh by opening the app.
2022-09-03 19:45:31 -05:00
13af054214 Use different request codes for showNumberPicker pendingIntent
Fixes #1468
2022-09-03 18:46:52 -05:00
fcbb586e80 Use pending intent templates on StackWidget
Fixes #1463
2022-09-03 17:51:55 -05:00
Jakub Kalinowski
428bf42e79 Reimplementing the multiple popups handling in the new popup solution. (#1454)
Co-authored-by: Jakub Kalinowski <kalj@netcompany.com>
2022-08-14 13:25:01 -05:00
838e13f30c Prevent crashes in AutoBackup from blocking app launch 2022-08-13 07:25:12 -05:00
22 changed files with 188 additions and 36 deletions

View File

@@ -1,6 +1,10 @@
# Changelog # Changelog
## [2.1.0] -- Unreleased ## [2.1.1] -- 2022-09-24
### Fixed
- Fix Tasker plugin (@iSoron, #1503)
## [2.1.0] -- 2022-09-10
### Added ### Added
- Allow user to add notes to specific dates (@vbh, #1103) - Allow user to add notes to specific dates (@vbh, #1103)
- Allow user to track "at most" numerical habits (@KristianTashkov, #1101) - Allow user to track "at most" numerical habits (@KristianTashkov, #1101)
@@ -9,6 +13,7 @@
- Improve number picker (@hiqua, @iSoron, #1082, #1370) - Improve number picker (@hiqua, @iSoron, #1082, #1370)
- Add new checkmark and number picker (@iSoron, #1370) - Add new checkmark and number picker (@iSoron, #1370)
- Allow user to import numerical habits from HabitBull (@hiqua, #1278) - Allow user to import numerical habits from HabitBull (@hiqua, #1278)
- Add support for Android 13 themed icons (@cheeeeer, #1497)
### Removed ### Removed
- Hide snooze button Android 12 notifications (@hiqua, #1226) - Hide snooze button Android 12 notifications (@hiqua, #1226)
@@ -28,6 +33,7 @@
- Fix small issues in calendar chart (@kalina559, #1314) - Fix small issues in calendar chart (@kalina559, #1314)
- Resort habit list after edit (@hiqua, #1350) - Resort habit list after edit (@hiqua, #1350)
- Fix marker scaling in frequency display (@eduebernal, #1425) - Fix marker scaling in frequency display (@eduebernal, #1425)
- Fix widgets not working correctly on API 33 (@iSoron, #1488)
### Refactoring & Testing ### Refactoring & Testing
- Replace raster icons by vector assets (@kalina559) - Replace raster icons by vector assets (@kalina559)

View File

@@ -35,8 +35,8 @@ android {
compileSdk = 31 compileSdk = 31
defaultConfig { defaultConfig {
versionCode = 20100 versionCode = 20101
versionName = "2.1.0" versionName = "2.1.1"
minSdk = 23 minSdk = 23
targetSdk = 31 targetSdk = 31
applicationId = "org.isoron.uhabits" applicationId = "org.isoron.uhabits"

View File

@@ -270,7 +270,7 @@
<!-- Locale/Tasker --> <!-- Locale/Tasker -->
<receiver <receiver
android:name=".automation.FireSettingReceiver" android:name=".automation.FireSettingReceiver"
android:exported="false"> android:exported="true">
<intent-filter> <intent-filter>
<action android:name="com.twofortyfouram.locale.intent.action.FIRE_SETTING" /> <action android:name="com.twofortyfouram.locale.intent.action.FIRE_SETTING" />
</intent-filter> </intent-filter>

View File

@@ -63,7 +63,9 @@ class FrequencyChart : ScrollableChart {
private var primaryColor = 0 private var primaryColor = 0
private var isBackgroundTransparent = false private var isBackgroundTransparent = false
private lateinit var frequency: HashMap<Timestamp, Array<Int>> private lateinit var frequency: HashMap<Timestamp, Array<Int>>
private var maxFreq = 0
private var firstWeekday = Calendar.SUNDAY private var firstWeekday = Calendar.SUNDAY
private var isNumerical: Boolean = false
constructor(context: Context?) : super(context) { constructor(context: Context?) : super(context) {
init() init()
@@ -80,8 +82,14 @@ class FrequencyChart : ScrollableChart {
postInvalidate() postInvalidate()
} }
fun setIsNumerical(type: Boolean) {
isNumerical = type
postInvalidate()
}
fun setFrequency(frequency: java.util.HashMap<Timestamp, Array<Int>>) { fun setFrequency(frequency: java.util.HashMap<Timestamp, Array<Int>>) {
this.frequency = frequency this.frequency = frequency
maxFreq = getMaxFreq(frequency)
postInvalidate() postInvalidate()
} }
@@ -90,6 +98,15 @@ class FrequencyChart : ScrollableChart {
postInvalidate() postInvalidate()
} }
private fun getMaxFreq(frequency: HashMap<Timestamp, Array<Int>>): Int {
var maxValue = 1
for (values in frequency.values) for (value in values) maxValue = max(
value,
maxValue
)
return maxValue
}
fun setIsBackgroundTransparent(isBackgroundTransparent: Boolean) { fun setIsBackgroundTransparent(isBackgroundTransparent: Boolean) {
this.isBackgroundTransparent = isBackgroundTransparent this.isBackgroundTransparent = isBackgroundTransparent
initColors() initColors()
@@ -213,7 +230,7 @@ class FrequencyChart : ScrollableChart {
canvas.drawLine(rGrid.left, rGrid.top, rGrid.right, rGrid.top, pGrid!!) canvas.drawLine(rGrid.left, rGrid.top, rGrid.right, rGrid.top, pGrid!!)
} }
private fun drawMarker(canvas: Canvas, rect: RectF?, value: Int?, frequency: Int) { private fun drawMarker(canvas: Canvas, rect: RectF?, value: Int?, weekdayFrequency: Int) {
// value can be negative when the entry is skipped // value can be negative when the entry is skipped
val valueCopy = value?.let { max(0, it) } val valueCopy = value?.let { max(0, it) }
@@ -221,8 +238,8 @@ class FrequencyChart : ScrollableChart {
// maximal allowed mark radius // maximal allowed mark radius
val maxRadius = (rect.height() - 2 * padding) / 2.0f val maxRadius = (rect.height() - 2 * padding) / 2.0f
// the real mark radius is scaled down by a factor depending on the maximal frequency // the real mark radius is scaled down by a factor depending on the maximal frequency
val scalingFactor = if (isNumerical) maxFreq else weekdayFrequency
val scale = 1.0f / frequency * valueCopy!! val scale = 1.0f / scalingFactor * valueCopy!!
val radius = maxRadius * scale val radius = maxRadius * scale
val colorIndex = min((colors.size - 1), ((colors.size - 1) * scale).roundToInt()) val colorIndex = min((colors.size - 1), ((colors.size - 1) * scale).roundToInt())
pGraph!!.color = colors[colorIndex] pGraph!!.color = colors[colorIndex]
@@ -285,5 +302,6 @@ class FrequencyChart : ScrollableChart {
frequency[Timestamp(date)] = values frequency[Timestamp(date)] = values
date.add(Calendar.MONTH, -1) date.add(Calendar.MONTH, -1)
} }
maxFreq = getMaxFreq(frequency)
} }
} }

View File

@@ -21,6 +21,7 @@ package org.isoron.uhabits.activities.habits.list
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.util.Log
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
@@ -84,7 +85,6 @@ class ListHabitsActivity : AppCompatActivity(), Preferences.Listener {
Thread.setDefaultUncaughtExceptionHandler(BaseExceptionHandler(this)) Thread.setDefaultUncaughtExceptionHandler(BaseExceptionHandler(this))
component.listHabitsBehavior.onStartup() component.listHabitsBehavior.onStartup()
setContentView(rootView) setContentView(rootView)
parseIntents()
} }
override fun onPause() { override fun onPause() {
@@ -100,11 +100,17 @@ class ListHabitsActivity : AppCompatActivity(), Preferences.Listener {
rootView.postInvalidate() rootView.postInvalidate()
midnightTimer.onResume() midnightTimer.onResume()
taskRunner.run { taskRunner.run {
AutoBackup(this@ListHabitsActivity).run() try {
AutoBackup(this@ListHabitsActivity).run()
appComponent.widgetUpdater.updateWidgets()
} catch (e: Exception) {
Log.e("ListHabitActivity", "TaskRunner failed", e)
}
} }
if (prefs.theme == THEME_DARK && prefs.isPureBlackEnabled != pureBlack) { if (prefs.theme == THEME_DARK && prefs.isPureBlackEnabled != pureBlack) {
restartWithFade(ListHabitsActivity::class.java) restartWithFade(ListHabitsActivity::class.java)
} }
parseIntents()
super.onResume() super.onResume()
} }
@@ -124,6 +130,7 @@ class ListHabitsActivity : AppCompatActivity(), Preferences.Listener {
} }
private fun parseIntents() { private fun parseIntents() {
if (intent == null) return
if (intent.action == ACTION_EDIT) { if (intent.action == ACTION_EDIT) {
val habitId = intent.extras?.getLong("habit") val habitId = intent.extras?.getLong("habit")
val timestamp = intent.extras?.getLong("timestamp") val timestamp = intent.extras?.getLong("timestamp")
@@ -132,6 +139,12 @@ class ListHabitsActivity : AppCompatActivity(), Preferences.Listener {
component.listHabitsBehavior.onEdit(habit, Timestamp(timestamp)) component.listHabitsBehavior.onEdit(habit, Timestamp(timestamp))
} }
} }
intent = null
}
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
setIntent(intent)
} }
companion object { companion object {

View File

@@ -225,7 +225,7 @@ class ListHabitsScreen
override fun showColorPicker(defaultColor: PaletteColor, callback: OnColorPickedCallback) { override fun showColorPicker(defaultColor: PaletteColor, callback: OnColorPickedCallback) {
val picker = colorPickerFactory.create(defaultColor, themeSwitcher.currentTheme!!) val picker = colorPickerFactory.create(defaultColor, themeSwitcher.currentTheme!!)
picker.setListener(callback) picker.setListener(callback)
picker.dialog?.dismissCurrentAndShow() picker.dismissCurrentAndShow(activity.supportFragmentManager, "picker")
} }
override fun showNumberPopup( override fun showNumberPopup(

View File

@@ -33,6 +33,7 @@ class FrequencyCardView(context: Context, attrs: AttributeSet) : LinearLayout(co
fun setState(state: FrequencyCardState) { fun setState(state: FrequencyCardState) {
val androidColor = state.theme.color(state.color).toInt() val androidColor = state.theme.color(state.color).toInt()
binding.frequencyChart.setFrequency(state.frequency) binding.frequencyChart.setFrequency(state.frequency)
binding.frequencyChart.setIsNumerical(state.isNumerical)
binding.frequencyChart.setFirstWeekday(state.firstWeekday) binding.frequencyChart.setFirstWeekday(state.firstWeekday)
binding.title.setTextColor(androidColor) binding.title.setTextColor(androidColor)
binding.frequencyChart.setColor(androidColor) binding.frequencyChart.setColor(androidColor)

View File

@@ -21,13 +21,16 @@ package org.isoron.uhabits.intents
import android.app.PendingIntent import android.app.PendingIntent
import android.app.PendingIntent.FLAG_IMMUTABLE import android.app.PendingIntent.FLAG_IMMUTABLE
import android.app.PendingIntent.FLAG_MUTABLE
import android.app.PendingIntent.FLAG_UPDATE_CURRENT import android.app.PendingIntent.FLAG_UPDATE_CURRENT
import android.app.PendingIntent.getActivity import android.app.PendingIntent.getActivity
import android.app.PendingIntent.getBroadcast import android.app.PendingIntent.getBroadcast
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.Build
import org.isoron.uhabits.activities.habits.list.ListHabitsActivity import org.isoron.uhabits.activities.habits.list.ListHabitsActivity
import org.isoron.uhabits.activities.habits.show.ShowHabitActivity
import org.isoron.uhabits.core.AppScope import org.isoron.uhabits.core.AppScope
import org.isoron.uhabits.core.models.Habit import org.isoron.uhabits.core.models.Habit
import org.isoron.uhabits.core.models.Timestamp import org.isoron.uhabits.core.models.Timestamp
@@ -89,6 +92,20 @@ class PendingIntentFactory
) )
.getPendingIntent(0, FLAG_IMMUTABLE or FLAG_UPDATE_CURRENT)!! .getPendingIntent(0, FLAG_IMMUTABLE or FLAG_UPDATE_CURRENT)!!
fun showHabitTemplate(): PendingIntent {
return getActivity(
context,
0,
Intent(context, ShowHabitActivity::class.java),
getIntentTemplateFlags()
)
}
fun showHabitFillIn(habit: Habit) =
Intent().apply {
data = Uri.parse(habit.uriString)
}
fun showReminder( fun showReminder(
habit: Habit, habit: Habit,
reminderTime: Long?, reminderTime: Long?,
@@ -142,7 +159,7 @@ class PendingIntentFactory
fun showNumberPicker(habit: Habit, timestamp: Timestamp): PendingIntent? { fun showNumberPicker(habit: Habit, timestamp: Timestamp): PendingIntent? {
return getActivity( return getActivity(
context, context,
0, (habit.id!! % Integer.MAX_VALUE).toInt() + 1,
Intent(context, ListHabitsActivity::class.java).apply { Intent(context, ListHabitsActivity::class.java).apply {
action = ListHabitsActivity.ACTION_EDIT action = ListHabitsActivity.ACTION_EDIT
putExtra("habit", habit.id) putExtra("habit", habit.id)
@@ -151,4 +168,43 @@ class PendingIntentFactory
FLAG_IMMUTABLE or FLAG_UPDATE_CURRENT FLAG_IMMUTABLE or FLAG_UPDATE_CURRENT
) )
} }
fun showNumberPickerTemplate(): PendingIntent {
return getActivity(
context,
1,
Intent(context, ListHabitsActivity::class.java).apply {
action = ListHabitsActivity.ACTION_EDIT
},
getIntentTemplateFlags()
)
}
fun showNumberPickerFillIn(habit: Habit, timestamp: Timestamp) = Intent().apply {
putExtra("habit", habit.id)
putExtra("timestamp", timestamp.unixTime)
}
private fun getIntentTemplateFlags(): Int {
var flags = 0
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
flags = flags or FLAG_MUTABLE
}
return flags
}
fun toggleCheckmarkTemplate(): PendingIntent =
getBroadcast(
context,
2,
Intent(context, WidgetReceiver::class.java).apply {
action = WidgetReceiver.ACTION_TOGGLE_REPETITION
},
getIntentTemplateFlags()
)
fun toggleCheckmarkFillIn(habit: Habit, timestamp: Timestamp) = Intent().apply {
data = Uri.parse(habit.uriString)
putExtra("timestamp", timestamp.unixTime)
}
} }

View File

@@ -49,6 +49,7 @@ class FrequencyWidget(
(widgetView.dataView as FrequencyChart).apply { (widgetView.dataView as FrequencyChart).apply {
setFirstWeekday(firstWeekday) setFirstWeekday(firstWeekday)
setColor(WidgetTheme().color(habit.color).toInt()) setColor(WidgetTheme().color(habit.color).toInt())
setIsNumerical(habit.isNumerical)
setFrequency(habit.originalEntries.computeWeekdayFrequency(habit.isNumerical)) setFrequency(habit.originalEntries.computeWeekdayFrequency(habit.isNumerical))
} }
} }

View File

@@ -73,6 +73,10 @@ class StackWidget(
StackWidgetType.getStackWidgetAdapterViewId(widgetType), StackWidgetType.getStackWidgetAdapterViewId(widgetType),
StackWidgetType.getStackWidgetEmptyViewId(widgetType) StackWidgetType.getStackWidgetEmptyViewId(widgetType)
) )
remoteViews.setPendingIntentTemplate(
StackWidgetType.getStackWidgetAdapterViewId(widgetType),
StackWidgetType.getPendingIntentTemplate(pendingIntentFactory, widgetType, habits)
)
return remoteViews return remoteViews
} }
} }

View File

@@ -29,11 +29,14 @@ import android.widget.RemoteViewsService
import android.widget.RemoteViewsService.RemoteViewsFactory import android.widget.RemoteViewsService.RemoteViewsFactory
import org.isoron.platform.utils.StringUtils.Companion.splitLongs import org.isoron.platform.utils.StringUtils.Companion.splitLongs
import org.isoron.uhabits.HabitsApplication import org.isoron.uhabits.HabitsApplication
import org.isoron.uhabits.R
import org.isoron.uhabits.core.models.Habit import org.isoron.uhabits.core.models.Habit
import org.isoron.uhabits.core.models.HabitNotFoundException import org.isoron.uhabits.core.models.HabitNotFoundException
import org.isoron.uhabits.core.preferences.Preferences import org.isoron.uhabits.core.preferences.Preferences
import org.isoron.uhabits.core.utils.DateUtils.Companion.getToday
import org.isoron.uhabits.intents.IntentFactory
import org.isoron.uhabits.intents.PendingIntentFactory
import org.isoron.uhabits.utils.InterfaceUtils.dpToPixels import org.isoron.uhabits.utils.InterfaceUtils.dpToPixels
import java.util.ArrayList
class StackWidgetService : RemoteViewsService() { class StackWidgetService : RemoteViewsService() {
override fun onGetViewFactory(intent: Intent): RemoteViewsFactory { override fun onGetViewFactory(intent: Intent): RemoteViewsFactory {
@@ -54,7 +57,6 @@ internal class StackRemoteViewsFactory(private val context: Context, intent: Int
) )
private val habitIds: LongArray private val habitIds: LongArray
private val widgetType: StackWidgetType private val widgetType: StackWidgetType
private var remoteViews = ArrayList<RemoteViews>()
override fun onCreate() {} override fun onCreate() {}
override fun onDestroy() {} override fun onDestroy() {}
override fun getCount(): Int { override fun getCount(): Int {
@@ -85,8 +87,26 @@ 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 started")
return if (0 <= position && position < remoteViews.size) remoteViews[position] else null if (position < 0 || position >= habitIds.size) return null
val app = context.applicationContext as HabitsApplication
val prefs = app.component.preferences
val habitList = app.component.habitList
val options = AppWidgetManager.getInstance(context).getAppWidgetOptions(widgetId)
if (Looper.myLooper() == null) Looper.prepare()
val habits = habitIds.map { habitList.getById(it) ?: throw HabitNotFoundException() }
val h = habits[position]
val widget = constructWidget(h, prefs)
widget.setDimensions(getDimensionsFromOptions(context, options))
val landscapeViews = widget.landscapeRemoteViews
val portraitViews = widget.portraitRemoteViews
val factory = PendingIntentFactory(context, IntentFactory())
val intent = StackWidgetType.getIntentFillIn(factory, widgetType, h, habits, getToday())
landscapeViews.setOnClickFillInIntent(R.id.button, intent)
portraitViews.setOnClickFillInIntent(R.id.button, intent)
val remoteViews = RemoteViews(landscapeViews, portraitViews)
Log.i("StackRemoteViewsFactory", "getViewAt $position ended")
return remoteViews
} }
private fun constructWidget( private fun constructWidget(
@@ -131,24 +151,6 @@ internal class StackRemoteViewsFactory(private val context: Context, intent: Int
} }
override fun onDataSetChanged() { override fun onDataSetChanged() {
Log.i("StackRemoteViewsFactory", "onDataSetChanged started")
val app = context.applicationContext as HabitsApplication
val prefs = app.component.preferences
val habitList = app.component.habitList
val options = AppWidgetManager.getInstance(context).getAppWidgetOptions(widgetId)
val newRemoteViews = ArrayList<RemoteViews>()
if (Looper.myLooper() == null) Looper.prepare()
for (id in habitIds) {
val h = habitList.getById(id) ?: throw HabitNotFoundException()
val widget = constructWidget(h, prefs)
widget.setDimensions(getDimensionsFromOptions(context, options))
val landscapeViews = widget.landscapeRemoteViews
val portraitViews = widget.portraitRemoteViews
newRemoteViews.add(RemoteViews(landscapeViews, portraitViews))
Log.i("StackRemoteViewsFactory", "onDataSetChanged constructed widget $id")
}
remoteViews = newRemoteViews
Log.i("StackRemoteViewsFactory", "onDataSetChanged ended")
} }
init { init {

View File

@@ -18,7 +18,12 @@
*/ */
package org.isoron.uhabits.widgets package org.isoron.uhabits.widgets
import android.app.PendingIntent
import android.content.Intent
import org.isoron.uhabits.R import org.isoron.uhabits.R
import org.isoron.uhabits.core.models.Habit
import org.isoron.uhabits.core.models.Timestamp
import org.isoron.uhabits.intents.PendingIntentFactory
import java.lang.IllegalStateException import java.lang.IllegalStateException
enum class StackWidgetType(val value: Int) { enum class StackWidgetType(val value: Int) {
@@ -73,5 +78,39 @@ enum class StackWidgetType(val value: Int) {
else -> throw IllegalStateException() else -> throw IllegalStateException()
} }
} }
fun getPendingIntentTemplate(
factory: PendingIntentFactory,
widgetType: StackWidgetType,
habits: List<Habit>
): PendingIntent {
val containsNumerical = habits.any { it.isNumerical }
return when (widgetType) {
CHECKMARK -> if (containsNumerical) {
factory.showNumberPickerTemplate()
} else {
factory.toggleCheckmarkTemplate()
}
FREQUENCY, SCORE, HISTORY, STREAKS, TARGET -> factory.showHabitTemplate()
}
}
fun getIntentFillIn(
factory: PendingIntentFactory,
widgetType: StackWidgetType,
habit: Habit,
allHabitsInStackWidget: List<Habit>,
timestamp: Timestamp
): Intent {
val containsNumerical = allHabitsInStackWidget.any { it.isNumerical }
return when (widgetType) {
CHECKMARK -> if (containsNumerical) {
factory.showNumberPickerFillIn(habit, timestamp)
} else {
factory.toggleCheckmarkFillIn(habit, timestamp)
}
FREQUENCY, SCORE, HISTORY, STREAKS, TARGET -> factory.showHabitFillIn(habit)
}
}
} }
} }

View File

@@ -1,6 +1,9 @@
2.1.1:
* Fix Tasker plugin
2.1: 2.1:
* Add notes to specific dates * Add notes to specific dates
* Track at-most measurable habits * Track at-most measurable habits
* Add skips to measurable habits * Add skips to measurable habits
* Bring back custom frequencies * Bring back custom frequencies
* Other minor improvements and bug fixes * Other minor improvements and bug fixes

View File

@@ -21,4 +21,5 @@
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/> <background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/> <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
<monochrome android:drawable="@mipmap/ic_launcher_monochrome"/>
</adaptive-icon> </adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

View File

@@ -25,6 +25,12 @@ class StringUtils {
fun joinLongs(values: LongArray): String = values.joinToString(separator = ",") fun joinLongs(values: LongArray): String = values.joinToString(separator = ",")
fun splitLongs(str: String): LongArray = str.split(",").map { it.toLong() }.toLongArray() fun splitLongs(str: String): LongArray {
return try {
str.split(",").map { it.toLong() }.toLongArray()
} catch (e: NumberFormatException) {
LongArray(0)
}
}
} }
} }

View File

@@ -30,6 +30,7 @@ data class FrequencyCardState(
val firstWeekday: Int, val firstWeekday: Int,
val frequency: HashMap<Timestamp, Array<Int>>, val frequency: HashMap<Timestamp, Array<Int>>,
val theme: Theme, val theme: Theme,
val isNumerical: Boolean
) )
class FrequencyCardPresenter { class FrequencyCardPresenter {
@@ -40,6 +41,7 @@ class FrequencyCardPresenter {
theme: Theme theme: Theme
) = FrequencyCardState( ) = FrequencyCardState(
color = habit.color, color = habit.color,
isNumerical = habit.isNumerical,
frequency = habit.originalEntries.computeWeekdayFrequency( frequency = habit.originalEntries.computeWeekdayFrequency(
isNumerical = habit.isNumerical isNumerical = habit.isNumerical
), ),

View File

@@ -92,7 +92,7 @@ class BarChart(
val r = round(barWidth * 0.15) val r = round(barWidth * 0.15)
if (2 * r < barHeight) { if (2 * r < barHeight) {
canvas.fillRect(x, y + r, barWidth, barHeight - r) canvas.fillRect(x, y + r, barWidth, barHeight - r)
canvas.fillRect(x + r, y, barWidth - 2 * r, r) canvas.fillRect(x + r, y, barWidth - 2 * r, r + 1)
canvas.fillCircle(x + r, y + r, r) canvas.fillCircle(x + r, y + r, r)
canvas.fillCircle(x + barWidth - r, y + r, r) canvas.fillCircle(x + barWidth - r, y + r, r)
} else { } else {