about summary refs log tree commit diff
path: root/modules/expo-bluesky-swiss-army/android
diff options
context:
space:
mode:
authorHailey <me@haileyok.com>2024-08-07 14:45:06 -0700
committerGitHub <noreply@github.com>2024-08-07 14:45:06 -0700
commit1b02f81cb85333462e3a9a42accc05d09aca4f2c (patch)
tree766e80438c1f109a1a7d751e9f04b7f6242f9766 /modules/expo-bluesky-swiss-army/android
parentfff2c079c2554861764974aaeeb56f79a25ba82a (diff)
downloadvoidsky-1b02f81cb85333462e3a9a42accc05d09aca4f2c.tar.zst
[Video] Visibility detection view (#4741)
Co-authored-by: Samuel Newman <10959775+mozzius@users.noreply.github.com>
Diffstat (limited to 'modules/expo-bluesky-swiss-army/android')
-rw-r--r--modules/expo-bluesky-swiss-army/android/src/main/java/expo/modules/blueskyswissarmy/visibilityview/ExpoBlueskyVisibilityViewModule.kt23
-rw-r--r--modules/expo-bluesky-swiss-army/android/src/main/java/expo/modules/blueskyswissarmy/visibilityview/VisibilityView.kt63
-rw-r--r--modules/expo-bluesky-swiss-army/android/src/main/java/expo/modules/blueskyswissarmy/visibilityview/VisibilityViewManager.kt82
3 files changed, 168 insertions, 0 deletions
diff --git a/modules/expo-bluesky-swiss-army/android/src/main/java/expo/modules/blueskyswissarmy/visibilityview/ExpoBlueskyVisibilityViewModule.kt b/modules/expo-bluesky-swiss-army/android/src/main/java/expo/modules/blueskyswissarmy/visibilityview/ExpoBlueskyVisibilityViewModule.kt
new file mode 100644
index 000000000..ddbb05cde
--- /dev/null
+++ b/modules/expo-bluesky-swiss-army/android/src/main/java/expo/modules/blueskyswissarmy/visibilityview/ExpoBlueskyVisibilityViewModule.kt
@@ -0,0 +1,23 @@
+package expo.modules.blueskyswissarmy.visibilityview
+
+import expo.modules.kotlin.modules.Module
+import expo.modules.kotlin.modules.ModuleDefinition
+
+class ExpoBlueskyVisibilityViewModule : Module() {
+  override fun definition() =
+    ModuleDefinition {
+      Name("ExpoBlueskyVisibilityView")
+
+      AsyncFunction("updateActiveViewAsync") {
+        VisibilityViewManager.updateActiveView()
+      }
+
+      View(VisibilityView::class) {
+        Events(arrayOf("onChangeStatus"))
+
+        Prop("enabled") { view: VisibilityView, prop: Boolean ->
+          view.isViewEnabled = prop
+        }
+      }
+    }
+}
diff --git a/modules/expo-bluesky-swiss-army/android/src/main/java/expo/modules/blueskyswissarmy/visibilityview/VisibilityView.kt b/modules/expo-bluesky-swiss-army/android/src/main/java/expo/modules/blueskyswissarmy/visibilityview/VisibilityView.kt
new file mode 100644
index 000000000..a55ab80d5
--- /dev/null
+++ b/modules/expo-bluesky-swiss-army/android/src/main/java/expo/modules/blueskyswissarmy/visibilityview/VisibilityView.kt
@@ -0,0 +1,63 @@
+package expo.modules.blueskyswissarmy.visibilityview
+
+import android.content.Context
+import android.graphics.Rect
+import expo.modules.kotlin.AppContext
+import expo.modules.kotlin.viewevent.EventDispatcher
+import expo.modules.kotlin.views.ExpoView
+
+class VisibilityView(
+  context: Context,
+  appContext: AppContext,
+) : ExpoView(context, appContext) {
+  var isViewEnabled: Boolean = false
+
+  private val onChangeStatus by EventDispatcher()
+
+  private var isCurrentlyActive = false
+
+  override fun onAttachedToWindow() {
+    super.onAttachedToWindow()
+    VisibilityViewManager.addView(this)
+  }
+
+  override fun onDetachedFromWindow() {
+    super.onDetachedFromWindow()
+    VisibilityViewManager.removeView(this)
+  }
+
+  fun setIsCurrentlyActive(isActive: Boolean) {
+    if (isCurrentlyActive == isActive) {
+      return
+    }
+
+    this.isCurrentlyActive = isActive
+    this.onChangeStatus(
+      mapOf(
+        "isActive" to isActive,
+      ),
+    )
+  }
+
+  fun getPositionOnScreen(): Rect? {
+    if (!this.isShown) {
+      return null
+    }
+
+    val screenPosition = intArrayOf(0, 0)
+    this.getLocationInWindow(screenPosition)
+    return Rect(
+      screenPosition[0],
+      screenPosition[1],
+      screenPosition[0] + this.width,
+      screenPosition[1] + this.height,
+    )
+  }
+
+  fun isViewableEnough(): Boolean {
+    val positionOnScreen = this.getPositionOnScreen() ?: return false
+    val visibleArea = positionOnScreen.width() * positionOnScreen.height()
+    val totalArea = this.width * this.height
+    return visibleArea >= 0.5 * totalArea
+  }
+}
diff --git a/modules/expo-bluesky-swiss-army/android/src/main/java/expo/modules/blueskyswissarmy/visibilityview/VisibilityViewManager.kt b/modules/expo-bluesky-swiss-army/android/src/main/java/expo/modules/blueskyswissarmy/visibilityview/VisibilityViewManager.kt
new file mode 100644
index 000000000..ec1e49816
--- /dev/null
+++ b/modules/expo-bluesky-swiss-army/android/src/main/java/expo/modules/blueskyswissarmy/visibilityview/VisibilityViewManager.kt
@@ -0,0 +1,82 @@
+package expo.modules.blueskyswissarmy.visibilityview
+
+import android.graphics.Rect
+
+class VisibilityViewManager {
+  companion object {
+    private val views = HashMap<Int, VisibilityView>()
+    private var currentlyActiveView: VisibilityView? = null
+    private var prevCount = 0
+
+    fun addView(view: VisibilityView) {
+      this.views[view.id] = view
+
+      if (this.prevCount == 0) {
+        this.updateActiveView()
+      }
+      this.prevCount = this.views.count()
+    }
+
+    fun removeView(view: VisibilityView) {
+      this.views.remove(view.id)
+      this.prevCount = this.views.count()
+    }
+
+    fun updateActiveView() {
+      var activeView: VisibilityView? = null
+      val count = this.views.count()
+
+      if (count == 1) {
+        val view = this.views.values.first()
+        if (view.isViewableEnough()) {
+          activeView = view
+        }
+      } else if (count > 1) {
+        val views = this.views.values
+        var mostVisibleView: VisibilityView? = null
+        var mostVisiblePosition: Rect? = null
+
+        views.forEach { view ->
+          if (!view.isViewableEnough()) {
+            return
+          }
+
+          val position = view.getPositionOnScreen() ?: return@forEach
+          val topY = position.centerY() - (position.height() / 2)
+
+          if (topY >= 150) {
+            if (mostVisiblePosition == null) {
+              mostVisiblePosition = position
+            }
+
+            if (position.centerY() <= mostVisiblePosition!!.centerY()) {
+              mostVisibleView = view
+              mostVisiblePosition = position
+            }
+          }
+        }
+
+        activeView = mostVisibleView
+      }
+
+      if (activeView == this.currentlyActiveView) {
+        return
+      }
+
+      this.clearActiveView()
+      if (activeView != null) {
+        this.setActiveView(activeView)
+      }
+    }
+
+    private fun clearActiveView() {
+      this.currentlyActiveView?.setIsCurrentlyActive(false)
+      this.currentlyActiveView = null
+    }
+
+    private fun setActiveView(view: VisibilityView) {
+      view.setIsCurrentlyActive(true)
+      this.currentlyActiveView = view
+    }
+  }
+}