From 42713b3b89c279bee8e47979645baeb8c5897a09 Mon Sep 17 00:00:00 2001 From: stud203990 Date: Fri, 8 May 2026 02:31:02 +0300 Subject: [PATCH] second commit --- .../habits/show/views/SubtitleCardViewTest.kt | 1 + .../activities/common/dialogs/NumberDialog.kt | 19 +++++ .../habits/edit/EditHabitActivity.kt | 77 +++++++++++++++---- .../activities/habits/list/ListHabitsMenu.kt | 5 ++ .../habits/list/ListHabitsRootView.kt | 19 ++++- .../habits/list/ListHabitsScreen.kt | 19 +++++ .../habits/list/views/HabitCardView.kt | 15 +++- .../habits/show/ShowHabitActivity.kt | 7 ++ .../activities/habits/show/ShowHabitView.kt | 9 +++ .../habits/show/views/SubtitleCardView.kt | 29 ++++++- .../main/res/layout/activity_edit_habit.xml | 6 +- .../src/main/res/layout/checkmark_popup.xml | 34 +++++++- .../src/main/res/layout/show_habit.xml | 10 +++ .../src/main/res/menu/list_habits.xml | 6 ++ .../src/main/res/values-ru-rRU/strings.xml | 21 +++++ .../src/main/res/values/fontawesome.xml | 1 + .../src/main/res/values/strings.xml | 24 +++++- .../screens/habits/list/ListHabitsBehavior.kt | 30 +++++++- .../habits/list/ListHabitsMenuBehavior.kt | 5 ++ .../core/ui/screens/habits/show/ShowHabit.kt | 2 + .../screens/habits/show/views/SubtitleCard.kt | 14 ++-- 21 files changed, 317 insertions(+), 36 deletions(-) diff --git a/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/SubtitleCardViewTest.kt b/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/SubtitleCardViewTest.kt index 691e8419..521fea0e 100644 --- a/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/SubtitleCardViewTest.kt +++ b/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/SubtitleCardViewTest.kt @@ -51,6 +51,7 @@ class SubtitleCardViewTest : BaseViewTest() { color = PaletteColor(7), frequency = Frequency(3, 7), isNumerical = false, + isTimeCost = false, question = "Did you meditate this morning?", reminder = Reminder(8, 30, EVERY_DAY), theme = LightTheme() 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 index e282c3d6..8fd3f18a 100644 --- 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 @@ -51,9 +51,24 @@ class NumberDialog : AppCompatDialogFragment() { if (!prefs.areQuestionMarksEnabled) view.unknownBtnNumber.visibility = View.GONE view.numberButtons.visibility = View.VISIBLE fixDecimalSeparator(view) + + val name = requireArguments().getString("habitName") + val unit = requireArguments().getString("unit") + if (name != null) { + view.habitName.text = name + view.habitName.visibility = View.VISIBLE + } + if (unit != null) { + view.unitLabel.text = unit + view.unitLabel.visibility = View.VISIBLE + } + originalNotes = requireArguments().getString("notes")!! originalValue = requireArguments().getDouble("value") view.notes.setText(originalNotes) + view.deleteBtn.visibility = if (originalValue > 0 || originalNotes.isNotEmpty()) View.VISIBLE else View.GONE + view.deleteBtn.setTextColor(view.root.sres.getColor(R.attr.contrast60)) + view.deleteBtn.typeface = InterfaceUtils.getFontAwesome(requireContext()) view.value.setText( when { originalValue < 0.01 -> "0" @@ -70,6 +85,10 @@ class NumberDialog : AppCompatDialogFragment() { view.saveBtn.setOnClickListener { save() } + view.deleteBtn.setOnClickListener { + onToggle(0.0, "") + requireDialog().dismiss() + } view.skipBtnNumber.setOnClickListener { view.value.setText(DecimalFormat("#.###").format((Entry.SKIP.toDouble() / 1000))) save() diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/edit/EditHabitActivity.kt b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/edit/EditHabitActivity.kt index c8434567..acd5866d 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/edit/EditHabitActivity.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/edit/EditHabitActivity.kt @@ -44,6 +44,7 @@ import org.isoron.uhabits.activities.common.dialogs.FrequencyPickerDialog import org.isoron.uhabits.activities.common.dialogs.WeekdayPickerDialog import org.isoron.uhabits.core.commands.CommandRunner import org.isoron.uhabits.core.commands.CreateHabitCommand +import org.isoron.uhabits.core.commands.CreateRepetitionCommand import org.isoron.uhabits.core.commands.EditHabitCommand import org.isoron.uhabits.core.models.Frequency import org.isoron.uhabits.core.models.Habit @@ -52,6 +53,7 @@ import org.isoron.uhabits.core.models.NumericalHabitType import org.isoron.uhabits.core.models.PaletteColor import org.isoron.uhabits.core.models.Reminder import org.isoron.uhabits.core.models.WeekdayList +import org.isoron.platform.time.getToday import org.isoron.uhabits.databinding.ActivityEditHabitBinding import org.isoron.uhabits.utils.applyRootViewInsets import org.isoron.uhabits.utils.applyToolbarInsets @@ -60,17 +62,26 @@ import org.isoron.uhabits.utils.formatTime import org.isoron.uhabits.utils.toFormattedString private data class UnitPreset( - val label: String, + val labelRes: Int, val unit: String, val minutesPerUnit: Int ) private val TIME_COST_PRESETS = listOf( - UnitPreset("Банка (500 мл)", "банка", 30), - UnitPreset("Стакан (250 мл)", "стакан", 15), - UnitPreset("Глоток", "глоток", 1), - UnitPreset("Сигарета", "сигарета", 11), - UnitPreset("Затяжка", "затяжка", 1) + UnitPreset(R.string.time_cost_preset_cup_200, "cup_200", 12), + UnitPreset(R.string.time_cost_preset_small_cup_100, "cup_100", 6), + UnitPreset(R.string.time_cost_preset_large_cup_300, "cup_300", 18), + UnitPreset(R.string.time_cost_preset_sip_20, "sip_20", 1), + UnitPreset(R.string.time_cost_preset_shot_50, "shot_50", 3), + UnitPreset(R.string.time_cost_preset_glass_150, "glass_150", 9), + UnitPreset(R.string.time_cost_preset_can_500, "can_500", 30), + UnitPreset(R.string.time_cost_preset_bottle_1_5l, "bottle_1_5l", 90), + UnitPreset(R.string.time_cost_preset_sip_30, "sip_30", 2), + UnitPreset(R.string.time_cost_preset_small_can_250, "can_250", 15), + UnitPreset(R.string.time_cost_preset_medium_can_330, "can_330", 20), + UnitPreset(R.string.time_cost_preset_large_can_500, "can_500_large", 30), + UnitPreset(R.string.time_cost_preset_cigarette_1, "cigarette_1", 11), + UnitPreset(R.string.time_cost_preset_vape_puff_1, "vape_puff_1", 1) ) fun formatFrequency(freqNum: Int, freqDen: Int, resources: Resources) = when { @@ -168,16 +179,16 @@ class EditHabitActivity : AppCompatActivity() { } HabitType.TIME_COST -> { binding.nameInput.hint = getString(R.string.time_cost_short_example) - binding.questionInput.hint = getString(R.string.time_cost_question_example) + binding.questionOuterBox.visibility = View.GONE binding.frequencyOuterBox.visibility = View.GONE - binding.targetOuterBox.visibility = View.GONE binding.targetTypeOuterBox.visibility = View.GONE + binding.targetOuterBox.visibility = View.GONE val preset = findPresetByUnit(unit) ?: findPresetByMinutes(minutesPerUnit) ?: TIME_COST_PRESETS.first() - minutesPerUnit = preset.minutesPerUnit unit = preset.unit - binding.unitInput.setText(preset.label, false) + minutesPerUnit = preset.minutesPerUnit + binding.unitInput.setText(getPresetLabel(preset), false) } } @@ -303,7 +314,11 @@ class EditHabitActivity : AppCompatActivity() { } habit.name = binding.nameInput.text.trim().toString() - habit.question = binding.questionInput.text.trim().toString() + habit.question = if (habitType == HabitType.TIME_COST) { + "" + } else { + binding.questionInput.text.trim().toString() + } habit.description = binding.notesInput.text.trim().toString() habit.color = color if (reminderHour >= 0) { @@ -314,13 +329,15 @@ class EditHabitActivity : AppCompatActivity() { habit.frequency = Frequency(freqNum, freqDen) if (habitType == HabitType.NUMERICAL) { - habit.targetValue = binding.targetInput.text.toString().toDouble() + habit.targetValue = binding.targetInput.text.toString().toDoubleOrNull() ?: 0.0 habit.targetType = targetType habit.unit = binding.unitInput.text.trim().toString() } else if (habitType == HabitType.TIME_COST) { val preset = selectedPreset() ?: findPresetByUnit(unit) ?: TIME_COST_PRESETS.first() habit.unit = preset.unit habit.minutesPerUnit = preset.minutesPerUnit + habit.targetValue = 0.0 + habit.targetType = NumericalHabitType.AT_LEAST } habit.type = habitType @@ -338,6 +355,22 @@ class EditHabitActivity : AppCompatActivity() { ) } component.commandRunner.run(command) + + if (habitType == HabitType.TIME_COST) { + val count = 0 + val savedHabit = component.habitList.getByUUID(habit.uuid) ?: habit + if (savedHabit.id != null) { + component.commandRunner.run( + CreateRepetitionCommand( + component.habitList, + savedHabit, + getToday(), + count, + "" + ) + ) + } + } finish() } @@ -366,13 +399,14 @@ class EditHabitActivity : AppCompatActivity() { val adapter = ArrayAdapter( this, android.R.layout.simple_list_item_1, - TIME_COST_PRESETS.map { it.label } + TIME_COST_PRESETS.map { getPresetLabel(it) } ) binding.unitInput.setAdapter(adapter) binding.unitInput.setOnItemClickListener { _, _, position, _ -> val preset = TIME_COST_PRESETS[position] minutesPerUnit = preset.minutesPerUnit - binding.unitInput.setText(preset.label, false) + binding.unitInput.setText(getPresetLabel(preset), false) + unit = preset.unit } binding.unitInput.setOnFocusChangeListener { _, hasFocus -> if (!hasFocus) syncPresetFromText() @@ -395,7 +429,9 @@ class EditHabitActivity : AppCompatActivity() { private fun selectedPreset(): UnitPreset? { val value = binding.unitInput.text?.toString()?.trim().orEmpty() - return TIME_COST_PRESETS.firstOrNull { it.label == value } + return TIME_COST_PRESETS.firstOrNull { + getPresetLabel(it) == value || it.unit == value + } } private fun findPresetByMinutes(minutes: Int): UnitPreset? { @@ -403,16 +439,23 @@ class EditHabitActivity : AppCompatActivity() { } private fun findPresetByUnit(unit: String): UnitPreset? { - return TIME_COST_PRESETS.firstOrNull { it.unit == unit || it.label == unit } + return TIME_COST_PRESETS.firstOrNull { + it.unit == unit || getPresetLabel(it) == unit + } } private fun syncPresetFromText() { selectedPreset()?.let { preset -> minutesPerUnit = preset.minutesPerUnit - binding.unitInput.setText(preset.label, false) + unit = preset.unit + binding.unitInput.setText(getPresetLabel(preset), false) } } + private fun getPresetLabel(preset: UnitPreset): String { + return getString(preset.labelRes) + } + private fun populateReminder() { if (reminderHour < 0) { binding.reminderTimePicker.text = getString(R.string.reminder_off) diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsMenu.kt b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsMenu.kt index 70629fc8..fcb8d5a1 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsMenu.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsMenu.kt @@ -110,6 +110,11 @@ class ListHabitsMenu( return true } + R.id.actionRemoveAll -> { + behavior.onRemoveAll() + return true + } + R.id.actionHideArchived -> { behavior.onToggleShowArchived() activity.invalidateOptionsMenu() diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsRootView.kt b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsRootView.kt index b165e392..3788e22a 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsRootView.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsRootView.kt @@ -20,9 +20,12 @@ package org.isoron.uhabits.activities.habits.list import android.content.Context +import android.view.Gravity import android.view.ViewGroup.LayoutParams.MATCH_PARENT +import android.graphics.Typeface import android.widget.FrameLayout import android.widget.RelativeLayout +import android.widget.TextView import me.tatarka.inject.annotations.Inject import nl.dionsegijn.konfetti.xml.KonfettiView import org.isoron.uhabits.R @@ -75,6 +78,14 @@ class ListHabitsRootView( translationZ = 10f } val progressBar = TaskProgressBar(context, runner) + val timeLossSummary = TextView(context).apply { + setTextColor(sres.getColor(R.attr.contrast60)) + textSize = 16f + setTypeface(typeface, Typeface.BOLD) + gravity = Gravity.CENTER_VERTICAL + setPadding(dp(16f).toInt(), dp(8f).toInt(), dp(16f).toInt(), dp(8f).toInt()) + text = context.getString(R.string.time_lost_today_summary, 0, 0, 0) + } val hintView: HintView val header = HeaderView(context, preferences, midnightTimer) @@ -88,9 +99,10 @@ class ListHabitsRootView( addAtTop(konfettiView) addAtTop(tbar) addBelow(header, tbar) - addBelow(listView, header, height = MATCH_PARENT) - addBelow(llEmpty, header, height = MATCH_PARENT) - addBelow(progressBar, header) { + addBelow(timeLossSummary, header) + addBelow(listView, timeLossSummary, height = MATCH_PARENT) + addBelow(llEmpty, timeLossSummary, height = MATCH_PARENT) + addBelow(progressBar, timeLossSummary) { it.topMargin = dp(-6.0f).toInt() } addAtBottom(hintView) @@ -124,6 +136,7 @@ class ListHabitsRootView( super.onAttachedToWindow() setupControllers() listAdapter.observable.addListener(this) + updateEmptyView() } override fun onDetachedFromWindow() { 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 abcfdd51..c4707941 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 @@ -24,6 +24,7 @@ import android.content.Context import android.content.Intent import android.os.Bundle import android.provider.Settings +import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import me.tatarka.inject.annotations.Inject import nl.dionsegijn.konfetti.core.Party @@ -172,6 +173,17 @@ class ListHabitsScreen( dialog.show(activity.supportFragmentManager, "habitType") } + override fun showRemoveAllDialog() { + val builder = AlertDialog.Builder(activity) + builder.setTitle(R.string.remove_all) + builder.setMessage(R.string.remove_all_message) + builder.setPositiveButton(R.string.yes) { _, _ -> + behavior.value.onRemoveAllConfirmed() + } + builder.setNegativeButton(R.string.no, null) + builder.show() + } + override fun showDeleteConfirmationScreen(callback: OnConfirmedCallback, quantity: Int) { ConfirmDeleteDialog(activity, callback, quantity).dismissCurrentAndShow() } @@ -272,13 +284,20 @@ class ListHabitsScreen( override fun showNumberPopup( value: Double, notes: String, + habitName: String?, + unit: String?, + color: PaletteColor?, callback: NumberPickerCallback ) { + val theme = themeSwitcher.currentTheme!! val fm = (context as AppCompatActivity).supportFragmentManager val dialog = NumberDialog() dialog.arguments = Bundle().apply { putDouble("value", value) putString("notes", notes) + putString("habitName", habitName) + putString("unit", unit) + putInt("color", theme.color(color ?: PaletteColor(11)).toInt()) } dialog.onToggle = { v, n -> callback.onNumberPicked(v, n) } dialog.dismissCurrentAndShow(fm, "numberDialog") 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 9dac7843..a4a1a381 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 @@ -154,6 +154,8 @@ class HabitCardView( gravity = Gravity.CENTER } setThickness(thickness) + isClickable = false + isFocusable = false } label = TextView(context).apply { @@ -162,6 +164,8 @@ class HabitCardView( if (SDK_INT >= Build.VERSION_CODES.Q) { breakStrategy = BREAK_STRATEGY_BALANCED } + isClickable = false + isFocusable = false } lossText = TextView(context).apply { @@ -176,9 +180,12 @@ class HabitCardView( layoutParams = LinearLayout.LayoutParams(0, WRAP_CONTENT, 1f) addView(label) addView(lossText) + isClickable = false + isFocusable = false } checkmarkPanel = checkmarkPanelFactory.create().apply { + isClickable = true onToggle = { date, value, notes -> triggerRipple(date) val location = getAbsoluteButtonLocation(date) @@ -248,6 +255,12 @@ class HabitCardView( v.background.setHotspot(event.x, event.y) false } + + setOnClickListener { + val date = getToday() + val location = PointF(it.width / 2f, it.height / 2f) + habit?.let { behavior.onEdit(it, date, location.x, location.y) } + } } clipToPadding = false @@ -339,7 +352,7 @@ class HabitCardView( visibility = if (h.isTimeCost) VISIBLE else GONE text = if (h.isTimeCost) { val lostMin = h.getTodayValue() * h.minutesPerUnit - context.getString(R.string.time_lost_today, lostMin) + context.getString(R.string.time_lost_today_card, lostMin) } else { "" } 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 f5e86e92..10a56df7 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 @@ -40,6 +40,7 @@ import org.isoron.uhabits.activities.common.dialogs.HistoryEditorDialog 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.commands.DeleteHabitsCommand import org.isoron.uhabits.core.models.Habit import org.isoron.uhabits.core.models.PaletteColor import org.isoron.uhabits.core.preferences.Preferences @@ -111,6 +112,12 @@ class ShowHabitActivity : AppCompatActivity(), CommandRunner.Listener { ) view.setListener(presenter) + view.setDeleteListener { + screen.showDeleteConfirmationScreen { + commandRunner.run(DeleteHabitsCommand(appComponent.habitList, listOf(habit))) + screen.close() + } + } view.applyRootViewInsets() setContentView(view) } diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitView.kt b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitView.kt index 448a94b6..7401de9b 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitView.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitView.kt @@ -31,6 +31,7 @@ import org.isoron.uhabits.utils.setupToolbar class ShowHabitView(context: Context) : FrameLayout(context) { private val binding = ShowHabitBinding.inflate(LayoutInflater.from(context)) + private var deleteListener: (() -> Unit)? = null init { binding.toolbar.applyToolbarInsets() @@ -58,6 +59,7 @@ class ShowHabitView(context: Context) : FrameLayout(context) { } else { binding.targetCard.visibility = GONE } + binding.deleteButton.visibility = VISIBLE binding.linearLayout.applyBottomInset() } @@ -65,5 +67,12 @@ class ShowHabitView(context: Context) : FrameLayout(context) { binding.scoreCard.setListener(presenter.scoreCardPresenter) binding.historyCard.setListener(presenter.historyCardPresenter) binding.barCard.setListener(presenter.barCardPresenter) + binding.deleteButton.setOnClickListener { + deleteListener?.invoke() + } + } + + fun setDeleteListener(listener: () -> Unit) { + deleteListener = listener } } diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/SubtitleCardView.kt b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/SubtitleCardView.kt index 588b068a..2c9fc214 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/SubtitleCardView.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/SubtitleCardView.kt @@ -61,7 +61,13 @@ class SubtitleCardView(context: Context, attrs: AttributeSet) : LinearLayout(con } else { resources.getString(R.string.reminder_off) } - binding.targetText.text = "${state.targetValue.toShortString()} ${state.unit}" + binding.targetText.text = if (state.isNumerical) { + "${state.targetValue.toShortString()} ${state.unit}" + } else if (state.isTimeCost) { + getTimeCostUnitLabel(state.unit) + } else { + state.unit + } binding.questionLabel.visibility = View.VISIBLE binding.targetIcon.visibility = View.VISIBLE @@ -73,7 +79,6 @@ class SubtitleCardView(context: Context, attrs: AttributeSet) : LinearLayout(con } } else { binding.targetIcon.visibility = View.GONE - binding.targetText.visibility = View.GONE } if (state.question.isEmpty()) { binding.questionLabel.visibility = View.GONE @@ -81,4 +86,24 @@ class SubtitleCardView(context: Context, attrs: AttributeSet) : LinearLayout(con postInvalidate() } + + private fun getTimeCostUnitLabel(unit: String): String { + return when (unit) { + "cup_200" -> resources.getString(R.string.time_cost_preset_cup_200) + "cup_100" -> resources.getString(R.string.time_cost_preset_small_cup_100) + "cup_300" -> resources.getString(R.string.time_cost_preset_large_cup_300) + "sip_20" -> resources.getString(R.string.time_cost_preset_sip_20) + "shot_50" -> resources.getString(R.string.time_cost_preset_shot_50) + "glass_150" -> resources.getString(R.string.time_cost_preset_glass_150) + "can_500" -> resources.getString(R.string.time_cost_preset_can_500) + "bottle_1_5l" -> resources.getString(R.string.time_cost_preset_bottle_1_5l) + "sip_30" -> resources.getString(R.string.time_cost_preset_sip_30) + "can_250" -> resources.getString(R.string.time_cost_preset_small_can_250) + "can_330" -> resources.getString(R.string.time_cost_preset_medium_can_330) + "can_500_large" -> resources.getString(R.string.time_cost_preset_large_can_500) + "cigarette_1" -> resources.getString(R.string.time_cost_preset_cigarette_1) + "vape_puff_1" -> resources.getString(R.string.time_cost_preset_vape_puff_1) + else -> unit + } + } } diff --git a/uhabits-android/src/main/res/layout/activity_edit_habit.xml b/uhabits-android/src/main/res/layout/activity_edit_habit.xml index 3c699806..a93520eb 100644 --- a/uhabits-android/src/main/res/layout/activity_edit_habit.xml +++ b/uhabits-android/src/main/res/layout/activity_edit_habit.xml @@ -126,9 +126,12 @@ - + + + + + + + + + + - \ No newline at end of file + diff --git a/uhabits-android/src/main/res/layout/show_habit.xml b/uhabits-android/src/main/res/layout/show_habit.xml index 7a7a8fc3..c0cf65ca 100644 --- a/uhabits-android/src/main/res/layout/show_habit.xml +++ b/uhabits-android/src/main/res/layout/show_habit.xml @@ -47,6 +47,16 @@ android:id="@+id/subtitleCard" style="@style/ShowHabit.Subtitle" /> + + + + Не больше напр.: Вы упражнялись сегодня? Вопрос + Кол-во Цель Да Нет @@ -206,6 +207,22 @@ Например, вы рано проснулись сегодня? Вы занимались спортом? Вы играли в шахматы? Измеримый напр.: Сколько км вы пробежали сегодня? Сколько страниц прочитали? + Вредные привычки + Отслеживайте вредные привычки + Чашка (200 мл) + Маленькая чашка (100 мл) + Большая чашка (300 мл) + Глоток (20 мл) + Рюмка (50 мл) + Бокал (150 мл) + Банка (500 мл) + Бутылка (1,5 л) + Глоток (30 мл) + Маленькая банка (250 мл) + Средняя банка (330 мл) + Большая банка (500 мл) + Сигарета (1 шт) + Затяжка вейп (1 шт) %d раз в неделю %d раз в месяц %d раз в %d дней @@ -215,6 +232,10 @@ напр.: Побегать напр.: Сколько км вы пробежали сегодня? напр.: км + напр.: Курить + напр.: Сколько сигарет сегодня? + Сегодня потеряно\n%1$dд%2$dч%3$dм + Потеряно времени сегодня: %d мин Каждый месяц Не может быть пустым Сегодня diff --git a/uhabits-android/src/main/res/values/fontawesome.xml b/uhabits-android/src/main/res/values/fontawesome.xml index 30b15fca..a74405c5 100644 --- a/uhabits-android/src/main/res/values/fontawesome.xml +++ b/uhabits-android/src/main/res/values/fontawesome.xml @@ -29,6 +29,7 @@ + diff --git a/uhabits-android/src/main/res/values/strings.xml b/uhabits-android/src/main/res/values/strings.xml index 6e74e984..a0c6fd40 100644 --- a/uhabits-android/src/main/res/values/strings.xml +++ b/uhabits-android/src/main/res/values/strings.xml @@ -181,6 +181,8 @@ By color By score By status + Remove all + All habits and their history will be permanently deleted. This action cannot be undone. Export Press-and-hold to change the value Value @@ -191,6 +193,7 @@ At most e.g. Did you exercise today? Question + Count Target Yes No @@ -208,8 +211,22 @@ e.g. Did you wake up early today? Did you exercise? Did you play chess? Measurable e.g. How many miles did you run today? How many pages did you read? - Time Cost - Track lost time in minutes + Harmful habits + Track harmful habits + Cup (200 ml) + Small cup (100 ml) + Large cup (300 ml) + Sip (20 ml) + Shot (50 ml) + Glass (150 ml) + Can (500 ml) + Bottle (1.5 l) + Sip (30 ml) + Small can (250 ml) + Medium can (330 ml) + Large can (500 ml) + Cigarette (1 pc) + Vape puff (1 pc) %d times per week %d times per month %d times in %d days @@ -221,7 +238,8 @@ e.g. miles e.g. Smoke e.g. How many cigarettes today? - Time lost today: %d min + Time lost today\n%1$d%2$dh%3$dm + Time lost today: %d min Every month Cannot be blank Today diff --git a/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsBehavior.kt b/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsBehavior.kt index b228be7f..28efc43a 100644 --- a/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsBehavior.kt +++ b/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsBehavior.kt @@ -57,7 +57,13 @@ open class ListHabitsBehavior( val entry = habit.computedEntries.get(date) if (habit.type == HabitType.TIME_COST) { val oldValue = entry.value.coerceAtLeast(0).toDouble() - screen.showNumberPopup(oldValue, entry.notes) { newValue: Double, newNotes: String -> + screen.showNumberPopup( + oldValue, + entry.notes, + habit.name, + null, + habit.color + ) { newValue: Double, newNotes: String -> val value = newValue.roundToInt().coerceAtLeast(0) if (newValue != oldValue && oldValue <= 0.0 && value > 0) { screen.showConfetti(habit.color, x, y) @@ -66,7 +72,13 @@ open class ListHabitsBehavior( } } else if (habit.type == HabitType.NUMERICAL) { val oldValue = entry.value.toDouble() / 1000 - screen.showNumberPopup(oldValue, entry.notes) { newValue: Double, newNotes: String -> + screen.showNumberPopup( + oldValue, + entry.notes, + habit.name, + habit.unit, + habit.color + ) { newValue: Double, newNotes: String -> val value = (newValue * 1000).roundToInt() if (newValue != oldValue) { if ( @@ -114,6 +126,16 @@ open class ListHabitsBehavior( taskRunner.execute { habitList.reorder(from, to) } } + open fun onRemoveAll() { + screen.showRemoveAllDialog() + } + + open fun onRemoveAllConfirmed() { + taskRunner.execute { + habitList.removeAll() + } + } + open fun onRepairDB() { taskRunner.execute(object : Task { override suspend fun doInBackground() { @@ -178,6 +200,9 @@ open class ListHabitsBehavior( fun showNumberPopup( value: Double, notes: String, + habitName: String? = null, + unit: String? = null, + color: PaletteColor? = null, callback: NumberPickerCallback ) fun showCheckmarkPopup( @@ -189,5 +214,6 @@ open class ListHabitsBehavior( fun showSendBugReportToDeveloperScreen(log: String) fun showSendFileScreen(filename: String) fun showConfetti(color: PaletteColor, x: Float, y: Float) + fun showRemoveAllDialog() } } diff --git a/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsMenuBehavior.kt b/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsMenuBehavior.kt index ca36f80b..ea919071 100644 --- a/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsMenuBehavior.kt +++ b/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsMenuBehavior.kt @@ -50,6 +50,10 @@ class ListHabitsMenuBehavior( screen.showSettingsScreen() } + fun onRemoveAll() { + screen.showRemoveAllDialog() + } + fun onToggleShowArchived() { showArchived = !showArchived preferences.showArchived = showArchived @@ -134,6 +138,7 @@ class ListHabitsMenuBehavior( fun showFAQScreen() fun showSettingsScreen() fun showSelectHabitTypeDialog() + fun showRemoveAllDialog() } init { diff --git a/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/ui/screens/habits/show/ShowHabit.kt b/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/ui/screens/habits/show/ShowHabit.kt index 2b662c83..4c7baaab 100644 --- a/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/ui/screens/habits/show/ShowHabit.kt +++ b/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/ui/screens/habits/show/ShowHabit.kt @@ -47,6 +47,7 @@ import org.isoron.uhabits.core.ui.views.Theme data class ShowHabitState( val title: String = "", val isNumerical: Boolean = false, + val isTimeCost: Boolean = false, val color: PaletteColor = PaletteColor(1), val subtitle: SubtitleCardState, val overview: OverviewCardState, @@ -95,6 +96,7 @@ class ShowHabitPresenter( title = habit.name, color = habit.color, isNumerical = habit.isNumerical, + isTimeCost = habit.isTimeCost, theme = theme, subtitle = SubtitleCardPresenter.buildState( habit = habit, diff --git a/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/ui/screens/habits/show/views/SubtitleCard.kt b/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/ui/screens/habits/show/views/SubtitleCard.kt index dbaba982..94b41da3 100644 --- a/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/ui/screens/habits/show/views/SubtitleCard.kt +++ b/uhabits-core/src/commonMain/kotlin/org/isoron/uhabits/core/ui/screens/habits/show/views/SubtitleCard.kt @@ -30,6 +30,7 @@ data class SubtitleCardState( val color: PaletteColor, val frequency: Frequency, val isNumerical: Boolean, + val isTimeCost: Boolean, val question: String, val reminder: Reminder?, val targetValue: Double = 0.0, @@ -44,12 +45,13 @@ class SubtitleCardPresenter { habit: Habit, theme: Theme ): SubtitleCardState = SubtitleCardState( - color = habit.color, - frequency = habit.frequency, - isNumerical = habit.isNumerical, - question = habit.question, - reminder = habit.reminder, - targetValue = habit.targetValue, + color = habit.color, + frequency = habit.frequency, + isNumerical = habit.isNumerical, + isTimeCost = habit.isTimeCost, + question = habit.question, + reminder = habit.reminder, + targetValue = habit.targetValue, targetType = habit.targetType, unit = habit.unit, theme = theme