main

square/leakcanary

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

LeakViews.kt

TLDR

This file provides utility functions for sharing content, such as text or heap dumps, from a view in an Android application. It also includes functions for sharing content to specific platforms like Stack Overflow or GitHub issues.

Methods

share

This function allows a view to share content by creating and starting a share intent chooser.

shareHeapDump

This function allows a view to share a heap dump file by setting the file as readable, getting the content URI for the file, and starting a share intent chooser.

startShareIntentChooser

This private function starts a share intent chooser for a given content URI.

shareToStackOverflow

This function allows a view to share content to Stack Overflow by copying the content to the clipboard, showing a toast message, and opening a browser intent to the Stack Overflow page.

shareToGitHubIssue

This function allows a view to share a heap analysis failure to GitHub issues by copying the failure information to the clipboard, showing a toast message, and opening a browser intent to the GitHub new issue page.

Constants

STACKOVERFLOW_QUESTION_URL

This constant stores the URL for asking a new Stack Overflow question.

NEW_ISSUE_URL

This constant stores the URL for creating a new issue on the LeakCanary GitHub repository.

package leakcanary.internal.activity

import android.annotation.SuppressLint
import android.content.ActivityNotFoundException
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.AsyncTask
import android.os.Build
import android.view.View
import android.widget.Toast
import com.squareup.leakcanary.core.BuildConfig
import com.squareup.leakcanary.core.R
import leakcanary.internal.LeakCanaryFileProvider
import leakcanary.internal.navigation.activity
import shark.HeapAnalysisFailure
import java.io.File

internal fun View.share(content: String) {
  val intent = Intent(Intent.ACTION_SEND)
  intent.type = "text/plain"
  intent.putExtra(Intent.EXTRA_TEXT, content)
  activity.startActivity(
    Intent.createChooser(intent, resources.getString(R.string.leak_canary_share_with))
  )
}

@SuppressLint("SetWorldReadable")
internal fun View.shareHeapDump(heapDumpFile: File) {
  AsyncTask.SERIAL_EXECUTOR.execute {
    heapDumpFile.setReadable(true, false)
    val heapDumpUri = LeakCanaryFileProvider.getUriForFile(
      activity,
      "com.squareup.leakcanary.fileprovider." + activity.packageName,
      heapDumpFile
    )
    activity.runOnUiThread { startShareIntentChooser(heapDumpUri) }
  }
}

private fun View.startShareIntentChooser(uri: Uri) {
  val intent = Intent(Intent.ACTION_SEND)
  intent.type = "application/octet-stream"
  intent.putExtra(Intent.EXTRA_STREAM, uri)
  activity.startActivity(
    Intent.createChooser(intent, resources.getString(R.string.leak_canary_share_with))
  )
}

internal fun View.shareToStackOverflow(content: String) {
  val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
  // AsyncTask was needed here due to setPrimaryClip making a disk write which
  // violated StrictMode if on the main thread
  AsyncTask.execute {
    clipboard.setPrimaryClip(
      ClipData.newPlainText(
        context.getString(R.string.leak_canary_leak_clipdata_label),
        "```\n$content```"
      )
    )
  }
  Toast.makeText(context, R.string.leak_canary_leak_copied, Toast.LENGTH_LONG)
    .show()
  val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(STACKOVERFLOW_QUESTION_URL))
  try {
    activity.startActivity(browserIntent)
  } catch (e: ActivityNotFoundException) {
    Toast.makeText(context, R.string.leak_canary_leak_missing_browser_error, Toast.LENGTH_LONG)
      .show()
  }
}

internal fun View.shareToGitHubIssue(failure: HeapAnalysisFailure) {
  val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
  // AsyncTask was needed here due to setPrimaryClip making a disk write which
  // violated StrictMode if on the main thread
  AsyncTask.execute {
    clipboard.setPrimaryClip(
      ClipData.newPlainText(
        context.getString(R.string.leak_canary_failure_clipdata_label),
        """```
          |${failure.exception}
          |Build.VERSION.SDK_INT: ${Build.VERSION.SDK_INT}
          |Build.MANUFACTURER: ${Build.MANUFACTURER}
          |LeakCanary version: ${BuildConfig.LIBRARY_VERSION}
          |Analysis duration: ${failure.analysisDurationMillis} ms
          |Heap dump file path: ${failure.heapDumpFile.absolutePath}
          |Heap dump timestamp: ${failure.createdAtTimeMillis}
          |```
        """.trimMargin()
      )
    )
  }
  Toast.makeText(context, R.string.leak_canary_failure_copied, Toast.LENGTH_LONG)
    .show()
  val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(NEW_ISSUE_URL))
  try {
    activity.startActivity(browserIntent)
  } catch (e: ActivityNotFoundException) {
    Toast.makeText(context, R.string.leak_canary_leak_missing_browser_error, Toast.LENGTH_LONG)
      .show()
  }
}

private const val STACKOVERFLOW_QUESTION_URL =
  "http://stackoverflow.com/questions/ask?guided=false&tags=leakcanary"

private const val NEW_ISSUE_URL =
  "https://github.com/square/leakcanary/issues/new?labels=type%3A+bug&template=2-bug.md"