lmw
2025-04-24 718f31c92e2029d05260810435a2c70cef6e6ce5
app/src/main/java/com/sinata/xqmuse/ui/tree/TreeFragment.kt
@@ -8,9 +8,9 @@
import cn.sinata.xldutils.fragment.BaseFragment
import cn.sinata.xldutils.gone
import cn.sinata.xldutils.visible
import com.danikula.videocache.CacheListener
import com.github.penfeizhou.animation.apng.APNGDrawable
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.luck.picture.lib.tools.SPUtils
import com.sinata.xqmuse.MainActivity
import com.sinata.xqmuse.R
import com.sinata.xqmuse.dialog.TreeTipDialog
@@ -21,15 +21,27 @@
import com.sinata.xqmuse.utils.AudioUtils
import com.sinata.xqmuse.utils.Const
import com.sinata.xqmuse.utils.ScreenUtil
import com.sinata.xqmuse.utils.cache.ProxyVideoCacheManager
import com.sinata.xqmuse.utils.event.EmptyEvent
import com.sinata.xqmuse.utils.extention.checkLogin
import com.sinata.xqmuse.utils.extention.clickDelay
import com.sinata.xqmuse.utils.interfaces.StringCallback
import kotlinx.android.synthetic.main.fragment_tree.*
import okhttp3.*
import org.greenrobot.eventbus.EventBus
import org.jetbrains.anko.support.v4.dip
import org.jetbrains.anko.support.v4.runOnUiThread
import org.jetbrains.anko.support.v4.startActivity
import org.jetbrains.anko.support.v4.toast
import retrofit2.Retrofit
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.io.InputStream
import java.net.HttpURLConnection
import java.net.URL
import kotlin.math.max
class TreeFragment : BaseFragment() {
    override fun contentViewId() = R.layout.fragment_tree
@@ -40,6 +52,7 @@
    private val audioPlayer by lazy { AudioUtils() } //音频播放
    private var tree:TreeInfo? = null
    private val cacheDir by lazy { requireActivity().cacheDir }
    //常量
    val GROWTH_ANIM_DURATION = 500L
@@ -56,20 +69,24 @@
        tv_rule.setOnClickListener {
            HttpManager.getH5(6).requestByF(this){ _, data->
                data?.let {
                    startActivity<H5Activity>("title" to "规则说明","url" to it.content,"showClose" to false)
                    startActivity<H5Activity>(
                        "title" to "规则说明",
                        "url" to it.content,
                        "showClose" to false
                    )
                }
            }
        }
        tv_sign.clickDelay {
            HttpManager.sign().requestByF(this){_,data->
            HttpManager.sign().requestByF(this){ _, data->
                tv_sign.isEnabled = false
                tv_sign.text = "已签到"
                tree?.energyValue = (tree?.energyValue?:0)+10
                tree?.totalEnergyValue = (tree?.totalEnergyValue?:0)+10
                tv_power.text = "当前能量值:${(tree?.energyValue?:0)}"
                signed.visible()
                signed.postDelayed({signed.gone()},1500)
                signed.postDelayed({ signed.gone() }, 1500)
            }
        }
        tv_to_think.setOnClickListener {
@@ -79,7 +96,8 @@
            EventBus.getDefault().post(EmptyEvent(Const.EventCode.SWITCH_HOME))
        }
        iv_water.clickDelay {
            showWater()
//            showWater()
//            return@clickDelay
//            tree?.energyValue = 0
//            tree!!.treeLevelType++
//            showTreeAnim(tree!!.getTreeApng(),true)
@@ -92,14 +110,14 @@
            else if (tree?.sowAgain == 1)
                toast("树苗已成熟,请重新播种")
            else
                HttpManager.watering().requestByF(this){_,data->
                HttpManager.watering().requestByF(this){ _, data->
                    data?.apply {
                        EventBus.getDefault().post(EmptyEvent(Const.EventCode.USER_INFO_CHANGED)) //刷新成长值
                        tv_max.text = "/$nextLevel"
                        tv_growth.text = growthValue.toString()
                        val targetHeight = growthValue.toDouble()/nextLevel*dip(155)
                        val layoutParams = progress.layoutParams
                        showGrowthAnim(layoutParams.height, max(1,targetHeight.toInt()))
                        showGrowthAnim(layoutParams.height, max(1, targetHeight.toInt()))
                        showWater()
                        tree?.energyValue = energyValue
                        tv_power.text = "当前能量值:${(tree?.energyValue?:0)}"
@@ -107,7 +125,7 @@
                            showGrowUpDialog(data.treeLevelType)
                        }else if (tree!!.status != 2){
                            tree!!.status = 2
                            showTreeAnim(tree!!.getTreeApng(),tree!!.treeLevelType != 1)
                            showTreeAnim(tree!!.getTreeApng(), tree!!.treeLevelType != 1)
                        }
                    }
                }
@@ -122,7 +140,7 @@
        }
        tv_again.setOnClickListener {
            HttpManager.restart().requestByF(this){_,_->
            HttpManager.restart().requestByF(this){ _, _->
                getTree()
            }
        }
@@ -148,7 +166,7 @@
    private fun checkFirst() {
        if (checkLogin())
            HttpManager.isFirst().requestByF(this){_,data->
            HttpManager.isFirst().requestByF(this){ _, data->
                if (data == true)
                    showFirst()
            }
@@ -157,12 +175,12 @@
    /**
     * 依次弹窗升级和播放语音
     */
    private fun showGrowUpDialog(targetLevel:Int){
    private fun showGrowUpDialog(targetLevel: Int){
        tree!!.treeLevelType++
        showTreeAnim(tree!!.getTreeApng(),true)
        audioPlayer.startPlayMusic(requireContext(),tree!!.getLevelUpAudio())
        val dialog = TreeTipDialog.show(childFragmentManager,"生命之树",tree!!.getLevelName())
        audioPlayer.setOnAudioStatusUpdateListener(object :AudioUtils.OnAudioStatusUpdateListener{
        showTreeAnim(tree!!.getTreeApng(), true)
        audioPlayer.startPlayMusic(requireContext(), tree!!.getLevelUpAudio())
        val dialog = TreeTipDialog.show(childFragmentManager, "生命之树", tree!!.getLevelName())
        audioPlayer.setOnAudioStatusUpdateListener(object : AudioUtils.OnAudioStatusUpdateListener {
            override fun onUpdate(db: Double, time: Long) {
            }
@@ -191,7 +209,7 @@
    }
    fun getTree(){
        HttpManager.getUserTree().requestByF(this){_,data->
        HttpManager.getUserTree().requestByF(this){ _, data->
            val changed = data?.treeLevelType != tree?.treeLevelType //等级发生变化
            tree = data
            data?.apply {
@@ -200,14 +218,14 @@
                tv_growth.text = growthValue.toString()
                val targetHeight = growthValue.toDouble()/nextLevel*dip(155)
                val layoutParams = progress.layoutParams
                showGrowthAnim(layoutParams.height, max(1,targetHeight.toInt()))
                showGrowthAnim(layoutParams.height, max(1, targetHeight.toInt()))
                tv_sign.isEnabled = isSign == 2
                tv_sign.text = if (tv_sign.isEnabled) "打卡签到" else "已签到"
                tv_to_think.isEnabled = taskOne == 2
                tv_to_think_2.isEnabled = taskTwo == 2
                if (status == 2){
                    if (changed)
                        showTreeAnim(getTreeApng(),treeLevelType != 1)
                        showTreeAnim(getTreeApng(), treeLevelType != 1)
                } else
                    showTreeDead()
            }
@@ -220,7 +238,7 @@
    fun showFirst(){
        if (!(activity as MainActivity).hasTreeFirstShow){
            TreeTipDialog.show(childFragmentManager)
            audioPlayer.startPlayMusic(requireContext(),Const.TREE.first)
            audioPlayer.startPlayMusic(requireContext(), Const.VOICE_TREE.first)
        }
    }
@@ -232,7 +250,7 @@
    /**
     * 成长值动画
     */
    private fun showGrowthAnim(start:Int,end:Int){
    private fun showGrowthAnim(start: Int, end: Int){
        val ofInt = ValueAnimator.ofInt(start, end)
        ofInt.duration = GROWTH_ANIM_DURATION
        ofInt.addUpdateListener {
@@ -244,31 +262,123 @@
    }
    private fun showWater(){
        val apngDrawable = APNGDrawable.fromAsset(requireContext(),"apngb_water.png")
        iv_water_anim.setImageDrawable(apngDrawable)
        apngDrawable.setLoopLimit(2)
        apngDrawable.registerAnimationCallback(object :Animatable2Compat.AnimationCallback(){
            override fun onAnimationEnd(drawable: Drawable?) {
                super.onAnimationEnd(drawable)
                iv_water_anim.setImageResource(0)
//        val apngDrawable = APNGDrawable.fromAsset(requireContext(),"apngb_water.png")
//        iv_water_anim.setImageDrawable(apngDrawable)
//        apngDrawable.setLoopLimit(2)
//        apngDrawable.registerAnimationCallback(object : Animatable2Compat.AnimationCallback(){
//            override fun onAnimationEnd(drawable: Drawable?) {
//                super.onAnimationEnd(drawable)
//                iv_water_anim.setImageResource(0)
//            }
//        })
        showOrDownApng(Const.APNG_TREE.WATER,object :StringCallback{
            override fun onResult(rst: String) {
                waterAnim()
            }
        })
    }
    private fun showOrDownApng(url:String,callback: StringCallback){
        if (isFileCached(url)){
            callback.onResult("")
        }else
            downloadFileToCache(url,callback)
    }
    private fun waterAnim(){
        val apngDrawable = APNGDrawable.fromFile(getCachedFile(Const.APNG_TREE.WATER)?.absolutePath)
        runOnUiThread {
            iv_water_anim.setImageDrawable(apngDrawable)
            apngDrawable.setLoopLimit(2)
            apngDrawable.registerAnimationCallback(object : Animatable2Compat.AnimationCallback(){
                override fun onAnimationEnd(drawable: Drawable?) {
                    super.onAnimationEnd(drawable)
                    iv_water_anim.setImageResource(0)
                }
            })
        }
    }
    // 获取缓存文件路径
    fun getCachedFile(url: String): File? {
        val cacheDir = requireActivity().cacheDir
        val file = File(cacheDir, url.substring(url.lastIndexOf("/")))
        return if (file.exists()) file else null
    }
    // 检查文件是否已缓存
    fun isFileCached(url: String): Boolean {
        return getCachedFile(url) != null
    }
    fun downloadFileToCache(url: String,callback: StringCallback) {
        val client = OkHttpClient()
        val request = Request.Builder().url(url).build()
        client.newCall(request).enqueue(object : okhttp3.Callback {
            override fun onFailure(call: okhttp3.Call, e: IOException) {
                // 下载失败处理
                e.printStackTrace()
            }
            override fun onResponse(call: Call, response: Response) {
                if (!response.isSuccessful) {
                    throw IOException("下载失败: ${response.code()}")
                }
                // 获取内部缓存目录
                val cacheDir = requireActivity().cacheDir
                val outputFile = File(cacheDir, url.substring(url.lastIndexOf("/") + 1))
                if (outputFile.exists() && outputFile.isDirectory) {
                    // 删除目录或调整文件名
                    outputFile.delete()
                }
                try {
                    // 将文件写入缓存
                    val inputStream = response.body()?.byteStream()
                    val outputStream = FileOutputStream(outputFile)
                    inputStream?.use { input ->
                        outputStream.use { output ->
                            input.copyTo(output)
                        }
                    }
                    // 下载成功,文件路径为 outputFile.absolutePath
                    callback.onResult("")
                } catch (e: Exception) {
                    e.printStackTrace()
                }
            }
        })
    }
    private fun showSunshine(){
        val apngDrawable = APNGDrawable.fromAsset(requireContext(),"sunshine.png")
        iv_sunshine.setImageDrawable(apngDrawable)
        showOrDownApng(Const.APNG_TREE.SUNSHINE,object :StringCallback{
            override fun onResult(rst: String) {
                runOnUiThread {
                    val apngDrawable = APNGDrawable.fromFile(getCachedFile(Const.APNG_TREE.SUNSHINE)?.absolutePath)
                    iv_sunshine.setImageDrawable(apngDrawable)
                }
            }
        })
//        val apngDrawable = APNGDrawable.fromAsset(requireContext(), "sunshine.png")
//        iv_sunshine.setImageDrawable(apngDrawable)
//        apngDrawable.setLoopLimit(5)
    }
    private fun showTreeAnim(resStr:String,isLoop:Boolean){
        val apngDrawable = APNGDrawable.fromAsset(requireContext(),resStr)
        iv_tree.setImageDrawable(apngDrawable)
        apngDrawable.setLoopLimit(if (isLoop) -1 else 1)
        if (tree?.treeLevelType?:0 >= 10)
            tv_again.visible()
        else
            tv_again.gone()
    private fun showTreeAnim(resStr: String, isLoop: Boolean){
        showOrDownApng(resStr,object :StringCallback{
            override fun onResult(rst: String) {
                runOnUiThread {
                    val apngDrawable = APNGDrawable.fromFile(getCachedFile(resStr)?.absolutePath)
                    iv_tree.setImageDrawable(apngDrawable)
                    apngDrawable.setLoopLimit(if (isLoop) -1 else 1)
                    if (tree?.treeLevelType?:0 >= 10)
                        tv_again.visible()
                    else
                        tv_again.gone()
                }
            }
        })
    }
    override fun onPause() {