mirror of
https://github.com/iSoron/uhabits.git
synced 2025-12-07 17:48:52 -06:00
improve yes/no dialog design
This commit is contained in:
@@ -1,36 +1,63 @@
|
|||||||
package org.isoron.uhabits.activities.common.dialogs
|
package org.isoron.uhabits.activities.common.dialogs
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.graphics.Typeface
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
import android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE
|
import android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE
|
||||||
import android.widget.EditText
|
import android.widget.Button
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import org.isoron.uhabits.R
|
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.models.PaletteColor
|
||||||
|
import org.isoron.uhabits.core.preferences.Preferences
|
||||||
import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsBehavior
|
import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsBehavior
|
||||||
|
import org.isoron.uhabits.databinding.CheckmarkDialogBinding
|
||||||
import org.isoron.uhabits.inject.ActivityContext
|
import org.isoron.uhabits.inject.ActivityContext
|
||||||
|
import org.isoron.uhabits.utils.InterfaceUtils
|
||||||
|
import org.isoron.uhabits.utils.StyledResources
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class CheckmarkDialog
|
class CheckmarkDialog
|
||||||
@Inject constructor(
|
@Inject constructor(
|
||||||
@ActivityContext private val context: Context
|
@ActivityContext private val context: Context,
|
||||||
) {
|
private val preferences: Preferences,
|
||||||
|
) : View.OnClickListener {
|
||||||
|
|
||||||
|
private lateinit var binding: CheckmarkDialogBinding
|
||||||
|
private lateinit var fontAwesome: Typeface
|
||||||
|
private val allButtons = mutableListOf<Button>()
|
||||||
|
private var selectedButton: Button? = null
|
||||||
|
|
||||||
fun create(
|
fun create(
|
||||||
|
value: Int,
|
||||||
notes: String,
|
notes: String,
|
||||||
|
dateString: String,
|
||||||
|
paletteColor: PaletteColor,
|
||||||
callback: ListHabitsBehavior.CheckMarkDialogCallback
|
callback: ListHabitsBehavior.CheckMarkDialogCallback
|
||||||
): AlertDialog {
|
): AlertDialog {
|
||||||
val inflater = LayoutInflater.from(context)
|
binding = CheckmarkDialogBinding.inflate(LayoutInflater.from(context))
|
||||||
val view = inflater.inflate(R.layout.checkmark_dialog, null)
|
fontAwesome = InterfaceUtils.getFontAwesome(context)!!
|
||||||
|
|
||||||
val etNotes = view.findViewById<EditText>(R.id.etNotes)
|
binding.etNotes.setText(notes)
|
||||||
|
setUpButtons(value, Color.parseColor(paletteColor.toCsvColor()))
|
||||||
|
|
||||||
etNotes.setText(notes)
|
|
||||||
val dialog = AlertDialog.Builder(context)
|
val dialog = AlertDialog.Builder(context)
|
||||||
.setView(view)
|
.setView(binding.root)
|
||||||
.setTitle(R.string.edit_notes)
|
.setTitle(dateString)
|
||||||
.setPositiveButton(R.string.save) { _, _ ->
|
.setPositiveButton(R.string.save) { _, _ ->
|
||||||
val note = etNotes.text.toString()
|
val newValue = when (selectedButton?.id) {
|
||||||
callback.onNotesSaved(note)
|
R.id.yesBtn -> YES_MANUAL
|
||||||
|
R.id.noBtn -> NO
|
||||||
|
R.id.skippedBtn -> SKIP
|
||||||
|
else -> UNKNOWN
|
||||||
|
}
|
||||||
|
callback.onNotesSaved(newValue, binding.etNotes.text.toString())
|
||||||
}
|
}
|
||||||
.setNegativeButton(android.R.string.cancel) { _, _ ->
|
.setNegativeButton(android.R.string.cancel) { _, _ ->
|
||||||
callback.onNotesDismissed()
|
callback.onNotesDismissed()
|
||||||
@@ -41,10 +68,52 @@ class CheckmarkDialog
|
|||||||
.create()
|
.create()
|
||||||
|
|
||||||
dialog.setOnShowListener {
|
dialog.setOnShowListener {
|
||||||
etNotes.requestFocus()
|
binding.etNotes.requestFocus()
|
||||||
dialog.window?.setSoftInputMode(SOFT_INPUT_STATE_ALWAYS_VISIBLE)
|
dialog.window?.setSoftInputMode(SOFT_INPUT_STATE_ALWAYS_VISIBLE)
|
||||||
}
|
}
|
||||||
|
|
||||||
return dialog
|
return dialog
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun setUpButtons(value: Int, color: Int) {
|
||||||
|
val sres = StyledResources(context)
|
||||||
|
val mediumContrastColor = sres.getColor(R.attr.contrast60)
|
||||||
|
|
||||||
|
setButtonAttrs(binding.yesBtn, color)
|
||||||
|
setButtonAttrs(binding.noBtn, mediumContrastColor)
|
||||||
|
|
||||||
|
if (preferences.isSkipEnabled) {
|
||||||
|
setButtonAttrs(binding.skippedBtn, color)
|
||||||
|
if (value == SKIP) binding.skippedBtn.performClick()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preferences.areQuestionMarksEnabled) {
|
||||||
|
setButtonAttrs(binding.questionBtn, mediumContrastColor)
|
||||||
|
if (value == UNKNOWN) binding.questionBtn.performClick()
|
||||||
|
}
|
||||||
|
|
||||||
|
when (value) {
|
||||||
|
YES_MANUAL, YES_AUTO -> binding.yesBtn.performClick()
|
||||||
|
NO -> binding.noBtn.performClick()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setButtonAttrs(button: Button, color: Int) {
|
||||||
|
button.apply {
|
||||||
|
visibility = View.VISIBLE
|
||||||
|
typeface = fontAwesome
|
||||||
|
setTextColor(color)
|
||||||
|
setOnClickListener(this@CheckmarkDialog)
|
||||||
|
}
|
||||||
|
allButtons.add(button)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onClick(v: View?) {
|
||||||
|
allButtons.forEach {
|
||||||
|
if (v?.id == it.id) {
|
||||||
|
it.isSelected = true
|
||||||
|
selectedButton = it
|
||||||
|
} else it.isSelected = false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -235,10 +235,13 @@ class ListHabitsScreen
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun showCheckmarkDialog(
|
override fun showCheckmarkDialog(
|
||||||
|
value: Int,
|
||||||
notes: String,
|
notes: String,
|
||||||
|
dateString: String,
|
||||||
|
color: PaletteColor,
|
||||||
callback: ListHabitsBehavior.CheckMarkDialogCallback
|
callback: ListHabitsBehavior.CheckMarkDialogCallback
|
||||||
) {
|
) {
|
||||||
checkMarkDialog.create(notes, callback).show()
|
checkMarkDialog.create(value, notes, dateString, color, callback).show()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getExecuteString(command: Command): String? {
|
private fun getExecuteString(command: Command): String? {
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ import org.isoron.uhabits.activities.common.dialogs.NumberPickerFactory
|
|||||||
import org.isoron.uhabits.core.commands.Command
|
import org.isoron.uhabits.core.commands.Command
|
||||||
import org.isoron.uhabits.core.commands.CommandRunner
|
import org.isoron.uhabits.core.commands.CommandRunner
|
||||||
import org.isoron.uhabits.core.models.Habit
|
import org.isoron.uhabits.core.models.Habit
|
||||||
|
import org.isoron.uhabits.core.models.PaletteColor
|
||||||
import org.isoron.uhabits.core.preferences.Preferences
|
import org.isoron.uhabits.core.preferences.Preferences
|
||||||
import org.isoron.uhabits.core.ui.callbacks.OnConfirmedCallback
|
import org.isoron.uhabits.core.ui.callbacks.OnConfirmedCallback
|
||||||
import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsBehavior
|
import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsBehavior
|
||||||
@@ -173,10 +174,14 @@ class ShowHabitActivity : AppCompatActivity(), CommandRunner.Listener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun showCheckmarkDialog(
|
override fun showCheckmarkDialog(
|
||||||
|
value: Int,
|
||||||
notes: String,
|
notes: String,
|
||||||
|
dateString: String,
|
||||||
|
preferences: Preferences,
|
||||||
|
color: PaletteColor,
|
||||||
callback: ListHabitsBehavior.CheckMarkDialogCallback
|
callback: ListHabitsBehavior.CheckMarkDialogCallback
|
||||||
) {
|
) {
|
||||||
CheckmarkDialog(this@ShowHabitActivity).create(notes, callback).show()
|
CheckmarkDialog(this@ShowHabitActivity, preferences).create(value, notes, dateString, color, callback).show()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun showEditHabitScreen(habit: Habit) {
|
override fun showEditHabitScreen(habit: Habit) {
|
||||||
|
|||||||
14
uhabits-android/src/main/res/drawable/bg_select_button.xml
Normal file
14
uhabits-android/src/main/res/drawable/bg_select_button.xml
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:state_selected="true">
|
||||||
|
<shape>
|
||||||
|
<solid android:color="?attr/contrast40" />
|
||||||
|
<corners android:radius="4dp"/>
|
||||||
|
<padding
|
||||||
|
android:bottom="0dp"
|
||||||
|
android:left="8dp"
|
||||||
|
android:right="8dp"
|
||||||
|
android:top="0dp" />
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
</selector>
|
||||||
@@ -4,15 +4,92 @@
|
|||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:padding="30dp">
|
android:orientation="vertical"
|
||||||
|
android:paddingTop="12dp"
|
||||||
|
android:paddingStart="10dp"
|
||||||
|
android:paddingEnd="10dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:baselineAligned="false">
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
style="@style/FormOuterBox"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_weight="1">
|
||||||
|
<LinearLayout style="@style/DialogFormInnerBox">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/DialogFormLabel"
|
||||||
|
android:text="@string/value" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_horizontal"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginBottom="8dp">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/yesBtn"
|
||||||
|
android:text="@string/fa_check"
|
||||||
|
style="@style/CheckmarkDialogBtn"/>
|
||||||
|
<Button
|
||||||
|
android:id="@+id/skippedBtn"
|
||||||
|
android:text="@string/fa_skipped"
|
||||||
|
android:visibility="gone"
|
||||||
|
style="@style/CheckmarkDialogBtn"/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/noBtn"
|
||||||
|
android:text="@string/fa_times"
|
||||||
|
style="@style/CheckmarkDialogBtn"/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/questionBtn"
|
||||||
|
android:text="@string/fa_question"
|
||||||
|
android:visibility="gone"
|
||||||
|
style="@style/CheckmarkDialogBtn"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="5dp"
|
||||||
|
android:baselineAligned="false">
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
style="@style/FormOuterBox"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_weight="1">
|
||||||
|
|
||||||
|
<LinearLayout style="@style/DialogFormInnerBox">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/DialogFormLabel"
|
||||||
|
android:text="@string/notes" />
|
||||||
|
|
||||||
<EditText
|
<EditText
|
||||||
android:id="@+id/etNotes"
|
android:id="@+id/etNotes"
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:inputType="textCapSentences|textMultiLine"
|
android:inputType="textCapSentences|textMultiLine"
|
||||||
style="@style/TextAppearance.AppCompat.Body1"
|
style="@style/FormInput"
|
||||||
android:scrollbars="vertical"
|
android:scrollbars="vertical"
|
||||||
android:hint="@string/example_notes"/>
|
android:hint="@string/example_notes"/>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
@@ -232,5 +232,4 @@
|
|||||||
<string name="activity_not_found">No app was found to support this action</string>
|
<string name="activity_not_found">No app was found to support this action</string>
|
||||||
<string name="pref_midnight_delay_title">Extend day a few hours past midnight</string>
|
<string name="pref_midnight_delay_title">Extend day a few hours past midnight</string>
|
||||||
<string name="pref_midnight_delay_description">Wait until 3:00 AM to show a new day. Useful if you typically go to sleep after midnight. Requires app restart.</string>
|
<string name="pref_midnight_delay_description">Wait until 3:00 AM to show a new day. Useful if you typically go to sleep after midnight. Requires app restart.</string>
|
||||||
<string name="edit_notes">Edit notes</string>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -387,4 +387,16 @@
|
|||||||
<item name="android:textSize">@dimen/smallTextSize</item>
|
<item name="android:textSize">@dimen/smallTextSize</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="CheckmarkDialogBtn">
|
||||||
|
<item name="android:layout_width">48dp</item>
|
||||||
|
<item name="android:layout_height">48dp</item>
|
||||||
|
<item name="android:layout_marginTop">8dp</item>
|
||||||
|
<item name="android:layout_marginBottom">8dp</item>
|
||||||
|
<item name="android:layout_marginEnd">12dp</item>
|
||||||
|
<item name="android:textSize">@dimen/regularTextSize</item>
|
||||||
|
<item name="backgroundTint">@null</item>
|
||||||
|
<item name="android:background">@drawable/bg_select_button</item>
|
||||||
|
<item name="selectable">true</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import org.isoron.uhabits.core.commands.CreateRepetitionCommand
|
|||||||
import org.isoron.uhabits.core.models.Habit
|
import org.isoron.uhabits.core.models.Habit
|
||||||
import org.isoron.uhabits.core.models.HabitList
|
import org.isoron.uhabits.core.models.HabitList
|
||||||
import org.isoron.uhabits.core.models.HabitType
|
import org.isoron.uhabits.core.models.HabitType
|
||||||
|
import org.isoron.uhabits.core.models.PaletteColor
|
||||||
import org.isoron.uhabits.core.models.Timestamp
|
import org.isoron.uhabits.core.models.Timestamp
|
||||||
import org.isoron.uhabits.core.preferences.Preferences
|
import org.isoron.uhabits.core.preferences.Preferences
|
||||||
import org.isoron.uhabits.core.tasks.ExportCSVTask
|
import org.isoron.uhabits.core.tasks.ExportCSVTask
|
||||||
@@ -62,9 +63,12 @@ open class ListHabitsBehavior @Inject constructor(
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
screen.showCheckmarkDialog(
|
screen.showCheckmarkDialog(
|
||||||
entry.notes
|
entry.value,
|
||||||
) { newNotes ->
|
entry.notes,
|
||||||
commandRunner.run(CreateRepetitionCommand(habitList, habit, timestamp, entry.value, newNotes))
|
timestamp.toDialogDateString(),
|
||||||
|
habit.color,
|
||||||
|
) { newValue, newNotes ->
|
||||||
|
commandRunner.run(CreateRepetitionCommand(habitList, habit, timestamp, newValue, newNotes))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -148,7 +152,7 @@ open class ListHabitsBehavior @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun interface CheckMarkDialogCallback {
|
fun interface CheckMarkDialogCallback {
|
||||||
fun onNotesSaved(notes: String)
|
fun onNotesSaved(value: Int, notes: String)
|
||||||
fun onNotesDismissed() {}
|
fun onNotesDismissed() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,7 +168,10 @@ open class ListHabitsBehavior @Inject constructor(
|
|||||||
callback: NumberPickerCallback
|
callback: NumberPickerCallback
|
||||||
)
|
)
|
||||||
fun showCheckmarkDialog(
|
fun showCheckmarkDialog(
|
||||||
|
value: Int,
|
||||||
notes: String,
|
notes: String,
|
||||||
|
dateString: String,
|
||||||
|
color: PaletteColor,
|
||||||
callback: CheckMarkDialogCallback
|
callback: CheckMarkDialogCallback
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -90,13 +90,19 @@ class HistoryCardPresenter(
|
|||||||
showNumberPicker(timestamp)
|
showNumberPicker(timestamp)
|
||||||
} else {
|
} else {
|
||||||
val entry = habit.computedEntries.get(timestamp)
|
val entry = habit.computedEntries.get(timestamp)
|
||||||
screen.showCheckmarkDialog(entry.notes) { newNotes ->
|
screen.showCheckmarkDialog(
|
||||||
|
entry.value,
|
||||||
|
entry.notes,
|
||||||
|
timestamp.toDialogDateString(),
|
||||||
|
preferences,
|
||||||
|
habit.color,
|
||||||
|
) { newValue, newNotes ->
|
||||||
commandRunner.run(
|
commandRunner.run(
|
||||||
CreateRepetitionCommand(
|
CreateRepetitionCommand(
|
||||||
habitList,
|
habitList,
|
||||||
habit,
|
habit,
|
||||||
timestamp,
|
timestamp,
|
||||||
entry.value,
|
newValue,
|
||||||
newNotes,
|
newNotes,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -200,7 +206,11 @@ class HistoryCardPresenter(
|
|||||||
callback: ListHabitsBehavior.NumberPickerCallback,
|
callback: ListHabitsBehavior.NumberPickerCallback,
|
||||||
)
|
)
|
||||||
fun showCheckmarkDialog(
|
fun showCheckmarkDialog(
|
||||||
|
value: Int,
|
||||||
notes: String,
|
notes: String,
|
||||||
|
dateString: String,
|
||||||
|
preferences: Preferences,
|
||||||
|
color: PaletteColor,
|
||||||
callback: ListHabitsBehavior.CheckMarkDialogCallback,
|
callback: ListHabitsBehavior.CheckMarkDialogCallback,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user