Coverage Summary for Class: InAppPositionController (cloud.mindbox.mobile_sdk.inapp.presentation.view)
| Class |
Method, %
|
Branch, %
|
Line, %
|
Instruction, %
|
| InAppPositionController |
30%
(3/10)
|
32.1%
(9/28)
|
37.9%
(11/29)
|
38.6%
(66/171)
|
| InAppPositionController$fragmentLifecycleCallbacks$1 |
33.3%
(1/3)
|
0%
(0/4)
|
20%
(1/5)
|
14.3%
(2/14)
|
| InAppPositionController$repositionInApp$1 |
0%
(0/1)
|
0%
(0/24)
|
0%
(0/11)
|
0%
(0/99)
|
| InAppPositionController$start$1 |
0%
(0/1)
|
0%
(0/4)
|
0%
(0/13)
|
0%
(0/69)
|
| InAppPositionController$stop$1 |
0%
(0/1)
|
0%
(0/2)
|
0%
(0/8)
|
0%
(0/35)
|
| Total |
25%
(4/16)
|
14.5%
(9/62)
|
18.2%
(12/66)
|
17.5%
(68/388)
|
package cloud.mindbox.mobile_sdk.inapp.presentation.view
import android.app.Activity
import android.content.ContextWrapper
import android.view.View
import android.view.ViewGroup
import androidx.annotation.VisibleForTesting
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.FragmentManager
import cloud.mindbox.mobile_sdk.R
import cloud.mindbox.mobile_sdk.safeAs
import cloud.mindbox.mobile_sdk.utils.loggingRunCatching
internal class InAppPositionController {
private var inAppView: View? = null
private var originalParent: ViewGroup? = null
private var inAppOriginalIndex: Int = -1
private var hostActivity: FragmentActivity? = null
private val fragmentLifecycleCallbacks = object : FragmentManager.FragmentLifecycleCallbacks() {
override fun onFragmentResumed(fm: FragmentManager, f: Fragment) {
if (f is DialogFragment) {
repositionInApp()
}
}
override fun onFragmentStopped(fm: FragmentManager, f: Fragment) {
if (f is DialogFragment) {
repositionInApp()
}
}
}
fun start(entryView: View): Unit =
loggingRunCatching {
entryView.parent.safeAs<ViewGroup>()?.let { parent ->
this.originalParent = parent
this.inAppView = parent.findViewById(R.id.inapp_layout_container)
this.inAppOriginalIndex = parent.indexOfChild(inAppView)
}
entryView.findActivity().safeAs<FragmentActivity>()?.apply {
hostActivity = this
supportFragmentManager
.registerFragmentLifecycleCallbacks(
fragmentLifecycleCallbacks,
true
)
}
repositionInApp()
}
fun stop(): Unit = loggingRunCatching {
hostActivity?.apply {
supportFragmentManager
.unregisterFragmentLifecycleCallbacks(
fragmentLifecycleCallbacks
)
}
inAppView = null
originalParent = null
hostActivity = null
}
private fun repositionInApp(): Unit = loggingRunCatching {
val activity = hostActivity ?: return@loggingRunCatching
val topDialog = findTopDialogFragment(activity.supportFragmentManager)
val targetParent = topDialog?.dialog?.window?.decorView.safeAs<ViewGroup>()
if (targetParent != null) {
if (inAppView?.parent != targetParent) {
moveViewToTarget(inAppView, targetParent)
val currentFocus = originalParent?.findActivity()?.currentFocus
if (currentFocus != null && currentFocus != inAppView) {
currentFocus.clearFocus()
}
inAppView?.requestFocus()
}
} else {
repositionInappToOriginal()
}
}
private fun repositionInappToOriginal() {
val original = originalParent ?: return
if (inAppView?.parent == original) return
moveViewToTarget(inAppView, original, inAppOriginalIndex)
inAppView?.requestFocus()
}
private fun moveViewToTarget(view: View?, target: ViewGroup, index: Int = -1) {
if (view == null) return
view.parent.safeAs<ViewGroup>()?.removeView(view)
if (index != -1) {
target.addView(view, index)
} else {
target.addView(view)
}
}
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
internal fun findTopDialogFragment(fragmentManager: FragmentManager): DialogFragment? =
fragmentManager.allDialogFragments()
.lastOrNull { it.isAdded && it.dialog?.window != null }
private fun FragmentManager.allDialogFragments(): List<DialogFragment> =
fragments.flatMap { fragment ->
val self = if (fragment is DialogFragment) listOf(fragment) else emptyList()
val nested = if (fragment.isAdded) {
fragment.childFragmentManager.allDialogFragments()
} else {
emptyList()
}
self + nested
}
private fun View.findActivity(): Activity? {
var context = this.context
while (context is ContextWrapper) {
if (context is Activity) return context
context = context.baseContext
}
return null
}
}