Preserve log position when collapsing rows
This commit is contained in:
@@ -96,7 +96,7 @@ class FilterAppsFragment : Fragment(R.layout.fragment_event_settings) {
|
|||||||
|
|
||||||
private fun updateRuleLabels(rule: AppFilterSettings) {
|
private fun updateRuleLabels(rule: AppFilterSettings) {
|
||||||
binding!!.appRuleToggle.text = modeLabel(rule.mode)
|
binding!!.appRuleToggle.text = modeLabel(rule.mode)
|
||||||
binding!!.onlySeenToggle.text = "Show only seen and checked apps"
|
binding!!.onlySeenToggle.text = "Show only seen and edited apps"
|
||||||
binding!!.seenFirstToggle.text = "Show seen apps first"
|
binding!!.seenFirstToggle.text = "Show seen apps first"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import se.ajpanton.notificationlog.settings.DisplayEvent
|
|||||||
import java.text.DateFormat
|
import java.text.DateFormat
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.core.view.children
|
||||||
|
|
||||||
class ViewLogsFragment : Fragment(R.layout.fragment_view_logs) {
|
class ViewLogsFragment : Fragment(R.layout.fragment_view_logs) {
|
||||||
private var binding: FragmentViewLogsBinding? = null
|
private var binding: FragmentViewLogsBinding? = null
|
||||||
@@ -107,9 +108,12 @@ class ViewLogsFragment : Fragment(R.layout.fragment_view_logs) {
|
|||||||
val metadata = metadataValues(row.entry, settings)
|
val metadata = metadataValues(row.entry, settings)
|
||||||
val contents = fieldValue(LogField.CONTENTS, row.entry, settings)
|
val contents = fieldValue(LogField.CONTENTS, row.entry, settings)
|
||||||
val hasContents = LogField.CONTENTS in settings.visibleFields && !contents.isNullOrEmpty()
|
val hasContents = LogField.CONTENTS in settings.visibleFields && !contents.isNullOrEmpty()
|
||||||
|
val collapseAnchor = if (expanded) captureCollapseAnchor(this@apply) else null
|
||||||
val toggleExpanded = {
|
val toggleExpanded = {
|
||||||
if (!expandedIds.add(row.entry.id)) expandedIds.remove(row.entry.id)
|
if (!expandedIds.add(row.entry.id)) expandedIds.remove(row.entry.id)
|
||||||
renderExpanded(row.entry.id in expandedIds)
|
renderExpanded(row.entry.id in expandedIds)
|
||||||
|
collapseAnchor?.let(::restoreCollapseAnchor)
|
||||||
|
Unit
|
||||||
}
|
}
|
||||||
val metadataPill = metadataPill(metadata, expanded, toggleExpanded, row.entry.id).apply {
|
val metadataPill = metadataPill(metadata, expanded, toggleExpanded, row.entry.id).apply {
|
||||||
layoutParams = LinearLayout.LayoutParams(
|
layoutParams = LinearLayout.LayoutParams(
|
||||||
@@ -293,6 +297,45 @@ class ViewLogsFragment : Fragment(R.layout.fragment_view_logs) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun captureCollapseAnchor(collapsingRow: View): CollapseAnchor {
|
||||||
|
val currentBinding = binding ?: return CollapseAnchor.NONE
|
||||||
|
val scroll = currentBinding.logScroll
|
||||||
|
val rowsTop = currentBinding.logRows.top
|
||||||
|
val topEdge = scroll.scrollY - rowsTop
|
||||||
|
val bottomEdge = topEdge + scroll.height
|
||||||
|
val topRow = rowAt(currentBinding.logRows, topEdge)
|
||||||
|
val bottomRow = rowAt(currentBinding.logRows, bottomEdge)
|
||||||
|
return when {
|
||||||
|
topRow === collapsingRow && bottomRow === collapsingRow -> CollapseAnchor.CENTER_COLLAPSED_ROW(collapsingRow)
|
||||||
|
topRow === collapsingRow && bottomRow != null ->
|
||||||
|
CollapseAnchor.KEEP_BOTTOM_EDGE(bottomRow, bottomRow.bottom - bottomEdge)
|
||||||
|
bottomRow === collapsingRow && topRow != null ->
|
||||||
|
CollapseAnchor.KEEP_TOP_EDGE(topRow, topEdge - topRow.top)
|
||||||
|
else -> CollapseAnchor.NONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun restoreCollapseAnchor(anchor: CollapseAnchor) {
|
||||||
|
val currentBinding = binding ?: return
|
||||||
|
val scroll = currentBinding.logScroll
|
||||||
|
val rows = currentBinding.logRows
|
||||||
|
rows.post {
|
||||||
|
if (binding !== currentBinding) return@post
|
||||||
|
val target = when (anchor) {
|
||||||
|
CollapseAnchor.NONE -> return@post
|
||||||
|
is CollapseAnchor.CENTER_COLLAPSED_ROW -> rows.top + anchor.row.top + anchor.row.height / 2 - scroll.height / 2
|
||||||
|
is CollapseAnchor.KEEP_TOP_EDGE -> rows.top + anchor.row.top + anchor.offset
|
||||||
|
is CollapseAnchor.KEEP_BOTTOM_EDGE -> rows.top + anchor.row.bottom - anchor.offset - scroll.height
|
||||||
|
}
|
||||||
|
val maximum = (scroll.getChildAt(0).height - scroll.height).coerceAtLeast(0)
|
||||||
|
scroll.scrollTo(0, target.coerceIn(0, maximum))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun rowAt(rows: LinearLayout, edge: Int): View? = rows.children.firstOrNull { row ->
|
||||||
|
edge >= row.top && edge <= row.bottom
|
||||||
|
}
|
||||||
|
|
||||||
private fun dp(value: Int): Int = (value * resources.displayMetrics.density).toInt()
|
private fun dp(value: Int): Int = (value * resources.displayMetrics.density).toInt()
|
||||||
|
|
||||||
private data class LogRow(val entry: NotificationLogEntry)
|
private data class LogRow(val entry: NotificationLogEntry)
|
||||||
@@ -304,6 +347,13 @@ class ViewLogsFragment : Fragment(R.layout.fragment_view_logs) {
|
|||||||
val breakBeforePeriods: Boolean = false,
|
val breakBeforePeriods: Boolean = false,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private sealed interface CollapseAnchor {
|
||||||
|
data object NONE : CollapseAnchor
|
||||||
|
data class CENTER_COLLAPSED_ROW(val row: View) : CollapseAnchor
|
||||||
|
data class KEEP_TOP_EDGE(val row: View, val offset: Int) : CollapseAnchor
|
||||||
|
data class KEEP_BOTTOM_EDGE(val row: View, val offset: Int) : CollapseAnchor
|
||||||
|
}
|
||||||
|
|
||||||
private companion object {
|
private companion object {
|
||||||
const val LEFT_PILL_WEIGHT = 0.42f
|
const val LEFT_PILL_WEIGHT = 0.42f
|
||||||
const val RIGHT_PILL_WEIGHT = 0.58f
|
const val RIGHT_PILL_WEIGHT = 0.58f
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ class CaptureSettingsStore(context: Context) {
|
|||||||
private val preferences = context.applicationContext.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE)
|
private val preferences = context.applicationContext.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE)
|
||||||
|
|
||||||
var logGroupSummaries: Boolean
|
var logGroupSummaries: Boolean
|
||||||
get() = preferences.getBoolean(LOG_GROUP_SUMMARIES, true)
|
get() = preferences.getBoolean(LOG_GROUP_SUMMARIES, false)
|
||||||
set(value) = preferences.edit { putBoolean(LOG_GROUP_SUMMARIES, value) }
|
set(value) = preferences.edit { putBoolean(LOG_GROUP_SUMMARIES, value) }
|
||||||
|
|
||||||
private companion object {
|
private companion object {
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
android:id="@+id/only_seen_toggle"
|
android:id="@+id/only_seen_toggle"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="Show only seen apps" />
|
android:text="Show only seen and edited apps" />
|
||||||
|
|
||||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
<com.google.android.material.switchmaterial.SwitchMaterial
|
||||||
android:id="@+id/seen_first_toggle"
|
android:id="@+id/seen_first_toggle"
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ class GroupSummaryPolicyTest {
|
|||||||
imageBytes = null,
|
imageBytes = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Test fun `group summaries are logged by default`() {
|
@Test fun `enabled group-summary logging retains summaries`() {
|
||||||
assertTrue(GroupSummaryPolicy.shouldLog(snapshot(isGroupSummary = true), logGroupSummaries = true))
|
assertTrue(GroupSummaryPolicy.shouldLog(snapshot(isGroupSummary = true), logGroupSummaries = true))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user