/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ package com.facebook.react.views.progressbar import android.content.Context import android.util.Pair import android.view.View import android.widget.ProgressBar import com.facebook.common.logging.FLog import com.facebook.react.bridge.ReadableMap import com.facebook.react.common.ReactConstants import com.facebook.react.module.annotations.ReactModule import com.facebook.react.uimanager.BaseViewManager import com.facebook.react.uimanager.PixelUtil.toDIPFromPixel import com.facebook.react.uimanager.ThemedReactContext import com.facebook.react.uimanager.ViewManagerDelegate import com.facebook.react.uimanager.ViewProps import com.facebook.react.uimanager.annotations.ReactProp import com.facebook.react.viewmanagers.AndroidProgressBarManagerDelegate import com.facebook.react.viewmanagers.AndroidProgressBarManagerInterface import com.facebook.yoga.YogaMeasureMode import com.facebook.yoga.YogaMeasureOutput import java.util.WeakHashMap /** * Manages instances of [ProgressBar]. [ProgressBar] is wrapped in a [ProgressBarContainerView] * because the style of the [ProgressBar] can only be set in the constructor, whenever the style of * a [ProgressBar] changes, we have to drop the existing [ProgressBar] (if there is one) and create * a new one with the style given. */ @ReactModule(name = ReactProgressBarViewManager.REACT_CLASS) internal class ReactProgressBarViewManager : BaseViewManager(), AndroidProgressBarManagerInterface { private val measuredStyles = WeakHashMap>() private val delegate: ViewManagerDelegate = AndroidProgressBarManagerDelegate(this) override fun getName(): String = REACT_CLASS override fun createViewInstance(context: ThemedReactContext): ProgressBarContainerView { return ProgressBarContainerView(context) } @ReactProp(name = PROP_STYLE) override fun setStyleAttr(view: ProgressBarContainerView, styleName: String?) { view.setStyle(styleName) } @ReactProp(name = ViewProps.COLOR, customType = "Color") override fun setColor(view: ProgressBarContainerView, color: Int?) { view.color = color } @ReactProp(name = PROP_INDETERMINATE) override fun setIndeterminate(view: ProgressBarContainerView, indeterminate: Boolean) { view.indeterminate = indeterminate } @ReactProp(name = PROP_PROGRESS) override fun setProgress(view: ProgressBarContainerView, progress: Double) { view.progress = progress } @ReactProp(name = PROP_ANIMATING) override fun setAnimating(view: ProgressBarContainerView, animating: Boolean) { view.animating = animating } override fun setTestID(view: ProgressBarContainerView, value: String?) { super.setTestId(view, value) } @ReactProp(name = PROP_ATTR) override fun setTypeAttr(view: ProgressBarContainerView, value: String?): Unit = Unit override fun createShadowNodeInstance(): ProgressBarShadowNode = ProgressBarShadowNode() override fun getShadowNodeClass(): Class = ProgressBarShadowNode::class.java override fun updateExtraData(root: ProgressBarContainerView, extraData: Any) { // do nothing } override fun onAfterUpdateTransaction(view: ProgressBarContainerView) { view.apply() } override fun getDelegate(): ViewManagerDelegate = delegate override fun measure( context: Context, localData: ReadableMap, props: ReadableMap, state: ReadableMap, width: Float, widthMode: YogaMeasureMode, height: Float, heightMode: YogaMeasureMode, attachmentsPositions: FloatArray? ): Long { val style = getStyleFromString(props.getString(PROP_STYLE)) val value = measuredStyles.getOrPut(style) { val progressBar = createProgressBar(context, style) val spec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) progressBar.measure(spec, spec) Pair.create(progressBar.measuredWidth, progressBar.measuredHeight) } return YogaMeasureOutput.make( toDIPFromPixel(value.first.toFloat()), toDIPFromPixel(value.second.toFloat())) } companion object { const val REACT_CLASS: String = "AndroidProgressBar" const val PROP_STYLE: String = "styleAttr" const val PROP_ATTR: String = "typeAttr" const val PROP_INDETERMINATE: String = "indeterminate" const val PROP_PROGRESS: String = "progress" const val PROP_ANIMATING: String = "animating" const val DEFAULT_STYLE: String = "Normal" private val progressBarCtorLock = Any() /** * We create ProgressBars on both the UI and shadow threads. There is a race condition in the * ProgressBar constructor that may cause crashes when two ProgressBars are constructed at the * same time on two different threads. This static ctor wrapper protects against that. */ fun createProgressBar(context: Context?, style: Int): ProgressBar { synchronized(progressBarCtorLock) { return ProgressBar(context, null, style) } } internal fun getStyleFromString(styleStr: String?): Int { when (styleStr) { null -> { FLog.w(ReactConstants.TAG, "ProgressBar needs to have a style, null received") return android.R.attr.progressBarStyle } "Horizontal" -> return android.R.attr.progressBarStyleHorizontal "Small" -> return android.R.attr.progressBarStyleSmall "Large" -> return android.R.attr.progressBarStyleLarge "Inverse" -> return android.R.attr.progressBarStyleInverse "SmallInverse" -> return android.R.attr.progressBarStyleSmallInverse "LargeInverse" -> return android.R.attr.progressBarStyleLargeInverse "Normal" -> return android.R.attr.progressBarStyle else -> { FLog.w(ReactConstants.TAG, "Unknown ProgressBar style: $styleStr") return android.R.attr.progressBarStyle } } } } }