main

square/leakcanary

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

HeapAnalysisFailureScreen.kt

TLDR

This file defines the HeapAnalysisFailureScreen class, which is responsible for displaying details of a heap analysis failure on the UI. It retrieves the analysis information from the database and updates the UI accordingly. It also provides options to retry the analysis, file a bug report, share the heap dump file, and delete the analysis.

Classes

HeapAnalysisFailureScreen

This class represents a screen that displays the details of a heap analysis failure. It extends the Screen class. The main functionality of this class is to retrieve the heap analysis information from the database and update the UI accordingly. It also handles options related to retry the analysis, file a bug report, share the heap dump file, and delete the analysis.

The HeapAnalysisFailureScreen class has the following methods:

  • createView(container: ViewGroup): Creates the view for the screen by inflating the corresponding layout file and setting up the UI elements. It sets the screen title and retrieves the heap analysis information from the database. If the analysis is deleted, it displays a message. Otherwise, it calls the onFailureRetrieved method to update the UI with the analysis details.
  • onFailureRetrieved(heapAnalysis: HeapAnalysisFailure, heapDumpFileExist: Boolean): Updates the UI with the heap analysis details. It sets the screen title, constructs the failure text with HTML links, handles the URL actions, and sets the text and click listeners for the relevant UI elements. It also handles the options menu, allowing the user to delete the analysis if they are not a monkey user.
package leakcanary.internal.activity.screen

import android.app.ActivityManager
import android.text.Html
import android.text.SpannableStringBuilder
import android.text.method.LinkMovementMethod
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import com.squareup.leakcanary.core.R
import java.util.UUID
import leakcanary.EventListener.Event.HeapDump
import leakcanary.internal.InternalLeakCanary
import leakcanary.internal.activity.db.HeapAnalysisTable
import leakcanary.internal.activity.db.executeOnDb
import leakcanary.internal.activity.shareHeapDump
import leakcanary.internal.activity.shareToGitHubIssue
import leakcanary.internal.activity.ui.UiUtils
import leakcanary.internal.navigation.Screen
import leakcanary.internal.navigation.activity
import leakcanary.internal.navigation.goBack
import leakcanary.internal.navigation.inflate
import leakcanary.internal.navigation.onCreateOptionsMenu
import shark.HeapAnalysisFailure

internal class HeapAnalysisFailureScreen(
  private val analysisId: Long
) : Screen() {

  override fun createView(container: ViewGroup) =
    container.inflate(R.layout.leak_canary_heap_analysis_failure_screen).apply {
      activity.title = resources.getString(R.string.leak_canary_loading_title)
      executeOnDb {
        val heapAnalysis = HeapAnalysisTable.retrieve<HeapAnalysisFailure>(db, analysisId)
        if (heapAnalysis == null) {
          updateUi {
            activity.title = resources.getString(R.string.leak_canary_analysis_deleted_title)
          }
        } else {
          val heapDumpFileExist = heapAnalysis.heapDumpFile.exists()
          updateUi { onFailureRetrieved(heapAnalysis, heapDumpFileExist) }
        }
      }
    }

  private fun View.onFailureRetrieved(
    heapAnalysis: HeapAnalysisFailure,
    heapDumpFileExist: Boolean
  ) {
    activity.title = resources.getString(R.string.leak_canary_analysis_failed)

    val failureText =
      if (heapDumpFileExist) {
        "You can <a href=\"try_again\">run the analysis again</a>.<br><br>"
      } else {
        ""
      } + """
      Please <a href="file_issue">click here</a> to file a bug report.
      The stacktrace details will be copied into the clipboard and you just need to paste into the
      GitHub issue description.""" + (if (heapDumpFileExist) {
        """
        <br><br>To help reproduce the issue, please share the
        <a href="share_hprof">Heap Dump file</a> and upload it to the GitHub issue.
      """
      } else "")

    val failure = Html.fromHtml(failureText) as SpannableStringBuilder

    UiUtils.replaceUrlSpanWithAction(failure) { urlSpan ->
      when (urlSpan) {
        "file_issue" -> {
          {
            shareToGitHubIssue(heapAnalysis)
          }
        }
        "share_hprof" -> {
          {
            shareHeapDump(heapAnalysis.heapDumpFile)
          }
        }
        "try_again" -> {
          {
            InternalLeakCanary.sendEvent(
              HeapDump(
                uniqueId = UUID.randomUUID().toString(),
                file = heapAnalysis.heapDumpFile,
                durationMillis = heapAnalysis.dumpDurationMillis,
                reason = "Retrying heap analysis after failure."
              )
            )
          }
        }
        else -> null
      }
    }
    findViewById<TextView>(R.id.leak_canary_header_text).apply {
      movementMethod = LinkMovementMethod.getInstance()
      text = failure
    }

    findViewById<TextView>(R.id.leak_canary_stacktrace).text = heapAnalysis.exception.toString()

    onCreateOptionsMenu { menu ->
      if (!ActivityManager.isUserAMonkey()) {
        menu.add(R.string.leak_canary_delete)
          .setOnMenuItemClickListener {
            executeOnDb {
              HeapAnalysisTable.delete(db, analysisId, heapAnalysis.heapDumpFile)
              updateUi {
                goBack()
              }
            }
            true
          }
      }
    }
  }
}