From 4ff10895497e79f6e415788540b3ec076fbf0388 Mon Sep 17 00:00:00 2001 From: zlzw <583819556@qq.com> Date: Wed, 18 Oct 2023 16:17:07 +0800 Subject: [PATCH] =?UTF-8?q?update=20=E8=A7=86=E9=A2=91=E3=80=81=E9=9F=B3?= =?UTF-8?q?=E9=A2=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- OneToOne/src/main/AndroidManifest.xml | 6 +- .../activity/fragments/MyFragment.java | 23 +- .../message/ChatMessageFragment.java | 25 +- .../activity/message/CallAudioActivity.java | 314 ++++++++++++++++++ .../activity/message/CallVideoActivity.java | 136 ++++++-- .../listener/OnCallStatusListener.java | 3 + .../onetoone/manager/CallClientManager.java | 188 +++++++++-- .../shayu/onetoone/manager/RouteManager.java | 1 + .../OTOCallEndMessageItemProvider.java | 185 +---------- .../main/res/layout/activity_call_audio.xml | 129 +++++++ .../main/res/layout/dialog_bottom_list.xml | 2 +- .../main/res/layout/view_call_video_item.xml | 12 + .../main/res/layout/view_call_video_wait.xml | 65 ++++ .../res/mipmap-xxhdpi/ic_call_audio_msg.png | Bin 0 -> 10670 bytes .../main/res/mipmap-xxhdpi/ic_call_money.png | Bin 12616 -> 11698 bytes .../com/yunbao/common/glide/ImgLoader.java | 17 + 16 files changed, 839 insertions(+), 267 deletions(-) create mode 100644 OneToOne/src/main/java/com/shayu/onetoone/activity/message/CallAudioActivity.java create mode 100644 OneToOne/src/main/res/layout/activity_call_audio.xml create mode 100644 OneToOne/src/main/res/layout/view_call_video_wait.xml create mode 100644 OneToOne/src/main/res/mipmap-xxhdpi/ic_call_audio_msg.png diff --git a/OneToOne/src/main/AndroidManifest.xml b/OneToOne/src/main/AndroidManifest.xml index 5c79f1afb..902225d5d 100644 --- a/OneToOne/src/main/AndroidManifest.xml +++ b/OneToOne/src/main/AndroidManifest.xml @@ -149,9 +149,11 @@ android:windowSoftInputMode="stateHidden|adjustResize" /> + android:windowSoftInputMode="stateHidden|adjustResize"/> + - () { @Override public void onItemClick(String bean, int position) { - ToastUtil.show(bean + "|" + position); + Bundle bundle = new Bundle(); + bundle.putString("model", CallClientManager.VIDEO_CALL); + bundle.putString("targetId", targetId); + bundle.putString("callId", targetId); + RouteManager.forwardActivity(RouteManager.ACTIVITY_CALL_VIDEO, bundle); } - }).setStrings(Arrays.asList("发起语音通话\n200钻/分钟", "发起视频通话\n100钻/分钟")) + }).setStrings(Collections.singletonList("发起视频通话\n100钻/分钟")) .showDialog(); } @@ -234,7 +241,17 @@ public class ChatMessageFragment extends AbsConversationFragment { @Override public void onSuccess(String token) { super.onSuccess(token); - ToastUtil.show("弹音频聊天"); + new BottomListDialog(mContext).setSelect(new OnItemClickListener() { + @Override + public void onItemClick(String bean, int position) { + Bundle bundle = new Bundle(); + bundle.putString("model", CallClientManager.AUDIO_CALL); + bundle.putString("targetId", targetId); + bundle.putString("callId", targetId); + RouteManager.forwardActivity(RouteManager.ACTIVITY_CALL_AUDIO, bundle); + } + }).setStrings(Collections.singletonList("发起语音通话\n200钻/分钟")) + .showDialog(); } @Override diff --git a/OneToOne/src/main/java/com/shayu/onetoone/activity/message/CallAudioActivity.java b/OneToOne/src/main/java/com/shayu/onetoone/activity/message/CallAudioActivity.java new file mode 100644 index 000000000..827567268 --- /dev/null +++ b/OneToOne/src/main/java/com/shayu/onetoone/activity/message/CallAudioActivity.java @@ -0,0 +1,314 @@ +package com.shayu.onetoone.activity.message; + +import android.os.Bundle; +import android.view.MotionEvent; +import android.view.SurfaceView; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.alibaba.android.arouter.facade.annotation.Route; +import com.lzf.easyfloat.EasyFloat; +import com.lzf.easyfloat.enums.ShowPattern; +import com.lzf.easyfloat.interfaces.OnFloatCallbacks; +import com.makeramen.roundedimageview.RoundedImageView; +import com.shayu.onetoone.R; +import com.shayu.onetoone.activity.AbsOTOActivity; +import com.shayu.onetoone.bean.FollowBean; +import com.shayu.onetoone.bean.UserBean; +import com.shayu.onetoone.dialog.GiftDialog; +import com.shayu.onetoone.listener.OnCallStatusListener; +import com.shayu.onetoone.manager.CallClientManager; +import com.shayu.onetoone.manager.OTONetManager; +import com.shayu.onetoone.manager.RouteManager; +import com.shayu.onetoone.utils.ConversationUtils; +import com.yunbao.common.glide.ImgLoader; +import com.yunbao.common.http.base.HttpCallback; +import com.yunbao.common.utils.DpUtil; +import com.yunbao.common.utils.ToastUtil; +import com.yunbao.common.utils.WordUtil; + + +@Route(path = RouteManager.ACTIVITY_CALL_AUDIO) +public class CallAudioActivity extends AbsOTOActivity implements View.OnClickListener { + private ImageView vague; + private ImageView close; + private ImageView avatar; + private ImageView follow; + private TextView userName; + private TextView userInfo; + private ImageView gift; + private ImageView money; + private ImageView callStop; + private ImageView callMsg; + private TextView callTime; + + private String targetId; + private String callId; + private String model; + + + private OnCallStatusListener onCallStatusListener; + + @Override + protected int getLayoutId() { + return R.layout.activity_call_audio; + } + + @Override + protected void main(Bundle savedInstanceState) { + + Bundle bundle = getIntent().getBundleExtra("bundle"); + if (bundle == null) { + finish(); + return; + } + targetId = bundle.getString("targetId"); + callId = bundle.getString("callId"); + model = bundle.getString("model"); + initView(); + onCallStatusListener = new CallStatusListener(); + CallClientManager.getManager().addOnVoIPCallListener(onCallStatusListener); + initTargetData(); + if (model.equals(CallClientManager.VIDEO_FLOAT)) { + EasyFloat.dismiss("call"); + callMsg.setTag(true); + callMsg.setImageResource(R.mipmap.ic_call_audio_msg); + gift.setVisibility(View.VISIBLE); + money.setVisibility(View.VISIBLE); + } + if (model.equals(CallClientManager.AUDIO_CALL)) { + CallClientManager.getManager().callAudio(targetId); + callMsg.setTag(true); + callMsg.setImageResource(R.mipmap.ic_call_audio_msg); + } + } + + @Override + protected void onDestroy() { + super.onDestroy(); + CallClientManager.getManager().removeOnVoIPCallListener(onCallStatusListener); + } + + private void initView() { + // 获取全局变量 + vague = findViewById(R.id.vague); + close = findViewById(R.id.close); + avatar = findViewById(R.id.avatar); + follow = findViewById(R.id.follow); + userName = findViewById(R.id.user_name); + userInfo = findViewById(R.id.user_info); + gift = findViewById(R.id.gift); + money = findViewById(R.id.money); + callStop = findViewById(R.id.call_stop); + callMsg = findViewById(R.id.call_msg); + callTime = findViewById(R.id.call_time); + + callMsg.setImageResource(io.rong.callkit.R.drawable.rc_voip_audio_answer_selector_new); + + // 注册点击事件 + vague.setOnClickListener(this); + close.setOnClickListener(this); + avatar.setOnClickListener(this); + follow.setOnClickListener(this); + userName.setOnClickListener(this); + userInfo.setOnClickListener(this); + gift.setOnClickListener(this); + money.setOnClickListener(this); + callStop.setOnClickListener(this); + callMsg.setOnClickListener(this); + callTime.setOnClickListener(this); + + gift.setVisibility(View.INVISIBLE); + money.setVisibility(View.INVISIBLE); + + } + + private void initTargetData() { + OTONetManager.getInstance(mContext) + .getTargetUserInfo(Integer.parseInt(targetId), new HttpCallback() { + @Override + public void onSuccess(UserBean data) { + userName.setText(data.getUser().getUserNicename()); + userInfo.setText(data.getUser().getSignature()); + ImgLoader.display(mContext, data.getUser().getAvatar(), avatar); + ImgLoader.displayBlur(mContext, data.getUser().getAvatar(), vague, 50); + if (data.getUser().isFollow()) { + follow.setVisibility(View.GONE); + } else { + follow.setVisibility(View.VISIBLE); + } + } + + @Override + public void onError(String error) { + } + }); + } + + private void accept() { + CallClientManager.getManager().acceptCall(targetId); + } + + private void stop() { + CallClientManager.getManager().endCall(); + } + + private void showGift() { + new GiftDialog(mContext) + .setTargetId(targetId) + .showDialog(); + } + private void follow(){ + OTONetManager.getInstance(mContext) + .follow(targetId, new HttpCallback() { + @Override + public void onSuccess(FollowBean data) { + ToastUtil.show(WordUtil.getNewString(R.string.system_tip_success)); + follow.setVisibility(View.GONE); + } + + @Override + public void onError(String error) { + + } + }); + } + + private void showWindow(boolean toChatView) { + ImageView icon = new ImageView(mContext); + icon.setTag(getIntent().getBundleExtra("bundle")); + icon.setImageResource(io.rong.callkit.R.drawable.rc_voip_audio_answer_selector_new); + icon.setLayoutParams(new ViewGroup.LayoutParams(DpUtil.dp2px(40), DpUtil.dp2px(40))); + finish(); + EasyFloat.with(this) + .setLayout(icon) + .setShowPattern(ShowPattern.FOREGROUND) + .setTag("call") + .setDragEnable(true) + .setBorder() + .registerCallbacks(new OnFloatCallbacks() { + OnCallStatusListener windowListener; + + @Override + public void createdResult(boolean b, @Nullable String s, @Nullable View view) { + + } + + @Override + public void show(@NonNull View view) { + view.setOnClickListener(v -> { + Bundle bundle = (Bundle) v.getTag(); + bundle.putString("model", CallClientManager.AUDIO_FLOAT); + RouteManager.forwardActivity(RouteManager.ACTIVITY_CALL_AUDIO, bundle); + }); + windowListener = new WindowCallStatusListener(); + CallClientManager.getManager().addOnVoIPCallListener(windowListener); + } + + @Override + public void hide(@NonNull View view) { + + } + + @Override + public void dismiss() { + CallClientManager.getManager().removeOnVoIPCallListener(windowListener); + } + + @Override + public void touchEvent(@NonNull View view, @NonNull MotionEvent motionEvent) { + + } + + @Override + public void drag(@NonNull View view, @NonNull MotionEvent motionEvent) { + + } + + @Override + public void dragEnd(@NonNull View view) { + + } + }).show(); + if (toChatView) { + ConversationUtils.startConversation(mContext, targetId); + } + } + + private class CallStatusListener extends OnCallStatusListener { + @Override + public void onCallWait(SurfaceView surfaceView) { + + } + + @Override + public void onCallStart(String userId, SurfaceView surfaceView) { + gift.setVisibility(View.VISIBLE); + money.setVisibility(View.VISIBLE); + } + + @Override + public void onCallEnd() { + finish(); + } + + @Override + public void onStartFirstFrame() { + + } + + @Override + public void onTime(String time) { + super.onTime(time); + callTime.setText("通话时长:" + time); + } + } + + private static class WindowCallStatusListener extends OnCallStatusListener { + @Override + public void onCallWait(SurfaceView surfaceView) { + + } + + @Override + public void onCallStart(String userId, SurfaceView surfaceView) { + + } + + @Override + public void onCallEnd() { + EasyFloat.dismiss("call"); + } + + @Override + public void onStartFirstFrame() { + } + } + + @Override + public void onClick(View v) { + int id = v.getId(); + if (id == R.id.call_msg) { + if (v.getTag() == null) { + accept(); + v.setTag(true); + callMsg.setImageResource(R.mipmap.ic_call_audio_msg); + } else { + showWindow(true); + } + } else if (id == R.id.call_stop) { + stop(); + } else if (id == R.id.gift) { + showGift(); + } else if (id == R.id.close) { + showWindow(false); + }else if(id ==R.id.follow){ + follow(); + } + } +} diff --git a/OneToOne/src/main/java/com/shayu/onetoone/activity/message/CallVideoActivity.java b/OneToOne/src/main/java/com/shayu/onetoone/activity/message/CallVideoActivity.java index e52020830..4b4ae62bb 100644 --- a/OneToOne/src/main/java/com/shayu/onetoone/activity/message/CallVideoActivity.java +++ b/OneToOne/src/main/java/com/shayu/onetoone/activity/message/CallVideoActivity.java @@ -29,13 +29,20 @@ import com.lzf.easyfloat.interfaces.OnFloatCallbacks; import com.makeramen.roundedimageview.RoundedImageView; import com.shayu.onetoone.R; import com.shayu.onetoone.activity.AbsOTOActivity; +import com.shayu.onetoone.bean.FollowBean; +import com.shayu.onetoone.bean.UserBean; import com.shayu.onetoone.dialog.GiftDialog; import com.shayu.onetoone.listener.OnCallStatusListener; import com.shayu.onetoone.manager.CallClientManager; +import com.shayu.onetoone.manager.OTONetManager; import com.shayu.onetoone.manager.RouteManager; import com.shayu.onetoone.utils.ConversationUtils; +import com.yunbao.common.glide.ImgLoader; +import com.yunbao.common.http.base.HttpCallback; import com.yunbao.common.utils.DpUtil; +import com.yunbao.common.utils.StringUtil; import com.yunbao.common.utils.ToastUtil; +import com.yunbao.common.utils.WordUtil; import java.util.HashMap; @@ -53,6 +60,7 @@ public class CallVideoActivity extends AbsOTOActivity { private ViewGroup myView; private ViewGroup targetView; private ViewGroup callLayout; + private View callWaitLayout; private ViewGroup rootView; private View buttonView; private ImageView callStop; @@ -66,6 +74,7 @@ public class CallVideoActivity extends AbsOTOActivity { private ImageView close; private ImageView follow; private TextView followText; + private TextView callTime; private OnCallStatusListener onCallStatusListener; @@ -97,10 +106,10 @@ public class CallVideoActivity extends AbsOTOActivity { callId = bundle.getString("callId"); model = bundle.getString("model"); - onCallStatusListener = new CallStatusListener(); CallClientManager.getManager().addOnVoIPCallListener(onCallStatusListener); - if (!EasyFloat.isShow("call")) { + + if (!model.equals(CallClientManager.VIDEO_FLOAT)) { RongCallClient.getInstance() .startIncomingPreview( new StartIncomingPreviewCallback() { @@ -108,6 +117,7 @@ public class CallVideoActivity extends AbsOTOActivity { public void onDone(boolean isFront, SurfaceView localVideo) { myView.removeAllViews(); myView.addView(localVideo); + callLayout.setVisibility(View.VISIBLE); } @Override @@ -115,12 +125,17 @@ public class CallVideoActivity extends AbsOTOActivity { } }); } + initButton(buttonView); + if (!StringUtil.isEmpty(callId)) { + CallClientManager.getManager().callVideo(callId); + initWaitView(); + } } @Override protected void onResume() { super.onResume(); - if (EasyFloat.isShow("call") && CallClientManager.getManager().getRemoteVideo(targetId) != null) { + if (model.equals(CallClientManager.VIDEO_FLOAT)) { callLayout.setVisibility(View.GONE); EasyFloat.getFloatView("call").setOnClickListener(null); ((ViewGroup) EasyFloat.getFloatView("call").getParent()).removeAllViews(); @@ -129,11 +144,14 @@ public class CallVideoActivity extends AbsOTOActivity { new Handler(Looper.getMainLooper()) .postDelayed(() -> { - SurfaceView surfaceView = CallClientManager.getManager().getRemoteVideo(targetId); + SurfaceView surfaceView = CallClientManager.getManager().getRemoteVideo(); surfaceView.setLayoutParams(new ViewGroup.LayoutParams(-1, -1)); + initView(); + initButton(buttonView); myView.addView(surfaceView); myView.addView(buttonView); targetView.addView(CallClientManager.getManager().getLocalVideo()); + initTargetData(); }, 300); } @@ -145,9 +163,22 @@ public class CallVideoActivity extends AbsOTOActivity { targetView = findViewById(R.id.target_view); callLayout = findViewById(R.id.rc_voip_two_btn); buttonView = LayoutInflater.from(mContext).inflate(R.layout.view_call_video_item, rootView, false); - initButton(buttonView); - // 为所有View设置点击事件监听器 - setClickListeners(); + callWaitLayout = LayoutInflater.from(mContext).inflate(R.layout.view_call_video_wait, rootView, false); + + } + + private void initWaitView() { + callLayout.setVisibility(View.GONE); + callStop = callWaitLayout.findViewById(R.id.call_stop); + cameraCloseSwitch = callWaitLayout.findViewById(R.id.camera_close_switch); + cameraSwitch = callWaitLayout.findViewById(R.id.camera_switch); + micSwitch = callWaitLayout.findViewById(R.id.mic_switch); + close = callWaitLayout.findViewById(R.id.close); + callStop.setOnClickListener(onClickListener); + cameraCloseSwitch.setOnClickListener(onClickListener); + cameraSwitch.setOnClickListener(onClickListener); + micSwitch.setOnClickListener(onClickListener); + close.setOnClickListener(onClickListener); } private void initButton(View itemView) { @@ -162,6 +193,7 @@ public class CallVideoActivity extends AbsOTOActivity { close = itemView.findViewById(R.id.close); follow = itemView.findViewById(R.id.follow); followText = itemView.findViewById(R.id.follow_text); + callTime = itemView.findViewById(R.id.call_time); // 为所有View设置点击事件监听器 setClickListeners(); } @@ -204,19 +236,19 @@ public class CallVideoActivity extends AbsOTOActivity { } private void switchAudio() { - RongCallClient.getInstance().setEnableLocalAudio(!RongCallClient.getInstance().isLocalAudioEnabled()); - ToastUtil.show("麦克风状态:" + RongCallClient.getInstance().isLocalAudioEnabled()); - micSwitch.setImageResource(RongCallClient.getInstance().isLocalAudioEnabled() ? R.mipmap.ic_call_audio_select : R.mipmap.ic_call_audio); + boolean enabled = RongCallClient.getInstance().isLocalAudioEnabled(); + RongCallClient.getInstance().setEnableLocalAudio(!enabled); + micSwitch.setImageResource(!enabled ? R.mipmap.ic_call_audio_select : R.mipmap.ic_call_audio); } private void showWindow(boolean toChatView) { - CallClientManager.getManager().getRemoteVideo(targetId).setTag(getIntent().getBundleExtra("bundle")); + CallClientManager.getManager().getRemoteVideo().setTag(getIntent().getBundleExtra("bundle")); myView.removeAllViews(); targetView.removeAllViews(); finish(); - CallClientManager.getManager().getRemoteVideo(targetId).setLayoutParams(new ViewGroup.LayoutParams(DpUtil.dp2px(114), DpUtil.dp2px(164))); + CallClientManager.getManager().getRemoteVideo().setLayoutParams(new ViewGroup.LayoutParams(DpUtil.dp2px(114), DpUtil.dp2px(164))); EasyFloat.with(this) - .setLayout(CallClientManager.getManager().getRemoteVideo(targetId)) + .setLayout(CallClientManager.getManager().getRemoteVideo()) .setShowPattern(ShowPattern.FOREGROUND) .setTag("call") .setDragEnable(true) @@ -232,7 +264,9 @@ public class CallVideoActivity extends AbsOTOActivity { @Override public void show(@NonNull View view) { view.setOnClickListener(v -> { - RouteManager.forwardActivity(RouteManager.ACTIVITY_CALL_VIDEO, (Bundle) v.getTag()); + Bundle bundle = (Bundle) v.getTag(); + bundle.putString("model", CallClientManager.VIDEO_FLOAT); + RouteManager.forwardActivity(RouteManager.ACTIVITY_CALL_VIDEO, bundle); }); windowListener = new WindowCallStatusListener(); CallClientManager.getManager().addOnVoIPCallListener(windowListener); @@ -266,10 +300,41 @@ public class CallVideoActivity extends AbsOTOActivity { if (toChatView) { ConversationUtils.startConversation(mContext, targetId); } - - } + private void initTargetData() { + OTONetManager.getInstance(mContext) + .getTargetUserInfo(Integer.parseInt(targetId), new HttpCallback() { + @Override + public void onSuccess(UserBean data) { + ImgLoader.display(mContext, data.getUser().getAvatar(), avatar); + if (data.getUser().isFollow()) { + follow.setVisibility(View.GONE); + } else { + follow.setVisibility(View.VISIBLE); + } + } + + @Override + public void onError(String error) { + } + }); + } + private void follow(){ + OTONetManager.getInstance(mContext) + .follow(targetId, new HttpCallback() { + @Override + public void onSuccess(FollowBean data) { + ToastUtil.show(WordUtil.getNewString(R.string.system_tip_success)); + follow.setVisibility(View.GONE); + } + + @Override + public void onError(String error) { + + } + }); + } private final View.OnClickListener onClickListener = new View.OnClickListener() { @Override public void onClick(View v) { @@ -293,6 +358,8 @@ public class CallVideoActivity extends AbsOTOActivity { showWindow(false); } else if (id == R.id.message) { showWindow(true); + }else if(id == R.id.follow){ + follow(); } } }; @@ -302,10 +369,16 @@ public class CallVideoActivity extends AbsOTOActivity { public void onCallWait(SurfaceView surfaceView) { myView.removeAllViews(); myView.addView(surfaceView); + if (model.equals(CallClientManager.VIDEO_CALL)) { + myView.addView(callWaitLayout); + } } @Override public void onCallStart(String userId, SurfaceView surfaceView) { + initView(); + initButton(buttonView); + initTargetData(); surfaceView.setZOrderOnTop(false); surfaceView.setZOrderMediaOverlay(false); surfaceView.invalidate(); @@ -314,18 +387,11 @@ public class CallVideoActivity extends AbsOTOActivity { surfaceView.setZOrderMediaOverlay(false); surfaceView.invalidate(); }); - if (model.equals(CallClientManager.VIDEO_CALL)) { - targetView.removeAllViews(); - targetView.addView(surfaceView); - myView.removeAllViews(); - myView.addView(CallClientManager.getManager().getLocalVideo()); - } else { - myView.removeAllViews(); - myView.addView(surfaceView); - targetView.removeAllViews(); - targetView.addView(CallClientManager.getManager().getLocalVideo()); - myView.addView(buttonView); - } + myView.removeAllViews(); + myView.addView(surfaceView); + targetView.removeAllViews(); + targetView.addView(CallClientManager.getManager().getLocalVideo()); + myView.addView(buttonView); } @Override @@ -335,13 +401,19 @@ public class CallVideoActivity extends AbsOTOActivity { @Override public void onStartFirstFrame() { - /* CallClientManager.getManager().getRemoteVideo(targetId).setZOrderOnTop(false); - CallClientManager.getManager().getRemoteVideo(targetId).setZOrderMediaOverlay(false); - CallClientManager.getManager().getRemoteVideo(targetId).invalidate();*/ + + } + + @Override + public void onTime(String time) { + super.onTime(time); + if(callTime!=null){ + callTime.setText("通话时长:"+time); + } } } - private class WindowCallStatusListener extends OnCallStatusListener { + private static class WindowCallStatusListener extends OnCallStatusListener { @Override public void onCallWait(SurfaceView surfaceView) { diff --git a/OneToOne/src/main/java/com/shayu/onetoone/listener/OnCallStatusListener.java b/OneToOne/src/main/java/com/shayu/onetoone/listener/OnCallStatusListener.java index 837d60301..3d5bccd10 100644 --- a/OneToOne/src/main/java/com/shayu/onetoone/listener/OnCallStatusListener.java +++ b/OneToOne/src/main/java/com/shayu/onetoone/listener/OnCallStatusListener.java @@ -10,4 +10,7 @@ public abstract class OnCallStatusListener { public abstract void onCallEnd(); public abstract void onStartFirstFrame(); + public void onTime(String time){ + + } } diff --git a/OneToOne/src/main/java/com/shayu/onetoone/manager/CallClientManager.java b/OneToOne/src/main/java/com/shayu/onetoone/manager/CallClientManager.java index 73182e797..07a945d20 100644 --- a/OneToOne/src/main/java/com/shayu/onetoone/manager/CallClientManager.java +++ b/OneToOne/src/main/java/com/shayu/onetoone/manager/CallClientManager.java @@ -2,22 +2,23 @@ package com.shayu.onetoone.manager; import android.Manifest; import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; import android.text.TextUtils; import android.view.SurfaceView; import com.blankj.utilcode.util.PermissionUtils; import com.shayu.onetoone.listener.OnCallStatusListener; -import com.yunbao.common.CommonAppContext; -import com.yunbao.common.manager.IMLoginManager; import com.yunbao.common.utils.ToastUtil; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Locale; +import java.util.Timer; +import java.util.TimerTask; import io.rong.callkit.util.CallKitUtils; -import io.rong.calllib.CallUserProfile; import io.rong.calllib.IRongCallListener; import io.rong.calllib.IRongReceivedCallListener; import io.rong.calllib.RongCallClient; @@ -31,11 +32,16 @@ import io.rong.imlib.RongIMClient; import io.rong.imlib.model.Conversation; public class CallClientManager { - public static final String VIDEO_RECEIVED_CALL = "receivedCall"; - public static final String VIDEO_CALL = "call"; + public static final String VIDEO_RECEIVED_CALL = "receivedCall";//接听 + public static final String VIDEO_CALL = "call";//拨打 + public static final String VIDEO_FLOAT = "floatWindow";//浮窗 + public static final String AUDIO_RECEIVED_CALL = "receivedCall";//接听 + public static final String AUDIO_CALL = "call";//拨打 + public static final String AUDIO_FLOAT = "floatWindow";//浮窗 private static CallClientManager manager; - // private SurfaceView localVideo, remoteVideo; + private SurfaceView localVideo, remoteVideo; private List listeners; + private CallTimeTask timeTask = null; public static CallClientManager getManager() { if (manager == null) { @@ -50,26 +56,11 @@ public class CallClientManager { } public SurfaceView getLocalVideo() { - RongCallSession session = RongCallClient.getInstance().getCallSession(); - String userId= IMLoginManager.get(CommonAppContext.getTopActivity()).getUserInfo().getId()+""; - for (CallUserProfile profile : session.getParticipantProfileList()) { - if(profile.getUserId().equals(userId)){ - return profile.getVideoView(); - } - - } - return null; + return localVideo; } - public SurfaceView getRemoteVideo(String id) { - RongCallSession session = RongCallClient.getInstance().getCallSession(); - for (CallUserProfile profile : session.getParticipantProfileList()) { - if(profile.getUserId().equals(id)){ - return profile.getVideoView(); - } - - } - return null; + public SurfaceView getRemoteVideo() { + return remoteVideo; } private void init() { @@ -84,6 +75,21 @@ public class CallClientManager { listeners.remove(statusListener); } + private void startTimer() { + if (timeTask != null) { + timeTask.cancel(); + } + timeTask = new CallTimeTask(); + new Timer().schedule(timeTask, 0, 1000); + } + + private void endTimer() { + if (timeTask != null) { + timeTask.cancel(); + } + timeTask = null; + } + public void callVideo(String targetId) { RongCallClient.getInstance().setVoIPCallListener(new CallStatusListener(new OnCallStatusListener() { @Override @@ -91,6 +97,7 @@ public class CallClientManager { for (OnCallStatusListener listener : listeners) { listener.onCallWait(localVideo); } + ToastUtil.show("等待对方接受邀请..."); } @Override @@ -98,6 +105,8 @@ public class CallClientManager { for (OnCallStatusListener listener : listeners) { listener.onCallStart(userId, remoteVideo); } + ToastUtil.show("连接成功"); + startTimer(); } @Override @@ -105,6 +114,7 @@ public class CallClientManager { for (OnCallStatusListener listener : listeners) { listener.onCallEnd(); } + endTimer(); } @Override @@ -119,6 +129,45 @@ public class CallClientManager { RongCallClient.getInstance().startCall(Conversation.ConversationType.PRIVATE, targetId, userIds, null, RongCallCommon.CallMediaType.VIDEO, null); } + public void callAudio(String targetId) { + RongCallClient.getInstance().setVoIPCallListener(new CallStatusListener(new OnCallStatusListener() { + @Override + public void onCallWait(SurfaceView localVideo) { + for (OnCallStatusListener listener : listeners) { + listener.onCallWait(localVideo); + } + ToastUtil.show("等待对方接受邀请..."); + } + + @Override + public void onCallStart(String userId, SurfaceView remoteVideo) { + for (OnCallStatusListener listener : listeners) { + listener.onCallStart(userId, remoteVideo); + } + ToastUtil.show("连接成功"); + startTimer(); + } + + @Override + public void onCallEnd() { + for (OnCallStatusListener listener : listeners) { + listener.onCallEnd(); + } + endTimer(); + } + + @Override + public void onStartFirstFrame() { + for (OnCallStatusListener listener : listeners) { + listener.onStartFirstFrame(); + } + } + })); + List userIds = new ArrayList<>(); + userIds.add(targetId); + RongCallClient.getInstance().startCall(Conversation.ConversationType.PRIVATE, targetId, userIds, null, RongCallCommon.CallMediaType.AUDIO, null); + } + public void acceptCall(String callId) { RongCallClient.getInstance().setVoIPCallListener(new CallStatusListener(new OnCallStatusListener() { @Override @@ -133,6 +182,7 @@ public class CallClientManager { for (OnCallStatusListener listener : listeners) { listener.onCallStart(userId, remoteVideo); } + startTimer(); } @Override @@ -140,6 +190,7 @@ public class CallClientManager { for (OnCallStatusListener listener : listeners) { listener.onCallEnd(); } + endTimer(); } @Override @@ -162,6 +213,50 @@ public class CallClientManager { return RongCallClient.getInstance() != null && RongCallClient.getInstance().getCallSession() != null; } + public boolean isCallVideo(RongCallSession callSession) { + return callSession.getMediaType().equals(RongCallCommon.CallMediaType.VIDEO); + } + + public long getTime(long activeTime) { + return activeTime == 0 ? 0 : (System.currentTimeMillis() - activeTime) / 1000; + } + + private class CallTimeTask extends TimerTask { + Handler handler = new Handler(Looper.getMainLooper()); + + @Override + public void run() { + RongCallSession callSession = RongCallClient.getInstance().getCallSession(); + if (callSession == null) { + cancel(); + timeTask = null; + return; + } + long time = getTime(callSession.getActiveTime()); + String extra; + if (time > 0) { + if (time >= 3600) { + extra = + String.format( + Locale.ROOT, + "%d:%02d:%02d", + time / 3600, + (time % 3600) / 60, + (time % 60)); + } else { + extra = String.format(Locale.ROOT, "%02d:%02d", (time % 3600) / 60, (time % 60)); + } + handler.post(() -> { + for (OnCallStatusListener listener : listeners) { + listener.onTime(extra); + } + }); + + } + + } + } + private static class CallMeListener implements IRongReceivedCallListener { @Override @@ -171,7 +266,11 @@ public class CallClientManager { bundle.putString("targetId", callSession.getTargetId()); bundle.putString("callId", callSession.getCallId()); bundle.putString("sessionId", callSession.getSessionId()); - RouteManager.forwardActivity(RouteManager.ACTIVITY_CALL_VIDEO, bundle); + if (callSession.getMediaType() == RongCallCommon.CallMediaType.VIDEO) { + RouteManager.forwardActivity(RouteManager.ACTIVITY_CALL_VIDEO, bundle); + } else { + RouteManager.forwardActivity(RouteManager.ACTIVITY_CALL_AUDIO, bundle); + } } @Override @@ -191,7 +290,6 @@ public class CallClientManager { } } - private class CallStatusListener implements IRongCallListener { OnCallStatusListener statusListener; private long time = 0; @@ -220,8 +318,11 @@ public class CallClientManager { */ @Override public void onCallOutgoing(RongCallSession callSession, SurfaceView localVideo) { - localVideo.setZOrderOnTop(true); - localVideo.setZOrderMediaOverlay(true); + if (isCallVideo(callSession)) { + localVideo.setZOrderOnTop(true); + localVideo.setZOrderMediaOverlay(true); + CallClientManager.this.localVideo = localVideo; + } statusListener.onCallWait(localVideo); System.out.println("CallStatusListener.onCallOutgoing"); } @@ -235,8 +336,11 @@ public class CallClientManager { */ @Override public void onCallConnected(RongCallSession callSession, SurfaceView localVideo) { - localVideo.setZOrderOnTop(true); - localVideo.setZOrderMediaOverlay(true); + if (isCallVideo(callSession)) { + localVideo.setZOrderOnTop(true); + localVideo.setZOrderMediaOverlay(true); + CallClientManager.this.localVideo = localVideo; + } statusListener.onCallWait(localVideo); } @@ -274,6 +378,7 @@ public class CallClientManager { extra = String.format(Locale.ROOT, "%02d:%02d", (time % 3600) / 60, (time % 60)); } } + if (!TextUtils.isEmpty(senderId)) { CallSTerminateMessage message = new CallSTerminateMessage(); message.setReason(reason); @@ -305,6 +410,8 @@ public class CallClientManager { } } statusListener.onCallEnd(); + CallClientManager.this.remoteVideo = null; + CallClientManager.this.localVideo = null; } @Override @@ -328,17 +435,26 @@ public class CallClientManager { * 如果对端调用{@link RongCallClient#startCall(int, boolean, Conversation.ConversationType, String, List, List, RongCallCommon.CallMediaType, String, StartCameraCallback)} 或 * {@link RongCallClient#acceptCall(String, int, boolean, StartCameraCallback)}开始的音视频通话,则可以使用如下设置改变对端视频流的镜像显示:
*
-         *                                                               public void onRemoteUserJoined(String userId, RongCallCommon.CallMediaType mediaType, int userType, SurfaceView remoteVideo) {
-         *                                                                    if (null != remoteVideo) {
-         *                                                                        ((RongRTCVideoView) remoteVideo).setMirror( boolean);//观看对方视频流是否镜像处理
-         *                                                                    }
-         *                                                               }
-         *                                                               
+ * public void onRemoteUserJoined(String userId, RongCallCommon.CallMediaType mediaType, int userType, SurfaceView remoteVideo) { + * if (null != remoteVideo) { + * ((RongRTCVideoView) remoteVideo).setMirror( boolean);//观看对方视频流是否镜像处理 + * } + * } + * */ @Override public void onRemoteUserJoined(String userId, RongCallCommon.CallMediaType mediaType, int userType, SurfaceView remoteVideo) { + if (mediaType == RongCallCommon.CallMediaType.AUDIO) { + statusListener.onCallStart(userId, null); + return; + } + if (CallClientManager.this.remoteVideo != null) { + statusListener.onCallStart(userId, CallClientManager.this.remoteVideo); + return; + } remoteVideo.setZOrderOnTop(false); remoteVideo.setZOrderMediaOverlay(false); + CallClientManager.this.remoteVideo = remoteVideo; statusListener.onCallStart(userId, remoteVideo); System.out.println("CallStatusListener.onRemoteUserJoined"); } diff --git a/OneToOne/src/main/java/com/shayu/onetoone/manager/RouteManager.java b/OneToOne/src/main/java/com/shayu/onetoone/manager/RouteManager.java index 16e669c3a..965efb403 100644 --- a/OneToOne/src/main/java/com/shayu/onetoone/manager/RouteManager.java +++ b/OneToOne/src/main/java/com/shayu/onetoone/manager/RouteManager.java @@ -18,6 +18,7 @@ public class RouteManager { public static final String ACTIVITY_HOME_SEARCH = "/activity/HomeSearchActivity"; public static final String ACTIVITY_HOME_SCREEN = "/activity/HomeScreenActivity"; public static final String ACTIVITY_CALL_VIDEO = "/activity/CallVideoActivity"; + public static final String ACTIVITY_CALL_AUDIO = "/activity/CallVAudioActivity"; public static final String PATH_EDITPROFILE = "/main/EditProfileActivity"; //设置基本资料 diff --git a/OneToOne/src/main/java/com/shayu/onetoone/provider/OTOCallEndMessageItemProvider.java b/OneToOne/src/main/java/com/shayu/onetoone/provider/OTOCallEndMessageItemProvider.java index 733a97bdb..d95de5fbe 100644 --- a/OneToOne/src/main/java/com/shayu/onetoone/provider/OTOCallEndMessageItemProvider.java +++ b/OneToOne/src/main/java/com/shayu/onetoone/provider/OTOCallEndMessageItemProvider.java @@ -1,185 +1,33 @@ package com.shayu.onetoone.provider; -import static io.rong.calllib.RongCallCommon.CallDisconnectedReason.HANGUP; import static io.rong.calllib.RongCallCommon.CallDisconnectedReason.OTHER_DEVICE_HAD_ACCEPTED; import android.content.Context; -import android.content.Intent; -import android.graphics.drawable.Drawable; +import android.os.Bundle; import android.text.Spannable; import android.text.SpannableString; -import android.text.TextUtils; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; import android.widget.Toast; import com.shayu.onetoone.R; +import com.shayu.onetoone.manager.CallClientManager; +import com.shayu.onetoone.manager.RouteManager; import com.yunbao.common.utils.ToastUtil; -import io.rong.callkit.RongCallAction; +import java.util.List; + +import io.rong.callkit.CallEndMessageItemProvider; import io.rong.callkit.util.CallKitUtils; import io.rong.calllib.RongCallClient; import io.rong.calllib.RongCallCommon; import io.rong.calllib.RongCallSession; import io.rong.calllib.message.CallSTerminateMessage; -import io.rong.imkit.conversation.messgelist.provider.BaseMessageItemProvider; import io.rong.imkit.model.UiMessage; import io.rong.imkit.widget.adapter.IViewProviderListener; import io.rong.imkit.widget.adapter.ViewHolder; -import io.rong.imlib.model.Message; import io.rong.imlib.model.MessageContent; -import java.util.List; -import java.util.Locale; -public class OTOCallEndMessageItemProvider extends BaseMessageItemProvider { - @Override - protected io.rong.imkit.widget.adapter.ViewHolder onCreateMessageContentViewHolder( - ViewGroup parent, int viewType) { - View textView = - LayoutInflater.from(parent.getContext()) - .inflate(R.layout.rc_text_message_item, parent, false); - return new ViewHolder(parent.getContext(), textView); - } - - @Override - protected void bindMessageContentViewHolder( - ViewHolder holder, - ViewHolder parentHolder, - CallSTerminateMessage callSTerminateMessage, - UiMessage uiMessage, - int position, - List list, - IViewProviderListener listener) { - Message message = uiMessage.getMessage(); - final TextView view = holder.getView(io.rong.imkit.R.id.rc_text); - if (message.getMessageDirection() == Message.MessageDirection.SEND) { - view.setBackgroundResource(R.drawable.rc_ic_bubble_right); - } else { - view.setBackgroundResource(R.drawable.rc_ic_bubble_left); - } - - RongCallCommon.CallMediaType mediaType = callSTerminateMessage.getMediaType(); - String direction = callSTerminateMessage.getDirection(); - Drawable drawable = null; - - String msgContent = ""; - switch (callSTerminateMessage.getReason()) { - case CANCEL: - msgContent = view.getResources().getString(R.string.rc_voip_mo_cancel); - break; - case REJECT: - msgContent = view.getResources().getString(R.string.rc_voip_mo_reject); - break; - case NO_RESPONSE: - case BUSY_LINE: - msgContent = view.getResources().getString(R.string.rc_voip_mo_no_response); - break; - case REMOTE_BUSY_LINE: - msgContent = view.getResources().getString(R.string.rc_voip_mt_busy); - break; - case REMOTE_CANCEL: - msgContent = view.getResources().getString(R.string.rc_voip_mt_cancel); - break; - case REMOTE_REJECT: - msgContent = view.getResources().getString(R.string.rc_voip_mt_reject); - break; - case REMOTE_NO_RESPONSE: - msgContent = view.getResources().getString(R.string.rc_voip_mt_no_response); - break; - case NETWORK_ERROR: - case REMOTE_NETWORK_ERROR: - case INIT_VIDEO_ERROR: - msgContent = view.getResources().getString(R.string.rc_voip_call_interrupt); - break; - case OTHER_DEVICE_HAD_ACCEPTED: - msgContent = view.getResources().getString(R.string.rc_voip_call_other); - break; - case SERVICE_NOT_OPENED: - case REMOTE_ENGINE_UNSUPPORTED: - msgContent = view.getResources().getString(R.string.rc_voip_engine_notfound); - break; - case REJECTED_BY_BLACKLIST: - msgContent = - view.getResources().getString(R.string.rc_voip_mo_rejected_by_blocklist); - break; - default: - String mo_reject = view.getResources().getString(R.string.rc_voip_mo_reject); - String mt_reject = view.getResources().getString(R.string.rc_voip_mt_reject); - String extra = callSTerminateMessage.getExtra(); - String timeRegex = "([0-9]?[0-9]:)?([0-5][0-9]:)?([0-5][0-9])$"; - if (!TextUtils.isEmpty(extra)) { - boolean val = extra.matches(timeRegex); - if (val) { - msgContent = - view.getResources().getString(R.string.rc_voip_call_time_length); - msgContent += extra; - } else { - msgContent = - view.getResources().getString(R.string.rc_voip_call_time_length); - } - } else { - msgContent = - callSTerminateMessage.getReason() == HANGUP ? mo_reject : mt_reject; - } - break; - } - - view.setText(msgContent); - view.setCompoundDrawablePadding(15); - - if (mediaType.equals(RongCallCommon.CallMediaType.VIDEO)) { - if (direction != null && direction.equals("MO")) { - drawable = view.getResources().getDrawable(R.drawable.rc_voip_video_right); - drawable.setBounds( - 0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); - view.setCompoundDrawablesRelative(null, null, drawable, null); - view.setTextColor(view.getResources().getColor(R.color.rc_voip_color_right)); - } else { - drawable = view.getResources().getDrawable(R.drawable.rc_voip_video_left); - drawable.setBounds( - 0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); - view.setCompoundDrawablesRelative(drawable, null, null, null); - view.setTextColor(view.getResources().getColor(R.color.rc_voip_color_left)); - } - } else { - if (direction != null && direction.equals("MO")) { - if (callSTerminateMessage.getReason().equals(HANGUP) - || callSTerminateMessage - .getReason() - .equals(RongCallCommon.CallDisconnectedReason.REMOTE_HANGUP)) { - drawable = - view.getResources() - .getDrawable(R.drawable.rc_voip_audio_right_connected); - } else { - drawable = - view.getResources().getDrawable(R.drawable.rc_voip_audio_right_cancel); - } - drawable.setBounds( - 0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); - view.setCompoundDrawablesRelative(null, null, drawable, null); - view.setTextColor(view.getResources().getColor(R.color.rc_voip_color_right)); - } else { - if (callSTerminateMessage.getReason().equals(HANGUP) - || callSTerminateMessage - .getReason() - .equals(RongCallCommon.CallDisconnectedReason.REMOTE_HANGUP)) { - drawable = - view.getResources() - .getDrawable(R.drawable.rc_voip_audio_left_connected); - } else { - drawable = - view.getResources().getDrawable(R.drawable.rc_voip_audio_left_cancel); - } - drawable.setBounds( - 0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); - view.setCompoundDrawablesRelative(drawable, null, null, null); - view.setTextColor(view.getResources().getColor(R.color.rc_voip_color_left)); - } - } - } +public class OTOCallEndMessageItemProvider extends CallEndMessageItemProvider { @Override protected boolean onItemClick( @@ -192,7 +40,6 @@ public class OTOCallEndMessageItemProvider extends BaseMessageItemProvider 0) { @@ -221,19 +68,17 @@ public class OTOCallEndMessageItemProvider extends BaseMessageItemProvider + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OneToOne/src/main/res/layout/dialog_bottom_list.xml b/OneToOne/src/main/res/layout/dialog_bottom_list.xml index 05b48de55..b09685671 100644 --- a/OneToOne/src/main/res/layout/dialog_bottom_list.xml +++ b/OneToOne/src/main/res/layout/dialog_bottom_list.xml @@ -34,6 +34,6 @@ app:layout_constraintHorizontal_bias="1.0" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/recyclerView" - tools:text="取消" /> + android:text="取消" /> \ No newline at end of file diff --git a/OneToOne/src/main/res/layout/view_call_video_item.xml b/OneToOne/src/main/res/layout/view_call_video_item.xml index d36c6a597..7648321cc 100644 --- a/OneToOne/src/main/res/layout/view_call_video_item.xml +++ b/OneToOne/src/main/res/layout/view_call_video_item.xml @@ -48,6 +48,17 @@ app:layout_constraintStart_toStartOf="parent" app:srcCompat="@mipmap/ic_call_video_select" /> + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OneToOne/src/main/res/mipmap-xxhdpi/ic_call_audio_msg.png b/OneToOne/src/main/res/mipmap-xxhdpi/ic_call_audio_msg.png new file mode 100644 index 0000000000000000000000000000000000000000..b4e6f3318746b465e3dd29252ef24da4241eaf6c GIT binary patch literal 10670 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 IM6N<$g1TbGz5oCK literal 12616 zcmV-OF}Kc%P)PyA07*naRCr$1eF>CZRhjO$&pCIfDXFBA%A61|tu`VE0hC?_XdGUnHo8zA(vHt= z=*M6SBp{02Sx$(IGWc5Beyy|vt%yw8f=B{B6_i#PoWKBPsLD{Os-(s{oU`BB!@u{x z&%HOP$^i6nVQ$@fhQ0ss`-i=cz<-nD+6yo?87&%sQh;zg?K3s@`lTw zIZQ;Ql^L=lgcPp^#qq$g=t~0Dc9Cr$vTeCvEW~|NHVKTVASkdC4*C*42-9>}sERDKhaE2ziJE zIt8MlqPt=SG*gHX>TmVE=80m9VVGu;{!X2&xCi>meyL8k=cua;Q)QM`gb*Vsvgaa! zZ_XP@{`rjSmQ1{qnD>%k*mY~4>n^78icH9>+e|x>@=q<1}SE^ zYl!QNk@{xzH-IEAX$`EUlaC6Bbt1;UoV#|}vo99oUKECX`Qy(nt44*JGMSwmMWREy zAtl*{3YzfR%xUpge`tGWO(G|i@=M|Oh9RLE(mC2x8eP)=8n)9t%r)M065qVm=|N=K z2qN)~WYLZb&N*jg?S*FC3(BxBt?iqWX7!8WsBoDE3|UIPCNzEA);T9-eH1Ae@x<9j zW+$E5bINIboW&%}rWytrt<#57Yt9PiB()61QLLFRWp+uuHu;qgU$J7x3&pq>gkd+Y z-QFo`<7Xp^&Mg(%4^!xjh$OU~g0W}zb2Gx_+?FG5NM|}rYBXq&WA4xz+u#PZxili- zf79QDuFttn^;=u13zLG`=>)i9d`oaRhlQt^jU&6 zp=Cf|@;4St7&k?VV0{r8Q4x`!@ehuccPqu^#;bbIy9a) zzdIAx)Yb)>8s3lWDubZhUJ`g3G3r@;b+lyr4F}7=9TbMCfVYU!m*co_Qa!Dj zX*$iY5ZhQn3*G1gF+maBIVonQJR`iRGgC1C?@ge4CgU2#Y3u^~k>6LJHGL7#kov#*$wm1_jp<;%E1yVVRh~DLt{vA$#Y<{6vf}@bOuZvEQkC zWRRnABtCHVCzpKZKr-!sF-)02^!A<&$eS9Bm7Rz|+dLMsvdjZn3VliT*$9Iy219xr ztGIYec}f#SJ4;Nr;sC?iSF7#}j086gNlxQ%|JtOA15z$F(7teu*s=W!=iG8&LVyFu zu*)~z6pQ3MmE)tlp1V0sfb5tC57r5Vh?%)JPHA(#kGZTFB;a5Gg%5WFsF&lI9fRjb zV5{9TYgnByp#j`)P^t=P1Y5UEvwQJWU3u}kn zE~4l(4J&RMYB_1xdMh?>9)Gk?a_gjovWU<2wi&kbIAmk72QjuvroGZ-I&3v0$rtY~@Y z?-_tm#trWNRD~c*)7xY*`OrCQ7mn{M)ApHRO0yQ%SNwYtm(Hx$C&Ks%LT1{O01->Z zQWUBPl-bmf0e3n$BOYtUk;ZvqdbO}2ms38nw9SHRPxu`A3n$8{WIGnzqjj`F z^LZ3VIrrxjt(lD_9rwREfKGw41?@!ofB?=SSO%H|r-?p{=DGy3&Pi(b<{h)mQZfPL z2^L^!qeu)j zh%(=X321ttWw!z-QLMhaxxT&s!s(*NX*2B0AK!F-snB*~wKfqJ3ppX|geYFo)m|-- zigE^+sN^Z>oB<0hl(3$u4O;dz)XQHCFAr>g>@`MjL`AA%1f|zd2 z6u+mpG`!wu=wbz%S+j<4!x5gC?5N!$Ftopu0{VLg4$ju^?2xEe*ik40)&nEs=dIjzv}Pn_wH(JXRZ5|}un~~JR_403npQ%a16w3X3Dx@K>JNQ( z={0-Pvb{3w-_|~NNK$WmPBF{%LxlNk_Y9AoY!V?~zE`MB2WA{&aVus~7Q4SXJyY&G zszCOBABcF&sYE^n6rpz=2a$FtX(fvoj50!G?j!0_c0DQ3`J|;dBY~(x?073=b&dl> z+BK8aBVVez>WmE9GLCH{uprm9H3R@9AE5ZUo4QXFC5RJo*x#(3x5s{wJu>Wu8)Rwb z)-4YS5go5`ghr|e(Xkv|4&=71R1q}i0VctC#&*&SAEpYR)DJoLKY^^+D1@R8)Vd** zc1$b>gz}Aw`Aoyqb>(f4UB3j%n*o_Lxlam=y$oXL_4Xdd%sRz#MI1guV<)u?=GyQ4 zSwXU~0Up@|gUawfIW|;0;k+;Qj<(XYJu>VoAKQ4LKy(#=I7nVMwKfJs7_1Z?p<&W4 z$ElLTBsPFEC28s?G;m$p`SuNvvmS0vXy?IW2yNDm0@4}UAL;R^V50MJV*-^AY*qRc z8N|dAh@EeMRN6+Md=Hs>nyDT>Pr=w9f;wmJKKxI0y(wPH)$hMz@wL-t*#EuknYnS) z_A?>HVcN{7)STr8Pe_yb_wf(|4X$5h+klNd9%|+_U~+*5 zB_bY!EcaQ6rUTa2he#nt4~G~&4nioW<*44A$IW6ym3GTA8_7`oY5vrC25VLuWL`5| z$AB9O#ptzXtz9_KY@jVQ>?=({6Zg$-j7N=@krJ|8>J*dJ@hjf<*@YjQGQ(8*sjGK!HO*8d!0?ner3BgS0Q$0E z)e_wSBT6Rdn=viqNe+fZY`2cf)B!27S^p2RZDVVbHU*yS@szBDWZPd$nU#DLY-??{ zcH?U@EW_e+Hhyld1Zd~fKr=fKlQ-r`2A~j^FqYL0`P9QcSRvCFa*zRck`?vXC5JDBqfca6QL>^*B6u)k-We?T_#=ljc+7S~IN zu|Gqs!UU)oEo_mCc!i-= z)1jG&e+`H(-i*TIENiX8SjA~n*do?YtKuzW6lk)#1TO$m&CEPcKtwgv=H3G&s;}`t z9|G~f$%rb8b!eccW5>nv^gfcdp^4TIn?vR;K-6RGcUGW0q~g%lmYvg5KD8y=H2q7Y6iA0aobk!h;?WVI=ZdVk=2 zosj?Zp3f}$WJ`wKx_WCzT&RCfi0H(|M#@G4GyzFL=s~cRpBH}iC3X@H>oM~MQSb1x z0%~*bg)DE?8HodR6t?`0Hii26EIpXNzYQCGw=z4y&o_`~J!RjpLjZ%DTw89S%C1_% zrk6j-C##dyRUf>5`EcIa>c78!$)@9qrH)6d^$C`F4m)X$CYko#J*3vm{Tnz+84wj1 znf4aS)mj?_(;BCYp|LYY?z{#n`y-2kqB`dX2EGR)oj&_iyj9%!Mj1^k6J!d>Xz=*a zw2`ZFC54b4JMoYv>?$$i;VtO;YF|1Fh9AbOKzp$hW0hUUz2|dFf6<&_mu>laF)p4} zt5+F7AljQ8Yre@OEL}_;z;V>n1P6O5cX1ml;{!;`n~`=u0$JDvk(MFlUSg~!ywoY7 zu@y*m9c80l6GH@W!{)jvellehd$Sg%gEbU|sz#u!nQ3bBT^tpWX4M~`b;Y7p4GdFj zh*!+&86O!Rvck>+A!{d$%9FtRGP=vi+q;G)I)qd~SzY@Io0FrUQu{%Js3`kx3!J{! z&8nPck`*GSjXbtvpJ~{}ELiQ;1XS49q}}p-;B_nKVQf(8GcgEK`xf*XHlvugcpMVjT)8*9W|B5jZ?g+VM*~YiD=f)WyQn-^ z&O!z*sB5!ca|;xCI~y(6CBoU~HfysZL3BlOYFW1{*R&uoD5w?`*0buT-u>By{}?dr zo0saj5}uKT>hCd9XuPvUYHulqc$Xzs33nY;r@#=RUva@^!4Q#A@k4mer4Z_ve@P=^_8e zNBd3(F3%^tFa2E}ZB`nq&r}G6b&tPg!jMevx{ys47;rFQxT%egH`)%qFM^H4l^hl{BEsC4x9vzSvy?c zsm|$Wq%5M<$3Zke5||YSQC)aXSab}W*)%xAPi5Ik#jwKg(MSf5HigifT)0?~77r3# zs2~mCE{O&ivfs%f$^BJX2cG-g&Et#2IFj5)MU} z?J~{<4ESr|rI?g!#3WK;#zf9_y@qagrvq7$C4 z9+9>?N{Y%X#N*3og^k;s#a-mwC840f z!1BT%tB9>1V%2Y7tFS`H(_qHu7zAdsd{l5WKUtKS316*t6=XX+ZwnoKch~uftnqEk zY!ZfJ4LbB|sKGwY6bc~8%p{A=z*P$2X;jkJiEn&t+q@)ApBBK3pgEki0yHkMaT}5} zt_60_#+iRV)`hI7ppCXhgcXv|TX^>A3Wa5!czHZ&p!lm|U_%r4oSU*k+! zb!u^FQf{K=jt0b->moBjpdBD%xR1y}I#3u8i5O{AT`azJao;gro!yU(Rd)Fm2qXge zl1SO$4FZ+;q0Y)rhRwGQ)PRBr)JZ#OolJ?RJ z?5YeOExx^`|9x%c_ODJ>$60?(h<3<@xC}&2f?a15>8~-8VTy-G@YIlRDd9UE{f*h; zNh=jX`>krn)2MZ9K$h$_NG@fxH1o=-A5v>&9dbcV}PBt zc1D$k<14RRGzkIZ62Z=_^1gf5Gh#);7*m$bVsT=*(1D5C$os{2F73avn3OK5)hkv} zSvi9O&;ZI(7m=5{yfpaO#RCB|P}Ava?GsDfHo&nk+}He<81Gq!dfTRES!L!m_VuIG zImyl)e+8i$RD-d$xM$GZ((IhPGjB|!yTo>2hWT~ob1^AnvO0dXxZ~3PuNLCc`>JWh zJIlPyXCyYXQ;jZW;}X&?qf`Fi4&Er7?`>MCjbm)iI;6V(HT8_VOxsW&JrYuN1xI5jbUhBC%xEB1&9y>gWwZ~Rf}}D} zYnm-61~A_Vr)`dPIT_24Fqkxt)jT(AT3CB|6SwI+y!DYPuM;I|UD+ezjx~MHM{&H2 zdJ4UZbs5`mM+WcEY!)NTJ%)^Cek`ZuMgx7?lCt<=a!Jauxetn3S((A8?_RpKFA~uL<6pe{C(uBVaF&H#oqp!IUp7354oU^&<= zXtLBunqhR#L#TFaU}mtrv@^+A~Y7G#Ot3)YWRIJ!qS% zZDmEsiNpMPjyDCMupLohK=*F!@sKgZjK`7GW}tK6jfm7ewRsG;ebD0%k}URNw1|=7 z7mx0Z1E4*(I9_n^AcfyyT&ikg;_geg`d%C7NxiM&O(bSqP7`>Wz0oJjlt6gBt}}sP z5^6V{-QL+^(fNk!(CSa;3MgTq{N>C? zfv(@6IJN?vJ5R6xhLC`#gEt=BNO{IJC^HSpRnx7$5RhR`-h~$SJ8{<~TlsDpxrup| z41fpg+2g|H5D|m$4FH2Hv3HY}>_A%Bgm2xtVGI#L9F`t^z ziwA?^o=dk&@?TzM>{1xlE8Sc`^LuTy7Gt76FFG=*B6BEX3@!RL(!y|KM^wEF$&R=9 zWUZ4WTUb^;S#4KHlIXP1=JvD@sSzuvuNKj%@AH0)kt-`e&V3m5w%=J!3e4<34N0}f z0~fRCEkxHXh|nSl9yqCnv|P@Vs9nLuy_an31H=MVPT)NDaa7=FUB*60*wP@Kzw>a^ zjfk(03@KIf$m<5^ji+eBG`X?cxrKbOa;HMoAPcK;)Wnx8N+_c?aED20Dk7hw-e{*jG zM)=ka2BB2TeHfYhBfZRL`iDeel$V0J*z`4Y1Z`S&kaA($eM*hs)Vb*e_q_P zX6w(AxbWI6t-C%!s^J*(;vKq6zxnmap0-+qa=}(Z63Ge|L!P>K5-E1ieF)XIhTi`v z@J=!72o#5p(J@Qdk}Of}`VHjlrw|otZf%*ad-eY%M6Haptq+qu>mha9fmv;-q*;rv zEgkVmMrk`{^}iiaR(9GEYKAwfWU)*Roko@mO^gOu|4H<*kkWcP^}7k2>yL{Xp(fC3n{jay?RcN0%Ht%k`c6!ffMrm;8@X_c{ z&lD-o6L{PUyVQ~Jv5Y5&uw;)_pZ%60gDikIc+C}Ibo*UxGcmlFG z3>nvSOSeoa$l?hgon5-5LF+JTWd4j}LEpX0rO2=BQ`$AK;7(1bspbl0RKx7eXChHKz82{vFF$#rD?_9*_eR^ah6IN| z7LQ|a;T@2Ix6(DTP6^oTZKtA8?Xj^Ka~rH6yNAF*U(lrXskI{CLDNy5iILjiN^$=s zI~D@b7UlSPr>=Q?7{=uxg(nAT6#33ZCi}3-|5h&yVds2hhNimRyCQNC^Edu4WXcS> z6kQWtXfRc5E*$au^}J3!q?o|qy!%iu4(`D?3Umz~hmMiMNmcwtV_{QfPA2>wlI{l@ zT8pxl;;L#9s8;1-@xW!fdusKGUkHfhywRVp8Kso4U6gEr1#QYjX<{Z}tT&u`IgBy& zlu~0Ho$&-lW<9nyml6D~{8ew?=_pj@c?fUM2*t~U5K4>nQ#C;Fdw8g?BHf#>ewW6y zxv(ZerELp_dhc&|h98VXX>t*|x4!{VmIV8DkhLIy9xSBLVY}KhgvVo6J%S>pmpId9 zEuThOd7Zd#_0|q4#GOJ!Z_p$)7}Tf7ltL(YYx@$t8s`I3zpBH91wrsi;ei>PBK*+Y zbx=NRs^*Z071uj3*O()%Wh%p=18S{t-VUVE1jf34uQj-H_Yp|acI*7rSYqs+`6NbX zJc+p0g*n?#K_uIBu2L~}VBUSGsFu3D9P>7x2?h0YmX+^Yq<2>`E-@4--Y#H>!kinc z%q#86q`Z4~eez6woBv!h@To-La+S6Brpsp{O&DQt4!sCit090inZWi1 zw?p})eL1qIhNRwsdNQKZO$++ogQVdn5_G&>?hs1^sX zW$|r@vXZ_gPTMhW>)Q~gWo*;T>T8)*q46rA>mE269V0Kdji+8Kay=cbr^rJ`b-83@D2#SkQkO+9sBmY`zqzlm@V6(d{<2ljt5kM*n9vTZvFD3}f@+Z$P-E zw^9egiuv_qbg#@ZRw6@rd@1H^JJn`rY^Y32VELAOq2c0|)dCOOC`ntxxNCxUZduKB5xSMCTmbJFWhI_lN0kX5>{*Yo8NZtn zVfMz*C`+(-^BLOY>5wmruzSYuux;*xh%`eaW(^;M*+a+JU|KC$qyPXA_DMuRR3k#A zIEal(!=}?C(l8KZF=8x!{%qAVs4cQX#sR@|@mZdE)HjgxGR%bTj3JLd<*IoboDTnR z&5r*P5JxMs=*u+pa|lk&qHVJimg$9L+s*}zymmVyo9ZE^MOgv;OKwA5W#y+;`ftWM ziJ7|($6Q^nGUHW5?4SPwjOc2$37lsStwfJvm@XVfsFnus+~WUeU|5_gKL#k1+bkf@ zk3gNXV9Qy$(CI*%C6|NjW@VAk4L0;{&S1NAtC>tOT++kjm;dnQt9xHTUQmzW{!4az zvLFhd&(A}F52VHSq+Rq$TXOkU3TTQdW?TA7l~hkSY3si zYVSmww3ZQmzx)=KYLc2+a|Tx$4TQjCaRARQ2^m(b&BT)aw`jjMQQC&VS---#-u9?< z5Uo;Gvv-_`&e1=Igh>?p?IP@mNduZ!cAYWeVUleq7lovVN;-Mbo3EOGUBEDf7H5`c zkB&~d1?wg)NKGb<4bhGPIuk}+kuJyKk*2oyZnMJX`>yq(i7~2Lrq9j;Or`QBb~jq{e+9m%Toi<`O%sk z>tm6eSl3$?g)qFeAK~Ts<|fqSgH3;eGEt*lzsJtG>-NKcDS%MKlC5t-*Thnd&`g;} zRh#N~ZqZ#@gJ^o{w|RprF>mNtwJcDZ#L3bSo?iAf#te1NahU7Ord~pbr&fG%j|@|E zDo-rI{QkGfG^(;buvQSBOLP#yvwEb-fW$&=4h+rH^1zN7os=rM(k{Aw0AA4vpCOp*3viXe=5$KJX&-XcE=JE}hLNReO-6C9+b8 z(h{Ct{2h$6?>(?j>YZ4;@x92{bKN@VAe8rvV#tolVa8Nh>|pk`%AUyLWYrt5oV}|l z!_=4SF4?(ONc@v)eRJZ_!uRq>o0SC|lFac!F>FWg0~nho!{V%jmv4Rt+Nvt@2wT*Y zG)@)<@!O@}vZIx{3t zGt~-cWh-KAv5ZwZ+h7~1x50nzlxun~KM$@RI3AD>LZaPmwZJOf?k!S04kH*# zUa`RxdW5KArky=M!LIHnrezY930uDXB=qR4f=8X|`@zl)c&_(BC=k*L(J52*;0apxL`lt7qtc||0UD;wc-_itUFCFkXOP{XSGqT*U z|BNI_cGT-X7w!Sv6tAvZGjLCY=&hlaBOoJB0Sibc{REs0%?|uUH zcybz=Dv?Dg;$xa1tC%!e-flrg>)0#sk17rud>v-#Jf(jn7gI9A zAr{H{7lZpOzV|!oJJr9@KK#ex$|i61YdNa*s|P;YUg*4RygJ&@X-hqlz6HvrI|u=| z8F@5mt%8y4EW!49_hW>&Voyn$&BW5Eu6>a~Z&M>|czfF+<`=ITtEJK7basd{N#kW3 z-)sG90J{u%csOc*)*+7Xuy-v@YE+%Y8JMVzU9#%x*=lp_hNI2vNUVqV!iy|Se_LVMki(m)sT&IuLd$XT6tNt)kYDb|Y zY_|%aXFp=$@V;ouLFLCDZm#@MgxR_RaFl-m!(Lh@8vGG2%o{qULWt1(+R!vL{BX!^fR^M(ItLB=c&$>q8H)P_&|RL59pgI=d&716@hT4* z&yO$Od2ul=Uai(aTchYKu_h>AUg3HXZH&RP5xz`Lmxo&{2&nex45RJOW1_7O)xsdE zr6C{W+BIF_&Y{x*2_c0Iy(kE`FhG5!wOk<1iYQjPQLfBG`@~X|Cgy=Ngx^C`2?M?coB)*TGEen%fJMWJWouqot2{!sAGk!-h zJzpn!Ho(oj_0EAMzP!^;*nCB%QCA~TkH_`DsZa~&Bh+KH#jTN86%cD_)iY_;%gzME zZ8>3j(bA-1UN|ZXh%%is)GIDynIKA41zOdz)DAgPW+FAVfvqjj7ZAHxDaoys$5S~^pY!lXw!brq;%`&es*x^_0Nss3K@YG=1N?Ur2`g`YP}D5O zp3{&($S;8iojM;cYX}Q1m>vgPDwL0(T2P7mB|61VIG1$D`(q)HruBENy1w`JsWM^8 z8s_}jMFZy*qry$ zmVTfuatA)a>&Kzr0QkjZQjQN^+ z=(fq1TgMgj)%!JJdcLY3H;$2J*@0Wn4PW-SuAxJu4sZJ2oTt7gp4e6ST%vnC&fC(o;DE`vV=@-8^V&j zXKL~{-?!I2(>rpr>Qtg!Xv1V}^0vueJ^tR4*PpykIk|n-Fk>G3+G`!9e~shh%q%mD zDU8+8>z9@-B$um6f*Y5|^oLj?ah+_Z$)Yb$h4rLCs1=ecod5n?aaZg@5+ zQ?9D$c*OQBvG@0zp}4ME-`>o;yfT8Rl(!YeW_@VoErP$ub2=_;|I-L-PgS8Ah19u8@PMr9tCkhLL( zksKpnjNxW4D7$+$8w9(&(Hi8V3wvLhgap=Y-2A{B&-nHk_GO~e;*&dhzch?L^5+*0 z-ds%D&aI}CW;KXg8%?xe)V2`X?dcvmkmd=4V1Ddv=XszI%0qY++pWhKNc)}_{<_0( z4SnvZ9;S15YVTxYcMm~zZA&j?NN+yw+F9rC8;ioL4j98!Hbiy|os$7K(oIRSttFzU z-nI%GROUC$q|Pi#ALg97^$lEi2*U(!$Ip*9&${ML@|J<=wp$yJx#T3WK*_H{B-CQJ zch?Ky84BT56`KWKeeBHcFNi5*F{Z-M1I93V*drg=`Az}+TO^`Rlf|J<0sDawlvrbw zqQL-8W&F+0u}h@*+O)mgj6-t$;up7kxW$E97&W3&vf#U<^@}8g5UI>YrIa6h?e#rV z4vMET;RDVv_0UIG4=<0>?509oJhhfqErj>MbspZE(n8?;45F}$3yfA!yr=&1h}8Qq zb3MU)U$bLye@`yk$qPaG=5>O`V2fu*1d34+)p~V(HLd>bU)(rv!&I0sopTNf!_)xQ zt(8gF(C|ORh2&~Nkoi_J=jU^gj-egj%GNW}6`NQUw^gJ>t#(>_Ehnk0L0eCd!{p7e zTtGte$c2t?d}Q8q5sL)%tg;$ovu{{=OTHs#I&5iu?m=T1J@V1>hnGi*yiEukBLrf< zou54;>@i~_ZSYpP-^j;Nmynlz9WvN0ZksJQ)PBak2qz>B-{dhg*rw}=c1Q7kOIZiA z+)+LyA>MxMb=`Y>HU0jw?VvME4e*%@WNCLYc&?Pu`JKhCL&s_(Va|e}xht{LN>8eo z_^px*_V9=^>`pS|;2C!ds|`#K%AtXYCRdva|8x0a9prm(q~4%6S{ZpZhPZiGwRX$N zdw3=N{xXWLe?b^V5Buc>{c~kp`d}Ovu2kRYDz=eO6LAH8%osA`#`w~wCYs6PGp3mI z5>;YqkPqV&$U7#!lTTjJ^>XhBYA>pKTDxL2ow#n*jSJMi6)(u~f-;O|q(XqWeb-Hy zknfHlx&k!H4b=OZ>CxpBGWMl zu}(zg^N+q}pEk$ZFUGzo45JbMLiLLPXG6-@mXr1q)$*QNz2a#V6a{^-<6|9P%y77o zZGdbR8d}JqU%tcNF>S8!ix%7_4Z$lC3Q+;YLK&lzyPt}LxIKlqXXW+XzuYgX@5jY2 zD#K{pkFS;;GTnW+g#3q@rI}}q*Cx~+?LHHii=la->NC3by_qbCwHVUKP+Ul8X|kdHQ(HS`{5_<6Hw2bL zqAL=S+MB_pyy!EvgwWqSK69bL9%3)UP+gm%d#m2`Q2{(Jgt%A66Q4iw>iGxbwe0)M zv6mdf_)LC%;lLu1C9jo{JPK0$8GzRa;E>MJ%z~Go2!I5||@HGaZ%W)1O6{rLY4-jpxum^MBD0000UNJ diff --git a/common/src/main/java/com/yunbao/common/glide/ImgLoader.java b/common/src/main/java/com/yunbao/common/glide/ImgLoader.java index c6f6c01c8..b7f48aa3e 100644 --- a/common/src/main/java/com/yunbao/common/glide/ImgLoader.java +++ b/common/src/main/java/com/yunbao/common/glide/ImgLoader.java @@ -321,6 +321,23 @@ public class ImgLoader { } builder.into(imageView); } + public static void displayBlur(Context context, String url, ImageView imageView,int radius) { + displayBlur(context, url, imageView,-1,-1,radius); + } + public static void displayBlur(Context context, String url, ImageView imageView, int width, int height,int radius) { + if (!contextIsExist(context)) { + return; + } + + RequestBuilder builder = Glide.with(context) + .load(url) + .thumbnail(thumbnail) + .apply(RequestOptions.bitmapTransform(new BlurTransformation(radius))); + if (width != -1 && height != -1) { + builder = builder.override(width, height); + } + builder.into(imageView); + } /** * 显示模糊的毛玻璃图片