All Downloads are FREE. Search and download functionalities are using the official Maven repository.

androidMain.tech.skot.core.components.SKActivity.kt Maven / Gradle / Ivy

There is a newer version: 1.2.9
Show newest version
package tech.skot.core.components

import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import android.os.PersistableBundle
import android.util.TypedValue
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.launch
import tech.skot.core.SKFeatureInitializer
import tech.skot.core.SKLog
import tech.skot.core.toSKUri
import tech.skot.view.SKPermissionAndroid
import tech.skot.view.SKPermissionsRequestResultAndroid
import tech.skot.view.extensions.updatePadding

abstract class SKActivity : AppCompatActivity() {

    var screenKey: Long? = null

    companion object {
        private var oneActivityAlreadyLaunched = false
        var launchActivityClass: Class? = null
    }


    override fun onNewIntent(intent: Intent?) {
        super.onNewIntent(intent)
        if (launchActivityClass?.isInstance(this) == true) {
            featureInitializer.resetToRoot()
            intent?.data?.toSKUri()?.let { featureInitializer.onDeepLink?.invoke(it, false) }
        }
    }

    var screen: SKScreenView<*>? = null

    abstract val featureInitializer: SKFeatureInitializer

    var statusBarColor: Int = 0
    var themeWindowLightStatusBar: Boolean = true
    var requestedStatusBarColor: Int? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            val typedValue = TypedValue()
            theme.resolveAttribute(android.R.attr.windowLightStatusBar, typedValue, true)
            themeWindowLightStatusBar = typedValue.data != 0
        }

        lifecycleScope.launch {

            featureInitializer.initializeIfNeeded(intent?.data?.toSKUri())

            val viewKey = getKeyForThisActivity(savedInstanceState)

            if (SKRootStackViewProxy.stateLD.value.screens.isEmpty()) {
                featureInitializer.start()
            }

            if (!oneActivityAlreadyLaunched && viewKey != -1L) {
                oneActivityAlreadyLaunched = true
                SKRootStackViewProxy.stateLD.value.screens.getOrNull(0)?.let {
                    startActivityForProxy(it)
                }
                finish()
            } else {
                val screenProxy =
                    if (viewKey != -1L) {
                        ScreensManager.getInstance(viewKey)
                            ?: SKRootStackViewProxy.stateLD.value.screens.getOrNull(0)
                    } else {
                        SKRootStackViewProxy.stateLD.value.screens.getOrNull(0)
                    } as? SKScreenViewProxy<*>

                oneActivityAlreadyLaunched = true


                screenProxy?.run {
                    screenKey = key
                    bindTo(this@SKActivity, null, layoutInflater)
                }
                    ?.run {
                        setContentView(this.view)
                        screen = this
                        linkToRootStack()
                        linkToCustoms()
                        if (resumedWithoutScreen) {
                            resumedWithoutScreen = false
                            onResume()
                        }
                    }
            }
        }

        statusBarColor = window.statusBarColor
    }

    private var loadingInsetsCounter: Long = 0L


    fun setFullScreen(
        fullScreen: Boolean,
        lightStatusBar: Boolean?,
        onWindowInsets: ((windowInsets: WindowInsetsCompat) -> Unit)? = null
    ) {

        screen?.view?.let {
            WindowInsetsControllerCompat(window, it).isAppearanceLightStatusBars =
                lightStatusBar ?: themeWindowLightStatusBar
            WindowCompat.setDecorFitsSystemWindows(window, !fullScreen)
            loadingInsetsCounter++
            val loadedInsets = ViewCompat.getRootWindowInsets(it)
            if (loadedInsets != null) {
                it.updatePadding(
                    bottom = if (fullScreen) loadedInsets.getInsets(
                        WindowInsetsCompat.Type.systemBars()
                    ).bottom else 0
                )
                onWindowInsets?.invoke(loadedInsets)
                it.requestApplyInsets()
            } else {
                val loadingIndex = loadingInsetsCounter
                ViewCompat.setOnApplyWindowInsetsListener(it) { view, windowInsets ->
                    if (loadingInsetsCounter == loadingIndex) {
                        ViewCompat.setOnApplyWindowInsetsListener(it, null)
                        it.updatePadding(
                            bottom = if (fullScreen) windowInsets.getInsets(
                                WindowInsetsCompat.Type.systemBars()
                            ).bottom else 0
                        )
                    }
                    onWindowInsets?.invoke(windowInsets)
                    it.requestApplyInsets()
                    windowInsets
                }
            }
        }

    }


    private fun getKeyForThisActivity(savedInstanceState: Bundle?) =
        when {
            intent.hasExtra(ScreensManager.SK_EXTRA_VIEW_KEY) -> {
                intent.getLongExtra(
                    ScreensManager.SK_EXTRA_VIEW_KEY,
                    -1
                )
            }
            savedInstanceState?.containsKey(ScreensManager.SK_EXTRA_VIEW_KEY) == true -> {
                savedInstanceState.getLong(
                    ScreensManager.SK_EXTRA_VIEW_KEY,
                    -1
                )
            }
            else -> -1
        }


    private var resumedWithoutScreen = false

    override fun onResume() {
        super.onResume()
        screen.let {
            if (it == null) {
                resumedWithoutScreen = true
            } else {
                it.onResume()
            }
        }
    }

    override fun onPause() {
        super.onPause()
        screen?.onPause()
    }

    override fun onDestroy() {
        screenKey?.let { ScreensManager.getInstance(it) }?.apply {
            saveState()
        }
        super.onDestroy()
    }

    override fun onSaveInstanceState(outState: Bundle, outPersistentState: PersistableBundle) {
        super.onSaveInstanceState(outState, outPersistentState)
        screenKey?.let { outState.putLong(ScreensManager.SK_EXTRA_VIEW_KEY, it) }
    }

    private var finishedAsked = false

    private fun linkToRootStack() {

        SKRootStackViewProxy.stateLD.observe(this) { state ->
            if (!finishedAsked) {
                val thisScreenPosition = state.screens.indexOfFirst {
                    it.key == screenKey
                }
                val isTaskRootBeforeFinish = isTaskRoot
                if (thisScreenPosition == -1) {
                    finishedAsked = true
                    finish()
                    state.transition?.let { overridePendingTransition(it.enterAnim, it.exitAnim) }
                } else {
                    if (state.screens.size > thisScreenPosition + 1) {
                        startActivityForProxy(state.screens.get(thisScreenPosition + 1))
                        state.transition?.let {
                            overridePendingTransition(
                                it.enterAnim,
                                it.exitAnim
                            )
                        }
                    }
                }

                if (finishedAsked && isTaskRootBeforeFinish) {
                    state.screens.firstOrNull()?.let { newScreen ->
                        val launchClass = launchActivityClass
                            ?: throw IllegalStateException("You have to set SKActivity.launchActivityClass to be allowed to change root Screen. New root screen will be loaded in this activity even if you have redefined getActivityClas method")
                        startActivity(Intent(this@SKActivity, launchClass).apply {
                            flags = flags.or(Intent.FLAG_ACTIVITY_NEW_TASK)
                            putExtra(ScreensManager.SK_EXTRA_VIEW_KEY, newScreen.key)
                        })
                    }
                }

            }


        }

    }

    /**
     * override this method to register to messages or LiveData
     * main use is globally, for all activities, for platform specific work (sdk)
     */
    protected open fun linkToCustoms() {

    }

    private fun startActivityForProxy(proxy: SKScreenViewProxy<*>) {
        startActivity(Intent(this, proxy.getActivityClass()).apply {
            putExtra(ScreensManager.SK_EXTRA_VIEW_KEY, proxy.key)
        })
    }


    //Pas de back par défaut, il faut faire attention avec ça
    override fun onBackPressed() {
        ScreensManager.backPressed.post(Unit)
    }


    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array,
        grantResults: IntArray
    ) {
        ScreensManager.permissionsResults.post(
            SKPermissionsRequestResultAndroid(
                requestCode = requestCode,
                grantedPermissions = permissions.filterIndexed { index, _ ->
                    grantResults[index] == PackageManager.PERMISSION_GRANTED
                }.map { SKPermissionAndroid(it) }
            )

        )
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy