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
     }
 }