lmw
2025-04-24 718f31c92e2029d05260810435a2c70cef6e6ce5
app/src/main/java/com/sinata/xqmuse/MainActivity.kt
@@ -2,11 +2,11 @@
import android.annotation.SuppressLint
import android.content.Intent
import android.os.Handler
import android.os.Looper
import android.os.Message
import android.net.Uri
import android.os.*
import android.provider.Settings
import android.util.Log
import android.view.Gravity
import android.view.View
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentPagerAdapter
@@ -16,6 +16,10 @@
import com.flyco.tablayout.listener.CustomTabEntity
import com.flyco.tablayout.listener.OnTabSelectListener
import com.google.gson.Gson
import com.lzf.easyfloat.EasyFloat
import com.lzf.easyfloat.enums.ShowPattern
import com.lzf.easyfloat.enums.SidePattern
import com.lzf.easyfloat.interfaces.OnInvokeView
import com.sinata.xqmuse.dialog.TipDialog
import com.sinata.xqmuse.network.HttpManager
import com.sinata.xqmuse.network.entity.VoiceDetail
@@ -34,12 +38,14 @@
import com.sinata.xqmuse.utils.Const
import com.sinata.xqmuse.utils.event.EmptyEvent
import com.sinata.xqmuse.utils.event.IntEvent
import com.umeng.socialize.utils.DeviceConfigInternal.context
import kotlinx.android.synthetic.main.activity_main.*
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.jetbrains.anko.startActivity
import org.jetbrains.anko.toast
import xyz.doikki.videoplayer.player.VideoView
class MainActivity : TransparentStatusBarActivity(), OnTabSelectListener,AudioUtils.OnAudioStatusUpdateListener {
    override fun setContentView() = R.layout.activity_main
@@ -48,7 +54,7 @@
    var teacherVideoView:VideoView? = null
    private var bgPlayer:AudioUtils? = null//背景音乐播放器
    private var thinkBgPlayer:AudioUtils? = null//冥想背景音播放器
//    private var thinkBgPlayer:AudioUtils? = null//冥想背景音播放器
//    private var thinkPlayer:AudioUtils? = null//冥想背景音播放器
    private var guideAudio:String? = null
@@ -57,7 +63,7 @@
    private var thinkHandler:Handler? = null //冥想时间相关
    private var inGuide = false //true 引导中...
    private val isFirst by lazy { intent.getBooleanExtra("isFirst",false) } //首次安装,需要到导师引导页
    private val isFirst by lazy { intent.getBooleanExtra("isFirst", false) } //首次安装,需要到导师引导页
    private val MSG_PROGRESS = 0//疗愈进度+1
    private val MSG_TODAY = 1//今日疗愈更新计时
@@ -70,33 +76,40 @@
    var hasTreeFirstShow = false //此字段用来判断 树苗的首次弹窗是否已经触发,和isFirst字段配合使用
    var isBGMChanged = false //此字段用来判断 在疗愈播放中,修改了背景音乐,在疗愈结束后 需要更新BGM音源
    private val EasyFloatTag = "BACKGROUND"
    private var floater: EasyFloat.Builder? = null //浮窗
    override fun initClick() {
        player_close.setOnClickListener {
            TipDialog.show(supportFragmentManager,"是否关闭当前音频?",object :TipDialog.OnClickCallback{
            TipDialog.show(supportFragmentManager, "是否关闭当前音频?", object : TipDialog.OnClickCallback {
                override fun onOk() {
                    EventBus.getDefault().post(EmptyEvent(Const.EventCode.FINISH_THINK))
                }
                override fun onCancel() {
                }
            },"确认","取消")
            }, "确认", "取消")
        }
        cl_player.setOnClickListener {
            voice?.goDetail(this)
            ThinkAudioService.voice?.goDetail(this)
        }
        player_play.setOnClickListener {
            playing = !playing
            if (playing){
            ThinkAudioService.playing = !ThinkAudioService.playing
            if (ThinkAudioService.playing){
                player_play.setImageResource(R.mipmap.player_pause)
                thinkBgPlayer?.resume()
                EventBus.getDefault().post(EmptyEvent(Const.EventCode.USER_INFO_CHANGED))
                EventBus.getDefault().post(EmptyEvent(Const.EventCode.SERVICE_AUDIO_RESUME))
//                thinkBgPlayer?.resume()
//                thinkPlayer?.resume()
                thinkHandler?.sendEmptyMessage(MSG_PROGRESS)
                startTime = System.currentTimeMillis()
            }else{
                player_play.setImageResource(R.mipmap.player_start)
                thinkBgPlayer?.pause()
                EventBus.getDefault().post(EmptyEvent(Const.EventCode.USER_INFO_CHANGED))
                EventBus.getDefault().post(EmptyEvent(Const.EventCode.SERVICE_AUDIO_PAUSE))
//                thinkBgPlayer?.pause()
//                thinkPlayer?.pause()
                thinkHandler?.removeMessages(MSG_PROGRESS)
                saveThinkRecord()
@@ -121,24 +134,26 @@
            override fun handleMessage(msg: Message) {
                super.handleMessage(msg)
                when(msg.what){
                    MSG_PROGRESS->{
                        currentPosition = thinkBgPlayer?.currentPosition ?:0
                        EventBus.getDefault().post(EmptyEvent(Const.EventCode.GOT_THINK_POSITION))
                        sendEmptyMessageDelayed(MSG_PROGRESS,1000)
                    MSG_PROGRESS -> {
                        EventBus.getDefault().post(EmptyEvent(Const.EventCode.SERVICE_AUDIO_PROGRESS))
                        sendEmptyMessageDelayed(MSG_PROGRESS, 1000)
                        if (System.currentTimeMillis() - startTime >= 60000){
                            saveThinkRecord()
                        }
                    }
                    MSG_COUNTDOWN->{
                        if (System.currentTimeMillis()>= finishTime)
                    MSG_COUNTDOWN -> {
                        if (System.currentTimeMillis() >= ThinkAudioService.finishTime)
                            EventBus.getDefault().post(EmptyEvent(Const.EventCode.FINISH_THINK))
                        else
                            sendEmptyMessageDelayed(MSG_COUNTDOWN,1000)
                            sendEmptyMessageDelayed(MSG_COUNTDOWN, 1000)
                    }
                    MSG_TODAY->{
                    MSG_TODAY -> { //todo 分离hanlder,这个保持原功能,进度和计时相关应放进Service
                        if (System.currentTimeMillis() - lastTodayTime > 60000) { //距离上次刷新过去了1分钟
                            Log.e(Const.Tag,"已经过1分钟,需要重新获取今日疗愈数据")
                            Log.e(Const.Tag, "已经过1分钟,需要重新获取今日疗愈数据")
                            lastTodayTime = System.currentTimeMillis()
                            (fragments[0] as HomeFragment).getToday()
                        }
                        sendEmptyMessageDelayed(MSG_TODAY,5000)
                        sendEmptyMessageDelayed(MSG_TODAY, 5000)
                    }
                }
            }
@@ -151,27 +166,29 @@
        if (guidePlayer == null)
            guidePlayer = AudioUtils()
        if (guideAudio.isNullOrEmpty())
            HttpManager.getPlan().request(this){_,data->
            HttpManager.getPlan().request(this){ _, data->
                guideAudio = data
                if (inGuide){
                    guidePlayer?.setVolume(0.6f)
                    guidePlayer?.loopPlayMusic(this,data)
                    guidePlayer?.loopPlayMusic(this, data)
                }
            }
        else{
            guidePlayer?.setVolume(0.6f)
            guidePlayer?.loopPlayMusic(this,guideAudio)
            guidePlayer?.loopPlayMusic(this, guideAudio)
        }
    }
    private fun startBgm() {
        val bgm = SPUtils.instance().getString(Const.User.BGM)
        if (!bgm.isNullOrEmpty()){
            if (bgPlayer == null)
                bgPlayer = AudioUtils()
            val volume = SPUtils.instance().getInt(Const.User.VOLUME,50)
            bgPlayer?.setVolume(volume.toFloat()/100)
            bgPlayer?.loopPlayMusic(this,bgm)
        HttpManager.getHomeBackgroundMusicByUserId().request(this){ _, data->
            if (!data?.audioFile.isNullOrEmpty()){
                if (bgPlayer == null)
                    bgPlayer = AudioUtils()
                val volume = SPUtils.instance().getInt(Const.User.VOLUME, 50)
                bgPlayer?.setVolume(volume.toFloat() / 100)
                bgPlayer?.loopPlayMusic(this, data?.audioFile)
            }
            (fragments[0] as HomeFragment).changeBg(data?.imageUrl ?: "", data?.backUrl ?: "")
        }
    }
@@ -180,21 +197,22 @@
     */
    private fun startThink() {
        bgPlayer?.pause()
        index = 0
        if (voice?.meditationMusicList?.isNullOrEmpty() == false){
            if (thinkBgPlayer == null){
                thinkBgPlayer = AudioUtils()
                thinkBgPlayer!!.setOnAudioStatusUpdateListener(this)
        ThinkAudioService.index = 0
        if (ThinkAudioService.voice?.meditationMusicList?.isNullOrEmpty() == false){
            checkFloat() //检测浮窗
            // 启动音乐服务
            val serviceIntent = Intent(this, ThinkAudioService::class.java)
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                startForegroundService(serviceIntent)
            } else {
                startService(serviceIntent)
            }
            val volume = SPUtils.instance().getInt(Const.User.VOLUME_THINK,50)
            thinkBgPlayer?.setVolume(volume.toFloat()/100)
            thinkBgPlayer?.startPlayMusic(this, voice?.meditationMusicList?.get(index))
            currentDuration = voice?.meditationSecondList?.get(index)?:0
            ThinkAudioService.currentDuration = ThinkAudioService.voice?.meditationSecondList?.get(ThinkAudioService.index)?:0
            EventBus.getDefault().post(EmptyEvent(Const.EventCode.GOT_THINK_DURATION))
            playing = true
            ThinkAudioService.playing = true
            cl_player.visible()
            tv_player.text = voice?.meditationTitle
            iv_player.setImageURI(voice?.coverUrl?.split(",")?.firstOrNull())
            tv_player.text = ThinkAudioService.voice?.meditationTitle
            iv_player.setImageURI(ThinkAudioService.voice?.coverUrl?.split(",")?.firstOrNull())
            player_play.setImageResource(R.mipmap.player_pause)
            thinkHandler?.sendEmptyMessage(MSG_PROGRESS)
            startTime = System.currentTimeMillis()  //记录开始冥想的时间
@@ -207,15 +225,16 @@
     */
    private fun finishThink(){
        saveThinkRecord()
        voice = null
        index = 0
        finishTime = 0L
        thinkBgPlayer?.stopPlayMusic(true)
        playing = false
        ThinkAudioService.voice = null
        ThinkAudioService.index = 0
        ThinkAudioService.finishTime = 0L
        // 停止服务
        stopService(Intent(this, ThinkAudioService::class.java))
        ThinkAudioService.playing = false
        thinkHandler?.removeMessages(0)
        cl_player.gone()
        (fragments[0] as HomeFragment).refreshTodayPlayingState() //对比当前音频是否是每日疗愈
        if (JkApplication.isForeground){
        if (XQApplication.isForeground){
            if (isBGMChanged) //BGM已经被切换,重新播放新BGM
                startBgm()
            else //BGM未改变,直接续播
@@ -224,17 +243,44 @@
    }
    /**
     * 申请浮窗权限 增加稳定性
     */
    private fun checkFloat() {
        if (!Settings.canDrawOverlays(this) && SPUtils.instance().getString("isRefusedFloat").isNullOrEmpty()) { //没有浮窗权限并且没有拒绝过
            TipDialog.show(
                supportFragmentManager,
                "为了增加后台播放的稳定性,我们需要开启悬浮窗口权限",
                object : TipDialog.OnClickCallback {
                    override fun onOk() {
                        var intent = Intent(
                            Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
                            Uri.parse("package:" + packageName)
                        )
                        startActivityForResult(intent, 1234)
                    }
                    override fun onCancel() {
                    }
                },
                "去开启",
                "取消"
            )
        }
    }
    /**
     * 保存冥想记录
     */
    private fun saveThinkRecord() {
        if (voice == null||startTime == 0L||SPUtils.instance().getString(Const.User.TOKEN).isNullOrEmpty())
        if (ThinkAudioService.voice == null||startTime == 0L||SPUtils.instance().getString(Const.User.TOKEN).isNullOrEmpty())
            return
        val time = ((System.currentTimeMillis() - startTime) / 1000).toInt()
        startTime = 0L
        HttpManager.saveViewingHistory(voice?.id?:"", time).request(this,false,{ _, _->
            Log.e(Const.Tag,"冥想记录成功:$time 秒")
        }){_,_->
            Log.e(Const.Tag,"冥想记录失败:$time 秒")
        startTime = System.currentTimeMillis()
        HttpManager.saveViewingHistory(ThinkAudioService.voice?.id ?: "", time).request(this, false, { _, _ ->
            Log.e(Const.Tag, "冥想记录成功:$time 秒")
        }){ _, _->
            Log.e(Const.Tag, "冥想记录失败:$time 秒")
        }
    }
@@ -246,7 +292,7 @@
    }
    private fun initTab() {
        val titles = arrayListOf("疗愈","课程","","疗愈馆","我的")
        val titles = arrayListOf("疗愈", "课程", "", "疗愈馆", "我的")
        val iconChecked = arrayListOf(
            R.mipmap.home_selected,
            R.mipmap.play_selected,
@@ -267,7 +313,7 @@
        fragments.add(DiscoveryFragment())
        fragments.add(MineFragment())
        view_pager.offscreenPageLimit = fragments.size
        view_pager.adapter = object : FragmentPagerAdapter(supportFragmentManager,0) {
        view_pager.adapter = object : FragmentPagerAdapter(supportFragmentManager, 0) {
            override fun getItem(p0: Int): Fragment {
                return fragments[p0]
            }
@@ -291,12 +337,16 @@
    }
    override fun onTabSelect(position: Int) {
        if (position == 4 && SPUtils.instance().getString(Const.User.TOKEN).isNullOrEmpty()){
            toast("请先登录")
            startActivity<LoginActivity>()
            tab_bar.currentTab = 0
            return
        if (position == 4 ){
            if (SPUtils.instance().getString(Const.User.TOKEN).isNullOrEmpty()){
                toast("请先登录")
                startActivity<LoginActivity>()
                tab_bar.currentTab = 0
                return
            }else
                (fragments[4] as MineFragment).showUserInfo()
        }
        view_pager.currentItem = position
        if (position == 2){
            (fragments[2] as TreeFragment).getTree()
@@ -310,13 +360,18 @@
                cl_player.alpha = 1f
            }
        }
        if (position == 1){
            (fragments[1] as CourseFragment).refreshDataByResume()
        }
        if (position == 4){
            (fragments[4] as MineFragment).queryUnread()
        }
        if (position != 1)
            EventBus.getDefault().post(EmptyEvent(Const.EventCode.PAUSE_TEACHER_VIDEO))
        if (position == 0)
        if (position == 0){
            (fragments[0] as HomeFragment).refreshDataByResume()
            startTodayCheck()
        }
        else
            stopTodayCheck()
    }
@@ -325,7 +380,7 @@
    }
    @Subscribe
    fun onEvent(e:EmptyEvent){
    fun onEvent(e: EmptyEvent){
        if (e.code == Const.EventCode.CHANGE_USER){
            tab_bar.currentTab = 0
            onTabSelect(0)
@@ -341,37 +396,38 @@
            tab_bar.currentTab = 3
            onTabSelect(3)
        }else if(e.code == Const.EventCode.APP_FOREGROUND){
            if (voice==null)
            EasyFloat.hide(EasyFloatTag)
            if (ThinkAudioService.voice==null)
                bgPlayer?.resume()
        }else if(e.code == Const.EventCode.APP_BACKGROUND){
            bgPlayer?.pause()
            if ( ThinkAudioService.playing && Settings.canDrawOverlays(this) ) {
                showFloater()
                EasyFloat.show(EasyFloatTag)
            }
        }else if(e.code == Const.EventCode.CHANGE_BGM){
            if (voice == null)
            if (ThinkAudioService.voice == null)
                startBgm()
            else
                isBGMChanged = true //正在播放疗愈,无法立即切换背景音乐
            (fragments[0] as HomeFragment).changeBg()
        }else if(e.code == Const.EventCode.START_THINK){
            startThink()
        }else if(e.code == Const.EventCode.FINISH_THINK){
            finishThink()
        }else if(e.code == Const.EventCode.PAUSE_OR_RESUME_THINK){
            player_play.callOnClick()
        }else if(e.code == Const.EventCode.CHANGE_THINK_VOLUME){
            val v = SPUtils.instance().getInt(Const.User.VOLUME_THINK,50)
            thinkBgPlayer?.setVolume(v.toFloat()/100)
        }else if(e.code == Const.EventCode.START_GUIDE_AUDIO){
            inGuide = true
            startGuide()
            bgPlayer?.pause()
            thinkBgPlayer?.pause()
            EventBus.getDefault().post(EmptyEvent(Const.EventCode.SERVICE_AUDIO_PAUSE))
        }else if(e.code == Const.EventCode.FINISH_GUIDE_AUDIO){
            inGuide = false
            guidePlayer?.stopPlayMusic(false)
            if (voice!=null&& playing){
                thinkBgPlayer?.resume()
            if (ThinkAudioService.voice!=null&& ThinkAudioService.playing){
                EventBus.getDefault().post(EmptyEvent(Const.EventCode.SERVICE_AUDIO_RESUME))
            }
            if (voice == null)
            if (ThinkAudioService.voice == null)
                bgPlayer?.resume()
        }else if(e.code == Const.EventCode.REFRESH_PRIVATE){ //重新答题后,刷新私人定制
            (fragments[0] as HomeFragment).getPrivacy()
@@ -381,9 +437,9 @@
    }
    @Subscribe
    fun onIntEvent(e:IntEvent){
    fun onIntEvent(e: IntEvent){
        if (e.code == Const.EventCode.THINK_SEEK_PROGRESS){
            thinkBgPlayer?.seekTo(e.i)
            EventBus.getDefault().post(IntEvent(Const.EventCode.SERVICE_AUDIO_SEEK,e.i))
            player_play.callOnClick()
        }
    }
@@ -393,16 +449,38 @@
        val answer = SPUtils.instance().getString(Const.User.ANSWER)
        if (!answer.isNullOrEmpty()){ //已登录并且有答案
            val reqAnswer = Gson().fromJson(answer, ReqAnswer::class.java)
            reqAnswer?.device = Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID)
            HttpManager.saveUserAnswers(reqAnswer).request(this,false,success = { _, _->
                SPUtils.instance().put(Const.User.ANSWER,"").apply()
                Log.e(Const.Tag,"私人定制已保存")
            reqAnswer?.device = Settings.Secure.getString(
                contentResolver,
                Settings.Secure.ANDROID_ID
            )
            HttpManager.saveUserAnswers(reqAnswer).request(this, false, success = { _, _ ->
                SPUtils.instance().put(Const.User.ANSWER, "").apply()
                Log.e(Const.Tag, "私人定制已保存")
                (fragments[0] as HomeFragment).getPrivacy()
            }){_,_->
            }){ _, _->
            }
        }
    }
    private fun showFloater() {
        if (floater == null){
            floater = EasyFloat.with(applicationContext)
                .setLayout(R.layout.layout_floter, OnInvokeView {
                })
                .setShowPattern(ShowPattern.ALL_TIME)
                .setSidePattern(SidePattern.RESULT_LEFT)
                .setGravity(Gravity.START or Gravity.BOTTOM, 0, 0)
                .setDragEnable(true)
                .setTag(EasyFloatTag)
                .setMatchParent(widthMatch = false, heightMatch = false)
                .registerCallback {
                    touchEvent { view, motionEvent ->
                        motionEvent.action
                    }
                }
            floater?.show()
        }
    }
    private fun startTodayCheck(){
        thinkHandler?.sendEmptyMessage(MSG_TODAY)
@@ -416,16 +494,25 @@
        super.onResume()
        if (view_pager.currentItem == 4){
            (fragments[4] as MineFragment).queryUnread()
            (fragments[4] as MineFragment).showUserInfo()
        } else if (view_pager.currentItem == 0){
            startTodayCheck()
            (fragments[0] as HomeFragment).refreshDataByResume()
        } else if (view_pager.currentItem == 2){
            (fragments[2] as TreeFragment).getTree()
        } else if (view_pager.currentItem == 1){
            (fragments[1] as CourseFragment).refreshDataByResume()
        }
        if (isFirst&&!hasTreeFirstShow&&!SPUtils.instance().getString(Const.User.ANSWER).isNullOrEmpty()){ //如果是第一次安装并且还没显示树苗打卡引导并且答完题了 就去树苗引导
            tab_bar.currentTab = 2
            onTabSelect(2)
            (fragments[2] as TreeFragment).showFirst()
            hasTreeFirstShow = true
        }
        if (XQApplication.finishAnswer){
            XQApplication.finishAnswer = false
            tab_bar.currentTab = 2
            onTabSelect(2)
        }
    }
@@ -450,22 +537,7 @@
    }
    override fun onFinishPlay() {
        if (voice == null) //说明是手动关闭的,不需要处理下一步播放逻辑
            return
        if (isRecycle){ //单曲
            if (playing)
                thinkBgPlayer?.startPlayMusic(this, voice?.meditationMusicList?.get(index))
        }else{//顺序
            index++
            if (index>=voice?.meditationMusicList?.size?:0)//列表已播完
                EventBus.getDefault().post(EmptyEvent(Const.EventCode.FINISH_THINK))
            else{
                currentDuration = voice?.meditationSecondList?.get(index)?:0
                EventBus.getDefault().post(EmptyEvent(Const.EventCode.GOT_THINK_DURATION))
                if (playing)
                    thinkBgPlayer?.startPlayMusic(this, voice?.meditationMusicList?.get(index))
            }
        }
    }
    override fun onGetDuration(duration: Int) {
@@ -475,15 +547,5 @@
    override fun onBackPressed() {
        if (teacherVideoView?.onBackPressed() != true)
            super.onBackPressed()
    }
    companion object{ //冥想播放相关参数
        var playing = false //true播放中
        var isRecycle = false //true单曲循环播放
        var voice: VoiceDetail? = null //冥想详情
        var index = 0 //当前播放序号
        var currentDuration = 0 //当前音频长度(秒)
        var currentPosition = 0L //当前音频进度(毫秒)
        var finishTime = 0L //自动结束的时间戳
    }
}