@@ -7,33 +7,40 @@ import android.graphics.drawable.RippleDrawable
77import android.view.MotionEvent
88import android.webkit.URLUtil
99import androidx.appcompat.widget.AppCompatImageView
10+ import androidx.core.graphics.drawable.toDrawable
1011import androidx.core.graphics.toColorInt
11- import com.bumptech.glide.RequestManager
12- import com.bumptech.glide.load.DataSource
13- import com.bumptech.glide.load.engine.GlideException
14- import com.bumptech.glide.load.model.GlideUrl
15- import com.bumptech.glide.request.RequestListener
16- import com.bumptech.glide.request.target.Target
12+ import androidx.core.net.toUri
13+ import com.facebook.common.executors.UiThreadImmediateExecutorService
14+ import com.facebook.common.references.CloseableReference
15+ import com.facebook.datasource.BaseDataSubscriber
16+ import com.facebook.datasource.DataSource
17+ import com.facebook.drawee.backends.pipeline.Fresco
18+ import com.facebook.imagepipeline.image.CloseableBitmap
19+ import com.facebook.imagepipeline.image.CloseableImage
20+ import com.facebook.imagepipeline.request.ImageRequestBuilder
1721import com.facebook.react.bridge.ReadableMap
1822import com.facebook.react.bridge.WritableMap
1923import com.facebook.react.uimanager.ThemedReactContext
2024import com.facebook.react.uimanager.UIManagerHelper
25+ import com.facebook.react.views.imagehelper.ResourceDrawableIdHelper
2126import com.reactnativestripesdk.utils.createError
22- import com.reactnativestripesdk.utils.getDoubleOr
2327
2428@SuppressLint(" ViewConstructor" )
2529class AddToWalletButtonView (
2630 private val context : ThemedReactContext ,
27- private val requestManager : RequestManager ,
2831) : AppCompatImageView(context) {
2932 private var cardDetails: ReadableMap ? = null
3033 private var ephemeralKey: String? = null
3134 private var sourceMap: ReadableMap ? = null
3235 private var token: ReadableMap ? = null
3336
34- private var loadedSource: Any? = null
35- private var heightOverride: Int = 0
36- private var widthOverride: Int = 0
37+ private var loadedSource: String? = null
38+ private var currentDataSource: DataSource <CloseableReference <CloseableImage >>? = null
39+
40+ init {
41+ scaleType = ScaleType .CENTER_CROP
42+ clipToOutline = true
43+ }
3744
3845 override fun performClick (): Boolean {
3946 super .performClick()
@@ -79,86 +86,90 @@ class AddToWalletButtonView(
7986 }
8087
8188 fun onAfterUpdateTransaction () {
82- val sourceToLoad = getUrlOrResourceId( sourceMap)
83- if (sourceToLoad == null ) {
84- requestManager.clear( this )
89+ val uri = sourceMap?.getString( " uri " )
90+ if (uri == null ) {
91+ cancelCurrentRequest( )
8592 setImageDrawable(null )
8693 loadedSource = null
87- } else if (sourceToLoad != loadedSource || (heightOverride > 0 || widthOverride > 0 )) {
88- loadedSource = sourceToLoad
89- val scale = sourceMap.getDoubleOr(" scale" , 1.0 )
90-
91- requestManager
92- .load(sourceToLoad)
93- .addListener(
94- object : RequestListener <Drawable > {
95- override fun onLoadFailed (
96- e : GlideException ? ,
97- model : Any? ,
98- target : Target <Drawable >? ,
99- isFirstResource : Boolean ,
100- ): Boolean {
101- dispatchEvent(
102- createError(" Failed" , " Failed to load the source from $sourceToLoad " ),
103- )
104- return true
105- }
106-
107- override fun onResourceReady (
108- resource : Drawable ? ,
109- model : Any? ,
110- target : Target <Drawable >? ,
111- dataSource : DataSource ? ,
112- isFirstResource : Boolean ,
113- ): Boolean {
114- setImageDrawable(
115- RippleDrawable (
116- ColorStateList .valueOf(" #e0e0e0" .toColorInt()),
117- resource,
118- null ,
119- ),
120- )
121- return true
122- }
123- },
124- ).centerCrop()
125- .override ((widthOverride * scale).toInt(), (heightOverride * scale).toInt())
126- .into(this )
94+ return
12795 }
128- }
12996
130- private fun getUrlOrResourceId ( sourceMap : ReadableMap ? ): Any? {
131- sourceMap?.getString( " uri " )?. let {
132- return if (URLUtil .isValidUrl(it )) {
133- // Debug mode, Image.resolveAssetSource resolves to local http:// URL
134- GlideUrl (it )
97+ if (uri != loadedSource) {
98+ loadedSource = uri
99+ if (URLUtil .isValidUrl(uri )) {
100+ // Debug mode: Image.resolveAssetSource resolves to local http:// URL
101+ loadImageFromUrl(uri )
135102 } else {
136- // Release mode, Image.resolveAssetSource resolves to a drawable resource
137- @SuppressLint(" DiscouragedApi" )
138- context.resources.getIdentifier(it, " drawable" , context.packageName)
103+ // Release mode: Image.resolveAssetSource resolves to a drawable resource name
104+ loadImageFromDrawable(uri)
139105 }
140106 }
141- return null
142107 }
143108
144- override fun onSizeChanged (
145- w : Int ,
146- h : Int ,
147- oldw : Int ,
148- oldh : Int ,
149- ) {
150- super .onSizeChanged(w, h, oldw, oldh)
151- if (w > 0 && h > 0 ) {
152- heightOverride = h
153- widthOverride = w
154- onAfterUpdateTransaction()
155- heightOverride = 0
156- widthOverride = 0
109+ private fun loadImageFromUrl (url : String ) {
110+ cancelCurrentRequest()
111+
112+ val imageRequest =
113+ ImageRequestBuilder
114+ .newBuilderWithSource(url.toUri())
115+ .build()
116+
117+ val dataSource = Fresco .getImagePipeline().fetchDecodedImage(imageRequest, context)
118+ currentDataSource = dataSource
119+
120+ dataSource.subscribe(
121+ object : BaseDataSubscriber <CloseableReference <CloseableImage >>() {
122+ override fun onNewResultImpl (dataSource : DataSource <CloseableReference <CloseableImage >>) {
123+ if (! dataSource.isFinished) return
124+ val imageRef = dataSource.result ? : return
125+
126+ try {
127+ val image = imageRef.get()
128+ if (image is CloseableBitmap ) {
129+ val drawable = image.underlyingBitmap.toDrawable(resources)
130+ setImageWithRipple(drawable)
131+ }
132+ } finally {
133+ CloseableReference .closeSafely(imageRef)
134+ }
135+ }
136+
137+ override fun onFailureImpl (dataSource : DataSource <CloseableReference <CloseableImage >>) {
138+ dispatchEvent(createError(" Failed" , " Failed to load the source from $url " ))
139+ }
140+ },
141+ UiThreadImmediateExecutorService .getInstance(),
142+ )
143+ }
144+
145+ private fun loadImageFromDrawable (resourceName : String ) {
146+ // Instance is deprecated, but have to use until we drop support for RN 0.79
147+ @Suppress(" DEPRECATION" )
148+ val drawable = ResourceDrawableIdHelper .instance.getResourceDrawable(context, resourceName)
149+ if (drawable != null ) {
150+ setImageWithRipple(drawable)
151+ } else {
152+ dispatchEvent(createError(" Failed" , " Failed to load drawable resource: $resourceName " ))
157153 }
158154 }
159155
156+ private fun setImageWithRipple (drawable : Drawable ) {
157+ setImageDrawable(
158+ RippleDrawable (
159+ ColorStateList .valueOf(" #e0e0e0" .toColorInt()),
160+ drawable,
161+ null ,
162+ ),
163+ )
164+ }
165+
166+ private fun cancelCurrentRequest () {
167+ currentDataSource?.close()
168+ currentDataSource = null
169+ }
170+
160171 fun onDropViewInstance () {
161- requestManager.clear( this )
172+ cancelCurrentRequest( )
162173 }
163174
164175 fun setSourceMap (map : ReadableMap ? ) {
0 commit comments