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/res/layout/fragment_tree.xml                           |    5 
 /dev/null                                                           |    0 
 app/src/main/java/com/sinata/xqmuse/ui/course/PushCourseFragment.kt |    1 
 app/src/main/java/com/sinata/xqmuse/network/entity/TreeInfo.kt      |   51 +++++---
 app/src/main/java/com/sinata/xqmuse/ui/mine/MineFragment.kt         |    3 
 app/src/main/AndroidManifest.xml                                    |    1 
 app/src/main/java/com/sinata/xqmuse/utils/Const.kt                  |   17 ++
 app/src/main/java/com/sinata/xqmuse/network/RRetrofit.kt            |    4 
 app/src/main/res/layout/activity_only_web.xml                       |    6 +
 app/src/main/java/com/sinata/xqmuse/ui/OnlyWebActivity.kt           |   62 ++++++++++
 app/src/main/java/com/sinata/xqmuse/ui/tree/TreeFragment.kt         |  184 ++++++++++++++++++++++++------
 11 files changed, 271 insertions(+), 63 deletions(-)

diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 0fb7ddd..7e8f229 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -150,6 +150,7 @@
         <activity android:name=".ui.course.StudyActivity" android:configChanges="orientation|screenSize|keyboardHidden"/>
         <activity android:name=".ui.home.BuyVoiceActivity" android:label="确认订单"/>
         <activity android:name=".ui.home.MicroVideoActivity" android:launchMode="singleTask"  android:configChanges="orientation|screenSize|keyboardHidden" />
+        <activity android:name=".ui.OnlyWebActivity" />
 
         <service android:name="com.amap.api.location.APSService"/>
         <service android:name=".ThinkAudioService"
diff --git a/app/src/main/assets/apngb_level_1.png b/app/src/main/assets/apngb_level_1.png
deleted file mode 100644
index 58f4538..0000000
--- a/app/src/main/assets/apngb_level_1.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/assets/apngb_level_10.png b/app/src/main/assets/apngb_level_10.png
deleted file mode 100644
index 11fc3df..0000000
--- a/app/src/main/assets/apngb_level_10.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/assets/apngb_level_2.png b/app/src/main/assets/apngb_level_2.png
deleted file mode 100644
index 1b228ff..0000000
--- a/app/src/main/assets/apngb_level_2.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/assets/apngb_level_3.png b/app/src/main/assets/apngb_level_3.png
deleted file mode 100644
index 212d4cd..0000000
--- a/app/src/main/assets/apngb_level_3.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/assets/apngb_level_4.png b/app/src/main/assets/apngb_level_4.png
deleted file mode 100644
index d83606d..0000000
--- a/app/src/main/assets/apngb_level_4.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/assets/apngb_level_5.png b/app/src/main/assets/apngb_level_5.png
deleted file mode 100644
index 8595004..0000000
--- a/app/src/main/assets/apngb_level_5.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/assets/apngb_level_6.png b/app/src/main/assets/apngb_level_6.png
deleted file mode 100644
index 86b613a..0000000
--- a/app/src/main/assets/apngb_level_6.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/assets/apngb_level_7.png b/app/src/main/assets/apngb_level_7.png
deleted file mode 100644
index c3cf531..0000000
--- a/app/src/main/assets/apngb_level_7.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/assets/apngb_level_8.png b/app/src/main/assets/apngb_level_8.png
deleted file mode 100644
index e09511b..0000000
--- a/app/src/main/assets/apngb_level_8.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/assets/apngb_level_9.png b/app/src/main/assets/apngb_level_9.png
deleted file mode 100644
index da6dc53..0000000
--- a/app/src/main/assets/apngb_level_9.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/assets/apngb_water.png b/app/src/main/assets/apngb_water.png
deleted file mode 100644
index 9dd7b18..0000000
--- a/app/src/main/assets/apngb_water.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/assets/sunshine.png b/app/src/main/assets/sunshine.png
deleted file mode 100644
index 0e318b5..0000000
--- a/app/src/main/assets/sunshine.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/java/com/sinata/xqmuse/network/RRetrofit.kt b/app/src/main/java/com/sinata/xqmuse/network/RRetrofit.kt
index d3f340d..70753b4 100644
--- a/app/src/main/java/com/sinata/xqmuse/network/RRetrofit.kt
+++ b/app/src/main/java/com/sinata/xqmuse/network/RRetrofit.kt
@@ -8,6 +8,7 @@
 import okhttp3.logging.HttpLoggingInterceptor
 import retrofit2.Retrofit
 import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
+import java.util.concurrent.TimeUnit
 
 /**
  * 网络请求retrofit初始化。
@@ -19,6 +20,9 @@
         .addConverterFactory(JsonConverterFactory.create())
         .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
         .client(OkHttpClient.Builder()
+            .connectTimeout(1,TimeUnit.MINUTES)
+            .writeTimeout(1,TimeUnit.MINUTES)
+            .readTimeout(1,TimeUnit.MINUTES)
             .addInterceptor { chain ->
                 val token = SPUtils.instance().getString(Const.User.TOKEN)
                 val request =
diff --git a/app/src/main/java/com/sinata/xqmuse/network/entity/TreeInfo.kt b/app/src/main/java/com/sinata/xqmuse/network/entity/TreeInfo.kt
index cf150bc..fdac3d1 100644
--- a/app/src/main/java/com/sinata/xqmuse/network/entity/TreeInfo.kt
+++ b/app/src/main/java/com/sinata/xqmuse/network/entity/TreeInfo.kt
@@ -17,17 +17,28 @@
     var treeLevelType: Int
 ){
     fun getTreeApng() = when(treeLevelType){ //1=种子 2=发芽 3=幼苗 4=小树苗 5=中等树苗 6=小树 7=中树 8=大树 9=成熟的大树 10=参天大树
-        1-> "apngb_level_1.png"
-        2-> "apngb_level_2.png"
-        3-> "apngb_level_3.png"
-        4-> "apngb_level_4.png"
-        5-> "apngb_level_5.png"
-        6-> "apngb_level_6.png"
-        7-> "apngb_level_7.png"
-        8-> "apngb_level_8.png"
-        9-> "apngb_level_9.png"
-        10-> "apngb_level_10.png"
-        else->"apngb_level_1.png"
+//        1-> "apngb_level_1.png"
+//        2-> "apngb_level_2.png"
+//        3-> "apngb_level_3.png"
+//        4-> "apngb_level_4.png"
+//        5-> "apngb_level_5.png"
+//        6-> "apngb_level_6.png"
+//        7-> "apngb_level_7.png"
+//        8-> "apngb_level_8.png"
+//        9-> "apngb_level_9.png"
+//        10-> "apngb_level_10.png"
+//        else->"apngb_level_1.png"
+        1-> Const.APNG_TREE.LEVEL_1
+        2-> Const.APNG_TREE.LEVEL_2
+        3-> Const.APNG_TREE.LEVEL_3
+        4-> Const.APNG_TREE.LEVEL_4
+        5-> Const.APNG_TREE.LEVEL_5
+        6-> Const.APNG_TREE.LEVEL_6
+        7-> Const.APNG_TREE.LEVEL_7
+        8-> Const.APNG_TREE.LEVEL_8
+        9-> Const.APNG_TREE.LEVEL_9
+        10-> Const.APNG_TREE.LEVEL_10
+        else->Const.APNG_TREE.LEVEL_1
     }
 
     fun getTreeDead() = when(treeLevelType){ //1=种子 2=发芽 3=幼苗 4=小树苗 5=中等树苗 6=小树 7=中树 8=大树 9=成熟的大树 10=参天大树
@@ -44,15 +55,15 @@
     }
 
     fun getLevelUpAudio() = when(treeLevelType){ //1=种子 2=发芽 3=幼苗 4=小树苗 5=中等树苗 6=小树 7=中树 8=大树 9=成熟的大树 10=参天大树
-        2-> Const.TREE.LEVEL_2
-        3-> Const.TREE.LEVEL_3
-        4-> Const.TREE.LEVEL_4
-        5-> Const.TREE.LEVEL_5
-        6-> Const.TREE.LEVEL_6
-        7-> Const.TREE.LEVEL_7
-        8-> Const.TREE.LEVEL_8
-        9-> Const.TREE.LEVEL_9
-        10-> Const.TREE.LEVEL_10
+        2-> Const.VOICE_TREE.LEVEL_2
+        3-> Const.VOICE_TREE.LEVEL_3
+        4-> Const.VOICE_TREE.LEVEL_4
+        5-> Const.VOICE_TREE.LEVEL_5
+        6-> Const.VOICE_TREE.LEVEL_6
+        7-> Const.VOICE_TREE.LEVEL_7
+        8-> Const.VOICE_TREE.LEVEL_8
+        9-> Const.VOICE_TREE.LEVEL_9
+        10-> Const.VOICE_TREE.LEVEL_10
         else->""
     }
 
diff --git a/app/src/main/java/com/sinata/xqmuse/ui/OnlyWebActivity.kt b/app/src/main/java/com/sinata/xqmuse/ui/OnlyWebActivity.kt
new file mode 100644
index 0000000..ff4e6b1
--- /dev/null
+++ b/app/src/main/java/com/sinata/xqmuse/ui/OnlyWebActivity.kt
@@ -0,0 +1,62 @@
+package com.sinata.xqmuse.ui
+
+import android.graphics.Bitmap
+import android.text.TextUtils
+import android.view.View
+import android.webkit.WebChromeClient
+import android.webkit.WebView
+import android.webkit.WebViewClient
+import com.github.zackratos.ultimatebar.UltimateBar
+import com.sinata.xqmuse.R
+import kotlinx.android.synthetic.main.activity_only_web.*
+import org.jetbrains.anko.startActivity
+
+class OnlyWebActivity:TransparentStatusBarActivity() {
+    override fun setContentView() = R.layout.activity_only_web
+
+    override fun initClick() {
+    }
+
+    override fun initView() {
+        UltimateBar.with(this)
+            .statusDark(true)
+            .create().immersionBar() //沉浸状态栏
+        val titleParam = intent.getStringExtra("title")
+        val url = intent.getStringExtra("url")?:""
+        title = titleParam
+        val settings = webView.settings
+        settings.javaScriptEnabled = true
+        settings.javaScriptCanOpenWindowsAutomatically = true
+        settings.defaultTextEncodingName = "utf-8"
+        settings.domStorageEnabled = true
+        webView.webChromeClient = object : WebChromeClient() {
+            override fun onReceivedTitle(view: WebView, title: String?) {
+                super.onReceivedTitle(view, title)
+            }
+        }
+        webView.webViewClient = object : WebViewClient() {
+            override fun onPageFinished(view: WebView, url: String?) {
+                super.onPageFinished(view, url)
+                dismissDialog()
+            }
+
+            override fun onPageStarted(view: WebView, url: String?, favicon: Bitmap?) {
+                super.onPageStarted(view, url, favicon)
+                showDialog()
+            }
+
+            override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean {
+                if (!TextUtils.isEmpty(url)) {
+                    webView.loadUrl(url)
+                }
+                return true
+            }
+        }
+        webView.loadUrl(url)
+
+        if (titleParam == "爱心助力榜单"){
+            titleBar.addRightButton("我的助力",onClickListener = View.OnClickListener {
+                startActivity<OnlyWebActivity>("title" to "推荐名单","url" to intent.getStringExtra("pushList"))
+            })}
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/sinata/xqmuse/ui/course/PushCourseFragment.kt b/app/src/main/java/com/sinata/xqmuse/ui/course/PushCourseFragment.kt
index a6cfd40..2d3f283 100644
--- a/app/src/main/java/com/sinata/xqmuse/ui/course/PushCourseFragment.kt
+++ b/app/src/main/java/com/sinata/xqmuse/ui/course/PushCourseFragment.kt
@@ -62,7 +62,6 @@
     }
 
     fun refresh(){
-        getBanner()
         getType()
         page = 1
         getData()
diff --git a/app/src/main/java/com/sinata/xqmuse/ui/mine/MineFragment.kt b/app/src/main/java/com/sinata/xqmuse/ui/mine/MineFragment.kt
index 4ed3d35..3ca59bf 100644
--- a/app/src/main/java/com/sinata/xqmuse/ui/mine/MineFragment.kt
+++ b/app/src/main/java/com/sinata/xqmuse/ui/mine/MineFragment.kt
@@ -15,6 +15,7 @@
 import com.sinata.xqmuse.network.entity.MineInfo
 import com.sinata.xqmuse.network.requestByF
 import com.sinata.xqmuse.ui.H5Activity
+import com.sinata.xqmuse.ui.OnlyWebActivity
 import com.sinata.xqmuse.ui.course.MyCourseActivity
 import com.sinata.xqmuse.ui.login.LoginActivity
 import com.sinata.xqmuse.utils.Const
@@ -73,7 +74,7 @@
         }
         tv_rank.setOnClickListener {
             userInfo?.apply {
-                startActivity<H5Activity>("title" to "爱心助力榜单","url" to Apis.RANK.format(id),"type" to 0,"pushList" to Apis.PUSH_LIST.format(id))
+                startActivity<OnlyWebActivity>("title" to "爱心助力榜单","url" to Apis.RANK.format(id),"type" to 0,"pushList" to Apis.PUSH_LIST.format(id))
             }
         }
         tv_account.setOnClickListener {
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() {
diff --git a/app/src/main/java/com/sinata/xqmuse/utils/Const.kt b/app/src/main/java/com/sinata/xqmuse/utils/Const.kt
index 8e2413c..f1d7266 100644
--- a/app/src/main/java/com/sinata/xqmuse/utils/Const.kt
+++ b/app/src/main/java/com/sinata/xqmuse/utils/Const.kt
@@ -24,7 +24,7 @@
         const val SHARE_COURSE_TITLE = "传递心灵温暖,一起感受疗愈力量"
     }
 
-    object TREE {
+    object VOICE_TREE {
         val first = OSS_DOMAIN+"xinquan/1d3254a9966b476789491032d8bb908a.wav"
         val LEVEL_2 = OSS_DOMAIN+"xinquan/d8daa4d5490f40f9a68a8469ad697660.wav"
         val LEVEL_3 = OSS_DOMAIN+"xinquan/0da0e117c4374c398347c480d2cfc6b4.wav"
@@ -37,6 +37,21 @@
         val LEVEL_10 = OSS_DOMAIN+"xinquan/8b0303829a6547f5a16c5232883b94cc.wav"
     }
 
+    object APNG_TREE {
+        val LEVEL_1 = OSS_DOMAIN+"1744625964828apngb_level_1.png"
+        val LEVEL_2 = OSS_DOMAIN+"1744626102004apngb_level_2.png"
+        val LEVEL_3 = OSS_DOMAIN+"1744626112494apngb_level_3.png"
+        val LEVEL_4 = OSS_DOMAIN+"1744626121574apngb_level_4.png"
+        val LEVEL_5 = OSS_DOMAIN+"1744626132663apngb_level_5.png"
+        val LEVEL_6 = OSS_DOMAIN+"1744626142519apngb_level_6.png"
+        val LEVEL_7 = OSS_DOMAIN+"1744626152068apngb_level_7.png"
+        val LEVEL_8 = OSS_DOMAIN+"1744626162562apngb_level_8.png"
+        val LEVEL_9 = OSS_DOMAIN+"1744626170896apngb_level_9.png"
+        val LEVEL_10 = OSS_DOMAIN+"1744626182350apngb_level_10.png"
+        val WATER = OSS_DOMAIN+"1744626198262apngb_water.png"
+        val SUNSHINE = OSS_DOMAIN+"1744626206206sunshine.png"
+    }
+
     object User {
         const val TOKEN = "token"
         const val USER_ID = "userId"
diff --git a/app/src/main/res/layout/activity_only_web.xml b/app/src/main/res/layout/activity_only_web.xml
new file mode 100644
index 0000000..ae4329d
--- /dev/null
+++ b/app/src/main/res/layout/activity_only_web.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<WebView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:id="@+id/webView">
+</WebView>
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_tree.xml b/app/src/main/res/layout/fragment_tree.xml
index c5a6805..1fa416e 100644
--- a/app/src/main/res/layout/fragment_tree.xml
+++ b/app/src/main/res/layout/fragment_tree.xml
@@ -158,14 +158,13 @@
 
 
             <ImageView
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
+                android:layout_width="150dp"
+                android:layout_height="150dp"
                 android:id="@+id/iv_water_anim"
                 android:layout_marginEnd="30dp"
                 app:layout_constraintEnd_toEndOf="parent"
                 android:layout_marginBottom="200dp"
                 app:layout_constraintBottom_toBottomOf="@id/iv_tree"/>
-
             <TextView
                 android:layout_width="164dp"
                 android:layout_height="wrap_content"

--
Gitblit v1.7.1