<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