second commit
This commit is contained in:
parent
bdddb15989
commit
42713b3b89
@ -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()
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -110,6 +110,11 @@ class ListHabitsMenu(
|
||||
return true
|
||||
}
|
||||
|
||||
R.id.actionRemoveAll -> {
|
||||
behavior.onRemoveAll()
|
||||
return true
|
||||
}
|
||||
|
||||
R.id.actionHideArchived -> {
|
||||
behavior.onToggleShowArchived()
|
||||
activity.invalidateOptionsMenu()
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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")
|
||||
|
||||
@ -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 {
|
||||
""
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -126,9 +126,12 @@
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Habit Question -->
|
||||
<FrameLayout style="@style/FormOuterBox">
|
||||
<FrameLayout
|
||||
android:id="@+id/questionOuterBox"
|
||||
style="@style/FormOuterBox">
|
||||
<LinearLayout style="@style/FormInnerBox">
|
||||
<TextView
|
||||
android:id="@+id/questionLabel"
|
||||
style="@style/FormLabel"
|
||||
android:text="@string/question" />
|
||||
<EditText
|
||||
@ -187,6 +190,7 @@
|
||||
android:layout_weight="1">
|
||||
<LinearLayout style="@style/FormInnerBox">
|
||||
<TextView
|
||||
android:id="@+id/targetLabel"
|
||||
style="@style/FormLabel"
|
||||
android:text="@string/target" />
|
||||
<EditText
|
||||
|
||||
@ -30,6 +30,32 @@
|
||||
app:divider="@drawable/checkmark_dialog_divider"
|
||||
app:showDividers="middle">
|
||||
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:id="@+id/header"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="8dp"
|
||||
android:gravity="center">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/habitName"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="?attr/contrast100"
|
||||
android:visibility="gone" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/unitLabel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="12sp"
|
||||
android:textColor="?attr/contrast60"
|
||||
android:visibility="gone" />
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatEditText
|
||||
android:id="@+id/notes"
|
||||
android:layout_width="match_parent"
|
||||
@ -82,6 +108,12 @@
|
||||
app:divider="@drawable/checkmark_dialog_divider"
|
||||
app:showDividers="middle">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/deleteBtn"
|
||||
style="@style/NumericalPopupBtn"
|
||||
android:text="@string/fa_trash"
|
||||
android:visibility="gone" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatEditText
|
||||
android:id="@+id/value"
|
||||
android:layout_width="0dp"
|
||||
@ -108,4 +140,4 @@
|
||||
style="@style/CheckmarkPopupBtn"
|
||||
android:text="@string/fa_question" />
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
@ -47,6 +47,16 @@
|
||||
android:id="@+id/subtitleCard"
|
||||
style="@style/ShowHabit.Subtitle" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/deleteButton"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginRight="16dp"
|
||||
android:text="@string/delete" />
|
||||
|
||||
<org.isoron.uhabits.activities.habits.show.views.NotesCardView
|
||||
android:id="@+id/notesCard"
|
||||
style="@style/Card"
|
||||
|
||||
@ -88,6 +88,12 @@
|
||||
android:title="@string/action_settings"
|
||||
app:showAsAction="never"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/actionRemoveAll"
|
||||
android:orderInCategory="100"
|
||||
android:title="@string/remove_all"
|
||||
app:showAsAction="never"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/actionFAQ"
|
||||
android:orderInCategory="100"
|
||||
|
||||
@ -189,6 +189,7 @@
|
||||
<string name="target_type_at_most">Не больше</string>
|
||||
<string name="example_question_boolean">напр.: Вы упражнялись сегодня?</string>
|
||||
<string name="question">Вопрос</string>
|
||||
<string name="quantity">Кол-во</string>
|
||||
<string name="target">Цель</string>
|
||||
<string name="yes">Да</string>
|
||||
<string name="no">Нет</string>
|
||||
@ -206,6 +207,22 @@
|
||||
<string name="yes_or_no_example">Например, вы рано проснулись сегодня? Вы занимались спортом? Вы играли в шахматы?</string>
|
||||
<string name="measurable">Измеримый</string>
|
||||
<string name="measurable_example">напр.: Сколько км вы пробежали сегодня? Сколько страниц прочитали?</string>
|
||||
<string name="time_cost">Вредные привычки</string>
|
||||
<string name="time_cost_example">Отслеживайте вредные привычки</string>
|
||||
<string name="time_cost_preset_cup_200">Чашка (200 мл)</string>
|
||||
<string name="time_cost_preset_small_cup_100">Маленькая чашка (100 мл)</string>
|
||||
<string name="time_cost_preset_large_cup_300">Большая чашка (300 мл)</string>
|
||||
<string name="time_cost_preset_sip_20">Глоток (20 мл)</string>
|
||||
<string name="time_cost_preset_shot_50">Рюмка (50 мл)</string>
|
||||
<string name="time_cost_preset_glass_150">Бокал (150 мл)</string>
|
||||
<string name="time_cost_preset_can_500">Банка (500 мл)</string>
|
||||
<string name="time_cost_preset_bottle_1_5l">Бутылка (1,5 л)</string>
|
||||
<string name="time_cost_preset_sip_30">Глоток (30 мл)</string>
|
||||
<string name="time_cost_preset_small_can_250">Маленькая банка (250 мл)</string>
|
||||
<string name="time_cost_preset_medium_can_330">Средняя банка (330 мл)</string>
|
||||
<string name="time_cost_preset_large_can_500">Большая банка (500 мл)</string>
|
||||
<string name="time_cost_preset_cigarette_1">Сигарета (1 шт)</string>
|
||||
<string name="time_cost_preset_vape_puff_1">Затяжка вейп (1 шт)</string>
|
||||
<string name="x_times_per_week">%d раз в неделю</string>
|
||||
<string name="x_times_per_month">%d раз в месяц</string>
|
||||
<string name="x_times_per_y_days">%d раз в %d дней</string>
|
||||
@ -215,6 +232,10 @@
|
||||
<string name="measurable_short_example">напр.: Побегать</string>
|
||||
<string name="measurable_question_example">напр.: Сколько км вы пробежали сегодня?</string>
|
||||
<string name="measurable_units_example">напр.: км</string>
|
||||
<string name="time_cost_short_example">напр.: Курить</string>
|
||||
<string name="time_cost_question_example">напр.: Сколько сигарет сегодня?</string>
|
||||
<string name="time_lost_today_summary">Сегодня потеряно\n%1$dд%2$dч%3$dм</string>
|
||||
<string name="time_lost_today_card">Потеряно времени сегодня: %d мин</string>
|
||||
<string name="every_month">Каждый месяц</string>
|
||||
<string name="validation_cannot_be_blank">Не может быть пустым</string>
|
||||
<string name="today">Сегодня</string>
|
||||
|
||||
@ -29,6 +29,7 @@
|
||||
<string translatable="false" name="fa_calendar"></string>
|
||||
<string translatable="false" name="fa_exclamation_circle"></string>
|
||||
<string translatable="false" name="fa_question"></string>
|
||||
<string translatable="false" name="fa_trash"></string>
|
||||
<string translatable="false" name="fa_umbrella_beach"></string>
|
||||
|
||||
<!--<string translatable="false" name="fa_glass"></string>-->
|
||||
|
||||
@ -181,6 +181,8 @@
|
||||
<string name="by_color">By color</string>
|
||||
<string name="by_score">By score</string>
|
||||
<string name="by_status">By status</string>
|
||||
<string name="remove_all">Remove all</string>
|
||||
<string name="remove_all_message">All habits and their history will be permanently deleted. This action cannot be undone.</string>
|
||||
<string name="export">Export</string>
|
||||
<string name="long_press_to_edit">Press-and-hold to change the value</string>
|
||||
<string name="value">Value</string>
|
||||
@ -191,6 +193,7 @@
|
||||
<string name="target_type_at_most">At most</string>
|
||||
<string name="example_question_boolean">e.g. Did you exercise today?</string>
|
||||
<string name="question">Question</string>
|
||||
<string name="quantity">Count</string>
|
||||
<string name="target">Target</string>
|
||||
<string name="yes">Yes</string>
|
||||
<string name="no">No</string>
|
||||
@ -208,8 +211,22 @@
|
||||
<string name="yes_or_no_example">e.g. Did you wake up early today? Did you exercise? Did you play chess?</string>
|
||||
<string name="measurable">Measurable</string>
|
||||
<string name="measurable_example">e.g. How many miles did you run today? How many pages did you read?</string>
|
||||
<string name="time_cost">Time Cost</string>
|
||||
<string name="time_cost_example">Track lost time in minutes</string>
|
||||
<string name="time_cost">Harmful habits</string>
|
||||
<string name="time_cost_example">Track harmful habits</string>
|
||||
<string name="time_cost_preset_cup_200">Cup (200 ml)</string>
|
||||
<string name="time_cost_preset_small_cup_100">Small cup (100 ml)</string>
|
||||
<string name="time_cost_preset_large_cup_300">Large cup (300 ml)</string>
|
||||
<string name="time_cost_preset_sip_20">Sip (20 ml)</string>
|
||||
<string name="time_cost_preset_shot_50">Shot (50 ml)</string>
|
||||
<string name="time_cost_preset_glass_150">Glass (150 ml)</string>
|
||||
<string name="time_cost_preset_can_500">Can (500 ml)</string>
|
||||
<string name="time_cost_preset_bottle_1_5l">Bottle (1.5 l)</string>
|
||||
<string name="time_cost_preset_sip_30">Sip (30 ml)</string>
|
||||
<string name="time_cost_preset_small_can_250">Small can (250 ml)</string>
|
||||
<string name="time_cost_preset_medium_can_330">Medium can (330 ml)</string>
|
||||
<string name="time_cost_preset_large_can_500">Large can (500 ml)</string>
|
||||
<string name="time_cost_preset_cigarette_1">Cigarette (1 pc)</string>
|
||||
<string name="time_cost_preset_vape_puff_1">Vape puff (1 pc)</string>
|
||||
<string name="x_times_per_week">%d times per week</string>
|
||||
<string name="x_times_per_month">%d times per month</string>
|
||||
<string name="x_times_per_y_days">%d times in %d days</string>
|
||||
@ -221,7 +238,8 @@
|
||||
<string name="measurable_units_example">e.g. miles</string>
|
||||
<string name="time_cost_short_example">e.g. Smoke</string>
|
||||
<string name="time_cost_question_example">e.g. How many cigarettes today?</string>
|
||||
<string name="time_lost_today">Time lost today: %d min</string>
|
||||
<string name="time_lost_today_summary">Time lost today\n%1$d%2$dh%3$dm</string>
|
||||
<string name="time_lost_today_card">Time lost today: %d min</string>
|
||||
<string name="every_month">Every month</string>
|
||||
<string name="validation_cannot_be_blank">Cannot be blank</string>
|
||||
<string name="today">Today</string>
|
||||
|
||||
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user