diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 46bed95e5..c90dd93f4 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -17,7 +17,7 @@ jobs: run: ./build.sh build - name: Run Android tests - run: ./build.sh android-tests-parallel 23 24 25 26 27 28 30 31 + run: ./build.sh android-tests-parallel 24 25 26 28 30 31 - name: Upload artifacts if: always() diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a9651df2..bbc344341 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +## [2.1.2] -- 2023-05-26 +### Fixed +- Fix bug that caused widget to enter checkmark on wrong date (@iSoron, #1541) +- Fix widget corners on Android 12 (@iSoron) +- Fix bug that caused notes to be lost when editing a checkmark (@iSoron, #1566) +- Prevent soft keyboard from covering entry popup (@iSoron) +- Accept comma (instead of dot) in certain locales (@iSoron) + +### Changed +- Remove update delay after entering a checkmark (@iSoron) + +### Removed +- Remove stack widgets (@iSoron) + + ## [2.1.1] -- 2022-09-24 ### Fixed - Fix Tasker plugin (@iSoron, #1503) diff --git a/build.sh b/build.sh index 5c27503fa..4dac8964f 100755 --- a/build.sh +++ b/build.sh @@ -217,20 +217,28 @@ android_test_parallel() { ( LOG=build/android-test-$API.log log_info "API $API: Running tests..." - if android_test $API 1>$LOG 2>&1; then + android_test $API 1>$LOG 2>&1 + ret_code=$? + if [ $ret_code = 0 ]; then log_info "API $API: Passed" else log_error "API $API: Failed" fi pkill -9 -f ${AVD_PREFIX}${API} + exit $ret_code )& PIDS+=" $!" done # Check exit codes - RET_CODE=0 + success=0 for pid in $PIDS; do - wait $pid || RET_CODE=1 + wait $pid + ret_code=$? + if [ $ret_code != 0 ]; then + success=1 + fi + echo pid=$pid ret_code=$ret_code success=$success done # Print all logs @@ -240,7 +248,7 @@ android_test_parallel() { echo "::endgroup::" done - return $RET_CODE + return $success } android_build() { diff --git a/uhabits-android/build.gradle.kts b/uhabits-android/build.gradle.kts index 62a0489e2..49fb4c8b7 100644 --- a/uhabits-android/build.gradle.kts +++ b/uhabits-android/build.gradle.kts @@ -35,8 +35,8 @@ android { compileSdk = 31 defaultConfig { - versionCode = 20101 - versionName = "2.1.1" + versionCode = 20102 + versionName = "2.1.2" minSdk = 23 targetSdk = 31 applicationId = "org.isoron.uhabits" diff --git a/uhabits-android/src/androidTest/assets/views/widgets/CheckmarkWidget/render.png b/uhabits-android/src/androidTest/assets/views/widgets/CheckmarkWidget/render.png index 642264b62..2ffbb10ac 100644 Binary files a/uhabits-android/src/androidTest/assets/views/widgets/CheckmarkWidget/render.png and b/uhabits-android/src/androidTest/assets/views/widgets/CheckmarkWidget/render.png differ diff --git a/uhabits-android/src/androidTest/assets/views/widgets/CheckmarkWidgetView/checked.png b/uhabits-android/src/androidTest/assets/views/widgets/CheckmarkWidgetView/checked.png index f5a3df56e..a8bf398a3 100644 Binary files a/uhabits-android/src/androidTest/assets/views/widgets/CheckmarkWidgetView/checked.png and b/uhabits-android/src/androidTest/assets/views/widgets/CheckmarkWidgetView/checked.png differ diff --git a/uhabits-android/src/androidTest/assets/views/widgets/CheckmarkWidgetView/large_size.png b/uhabits-android/src/androidTest/assets/views/widgets/CheckmarkWidgetView/large_size.png index ea1aab565..bc7e34f20 100644 Binary files a/uhabits-android/src/androidTest/assets/views/widgets/CheckmarkWidgetView/large_size.png and b/uhabits-android/src/androidTest/assets/views/widgets/CheckmarkWidgetView/large_size.png differ diff --git a/uhabits-android/src/androidTest/assets/views/widgets/FrequencyWidget/render.png b/uhabits-android/src/androidTest/assets/views/widgets/FrequencyWidget/render.png index 3fb786226..e72c6bedf 100644 Binary files a/uhabits-android/src/androidTest/assets/views/widgets/FrequencyWidget/render.png and b/uhabits-android/src/androidTest/assets/views/widgets/FrequencyWidget/render.png differ diff --git a/uhabits-android/src/androidTest/assets/views/widgets/HistoryWidget/render.png b/uhabits-android/src/androidTest/assets/views/widgets/HistoryWidget/render.png index 5b71e7300..23e3c979e 100644 Binary files a/uhabits-android/src/androidTest/assets/views/widgets/HistoryWidget/render.png and b/uhabits-android/src/androidTest/assets/views/widgets/HistoryWidget/render.png differ diff --git a/uhabits-android/src/androidTest/assets/views/widgets/ScoreWidget/render.png b/uhabits-android/src/androidTest/assets/views/widgets/ScoreWidget/render.png index 35f65f59e..4f50ce820 100644 Binary files a/uhabits-android/src/androidTest/assets/views/widgets/ScoreWidget/render.png and b/uhabits-android/src/androidTest/assets/views/widgets/ScoreWidget/render.png differ diff --git a/uhabits-android/src/androidTest/assets/views/widgets/TargetWidget/render.png b/uhabits-android/src/androidTest/assets/views/widgets/TargetWidget/render.png index 4a8fa7f9f..5b3270649 100644 Binary files a/uhabits-android/src/androidTest/assets/views/widgets/TargetWidget/render.png and b/uhabits-android/src/androidTest/assets/views/widgets/TargetWidget/render.png differ diff --git a/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/list/views/EntryButtonViewTest.kt b/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/list/views/EntryButtonViewTest.kt index f37cc1838..32df08758 100644 --- a/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/list/views/EntryButtonViewTest.kt +++ b/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/list/views/EntryButtonViewTest.kt @@ -44,7 +44,7 @@ class EntryButtonViewTest : BaseViewTest() { view = component.getEntryButtonViewFactory().create().apply { value = Entry.NO color = PaletteUtils.getAndroidTestColor(5) - onToggle = { _, _, _ -> toggled = true } + onToggle = { _, _ -> toggled = true } onEdit = { edited = true } } measureView(view, dpToPixels(48), dpToPixels(48)) diff --git a/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/list/views/EntryPanelViewTest.kt b/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/list/views/EntryPanelViewTest.kt index f09c48dba..de955deb8 100644 --- a/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/list/views/EntryPanelViewTest.kt +++ b/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/list/views/EntryPanelViewTest.kt @@ -77,7 +77,7 @@ class EntryPanelViewTest : BaseViewTest() { @Test fun testToggle() { val timestamps = mutableListOf() - view.onToggle = { t, _, _, _ -> timestamps.add(t) } + view.onToggle = { t, _, _ -> timestamps.add(t) } view.buttons[0].performLongClick() view.buttons[2].performLongClick() view.buttons[3].performLongClick() @@ -88,7 +88,7 @@ class EntryPanelViewTest : BaseViewTest() { fun testToggle_withOffset() { val timestamps = mutableListOf() view.dataOffset = 3 - view.onToggle = { t, _, _, _ -> timestamps += t } + view.onToggle = { t, _, _ -> timestamps += t } view.buttons[0].performLongClick() view.buttons[2].performLongClick() view.buttons[3].performLongClick() diff --git a/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/views/CheckmarkWidgetViewTest.kt b/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/views/CheckmarkWidgetViewTest.kt index f3a842f04..e2fe9a2a3 100644 --- a/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/views/CheckmarkWidgetViewTest.kt +++ b/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/views/CheckmarkWidgetViewTest.kt @@ -36,6 +36,7 @@ class CheckmarkWidgetViewTest : BaseViewTest() { @Before override fun setUp() { super.setUp() + similarityCutoff = 0.00025 setTheme(R.style.WidgetTheme) val habit = fixtures.createShortHabit() val computedEntries = habit.computedEntries diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/CheckmarkPopup.kt b/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/CheckmarkDialog.kt similarity index 54% rename from uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/CheckmarkPopup.kt rename to uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/CheckmarkDialog.kt index fb7f43d51..5df8ffd7b 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/CheckmarkPopup.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/CheckmarkDialog.kt @@ -20,109 +20,60 @@ package org.isoron.uhabits.activities.common.dialogs import android.app.Dialog -import android.content.Context +import android.os.Bundle import android.view.LayoutInflater -import android.view.View import android.view.View.GONE import android.view.View.VISIBLE +import androidx.appcompat.app.AppCompatDialogFragment +import org.isoron.uhabits.HabitsApplication import org.isoron.uhabits.R 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 -import org.isoron.uhabits.core.models.Entry.Companion.YES_AUTO import org.isoron.uhabits.core.models.Entry.Companion.YES_MANUAL -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 -const val POPUP_WIDTH = 4 * 48f + 16f -const val POPUP_HEIGHT = 48f * 2.5f + 8f - -class CheckmarkPopup( - private val context: Context, - private val color: Int, - private var notes: String, - private var value: Int, - private val prefs: Preferences, - private val anchor: View, -) { +class CheckmarkDialog : AppCompatDialogFragment() { var onToggle: (Int, String) -> Unit = { _, _ -> } - private lateinit var dialog: Dialog - - private val view = CheckmarkPopupBinding.inflate(LayoutInflater.from(context)).apply { - // Required for round corners - container.clipToOutline = true - } - init { - view.booleanButtons.visibility = VISIBLE - initColors() - initTypefaces() - hideDisabledButtons() - populate() - } - - private fun initColors() { + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + val appComponent = (requireActivity().application as HabitsApplication).component + val prefs = appComponent.preferences + val view = CheckmarkPopupBinding.inflate(LayoutInflater.from(context)) arrayOf(view.yesBtn, view.skipBtn).forEach { - it.setTextColor(color) + it.setTextColor(requireArguments().getInt("color")) } arrayOf(view.noBtn, view.unknownBtn).forEach { it.setTextColor(view.root.sres.getColor(R.attr.contrast60)) } - } - - private fun initTypefaces() { arrayOf(view.yesBtn, view.noBtn, view.skipBtn, view.unknownBtn).forEach { - it.typeface = getFontAwesome(context) + it.typeface = getFontAwesome(requireContext()) } - } - - private fun hideDisabledButtons() { + view.notes.setText(requireArguments().getString("notes")!!) if (!prefs.isSkipEnabled) view.skipBtn.visibility = GONE if (!prefs.areQuestionMarksEnabled) view.unknownBtn.visibility = GONE - } - - private fun populate() { - val selectedBtn = when (value) { - YES_MANUAL -> view.yesBtn - YES_AUTO -> view.noBtn - NO -> view.noBtn - UNKNOWN -> if (prefs.areQuestionMarksEnabled) view.unknownBtn else view.noBtn - SKIP -> if (prefs.isSkipEnabled) view.skipBtn else view.noBtn - else -> null - } - view.notes.setText(notes) - } - - fun show() { - dialog = Dialog(context, android.R.style.Theme_NoTitleBar) + view.booleanButtons.visibility = VISIBLE + val dialog = Dialog(requireContext()) dialog.setContentView(view.root) dialog.window?.apply { - setLayout( - view.root.dp(POPUP_WIDTH).toInt(), - view.root.dp(POPUP_HEIGHT).toInt() - ) setBackgroundDrawableResource(android.R.color.transparent) } fun onClick(v: Int) { - this.value = v - save() + val notes = view.notes.text.toString().trim() + onToggle(v, notes) + requireDialog().dismiss() } view.yesBtn.setOnClickListener { onClick(YES_MANUAL) } view.noBtn.setOnClickListener { onClick(NO) } view.skipBtn.setOnClickListener { onClick(SKIP) } view.unknownBtn.setOnClickListener { onClick(UNKNOWN) } - dialog.setCanceledOnTouchOutside(true) - dialog.dimBehind() - dialog.dismissCurrentAndShow() - } + view.notes.setOnEditorActionListener { v, actionId, event -> + onClick(requireArguments().getInt("value")) + true + } - fun save() { - onToggle(value, view.notes.text.toString().trim()) - dialog.dismiss() + return dialog } } diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/NumberDialog.kt b/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/NumberDialog.kt new file mode 100644 index 000000000..7e10baa51 --- /dev/null +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/NumberDialog.kt @@ -0,0 +1,104 @@ +package org.isoron.uhabits.activities.common.dialogs + +import android.app.Dialog +import android.os.Bundle +import android.text.method.DigitsKeyListener +import android.view.KeyEvent +import android.view.LayoutInflater +import android.view.MotionEvent +import android.view.View +import androidx.appcompat.app.AppCompatDialogFragment +import org.isoron.uhabits.HabitsApplication +import org.isoron.uhabits.R +import org.isoron.uhabits.core.models.Entry +import org.isoron.uhabits.databinding.CheckmarkPopupBinding +import org.isoron.uhabits.utils.InterfaceUtils +import org.isoron.uhabits.utils.requestFocusWithKeyboard +import org.isoron.uhabits.utils.sres +import java.text.DecimalFormat +import java.text.DecimalFormatSymbols +import java.text.NumberFormat +import java.text.ParseException + +class NumberDialog : AppCompatDialogFragment() { + + var onToggle: (Double, String) -> Unit = { _, _ -> } + var onDismiss: () -> Unit = {} + + private var originalNotes: String = "" + private var originalValue: Double = 0.0 + private lateinit var view: CheckmarkPopupBinding + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + val appComponent = (requireActivity().application as HabitsApplication).component + val prefs = appComponent.preferences + view = CheckmarkPopupBinding.inflate(LayoutInflater.from(context)) + arrayOf(view.yesBtn, view.skipBtn).forEach { + it.setTextColor(requireArguments().getInt("color")) + } + arrayOf(view.noBtn, view.unknownBtn).forEach { + it.setTextColor(view.root.sres.getColor(R.attr.contrast60)) + } + arrayOf(view.yesBtn, view.noBtn, view.skipBtn, view.unknownBtn).forEach { + it.typeface = InterfaceUtils.getFontAwesome(requireContext()) + } + if (!prefs.isSkipEnabled) view.skipBtnNumber.visibility = View.GONE + view.numberButtons.visibility = View.VISIBLE + fixDecimalSeparator(view) + originalNotes = requireArguments().getString("notes")!! + originalValue = requireArguments().getDouble("value") + view.notes.setText(originalNotes) + view.value.setText( + when { + originalValue < 0.01 -> "0" + else -> DecimalFormat("#.##").format(originalValue) + } + ) + view.value.setOnKeyListener { _, keyCode, event -> + if (event.action == MotionEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_ENTER) { + save() + return@setOnKeyListener true + } + return@setOnKeyListener false + } + view.saveBtn.setOnClickListener { + save() + } + view.skipBtnNumber.setOnClickListener { + view.value.setText((Entry.SKIP.toDouble() / 1000).toString()) + save() + } + view.notes.setOnEditorActionListener { v, actionId, event -> + save() + true + } + view.value.requestFocusWithKeyboard() + val dialog = Dialog(requireContext()) + dialog.setContentView(view.root) + dialog.window?.apply { + setBackgroundDrawableResource(android.R.color.transparent) + } + dialog.setOnDismissListener { onDismiss() } + return dialog + } + + private fun fixDecimalSeparator(view: CheckmarkPopupBinding) { + // https://stackoverflow.com/a/34256139 + val separator = DecimalFormatSymbols.getInstance().decimalSeparator + view.value.keyListener = DigitsKeyListener.getInstance("0123456789$separator") + } + + fun save() { + var value = originalValue + try { + val numberFormat = NumberFormat.getInstance() + val valueStr = view.value.text.toString() + value = numberFormat.parse(valueStr)!!.toDouble() + } catch (e: ParseException) { + // NOP + } + val notes = view.notes.text.toString() + onToggle(value, notes) + requireDialog().dismiss() + } +} diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/NumberPopup.kt b/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/NumberPopup.kt deleted file mode 100644 index c3a464311..000000000 --- a/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/NumberPopup.kt +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (C) 2016-2021 Álinson Santos Xavier - * - * This file is part of Loop Habit Tracker. - * - * Loop Habit Tracker is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by the - * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. - * - * Loop Habit Tracker is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - */ - -package org.isoron.uhabits.activities.common.dialogs - -import android.app.Dialog -import android.content.Context -import android.view.KeyEvent.KEYCODE_ENTER -import android.view.LayoutInflater -import android.view.MotionEvent.ACTION_DOWN -import android.view.View -import android.view.View.GONE -import android.view.View.VISIBLE -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 - -class NumberPopup( - private val context: Context, - private var notes: String, - private var value: Double, - private val prefs: Preferences, - private val anchor: View, -) { - var onToggle: (Double, String) -> Unit = { _, _ -> } - var onDismiss: () -> Unit = {} - private val originalValue = value - private lateinit var dialog: Dialog - - private val view = CheckmarkPopupBinding.inflate(LayoutInflater.from(context)).apply { - // Required for round corners - container.clipToOutline = true - } - - init { - view.numberButtons.visibility = VISIBLE - hideDisabledButtons() - populate() - } - - private fun hideDisabledButtons() { - if (!prefs.isSkipEnabled) view.skipBtnNumber.visibility = GONE - } - - private fun populate() { - view.notes.setText(notes) - view.value.setText( - when { - value < 0.01 -> "0" - else -> DecimalFormat("#.##").format(value) - } - ) - } - - fun show() { - dialog = Dialog(context, android.R.style.Theme_NoTitleBar) - dialog.setContentView(view.root) - dialog.window?.apply { - setLayout( - view.root.dp(POPUP_WIDTH).toInt(), - view.root.dp(POPUP_HEIGHT).toInt() - ) - setBackgroundDrawableResource(android.R.color.transparent) - } - dialog.setOnDismissListener { - onDismiss() - } - - view.value.setOnKeyListener { _, keyCode, event -> - if (event.action == ACTION_DOWN && keyCode == KEYCODE_ENTER) { - save() - return@setOnKeyListener true - } - return@setOnKeyListener false - } - view.saveBtn.setOnClickListener { - save() - } - view.skipBtnNumber.setOnClickListener { - view.value.setText((Entry.SKIP.toDouble() / 1000).toString()) - save() - } - view.value.requestFocusWithKeyboard() - dialog.setCanceledOnTouchOutside(true) - dialog.dimBehind() - dialog.dismissCurrentAndShow() - } - - fun save() { - val value = view.value.text.toString().toDoubleOrNull() ?: originalValue - val notes = view.notes.text.toString() - onToggle(value, notes) - dialog.dismiss() - } -} diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsActivity.kt b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsActivity.kt index e14bdefd5..b7ca3235f 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsActivity.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsActivity.kt @@ -40,6 +40,7 @@ import org.isoron.uhabits.inject.ActivityContextModule import org.isoron.uhabits.inject.DaggerHabitsActivityComponent import org.isoron.uhabits.inject.HabitsActivityComponent import org.isoron.uhabits.inject.HabitsApplicationComponent +import org.isoron.uhabits.utils.dismissCurrentDialog import org.isoron.uhabits.utils.restartWithFade class ListHabitsActivity : AppCompatActivity(), Preferences.Listener { @@ -91,6 +92,7 @@ class ListHabitsActivity : AppCompatActivity(), Preferences.Listener { midnightTimer.onPause() screen.onDetached() adapter.cancelRefresh() + dismissCurrentDialog() super.onPause() } @@ -99,6 +101,7 @@ class ListHabitsActivity : AppCompatActivity(), Preferences.Listener { screen.onAttached() rootView.postInvalidate() midnightTimer.onResume() + appComponent.reminderScheduler.scheduleAll() taskRunner.run { try { AutoBackup(this@ListHabitsActivity).run() diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsScreen.kt b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsScreen.kt index 731342974..acbb8c102 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsScreen.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsScreen.kt @@ -22,14 +22,15 @@ package org.isoron.uhabits.activities.habits.list import android.app.Activity import android.content.Context import android.content.Intent +import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import dagger.Lazy import org.isoron.platform.gui.toInt import org.isoron.uhabits.R -import org.isoron.uhabits.activities.common.dialogs.CheckmarkPopup +import org.isoron.uhabits.activities.common.dialogs.CheckmarkDialog import org.isoron.uhabits.activities.common.dialogs.ColorPickerDialogFactory import org.isoron.uhabits.activities.common.dialogs.ConfirmDeleteDialog -import org.isoron.uhabits.activities.common.dialogs.NumberPopup +import org.isoron.uhabits.activities.common.dialogs.NumberDialog import org.isoron.uhabits.activities.habits.edit.HabitTypeDialog import org.isoron.uhabits.activities.habits.list.views.HabitCardListAdapter import org.isoron.uhabits.core.commands.ArchiveHabitsCommand @@ -233,17 +234,14 @@ class ListHabitsScreen notes: String, callback: ListHabitsBehavior.NumberPickerCallback ) { - val view = rootView.get() - NumberPopup( - context = context, - prefs = preferences, - anchor = view, - notes = notes, - value = value, - ).apply { - onToggle = { value, notes -> callback.onNumberPicked(value, notes) } - show() + val fm = (context as AppCompatActivity).supportFragmentManager + val dialog = NumberDialog() + dialog.arguments = Bundle().apply { + putDouble("value", value) + putString("notes", notes) } + dialog.onToggle = { v, n -> callback.onNumberPicked(v, n) } + dialog.dismissCurrentAndShow(fm, "numberDialog") } override fun showCheckmarkPopup( @@ -252,18 +250,16 @@ class ListHabitsScreen color: PaletteColor, callback: ListHabitsBehavior.CheckMarkDialogCallback ) { - val view = rootView.get() - CheckmarkPopup( - context = context, - prefs = preferences, - anchor = view, - color = view.currentTheme().color(color).toInt(), - notes = notes, - value = selectedValue, - ).apply { - onToggle = { value, notes -> callback.onNotesSaved(value, notes) } - show() + val theme = rootView.get().currentTheme() + val fm = (context as AppCompatActivity).supportFragmentManager + val dialog = CheckmarkDialog() + dialog.arguments = Bundle().apply { + putInt("color", theme.color(color).toInt()) + putInt("value", selectedValue) + putString("notes", notes) } + dialog.onToggle = { v, n -> callback.onNotesSaved(v, n) } + dialog.dismissCurrentAndShow(fm, "checkmarkDialog") } private fun getExecuteString(command: Command): String? { diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/CheckmarkButtonView.kt b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/CheckmarkButtonView.kt index 176cec62a..39faa0283 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/CheckmarkButtonView.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/CheckmarkButtonView.kt @@ -44,8 +44,6 @@ import org.isoron.uhabits.utils.sres import org.isoron.uhabits.utils.toMeasureSpec import javax.inject.Inject -const val TOGGLE_DELAY_MILLIS = 2000L - class CheckmarkButtonViewFactory @Inject constructor( @ActivityContext val context: Context, @@ -79,7 +77,7 @@ class CheckmarkButtonView( invalidate() } - var onToggle: (Int, String, Long) -> Unit = { _, _, _ -> } + var onToggle: (Int, String) -> Unit = { _, _ -> } var onEdit: () -> Unit = { } @@ -90,25 +88,25 @@ class CheckmarkButtonView( setOnLongClickListener(this) } - fun performToggle(delay: Long) { + fun performToggle() { value = Entry.nextToggleValue( value = value, isSkipEnabled = preferences.isSkipEnabled, areQuestionMarksEnabled = preferences.areQuestionMarksEnabled ) - onToggle(value, notes, delay) + onToggle(value, notes) performHapticFeedback(HapticFeedbackConstants.LONG_PRESS) invalidate() } override fun onClick(v: View) { - if (preferences.isShortToggleEnabled) performToggle(TOGGLE_DELAY_MILLIS) + if (preferences.isShortToggleEnabled) performToggle() else onEdit() } override fun onLongClick(v: View): Boolean { if (preferences.isShortToggleEnabled) onEdit() - else performToggle(TOGGLE_DELAY_MILLIS) + else performToggle() return true } diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/CheckmarkPanelView.kt b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/CheckmarkPanelView.kt index 35f639691..525dac5ad 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/CheckmarkPanelView.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/CheckmarkPanelView.kt @@ -60,7 +60,7 @@ class CheckmarkPanelView( setupButtons() } - var onToggle: (Timestamp, Int, String, Long) -> Unit = { _, _, _, _ -> } + var onToggle: (Timestamp, Int, String) -> Unit = { _, _, _ -> } set(value) { field = value setupButtons() @@ -89,7 +89,7 @@ class CheckmarkPanelView( else -> "" } button.color = color - button.onToggle = { value, notes, delay -> onToggle(timestamp, value, notes, delay) } + button.onToggle = { value, notes -> onToggle(timestamp, value, notes) } button.onEdit = { onEdit(timestamp) } } } diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardView.kt b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardView.kt index 424aae50b..189c5c532 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardView.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardView.kt @@ -57,13 +57,6 @@ class HabitCardViewFactory fun create() = HabitCardView(context, checkmarkPanelFactory, numberPanelFactory, behavior) } -data class DelayedToggle( - var habit: Habit, - var timestamp: Timestamp, - var value: Int, - var notes: String -) - class HabitCardView( @ActivityContext context: Context, checkmarkPanelFactory: CheckmarkPanelViewFactory, @@ -136,7 +129,6 @@ class HabitCardView( private var scoreRing: RingView private var currentToggleTaskId = 0 - private var queuedToggles = mutableListOf() init { scoreRing = RingView(context).apply { @@ -160,12 +152,9 @@ class HabitCardView( } checkmarkPanel = checkmarkPanelFactory.create().apply { - onToggle = { timestamp, value, notes, delay -> - if (delay > 0) triggerRipple(timestamp) - habit?.let { - val taskId = queueToggle(it, timestamp, value, notes); - { runPendingToggles(taskId) }.delay(delay) - } + onToggle = { timestamp, value, notes -> + triggerRipple(timestamp) + habit?.let { behavior.onToggle(it, timestamp, value, notes) } } onEdit = { timestamp -> triggerRipple(timestamp) @@ -205,25 +194,6 @@ class HabitCardView( addView(innerFrame) } - @Synchronized - private fun runPendingToggles(id: Int) { - if (currentToggleTaskId != id) return - for ((h, t, v, n) in queuedToggles) behavior.onToggle(h, t, v, n) - queuedToggles.clear() - } - - @Synchronized - private fun queueToggle( - it: Habit, - timestamp: Timestamp, - value: Int, - notes: String, - ): Int { - currentToggleTaskId += 1 - queuedToggles.add(DelayedToggle(it, timestamp, value, notes)) - return currentToggleTaskId - } - override fun onModelChange() { Handler(Looper.getMainLooper()).post { habit?.let { copyAttributesFrom(it) } diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitActivity.kt b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitActivity.kt index b993d0d20..beb488095 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitActivity.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitActivity.kt @@ -34,10 +34,10 @@ import org.isoron.uhabits.HabitsApplication import org.isoron.uhabits.R import org.isoron.uhabits.activities.AndroidThemeSwitcher import org.isoron.uhabits.activities.HabitsDirFinder -import org.isoron.uhabits.activities.common.dialogs.CheckmarkPopup +import org.isoron.uhabits.activities.common.dialogs.CheckmarkDialog import org.isoron.uhabits.activities.common.dialogs.ConfirmDeleteDialog import org.isoron.uhabits.activities.common.dialogs.HistoryEditorDialog -import org.isoron.uhabits.activities.common.dialogs.NumberPopup +import org.isoron.uhabits.activities.common.dialogs.NumberDialog import org.isoron.uhabits.core.commands.Command import org.isoron.uhabits.core.commands.CommandRunner import org.isoron.uhabits.core.models.Habit @@ -51,6 +51,7 @@ 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.dismissCurrentDialog import org.isoron.uhabits.utils.showMessage import org.isoron.uhabits.utils.showSendFileScreen import org.isoron.uhabits.widgets.WidgetUpdater @@ -129,6 +130,7 @@ class ShowHabitActivity : AppCompatActivity(), CommandRunner.Listener { } override fun onPause() { + dismissCurrentDialog() commandRunner.removeListener(this) super.onPause() } @@ -170,41 +172,32 @@ class ShowHabitActivity : AppCompatActivity(), CommandRunner.Listener { override fun showNumberPopup( value: Double, notes: String, - preferences: Preferences, callback: ListHabitsBehavior.NumberPickerCallback ) { - val anchor = getPopupAnchor() ?: return - NumberPopup( - context = this@ShowHabitActivity, - prefs = preferences, - notes = notes, - anchor = anchor, - value = value, - ).apply { - onToggle = { v, n -> callback.onNumberPicked(v, n) } - show() + val dialog = NumberDialog() + dialog.arguments = Bundle().apply { + putDouble("value", value) + putString("notes", notes) } + dialog.onToggle = { v, n -> callback.onNumberPicked(v, n) } + dialog.dismissCurrentAndShow(supportFragmentManager, "numberDialog") } override fun showCheckmarkPopup( selectedValue: Int, notes: String, - preferences: Preferences, color: PaletteColor, callback: ListHabitsBehavior.CheckMarkDialogCallback ) { - val anchor = getPopupAnchor() ?: return - CheckmarkPopup( - context = this@ShowHabitActivity, - prefs = preferences, - notes = notes, - color = view.currentTheme().color(color).toInt(), - anchor = anchor, - value = selectedValue, - ).apply { - onToggle = { v, n -> callback.onNotesSaved(v, n) } - show() + val theme = view.currentTheme() + val dialog = CheckmarkDialog() + dialog.arguments = Bundle().apply { + putInt("color", theme.color(color).toInt()) + putInt("value", selectedValue) + putString("notes", notes) } + dialog.onToggle = { v, n -> callback.onNotesSaved(v, n) } + dialog.dismissCurrentAndShow(supportFragmentManager, "checkmarkDialog") } private fun getPopupAnchor(): View? { diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/utils/DialogUtils.kt b/uhabits-android/src/main/java/org/isoron/uhabits/utils/DialogUtils.kt index 855f865c7..a1aefe16f 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/utils/DialogUtils.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/utils/DialogUtils.kt @@ -6,16 +6,24 @@ import androidx.fragment.app.FragmentManager import java.lang.ref.WeakReference var currentDialog: WeakReference = WeakReference(null) +var currentDialogFragment: WeakReference = WeakReference(null) -fun Dialog.dismissCurrentAndShow() { +fun dismissCurrentDialog() { currentDialog.get()?.dismiss() + currentDialog = WeakReference(null) + currentDialogFragment.get()?.dismiss() + currentDialogFragment = WeakReference(null) +} + +fun Dialog.dismissCurrentAndShow() { + dismissCurrentDialog() currentDialog = WeakReference(this) show() } fun DialogFragment.dismissCurrentAndShow(fragmentManager: FragmentManager, tag: String) { - currentDialog.get()?.dismiss() + dismissCurrentDialog() + currentDialogFragment = WeakReference(this) show(fragmentManager, tag) fragmentManager.executePendingTransactions() - currentDialog = WeakReference(this.dialog) } diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/widgets/CheckmarkWidget.kt b/uhabits-android/src/main/java/org/isoron/uhabits/widgets/CheckmarkWidget.kt index fbbb7ffd1..60110ea3a 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/widgets/CheckmarkWidget.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/widgets/CheckmarkWidget.kt @@ -43,7 +43,7 @@ open class CheckmarkWidget( override fun getOnClickPendingIntent(context: Context): PendingIntent? { return if (habit.isNumerical) { - pendingIntentFactory.showNumberPicker(habit, DateUtils.getToday()) + pendingIntentFactory.showNumberPicker(habit, DateUtils.getTodayWithOffset()) } else { pendingIntentFactory.toggleCheckmark(habit, null) } diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/widgets/StackWidgetService.kt b/uhabits-android/src/main/java/org/isoron/uhabits/widgets/StackWidgetService.kt index d23005c96..b8e2ba49b 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/widgets/StackWidgetService.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/widgets/StackWidgetService.kt @@ -33,7 +33,7 @@ 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.core.utils.DateUtils.Companion.getTodayWithOffset import org.isoron.uhabits.intents.IntentFactory import org.isoron.uhabits.intents.PendingIntentFactory import org.isoron.uhabits.utils.InterfaceUtils.dpToPixels @@ -101,7 +101,7 @@ internal class StackRemoteViewsFactory(private val context: Context, intent: Int val landscapeViews = widget.landscapeRemoteViews val portraitViews = widget.portraitRemoteViews val factory = PendingIntentFactory(context, IntentFactory()) - val intent = StackWidgetType.getIntentFillIn(factory, widgetType, h, habits, getToday()) + val intent = StackWidgetType.getIntentFillIn(factory, widgetType, h, habits, getTodayWithOffset()) landscapeViews.setOnClickFillInIntent(R.id.button, intent) portraitViews.setOnClickFillInIntent(R.id.button, intent) val remoteViews = RemoteViews(landscapeViews, portraitViews) diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/widgets/activities/HabitPickerDialog.kt b/uhabits-android/src/main/java/org/isoron/uhabits/widgets/activities/HabitPickerDialog.kt index ff743816b..67ad8ae2d 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/widgets/activities/HabitPickerDialog.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/widgets/activities/HabitPickerDialog.kt @@ -24,7 +24,6 @@ import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID import android.appwidget.AppWidgetManager.INVALID_APPWIDGET_ID import android.content.Intent import android.os.Bundle -import android.widget.AbsListView.CHOICE_MODE_MULTIPLE import android.widget.ArrayAdapter import android.widget.Button import android.widget.ListView @@ -34,7 +33,6 @@ import org.isoron.uhabits.R import org.isoron.uhabits.activities.AndroidThemeSwitcher import org.isoron.uhabits.core.preferences.WidgetPreferences import org.isoron.uhabits.widgets.WidgetUpdater -import java.util.ArrayList class BooleanHabitPickerDialog : HabitPickerDialog() { override fun shouldHideNumerical() = true @@ -88,20 +86,12 @@ open class HabitPickerDialog : Activity() { with(listView) { adapter = ArrayAdapter( context, - android.R.layout.simple_list_item_multiple_choice, + android.R.layout.simple_list_item_1, habitNames ) - choiceMode = CHOICE_MODE_MULTIPLE - itemsCanFocus = false - } - saveButton.setOnClickListener { - val selectedIds = mutableListOf() - for (i in 0..listView.count) { - if (listView.isItemChecked(i)) { - selectedIds.add(habitIds[i]) - } + setOnItemClickListener { parent, view, position, id -> + confirm(mutableListOf(habitIds[position])) } - confirm(selectedIds) } } diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/widgets/views/CheckmarkWidgetView.kt b/uhabits-android/src/main/java/org/isoron/uhabits/widgets/views/CheckmarkWidgetView.kt index 57cff3ddb..a0e455688 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/widgets/views/CheckmarkWidgetView.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/widgets/views/CheckmarkWidgetView.kt @@ -66,23 +66,21 @@ class CheckmarkWidgetView : HabitWidgetView { val res = StyledResources(context) val bgColor: Int val fgColor: Int + setShadowAlpha(0x4f) when (entryState) { YES_MANUAL, SKIP -> { bgColor = activeColor fgColor = res.getColor(R.attr.contrast0) - setShadowAlpha(0x4f) backgroundPaint!!.color = bgColor frame!!.setBackgroundDrawable(background) } YES_AUTO, NO, UNKNOWN -> { bgColor = res.getColor(R.attr.cardBgColor) fgColor = res.getColor(R.attr.contrast60) - setShadowAlpha(0x00) } else -> { bgColor = res.getColor(R.attr.cardBgColor) fgColor = res.getColor(R.attr.contrast60) - setShadowAlpha(0x00) } } ring.setPercentage(percentage) @@ -126,7 +124,7 @@ class CheckmarkWidgetView : HabitWidgetView { } else { width = min(width, height) } - val textSize = min(0.2f * width, getDimension(context, R.dimen.smallerTextSize)) + val textSize = min(0.175f * width, getDimension(context, R.dimen.smallTextSize)) label.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize) if (isNumerical) { ring.setTextSize(textSize * 0.9f) @@ -141,7 +139,8 @@ class CheckmarkWidgetView : HabitWidgetView { } private fun init() { - val appComponent: HabitsApplicationComponent = (context.applicationContext as HabitsApplication).component + val appComponent: HabitsApplicationComponent = + (context.applicationContext as HabitsApplication).component preferences = appComponent.preferences ring = findViewById(R.id.scoreRing) as RingView label = findViewById(R.id.label) as TextView diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/widgets/views/HabitWidgetView.kt b/uhabits-android/src/main/java/org/isoron/uhabits/widgets/views/HabitWidgetView.kt index 85a219479..6756b8acd 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/widgets/views/HabitWidgetView.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/widgets/views/HabitWidgetView.kt @@ -69,7 +69,7 @@ abstract class HabitWidgetView : FrameLayout { val shadowRadius = dpToPixels(context, 2f).toInt() val shadowOffset = dpToPixels(context, 1f).toInt() val shadowColor = Color.argb(shadowAlpha, 0, 0, 0) - val cornerRadius = dpToPixels(context, 5f) + val cornerRadius = dpToPixels(context, 12f) val radii = FloatArray(8) Arrays.fill(radii, cornerRadius) val shape = RoundRectShape(radii, null, null) diff --git a/uhabits-android/src/main/res/drawable-v21/widget_button_background.xml b/uhabits-android/src/main/res/drawable-v21/widget_button_background.xml index 9187c326a..a5d8b5fbf 100644 --- a/uhabits-android/src/main/res/drawable-v21/widget_button_background.xml +++ b/uhabits-android/src/main/res/drawable-v21/widget_button_background.xml @@ -28,7 +28,7 @@ - + diff --git a/uhabits-android/src/main/res/layout/checkmark_popup.xml b/uhabits-android/src/main/res/layout/checkmark_popup.xml index 72dc79786..e21c2fb54 100644 --- a/uhabits-android/src/main/res/layout/checkmark_popup.xml +++ b/uhabits-android/src/main/res/layout/checkmark_popup.xml @@ -21,8 +21,10 @@ -