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