1 package leakcanary
2 
3 import android.animation.Animator
4 import android.animation.AnimatorListenerAdapter
5 import android.view.Gravity
6 import android.view.LayoutInflater
7 import android.view.View
8 import android.widget.Toast
9 import com.squareup.leakcanary.core.R
10 import java.util.concurrent.CountDownLatch
11 import java.util.concurrent.TimeUnit.SECONDS
12 import leakcanary.EventListener.Event
13 import leakcanary.EventListener.Event.DumpingHeap
14 import leakcanary.EventListener.Event.HeapDumpFailed
15 import leakcanary.EventListener.Event.HeapDump
16 import leakcanary.internal.InternalLeakCanary
17 import leakcanary.internal.friendly.mainHandler
18 
19 object ToastEventListener : EventListener {
20 
21   // Only accessed from the main thread
22   private var toastCurrentlyShown: Toast? = null
23 
onEventnull24   override fun onEvent(event: Event) {
25     when (event) {
26       is DumpingHeap -> {
27         showToastBlocking()
28       }
29       is HeapDump, is HeapDumpFailed -> {
30         mainHandler.post {
31           toastCurrentlyShown?.cancel()
32           toastCurrentlyShown = null
33         }
34       }
35     }
36   }
37 
38   @Suppress("DEPRECATION")
showToastBlockingnull39   private fun showToastBlocking() {
40     val appContext = InternalLeakCanary.application
41     val waitingForToast = CountDownLatch(1)
42     mainHandler.post(Runnable {
43       val resumedActivity = InternalLeakCanary.resumedActivity
44       if (resumedActivity == null || toastCurrentlyShown != null) {
45         waitingForToast.countDown()
46         return@Runnable
47       }
48       val toast = Toast(resumedActivity)
49       // Resources from application context: https://github.com/square/leakcanary/issues/2023
50       val iconSize = appContext.resources.getDimensionPixelSize(
51         R.dimen.leak_canary_toast_icon_size
52       )
53       toast.setGravity(Gravity.CENTER_VERTICAL, 0, -iconSize)
54       toast.duration = Toast.LENGTH_LONG
55       // Need an activity context because StrictMode added new stupid checks:
56       // https://github.com/square/leakcanary/issues/2153
57       val inflater = LayoutInflater.from(resumedActivity)
58       toast.view = inflater.inflate(R.layout.leak_canary_heap_dump_toast, null)
59       toast.show()
60 
61       val toastIcon = toast.view!!.findViewById<View>(R.id.leak_canary_toast_icon)
62       toastIcon.translationY = -iconSize.toFloat()
63       toastIcon
64         .animate()
65         .translationY(0f)
66         .setListener(object : AnimatorListenerAdapter() {
67           override fun onAnimationEnd(animation: Animator) {
68             toastCurrentlyShown = toast
69             waitingForToast.countDown()
70           }
71         })
72     })
73     waitingForToast.await(5, SECONDS)
74   }
75 }
76