main

square/leakcanary

Last updated at: 29/12/2023 09:39

HeapDumpsScreen.kt

TLDR

This file contains the HeapDumpsScreen class, which represents a screen in the LeakCanary library for displaying heap dumps and performing various actions related to them.

Classes

HeapDumpsScreen

This class represents a screen in the LeakCanary library for displaying heap dumps and interacting with them. It inflates a layout file and handles UI interactions such as refreshing the screen, deleting all heap dumps, importing new heap dumps, and dumping the heap. It also retrieves heap analyses from a database and displays them in a list view. Each analysis can be clicked to view more details.

Methods

No methods are defined in this file.

package leakcanary.internal.activity.screen

import android.app.ActivityManager
import android.app.AlertDialog
import android.view.View
import android.view.ViewGroup
import android.widget.ListView
import android.widget.TextView
import com.squareup.leakcanary.core.R
import leakcanary.LeakCanary
import leakcanary.internal.activity.LeakActivity
import leakcanary.internal.activity.db.HeapAnalysisTable
import leakcanary.internal.activity.db.HeapAnalysisTable.Projection
import leakcanary.internal.activity.db.executeOnDb
import leakcanary.internal.activity.ui.SimpleListAdapter
import leakcanary.internal.activity.ui.TimeFormatter
import leakcanary.internal.navigation.NavigatingActivity
import leakcanary.internal.navigation.Screen
import leakcanary.internal.navigation.activity
import leakcanary.internal.navigation.goTo
import leakcanary.internal.navigation.inflate
import leakcanary.internal.navigation.onCreateOptionsMenu
import leakcanary.internal.navigation.onScreenExiting

internal class HeapDumpsScreen : Screen() {
  override fun createView(container: ViewGroup) =
    container.inflate(R.layout.leak_canary_heap_dumps_screen).apply {

      val unsubscribeRefresh = HeapAnalysisTable.onUpdate {
        activity<NavigatingActivity>().refreshCurrentScreen()
      }

      onScreenExiting { unsubscribeRefresh() }

      onCreateOptionsMenu { menu ->
        if (!ActivityManager.isUserAMonkey()) {
          menu.add(R.string.leak_canary_delete_all)
            .setOnMenuItemClickListener {
              AlertDialog.Builder(context)
                .setIcon(android.R.drawable.ic_dialog_alert)
                .setTitle(R.string.leak_canary_delete_all)
                .setMessage(R.string.leak_canary_delete_all_leaks_title)
                .setPositiveButton(android.R.string.ok) { _, _ ->
                  executeOnDb {
                    HeapAnalysisTable.deleteAll(db)
                    updateUi {
                      val listView = findViewById<ListView>(R.id.leak_canary_list)
                      listView.adapter =
                        SimpleListAdapter(
                          R.layout.leak_canary_simple_row, emptyList<Any>()
                        ) { _, _ -> }
                    }
                  }
                }
                .setNegativeButton(android.R.string.cancel, null)
                .show()
              true
            }
        }
      }

      findViewById<View>(R.id.leak_canary_import_heap_dump).setOnClickListener {
        activity<LeakActivity>().requestImportHprof()
      }

      findViewById<View>(R.id.leak_canary_dump_heap_now).setOnClickListener {
        LeakCanary.dumpHeap()
      }

      executeOnDb {
        val projections = HeapAnalysisTable.retrieveAll(db)
        updateUi { onAnalysesRetrieved(projections) }
      }

    }

  private fun View.onAnalysesRetrieved(projections: List<Projection>) {
    activity.title = resources.getQuantityString(
      R.plurals.leak_canary_heap_analysis_list_screen_title,
      projections.size, projections.size
    )

    val listView = findViewById<ListView>(R.id.leak_canary_list)

    listView.setOnItemClickListener { _, _, position, _ ->
      val projection = projections[position]
      val analysisScreen = if (projection.exceptionSummary != null) {
        HeapAnalysisFailureScreen(projection.id)
      } else {
        HeapDumpScreen(projection.id)
      }
      goTo(analysisScreen)
    }

    listView.adapter =
      SimpleListAdapter(R.layout.leak_canary_leak_row, projections) { view, position ->
        val goneView = view.findViewById<TextView>(R.id.leak_canary_count_text)
        goneView.visibility = View.GONE
        val timeView = view.findViewById<TextView>(R.id.leak_canary_leak_text)
        val countView = view.findViewById<TextView>(R.id.leak_canary_time_text)

        val projection = getItem(position)
        // Enable means "new"
        countView.isEnabled = false

        timeView.text = TimeFormatter.formatTimestamp(view.context, projection.createdAtTimeMillis)

        val count = projection.exceptionSummary ?: resources.getQuantityString(
          R.plurals.leak_canary_distinct_leaks,
          projection.leakCount, projection.leakCount
        )
        countView.text = count
      }
  }
}