From 718f31c92e2029d05260810435a2c70cef6e6ce5 Mon Sep 17 00:00:00 2001
From: lmw <125975490@qq.com>
Date: 星期四, 24 四月 2025 10:45:55 +0800
Subject: [PATCH] save

---
 app/src/main/java/com/sinata/xqmuse/ui/tree/TreeFragment.kt |  184 ++++++++++++++++++++++++++++++++++++---------
 1 files changed, 147 insertions(+), 37 deletions(-)

diff --git a/app/src/main/java/com/sinata/xqmuse/ui/tree/TreeFragment.kt b/app/src/main/java/com/sinata/xqmuse/ui/tree/TreeFragment.kt
index 0ff6077..90bd32c 100644
--- a/app/src/main/java/com/sinata/xqmuse/ui/tree/TreeFragment.kt
+++ b/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() {

--
Gitblit v1.7.1