honghengqiang 2 years ago
parent
commit
2bd1f2ba5e
36 changed files with 980 additions and 48 deletions
  1. 1 1
      .idea/misc.xml
  2. 10 0
      baseswago/src/main/java/com/swago/baseswago/im/GroupMsgParser.kt
  3. 1 1
      baseswago/src/main/java/com/swago/baseswago/im/ImConstant.kt
  4. 18 3
      baseswago/src/main/java/com/swago/baseswago/inter/RoomApi.kt
  5. 52 0
      baseswago/src/main/java/com/swago/baseswago/model/live/audio/AudioGiftDataBean.java
  6. 32 0
      baseswago/src/main/java/com/swago/baseswago/model/live/audio/AudioSendGiftModel.kt
  7. 12 0
      baseswago/src/main/java/com/swago/baseswago/model/live/audio/MaiUserModel.kt
  8. 13 0
      baseswago/src/main/java/com/swago/baseswago/model/live/audio/PositionBean.kt
  9. 16 0
      room/src/main/java/com/swago/room/anchor/AnchorRoomFragment.kt
  10. 187 0
      room/src/main/java/com/swago/room/audio/widget/AudioContainGiftView.kt
  11. 101 0
      room/src/main/java/com/swago/room/audio/widget/AudioGiftAnimManager.kt
  12. 87 0
      room/src/main/java/com/swago/room/audio/widget/AudioGiftView.kt
  13. 65 2
      room/src/main/java/com/swago/room/base/BaseComFragment.kt
  14. 138 27
      room/src/main/java/com/swago/room/gift/GiftDialog.kt
  15. 84 6
      room/src/main/java/com/swago/room/gift/GiftVm.kt
  16. 27 0
      room/src/main/java/com/swago/room/gift/audio/MaiUserAdapter.kt
  17. 25 3
      room/src/main/java/com/swago/room/vm/MsgVm.kt
  18. 9 0
      room/src/main/java/com/swago/room/widget/AnchorFooterView.kt
  19. 5 0
      room/src/main/res/drawable/shape_ff56b7_stroke_50.xml
  20. 13 5
      room/src/main/res/layout/dialog_gift.xml
  21. 5 0
      room/src/main/res/layout/fragment_base_com.xml
  22. 29 0
      room/src/main/res/layout/item_mai_user.xml
  23. 13 0
      room/src/main/res/layout/layout_anchor_footer_view.xml
  24. 7 0
      room/src/main/res/layout/view_audio_contain_gift.xml
  25. 30 0
      room/src/main/res/layout/view_audio_gift.xml
  26. BIN
      room/src/main/res/mipmap-xxhdpi/audio_icon_eight.webp
  27. BIN
      room/src/main/res/mipmap-xxhdpi/audio_icon_five.webp
  28. BIN
      room/src/main/res/mipmap-xxhdpi/audio_icon_four.webp
  29. BIN
      room/src/main/res/mipmap-xxhdpi/audio_icon_nine.webp
  30. BIN
      room/src/main/res/mipmap-xxhdpi/audio_icon_one.webp
  31. BIN
      room/src/main/res/mipmap-xxhdpi/audio_icon_seven.webp
  32. BIN
      room/src/main/res/mipmap-xxhdpi/audio_icon_six.webp
  33. BIN
      room/src/main/res/mipmap-xxhdpi/audio_icon_three.webp
  34. BIN
      room/src/main/res/mipmap-xxhdpi/audio_icon_two.webp
  35. BIN
      room/src/main/res/mipmap-xxhdpi/audio_icon_x.webp
  36. BIN
      room/src/main/res/mipmap-xxhdpi/audio_icon_zero.webp

+ 1 - 1
.idea/misc.xml

@@ -305,7 +305,7 @@
     <option name="priority" value="Medium" />
     <option name="excludeFilter" value="" />
   </component>
-  <component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="11" project-jdk-type="JavaSDK">
+  <component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="11" project-jdk-type="JavaSDK">
     <output url="file://$PROJECT_DIR$/build/classes" />
   </component>
   <component name="ProjectType">

+ 10 - 0
baseswago/src/main/java/com/swago/baseswago/im/GroupMsgParser.kt

@@ -25,6 +25,7 @@ import com.swago.baseswago.im.ImConstant.launch_mic
 import com.swago.baseswago.im.ImConstant.lock_un_lock_mic
 import com.swago.baseswago.im.ImConstant.lucky_gift
 import com.swago.baseswago.im.ImConstant.lucky_gift_prize
+import com.swago.baseswago.im.ImConstant.multi_send_gift
 import com.swago.baseswago.im.ImConstant.notice_update
 import com.swago.baseswago.im.ImConstant.open_close_mic
 import com.swago.baseswago.im.ImConstant.pk_process
@@ -52,6 +53,7 @@ import com.swago.baseswago.model.live.ReceiveModel
 import com.swago.baseswago.model.im.GamePrize
 import com.swago.baseswago.model.live.RoomUserChangeModel
 import com.swago.baseswago.model.live.audio.AudioSeatModel
+import com.swago.baseswago.model.live.audio.AudioSendGiftModel
 import com.swago.baseswago.model.live.audio.IMAudioModel
 import com.swago.baseswago.model.live.audio.IMAudioSeatUpdateModel
 import com.swago.baseswago.model.live.gift.IMGiftModel
@@ -273,6 +275,14 @@ class GroupMsgParser : IGroupNewMsgParser {
                     )
                     parserModel = gson.fromJson(resultString,parameterizedTypeImpl)
                 }
+                multi_send_gift -> {
+                    parameterizedTypeImpl = ParameterizedTypeImpl(
+                        arrayOf<Type>(AudioSendGiftModel::class.java),
+                        CusNewMsgBean::class.java,
+                        CusNewMsgBean::class.java
+                    )
+                    parserModel = gson.fromJson(resultString,parameterizedTypeImpl)
+                }
 
                 update_mic_list_data -> {
                     parameterizedTypeImpl = ParameterizedTypeImpl(

+ 1 - 1
baseswago/src/main/java/com/swago/baseswago/im/ImConstant.kt

@@ -193,7 +193,7 @@ object ImConstant {
     const val multi_jifen_change = 310
 
     /**
-     * 积分清零
+     * 多人送礼
      */
     const val multi_send_gift = 311
 

+ 18 - 3
baseswago/src/main/java/com/swago/baseswago/inter/RoomApi.kt

@@ -3,9 +3,7 @@ package com.swago.baseswago.inter
 import com.swago.baseswago.model.MomentModel
 import com.swago.baseswago.model.RedEnvelope
 import com.swago.baseswago.model.live.*
-import com.swago.baseswago.model.live.audio.AudioInviteUserModel
-import com.swago.baseswago.model.live.audio.AudioSeatList
-import com.swago.baseswago.model.live.audio.AudioSeatModel
+import com.swago.baseswago.model.live.audio.*
 import com.swago.baseswago.model.live.game.GameListModel
 import com.swago.baseswago.model.live.gift.GiftAllModel
 import com.swago.baseswago.model.live.gift.GiftSendModel
@@ -15,6 +13,7 @@ import com.swago.baseswago.model.live.mic.LianMaiUserModel
 import com.swago.baseswago.model.live.mic.RoomLianMaiInfo
 import com.swago.baseswago.model.live.pk.ListPKLiverModel
 import com.swago.baseswago.model.live.pk.PKInfoModel
+import okhttp3.RequestBody
 import retrofit2.http.*
 
 /**
@@ -91,6 +90,12 @@ interface RoomApi {
     @POST("/v1/rtc/gift/list")
     suspend fun getGiftList(): List<GiftAllModel>
 
+    /**
+     * 直播间礼物
+     */
+    @POST("/v3/rtc/yuying/gift/list")
+    suspend fun getAudioGiftList(): List<GiftAllModel>
+
     /**
      * 赠送礼物
      */
@@ -441,4 +446,14 @@ interface RoomApi {
     suspend fun updateAudioNotice(@Field("room_id") room_id: String,
                                   @Field("yuying_notice_content") yuying_notice_content: String):Any
 
+
+    @POST("/v3/rtc/yuying/buy/gift")
+    suspend fun sendAudioGift(@Body requestBody: RequestBody): AudioSendGiftModel
+
+    /**
+     * 语音房里麦上的用户信息
+     */
+    @FormUrlEncoded
+    @POST("/v3/rtc/yuying/position/user/list")
+    suspend fun getMaiUserList(@Field("broadcast_log_id") broadcast_log_id: String): MaiUserModel
 }

+ 52 - 0
baseswago/src/main/java/com/swago/baseswago/model/live/audio/AudioGiftDataBean.java

@@ -0,0 +1,52 @@
+package com.swago.baseswago.model.live.audio;
+
+import java.util.ArrayList;
+
+public class AudioGiftDataBean {
+
+    private String giftNum;
+    private String giftUrl;
+    private ArrayList<PositionBean> allPositionData;
+    private String senderId;
+
+
+    public AudioGiftDataBean(String giftNum, String giftUrl, ArrayList<PositionBean> allPositionData, String senderId) {
+        this.giftNum = giftNum;
+        this.giftUrl = giftUrl;
+        this.allPositionData = allPositionData;
+        this.senderId = senderId;
+    }
+
+    public String getGiftNum() {
+        return giftNum;
+    }
+
+    public void setGiftNum(String giftNum) {
+        this.giftNum = giftNum;
+    }
+
+    public String getGiftUrl() {
+        return giftUrl;
+    }
+
+    public void setGiftUrl(String giftUrl) {
+        this.giftUrl = giftUrl;
+    }
+
+    public ArrayList<PositionBean> getAllPositionData() {
+        return allPositionData;
+    }
+
+    public void setAllPositionData(ArrayList<PositionBean> allPositionData) {
+        this.allPositionData = allPositionData;
+    }
+
+    public String getSenderId() {
+        return senderId;
+    }
+
+    public void setSenderId(String senderId) {
+        this.senderId = senderId;
+    }
+
+}

+ 32 - 0
baseswago/src/main/java/com/swago/baseswago/model/live/audio/AudioSendGiftModel.kt

@@ -0,0 +1,32 @@
+package com.swago.baseswago.model.live.audio
+
+data class AudioSendGiftModel(
+    val giftBatch: Int,
+    val giftIcon: String,
+    val giftNum: String,
+    val group_id: String,
+    val incomeCount: String,
+    val isSvga: Int,
+    val multiple: Int,
+    val receivers: List<Receiver>,
+    val roomId: String,
+    val roomSessionId: String,
+    val senderId: String,
+    val senderName: String,
+    val svgaUrl: String,
+    val userCoins: String
+)
+
+data class Receiver(
+    val maiIndex: Int,
+    val receiverId: String,
+    /**
+     * dx,dy标识位移的距离
+     */
+    var dx:Float,
+    var dy:Float,
+    /**
+     * 最后上移动动画的距离
+     */
+    var offSetY:Float
+)

+ 12 - 0
baseswago/src/main/java/com/swago/baseswago/model/live/audio/MaiUserModel.kt

@@ -0,0 +1,12 @@
+package com.swago.baseswago.model.live.audio
+
+data class MaiUserModel(
+    val list: List<MaiUserInfo>
+)
+
+data class MaiUserInfo(
+    val user_head_img_url: String,
+    val user_id: String,
+    val user_name: String,
+    var selected:Boolean,
+)

+ 13 - 0
baseswago/src/main/java/com/swago/baseswago/model/live/audio/PositionBean.kt

@@ -0,0 +1,13 @@
+package com.swago.baseswago.model.live.audio
+
+data class PositionBean(
+    /**
+     * dx,dy标识位移的距离
+     */
+    var dx:Float,
+    var dy:Float,
+    /**
+     * 最后上移动动画的距离
+     */
+    var offSetY:Float
+)

+ 16 - 0
room/src/main/java/com/swago/room/anchor/AnchorRoomFragment.kt

@@ -92,6 +92,22 @@ class AnchorRoomFragment : BaseComFragment<FragmentBaseComBinding>() {
             AudioNoticeDialog.newInstance().show(childFragmentManager,"AudioNoticeDialog")
         }
 
+        footerView?.openGiftPanelFun = {
+            //打开礼物列表
+            SwagoRoomManager.iRoomInfo?.let {
+                if (it.getAnchorId().isNotEmpty()) {
+                    GiftDialog.newInstance(
+                        it.getAnchorName(),
+                        it.getAnchorId()
+                    ).apply {
+                        this.openSendRedEnvelopeFun = {
+                            RedEnvelopeDialog.newInstance(roomConfig).show(this@AnchorRoomFragment.childFragmentManager,"RedEnvelopeDialog")
+                        }
+                    }.show(childFragmentManager, "GiftDialog")
+                }
+            }
+        }
+
         (iHeader as ComHeaderView).showUserInfo = {
             PersonDataDFragment.newInstance(it, isAnchor = true, inRoom = true,SwagoRoomManager.iRoomInfo?.getRoomId()?:"")
                 .apply {

+ 187 - 0
room/src/main/java/com/swago/room/audio/widget/AudioContainGiftView.kt

@@ -0,0 +1,187 @@
+package com.swago.room.audio.widget
+
+import android.animation.Animator
+import android.animation.AnimatorSet
+import android.animation.ObjectAnimator
+import android.content.Context
+import android.util.AttributeSet
+import androidx.constraintlayout.widget.ConstraintLayout
+import com.swago.baseswago.baseroom.IRoomActiveListener
+import com.swago.baseswago.baseroom.IRoomInfo
+import com.swago.baseswago.baseroom.SwagoRoomManager
+import com.swago.baseswago.model.live.audio.AudioSendGiftModel
+import com.swago.room.R
+import com.swago.room.databinding.ViewAudioContainGiftBinding
+
+class AudioContainGiftView : ConstraintLayout, IRoomActiveListener {
+
+
+    constructor(context: Context) : this(context, null)
+    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
+    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
+        context,
+        attrs,
+        defStyleAttr
+    ) {
+        initView(context)
+    }
+
+    private var binding: ViewAudioContainGiftBinding? = null
+    private var allAnimationSetList  = ArrayList<AnimatorSet>()
+    private var allAudioGiftViewList  = ArrayList<AudioGiftView>()
+
+    @Volatile
+    private var currentModel: AudioSendGiftModel? = null
+    var isBusy = false
+
+    private fun initView(context: Context) {
+        binding =
+            ViewAudioContainGiftBinding.inflate(android.view.LayoutInflater.from(context), this, true)
+        SwagoRoomManager.addListener(this)
+    }
+
+    fun playGiftAnim(audioSendGiftModel: AudioSendGiftModel) {
+        binding?.apply {
+            isBusy = true
+            clearAnimData()
+            //默认初始化8个
+            if (allAudioGiftViewList.isEmpty()){
+                for (i in 0 until 8){
+                    val audioGiftAnimView = AudioGiftView(context)
+                    val layoutParams = ConstraintLayout.LayoutParams(ConstraintLayout.LayoutParams.WRAP_CONTENT,ConstraintLayout.LayoutParams.WRAP_CONTENT).apply {
+                        this.topToTop = R.id.clMultiAudio
+                        this.bottomToBottom = R.id.clMultiAudio
+                        this.startToStart = R.id.clMultiAudio
+                        this.endToEnd = R.id.clMultiAudio
+                    }
+                    audioGiftAnimView.layoutParams = layoutParams
+                    allAudioGiftViewList.add(audioGiftAnimView)
+                }
+            }
+            //如果后续超过几个的话 就在添加几个
+            if (audioSendGiftModel.receivers.size>allAudioGiftViewList.size){
+                for (i in 0 until (audioSendGiftModel.receivers.size-allAudioGiftViewList.size)){
+                    val audioGiftAnimView = AudioGiftView(context)
+                    val layoutParams = ConstraintLayout.LayoutParams(ConstraintLayout.LayoutParams.WRAP_CONTENT,ConstraintLayout.LayoutParams.WRAP_CONTENT).apply {
+                        this.topToTop = R.id.clMultiAudio
+                        this.bottomToBottom = R.id.clMultiAudio
+                        this.startToStart = R.id.clMultiAudio
+                        this.endToEnd = R.id.clMultiAudio
+                    }
+                    audioGiftAnimView.layoutParams = layoutParams
+                    allAudioGiftViewList.add(audioGiftAnimView)
+                }
+            }
+
+            audioSendGiftModel.receivers.forEachIndexed {position, positionBean ->
+                //创建从屏幕中心弹出的动画view
+                allAudioGiftViewList[position].setViewData(audioSendGiftModel.giftIcon, audioSendGiftModel.giftNum)
+                clMultiAudio.addView(allAudioGiftViewList[position])
+
+                //从中间缩放出来
+                val alphaAnim = ObjectAnimator.ofFloat(allAudioGiftViewList[position], "alpha", 1f, 1f)
+                val scaleX = ObjectAnimator.ofFloat(allAudioGiftViewList[position], "scaleX", 0f, 1f)
+                val scaleY = ObjectAnimator.ofFloat(allAudioGiftViewList[position], "scaleY", 0f, 1f)
+                val scaleFromCenter = AnimatorSet()
+                scaleFromCenter.playTogether(scaleX,scaleY,alphaAnim)
+                scaleFromCenter.setTarget(allAudioGiftViewList[position])
+                scaleX.duration = 300
+                scaleY.duration = 300
+
+                //从中间向其他地方位移缩放
+                val moveX = ObjectAnimator.ofFloat(allAudioGiftViewList[position],"translationX", positionBean.dx)
+                val moveY = ObjectAnimator.ofFloat(allAudioGiftViewList[position],"translationY", positionBean.dy)
+                val scaleXX = ObjectAnimator.ofFloat(allAudioGiftViewList[position],"scaleX",1f,0.2f)
+                val scaleYY = ObjectAnimator.ofFloat(allAudioGiftViewList[position],"scaleY",1f,0.2f)
+                val moveAndScale = AnimatorSet()
+                moveAndScale.playTogether(moveX,moveY,scaleXX,scaleYY)
+                moveAndScale.duration = 700
+
+                val alphaAnimX = ObjectAnimator.ofFloat(allAudioGiftViewList[position], "alpha", 1f, 0f)
+                alphaAnimX.duration = 200
+
+                val moveAnimatorPlus = AnimatorSet()
+                moveAnimatorPlus.play(alphaAnimX).after(moveAndScale)
+
+                //在头像上方消失
+                val alphaAnimEnd = ObjectAnimator.ofFloat(allAudioGiftViewList[position],"alpha",1f,0f)
+                alphaAnimEnd.duration = 800
+                //向头像上方移动
+                val upYAnima = ObjectAnimator.ofFloat(allAudioGiftViewList[position],"translationY",positionBean.offSetY)
+                val alphaAnimUp = ObjectAnimator.ofFloat(allAudioGiftViewList[position], "alpha", 0f, 1f)
+                val upAnimatorSet = AnimatorSet()
+                upAnimatorSet.playTogether(upYAnima,alphaAnimUp)
+                upAnimatorSet.duration = 200
+
+                val animationScaleAndMoveSet = AnimatorSet()
+                animationScaleAndMoveSet.play(moveAnimatorPlus)
+                    .after(scaleFromCenter)
+                    .after(2300)
+
+                val animatorSet = AnimatorSet()
+                animatorSet.play(upAnimatorSet)
+                    .after(animationScaleAndMoveSet)
+                    .before(alphaAnimEnd)
+                animatorSet.setTarget(allAudioGiftViewList[position])
+                allAnimationSetList.add(animatorSet)
+
+                animatorSet.addListener(object: Animator.AnimatorListener{
+                    override fun onAnimationStart(animation: Animator?) {
+                    }
+
+                    override fun onAnimationEnd(animation: Animator?) {
+                        currentModel = null
+                        isBusy = false
+                    }
+
+                    override fun onAnimationCancel(animation: Animator?) {
+
+                    }
+
+                    override fun onAnimationRepeat(animation: Animator?) {}
+
+                })
+            }
+            allAnimationSetList.forEach {
+                it.start()
+            }
+        }
+    }
+    
+
+    private fun clearAnimData(){
+        allAnimationSetList.forEach {
+            it.cancel()
+        }
+        allAudioGiftViewList.forEach {
+            it.clearImageView()
+            //属性动画重置
+            it.translationX = 0f
+            it.translationY = 0f
+        }
+        allAnimationSetList.clear()
+        binding?.clMultiAudio?.removeAllViews()
+    }
+
+    override fun changeRoom(iRoomInfo: IRoomInfo) {
+    }
+
+    override fun leaveRoom(iRoomInfo: IRoomInfo) {
+        isBusy = false
+    }
+
+    override fun joinedRoom(iRoomInfo: IRoomInfo) {
+       
+    }
+
+    override fun endRoom(iRoomInfo: IRoomInfo?) {
+       
+    }
+
+    override fun closeRoomed() {
+        isBusy = false
+        clearAnimData()
+        SwagoRoomManager.removeListener(this)
+    }
+
+}

+ 101 - 0
room/src/main/java/com/swago/room/audio/widget/AudioGiftAnimManager.kt

@@ -0,0 +1,101 @@
+package com.swago.room.audio.widget
+
+import com.swago.baseswago.baseroom.IRoomActiveListener
+import com.swago.baseswago.baseroom.IRoomInfo
+import com.swago.baseswago.baseroom.SwagoRoomManager
+import com.swago.baseswago.model.live.audio.AudioGiftDataBean
+import com.swago.baseswago.model.live.audio.AudioSendGiftModel
+import com.swago.baseswago.model.live.gift.IMGiftModel
+import com.swago.baseswago.util.LogUtil
+import kotlinx.coroutines.*
+import java.util.*
+import kotlin.collections.ArrayList
+
+class AudioGiftAnimManager : IRoomActiveListener {
+
+    val audioGiftViewList = ArrayList<AudioContainGiftView>()
+    private val audioSendGiftModelList = LinkedList<AudioSendGiftModel>()
+
+    private var loopJob: Job? = null
+
+    fun init(){
+        SwagoRoomManager.addListener(this)
+    }
+
+    override fun changeRoom(iRoomInfo: IRoomInfo) {
+
+    }
+
+    override fun leaveRoom(iRoomInfo: IRoomInfo) {
+        audioSendGiftModelList.clear()
+        audioGiftViewList.forEach {
+            it.clearAnimation()
+        }
+    }
+
+    override fun joinedRoom(iRoomInfo: IRoomInfo) {
+
+    }
+
+    override fun endRoom(iRoomInfo: IRoomInfo?) {
+        audioSendGiftModelList.clear()
+    }
+
+    override fun closeRoomed() {
+        audioSendGiftModelList.clear()
+        audioGiftViewList.forEach {
+            it.clearAnimation()
+        }
+        loopJob?.cancel()
+        loopJob = null
+        SwagoRoomManager.removeListener(this)
+    }
+
+
+    fun addNewMessage(audioSendGiftModel: AudioSendGiftModel){
+        audioSendGiftModelList.add(audioSendGiftModel)
+        if (loopJob==null){
+            initQueue()
+            loopJob?.start()
+        }
+
+    }
+
+    private fun initQueue(){
+        loopJob = GlobalScope.launch(Dispatchers.Main,start = CoroutineStart.LAZY) {
+            var isLoop = true
+            //用来在都忙的时候往
+            while (isLoop){
+                val imGiftModel  = audioSendGiftModelList.peek()
+                LogUtil.d("giftDanDaoViewList","peek")
+                if (imGiftModel!=null){
+                    var isSetData = false
+                    if(!isSetData){
+                        for (index in 0 until audioGiftViewList.size){
+                            if (!audioGiftViewList[index].isBusy){
+                                //有空闲轨道
+                                LogUtil.d("giftDanDaoViewList","ddddd-$index")
+                                audioGiftViewList[index].playGiftAnim((imGiftModel))
+                                isSetData = true
+                                break
+                            }
+                        }
+                    }
+
+                    if (isSetData){
+                        audioSendGiftModelList.poll()
+                        if (audioSendGiftModelList.isEmpty()){
+                            isLoop = false
+                            loopJob= null
+                        }
+                    }else{
+                        delay(200)
+                    }
+                }else{
+                    isLoop = false
+                    loopJob= null
+                }
+            }
+        }
+    }
+}

+ 87 - 0
room/src/main/java/com/swago/room/audio/widget/AudioGiftView.kt

@@ -0,0 +1,87 @@
+package com.swago.room.audio.widget
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.LayoutInflater
+import android.widget.ImageView
+import androidx.constraintlayout.widget.ConstraintLayout
+import com.swago.loadUrl
+import com.swago.room.R
+import com.swago.room.databinding.ViewAudioGiftBinding
+
+class AudioGiftView :ConstraintLayout {
+
+    constructor(context: Context) : this(context, null)
+    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
+
+    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
+        context,
+        attrs,
+        defStyleAttr
+    ) {
+        initView(context)
+    }
+
+    private var binding : ViewAudioGiftBinding? = null
+    private var dataImageView = ArrayList<ImageView>()
+
+
+    private fun initView(context: Context) {
+        binding = ViewAudioGiftBinding.inflate(LayoutInflater.from(context), this, true)
+    }
+
+    fun clearImageView(){
+        binding?.ll?.removeAllViews()
+    }
+
+
+    fun setViewData(imageUrl:String,giftNum:String){
+        binding?.apply {
+            ivGiftIcon.loadUrl(context,imageUrl)
+            val length = giftNum.length+1
+            for(i in 0 until length){
+                val imageView = ImageView(context)
+                dataImageView.add(imageView)
+            }
+
+            dataImageView[0].setImageResource(R.mipmap.audio_icon_x)
+            ll.addView(dataImageView[0])
+            giftNum.toCharArray().forEachIndexed { index, c ->
+                when(c){
+                    '0' -> {
+                        dataImageView[index+1].setImageResource(R.mipmap.audio_icon_zero)
+                    }
+                    '1' -> {
+                        dataImageView[index+1].setImageResource(R.mipmap.audio_icon_one)
+                    }
+                    '2' -> {
+                        dataImageView[index+1].setImageResource(R.mipmap.audio_icon_two)
+                    }
+                    '3' -> {
+                        dataImageView[index+1].setImageResource(R.mipmap.audio_icon_three)
+                    }
+                    '4' -> {
+                        dataImageView[index+1].setImageResource(R.mipmap.audio_icon_four)
+                    }
+                    '5' -> {
+                        dataImageView[index+1].setImageResource(R.mipmap.audio_icon_five)
+                    }
+                    '6' -> {
+                        dataImageView[index+1].setImageResource(R.mipmap.audio_icon_six)
+                    }
+                    '7' -> {
+                        dataImageView[index+1].setImageResource(R.mipmap.audio_icon_seven)
+                    }
+                    '8' -> {
+                        dataImageView[index+1].setImageResource(R.mipmap.audio_icon_eight)
+                    }
+                    '9' -> {
+                        dataImageView[index+1].setImageResource(R.mipmap.audio_icon_nine)
+                    }
+                }
+                ll.addView(dataImageView[index+1])
+            }
+        }
+    }
+
+}

+ 65 - 2
room/src/main/java/com/swago/room/base/BaseComFragment.kt

@@ -39,6 +39,7 @@ import com.swago.baseswago.model.live.RoomConfig
 import com.swago.baseswago.model.live.RoomUserModel
 import com.swago.baseswago.model.live.audio.AudioSeatModel
 import com.swago.baseswago.model.live.audio.IMAudioModel
+import com.swago.baseswago.model.live.audio.PositionBean
 import com.swago.baseswago.model.live.game.GameModel
 import com.swago.baseswago.util.*
 import com.swago.room.R
@@ -47,6 +48,7 @@ import com.swago.room.audio.AudioActionDialog
 import com.swago.room.audio.AudioRoomManager
 import com.swago.room.audio.AudioSeatAdapter
 import com.swago.room.audio.IAudioRoomListener
+import com.swago.room.audio.widget.AudioGiftAnimManager
 import com.swago.room.bean.UserRoomModel
 import com.swago.room.databinding.FragmentBaseComBinding
 import com.swago.room.dialog.AudienceListDialog
@@ -99,6 +101,14 @@ abstract class BaseComFragment<T : FragmentBaseComBinding> : BaseXFragment<T>(),
     val msgVm by activityViewModels<MsgVm>()
     val userVm by activityViewModels<UserVm>()
 
+    /**
+     * 麦位信息
+     */
+    private val allPositionXYList by lazy {
+        CopyOnWriteArrayList<PositionBean>()
+    }
+    var count = 2
+
     //直播间观众列表
     var roomUserList = CopyOnWriteArrayList<RoomUserModel.ListBean>()
 
@@ -137,6 +147,10 @@ abstract class BaseComFragment<T : FragmentBaseComBinding> : BaseXFragment<T>(),
         GiftDanDaoManager()
     }
 
+    private val audioGiftManager by lazy {
+        AudioGiftAnimManager()
+    }
+
     override fun loadData() {
 
     }
@@ -149,8 +163,6 @@ abstract class BaseComFragment<T : FragmentBaseComBinding> : BaseXFragment<T>(),
         binding.headFl.addView(iHeader.getHeaderView())
         binding.footerFl.addView(iFooter.getFooterView())
         setHeaderViewPosition()
-        //获取礼物列表
-        giftVm.getGiftList()
         //获取直播间配置
         roomVm.getRoomConfig()
         //初始话语音列表
@@ -164,6 +176,9 @@ abstract class BaseComFragment<T : FragmentBaseComBinding> : BaseXFragment<T>(),
         danDaoManager.giftDanDaoViewList.add(binding.danDaoView)
         danDaoManager.giftDanDaoViewList.add(binding.danDaoView2)
         danDaoManager.giftDanDaoViewList.add(binding.danDaoView3)
+        //语音直播间动画
+        audioGiftManager.init()
+        audioGiftManager.audioGiftViewList.add(binding.audioAllGiftView)
         //进场特效
         msgVm.joinRoomManager.initViewStub(binding.joinRoomViewStub, activity)
 
@@ -406,6 +421,7 @@ abstract class BaseComFragment<T : FragmentBaseComBinding> : BaseXFragment<T>(),
         roomVm.audioSeatListLiveData.observe(this) {
             if (it != null) {
                 audioSeatAdapter.setNewData(it)
+                initMaiPosition()
             }
         }
 
@@ -424,6 +440,19 @@ abstract class BaseComFragment<T : FragmentBaseComBinding> : BaseXFragment<T>(),
             (iHeader as ComHeaderView).updateAudioNotice(it)
         }
 
+        msgVm.addAudioGiftAnimFun = { data ->
+            if (allPositionXYList.size > 0) {
+                data.receivers.forEach {
+                    if (allPositionXYList.size>=it.maiIndex){
+                        it.offSetY = allPositionXYList[it.maiIndex-1].offSetY
+                        it.dx = allPositionXYList[it.maiIndex-1].dx
+                        it.dy = allPositionXYList[it.maiIndex-1].dy
+                    }
+                }
+                audioGiftManager.addNewMessage(data)
+            }
+        }
+
         //打开红包
         binding.ivRedEnvelopeView.openRedEnvelope = {
             RedEnvelopResultDialog.newInstance(it.id, it.senderAvatar, it.senderName).apply {
@@ -439,6 +468,40 @@ abstract class BaseComFragment<T : FragmentBaseComBinding> : BaseXFragment<T>(),
         }
     }
 
+    private fun initMaiPosition() {
+        allPositionXYList.clear()
+        val centerX = DpPxUtil.getScreenWidth() / 2
+        val centerY =  DpPxUtil.getScreenHeight() / 2
+        binding.apply {
+            audioSeatAdapter.data.forEachIndexed { index, _ ->
+                val positionXY = IntArray(2)
+                val ivAvatar = audioSeatAdapter.getViewByPosition(rvAudio, index, R.id.ivImageView)
+                ivAvatar?.let {
+                    it.getLocationOnScreen(positionXY)
+                    val ivAvatarCenterX = positionXY[0].toFloat() + it.width / 2
+                    val ivAvatarCenterY = positionXY[1].toFloat() + it.width / 2
+                    val positionBean2 = PositionBean(
+                        ivAvatarCenterX - centerX,
+                        ivAvatarCenterY - centerY,
+                        ivAvatarCenterY - centerY - DpPxUtil.dip2px(50f).toFloat()
+                    )
+                    allPositionXYList.add(positionBean2)
+                }
+            }
+
+            //这里为了防止第一次获取位置时获取失败
+            lifecycleScope.launch(Dispatchers.Main) {
+                if (allPositionXYList.size > 3 && allPositionXYList[1].dx != allPositionXYList[2].dx) {} else {
+                    delay(1500)
+                    if (count > 0) {
+                        count--
+                        initMaiPosition()
+                    }
+                }
+            }
+        }
+    }
+
     private fun openGiftDialog(nickName: String, userId: String) {
         GiftDialog.newInstance(
             nickName,

+ 138 - 27
room/src/main/java/com/swago/room/gift/GiftDialog.kt

@@ -10,6 +10,7 @@ import android.view.View
 import android.widget.Toast
 import androidx.fragment.app.activityViewModels
 import androidx.fragment.app.viewModels
+import androidx.recyclerview.widget.LinearLayoutManager
 import androidx.viewpager.widget.ViewPager
 import com.facebook.internal.AppCall
 import com.google.gson.Gson
@@ -28,6 +29,7 @@ import com.swago.room.databinding.DialogGiftBinding
 import com.swago.room.dialog.ReachLevelServiceDialog
 import com.swago.room.dialog.UnReachLevelDialog
 import com.swago.room.gift.GiftConfig.giftSelectedWhichType
+import com.swago.room.gift.audio.MaiUserAdapter
 import com.swago.room.vm.MsgVm
 import net.lucode.hackware.magicindicator.ViewPagerHelper
 import net.lucode.hackware.magicindicator.buildins.commonnavigator.CommonNavigator
@@ -50,7 +52,7 @@ class GiftDialog : BaseXDFragment<DialogGiftBinding>() {
     private var receiveId = ""
     private var receiveName = ""
 
-    var openSendRedEnvelopeFun:(()->Unit)? = null
+    var openSendRedEnvelopeFun: (() -> Unit)? = null
 
     private val gson by lazy {
         Gson()
@@ -67,7 +69,7 @@ class GiftDialog : BaseXDFragment<DialogGiftBinding>() {
     private var luckyFragment: GiftFragment? = null
     private var hotFragment: GiftFragment? = null
     private var luxuryFragment: GiftFragment? = null
-    private var specialFragment:GiftFragment? = null
+    private var specialFragment: GiftFragment? = null
 
     private var luckyData = ArrayList<GiftModel>()
     private var hotData = ArrayList<GiftModel>()
@@ -79,6 +81,11 @@ class GiftDialog : BaseXDFragment<DialogGiftBinding>() {
 
     private var json: String? = ""
 
+    private val selectedUserList = ArrayList<String>()
+    private val adapter by lazy {
+        MaiUserAdapter()
+    }
+
     init {
         setGravity(Gravity.BOTTOM)
         setDimAmount(0f)
@@ -101,10 +108,23 @@ class GiftDialog : BaseXDFragment<DialogGiftBinding>() {
     override fun initOther() {
         arguments?.let {
             payVm.getCoins()
+            SwagoRoomManager.iRoomInfo?.let { iRoomInfo ->
+                if (iRoomInfo.getRoomType() == 1) {
+                    binding.tvSender.visibility = View.VISIBLE
+                    binding.rvAudioUser.visibility = View.INVISIBLE
+                    //先走缓存
+                    json = SpUtil.readString("giftList")
+                } else if (iRoomInfo.getRoomType() == 2) {
+                    //获取语音房麦位用户列表
+                    binding.tvSender.visibility = View.GONE
+                    binding.rvAudioUser.visibility = View.VISIBLE
+                    getAudioMaiList()
+                    json = SpUtil.readString("audioGiftList")
+                }
+            }
             receiveId = it.getString("receiveId", "")
             receiveName = it.getString("receiveName", "")
-            //先走缓存
-            json = SpUtil.readString("giftList")
+
             if (json.isNullOrEmpty()) {
 
             } else {
@@ -287,9 +307,13 @@ class GiftDialog : BaseXDFragment<DialogGiftBinding>() {
                 when (type) {
                     1 -> {
                         //幸运
-                        binding.combo.visibility = View.VISIBLE
-                        binding.tvToSend.visibility = View.GONE
-                        binding.combo.start()
+                        SwagoRoomManager.iRoomInfo?.let {
+                            if (it.getRoomType() == 1){
+                                binding.combo.visibility = View.VISIBLE
+                                binding.combo.start()
+                                binding.tvToSend.visibility = View.GONE
+                            }
+                        }
                         val gifModel =
                             luckyData[GiftConfig.giftSelectedPageIndex * 8 + GiftConfig.giftSelectedPageSelectedPosition]
                         LogUtil.d("礼物id", "${gifModel.gift_name}")
@@ -318,7 +342,7 @@ class GiftDialog : BaseXDFragment<DialogGiftBinding>() {
                         val gifModel =
                             specialData[GiftConfig.giftSelectedPageIndex * 8 + GiftConfig.giftSelectedPageSelectedPosition]
                         LogUtil.d("礼物id", "${gifModel.gift_name}")
-                        when(gifModel.gift_mode){
+                        when (gifModel.gift_mode) {
                             1 -> {
                                 openSendRedEnvelopeFun?.invoke()
                                 dismissAllowingStateLoss()
@@ -335,6 +359,14 @@ class GiftDialog : BaseXDFragment<DialogGiftBinding>() {
         }
     }
 
+    private fun getAudioMaiList() {
+        SwagoRoomManager.iRoomInfo?.let {
+            if (it.getRoomType() == 2) {
+                giftVm.getMaiUserList()
+            }
+        }
+    }
+
     private fun resetSendState() {
         binding.combo.cancel()
         binding.combo.visibility = View.GONE
@@ -343,17 +375,30 @@ class GiftDialog : BaseXDFragment<DialogGiftBinding>() {
 
 
     private fun sendGift(giftModel: GiftModel, isCombo: Int = 0, giftNum: Int = 1) {
-        giftBatch++
-        if (receiveId.isNotEmpty()) {
-            SwagoRoomManager.iRoomInfo?.let {
-                giftVm.sendGift(
-                    it.getRoomId(),
-                    giftModel.id,
-                    giftBatch,
-                    receiveId,
-                    isCombo,
-                    giftNum
-                )
+        SwagoRoomManager.iRoomInfo?.let { iRoomInfo ->
+            giftBatch++
+            if (iRoomInfo.getRoomType() == 1) {
+                if (receiveId.isNotEmpty()) {
+                    giftVm.sendGift(
+                        iRoomInfo.getRoomId(),
+                        giftModel.id,
+                        giftBatch,
+                        receiveId,
+                        isCombo,
+                        giftNum
+                    )
+                }
+            } else if (iRoomInfo.getRoomType() == 2) {
+                //语音房送礼
+                if (selectedUserList.isNotEmpty()) {
+                    giftVm.sendAudioGift(
+                        iRoomInfo.getRoomId(),
+                        giftModel.id,
+                        giftBatch,
+                        selectedUserList,
+                        giftNum
+                    )
+                }
             }
         }
     }
@@ -362,7 +407,13 @@ class GiftDialog : BaseXDFragment<DialogGiftBinding>() {
         giftVm.allGiftLiveData.observe(this) {
             val netJson = gson.toJson(it)
             if (json != netJson || json.isNullOrEmpty()) {
-                SpUtil.putString("giftList", netJson)
+                SwagoRoomManager.iRoomInfo?.let { iRoomInfo ->
+                    if (iRoomInfo.getRoomType() == 1) {
+                        SpUtil.putString("giftList", netJson)
+                    } else if (iRoomInfo.getRoomType() == 2) {
+                        SpUtil.putString("audioGiftList", netJson)
+                    }
+                }
 
                 it.forEach { giftAllModel ->
                     when (giftAllModel.gift_type) {
@@ -400,7 +451,7 @@ class GiftDialog : BaseXDFragment<DialogGiftBinding>() {
 
         giftVm.sendResultLiveData.observe(this) {
             if (it != null) {
-                if (it.im_data!=null){
+                if (it.im_data != null) {
                     msgVm.showGiftDanDao?.invoke(it.im_data)
                     msgVm.inComeChange?.invoke(it.im_data.incomeCount)
                     if (it.im_data.multiple >= 500) {
@@ -408,31 +459,91 @@ class GiftDialog : BaseXDFragment<DialogGiftBinding>() {
                     }
                 }
 
-                if (returnGiftBatch < it.gift_batch){
+                if (returnGiftBatch < it.gift_batch) {
                     UserInfo.getUserInfo()?.user_coins = it.user_coins
                     binding.tvCoins.text = it.user_coins ?: "0"
                     returnGiftBatch = it.gift_batch
                 }
             }
-            if (type==2||type==3){
+            if (type == 2 || type == 3) {
                 dismissAllowingStateLoss()
             }
         }
 
         giftVm.sendGiftCodeLiveData.observe(this) {
-            when(it.code){
+            when (it.code) {
+                517 -> {
+                    val payDialog = PayDialog.newInstance()
+                    payDialog.show(parentFragmentManager, "PayDialog")
+                    dismissAllowingStateLoss()
+                    Toast.makeText(AppContext.getContext(), it.msg, Toast.LENGTH_SHORT).show()
+                }
+                542, 543 -> {
+                    UnReachLevelDialog.newInstance(it.msg)
+                        .show(parentFragmentManager, "UnReachLevelDialog")
+                }
+
+                544 -> {
+                    ReachLevelServiceDialog.newInstance(it.msg)
+                        .show(parentFragmentManager, "ReachLevelServiceDialog")
+                }
+            }
+        }
+
+        giftVm.maiUserListLiveData.observe(this) { data ->
+            activity?.let {
+                data.forEach {
+                    it.selected = true
+                    selectedUserList.add(it.user_id)
+                }
+                binding.rvAudioUser.layoutManager = LinearLayoutManager(it)
+                binding.rvAudioUser.adapter = adapter
+                adapter.setNewData(data)
+                adapter.setOnItemClickListener { _, view, position ->
+                    adapter.data[position].selected = !adapter.data[position].selected
+                    if (adapter.data[position].selected){
+                        selectedUserList.add(adapter.data[position].user_id)
+                    }else{
+                        selectedUserList.remove(adapter.data[position].user_id)
+                    }
+                    adapter.notifyItemChanged(position)
+                }
+            }
+        }
+
+        giftVm.sendResultAudioLiveData.observe(this){
+            if (it != null) {
+                msgVm.inComeChange?.invoke(it.incomeCount)
+//                if (it.multiple >= 500) {
+//                    msgVm.waftFun?.invoke(it.im_data)
+//                }
+                if (returnGiftBatch < it.giftBatch) {
+                    UserInfo.getUserInfo()?.user_coins = it.userCoins
+                    binding.tvCoins.text = it.userCoins
+                    returnGiftBatch = it.giftBatch
+                }
+            }
+            if (type == 2 || type == 3) {
+                dismissAllowingStateLoss()
+            }
+        }
+
+        giftVm.sendGiftAudioCodeLiveData.observe(this){
+            when (it.code) {
                 517 -> {
                     val payDialog = PayDialog.newInstance()
                     payDialog.show(parentFragmentManager, "PayDialog")
                     dismissAllowingStateLoss()
                     Toast.makeText(AppContext.getContext(), it.msg, Toast.LENGTH_SHORT).show()
                 }
-                542,543 -> {
-                    UnReachLevelDialog.newInstance(it.msg).show(parentFragmentManager,"UnReachLevelDialog")
+                542, 543 -> {
+                    UnReachLevelDialog.newInstance(it.msg)
+                        .show(parentFragmentManager, "UnReachLevelDialog")
                 }
 
                 544 -> {
-                    ReachLevelServiceDialog.newInstance(it.msg).show(parentFragmentManager,"ReachLevelServiceDialog")
+                    ReachLevelServiceDialog.newInstance(it.msg)
+                        .show(parentFragmentManager, "ReachLevelServiceDialog")
                 }
             }
         }

+ 84 - 6
room/src/main/java/com/swago/room/gift/GiftVm.kt

@@ -4,16 +4,22 @@ import android.app.Application
 import android.util.Log
 import android.widget.Toast
 import androidx.lifecycle.MutableLiveData
+import com.swago.baseswago.baseroom.SwagoRoomManager
 import com.swago.baseswago.http.SwagoException
 import com.swago.baseswago.inter.ApiManager
 import com.swago.baseswago.model.live.ErrorModel
+import com.swago.baseswago.model.live.audio.AudioSendGiftModel
+import com.swago.baseswago.model.live.audio.MaiUserInfo
+import com.swago.baseswago.model.live.audio.MaiUserModel
 import com.swago.baseswago.model.live.gift.GiftAllModel
 import com.swago.baseswago.model.live.gift.GiftSendModel
-import com.swago.baseswago.util.AppContext
-import com.swago.baseswago.util.BaseViewModel
-import com.swago.baseswago.util.LogUtil
-import com.swago.baseswago.util.UserInfo
+import com.swago.baseswago.util.*
 import com.swago.room.pk.PKStateManager
+import okhttp3.MediaType.Companion.toMediaType
+import okhttp3.RequestBody.Companion.toRequestBody
+import org.json.JSONArray
+import org.json.JSONObject
+import retrofit2.http.Field
 
 /**
  *@date 2021/12/12 17:03
@@ -30,8 +36,16 @@ class GiftVm(application: Application) : BaseViewModel(application) {
      */
     fun getGiftList() {
         requestData {
-            val data = ApiManager.roomApi.getGiftList()
-            allGiftLiveData.value = data
+            SwagoRoomManager.iRoomInfo?.let { iRoomInfo ->
+                if (iRoomInfo.getRoomType() == 1){
+                    val data = ApiManager.roomApi.getGiftList()
+                    allGiftLiveData.value = data
+                }else if (iRoomInfo.getRoomType() == 2){
+                    val data = ApiManager.roomApi.getAudioGiftList()
+                    allGiftLiveData.value = data
+                }
+            }
+
         }
     }
 
@@ -76,4 +90,68 @@ class GiftVm(application: Application) : BaseViewModel(application) {
         }
     }
 
+
+    val sendResultAudioLiveData by lazy {
+        MutableLiveData<AudioSendGiftModel?>()
+    }
+
+    val sendGiftAudioCodeLiveData by lazy {
+        MutableLiveData<ErrorModel>()
+    }
+
+    /**
+     * 语音房送礼
+     */
+    fun sendAudioGift(
+        room_id: String,
+        gift_id: String,
+        gift_batch: Int,
+        gift_receive_id:List<String>,
+        gift_num: Int = 1,
+    ) {
+        requestData2(false) {
+            requestData {
+                val jsonArray = JSONArray()
+                gift_receive_id.forEach {
+                    jsonArray.put(it)
+                }
+                val jsonObject = JSONObject()
+                jsonObject.put("room_id", room_id)
+                jsonObject.put("gift_id", gift_id)
+                jsonObject.put("gift_batch", gift_batch)
+                jsonObject.put("gift_num", gift_num)
+                jsonObject.put("gift_receive_id", jsonArray)
+                val data = ApiManager.roomApi.sendAudioGift(jsonObject.toString().toRequestBody("application/json; charset=UTF-8".toMediaType()))
+                sendResultAudioLiveData.value = data
+                UserInfo.getUserInfo()?.let {
+                    if (data.userCoins.toLong() < it.user_coins.toLong()){
+                        it.user_coins = data.userCoins
+                    }
+                }
+            }
+
+            requestError {
+                if (it is SwagoException){
+                    val errorModel = ErrorModel(it.code,it.message?:"")
+                    sendGiftAudioCodeLiveData.value = errorModel
+                }
+            }
+        }
+    }
+
+    /**
+     * 获取麦位列表上的用户信息
+     */
+    val maiUserListLiveData by lazy {
+        MutableLiveData<List<MaiUserInfo>>()
+    }
+    fun getMaiUserList(){
+        requestData {
+            SwagoRoomManager.iRoomInfo?.let { iRoomInfo ->
+                val data = ApiManager.roomApi.getMaiUserList(iRoomInfo.getBroadcastId())
+                maiUserListLiveData.value = data.list
+            }
+        }
+    }
+
 }

+ 27 - 0
room/src/main/java/com/swago/room/gift/audio/MaiUserAdapter.kt

@@ -0,0 +1,27 @@
+package com.swago.room.gift.audio
+
+import android.view.View
+import com.chad.library.adapter.base.BaseQuickAdapter
+import com.chad.library.adapter.base.BaseViewHolder
+import com.swago.baseswago.model.live.audio.MaiUserInfo
+import com.swago.loadUrl
+import com.swago.room.R
+import de.hdodenhof.circleimageview.CircleImageView
+
+class MaiUserAdapter : BaseQuickAdapter<MaiUserInfo, BaseViewHolder>(R.layout.item_mai_user, arrayListOf()) {
+
+    override fun convert(helper: BaseViewHolder?, item: MaiUserInfo?) {
+        helper?.apply {
+            item?.let {
+                itemView.findViewById<CircleImageView>(R.id.ivAvatar).loadUrl(mContext,it.user_head_img_url)
+                val view = itemView.findViewById<View>(R.id.view)
+                if (it.selected){
+                    view.visibility = View.VISIBLE
+                }else{
+                    view.visibility = View.GONE
+                }
+            }
+        }
+    }
+
+}

+ 25 - 3
room/src/main/java/com/swago/room/vm/MsgVm.kt

@@ -20,6 +20,7 @@ import com.swago.baseswago.im.ImConstant.jifen_clear
 import com.swago.baseswago.im.ImConstant.lock_un_lock_mic
 import com.swago.baseswago.im.ImConstant.lucky_gift
 import com.swago.baseswago.im.ImConstant.lucky_gift_prize
+import com.swago.baseswago.im.ImConstant.multi_send_gift
 import com.swago.baseswago.im.ImConstant.notice_update
 import com.swago.baseswago.im.ImConstant.open_close_mic
 import com.swago.baseswago.im.ImConstant.red_envelope
@@ -42,15 +43,14 @@ import com.swago.baseswago.model.live.ReceiveModel
 import com.swago.baseswago.model.im.GamePrize
 import com.swago.baseswago.model.live.RoomUserChangeModel
 import com.swago.baseswago.model.live.RoomUserModel
-import com.swago.baseswago.model.live.audio.AudioSeatModel
-import com.swago.baseswago.model.live.audio.IMAudioModel
-import com.swago.baseswago.model.live.audio.IMAudioSeatUpdateModel
+import com.swago.baseswago.model.live.audio.*
 import com.swago.baseswago.model.live.gift.IMGiftModel
 import com.swago.baseswago.util.AppContext
 import com.swago.baseswago.util.UserInfo
 import com.swago.room.audio.AudioRoomManager
 import com.swago.room.audio.IAudioRoomListener
 import com.swago.room.manager.JoinRoomManager
+import java.util.concurrent.CopyOnWriteArrayList
 
 /**
  *@date 2021/10/10 10:00
@@ -75,6 +75,8 @@ class MsgVm(application: Application) : AbsMsgVm(application) {
 
     var forbidSpeakOrCancel: ((cancel: Boolean) -> Unit)? = null
 
+    var addAudioGiftAnimFun: ((audioSendGiftModel:AudioSendGiftModel) -> Unit)? = null
+
     var updateUserRoom: ((data: ArrayList<RoomUserModel.ListBean>, count: Int) -> Unit)? = null
 
     var newRedEnvelopeCome: ((redEnvelope: RedEnvelope) -> Unit)? = null
@@ -513,6 +515,26 @@ class MsgVm(application: Application) : AbsMsgVm(application) {
             }
         }
 
+        /**
+         * 多人送礼
+         */
+        imGroupNewMsgListener.handleMsgType<CusNewMsgBean<AudioSendGiftModel>>(
+            multi_send_gift
+        ) {
+            SwagoRoomManager.iRoomInfo?.let { iRoomInfo ->
+                it.data?.let {
+                    if (it.roomId == iRoomInfo.getRoomId() && it.roomSessionId == iRoomInfo.getBroadcastId()) {
+                        if (it.isSvga == 1){
+                            val imGiftModel = IMGiftModel()
+                            imGiftModel.svga = it.svgaUrl
+                            playSvgUrl?.invoke(imGiftModel)
+                        }
+                        //多人送礼动画
+                        addAudioGiftAnimFun?.invoke(it)
+                    }
+                }
+            }
+        }
 
     }
 

+ 9 - 0
room/src/main/java/com/swago/room/widget/AnchorFooterView.kt

@@ -35,6 +35,7 @@ class AnchorFooterView : ConstraintLayout, IFooter, IRoomActiveListener {
     var openLianMaiFun:(()->Unit)? = null
     var clearJiFen:(()->Unit)? = null
     var openAudioNoticeDialogFun:(()->Unit)? = null
+    var openGiftPanelFun:(()->Unit)? = null
 
     var isMute = false
 
@@ -90,6 +91,13 @@ class AnchorFooterView : ConstraintLayout, IFooter, IRoomActiveListener {
             }
         })
 
+        binding.ivGift.setOnClickListener(object:NoDoubleClickListener(){
+            override fun onClick() {
+                openGiftPanelFun?.invoke()
+            }
+        })
+
+
         binding.ivPK.setOnClickListener(object : NoDoubleClickListener() {
             override fun onClick() {
                 if (PKStateManager.roomState == PKStateManager.LIVING){
@@ -154,6 +162,7 @@ class AnchorFooterView : ConstraintLayout, IFooter, IRoomActiveListener {
             binding.ivMute.visibility = View.GONE
             binding.ivClearJifen.visibility = View.VISIBLE
             binding.ivNotice.visibility = View.VISIBLE
+            binding.ivGift.visibility = View.VISIBLE
         }
         visibility = View.VISIBLE
     }

+ 5 - 0
room/src/main/res/drawable/shape_ff56b7_stroke_50.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <corners android:radius="50dp"/>
+    <stroke android:color="#ff56b7" android:width="2dp"/>
+</shape>

+ 13 - 5
room/src/main/res/layout/dialog_gift.xml

@@ -8,22 +8,29 @@
     <androidx.constraintlayout.widget.ConstraintLayout
         app:layout_constraintBottom_toBottomOf="parent"
         android:layout_width="match_parent"
-        android:layout_height="298dp">
+        android:layout_height="wrap_content">
 
         <View
             android:id="@+id/view"
             android:background="#161722"
-            android:layout_marginTop="30dp"
+            app:layout_constraintBottom_toBottomOf="parent"
             android:layout_width="match_parent"
-            android:layout_height="match_parent"/>
+            android:layout_height="268dp"/>
 
         <net.lucode.hackware.magicindicator.MagicIndicator
             android:id="@+id/magicIndicator"
-            app:layout_constraintTop_toTopOf="parent"
-            android:layout_marginTop="30dp"
+            app:layout_constraintTop_toTopOf="@+id/view"
             android:layout_width="match_parent"
             android:layout_height="40dp"/>
 
+
+        <androidx.recyclerview.widget.RecyclerView
+            android:id="@+id/rvAudioUser"
+            android:background="#161722"
+            app:layout_constraintBottom_toTopOf="@+id/view"
+            android:layout_width="match_parent"
+            android:layout_height="44dp"/>
+
         <TextView
             android:id="@+id/tvSender"
             android:textColor="#FFE398"
@@ -31,6 +38,7 @@
             app:layout_constraintBottom_toTopOf="@+id/view"
             android:layout_marginBottom="4dp"
             android:layout_marginEnd="8dp"
+            android:layout_marginTop="15dp"
             android:paddingEnd="8dp"
             android:paddingStart="8dp"
             android:textSize="14dp"

+ 5 - 0
room/src/main/res/layout/fragment_base_com.xml

@@ -176,4 +176,9 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"/>
 
+    <com.swago.room.audio.widget.AudioContainGiftView
+        android:id="@+id/audioAllGiftView"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"/>
+
 </androidx.constraintlayout.widget.ConstraintLayout>

+ 29 - 0
room/src/main/res/layout/item_mai_user.xml

@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="44dp"
+    android:layout_height="44dp">
+
+    <de.hdodenhof.circleimageview.CircleImageView
+        android:id="@+id/ivAvatar"
+        android:layout_width="35dp"
+        android:layout_height="35dp"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+
+    <View
+        android:id="@+id/view"
+        android:visibility="gone"
+        tools:visibility="visible"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        android:background="@drawable/shape_ff56b7_stroke_50"
+        android:layout_width="40dp"
+        android:layout_height="40dp"/>
+
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 13 - 0
room/src/main/res/layout/layout_anchor_footer_view.xml

@@ -73,6 +73,19 @@
         android:layout_width="10dp"
         android:layout_height="10dp"/>
 
+    <ImageView
+        android:id="@+id/ivGift"
+        android:layout_marginStart="10dp"
+        android:background="@drawable/shape_80000000_20"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintEnd_toStartOf="@+id/ivNotice"
+        android:src="@mipmap/live_gift"
+        android:visibility="gone"
+        tools:visibility="visible"
+        android:padding="7dp"
+        android:layout_width="40dp"
+        android:layout_height="40dp"/>
+
     <ImageView
         android:id="@+id/ivNotice"
         android:layout_marginEnd="10dp"

+ 7 - 0
room/src/main/res/layout/view_audio_contain_gift.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/clMultiAudio"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 30 - 0
room/src/main/res/layout/view_audio_gift.xml

@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layoutDirection="ltr"
+    xmlns:tools="http://schemas.android.com/tools"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+
+    <ImageView
+        android:id="@+id/ivGiftIcon"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        android:layout_width="126dp"
+        android:layout_height="126dp"/>
+
+
+    <LinearLayout
+        android:id="@+id/ll"
+        android:layout_marginStart="87dp"
+        android:orientation="horizontal"
+        app:layout_constraintStart_toStartOf="@+id/ivGiftIcon"
+        app:layout_constraintBottom_toBottomOf="@+id/ivGiftIcon"
+        android:layout_marginBottom="13dp"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content">
+
+
+    </LinearLayout>
+
+</androidx.constraintlayout.widget.ConstraintLayout>

BIN
room/src/main/res/mipmap-xxhdpi/audio_icon_eight.webp


BIN
room/src/main/res/mipmap-xxhdpi/audio_icon_five.webp


BIN
room/src/main/res/mipmap-xxhdpi/audio_icon_four.webp


BIN
room/src/main/res/mipmap-xxhdpi/audio_icon_nine.webp


BIN
room/src/main/res/mipmap-xxhdpi/audio_icon_one.webp


BIN
room/src/main/res/mipmap-xxhdpi/audio_icon_seven.webp


BIN
room/src/main/res/mipmap-xxhdpi/audio_icon_six.webp


BIN
room/src/main/res/mipmap-xxhdpi/audio_icon_three.webp


BIN
room/src/main/res/mipmap-xxhdpi/audio_icon_two.webp


BIN
room/src/main/res/mipmap-xxhdpi/audio_icon_x.webp


BIN
room/src/main/res/mipmap-xxhdpi/audio_icon_zero.webp