1 package expo.modules.image
2 
3 import android.util.Log
4 import com.bumptech.glide.Glide
5 import com.bumptech.glide.load.model.GlideUrl
6 import com.facebook.react.bridge.Promise
7 import com.facebook.react.bridge.ReactApplicationContext
8 import com.facebook.react.bridge.ReactContextBaseJavaModule
9 import com.facebook.react.bridge.ReactMethod
10 import kotlinx.coroutines.CoroutineScope
11 import kotlinx.coroutines.Dispatchers
12 import kotlinx.coroutines.cancel
13 import kotlinx.coroutines.launch
14 import java.lang.Exception
15 import java.lang.IllegalStateException
16 import java.util.concurrent.CancellationException
17 
18 class ExpoImageModule(val context: ReactApplicationContext) : ReactContextBaseJavaModule(context) {
19   private val moduleCoroutineScope = CoroutineScope(Dispatchers.IO)
20   override fun getName() = "ExpoImageModule"
21 
22   @ReactMethod
23   fun prefetch(url: String, promise: Promise) {
24     moduleCoroutineScope.launch {
25       try {
26         val glideUrl = GlideUrl(url)
27         val result = Glide.with(context)
28             .download(glideUrl)
29             .submit()
30             .awaitGet()
31         if (result != null) {
32           promise.resolve(null)
33         } else {
34           promise.reject("ERR_IMAGE_PREFETCH_FAILURE", "Failed to prefetch the image: ${url}.")
35         }
36       } catch (e: Exception) {
37         promise.reject("ERR_IMAGE_PREFETCH_FAILURE", "Failed to prefetch the image: ${e.message}", e)
38       }
39     }
40   }
41 
42   override fun onCatalystInstanceDestroy() {
43     try {
44       // TODO: Use [expo.modules.core.errors.ModuleDestroyedException] when migrated to Expo Module
45       moduleCoroutineScope.cancel(CancellationException("ExpoImage module is destroyed. Cancelling all jobs."))
46     } catch (e: IllegalStateException) {
47       Log.w("ExpoImageModule", "No coroutines to cancel")
48     }
49 
50     super.onCatalystInstanceDestroy()
51   }
52 }
53