@@ -13,8 +13,6 @@ import android.graphics.Color
1313import android.graphics.Typeface
1414import android.graphics.drawable.ColorDrawable
1515import android.graphics.drawable.GradientDrawable
16- import android.os.Handler
17- import android.os.Looper
1816import android.view.Gravity
1917import android.view.Window
2018import android.view.WindowManager
@@ -26,186 +24,88 @@ import com.facebook.react.R
2624import com.facebook.react.devsupport.interfaces.TracingState
2725import com.facebook.react.uimanager.DisplayMetricsHolder
2826import com.facebook.react.uimanager.PixelUtil
29- import java.util.Locale
3027
3128internal class PerfMonitorOverlayView (
3229 private val context : Context ,
33- private val onButtonPress : () -> Unit ,
3430) {
35- private var hidden: Boolean = true
36- private var hasEventData: Boolean = false
37- private val metricsDialog: Dialog
38- private val toolbarDialog: Dialog
39- private val tooltipDialog: Dialog
40- private lateinit var buttonLabel: TextView
41- private lateinit var recordingStateLabel: TextView
42- private lateinit var durationLabel: TextView
31+ private val dialog: Dialog
32+ private lateinit var statusLabel: TextView
4333 private lateinit var tooltipLabel: TextView
44- private var ttl: Int = 0
45- private var hideAfterTimeoutHandler: Handler ? = null
34+ private lateinit var statusIndicator: TextView
4635
4736 init {
4837 DisplayMetricsHolder .initDisplayMetricsIfNotInitialized(context)
49- tooltipDialog = createTooltipDialog()
50- metricsDialog = createMetricsDialog()
51- toolbarDialog = createToolbarDialog()
38+ dialog = createToolbarDialog()
5239 }
5340
5441 fun show () {
55- toolbarDialog.show()
56- tooltipDialog.show()
57- if (hasEventData) {
58- toolbarDialog.window?.decorView?.post { updateMetricsDialogPosition() }
59- metricsDialog.show()
60- }
61- hidden = false
42+ dialog.show()
6243 }
6344
6445 fun hide () {
65- metricsDialog.hide()
66- toolbarDialog.hide()
67- tooltipDialog.hide()
68- hidden = true
69- }
70-
71- fun resetState () {
72- hasEventData = false
73- metricsDialog.hide()
74- }
75-
76- fun updateFocusedEvent (data : PerfMonitorUpdateListener .LongTaskEventData ) {
77- durationLabel.text = String .format(Locale .US , " %d ms" , data.durationMs)
78- durationLabel.setTextColor(getDurationHighlightColor(data.responsivenessScore))
79- hasEventData = true
80- ttl = data.ttl
81-
82- hideAfterTimeoutHandler?.removeCallbacksAndMessages(null )
83-
84- if (! hidden) {
85- metricsDialog.show()
86-
87- // Schedule hiding metrics overlay after ttl milliseconds
88- if (ttl > 0 ) {
89- if (hideAfterTimeoutHandler == null ) {
90- hideAfterTimeoutHandler = Handler (Looper .getMainLooper())
91- }
92- hideAfterTimeoutHandler?.postDelayed({ metricsDialog.hide() }, ttl.toLong())
93- }
94- }
46+ dialog.hide()
9547 }
9648
9749 fun updateRecordingState (state : TracingState ) {
98- recordingStateLabel.text =
99- when (state) {
100- TracingState .ENABLEDINBACKGROUNDMODE -> " Profiling: ON"
101- TracingState .DISABLED -> " Profiling: OFF"
102- TracingState .ENABLEDINCDPMODE -> " Profiling: DISABLED"
103- }
104- buttonLabel.text =
105- when (state) {
106- TracingState .ENABLEDINBACKGROUNDMODE -> " Open ↗️"
107- TracingState .DISABLED -> " Start"
108- TracingState .ENABLEDINCDPMODE -> " "
109- }
110- tooltipLabel.text =
111- when (state) {
112- TracingState .ENABLEDINBACKGROUNDMODE -> " Dev Menu > Finish performance trace"
113- TracingState .DISABLED -> " Dev Menu > Start performance trace"
114- TracingState .ENABLEDINCDPMODE -> " "
115- }
11650 if (state == TracingState .ENABLEDINCDPMODE ) {
117- tooltipDialog.hide()
118- } else {
119- tooltipDialog.show()
51+ dialog.hide()
52+ return
12053 }
12154
122- toolbarDialog.window?.decorView?.post { updateMetricsDialogPosition() }
55+ if (state == TracingState .ENABLEDINBACKGROUNDMODE ) {
56+ (statusIndicator.background as GradientDrawable ).setColor(Color .RED )
57+ statusLabel.text = " Background Profiling Active"
58+ tooltipLabel.text = " Press ☰ to open"
59+ } else {
60+ (statusIndicator.background as GradientDrawable ).setColor(Color .GRAY )
61+ statusLabel.text = " Background Profiling Stopped"
62+ tooltipLabel.text = " Press ☰ to restart"
63+ }
64+ dialog.show()
12365 }
12466
125- private fun createMetricsDialog (): Dialog {
126- val containerLayout = createInnerLayout()
127- val longTaskLabel =
128- TextView (context).apply {
129- textSize = TEXT_SIZE_PRIMARY
130- text = " Long Task"
131- setTextColor(Color .WHITE )
132- typeface = TYPEFACE_BOLD
133- }
134- durationLabel =
67+ private fun createToolbarDialog (): Dialog {
68+ statusIndicator =
13569 TextView (context).apply {
136- textSize = TEXT_SIZE_PRIMARY
137- setTextColor(COLOR_TEXT_GREEN )
138- typeface = TYPEFACE_BOLD
70+ width = dpToPx(12f ).toInt()
71+ height = dpToPx(12f ).toInt()
72+ background =
73+ GradientDrawable ().apply {
74+ shape = GradientDrawable .OVAL
75+ setColor(Color .RED )
76+ }
13977 }
140- containerLayout.addView(longTaskLabel)
141- containerLayout.addView(durationLabel)
14278
143- val dialog =
144- createAnchoredDialog(getMetricsDialogOffsetX(), dpToPx(16f )).apply {
145- setContentView(containerLayout)
79+ val textContainer =
80+ LinearLayout (context).apply {
81+ orientation = LinearLayout .VERTICAL
82+ layoutParams =
83+ LinearLayout .LayoutParams (
84+ LinearLayout .LayoutParams .WRAP_CONTENT ,
85+ LinearLayout .LayoutParams .WRAP_CONTENT ,
86+ )
14687 }
147- dialog.window?.apply {
148- attributes =
149- attributes?.apply {
150- flags =
151- flags or
152- WindowManager .LayoutParams .FLAG_NOT_TOUCHABLE or
153- WindowManager .LayoutParams .FLAG_NOT_FOCUSABLE
154- }
155- }
156-
157- return dialog
158- }
159-
160- private fun createToolbarDialog (): Dialog {
161- val buttonInner = createInnerLayout()
162- recordingStateLabel =
88+ statusLabel =
16389 TextView (context).apply {
16490 textSize = TEXT_SIZE_PRIMARY
16591 setTextColor(Color .WHITE )
16692 typeface = TYPEFACE_BOLD
16793 }
168- buttonInner.addView(recordingStateLabel)
169- buttonLabel =
170- TextView (context).apply {
171- textSize = TEXT_SIZE_PRIMARY
172- setTextColor(COLOR_TEXT_BLUE )
173- typeface = TYPEFACE_BOLD
174- }
175- buttonInner.addView(buttonLabel)
176- val buttonView =
177- LinearLayout (context).apply {
178- orientation = LinearLayout .VERTICAL
179- setPadding(
180- dpToPx(8f ).toInt(),
181- dpToPx(16f ).toInt(),
182- dpToPx(16f ).toInt(),
183- dpToPx(8f ).toInt(),
184- )
185- addView(buttonInner)
186- setOnClickListener { onButtonPress() }
187- }
188-
189- val dialog = createAnchoredDialog(dpToPx(0f ), dpToPx(0f )).apply { setContentView(buttonView) }
190- dialog.window?.apply {
191- attributes =
192- attributes?.apply { flags = flags or WindowManager .LayoutParams .FLAG_NOT_FOCUSABLE }
193- }
194-
195- return dialog
196- }
197-
198- private fun createTooltipDialog (): Dialog {
199- val containerLayout = createInnerLayout()
20094 tooltipLabel =
20195 TextView (context).apply {
20296 textSize = TEXT_SIZE_ACCESSORY
20397 setTextColor(Color .WHITE )
98+ typeface = TYPEFACE_BOLD
20499 }
205- containerLayout.addView(tooltipLabel)
100+ textContainer.addView(statusLabel)
101+ textContainer.addView(tooltipLabel)
102+
103+ val containerLayout = createInnerLayout()
104+ containerLayout.addView(statusIndicator)
105+ containerLayout.addView(textContainer)
206106
207107 val dialog =
208- createAnchoredDialog(dpToPx(16f ), dpToPx(52f )).apply { setContentView(containerLayout) }
108+ createAnchoredDialog(dpToPx(12f ), dpToPx(12f )).apply { setContentView(containerLayout) }
209109 dialog.window?.apply {
210110 attributes =
211111 attributes?.apply {
@@ -278,35 +178,12 @@ internal class PerfMonitorOverlayView(
278178 }
279179 }
280180
281- private fun getMetricsDialogOffsetX (): Float {
282- val toolbarWidth = toolbarDialog?.window?.decorView?.width ? : 0
283- return toolbarWidth.toFloat()
284- }
285-
286- private fun updateMetricsDialogPosition () {
287- metricsDialog?.window?.apply {
288- attributes = attributes?.apply { x = getMetricsDialogOffsetX().toInt() }
289- }
290- }
291-
292- private fun getDurationHighlightColor (responsivenessScore : Int ): Int {
293- return when (responsivenessScore) {
294- 2 -> COLOR_TEXT_RED
295- 1 -> COLOR_TEXT_YELLOW
296- else -> COLOR_TEXT_GREEN
297- }
298- }
299-
300181 private fun dpToPx (dp : Float ): Float = PixelUtil .toPixelFromDIP(dp)
301182
302183 companion object {
303- private val COLOR_TEXT_GREEN = Color .parseColor(" #4AEB2F" )
304- private val COLOR_TEXT_YELLOW = Color .parseColor(" #FFAA00" )
305- private val COLOR_TEXT_RED = Color .parseColor(" #FF0000" )
306- private val COLOR_TEXT_BLUE = Color .parseColor(" #00B0FF" )
307184 private val COLOR_OVERLAY_BORDER = Color .parseColor(" #6C6C6C" )
308- private val TEXT_SIZE_PRIMARY = 13f
309- private val TEXT_SIZE_ACCESSORY = 9f
185+ private val TEXT_SIZE_PRIMARY = 12f
186+ private val TEXT_SIZE_ACCESSORY = 10f
310187 private val TYPEFACE_BOLD = Typeface .create(" sans-serif" , Typeface .BOLD )
311188 }
312189}
0 commit comments