about summary refs log tree commit diff
path: root/modules/expo-bluesky-gif-view/android/src/main/java/expo/modules/blueskygifview/GifView.kt
diff options
context:
space:
mode:
Diffstat (limited to 'modules/expo-bluesky-gif-view/android/src/main/java/expo/modules/blueskygifview/GifView.kt')
-rw-r--r--modules/expo-bluesky-gif-view/android/src/main/java/expo/modules/blueskygifview/GifView.kt180
1 files changed, 180 insertions, 0 deletions
diff --git a/modules/expo-bluesky-gif-view/android/src/main/java/expo/modules/blueskygifview/GifView.kt b/modules/expo-bluesky-gif-view/android/src/main/java/expo/modules/blueskygifview/GifView.kt
new file mode 100644
index 000000000..be5830df7
--- /dev/null
+++ b/modules/expo-bluesky-gif-view/android/src/main/java/expo/modules/blueskygifview/GifView.kt
@@ -0,0 +1,180 @@
+package expo.modules.blueskygifview
+
+
+import android.content.Context
+import android.graphics.Color
+import android.graphics.drawable.Animatable
+import android.graphics.drawable.Drawable
+import com.bumptech.glide.Glide
+import com.bumptech.glide.load.engine.DiskCacheStrategy
+import com.bumptech.glide.load.engine.GlideException
+import com.bumptech.glide.request.RequestListener
+import com.bumptech.glide.request.target.Target
+import expo.modules.kotlin.AppContext
+import expo.modules.kotlin.exception.Exceptions
+import expo.modules.kotlin.viewevent.EventDispatcher
+import expo.modules.kotlin.views.ExpoView
+
+class GifView(context: Context, appContext: AppContext) : ExpoView(context, appContext) {
+  // Events
+  private val onPlayerStateChange by EventDispatcher()
+
+  // Glide
+  private val activity = appContext.currentActivity ?: throw Exceptions.MissingActivity()
+  private val glide = Glide.with(activity)
+  val imageView = AppCompatImageViewExtended(context, this)
+  var isPlaying = true
+  var isLoaded = false
+
+  // Requests
+  private var placeholderRequest: Target<Drawable>? = null
+  private var webpRequest: Target<Drawable>? = null
+
+  // Props
+  var placeholderSource: String? = null
+  var source: String? = null
+  var autoplay: Boolean = true
+    set(value) {
+      field = value
+
+      if (value) {
+        this.play()
+      } else {
+        this.pause()
+      }
+    }
+
+
+  //<editor-fold desc="Lifecycle">
+
+  init {
+    this.setBackgroundColor(Color.TRANSPARENT)
+
+    this.imageView.setBackgroundColor(Color.TRANSPARENT)
+    this.imageView.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
+
+    this.addView(this.imageView)
+  }
+
+  override fun onAttachedToWindow() {
+    if (this.imageView.drawable == null || this.imageView.drawable !is Animatable) {
+      this.load()
+    } else if (this.isPlaying) {
+      this.imageView.play()
+    }
+    super.onAttachedToWindow()
+  }
+
+  override fun onDetachedFromWindow() {
+    this.imageView.pause()
+    super.onDetachedFromWindow()
+  }
+
+  //</editor-fold>
+
+  //<editor-fold desc="Loading">
+
+  private fun load() {
+    if (placeholderSource == null || source == null) {
+      return
+    }
+
+    this.webpRequest = glide.load(source)
+      .diskCacheStrategy(DiskCacheStrategy.DATA)
+      .skipMemoryCache(false)
+      .listener(object: RequestListener<Drawable> {
+        override fun onResourceReady(
+          resource: Drawable?,
+          model: Any?,
+          target: Target<Drawable>?,
+          dataSource: com.bumptech.glide.load.DataSource?,
+          isFirstResource: Boolean
+        ): Boolean {
+          if (placeholderRequest != null) {
+            glide.clear(placeholderRequest)
+          }
+          return false
+        }
+
+        override fun onLoadFailed(
+          e: GlideException?,
+          model: Any?,
+          target: Target<Drawable>?,
+          isFirstResource: Boolean
+        ): Boolean {
+          return true
+        }
+      })
+      .into(this.imageView)
+
+    if (this.imageView.drawable == null || this.imageView.drawable !is Animatable) {
+      this.placeholderRequest = glide.load(placeholderSource)
+        .diskCacheStrategy(DiskCacheStrategy.DATA)
+        // Let's not bloat the memory cache with placeholders
+        .skipMemoryCache(true)
+        .listener(object: RequestListener<Drawable> {
+          override fun onResourceReady(
+            resource: Drawable?,
+            model: Any?,
+            target: Target<Drawable>?,
+            dataSource: com.bumptech.glide.load.DataSource?,
+            isFirstResource: Boolean
+          ): Boolean {
+            // Incase this request finishes after the webp, let's just not set
+            // the drawable. This shouldn't happen because the request should get cancelled
+            if (imageView.drawable == null) {
+              imageView.setImageDrawable(resource)
+            }
+            return true
+          }
+
+          override fun onLoadFailed(
+            e: GlideException?,
+            model: Any?,
+            target: Target<Drawable>?,
+            isFirstResource: Boolean
+          ): Boolean {
+            return true
+          }
+        })
+        .submit()
+    }
+  }
+
+  //</editor-fold>
+
+  //<editor-fold desc="Controls">
+
+  fun play() {
+    this.imageView.play()
+    this.isPlaying = true
+    this.firePlayerStateChange()
+  }
+
+  fun pause() {
+    this.imageView.pause()
+    this.isPlaying = false
+    this.firePlayerStateChange()
+  }
+
+  fun toggle() {
+    if (this.isPlaying) {
+      this.pause()
+    } else {
+      this.play()
+    }
+  }
+
+  //</editor-fold>
+
+  //<editor-fold desc="Util">
+
+  fun firePlayerStateChange() {
+    onPlayerStateChange(mapOf(
+      "isPlaying" to this.isPlaying,
+      "isLoaded" to this.isLoaded,
+    ))
+  }
+
+  //</editor-fold>
+}