react-native-morph-card 0.1.3 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -228,7 +228,8 @@ class MorphCardSourceView(context: Context) : ReactViewGroup(context) {
|
|
|
228
228
|
* Walk the view tree and hide any screen container that isn't already known.
|
|
229
229
|
* This catches modal screens added to separate ScreenStacks (e.g. transparentModal).
|
|
230
230
|
*/
|
|
231
|
-
private fun hideNewScreenContainers(root: ViewGroup, knownScreens: Set<View>) {
|
|
231
|
+
private fun hideNewScreenContainers(root: ViewGroup, knownScreens: Set<View>): Int {
|
|
232
|
+
var count = 0
|
|
232
233
|
fun walk(group: ViewGroup) {
|
|
233
234
|
val name = group.javaClass.name
|
|
234
235
|
if (name.contains("ScreenStack") || name.contains("ScreenContainer")) {
|
|
@@ -236,6 +237,7 @@ class MorphCardSourceView(context: Context) : ReactViewGroup(context) {
|
|
|
236
237
|
val child = group.getChildAt(i)
|
|
237
238
|
if (!knownScreens.contains(child) && child.visibility == View.VISIBLE) {
|
|
238
239
|
child.visibility = View.INVISIBLE
|
|
240
|
+
count++
|
|
239
241
|
Log.d(TAG, "preDraw: hid new screen ${child.javaClass.simpleName} in ${group.javaClass.simpleName}")
|
|
240
242
|
}
|
|
241
243
|
}
|
|
@@ -246,6 +248,7 @@ class MorphCardSourceView(context: Context) : ReactViewGroup(context) {
|
|
|
246
248
|
}
|
|
247
249
|
}
|
|
248
250
|
walk(root)
|
|
251
|
+
return count
|
|
249
252
|
}
|
|
250
253
|
|
|
251
254
|
/**
|
|
@@ -353,8 +356,16 @@ class MorphCardSourceView(context: Context) : ReactViewGroup(context) {
|
|
|
353
356
|
val knownScreens = collectExistingScreens(decorView)
|
|
354
357
|
Log.d(TAG, "prepareExpand: tracking ${knownScreens.size} existing screens")
|
|
355
358
|
val pdListener = ViewTreeObserver.OnPreDrawListener {
|
|
356
|
-
hideNewScreenContainers(decorView, knownScreens)
|
|
357
|
-
|
|
359
|
+
val hidCount = hideNewScreenContainers(decorView, knownScreens)
|
|
360
|
+
if (hidCount > 0) {
|
|
361
|
+
// Cancel this draw frame — the new screen was visible and we just hid it.
|
|
362
|
+
// Returning false prevents this frame from rendering, so the screen
|
|
363
|
+
// is never shown. The next frame will re-check and proceed.
|
|
364
|
+
Log.d(TAG, "preDraw: cancelled draw frame (hid $hidCount screens)")
|
|
365
|
+
false
|
|
366
|
+
} else {
|
|
367
|
+
true
|
|
368
|
+
}
|
|
358
369
|
}
|
|
359
370
|
decorView.viewTreeObserver.addOnPreDrawListener(pdListener)
|
|
360
371
|
preDrawListener = pdListener
|
|
@@ -362,16 +373,41 @@ class MorphCardSourceView(context: Context) : ReactViewGroup(context) {
|
|
|
362
373
|
// Capture snapshot
|
|
363
374
|
val cardImage = captureSnapshot()
|
|
364
375
|
|
|
365
|
-
// Create a full-screen overlay
|
|
366
|
-
//
|
|
367
|
-
//
|
|
376
|
+
// Create a full-screen overlay that blocks the modal target screen from
|
|
377
|
+
// being visible. We use PixelCopy to capture the current screen with
|
|
378
|
+
// hardware rendering preserved (clipToOutline, borderRadius, etc.).
|
|
368
379
|
val fullScreenOverlay = FrameLayout(context)
|
|
369
|
-
fullScreenOverlay.layoutParams = FrameLayout.LayoutParams(
|
|
370
|
-
FrameLayout.LayoutParams.MATCH_PARENT,
|
|
371
|
-
FrameLayout.LayoutParams.MATCH_PARENT
|
|
372
|
-
)
|
|
373
|
-
// Intercept all touches while overlay is up
|
|
380
|
+
fullScreenOverlay.layoutParams = FrameLayout.LayoutParams(decorView.width, decorView.height)
|
|
374
381
|
fullScreenOverlay.isClickable = true
|
|
382
|
+
// Ensure the overlay renders above any views with elevation (e.g. ScreenStack children)
|
|
383
|
+
fullScreenOverlay.translationZ = 1000f
|
|
384
|
+
|
|
385
|
+
// PixelCopy captures from the surface (hardware-rendered, preserves outlines).
|
|
386
|
+
// We use a background HandlerThread for the callback to avoid deadlocking main.
|
|
387
|
+
val window = (context as? android.app.Activity)?.window
|
|
388
|
+
if (window != null) {
|
|
389
|
+
val blockerBitmap = Bitmap.createBitmap(decorView.width, decorView.height, Bitmap.Config.ARGB_8888)
|
|
390
|
+
val copyThread = android.os.HandlerThread("PixelCopyThread")
|
|
391
|
+
copyThread.start()
|
|
392
|
+
val copyHandler = Handler(copyThread.looper)
|
|
393
|
+
val latch = java.util.concurrent.CountDownLatch(1)
|
|
394
|
+
android.view.PixelCopy.request(window, blockerBitmap, { result ->
|
|
395
|
+
Log.d(TAG, "prepareExpand: PixelCopy result=$result (0=SUCCESS)")
|
|
396
|
+
latch.countDown()
|
|
397
|
+
}, copyHandler)
|
|
398
|
+
// Wait for the copy (typically <5ms)
|
|
399
|
+
try { latch.await(100, java.util.concurrent.TimeUnit.MILLISECONDS) } catch (_: Exception) {}
|
|
400
|
+
copyThread.quitSafely()
|
|
401
|
+
|
|
402
|
+
val blockerImg = ImageView(context)
|
|
403
|
+
blockerImg.setImageBitmap(blockerBitmap)
|
|
404
|
+
blockerImg.scaleType = ImageView.ScaleType.FIT_XY
|
|
405
|
+
blockerImg.layoutParams = FrameLayout.LayoutParams(
|
|
406
|
+
FrameLayout.LayoutParams.MATCH_PARENT,
|
|
407
|
+
FrameLayout.LayoutParams.MATCH_PARENT
|
|
408
|
+
)
|
|
409
|
+
fullScreenOverlay.addView(blockerImg)
|
|
410
|
+
}
|
|
375
411
|
|
|
376
412
|
// Create card overlay at source position (on top of screen capture)
|
|
377
413
|
val bgColor = cardBgColor
|
|
@@ -526,11 +562,17 @@ class MorphCardSourceView(context: Context) : ReactViewGroup(context) {
|
|
|
526
562
|
}
|
|
527
563
|
|
|
528
564
|
// Crossfade: at 15% of animation, make target screen VISIBLE with alpha=0
|
|
529
|
-
// then fade alpha to 1 over 50% of duration
|
|
565
|
+
// then fade alpha to 1 over 50% of duration. Also remove the blocker image.
|
|
530
566
|
val targetScreen = targetScreenContainerRef?.get()
|
|
531
|
-
val
|
|
532
|
-
|
|
567
|
+
val sourceScreen2 = sourceScreenContainerRef?.get()
|
|
568
|
+
// Find the blocker image (first child of the full-screen overlay, before the card wrapper)
|
|
569
|
+
val blockerView = if (wrapper.childCount > 1) wrapper.getChildAt(0) else null
|
|
570
|
+
if (targetScreen != null && targetScreen !== sourceScreen2) {
|
|
533
571
|
mainHandler.postDelayed({
|
|
572
|
+
// Remove blocker — source screen is visible underneath
|
|
573
|
+
if (blockerView != null && blockerView.tag != "morphCardWrapper") {
|
|
574
|
+
(blockerView.parent as? ViewGroup)?.removeView(blockerView)
|
|
575
|
+
}
|
|
534
576
|
// Switch from INVISIBLE to VISIBLE but with alpha=0
|
|
535
577
|
targetScreen.alpha = 0f
|
|
536
578
|
targetScreen.visibility = View.VISIBLE
|
package/package.json
CHANGED