mirror of
https://github.com/iSoron/uhabits.git
synced 2025-12-06 01:08:50 -06:00
Compare commits
14 Commits
feature/ca
...
hotfix/2.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
177d01edd9
|
|||
|
ec1f0c5356
|
|||
|
961fb7618f
|
|||
|
11f726064a
|
|||
| abced92a07 | |||
|
|
eeacc5eef8 | ||
|
|
16c65f19fd | ||
| fc402fd81b | |||
| f7c6bc716c | |||
| 2535347d5a | |||
| 13af054214 | |||
| fcbb586e80 | |||
|
|
428bf42e79 | ||
| 838e13f30c |
@@ -1,6 +1,10 @@
|
||||
# Changelog
|
||||
|
||||
## [2.1.0] -- Unreleased
|
||||
## [2.1.1] -- 2022-09-24
|
||||
### Fixed
|
||||
- Fix Tasker plugin (@iSoron, #1503)
|
||||
|
||||
## [2.1.0] -- 2022-09-10
|
||||
### Added
|
||||
- Allow user to add notes to specific dates (@vbh, #1103)
|
||||
- Allow user to track "at most" numerical habits (@KristianTashkov, #1101)
|
||||
@@ -9,6 +13,7 @@
|
||||
- Improve number picker (@hiqua, @iSoron, #1082, #1370)
|
||||
- Add new checkmark and number picker (@iSoron, #1370)
|
||||
- Allow user to import numerical habits from HabitBull (@hiqua, #1278)
|
||||
- Add support for Android 13 themed icons (@cheeeeer, #1497)
|
||||
|
||||
### Removed
|
||||
- Hide snooze button Android 12 notifications (@hiqua, #1226)
|
||||
@@ -28,6 +33,7 @@
|
||||
- Fix small issues in calendar chart (@kalina559, #1314)
|
||||
- Resort habit list after edit (@hiqua, #1350)
|
||||
- Fix marker scaling in frequency display (@eduebernal, #1425)
|
||||
- Fix widgets not working correctly on API 33 (@iSoron, #1488)
|
||||
|
||||
### Refactoring & Testing
|
||||
- Replace raster icons by vector assets (@kalina559)
|
||||
|
||||
@@ -35,8 +35,8 @@ android {
|
||||
compileSdk = 31
|
||||
|
||||
defaultConfig {
|
||||
versionCode = 20100
|
||||
versionName = "2.1.0"
|
||||
versionCode = 20101
|
||||
versionName = "2.1.1"
|
||||
minSdk = 23
|
||||
targetSdk = 31
|
||||
applicationId = "org.isoron.uhabits"
|
||||
|
||||
@@ -270,7 +270,7 @@
|
||||
<!-- Locale/Tasker -->
|
||||
<receiver
|
||||
android:name=".automation.FireSettingReceiver"
|
||||
android:exported="false">
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="com.twofortyfouram.locale.intent.action.FIRE_SETTING" />
|
||||
</intent-filter>
|
||||
|
||||
@@ -26,7 +26,6 @@ import android.view.View
|
||||
import android.view.View.GONE
|
||||
import android.view.View.VISIBLE
|
||||
import org.isoron.uhabits.R
|
||||
import org.isoron.uhabits.activities.common.dialogs.MultipleDialogsHandler.Companion.dismissCurrent
|
||||
import org.isoron.uhabits.core.models.Entry.Companion.NO
|
||||
import org.isoron.uhabits.core.models.Entry.Companion.SKIP
|
||||
import org.isoron.uhabits.core.models.Entry.Companion.UNKNOWN
|
||||
@@ -36,6 +35,7 @@ import org.isoron.uhabits.core.preferences.Preferences
|
||||
import org.isoron.uhabits.databinding.CheckmarkPopupBinding
|
||||
import org.isoron.uhabits.utils.InterfaceUtils.getFontAwesome
|
||||
import org.isoron.uhabits.utils.dimBehind
|
||||
import org.isoron.uhabits.utils.dismissCurrentAndShow
|
||||
import org.isoron.uhabits.utils.dp
|
||||
import org.isoron.uhabits.utils.sres
|
||||
|
||||
@@ -118,8 +118,7 @@ class CheckmarkPopup(
|
||||
view.unknownBtn.setOnClickListener { onClick(UNKNOWN) }
|
||||
dialog.setCanceledOnTouchOutside(true)
|
||||
dialog.dimBehind()
|
||||
dialog.dismissCurrent()
|
||||
dialog.show()
|
||||
dialog.dismissCurrentAndShow()
|
||||
}
|
||||
|
||||
fun save() {
|
||||
|
||||
@@ -18,10 +18,7 @@
|
||||
*/
|
||||
package org.isoron.uhabits.activities.common.dialogs
|
||||
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import com.android.colorpicker.ColorPickerDialog
|
||||
import org.isoron.uhabits.activities.common.dialogs.MultipleDialogsHandler.Companion.dismissCurrent
|
||||
import org.isoron.uhabits.core.ui.callbacks.OnColorPickedCallback
|
||||
import org.isoron.uhabits.utils.toPaletteColor
|
||||
|
||||
@@ -35,10 +32,4 @@ class ColorPickerDialog : ColorPickerDialog() {
|
||||
callback.onColorPicked(pc)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val dialog = super.onCreateDialog(savedInstanceState)
|
||||
dialog.dismissCurrent()
|
||||
return dialog
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ import android.content.Context
|
||||
import android.content.DialogInterface
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import org.isoron.uhabits.R
|
||||
import org.isoron.uhabits.activities.common.dialogs.MultipleDialogsHandler.Companion.dismissCurrent
|
||||
import org.isoron.uhabits.core.ui.callbacks.OnConfirmedCallback
|
||||
import org.isoron.uhabits.inject.ActivityContext
|
||||
|
||||
@@ -46,7 +45,5 @@ class ConfirmDeleteDialog(
|
||||
BUTTON_NEGATIVE,
|
||||
res.getString(R.string.no)
|
||||
) { dialog: DialogInterface?, which: Int -> }
|
||||
|
||||
this.dismissCurrent()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,6 @@ import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatDialogFragment
|
||||
import kotlinx.android.synthetic.main.frequency_picker_dialog.view.*
|
||||
import org.isoron.uhabits.R
|
||||
import org.isoron.uhabits.activities.common.dialogs.MultipleDialogsHandler.Companion.dismissCurrent
|
||||
|
||||
class FrequencyPickerDialog(
|
||||
var freqNumerator: Int,
|
||||
@@ -112,12 +111,10 @@ class FrequencyPickerDialog(
|
||||
if (hasFocus) check(contentView.xTimesPerYDaysRadioButton)
|
||||
}
|
||||
|
||||
val dialog = AlertDialog.Builder(requireActivity())
|
||||
return AlertDialog.Builder(requireActivity())
|
||||
.setView(contentView)
|
||||
.setPositiveButton(R.string.save) { _, _ -> onSaveClicked() }
|
||||
.create()
|
||||
dialog.dismissCurrent()
|
||||
return dialog
|
||||
}
|
||||
|
||||
private fun addBeforeAfterText(
|
||||
@@ -152,8 +149,10 @@ class FrequencyPickerDialog(
|
||||
}
|
||||
contentView.xTimesPerYDaysRadioButton.isChecked -> {
|
||||
if (contentView.xTimesPerYDaysXTextView.text.isNotEmpty() && contentView.xTimesPerYDaysYTextView.text.isNotEmpty()) {
|
||||
numerator = Integer.parseInt(contentView.xTimesPerYDaysXTextView.text.toString())
|
||||
denominator = Integer.parseInt(contentView.xTimesPerYDaysYTextView.text.toString())
|
||||
numerator =
|
||||
Integer.parseInt(contentView.xTimesPerYDaysXTextView.text.toString())
|
||||
denominator =
|
||||
Integer.parseInt(contentView.xTimesPerYDaysYTextView.text.toString())
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
package org.isoron.uhabits.activities.common.dialogs
|
||||
|
||||
import android.app.Dialog
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
class MultipleDialogsHandler {
|
||||
companion object {
|
||||
var currentDialog: WeakReference<Dialog> = WeakReference(null)
|
||||
|
||||
fun Dialog.dismissCurrent() {
|
||||
currentDialog.get()?.dismiss()
|
||||
currentDialog = WeakReference(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,11 +27,11 @@ import android.view.MotionEvent.ACTION_DOWN
|
||||
import android.view.View
|
||||
import android.view.View.GONE
|
||||
import android.view.View.VISIBLE
|
||||
import org.isoron.uhabits.activities.common.dialogs.MultipleDialogsHandler.Companion.dismissCurrent
|
||||
import org.isoron.uhabits.core.models.Entry
|
||||
import org.isoron.uhabits.core.preferences.Preferences
|
||||
import org.isoron.uhabits.databinding.CheckmarkPopupBinding
|
||||
import org.isoron.uhabits.utils.dimBehind
|
||||
import org.isoron.uhabits.utils.dismissCurrentAndShow
|
||||
import org.isoron.uhabits.utils.dp
|
||||
import org.isoron.uhabits.utils.requestFocusWithKeyboard
|
||||
import java.text.DecimalFormat
|
||||
@@ -104,8 +104,7 @@ class NumberPopup(
|
||||
view.value.requestFocusWithKeyboard()
|
||||
dialog.setCanceledOnTouchOutside(true)
|
||||
dialog.dimBehind()
|
||||
dialog.dismissCurrent()
|
||||
dialog.show()
|
||||
dialog.dismissCurrentAndShow()
|
||||
}
|
||||
|
||||
fun save() {
|
||||
|
||||
@@ -25,7 +25,6 @@ import android.os.Bundle
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatDialogFragment
|
||||
import org.isoron.uhabits.R
|
||||
import org.isoron.uhabits.activities.common.dialogs.MultipleDialogsHandler.Companion.dismissCurrent
|
||||
import org.isoron.uhabits.core.models.WeekdayList
|
||||
import org.isoron.uhabits.core.utils.DateUtils
|
||||
import java.util.Calendar
|
||||
@@ -75,9 +74,7 @@ class WeekdayPickerDialog :
|
||||
android.R.string.cancel
|
||||
) { _: DialogInterface?, _: Int -> dismiss() }
|
||||
|
||||
val dialog = builder.create()
|
||||
dialog.dismissCurrent()
|
||||
return dialog
|
||||
return builder.create()
|
||||
}
|
||||
|
||||
fun setListener(listener: OnWeekdaysPickedListener?) {
|
||||
|
||||
@@ -63,7 +63,9 @@ class FrequencyChart : ScrollableChart {
|
||||
private var primaryColor = 0
|
||||
private var isBackgroundTransparent = false
|
||||
private lateinit var frequency: HashMap<Timestamp, Array<Int>>
|
||||
private var maxFreq = 0
|
||||
private var firstWeekday = Calendar.SUNDAY
|
||||
private var isNumerical: Boolean = false
|
||||
|
||||
constructor(context: Context?) : super(context) {
|
||||
init()
|
||||
@@ -80,8 +82,14 @@ class FrequencyChart : ScrollableChart {
|
||||
postInvalidate()
|
||||
}
|
||||
|
||||
fun setIsNumerical(type: Boolean) {
|
||||
isNumerical = type
|
||||
postInvalidate()
|
||||
}
|
||||
|
||||
fun setFrequency(frequency: java.util.HashMap<Timestamp, Array<Int>>) {
|
||||
this.frequency = frequency
|
||||
maxFreq = getMaxFreq(frequency)
|
||||
postInvalidate()
|
||||
}
|
||||
|
||||
@@ -90,6 +98,15 @@ class FrequencyChart : ScrollableChart {
|
||||
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) {
|
||||
this.isBackgroundTransparent = isBackgroundTransparent
|
||||
initColors()
|
||||
@@ -213,7 +230,7 @@ class FrequencyChart : ScrollableChart {
|
||||
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
|
||||
val valueCopy = value?.let { max(0, it) }
|
||||
|
||||
@@ -221,8 +238,8 @@ class FrequencyChart : ScrollableChart {
|
||||
// maximal allowed mark radius
|
||||
val maxRadius = (rect.height() - 2 * padding) / 2.0f
|
||||
// the real mark radius is scaled down by a factor depending on the maximal frequency
|
||||
|
||||
val scale = 1.0f / frequency * valueCopy!!
|
||||
val scalingFactor = if (isNumerical) maxFreq else weekdayFrequency
|
||||
val scale = 1.0f / scalingFactor * valueCopy!!
|
||||
val radius = maxRadius * scale
|
||||
val colorIndex = min((colors.size - 1), ((colors.size - 1) * scale).roundToInt())
|
||||
pGraph!!.color = colors[colorIndex]
|
||||
@@ -285,5 +302,6 @@ class FrequencyChart : ScrollableChart {
|
||||
frequency[Timestamp(date)] = values
|
||||
date.add(Calendar.MONTH, -1)
|
||||
}
|
||||
maxFreq = getMaxFreq(frequency)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,6 @@ import org.isoron.uhabits.R
|
||||
import org.isoron.uhabits.activities.AndroidThemeSwitcher
|
||||
import org.isoron.uhabits.activities.common.dialogs.ColorPickerDialogFactory
|
||||
import org.isoron.uhabits.activities.common.dialogs.FrequencyPickerDialog
|
||||
import org.isoron.uhabits.activities.common.dialogs.MultipleDialogsHandler.Companion.dismissCurrent
|
||||
import org.isoron.uhabits.activities.common.dialogs.WeekdayPickerDialog
|
||||
import org.isoron.uhabits.core.commands.CommandRunner
|
||||
import org.isoron.uhabits.core.commands.CreateHabitCommand
|
||||
@@ -60,6 +59,7 @@ import org.isoron.uhabits.core.models.Reminder
|
||||
import org.isoron.uhabits.core.models.WeekdayList
|
||||
import org.isoron.uhabits.databinding.ActivityEditHabitBinding
|
||||
import org.isoron.uhabits.utils.ColorUtils
|
||||
import org.isoron.uhabits.utils.dismissCurrentAndShow
|
||||
import org.isoron.uhabits.utils.formatTime
|
||||
import org.isoron.uhabits.utils.toFormattedString
|
||||
|
||||
@@ -157,23 +157,23 @@ class EditHabitActivity : AppCompatActivity() {
|
||||
|
||||
val colorPickerDialogFactory = ColorPickerDialogFactory(this)
|
||||
binding.colorButton.setOnClickListener {
|
||||
val dialog = colorPickerDialogFactory.create(color, themeSwitcher.currentTheme)
|
||||
dialog.setListener { paletteColor ->
|
||||
val picker = colorPickerDialogFactory.create(color, themeSwitcher.currentTheme)
|
||||
picker.setListener { paletteColor ->
|
||||
this.color = paletteColor
|
||||
updateColors()
|
||||
}
|
||||
dialog.show(supportFragmentManager, "colorPicker")
|
||||
picker.dismissCurrentAndShow(supportFragmentManager, "colorPicker")
|
||||
}
|
||||
|
||||
populateFrequency()
|
||||
binding.booleanFrequencyPicker.setOnClickListener {
|
||||
val dialog = FrequencyPickerDialog(freqNum, freqDen)
|
||||
dialog.onFrequencyPicked = { num, den ->
|
||||
val picker = FrequencyPickerDialog(freqNum, freqDen)
|
||||
picker.onFrequencyPicked = { num, den ->
|
||||
freqNum = num
|
||||
freqDen = den
|
||||
populateFrequency()
|
||||
}
|
||||
dialog.show(supportFragmentManager, "frequencyPicker")
|
||||
picker.dismissCurrentAndShow(supportFragmentManager, "frequencyPicker")
|
||||
}
|
||||
|
||||
populateTargetType()
|
||||
@@ -191,8 +191,7 @@ class EditHabitActivity : AppCompatActivity() {
|
||||
dialog.dismiss()
|
||||
}
|
||||
val dialog = builder.create()
|
||||
dialog.dismissCurrent()
|
||||
dialog.show()
|
||||
dialog.dismissCurrentAndShow()
|
||||
}
|
||||
|
||||
binding.numericalFrequencyPicker.setOnClickListener {
|
||||
@@ -238,7 +237,7 @@ class EditHabitActivity : AppCompatActivity() {
|
||||
is24HourMode,
|
||||
androidColor
|
||||
)
|
||||
dialog.show(supportFragmentManager, "timePicker")
|
||||
dialog.dismissCurrentAndShow(supportFragmentManager, "timePicker")
|
||||
}
|
||||
|
||||
binding.reminderDatePicker.setOnClickListener {
|
||||
@@ -250,7 +249,7 @@ class EditHabitActivity : AppCompatActivity() {
|
||||
populateReminder()
|
||||
}
|
||||
dialog.setSelectedDays(reminderDays)
|
||||
dialog.show(supportFragmentManager, "dayPicker")
|
||||
dialog.dismissCurrentAndShow(supportFragmentManager, "dayPicker")
|
||||
}
|
||||
|
||||
binding.buttonSave.setOnClickListener {
|
||||
|
||||
@@ -21,6 +21,7 @@ package org.isoron.uhabits.activities.habits.list
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
@@ -84,7 +85,6 @@ class ListHabitsActivity : AppCompatActivity(), Preferences.Listener {
|
||||
Thread.setDefaultUncaughtExceptionHandler(BaseExceptionHandler(this))
|
||||
component.listHabitsBehavior.onStartup()
|
||||
setContentView(rootView)
|
||||
parseIntents()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
@@ -100,11 +100,17 @@ class ListHabitsActivity : AppCompatActivity(), Preferences.Listener {
|
||||
rootView.postInvalidate()
|
||||
midnightTimer.onResume()
|
||||
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) {
|
||||
restartWithFade(ListHabitsActivity::class.java)
|
||||
}
|
||||
parseIntents()
|
||||
super.onResume()
|
||||
}
|
||||
|
||||
@@ -124,6 +130,7 @@ class ListHabitsActivity : AppCompatActivity(), Preferences.Listener {
|
||||
}
|
||||
|
||||
private fun parseIntents() {
|
||||
if (intent == null) return
|
||||
if (intent.action == ACTION_EDIT) {
|
||||
val habitId = intent.extras?.getLong("habit")
|
||||
val timestamp = intent.extras?.getLong("timestamp")
|
||||
@@ -132,6 +139,12 @@ class ListHabitsActivity : AppCompatActivity(), Preferences.Listener {
|
||||
component.listHabitsBehavior.onEdit(habit, Timestamp(timestamp))
|
||||
}
|
||||
}
|
||||
intent = null
|
||||
}
|
||||
|
||||
override fun onNewIntent(intent: Intent?) {
|
||||
super.onNewIntent(intent)
|
||||
setIntent(intent)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -64,6 +64,7 @@ import org.isoron.uhabits.tasks.ImportDataTask
|
||||
import org.isoron.uhabits.tasks.ImportDataTaskFactory
|
||||
import org.isoron.uhabits.utils.copyTo
|
||||
import org.isoron.uhabits.utils.currentTheme
|
||||
import org.isoron.uhabits.utils.dismissCurrentAndShow
|
||||
import org.isoron.uhabits.utils.restartWithFade
|
||||
import org.isoron.uhabits.utils.showMessage
|
||||
import org.isoron.uhabits.utils.showSendEmailScreen
|
||||
@@ -163,7 +164,7 @@ class ListHabitsScreen
|
||||
}
|
||||
|
||||
override fun showDeleteConfirmationScreen(callback: OnConfirmedCallback, quantity: Int) {
|
||||
ConfirmDeleteDialog(activity, callback, quantity).show()
|
||||
ConfirmDeleteDialog(activity, callback, quantity).dismissCurrentAndShow()
|
||||
}
|
||||
|
||||
override fun showEditHabitsScreen(selected: List<Habit>) {
|
||||
@@ -224,7 +225,7 @@ class ListHabitsScreen
|
||||
override fun showColorPicker(defaultColor: PaletteColor, callback: OnColorPickedCallback) {
|
||||
val picker = colorPickerFactory.create(defaultColor, themeSwitcher.currentTheme!!)
|
||||
picker.setListener(callback)
|
||||
picker.show(activity.supportFragmentManager, "picker")
|
||||
picker.dismissCurrentAndShow(activity.supportFragmentManager, "picker")
|
||||
}
|
||||
|
||||
override fun showNumberPopup(
|
||||
|
||||
@@ -50,6 +50,7 @@ import org.isoron.uhabits.core.ui.screens.habits.show.ShowHabitPresenter
|
||||
import org.isoron.uhabits.core.ui.views.OnDateClickedListener
|
||||
import org.isoron.uhabits.intents.IntentFactory
|
||||
import org.isoron.uhabits.utils.currentTheme
|
||||
import org.isoron.uhabits.utils.dismissCurrentAndShow
|
||||
import org.isoron.uhabits.utils.showMessage
|
||||
import org.isoron.uhabits.utils.showSendFileScreen
|
||||
import org.isoron.uhabits.widgets.WidgetUpdater
|
||||
@@ -228,7 +229,7 @@ class ShowHabitActivity : AppCompatActivity(), CommandRunner.Listener {
|
||||
}
|
||||
|
||||
override fun showDeleteConfirmationScreen(callback: OnConfirmedCallback) {
|
||||
ConfirmDeleteDialog(this@ShowHabitActivity, callback, 1).show()
|
||||
ConfirmDeleteDialog(this@ShowHabitActivity, callback, 1).dismissCurrentAndShow()
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
|
||||
@@ -33,6 +33,7 @@ class FrequencyCardView(context: Context, attrs: AttributeSet) : LinearLayout(co
|
||||
fun setState(state: FrequencyCardState) {
|
||||
val androidColor = state.theme.color(state.color).toInt()
|
||||
binding.frequencyChart.setFrequency(state.frequency)
|
||||
binding.frequencyChart.setIsNumerical(state.isNumerical)
|
||||
binding.frequencyChart.setFirstWeekday(state.firstWeekday)
|
||||
binding.title.setTextColor(androidColor)
|
||||
binding.frequencyChart.setColor(androidColor)
|
||||
|
||||
@@ -21,13 +21,16 @@ package org.isoron.uhabits.intents
|
||||
|
||||
import android.app.PendingIntent
|
||||
import android.app.PendingIntent.FLAG_IMMUTABLE
|
||||
import android.app.PendingIntent.FLAG_MUTABLE
|
||||
import android.app.PendingIntent.FLAG_UPDATE_CURRENT
|
||||
import android.app.PendingIntent.getActivity
|
||||
import android.app.PendingIntent.getBroadcast
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
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.models.Habit
|
||||
import org.isoron.uhabits.core.models.Timestamp
|
||||
@@ -89,6 +92,20 @@ class PendingIntentFactory
|
||||
)
|
||||
.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(
|
||||
habit: Habit,
|
||||
reminderTime: Long?,
|
||||
@@ -142,7 +159,7 @@ class PendingIntentFactory
|
||||
fun showNumberPicker(habit: Habit, timestamp: Timestamp): PendingIntent? {
|
||||
return getActivity(
|
||||
context,
|
||||
0,
|
||||
(habit.id!! % Integer.MAX_VALUE).toInt() + 1,
|
||||
Intent(context, ListHabitsActivity::class.java).apply {
|
||||
action = ListHabitsActivity.ACTION_EDIT
|
||||
putExtra("habit", habit.id)
|
||||
@@ -151,4 +168,43 @@ class PendingIntentFactory
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package org.isoron.uhabits.utils
|
||||
|
||||
import android.app.Dialog
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
var currentDialog: WeakReference<Dialog> = WeakReference(null)
|
||||
|
||||
fun Dialog.dismissCurrentAndShow() {
|
||||
currentDialog.get()?.dismiss()
|
||||
currentDialog = WeakReference(this)
|
||||
show()
|
||||
}
|
||||
|
||||
fun DialogFragment.dismissCurrentAndShow(fragmentManager: FragmentManager, tag: String) {
|
||||
currentDialog.get()?.dismiss()
|
||||
show(fragmentManager, tag)
|
||||
fragmentManager.executePendingTransactions()
|
||||
currentDialog = WeakReference(this.dialog)
|
||||
}
|
||||
@@ -49,6 +49,7 @@ class FrequencyWidget(
|
||||
(widgetView.dataView as FrequencyChart).apply {
|
||||
setFirstWeekday(firstWeekday)
|
||||
setColor(WidgetTheme().color(habit.color).toInt())
|
||||
setIsNumerical(habit.isNumerical)
|
||||
setFrequency(habit.originalEntries.computeWeekdayFrequency(habit.isNumerical))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,6 +73,10 @@ class StackWidget(
|
||||
StackWidgetType.getStackWidgetAdapterViewId(widgetType),
|
||||
StackWidgetType.getStackWidgetEmptyViewId(widgetType)
|
||||
)
|
||||
remoteViews.setPendingIntentTemplate(
|
||||
StackWidgetType.getStackWidgetAdapterViewId(widgetType),
|
||||
StackWidgetType.getPendingIntentTemplate(pendingIntentFactory, widgetType, habits)
|
||||
)
|
||||
return remoteViews
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,11 +29,14 @@ import android.widget.RemoteViewsService
|
||||
import android.widget.RemoteViewsService.RemoteViewsFactory
|
||||
import org.isoron.platform.utils.StringUtils.Companion.splitLongs
|
||||
import org.isoron.uhabits.HabitsApplication
|
||||
import org.isoron.uhabits.R
|
||||
import org.isoron.uhabits.core.models.Habit
|
||||
import org.isoron.uhabits.core.models.HabitNotFoundException
|
||||
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 java.util.ArrayList
|
||||
|
||||
class StackWidgetService : RemoteViewsService() {
|
||||
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 widgetType: StackWidgetType
|
||||
private var remoteViews = ArrayList<RemoteViews>()
|
||||
override fun onCreate() {}
|
||||
override fun onDestroy() {}
|
||||
override fun getCount(): Int {
|
||||
@@ -85,8 +87,26 @@ internal class StackRemoteViewsFactory(private val context: Context, intent: Int
|
||||
}
|
||||
|
||||
override fun getViewAt(position: Int): RemoteViews? {
|
||||
Log.i("StackRemoteViewsFactory", "getViewAt $position")
|
||||
return if (0 <= position && position < remoteViews.size) remoteViews[position] else null
|
||||
Log.i("StackRemoteViewsFactory", "getViewAt $position started")
|
||||
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(
|
||||
@@ -131,24 +151,6 @@ internal class StackRemoteViewsFactory(private val context: Context, intent: Int
|
||||
}
|
||||
|
||||
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 {
|
||||
|
||||
@@ -18,7 +18,12 @@
|
||||
*/
|
||||
package org.isoron.uhabits.widgets
|
||||
|
||||
import android.app.PendingIntent
|
||||
import android.content.Intent
|
||||
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
|
||||
|
||||
enum class StackWidgetType(val value: Int) {
|
||||
@@ -73,5 +78,39 @@ enum class StackWidgetType(val value: Int) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
2.1.1:
|
||||
* Fix Tasker plugin
|
||||
|
||||
2.1:
|
||||
* Add notes to specific dates
|
||||
* Track at-most measurable habits
|
||||
* Add skips to measurable habits
|
||||
* Bring back custom frequencies
|
||||
* Other minor improvements and bug fixes
|
||||
* Other minor improvements and bug fixes
|
||||
|
||||
@@ -21,4 +21,5 @@
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
<monochrome android:drawable="@mipmap/ic_launcher_monochrome"/>
|
||||
</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 |
@@ -25,6 +25,12 @@ class StringUtils {
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ data class FrequencyCardState(
|
||||
val firstWeekday: Int,
|
||||
val frequency: HashMap<Timestamp, Array<Int>>,
|
||||
val theme: Theme,
|
||||
val isNumerical: Boolean
|
||||
)
|
||||
|
||||
class FrequencyCardPresenter {
|
||||
@@ -40,6 +41,7 @@ class FrequencyCardPresenter {
|
||||
theme: Theme
|
||||
) = FrequencyCardState(
|
||||
color = habit.color,
|
||||
isNumerical = habit.isNumerical,
|
||||
frequency = habit.originalEntries.computeWeekdayFrequency(
|
||||
isNumerical = habit.isNumerical
|
||||
),
|
||||
|
||||
@@ -92,7 +92,7 @@ class BarChart(
|
||||
val r = round(barWidth * 0.15)
|
||||
if (2 * r < barHeight) {
|
||||
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 + barWidth - r, y + r, r)
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user