Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 30 additions & 30 deletions TRANSLATIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,34 +19,34 @@ See [Android Translations Converter](https://github.com/Crustack/android-transla
<!-- translations:start -->
| Language | Coverage |
|----------|----------|
| 🇺🇸 English | 100% (347/347) |
| 🇪🇸 Catalan | 18% (65/347) |
| 🇨🇿 Czech | 96% (336/347) |
| 🇩🇰 Danish | 19% (69/347) |
| 🇩🇪 German | 96% (336/347) |
| 🇬🇷 Greek | 20% (72/347) |
| 🇪🇸 Spanish | 90% (315/347) |
| 🇫🇷 French | 94% (328/347) |
| 🇭🇺 Hungarian | 18% (65/347) |
| 🇮🇩 Indonesian | 21% (75/347) |
| 🇮🇹 Italian | 84% (292/347) |
| 🇯🇵 Japanese | 21% (73/347) |
| 🇲🇲 Burmese | 26% (91/347) |
| 🇳🇴 Norwegian Bokmål | 30% (107/347) |
| 🇳🇱 Dutch | 61% (213/347) |
| 🇳🇴 Norwegian Nynorsk | 30% (107/347) |
| 🇵🇱 Polish | 86% (301/347) |
| 🇧🇷 Portuguese (Brazil) | 90% (313/347) |
| 🇵🇹 Portuguese (Portugal) | 20% (71/347) |
| 🇷🇴 Romanian | 87% (302/347) |
| 🇷🇺 Russian | 88% (306/347) |
| 🇸🇰 Slovak | 18% (65/347) |
| 🇸🇮 Slovenian | 31% (110/347) |
| 🇸🇪 Swedish | 18% (63/347) |
| 🇵🇭 Tagalog | 18% (65/347) |
| 🇹🇷 Turkish | 21% (73/347) |
| 🇺🇦 Ukrainian | 95% (332/347) |
| 🇻🇳 Vietnamese | 31% (108/347) |
| 🇨🇳 Chinese (Simplified) | 98% (341/347) |
| 🇹🇼 Chinese (Traditional) | 85% (295/347) |
| 🇺🇸 English | 100% (353/353) |
| 🇪🇸 Catalan | 18% (65/353) |
| 🇨🇿 Czech | 94% (335/353) |
| 🇩🇰 Danish | 19% (69/353) |
| 🇩🇪 German | 94% (335/353) |
| 🇬🇷 Greek | 20% (72/353) |
| 🇪🇸 Spanish | 88% (314/353) |
| 🇫🇷 French | 92% (327/353) |
| 🇭🇺 Hungarian | 18% (65/353) |
| 🇮🇩 Indonesian | 21% (75/353) |
| 🇮🇹 Italian | 82% (291/353) |
| 🇯🇵 Japanese | 20% (73/353) |
| 🇲🇲 Burmese | 25% (91/353) |
| 🇳🇴 Norwegian Bokmål | 30% (107/353) |
| 🇳🇱 Dutch | 60% (213/353) |
| 🇳🇴 Norwegian Nynorsk | 30% (107/353) |
| 🇵🇱 Polish | 84% (300/353) |
| 🇧🇷 Portuguese (Brazil) | 88% (312/353) |
| 🇵🇹 Portuguese (Portugal) | 20% (71/353) |
| 🇷🇴 Romanian | 85% (301/353) |
| 🇷🇺 Russian | 86% (305/353) |
| 🇸🇰 Slovak | 18% (65/353) |
| 🇸🇮 Slovenian | 31% (110/353) |
| 🇸🇪 Swedish | 17% (63/353) |
| 🇵🇭 Tagalog | 18% (65/353) |
| 🇹🇷 Turkish | 20% (73/353) |
| 🇺🇦 Ukrainian | 93% (331/353) |
| 🇻🇳 Vietnamese | 30% (108/353) |
| 🇨🇳 Chinese (Simplified) | 96% (340/353) |
| 🇹🇼 Chinese (Traditional) | 83% (294/353) |
<!-- translations:end -->
73 changes: 44 additions & 29 deletions app/src/main/java/com/philkes/notallyx/presentation/UiExtensions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -110,18 +110,16 @@ import com.philkes.notallyx.presentation.view.note.listitem.ListManager
import com.philkes.notallyx.presentation.view.note.listitem.adapter.ListItemVH
import com.philkes.notallyx.presentation.viewmodel.BaseNoteModel
import com.philkes.notallyx.presentation.viewmodel.preference.DateFormat
import com.philkes.notallyx.presentation.viewmodel.preference.TimeFormat
import com.philkes.notallyx.presentation.viewmodel.preference.displayBodySize
import com.philkes.notallyx.utils.changehistory.ChangeHistory
import com.philkes.notallyx.utils.changehistory.EditTextState
import com.philkes.notallyx.utils.changehistory.EditTextWithHistoryChange
import com.philkes.notallyx.utils.getUrl
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import me.zhanghai.android.fastscroll.FastScrollNestedScrollView
import me.zhanghai.android.fastscroll.FastScrollerBuilder
import me.zhanghai.android.fastscroll.PopupStyles
import org.ocpsoft.prettytime.PrettyTime

/**
* For some reason, this method crashes sometimes with an IndexOutOfBoundsException that I've not
Expand Down Expand Up @@ -290,12 +288,14 @@ fun ViewGroup.addIconButton(
fun TextView.displayFormattedTimestamp(
timestamp: Long?,
dateFormat: DateFormat,
timeFormat: TimeFormat,
prefixResId: Int? = null,
) {
if (dateFormat != DateFormat.NONE && timestamp != null) {
if ((dateFormat != DateFormat.NONE || timeFormat != TimeFormat.NONE) && timestamp != null) {
visibility = View.VISIBLE
text =
"${prefixResId?.let { getString(it) } ?: ""} ${formatTimestamp(timestamp, dateFormat)}"
"${prefixResId?.let { getString(it) } ?: ""} ${Date(timestamp).format(dateFormat, timeFormat)}"
.trim()
} else visibility = View.GONE
}

Expand Down Expand Up @@ -456,6 +456,17 @@ fun <T, C> NotNullLiveData<T>.merge(liveData: NotNullLiveData<C>): MediatorLiveD
}
}

fun <T, C, B> NotNullLiveData<T>.merge(
liveData: NotNullLiveData<C>,
liveData2: NotNullLiveData<B>,
): MediatorLiveData<Triple<T, C, B>> {
return MediatorLiveData<Triple<T, C, B>>().apply {
addSource(this@merge) { value1 -> value = Triple(value1, liveData.value, liveData2.value) }
addSource(liveData) { value2 -> value = Triple(this@merge.value, value2, liveData2.value) }
addSource(liveData2) { value3 -> value = Triple(this@merge.value, liveData.value, value3) }
}
}

fun <T, C> NotNullLiveData<T>.merge(liveData: LiveData<C>): MediatorLiveData<Pair<T, C?>> {
return MediatorLiveData<Pair<T, C?>>().apply {
addSource(this@merge) { value1 -> value = Pair(value1, liveData.value) }
Expand Down Expand Up @@ -612,29 +623,28 @@ fun Context.displayEditLabelDialog(
}
}

private fun formatTimestamp(timestamp: Long, dateFormat: DateFormat): String {
return Date(timestamp).format(dateFormat)
}
fun Date.format(
dateFormat: DateFormat = DateFormat.DD_MM_YY_GER,
timeFormat: TimeFormat = TimeFormat.TWENTY_FOUR_H,
ensureFullFormat: Boolean = false,
): String {
val (effectiveDateFormat, effectiveTimeFormat) =
if (ensureFullFormat && dateFormat != DateFormat.RELATIVE) {
Pair(
dateFormat.takeIf { it != DateFormat.NONE } ?: DateFormat.DD_MM_YY_GER,
timeFormat.takeIf { it != TimeFormat.NONE } ?: TimeFormat.TWENTY_FOUR_H,
)
} else Pair(dateFormat, timeFormat)
if (effectiveDateFormat == DateFormat.NONE && effectiveTimeFormat == TimeFormat.NONE) {
return ""
}
val datePart = effectiveDateFormat.format(this)
val timePart = effectiveTimeFormat.format(this)

private val ISO_DATE_FORMAT = SimpleDateFormat("yyyy-MM-dd", Locale.US)

fun Date.format(dateFormat: DateFormat = DateFormat.TIMESTAMP_SHORT): String {
return when (dateFormat) {
DateFormat.NONE -> ""
DateFormat.RELATIVE -> PrettyTime().format(this)
DateFormat.ABSOLUTE ->
java.text.DateFormat.getDateInstance(java.text.DateFormat.FULL).format(this)
DateFormat.ABSOLUTE_SHORT ->
java.text.DateFormat.getDateInstance(java.text.DateFormat.SHORT).format(this)
DateFormat.SHORT_ISO -> {
ISO_DATE_FORMAT.format(this)
}
DateFormat.TIMESTAMP_SHORT ->
java.text.DateFormat.getDateTimeInstance(
java.text.DateFormat.SHORT,
java.text.DateFormat.SHORT,
)
.format(this)
return if (datePart.isNotEmpty() && timePart.isNotEmpty()) {
"$datePart $timePart"
} else {
datePart + timePart
}
}

Expand Down Expand Up @@ -1142,7 +1152,12 @@ fun Context.createTextView(textResId: Int, padding: Int = 16.dp): TextView {
}
}

fun Chip.setupReminderChip(baseNote: BaseNote, textSize: Float? = null) {
fun Chip.setupReminderChip(
baseNote: BaseNote,
dateFormat: DateFormat,
timeFormat: TimeFormat,
textSize: Float? = null,
) {
val now = Date(System.currentTimeMillis())
val mostRecentNotificationDate =
baseNote.reminders.findNextNotificationDate()
Expand All @@ -1153,7 +1168,7 @@ fun Chip.setupReminderChip(baseNote: BaseNote, textSize: Float? = null) {
}
this.apply {
visibility = VISIBLE
text = mostRecentNotificationDate.format()
text = mostRecentNotificationDate.format(dateFormat, timeFormat, ensureFullFormat = true)
textSize?.let {
setTextSizeSp(it)
chipIconSize =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,8 @@ abstract class NotallyFragment : Fragment(), ItemListener {
with(model.preferences) {
BaseNoteAdapter(
model.actionMode.selectedIds,
dateFormat.value,
dateFormatOverview.value,
timeFormatOverview.value,
notesAdapterSortCallback(),
BaseNoteVHPreferences(
textSizeOverview.value,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import com.google.android.material.slider.Slider
import com.google.android.material.textfield.TextInputLayout.END_ICON_PASSWORD_TOGGLE
import com.philkes.notallyx.R
import com.philkes.notallyx.databinding.ChoiceItemBinding
import com.philkes.notallyx.databinding.DialogDatetimeFormatBinding
import com.philkes.notallyx.databinding.DialogNotesSortBinding
import com.philkes.notallyx.databinding.DialogPreferenceBooleanBinding
import com.philkes.notallyx.databinding.DialogPreferenceEnumWithToggleBinding
Expand Down Expand Up @@ -47,6 +48,7 @@ import com.philkes.notallyx.presentation.viewmodel.preference.SortDirection
import com.philkes.notallyx.presentation.viewmodel.preference.StringPreference
import com.philkes.notallyx.presentation.viewmodel.preference.TextProvider
import com.philkes.notallyx.presentation.viewmodel.preference.Theme
import com.philkes.notallyx.presentation.viewmodel.preference.TimeFormat
import com.philkes.notallyx.utils.canAuthenticateWithBiometrics
import com.philkes.notallyx.utils.toReadablePath

Expand Down Expand Up @@ -191,55 +193,6 @@ fun PreferenceBinding.setup(
}
}

fun PreferenceBinding.setup(
dateFormatPreference: EnumPreference<DateFormat>,
dateFormatValue: DateFormat,
applyToNoteViewValue: Boolean,
context: Context,
layoutInflater: LayoutInflater,
onSave: (dateFormat: DateFormat, applyToEditMode: Boolean) -> Unit,
) {
Title.setText(dateFormatPreference.titleResId!!)

Value.text = dateFormatValue.getText(context)

root.setOnClickListener {
val layout = DialogPreferenceEnumWithToggleBinding.inflate(layoutInflater, null, false)
layout.EnumHint.apply {
setText(R.string.date_format_hint)
isVisible = true
}
DateFormat.entries.forEachIndexed { idx, dateFormat ->
ChoiceItemBinding.inflate(layoutInflater).root.apply {
id = idx
text = dateFormat.getText(context)
tag = dateFormat
layout.EnumRadioGroup.addView(this)
if (dateFormat == dateFormatValue) {
layout.EnumRadioGroup.check(this.id)
}
}
}

layout.Toggle.apply {
setText(R.string.date_format_apply_in_note_view)
isChecked = applyToNoteViewValue
}

MaterialAlertDialogBuilder(context)
.setTitle(dateFormatPreference.titleResId)
.setView(layout.root)
.setPositiveButton(R.string.save) { dialog, _ ->
dialog.cancel()
val dateFormat = layout.EnumRadioGroup.checkedTag() as DateFormat
val applyToNoteView = layout.Toggle.isChecked
onSave(dateFormat, applyToNoteView)
}
.setCancelButton()
.show()
}
}

fun PreferenceBinding.setup(
themePreference: EnumPreference<Theme>,
themeValue: Theme,
Expand Down Expand Up @@ -634,3 +587,67 @@ fun PreferenceBinding.setupStartView(
.showAndFocus(allowFullSize = true)
}
}

fun PreferenceBinding.setupDateTimeFormat(
titleResId: Int,
datePreference: EnumPreference<DateFormat>,
timePreference: EnumPreference<TimeFormat>,
context: Context,
layoutInflater: LayoutInflater,
onSave: (dateFormat: DateFormat, timeFormat: TimeFormat) -> Unit,
) {
Title.setText(titleResId)
val updateValueText = {
val dateText =
if (datePreference.value == DateFormat.NONE) ""
else datePreference.value.getText(context)
val timeText =
if (timePreference.value == TimeFormat.NONE) ""
else timePreference.value.getText(context)
Value.text =
when {
datePreference.value == DateFormat.NONE &&
timePreference.value == TimeFormat.NONE ->
datePreference.value.getText(context) // Shows "None"
datePreference.value == DateFormat.NONE -> timeText
timePreference.value == TimeFormat.NONE -> dateText
else -> "$dateText $timeText"
}
}
updateValueText()

root.setOnClickListener {
val layout = DialogDatetimeFormatBinding.inflate(layoutInflater, null, false)
var selectedDate = datePreference.value
var selectedTime = timePreference.value

val dateEntries = DateFormat.entries.map { it.getText(context) }.toTypedArray()
layout.DateSelectionBox.apply {
setSimpleItems(dateEntries)
select(selectedDate.getText(context))
setOnItemClickListener { _, _, position, _ ->
selectedDate = DateFormat.entries[position]
}
}

val timeEntries = TimeFormat.entries.map { it.getText(context) }.toTypedArray()
layout.TimeSelectionBox.apply {
setSimpleItems(timeEntries)
select(selectedTime.getText(context))
setOnItemClickListener { _, _, position, _ ->
selectedTime = TimeFormat.entries[position]
}
}

MaterialAlertDialogBuilder(context)
.setTitle(titleResId)
.setView(layout.root)
.setPositiveButton(R.string.save) { dialog, _ ->
dialog.cancel()
onSave(selectedDate, selectedTime)
updateValueText()
}
.setCancelButton()
.showAndFocus(allowFullSize = true)
}
}
Loading