diff --git a/app/src/main/java/se/ajpanton/notificationlog/ViewLogsFragment.kt b/app/src/main/java/se/ajpanton/notificationlog/ViewLogsFragment.kt index 9f09019..3cd29d8 100644 --- a/app/src/main/java/se/ajpanton/notificationlog/ViewLogsFragment.kt +++ b/app/src/main/java/se/ajpanton/notificationlog/ViewLogsFragment.kt @@ -1,5 +1,10 @@ package se.ajpanton.notificationlog +import android.animation.Animator +import android.animation.AnimatorListenerAdapter +import android.animation.ArgbEvaluator +import android.animation.ValueAnimator +import android.graphics.Color import android.graphics.BitmapFactory import android.graphics.Paint import android.os.Bundle @@ -26,6 +31,7 @@ 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 import androidx.core.view.doOnPreDraw @@ -124,10 +130,18 @@ class ViewLogsFragment : Fragment(R.layout.fragment_view_logs) { val contents = fieldValue(LogField.CONTENTS, row.entry, settings) val hasContents = LogField.CONTENTS in settings.visibleFields && !contents.isNullOrEmpty() val toggleExpanded = { - val collapseAnchor = if (expanded) captureCollapseAnchor(this@apply) else null + val collapsing = expanded + val collapseAnchor = if (collapsing) captureCollapseAnchor(this@apply) else null if (!expandedIds.add(row.entry.id)) expandedIds.remove(row.entry.id) renderExpanded(row.entry.id in expandedIds) - collapseAnchor?.let(::restoreCollapseAnchor) + if (collapsing) { + val highlight = { highlightCollapsedRow(this@apply) } + if (collapseAnchor != null) { + restoreCollapseAnchor(collapseAnchor, highlight) + } else { + this@apply.doOnPreDraw { highlight() } + } + } Unit } val metadataCell = metadataCell(metadata, expanded, toggleExpanded, row.entry.id).apply { @@ -355,7 +369,7 @@ class ViewLogsFragment : Fragment(R.layout.fragment_view_logs) { } } - private fun restoreCollapseAnchor(anchor: CollapseAnchor) { + private fun restoreCollapseAnchor(anchor: CollapseAnchor, afterRestore: () -> Unit) { val currentBinding = binding ?: return val scroll = currentBinding.logScroll val rows = currentBinding.logRows @@ -368,6 +382,30 @@ class ViewLogsFragment : Fragment(R.layout.fragment_view_logs) { } val maximum = (scroll.getChildAt(0).height - scroll.height).coerceAtLeast(0) scroll.scrollTo(0, target.coerceIn(0, maximum)) + afterRestore() + } + } + + private fun highlightCollapsedRow(row: View) { + if (!row.isAttachedToWindow) return + val baseColor = ContextCompat.getColor(row.context, R.color.primary) + val startColor = Color.argb( + COLLAPSE_HIGHLIGHT_ALPHA, + Color.red(baseColor), + Color.green(baseColor), + Color.blue(baseColor), + ) + val endColor = Color.argb(0, Color.red(baseColor), Color.green(baseColor), Color.blue(baseColor)) + row.setBackgroundColor(startColor) + ValueAnimator.ofObject(ArgbEvaluator(), startColor, endColor).apply { + duration = COLLAPSE_HIGHLIGHT_DURATION_MILLIS + addUpdateListener { row.setBackgroundColor(it.animatedValue as Int) } + addListener(object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + row.background = null + } + }) + start() } } @@ -406,5 +444,7 @@ class ViewLogsFragment : Fragment(R.layout.fragment_view_logs) { const val ROW_SEPARATOR_MARGIN_DP = 4 const val METADATA_INDENT_DP = 8 const val MAXIMUM_METADATA_COLUMN_FRACTION = 0.45f + const val COLLAPSE_HIGHLIGHT_ALPHA = 52 + const val COLLAPSE_HIGHLIGHT_DURATION_MILLIS = 450L } }