diff options
author | Hailey <me@haileyok.com> | 2024-08-07 14:45:06 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-08-07 14:45:06 -0700 |
commit | 1b02f81cb85333462e3a9a42accc05d09aca4f2c (patch) | |
tree | 766e80438c1f109a1a7d751e9f04b7f6242f9766 /modules/expo-bluesky-swiss-army/android | |
parent | fff2c079c2554861764974aaeeb56f79a25ba82a (diff) | |
download | voidsky-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')
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 + } + } +} |