Add configurable timestamp date and clock formats
This commit is contained in:
@@ -4,12 +4,18 @@ import android.content.ClipData
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.Spinner
|
||||
import android.widget.TextView
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.google.android.material.switchmaterial.SwitchMaterial
|
||||
import se.ajpanton.notificationlog.databinding.FragmentLogDisplayBinding
|
||||
import se.ajpanton.notificationlog.settings.DisplayEvent
|
||||
import se.ajpanton.notificationlog.settings.LogField
|
||||
import se.ajpanton.notificationlog.settings.LogViewSettings
|
||||
import se.ajpanton.notificationlog.settings.LogViewSettingsStore
|
||||
import se.ajpanton.notificationlog.settings.TimestampClockFormat
|
||||
import se.ajpanton.notificationlog.settings.TimestampDateFormat
|
||||
import se.ajpanton.notificationlog.settings.TimestampZone
|
||||
|
||||
class LogDisplayFragment : Fragment(R.layout.fragment_log_display) {
|
||||
@@ -35,15 +41,18 @@ class LogDisplayFragment : Fragment(R.layout.fragment_log_display) {
|
||||
})
|
||||
}
|
||||
settings.order.forEach { field ->
|
||||
binding!!.fieldToggles.addView(SwitchMaterial(requireContext()).apply {
|
||||
val fieldGroup = LinearLayout(requireContext()).apply {
|
||||
orientation = LinearLayout.VERTICAL
|
||||
tag = field
|
||||
}
|
||||
val toggle = SwitchMaterial(requireContext()).apply {
|
||||
text = field.label()
|
||||
isChecked = field in settings.visibleFields
|
||||
tag = field
|
||||
setOnLongClickListener {
|
||||
startDragAndDrop(
|
||||
ClipData.newPlainText("field", field.name),
|
||||
View.DragShadowBuilder(this),
|
||||
this,
|
||||
View.DragShadowBuilder(fieldGroup),
|
||||
fieldGroup,
|
||||
0,
|
||||
)
|
||||
true
|
||||
@@ -56,7 +65,26 @@ class LogDisplayFragment : Fragment(R.layout.fragment_log_display) {
|
||||
)
|
||||
store.save(settings)
|
||||
}
|
||||
})
|
||||
}
|
||||
fieldGroup.addView(toggle)
|
||||
if (field == LogField.TIMESTAMP) {
|
||||
val controls = timestampControls({ settings }) { updated ->
|
||||
settings = updated
|
||||
store.save(settings)
|
||||
}
|
||||
fieldGroup.addView(controls)
|
||||
setEnabledRecursively(controls, toggle.isChecked)
|
||||
toggle.setOnCheckedChangeListener { _, checked ->
|
||||
settings = settings.copy(
|
||||
visibleFields = settings.visibleFields.toMutableSet().apply {
|
||||
if (checked) add(field) else remove(field)
|
||||
},
|
||||
)
|
||||
store.save(settings)
|
||||
setEnabledRecursively(controls, checked)
|
||||
}
|
||||
}
|
||||
binding!!.fieldToggles.addView(fieldGroup)
|
||||
}
|
||||
binding!!.fieldToggles.setOnDragListener { _, event ->
|
||||
if (event.action != android.view.DragEvent.ACTION_DROP) return@setOnDragListener true
|
||||
@@ -69,29 +97,57 @@ class LogDisplayFragment : Fragment(R.layout.fragment_log_display) {
|
||||
store.save(settings)
|
||||
true
|
||||
}
|
||||
binding!!.timezone.adapter = ArrayAdapter(
|
||||
requireContext(),
|
||||
android.R.layout.simple_spinner_dropdown_item,
|
||||
listOf("Current local timezone", "UTC", "Local timezone when event happened"),
|
||||
)
|
||||
binding!!.timezone.setSelection(TimestampZone.entries.indexOf(settings.timestampZone))
|
||||
binding!!.timezone.onItemSelectedListener = object : android.widget.AdapterView.OnItemSelectedListener {
|
||||
override fun onNothingSelected(parent: android.widget.AdapterView<*>?) = Unit
|
||||
|
||||
override fun onItemSelected(
|
||||
parent: android.widget.AdapterView<*>?,
|
||||
view: View?,
|
||||
position: Int,
|
||||
id: Long,
|
||||
) {
|
||||
settings = settings.copy(timestampZone = TimestampZone.entries[position])
|
||||
store.save(settings)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() { binding = null; super.onDestroyView() }
|
||||
|
||||
private fun timestampControls(
|
||||
currentSettings: () -> LogViewSettings,
|
||||
saveSettings: (LogViewSettings) -> Unit,
|
||||
): LinearLayout = LinearLayout(requireContext()).apply {
|
||||
orientation = LinearLayout.VERTICAL
|
||||
setPadding(dp(TIMESTAMP_CONTROLS_INDENT_DP), 0, 0, 0)
|
||||
addDropdown("Timestamp timezone", listOf("Current local timezone", "UTC", "Local timezone when event happened"), TimestampZone.entries.indexOf(currentSettings().timestampZone)) { position ->
|
||||
val updated = currentSettings().copy(timestampZone = TimestampZone.entries[position])
|
||||
saveSettings(updated)
|
||||
}
|
||||
addDropdown("Date format", listOf("System default", "YYYY-MM-DD", "DD-MM-YYYY", "MM-DD-YYYY"), TimestampDateFormat.entries.indexOf(currentSettings().timestampDateFormat)) { position ->
|
||||
val updated = currentSettings().copy(timestampDateFormat = TimestampDateFormat.entries[position])
|
||||
saveSettings(updated)
|
||||
}
|
||||
addDropdown("Clock format", listOf("System default", "24-hour clock", "12-hour clock"), TimestampClockFormat.entries.indexOf(currentSettings().timestampClockFormat)) { position ->
|
||||
val updated = currentSettings().copy(timestampClockFormat = TimestampClockFormat.entries[position])
|
||||
saveSettings(updated)
|
||||
}
|
||||
}
|
||||
|
||||
private fun LinearLayout.addDropdown(label: String, values: List<String>, selected: Int, onSelected: (Int) -> Unit) {
|
||||
addView(TextView(context).apply { text = label })
|
||||
addView(Spinner(context).apply {
|
||||
adapter = ArrayAdapter(context, android.R.layout.simple_spinner_dropdown_item, values)
|
||||
setSelection(selected)
|
||||
onItemSelectedListener = object : android.widget.AdapterView.OnItemSelectedListener {
|
||||
override fun onNothingSelected(parent: android.widget.AdapterView<*>?) = Unit
|
||||
override fun onItemSelected(parent: android.widget.AdapterView<*>?, view: View?, position: Int, id: Long) = onSelected(position)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun setEnabledRecursively(view: View, enabled: Boolean) {
|
||||
view.isEnabled = enabled
|
||||
view.alpha = if (enabled) 1f else DISABLED_TIMESTAMP_CONTROLS_ALPHA
|
||||
if (view is android.view.ViewGroup) {
|
||||
(0 until view.childCount).forEach { setEnabledRecursively(view.getChildAt(it), enabled) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun dp(value: Int): Int = (value * resources.displayMetrics.density).toInt()
|
||||
|
||||
private companion object {
|
||||
const val TIMESTAMP_CONTROLS_INDENT_DP = 24
|
||||
const val DISABLED_TIMESTAMP_CONTROLS_ALPHA = 0.5f
|
||||
}
|
||||
|
||||
private fun LogField.label() = when (this) {
|
||||
LogField.TIMESTAMP -> "Timestamp"
|
||||
LogField.APP_NAME -> "App name"
|
||||
|
||||
@@ -103,8 +103,8 @@ class SettingsFragment : Fragment(R.layout.fragment_settings) {
|
||||
val settings = LogViewSettingsStore(context).load()
|
||||
context.contentResolver.openOutputStream(uri)?.use { output ->
|
||||
when (format) {
|
||||
ExportFormat.CSV -> writeCsv(output.bufferedWriter(Charsets.UTF_8), logStore, settings)
|
||||
ExportFormat.FORMATTED -> writeFormatted(output.bufferedWriter(Charsets.UTF_8), logStore, settings)
|
||||
ExportFormat.CSV -> writeCsv(output.bufferedWriter(Charsets.UTF_8), logStore, settings, context)
|
||||
ExportFormat.FORMATTED -> writeFormatted(output.bufferedWriter(Charsets.UTF_8), logStore, settings, context)
|
||||
ExportFormat.HTML_ZIP -> writeHtmlZip(ZipOutputStream(output), logStore, settings, context)
|
||||
}
|
||||
}
|
||||
@@ -115,11 +115,12 @@ class SettingsFragment : Fragment(R.layout.fragment_settings) {
|
||||
writer: java.io.BufferedWriter,
|
||||
logStore: EncryptedNotificationLogStore,
|
||||
settings: se.ajpanton.notificationlog.settings.LogViewSettings,
|
||||
context: android.content.Context,
|
||||
) = writer.use {
|
||||
it.write(LogExporter.csvHeader(settings))
|
||||
logStore.forEachNewest { entry ->
|
||||
it.newLine()
|
||||
it.write(LogExporter.csvRow(entry, settings))
|
||||
it.write(LogExporter.csvRow(entry, settings, context))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,18 +128,19 @@ class SettingsFragment : Fragment(R.layout.fragment_settings) {
|
||||
writer: java.io.BufferedWriter,
|
||||
logStore: EncryptedNotificationLogStore,
|
||||
settings: se.ajpanton.notificationlog.settings.LogViewSettings,
|
||||
context: android.content.Context,
|
||||
) = writer.use {
|
||||
val headers = LogExporter.headers(settings)
|
||||
val widths = headers.map { header -> header.length }.toMutableList()
|
||||
logStore.forEachNewest { entry ->
|
||||
LogExporter.values(entry, settings).forEachIndexed { index, value ->
|
||||
LogExporter.values(entry, settings, context).forEachIndexed { index, value ->
|
||||
widths[index] = maxOf(widths[index], value.length)
|
||||
}
|
||||
}
|
||||
it.write(LogExporter.formattedRow(headers, widths))
|
||||
logStore.forEachNewest { entry ->
|
||||
it.newLine()
|
||||
it.write(LogExporter.formattedRow(LogExporter.values(entry, settings), widths))
|
||||
it.write(LogExporter.formattedRow(LogExporter.values(entry, settings, context), widths))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,7 +157,7 @@ class SettingsFragment : Fragment(R.layout.fragment_settings) {
|
||||
val imagePath = entry.imageId
|
||||
?.takeIf { LogField.CONTENTS in settings.visibleFields && imageStore.exists(it) }
|
||||
?.let { imageId -> "images/$imageId.png" }
|
||||
zipOutput.write(LogExporter.htmlRow(entry, settings, imagePath).encodeToByteArray())
|
||||
zipOutput.write(LogExporter.htmlRow(entry, settings, imagePath, context).encodeToByteArray())
|
||||
}
|
||||
zipOutput.write(LogExporter.htmlEnd().encodeToByteArray())
|
||||
zipOutput.closeEntry()
|
||||
|
||||
@@ -25,11 +25,9 @@ import se.ajpanton.notificationlog.model.NotificationLogEntry
|
||||
import se.ajpanton.notificationlog.settings.LogField
|
||||
import se.ajpanton.notificationlog.settings.LogViewSettings
|
||||
import se.ajpanton.notificationlog.settings.LogViewSettingsStore
|
||||
import se.ajpanton.notificationlog.settings.TimestampZone
|
||||
import se.ajpanton.notificationlog.settings.TimestampFormatter
|
||||
import se.ajpanton.notificationlog.settings.DisplayEvent
|
||||
import se.ajpanton.notificationlog.capture.NotificationCaptureService
|
||||
import java.text.DateFormat
|
||||
import java.util.Date
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.children
|
||||
@@ -257,14 +255,14 @@ class ViewLogsFragment : Fragment(R.layout.fragment_view_logs) {
|
||||
}
|
||||
|
||||
private fun metadataValues(entry: NotificationLogEntry, settings: LogViewSettings): List<MetadataValue> = buildList {
|
||||
if (LogField.TIMESTAMP in settings.visibleFields) add(MetadataValue(timestamp(entry.recordedAtEpochMillis, settings.timestampZone, entry.eventTimeZoneId), 12f, isTimestamp = true))
|
||||
if (LogField.TIMESTAMP in settings.visibleFields) add(MetadataValue(timestamp(entry.recordedAtEpochMillis, settings, entry.eventTimeZoneId), 12f, isTimestamp = true))
|
||||
if (LogField.APP_NAME in settings.visibleFields) add(MetadataValue(entry.appName, 15f, indented = true, bold = true))
|
||||
if (LogField.PACKAGE_NAME in settings.visibleFields) add(MetadataValue(entry.packageName, 12f, indented = true, breakBeforePeriods = true))
|
||||
if (LogField.ACTION in settings.visibleFields) add(MetadataValue(actionLabel(entry.action), 14f, indented = true))
|
||||
}
|
||||
|
||||
private fun fieldValue(field: LogField, entry: NotificationLogEntry, settings: LogViewSettings): String? = when (field) {
|
||||
LogField.TIMESTAMP -> timestamp(entry.recordedAtEpochMillis, settings.timestampZone, entry.eventTimeZoneId)
|
||||
LogField.TIMESTAMP -> timestamp(entry.recordedAtEpochMillis, settings, entry.eventTimeZoneId)
|
||||
LogField.APP_NAME -> entry.appName
|
||||
LogField.PACKAGE_NAME -> entry.packageName
|
||||
LogField.ACTION -> actionLabel(entry.action)
|
||||
@@ -328,13 +326,8 @@ class ViewLogsFragment : Fragment(R.layout.fragment_view_logs) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun timestamp(value: Long, zone: TimestampZone, eventTimeZoneId: String?): String = DateFormat.getDateTimeInstance().apply {
|
||||
timeZone = when (zone) {
|
||||
TimestampZone.UTC -> java.util.TimeZone.getTimeZone("UTC")
|
||||
TimestampZone.LOCAL_NOW -> java.util.TimeZone.getDefault()
|
||||
TimestampZone.EVENT_LOCAL -> eventTimeZoneId?.let(java.util.TimeZone::getTimeZone) ?: java.util.TimeZone.getDefault()
|
||||
}
|
||||
}.format(Date(value))
|
||||
private fun timestamp(value: Long, settings: LogViewSettings, eventTimeZoneId: String?): String =
|
||||
TimestampFormatter.format(requireContext(), value, settings, eventTimeZoneId)
|
||||
|
||||
private fun showDeleteDialog(entryId: String): Boolean {
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
|
||||
@@ -1,28 +1,26 @@
|
||||
package se.ajpanton.notificationlog.export
|
||||
|
||||
import android.content.Context
|
||||
import se.ajpanton.notificationlog.model.NotificationLogEntry
|
||||
import se.ajpanton.notificationlog.settings.LogField
|
||||
import se.ajpanton.notificationlog.settings.LogViewSettings
|
||||
import se.ajpanton.notificationlog.settings.TimestampZone
|
||||
import java.text.DateFormat
|
||||
import java.util.Date
|
||||
import java.util.TimeZone
|
||||
import se.ajpanton.notificationlog.settings.TimestampFormatter
|
||||
|
||||
object LogExporter {
|
||||
fun csv(entries: List<NotificationLogEntry>, settings: LogViewSettings): String = (
|
||||
listOf(csvHeader(settings)) + entries.sortedByDescending { it.recordedAtEpochMillis }.map { csvRow(it, settings) }
|
||||
fun csv(entries: List<NotificationLogEntry>, settings: LogViewSettings, context: Context? = null): String = (
|
||||
listOf(csvHeader(settings)) + entries.sortedByDescending { it.recordedAtEpochMillis }.map { csvRow(it, settings, context) }
|
||||
).joinToString("\n")
|
||||
|
||||
fun formatted(entries: List<NotificationLogEntry>, settings: LogViewSettings): String {
|
||||
val rows = listOf(headers(settings)) + entries.sortedByDescending { it.recordedAtEpochMillis }.map { values(it, settings) }
|
||||
fun formatted(entries: List<NotificationLogEntry>, settings: LogViewSettings, context: Context? = null): String {
|
||||
val rows = listOf(headers(settings)) + entries.sortedByDescending { it.recordedAtEpochMillis }.map { values(it, settings, context) }
|
||||
val widths = rows.fold(emptyList<Int>()) { current, row -> row.mapIndexed { i, value -> maxOf(current.getOrElse(i) { 0 }, value.length) } }
|
||||
return rows.joinToString("\n") { formattedRow(it, widths) }
|
||||
}
|
||||
|
||||
fun headers(settings: LogViewSettings): List<String> = fields(settings).map(::header)
|
||||
|
||||
fun values(entry: NotificationLogEntry, settings: LogViewSettings): List<String> = fields(settings).map { field -> when (field) {
|
||||
LogField.TIMESTAMP -> timestamp(entry.recordedAtEpochMillis, settings.timestampZone, entry.eventTimeZoneId)
|
||||
fun values(entry: NotificationLogEntry, settings: LogViewSettings, context: Context? = null): List<String> = fields(settings).map { field -> when (field) {
|
||||
LogField.TIMESTAMP -> timestamp(context, entry.recordedAtEpochMillis, settings, entry.eventTimeZoneId)
|
||||
LogField.APP_NAME -> entry.appName
|
||||
LogField.PACKAGE_NAME -> entry.packageName
|
||||
LogField.ACTION -> entry.action.name.lowercase().replace('_', ' ')
|
||||
@@ -31,7 +29,7 @@ object LogExporter {
|
||||
|
||||
fun csvHeader(settings: LogViewSettings): String = csvLine(headers(settings))
|
||||
|
||||
fun csvRow(entry: NotificationLogEntry, settings: LogViewSettings): String = csvLine(values(entry, settings))
|
||||
fun csvRow(entry: NotificationLogEntry, settings: LogViewSettings, context: Context? = null): String = csvLine(values(entry, settings, context))
|
||||
|
||||
fun formattedRow(values: List<String>, widths: List<Int>): String =
|
||||
values.mapIndexed { index, value -> value.padEnd(widths[index]) }.joinToString(" ")
|
||||
@@ -46,9 +44,10 @@ object LogExporter {
|
||||
entry: NotificationLogEntry,
|
||||
settings: LogViewSettings,
|
||||
imagePath: String? = null,
|
||||
context: Context? = null,
|
||||
): String = buildString {
|
||||
append("<tr>")
|
||||
fields(settings).forEach { field -> append("<td>${htmlValue(field, entry, settings, imagePath)}</td>") }
|
||||
fields(settings).forEach { field -> append("<td>${htmlValue(field, entry, settings, imagePath, context)}</td>") }
|
||||
append("</tr>")
|
||||
}
|
||||
|
||||
@@ -57,11 +56,12 @@ object LogExporter {
|
||||
fun html(
|
||||
entries: List<NotificationLogEntry>,
|
||||
settings: LogViewSettings,
|
||||
context: Context? = null,
|
||||
imagePath: (NotificationLogEntry) -> String? = { null },
|
||||
): String = buildString {
|
||||
append(htmlStart(settings))
|
||||
entries.sortedByDescending { it.recordedAtEpochMillis }.forEach { entry ->
|
||||
append(htmlRow(entry, settings, imagePath(entry)))
|
||||
append(htmlRow(entry, settings, imagePath(entry), context))
|
||||
}
|
||||
append(htmlEnd())
|
||||
}
|
||||
@@ -69,9 +69,9 @@ object LogExporter {
|
||||
private fun fields(settings: LogViewSettings): List<LogField> = settings.order.filter { it in settings.visibleFields }
|
||||
|
||||
private fun header(field: LogField) = field.name.lowercase().replace('_', ' ')
|
||||
private fun htmlValue(field: LogField, entry: NotificationLogEntry, settings: LogViewSettings, imagePath: String?): String {
|
||||
private fun htmlValue(field: LogField, entry: NotificationLogEntry, settings: LogViewSettings, imagePath: String?, context: Context?): String {
|
||||
val value = when (field) {
|
||||
LogField.TIMESTAMP -> timestamp(entry.recordedAtEpochMillis, settings.timestampZone, entry.eventTimeZoneId)
|
||||
LogField.TIMESTAMP -> timestamp(context, entry.recordedAtEpochMillis, settings, entry.eventTimeZoneId)
|
||||
LogField.APP_NAME -> entry.appName
|
||||
LogField.PACKAGE_NAME -> entry.packageName
|
||||
LogField.ACTION -> entry.action.name.lowercase().replace('_', ' ')
|
||||
@@ -84,13 +84,8 @@ object LogExporter {
|
||||
escaped
|
||||
}
|
||||
}
|
||||
private fun timestamp(value: Long, zone: TimestampZone, eventTimeZoneId: String?): String = DateFormat.getDateTimeInstance().apply {
|
||||
timeZone = when (zone) {
|
||||
TimestampZone.UTC -> TimeZone.getTimeZone("UTC")
|
||||
TimestampZone.LOCAL_NOW -> TimeZone.getDefault()
|
||||
TimestampZone.EVENT_LOCAL -> eventTimeZoneId?.let(TimeZone::getTimeZone) ?: TimeZone.getDefault()
|
||||
}
|
||||
}.format(Date(value))
|
||||
private fun timestamp(context: Context?, value: Long, settings: LogViewSettings, eventTimeZoneId: String?): String =
|
||||
TimestampFormatter.format(context, value, settings, eventTimeZoneId)
|
||||
private fun csvLine(values: List<String>): String = values.joinToString(",") { csvValue(it) }
|
||||
private fun csvValue(value: String) = "\"${value.replace("\"", "\"\"")}\""
|
||||
private fun escape(value: String) = value.replace("&", "&").replace("<", "<").replace(">", ">").replace("\"", """)
|
||||
|
||||
@@ -2,11 +2,15 @@ package se.ajpanton.notificationlog.settings
|
||||
|
||||
enum class LogField { TIMESTAMP, APP_NAME, PACKAGE_NAME, ACTION, CONTENTS }
|
||||
enum class TimestampZone { LOCAL_NOW, UTC, EVENT_LOCAL }
|
||||
enum class TimestampDateFormat { SYSTEM_DEFAULT, YEAR_MONTH_DAY, DAY_MONTH_YEAR, MONTH_DAY_YEAR }
|
||||
enum class TimestampClockFormat { SYSTEM_DEFAULT, HOUR_24, HOUR_12 }
|
||||
enum class DisplayEvent { APPEARING, DISAPPEARING, EDITS }
|
||||
|
||||
data class LogViewSettings(
|
||||
val visibleFields: Set<LogField> = setOf(LogField.TIMESTAMP, LogField.APP_NAME, LogField.ACTION, LogField.CONTENTS),
|
||||
val order: List<LogField> = LogField.entries,
|
||||
val timestampZone: TimestampZone = TimestampZone.LOCAL_NOW,
|
||||
val timestampDateFormat: TimestampDateFormat = TimestampDateFormat.SYSTEM_DEFAULT,
|
||||
val timestampClockFormat: TimestampClockFormat = TimestampClockFormat.SYSTEM_DEFAULT,
|
||||
val visibleEvents: Set<DisplayEvent> = DisplayEvent.entries.toSet(),
|
||||
)
|
||||
|
||||
@@ -10,6 +10,8 @@ class LogViewSettingsStore(context: Context) {
|
||||
.map(LogField::valueOf).toSet(),
|
||||
order = prefs.getString("order", null)?.split(',')?.map(LogField::valueOf) ?: LogField.entries,
|
||||
timestampZone = TimestampZone.valueOf(prefs.getString("zone", TimestampZone.LOCAL_NOW.name)!!),
|
||||
timestampDateFormat = TimestampDateFormat.valueOf(prefs.getString("date_format", TimestampDateFormat.SYSTEM_DEFAULT.name)!!),
|
||||
timestampClockFormat = TimestampClockFormat.valueOf(prefs.getString("clock_format", TimestampClockFormat.SYSTEM_DEFAULT.name)!!),
|
||||
visibleEvents = prefs.getStringSet("visible_events", DisplayEvent.entries.map { it.name }.toSet())!!
|
||||
.map(DisplayEvent::valueOf).toSet(),
|
||||
)
|
||||
@@ -17,6 +19,8 @@ class LogViewSettingsStore(context: Context) {
|
||||
putStringSet("visible", settings.visibleFields.map { it.name }.toSet())
|
||||
putString("order", settings.order.joinToString(",") { it.name })
|
||||
putString("zone", settings.timestampZone.name)
|
||||
putString("date_format", settings.timestampDateFormat.name)
|
||||
putString("clock_format", settings.timestampClockFormat.name)
|
||||
putStringSet("visible_events", settings.visibleEvents.map { it.name }.toSet())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package se.ajpanton.notificationlog.settings
|
||||
|
||||
import android.content.Context
|
||||
import android.text.format.DateFormat as AndroidDateFormat
|
||||
import java.text.DateFormat
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
import java.util.TimeZone
|
||||
|
||||
object TimestampFormatter {
|
||||
fun format(context: Context?, value: Long, settings: LogViewSettings, eventTimeZoneId: String?): String {
|
||||
val date = Date(value)
|
||||
val timeZone = when (settings.timestampZone) {
|
||||
TimestampZone.UTC -> TimeZone.getTimeZone("UTC")
|
||||
TimestampZone.LOCAL_NOW -> TimeZone.getDefault()
|
||||
TimestampZone.EVENT_LOCAL -> eventTimeZoneId?.let(TimeZone::getTimeZone) ?: TimeZone.getDefault()
|
||||
}
|
||||
if (context == null &&
|
||||
settings.timestampDateFormat == TimestampDateFormat.SYSTEM_DEFAULT &&
|
||||
settings.timestampClockFormat == TimestampClockFormat.SYSTEM_DEFAULT
|
||||
) {
|
||||
return DateFormat.getDateTimeInstance().apply { this.timeZone = timeZone }.format(date)
|
||||
}
|
||||
return "${dateFormatter(settings.timestampDateFormat, timeZone).format(date)} ${clockFormatter(context, settings.timestampClockFormat, timeZone).format(date)}"
|
||||
}
|
||||
|
||||
private fun dateFormatter(format: TimestampDateFormat, timeZone: TimeZone): DateFormat = when (format) {
|
||||
TimestampDateFormat.SYSTEM_DEFAULT -> DateFormat.getDateInstance()
|
||||
TimestampDateFormat.YEAR_MONTH_DAY -> SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
|
||||
TimestampDateFormat.DAY_MONTH_YEAR -> SimpleDateFormat("dd-MM-yyyy", Locale.getDefault())
|
||||
TimestampDateFormat.MONTH_DAY_YEAR -> SimpleDateFormat("MM-dd-yyyy", Locale.getDefault())
|
||||
}.apply { this.timeZone = timeZone }
|
||||
|
||||
private fun clockFormatter(context: Context?, format: TimestampClockFormat, timeZone: TimeZone): DateFormat = when (format) {
|
||||
TimestampClockFormat.SYSTEM_DEFAULT -> context?.let {
|
||||
SimpleDateFormat(if (AndroidDateFormat.is24HourFormat(it)) "HH:mm:ss" else "hh:mm:ss a", Locale.getDefault())
|
||||
} ?: DateFormat.getTimeInstance()
|
||||
TimestampClockFormat.HOUR_24 -> SimpleDateFormat("HH:mm:ss", Locale.getDefault())
|
||||
TimestampClockFormat.HOUR_12 -> SimpleDateFormat("hh:mm:ss a", Locale.getDefault())
|
||||
}.apply { this.timeZone = timeZone }
|
||||
}
|
||||
Reference in New Issue
Block a user