package com.margelo.nitro.progressandmodal import android.app.Activity import android.graphics.drawable.GradientDrawable import android.view.Gravity import android.view.ViewGroup import android.widget.FrameLayout import android.widget.LinearLayout import android.widget.TextView import androidx.core.graphics.toColorInt import com.airbnb.lottie.LottieAnimationView import com.airbnb.lottie.LottieDrawable class PAMLottieManager(private val getActivity: () -> Activity?) { private val lottieViewTag = 9998 private var backgroundColor: String = "#29000000" fun setConfig(backgroundColor: String?) { backgroundColor?.let { this.backgroundColor = it } } fun showLottie(params: PAM_LottieLoadingParams) { val activity = getActivity() ?: return activity.runOnUiThread { val rootView = activity.window.decorView as ViewGroup // Remove existing lottie view if present rootView.findViewWithTag(lottieViewTag)?.let { rootView.removeView(it) } val lottieView = FrameLayout(activity).apply { tag = lottieViewTag setBackgroundColor(backgroundColor.toColorInt()) isClickable = true isFocusable = true layoutParams = FrameLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT ).apply { gravity = Gravity.CENTER } } val containerView = buildContainerView(activity, params) lottieView.addView(containerView) rootView.addView(lottieView) ViewUtils.fadeIn(lottieView) } } fun hideLottie(params: PAM_LottieLoadingParams) { val activity = getActivity() ?: return activity.runOnUiThread { val rootView = activity.window.decorView as ViewGroup rootView.findViewWithTag(lottieViewTag)?.let { lottieView -> val containerView = lottieView.getChildAt(0) as? LinearLayout ?: return@let // Update title / subtitle text params.title?.let { containerView.findViewWithTag("title")?.text = it } params.subtitle?.let { containerView.findViewWithTag("subtitle")?.text = it } // Update animation — wait until composition is ready before playing containerView.findViewWithTag("animation")?.let { animView -> loadLottieAnimation(params.lottie.uri, animView) { animView.repeatCount = 0 animView.speed = 1f animView.removeAllAnimatorListeners() animView.addAnimatorListener(object : android.animation.Animator.AnimatorListener { override fun onAnimationStart(a: android.animation.Animator) {} override fun onAnimationEnd(a: android.animation.Animator) { ViewUtils.fadeOut(lottieView) { rootView.removeView(lottieView) } } override fun onAnimationCancel(a: android.animation.Animator) {} override fun onAnimationRepeat(a: android.animation.Animator) {} }) animView.playAnimation() } } } } } private fun buildContainerView(activity: Activity, params: PAM_LottieLoadingParams): LinearLayout { val dp = { v: Float -> ViewUtils.dpToPx(activity, v).toInt() } val container = LinearLayout(activity).apply { orientation = LinearLayout.VERTICAL background = GradientDrawable().apply { cornerRadius = ViewUtils.dpToPx(activity, 12f) setColor(PAMColors.CONTAINER_BG.toColorInt()) } layoutParams = FrameLayout.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT ).apply { gravity = Gravity.CENTER } setPadding(dp(16f), dp(16f), dp(16f), dp(16f)) } // Lottie animation view val animView = LottieAnimationView(activity).apply { tag = "animation" repeatCount = LottieDrawable.INFINITE speed = 2f layoutParams = LinearLayout.LayoutParams(728, 288).apply { gravity = Gravity.CENTER_HORIZONTAL topMargin = dp(8f) } } loadLottieAnimation(params.lottie.uri, animView) { animView.playAnimation() } container.addView(animView) // Title if (params.title != null) { container.addView(TextView(activity).apply { tag = "title" text = params.title textSize = 20f setTextColor(PAMColors.TITLE_COLOR.toColorInt()) gravity = Gravity.CENTER layoutParams = LinearLayout.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT ).apply { gravity = Gravity.CENTER_HORIZONTAL; topMargin = dp(8f) } }) } // Subtitle if (params.subtitle != null) { container.addView(TextView(activity).apply { tag = "subtitle" text = params.subtitle textSize = 14f setTextColor(PAMColors.SUBTITLE_COLOR.toColorInt()) gravity = Gravity.CENTER layoutParams = LinearLayout.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT ).apply { gravity = Gravity.CENTER_HORIZONTAL; topMargin = dp(8f) } }) } return container } /** * Load a Lottie animation from [uri]. * [onReady] is called on the main thread once the composition is set. * For JSON strings: bất đồng bộ → onReady called inside listener. * For asset/URL: đồng bộ → onReady called right away. */ fun loadLottieAnimation(uri: String, animationView: LottieAnimationView, onReady: (() -> Unit)? = null) { val trimmedUri = uri.trim() if (trimmedUri.startsWith("{") && trimmedUri.endsWith("}")) { try { com.airbnb.lottie.LottieCompositionFactory.fromJsonString(trimmedUri, null) .addListener { result -> animationView.setComposition(result) onReady?.invoke() } .addFailureListener { it.printStackTrace() onReady?.invoke() } } catch (e: Exception) { e.printStackTrace() onReady?.invoke() } return } if (uri.startsWith("http://") || uri.startsWith("https://")) { animationView.setAnimationFromUrl(uri) } else { val fileName = if (uri.endsWith(".json")) "lotties/$uri" else "lotties/$uri.json" animationView.setAnimation(fileName) } onReady?.invoke() } }