<lambda>null1 package leakcanary.internal.activity.screen
2
3 import android.content.Intent
4 import android.graphics.Bitmap
5 import android.os.Build.VERSION
6 import android.os.Build.VERSION_CODES
7 import android.os.Environment
8 import android.os.Environment.DIRECTORY_DOWNLOADS
9 import android.view.View
10 import android.view.ViewGroup
11 import android.view.ViewTreeObserver.OnGlobalLayoutListener
12 import android.widget.ImageView
13 import android.widget.Toast
14 import com.squareup.leakcanary.core.R
15 import java.io.File
16 import java.io.FileOutputStream
17 import java.io.IOException
18 import leakcanary.internal.InternalLeakCanary
19 import leakcanary.internal.LeakCanaryFileProvider
20 import leakcanary.internal.activity.db.executeOnIo
21 import leakcanary.internal.navigation.Screen
22 import leakcanary.internal.navigation.activity
23 import leakcanary.internal.navigation.inflate
24 import leakcanary.internal.navigation.onCreateOptionsMenu
25 import leakcanary.internal.utils.humanReadableByteCount
26 import shark.SharkLog
27
28 internal class RenderHeapDumpScreen(
29 private val heapDumpFile: File
30 ) : Screen() {
31
32 override fun createView(container: ViewGroup) =
33 container.inflate(R.layout.leak_canary_heap_render).apply {
34 container.activity.title = resources.getString(R.string.leak_canary_loading_title)
35
36 executeOnIo {
37 val byteCount = humanReadableByteCount(heapDumpFile.length(), si = true)
38 updateUi {
39 container.activity.title =
40 resources.getString(R.string.leak_canary_heap_dump_screen_title, byteCount)
41 }
42 }
43
44 val loadingView = findViewById<View>(R.id.leak_canary_loading)
45 val imageView = findViewById<ImageView>(R.id.leak_canary_heap_rendering)
46
47 viewTreeObserver.addOnGlobalLayoutListener(object : OnGlobalLayoutListener {
48 override fun onGlobalLayout() {
49
50 executeOnIo {
51 val bitmap = HeapDumpRenderer.render(
52 context, heapDumpFile, measuredWidth, measuredHeight, 0
53 )
54 updateUi {
55 imageView.setImageBitmap(bitmap)
56 loadingView.visibility = View.GONE
57 imageView.visibility = View.VISIBLE
58 }
59 }
60 if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {
61 viewTreeObserver.removeOnGlobalLayoutListener(this)
62 } else {
63 viewTreeObserver.removeGlobalOnLayoutListener(this)
64 }
65 }
66 })
67
68 onCreateOptionsMenu { menu ->
69 menu.add(R.string.leak_canary_options_menu_generate_hq_bitmap)
70 .setOnMenuItemClickListener {
71 val leakDirectoryProvider = InternalLeakCanary.createLeakDirectoryProvider(context)
72 if (!leakDirectoryProvider.hasStoragePermission()) {
73 Toast.makeText(
74 context,
75 R.string.leak_canary_options_menu_permission_toast,
76 Toast.LENGTH_LONG
77 )
78 .show()
79 leakDirectoryProvider.requestWritePermissionNotification()
80 } else {
81 Toast.makeText(
82 context,
83 R.string.leak_canary_generating_hq_bitmap_toast_notice,
84 Toast.LENGTH_LONG
85 )
86 .show()
87 executeOnIo {
88 val bitmap = HeapDumpRenderer.render(context, heapDumpFile, 2048, 0, 4)
89 @Suppress("DEPRECATION") val storageDir =
90 Environment.getExternalStoragePublicDirectory(DIRECTORY_DOWNLOADS)
91
92 val imageFile = File(storageDir, "${heapDumpFile.name}.png")
93 val saved = savePng(imageFile, bitmap)
94 if (saved) {
95 SharkLog.d { "Png saved at $imageFile" }
96 imageFile.setReadable(true, false)
97 val imageUri = LeakCanaryFileProvider.getUriForFile(
98 activity,
99 "com.squareup.leakcanary.fileprovider." + activity.packageName,
100 imageFile
101 )
102
103 updateUi {
104 val intent = Intent(Intent.ACTION_SEND)
105 intent.type = "image/png"
106 intent.putExtra(Intent.EXTRA_STREAM, imageUri)
107 activity.startActivity(
108 Intent.createChooser(
109 intent,
110 resources.getString(
111 R.string.leak_canary_share_heap_dump_bitmap_screen_title
112 )
113 )
114 )
115 }
116 } else {
117 updateUi {
118 Toast.makeText(
119 context,
120 R.string.leak_canary_generating_hq_bitmap_toast_failure_notice,
121 Toast.LENGTH_LONG
122 )
123 .show()
124 }
125 }
126 }
127 }
128 true
129 }
130 }
131 }
132
133 fun savePng(
134 imageFile: File,
135 source: Bitmap
136 ): Boolean {
137 var outStream: FileOutputStream? = null
138 return try {
139 outStream = imageFile.outputStream()
140 source.compress(Bitmap.CompressFormat.PNG, 100, outStream)
141 true
142 } catch (e: IOException) {
143 false
144 } finally {
145 outStream?.close()
146 }
147 }
148 }
149
150