Compare commits
3 Commits
53c270ee12
...
0eae43fe55
| Author | SHA1 | Date | |
|---|---|---|---|
| 0eae43fe55 | |||
| c0fcd4e763 | |||
| 79e2402c9d |
@@ -7,7 +7,7 @@
|
|||||||
- Allow user to add skips to measurable habits (@kalina559, #1319)
|
- Allow user to add skips to measurable habits (@kalina559, #1319)
|
||||||
- Bring back custom frequencies (x times in y days) (@hiqua, #1079)
|
- Bring back custom frequencies (x times in y days) (@hiqua, #1079)
|
||||||
- Improve number picker (@hiqua, @iSoron, #1082, #1370)
|
- Improve number picker (@hiqua, @iSoron, #1082, #1370)
|
||||||
- Add new checkmark state 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)
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
@@ -20,19 +20,20 @@
|
|||||||
- Add delay after toggling a habit (@hiqua, @kalina559, #1147)
|
- Add delay after toggling a habit (@hiqua, @kalina559, #1147)
|
||||||
- Small theme improvements (@KristianTashkov, #1113)
|
- Small theme improvements (@KristianTashkov, #1113)
|
||||||
- Left-align habit notes (@iSoron)
|
- Left-align habit notes (@iSoron)
|
||||||
|
- Increase target SDK to 31 (@hiqua)
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Fix small dialog buttons (@kalina559, #1096)
|
- Fix small dialog buttons (@kalina559, #1096)
|
||||||
- Fix invalid CSV files (@hiqua, #1177)
|
- Fix invalid CSV files (@hiqua, #1177)
|
||||||
- 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)
|
||||||
|
|
||||||
### Refactoring & Testing
|
### Refactoring & Testing
|
||||||
- Replace raster icons by vector assets (@kalina559)
|
- Replace raster icons by vector assets (@kalina559)
|
||||||
- Remove JVM dependencies from uhabits-core module (@sgallese)
|
- Remove JVM dependencies from uhabits-core module (@sgallese)
|
||||||
- Add various missing tests (@sgallese)
|
- Add various missing tests (@sgallese)
|
||||||
- Upgrade project dependencies (@hiqua, @sgallese)
|
- Upgrade project dependencies (@hiqua, @sgallese)
|
||||||
- Increase target SDK to 31 (@hiqua)
|
|
||||||
|
|
||||||
## [2.0.3] - 2021-08-21
|
## [2.0.3] - 2021-08-21
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 9.0 KiB After Width: | Height: | Size: 8.6 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 9.0 KiB After Width: | Height: | Size: 8.6 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 22 KiB |
@@ -119,18 +119,6 @@
|
|||||||
android:value=".activities.habits.list.ListHabitsActivity" />
|
android:value=".activities.habits.list.ListHabitsActivity" />
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
|
||||||
android:name=".widgets.activities.NumericalCheckmarkWidgetActivity"
|
|
||||||
android:excludeFromRecents="true"
|
|
||||||
android:exported="true"
|
|
||||||
android:label="NumericalCheckmarkWidget"
|
|
||||||
android:noHistory="true"
|
|
||||||
android:theme="@style/Theme.Transparent">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="org.isoron.uhabits.ACTION_SHOW_NUMERICAL_VALUE_ACTIVITY" />
|
|
||||||
</intent-filter>
|
|
||||||
</activity>
|
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".notifications.SnoozeDelayPickerActivity"
|
android:name=".notifications.SnoozeDelayPickerActivity"
|
||||||
android:excludeFromRecents="true"
|
android:excludeFromRecents="true"
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ class NumberPopup(
|
|||||||
private val anchor: View,
|
private val anchor: View,
|
||||||
) {
|
) {
|
||||||
var onToggle: (Double, String) -> Unit = { _, _ -> }
|
var onToggle: (Double, String) -> Unit = { _, _ -> }
|
||||||
|
var onDismiss: () -> Unit = {}
|
||||||
private val originalValue = value
|
private val originalValue = value
|
||||||
private lateinit var dialog: Dialog
|
private lateinit var dialog: Dialog
|
||||||
|
|
||||||
@@ -81,6 +82,9 @@ class NumberPopup(
|
|||||||
)
|
)
|
||||||
setBackgroundDrawableResource(android.R.color.transparent)
|
setBackgroundDrawableResource(android.R.color.transparent)
|
||||||
}
|
}
|
||||||
|
dialog.setOnDismissListener {
|
||||||
|
onDismiss()
|
||||||
|
}
|
||||||
|
|
||||||
view.value.setOnKeyListener { _, keyCode, event ->
|
view.value.setOnKeyListener { _, keyCode, event ->
|
||||||
if (event.action == ACTION_DOWN && keyCode == KEYCODE_ENTER) {
|
if (event.action == ACTION_DOWN && keyCode == KEYCODE_ENTER) {
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import org.isoron.uhabits.core.utils.DateUtils.Companion.getShortWeekdayNames
|
|||||||
import org.isoron.uhabits.core.utils.DateUtils.Companion.getStartOfTodayCalendar
|
import org.isoron.uhabits.core.utils.DateUtils.Companion.getStartOfTodayCalendar
|
||||||
import org.isoron.uhabits.core.utils.DateUtils.Companion.getStartOfTodayCalendarWithOffset
|
import org.isoron.uhabits.core.utils.DateUtils.Companion.getStartOfTodayCalendarWithOffset
|
||||||
import org.isoron.uhabits.core.utils.DateUtils.Companion.getWeekdaySequence
|
import org.isoron.uhabits.core.utils.DateUtils.Companion.getWeekdaySequence
|
||||||
|
import org.isoron.uhabits.core.utils.DateUtils.Companion.getWeekdaysInMonth
|
||||||
import org.isoron.uhabits.utils.ColorUtils.mixColors
|
import org.isoron.uhabits.utils.ColorUtils.mixColors
|
||||||
import org.isoron.uhabits.utils.StyledResources
|
import org.isoron.uhabits.utils.StyledResources
|
||||||
import org.isoron.uhabits.utils.toSimpleDataFormat
|
import org.isoron.uhabits.utils.toSimpleDataFormat
|
||||||
@@ -62,7 +63,6 @@ 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
|
||||||
|
|
||||||
constructor(context: Context?) : super(context) {
|
constructor(context: Context?) : super(context) {
|
||||||
@@ -82,7 +82,6 @@ class FrequencyChart : ScrollableChart {
|
|||||||
|
|
||||||
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()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,15 +90,6 @@ 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()
|
||||||
@@ -166,6 +156,7 @@ class FrequencyChart : ScrollableChart {
|
|||||||
|
|
||||||
private fun drawColumn(canvas: Canvas, rect: RectF?, date: GregorianCalendar) {
|
private fun drawColumn(canvas: Canvas, rect: RectF?, date: GregorianCalendar) {
|
||||||
val values = frequency[Timestamp(date)]
|
val values = frequency[Timestamp(date)]
|
||||||
|
val weekDaysInMonth = getWeekdaysInMonth(Timestamp(date))
|
||||||
val rowHeight = rect!!.height() / 8.0f
|
val rowHeight = rect!!.height() / 8.0f
|
||||||
prevRect!!.set(rect)
|
prevRect!!.set(rect)
|
||||||
val localeWeekdayList: Array<Int> = getWeekdaySequence(firstWeekday)
|
val localeWeekdayList: Array<Int> = getWeekdaySequence(firstWeekday)
|
||||||
@@ -174,7 +165,7 @@ class FrequencyChart : ScrollableChart {
|
|||||||
rect.offset(prevRect!!.left, prevRect!!.top + baseSize * j)
|
rect.offset(prevRect!!.left, prevRect!!.top + baseSize * j)
|
||||||
val i = localeWeekdayList[j] % 7
|
val i = localeWeekdayList[j] % 7
|
||||||
if (values != null)
|
if (values != null)
|
||||||
drawMarker(canvas, rect, values[i])
|
drawMarker(canvas, rect, values[i], weekDaysInMonth[i])
|
||||||
rect.offset(0f, rowHeight)
|
rect.offset(0f, rowHeight)
|
||||||
}
|
}
|
||||||
drawFooter(canvas, rect, date)
|
drawFooter(canvas, rect, date)
|
||||||
@@ -222,7 +213,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?) {
|
private fun drawMarker(canvas: Canvas, rect: RectF?, value: Int?, frequency: 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) }
|
||||||
|
|
||||||
@@ -230,7 +221,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 scale = 1.0f / maxFreq * valueCopy!!
|
|
||||||
|
val scale = 1.0f / frequency * 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]
|
||||||
@@ -293,6 +285,5 @@ class FrequencyChart : ScrollableChart {
|
|||||||
frequency[Timestamp(date)] = values
|
frequency[Timestamp(date)] = values
|
||||||
date.add(Calendar.MONTH, -1)
|
date.add(Calendar.MONTH, -1)
|
||||||
}
|
}
|
||||||
maxFreq = getMaxFreq(frequency)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import kotlinx.coroutines.Dispatchers
|
|||||||
import org.isoron.uhabits.BaseExceptionHandler
|
import org.isoron.uhabits.BaseExceptionHandler
|
||||||
import org.isoron.uhabits.HabitsApplication
|
import org.isoron.uhabits.HabitsApplication
|
||||||
import org.isoron.uhabits.activities.habits.list.views.HabitCardListAdapter
|
import org.isoron.uhabits.activities.habits.list.views.HabitCardListAdapter
|
||||||
|
import org.isoron.uhabits.core.models.Timestamp
|
||||||
import org.isoron.uhabits.core.preferences.Preferences
|
import org.isoron.uhabits.core.preferences.Preferences
|
||||||
import org.isoron.uhabits.core.tasks.TaskRunner
|
import org.isoron.uhabits.core.tasks.TaskRunner
|
||||||
import org.isoron.uhabits.core.ui.ThemeSwitcher.Companion.THEME_DARK
|
import org.isoron.uhabits.core.ui.ThemeSwitcher.Companion.THEME_DARK
|
||||||
@@ -36,11 +37,15 @@ import org.isoron.uhabits.core.utils.MidnightTimer
|
|||||||
import org.isoron.uhabits.database.AutoBackup
|
import org.isoron.uhabits.database.AutoBackup
|
||||||
import org.isoron.uhabits.inject.ActivityContextModule
|
import org.isoron.uhabits.inject.ActivityContextModule
|
||||||
import org.isoron.uhabits.inject.DaggerHabitsActivityComponent
|
import org.isoron.uhabits.inject.DaggerHabitsActivityComponent
|
||||||
|
import org.isoron.uhabits.inject.HabitsActivityComponent
|
||||||
|
import org.isoron.uhabits.inject.HabitsApplicationComponent
|
||||||
import org.isoron.uhabits.utils.restartWithFade
|
import org.isoron.uhabits.utils.restartWithFade
|
||||||
|
|
||||||
class ListHabitsActivity : AppCompatActivity(), Preferences.Listener {
|
class ListHabitsActivity : AppCompatActivity(), Preferences.Listener {
|
||||||
|
|
||||||
var pureBlack: Boolean = false
|
var pureBlack: Boolean = false
|
||||||
|
lateinit var appComponent: HabitsApplicationComponent
|
||||||
|
lateinit var component: HabitsActivityComponent
|
||||||
lateinit var taskRunner: TaskRunner
|
lateinit var taskRunner: TaskRunner
|
||||||
lateinit var adapter: HabitCardListAdapter
|
lateinit var adapter: HabitCardListAdapter
|
||||||
lateinit var rootView: ListHabitsRootView
|
lateinit var rootView: ListHabitsRootView
|
||||||
@@ -59,8 +64,8 @@ class ListHabitsActivity : AppCompatActivity(), Preferences.Listener {
|
|||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
val appComponent = (applicationContext as HabitsApplication).component
|
appComponent = (applicationContext as HabitsApplication).component
|
||||||
val component = DaggerHabitsActivityComponent
|
component = DaggerHabitsActivityComponent
|
||||||
.builder()
|
.builder()
|
||||||
.activityContextModule(ActivityContextModule(this))
|
.activityContextModule(ActivityContextModule(this))
|
||||||
.habitsApplicationComponent(appComponent)
|
.habitsApplicationComponent(appComponent)
|
||||||
@@ -79,6 +84,7 @@ 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() {
|
||||||
@@ -116,4 +122,19 @@ class ListHabitsActivity : AppCompatActivity(), Preferences.Listener {
|
|||||||
super.onActivityResult(request, result, data)
|
super.onActivityResult(request, result, data)
|
||||||
screen.onResult(request, result, data)
|
screen.onResult(request, result, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun parseIntents() {
|
||||||
|
if (intent.action == ACTION_EDIT) {
|
||||||
|
val habitId = intent.extras?.getLong("habit")
|
||||||
|
val timestamp = intent.extras?.getLong("timestamp")
|
||||||
|
if (habitId != null && timestamp != null) {
|
||||||
|
val habit = appComponent.habitList.getById(habitId)!!
|
||||||
|
component.listHabitsBehavior.onEdit(habit, Timestamp(timestamp))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val ACTION_EDIT = "org.isoron.uhabits.ACTION_EDIT"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,10 +22,12 @@ 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_UPDATE_CURRENT
|
import android.app.PendingIntent.FLAG_UPDATE_CURRENT
|
||||||
|
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 org.isoron.uhabits.activities.habits.list.ListHabitsActivity
|
||||||
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
|
||||||
@@ -127,25 +129,6 @@ class PendingIntentFactory
|
|||||||
FLAG_IMMUTABLE or FLAG_UPDATE_CURRENT
|
FLAG_IMMUTABLE or FLAG_UPDATE_CURRENT
|
||||||
)
|
)
|
||||||
|
|
||||||
fun setNumericalValue(
|
|
||||||
widgetContext: Context,
|
|
||||||
habit: Habit,
|
|
||||||
numericalValue: Int,
|
|
||||||
timestamp: Long?
|
|
||||||
):
|
|
||||||
PendingIntent =
|
|
||||||
getBroadcast(
|
|
||||||
widgetContext,
|
|
||||||
2,
|
|
||||||
Intent(widgetContext, WidgetReceiver::class.java).apply {
|
|
||||||
data = Uri.parse(habit.uriString)
|
|
||||||
action = WidgetReceiver.ACTION_SET_NUMERICAL_VALUE
|
|
||||||
putExtra("numericalValue", numericalValue)
|
|
||||||
if (timestamp != null) putExtra("timestamp", timestamp)
|
|
||||||
},
|
|
||||||
FLAG_IMMUTABLE or FLAG_UPDATE_CURRENT
|
|
||||||
)
|
|
||||||
|
|
||||||
fun updateWidgets(): PendingIntent =
|
fun updateWidgets(): PendingIntent =
|
||||||
getBroadcast(
|
getBroadcast(
|
||||||
context,
|
context,
|
||||||
@@ -155,4 +138,17 @@ class PendingIntentFactory
|
|||||||
},
|
},
|
||||||
FLAG_IMMUTABLE or FLAG_UPDATE_CURRENT
|
FLAG_IMMUTABLE or FLAG_UPDATE_CURRENT
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fun showNumberPicker(habit: Habit, timestamp: Timestamp): PendingIntent? {
|
||||||
|
return getActivity(
|
||||||
|
context,
|
||||||
|
0,
|
||||||
|
Intent(context, ListHabitsActivity::class.java).apply {
|
||||||
|
action = ListHabitsActivity.ACTION_EDIT
|
||||||
|
putExtra("habit", habit.id)
|
||||||
|
putExtra("timestamp", timestamp.unixTime)
|
||||||
|
},
|
||||||
|
FLAG_IMMUTABLE or FLAG_UPDATE_CURRENT
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ class AndroidNotificationTray
|
|||||||
val enterAction = Action(
|
val enterAction = Action(
|
||||||
R.drawable.ic_action_check,
|
R.drawable.ic_action_check,
|
||||||
context.getString(R.string.enter),
|
context.getString(R.string.enter),
|
||||||
pendingIntents.setNumericalValue(context, habit, 0, null)
|
pendingIntents.showNumberPicker(habit, timestamp)
|
||||||
)
|
)
|
||||||
|
|
||||||
val wearableBg = decodeResource(context.resources, R.drawable.stripe)
|
val wearableBg = decodeResource(context.resources, R.drawable.stripe)
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ import org.isoron.uhabits.HabitsApplication
|
|||||||
import org.isoron.uhabits.core.ui.widgets.WidgetBehavior
|
import org.isoron.uhabits.core.ui.widgets.WidgetBehavior
|
||||||
import org.isoron.uhabits.inject.HabitsApplicationComponent
|
import org.isoron.uhabits.inject.HabitsApplicationComponent
|
||||||
import org.isoron.uhabits.intents.IntentParser.CheckmarkIntentData
|
import org.isoron.uhabits.intents.IntentParser.CheckmarkIntentData
|
||||||
import org.isoron.uhabits.widgets.activities.NumericalCheckmarkWidgetActivity
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Android BroadcastReceiver for Loop Habit Tracker.
|
* The Android BroadcastReceiver for Loop Habit Tracker.
|
||||||
@@ -96,15 +95,6 @@ class WidgetReceiver : BroadcastReceiver() {
|
|||||||
data.timestamp
|
data.timestamp
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
ACTION_SET_NUMERICAL_VALUE -> {
|
|
||||||
context.sendBroadcast(Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS))
|
|
||||||
val numberSelectorIntent = Intent(context, NumericalCheckmarkWidgetActivity::class.java)
|
|
||||||
numberSelectorIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
|
||||||
numberSelectorIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
|
|
||||||
numberSelectorIntent.action = NumericalCheckmarkWidgetActivity.ACTION_SHOW_NUMERICAL_VALUE_ACTIVITY
|
|
||||||
parser.copyIntentData(intent, numberSelectorIntent)
|
|
||||||
context.startActivity(numberSelectorIntent)
|
|
||||||
}
|
|
||||||
ACTION_UPDATE_WIDGETS_VALUE -> {
|
ACTION_UPDATE_WIDGETS_VALUE -> {
|
||||||
widgetUpdater.updateWidgets()
|
widgetUpdater.updateWidgets()
|
||||||
widgetUpdater.scheduleStartDayWidgetUpdate()
|
widgetUpdater.scheduleStartDayWidgetUpdate()
|
||||||
@@ -126,7 +116,6 @@ class WidgetReceiver : BroadcastReceiver() {
|
|||||||
const val ACTION_DISMISS_REMINDER = "org.isoron.uhabits.ACTION_DISMISS_REMINDER"
|
const val ACTION_DISMISS_REMINDER = "org.isoron.uhabits.ACTION_DISMISS_REMINDER"
|
||||||
const val ACTION_REMOVE_REPETITION = "org.isoron.uhabits.ACTION_REMOVE_REPETITION"
|
const val ACTION_REMOVE_REPETITION = "org.isoron.uhabits.ACTION_REMOVE_REPETITION"
|
||||||
const val ACTION_TOGGLE_REPETITION = "org.isoron.uhabits.ACTION_TOGGLE_REPETITION"
|
const val ACTION_TOGGLE_REPETITION = "org.isoron.uhabits.ACTION_TOGGLE_REPETITION"
|
||||||
const val ACTION_SET_NUMERICAL_VALUE = "org.isoron.uhabits.ACTION_SET_NUMERICAL_VALUE"
|
|
||||||
const val ACTION_UPDATE_WIDGETS_VALUE = "org.isoron.uhabits.ACTION_UPDATE_WIDGETS_VALUE"
|
const val ACTION_UPDATE_WIDGETS_VALUE = "org.isoron.uhabits.ACTION_UPDATE_WIDGETS_VALUE"
|
||||||
private const val TAG = "WidgetReceiver"
|
private const val TAG = "WidgetReceiver"
|
||||||
var lastReceivedIntent: Intent? = null
|
var lastReceivedIntent: Intent? = null
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ open class CheckmarkWidget(
|
|||||||
|
|
||||||
override fun getOnClickPendingIntent(context: Context): PendingIntent? {
|
override fun getOnClickPendingIntent(context: Context): PendingIntent? {
|
||||||
return if (habit.isNumerical) {
|
return if (habit.isNumerical) {
|
||||||
pendingIntentFactory.setNumericalValue(context, habit, 10, null)
|
pendingIntentFactory.showNumberPicker(habit, DateUtils.getToday())
|
||||||
} else {
|
} else {
|
||||||
pendingIntentFactory.toggleCheckmark(habit, null)
|
pendingIntentFactory.toggleCheckmark(habit, null)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,101 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.uhabits.widgets.activities
|
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.content.Context
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
|
|
||||||
import android.widget.FrameLayout
|
|
||||||
import org.isoron.uhabits.HabitsApplication
|
|
||||||
import org.isoron.uhabits.activities.AndroidThemeSwitcher
|
|
||||||
import org.isoron.uhabits.activities.common.dialogs.NumberPopup
|
|
||||||
import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsBehavior
|
|
||||||
import org.isoron.uhabits.core.ui.widgets.WidgetBehavior
|
|
||||||
import org.isoron.uhabits.core.utils.DateUtils
|
|
||||||
import org.isoron.uhabits.intents.IntentParser
|
|
||||||
import org.isoron.uhabits.utils.SystemUtils
|
|
||||||
import org.isoron.uhabits.widgets.WidgetUpdater
|
|
||||||
|
|
||||||
class NumericalCheckmarkWidgetActivity : Activity(), ListHabitsBehavior.NumberPickerCallback {
|
|
||||||
|
|
||||||
private lateinit var behavior: WidgetBehavior
|
|
||||||
private lateinit var data: IntentParser.CheckmarkIntentData
|
|
||||||
private lateinit var widgetUpdater: WidgetUpdater
|
|
||||||
private lateinit var rootView: View
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
rootView = FrameLayout(this)
|
|
||||||
rootView.layoutParams = FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
|
|
||||||
setContentView(rootView)
|
|
||||||
val app = this.applicationContext as HabitsApplication
|
|
||||||
val component = app.component
|
|
||||||
val parser = app.component.intentParser
|
|
||||||
data = parser.parseCheckmarkIntent(intent)
|
|
||||||
behavior = WidgetBehavior(
|
|
||||||
component.habitList,
|
|
||||||
component.commandRunner,
|
|
||||||
component.notificationTray,
|
|
||||||
component.preferences
|
|
||||||
)
|
|
||||||
widgetUpdater = component.widgetUpdater
|
|
||||||
rootView.post {
|
|
||||||
showNumberSelector(this)
|
|
||||||
}
|
|
||||||
SystemUtils.unlockScreen(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onNumberPicked(newValue: Double, notes: String) {
|
|
||||||
behavior.setValue(data.habit, data.timestamp, (newValue * 1000).toInt(), notes)
|
|
||||||
widgetUpdater.updateWidgets()
|
|
||||||
finish()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onNumberPickerDismissed() {
|
|
||||||
finish()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun showNumberSelector(context: Context) {
|
|
||||||
val app = this.applicationContext as HabitsApplication
|
|
||||||
AndroidThemeSwitcher(this, app.component.preferences).apply()
|
|
||||||
val today = DateUtils.getTodayWithOffset()
|
|
||||||
val entry = data.habit.computedEntries.get(today)
|
|
||||||
NumberPopup(
|
|
||||||
context = context,
|
|
||||||
prefs = app.component.preferences,
|
|
||||||
anchor = rootView,
|
|
||||||
notes = entry.notes,
|
|
||||||
value = entry.value / 1000.0,
|
|
||||||
).apply {
|
|
||||||
onToggle = { value, notes ->
|
|
||||||
onNumberPicked(value, notes)
|
|
||||||
finish()
|
|
||||||
overridePendingTransition(0, 0)
|
|
||||||
}
|
|
||||||
show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val ACTION_SHOW_NUMERICAL_VALUE_ACTIVITY = "org.isoron.uhabits.ACTION_SHOW_NUMERICAL_VALUE_ACTIVITY"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -19,6 +19,7 @@
|
|||||||
package org.isoron.uhabits.core.utils
|
package org.isoron.uhabits.core.utils
|
||||||
|
|
||||||
import org.isoron.uhabits.core.models.Timestamp
|
import org.isoron.uhabits.core.models.Timestamp
|
||||||
|
import java.time.YearMonth
|
||||||
import java.util.Calendar
|
import java.util.Calendar
|
||||||
import java.util.Calendar.DAY_OF_MONTH
|
import java.util.Calendar.DAY_OF_MONTH
|
||||||
import java.util.Calendar.DAY_OF_WEEK
|
import java.util.Calendar.DAY_OF_WEEK
|
||||||
@@ -178,6 +179,26 @@ abstract class DateUtils {
|
|||||||
return getWeekdayNames(GregorianCalendar.SHORT, firstWeekday)
|
return getWeekdayNames(GregorianCalendar.SHORT, firstWeekday)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a vector of Int representing the frequency of each weekday in a given month.
|
||||||
|
*
|
||||||
|
* @param startOfMonth a Timestamp representing the beginning of the month.
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun getWeekdaysInMonth(startOfMonth: Timestamp): Array<Int> {
|
||||||
|
val month = startOfMonth.toCalendar()[Calendar.MONTH] + 1
|
||||||
|
val year = startOfMonth.toCalendar()[Calendar.YEAR]
|
||||||
|
val weekday = startOfMonth.weekday
|
||||||
|
val monthLength = YearMonth.of(year, month).lengthOfMonth()
|
||||||
|
|
||||||
|
val freq = Array(7) { 0 }
|
||||||
|
for (day in weekday until weekday + monthLength) {
|
||||||
|
freq[day % 7] += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return freq
|
||||||
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun getToday(): Timestamp = Timestamp(getStartOfToday())
|
fun getToday(): Timestamp = Timestamp(getStartOfToday())
|
||||||
|
|
||||||
|
|||||||
@@ -118,6 +118,31 @@ class DateUtilsTest : BaseUnitTest() {
|
|||||||
assertThat(arrayOf("Sat", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri"), equalTo(longWeekdayNames))
|
assertThat(arrayOf("Sat", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri"), equalTo(longWeekdayNames))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getWeekdaysInMonth() {
|
||||||
|
val february = GregorianCalendar(2018, Calendar.FEBRUARY, 1)
|
||||||
|
val leapFebruary = GregorianCalendar(2020, Calendar.FEBRUARY, 1)
|
||||||
|
val month = GregorianCalendar(2020, Calendar.APRIL, 1)
|
||||||
|
val longMonth = GregorianCalendar(2020, Calendar.AUGUST, 1)
|
||||||
|
|
||||||
|
assertThat(
|
||||||
|
arrayOf(4, 4, 4, 4, 4, 4, 4),
|
||||||
|
equalTo(DateUtils.getWeekdaysInMonth(Timestamp(february)))
|
||||||
|
)
|
||||||
|
assertThat(
|
||||||
|
arrayOf(5, 4, 4, 4, 4, 4, 4),
|
||||||
|
equalTo(DateUtils.getWeekdaysInMonth(Timestamp(leapFebruary)))
|
||||||
|
)
|
||||||
|
assertThat(
|
||||||
|
arrayOf(4, 4, 4, 4, 5, 5, 4),
|
||||||
|
equalTo(DateUtils.getWeekdaysInMonth(Timestamp(month)))
|
||||||
|
)
|
||||||
|
assertThat(
|
||||||
|
arrayOf(5, 5, 5, 4, 4, 4, 4),
|
||||||
|
equalTo(DateUtils.getWeekdaysInMonth(Timestamp(longMonth)))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testGetToday() {
|
fun testGetToday() {
|
||||||
setFixedLocalTime(FIXED_LOCAL_TIME)
|
setFixedLocalTime(FIXED_LOCAL_TIME)
|
||||||
|
|||||||