package com.netease.nim.demo.avchat; import android.content.Context; import android.graphics.Rect; import android.os.Handler; import android.view.Gravity; import android.view.MotionEvent; import android.view.SurfaceView; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import com.netease.nim.demo.DemoCache; import com.netease.nim.demo.R; import com.netease.nim.demo.avchat.constant.CallStateEnum; import com.netease.nim.uikit.common.util.sys.ScreenUtil; import com.netease.nimlib.sdk.avchat.AVChatManager; import com.netease.nimlib.sdk.avchat.constant.AVChatVideoScalingType; import com.netease.nimlib.sdk.avchat.model.AVChatVideoRender; /** * 视频绘制管理 * Created by hzxuwen on 2015/5/6. */ public class AVChatSurface { private Context context; private AVChatUI manager; private View surfaceRoot; private Handler uiHandler; // constant private static final int PEER_CLOSE_CAMERA = 0; private static final int LOCAL_CLOSE_CAMERA = 1; private static final int AUDIO_TO_VIDEO_WAIT = 2; private static final int TOUCH_SLOP = 10; // view private LinearLayout largeSizePreviewLayout; private FrameLayout smallSizePreviewFrameLayout; private LinearLayout smallSizePreviewLayout; private ImageView smallSizePreviewCoverImg;//stands for peer or local close camera private View largeSizePreviewCoverLayout;//stands for peer or local close camera //render private AVChatVideoRender smallRender; private AVChatVideoRender largeRender; // state private boolean init = false; private boolean localPreviewInSmallSize = true; private boolean isPeerVideoOff = false; private boolean isLocalVideoOff = false; // move private int lastX, lastY; private int inX, inY; private Rect paddingRect; // data private String largeAccount; // 显示在大图像的用户id private String smallAccount; // 显示在小图像的用户id public AVChatSurface(Context context, AVChatUI manager, View surfaceRoot) { this.context = context; this.manager = manager; this.surfaceRoot = surfaceRoot; this.uiHandler = new Handler(context.getMainLooper()); this.smallRender = new AVChatVideoRender(context); this.largeRender = new AVChatVideoRender(context); } private void findViews() { if (init) return; if (surfaceRoot != null) { smallSizePreviewFrameLayout = (FrameLayout) surfaceRoot.findViewById(R.id.small_size_preview_layout); smallSizePreviewLayout = (LinearLayout) surfaceRoot.findViewById(R.id.small_size_preview); smallSizePreviewCoverImg = (ImageView) surfaceRoot.findViewById(R.id.smallSizePreviewCoverImg); smallSizePreviewFrameLayout.setOnTouchListener(touchListener); largeSizePreviewLayout = (LinearLayout) surfaceRoot.findViewById(R.id.large_size_preview); largeSizePreviewCoverLayout = surfaceRoot.findViewById(R.id.notificationLayout); init = true; } } private View.OnTouchListener touchListener = new View.OnTouchListener() { @Override public boolean onTouch(final View v, MotionEvent event) { int x = (int) event.getRawX(); int y = (int) event.getRawY(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: lastX = x; lastY = y; int[] p = new int[2]; smallSizePreviewFrameLayout.getLocationOnScreen(p); inX = x - p[0]; inY = y - p[1]; break; case MotionEvent.ACTION_MOVE: final int diff = Math.max(Math.abs(lastX - x), Math.abs(lastY - y)); if (diff < TOUCH_SLOP) break; if (paddingRect == null) { paddingRect = new Rect(ScreenUtil.dip2px(10), ScreenUtil.dip2px(20), ScreenUtil.dip2px(10), ScreenUtil.dip2px(70)); } int destX, destY; if (x - inX <= paddingRect.left) { destX = paddingRect.left; } else if (x - inX + v.getWidth() >= ScreenUtil.screenWidth - paddingRect.right) { destX = ScreenUtil.screenWidth - v.getWidth() - paddingRect.right; } else { destX = x - inX; } if (y - inY <= paddingRect.top) { destY = paddingRect.top; } else if (y - inY + v.getHeight() >= ScreenUtil.screenHeight - paddingRect.bottom) { destY = ScreenUtil.screenHeight - v.getHeight() - paddingRect.bottom; } else { destY = y - inY; } FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) v.getLayoutParams(); params.gravity = Gravity.NO_GRAVITY; params.leftMargin = destX; params.topMargin = destY; v.setLayoutParams(params); break; case MotionEvent.ACTION_UP: if (Math.max(Math.abs(lastX - x), Math.abs(lastY - y)) <= 5) { if (largeAccount == null || smallAccount == null) { return true; } String temp; switchRender(smallAccount, largeAccount); temp = largeAccount; largeAccount = smallAccount; smallAccount = temp; switchAndSetLayout(); } else { } break; } return true; } }; public void onCallStateChange(CallStateEnum state) { if (CallStateEnum.isVideoMode(state)) findViews(); switch (state) { case VIDEO: largeSizePreviewCoverLayout.setVisibility(View.GONE); break; case OUTGOING_AUDIO_TO_VIDEO: showNotificationLayout(AUDIO_TO_VIDEO_WAIT); break; case INCOMING_AUDIO_TO_VIDEO: break; case AUDIO: if(smallSizePreviewFrameLayout != null) { smallSizePreviewFrameLayout.setVisibility(View.INVISIBLE); } break; default: break; } setSurfaceRoot(CallStateEnum.isVideoMode(state)); } /** * 大图像surfaceview 初始化 * * @param account 显示视频的用户id */ public void initLargeSurfaceView(String account) { largeAccount = account; findViews(); /** * 设置画布,加入到自己的布局中,用于呈现视频图像 * account 要显示视频的用户帐号 */ if (DemoCache.getAccount().equals(account)) { AVChatManager.getInstance().setupLocalVideoRender(largeRender, false, AVChatVideoScalingType.SCALE_ASPECT_BALANCED); } else { AVChatManager.getInstance().setupRemoteVideoRender(account, largeRender, false, AVChatVideoScalingType.SCALE_ASPECT_BALANCED); } addIntoLargeSizePreviewLayout(largeRender); } /** * 小图像surfaceview 初始化 * * @param account * @return */ public void initSmallSurfaceView(String account) { smallAccount = account; findViews(); smallSizePreviewFrameLayout.setVisibility(View.VISIBLE); /** * 设置画布,加入到自己的布局中,用于呈现视频图像 * account 要显示视频的用户帐号 */ if (DemoCache.getAccount().equals(account)) { AVChatManager.getInstance().setupLocalVideoRender(smallRender, false, AVChatVideoScalingType.SCALE_ASPECT_BALANCED); } else { AVChatManager.getInstance().setupRemoteVideoRender(account, smallRender, false, AVChatVideoScalingType.SCALE_ASPECT_BALANCED); } addIntoSmallSizePreviewLayout(smallRender); } /** * 添加surfaceview到largeSizePreviewLayout * * @param surfaceView */ private void addIntoLargeSizePreviewLayout(SurfaceView surfaceView) { if (surfaceView.getParent() != null) { ((ViewGroup) surfaceView.getParent()).removeView(surfaceView); } largeSizePreviewLayout.addView(surfaceView); surfaceView.setZOrderMediaOverlay(false); if (manager.getCallingState() == CallStateEnum.VIDEO || manager.getCallingState() == CallStateEnum.OUTGOING_VIDEO_CALLING) { largeSizePreviewCoverLayout.setVisibility(View.GONE); } } /** * 添加surfaceview到smallSizePreviewLayout */ private void addIntoSmallSizePreviewLayout(SurfaceView surfaceView) { smallSizePreviewCoverImg.setVisibility(View.GONE); if (surfaceView.getParent() != null) { ((ViewGroup) surfaceView.getParent()).removeView(surfaceView); } smallSizePreviewLayout.addView(surfaceView); surfaceView.setZOrderMediaOverlay(true); smallSizePreviewLayout.setVisibility(View.VISIBLE); } /** * 关闭小窗口 */ private void closeSmallSizePreview() { smallSizePreviewCoverImg.setVisibility(View.VISIBLE); } /** * 对方打开了摄像头 */ public void peerVideoOn() { isPeerVideoOff = false; if (localPreviewInSmallSize) { largeSizePreviewCoverLayout.setVisibility(View.GONE); } else { smallSizePreviewCoverImg.setVisibility(View.GONE); } } /** * 对方关闭了摄像头 */ public void peerVideoOff() { isPeerVideoOff = true; if (localPreviewInSmallSize) { //local preview in small size layout, then peer preview should in large size layout showNotificationLayout(PEER_CLOSE_CAMERA); } else { // peer preview in small size layout closeSmallSizePreview(); } } /** * 对方打开了摄像头 */ public void localVideoOn() { isLocalVideoOff = false; if (localPreviewInSmallSize) { smallSizePreviewCoverImg.setVisibility(View.GONE); } else { largeSizePreviewCoverLayout.setVisibility(View.GONE); } } /** * 本地关闭了摄像头 */ public void localVideoOff() { isLocalVideoOff = true; if (localPreviewInSmallSize) closeSmallSizePreview(); else showNotificationLayout(LOCAL_CLOSE_CAMERA); } /** * 摄像头切换时,布局显隐 */ private void switchAndSetLayout() { localPreviewInSmallSize = !localPreviewInSmallSize; largeSizePreviewCoverLayout.setVisibility(View.GONE); smallSizePreviewCoverImg.setVisibility(View.GONE); if (isPeerVideoOff) { peerVideoOff(); } if (isLocalVideoOff) { localVideoOff(); } } /** * 界面提示 * * @param closeType */ private void showNotificationLayout(int closeType) { if(largeSizePreviewCoverLayout == null) { return; } TextView textView = (TextView) largeSizePreviewCoverLayout; switch (closeType) { case PEER_CLOSE_CAMERA: textView.setText(R.string.avchat_peer_close_camera); break; case LOCAL_CLOSE_CAMERA: textView.setText(R.string.avchat_local_close_camera); break; case AUDIO_TO_VIDEO_WAIT: textView.setText(R.string.avchat_audio_to_video_wait); break; default: return; } largeSizePreviewCoverLayout.setVisibility(View.VISIBLE); } /** * 布局是否可见 * * @param visible */ private void setSurfaceRoot(boolean visible) { surfaceRoot.setVisibility(visible ? View.VISIBLE : View.GONE); } /** * 大小图像显示切换 * * @param user1 用户1的account * @param user2 用户2的account */ private void switchRender(String user1, String user2) { //先取消用户的画布 if (DemoCache.getAccount().equals(user1)) { AVChatManager.getInstance().setupLocalVideoRender(null, false, 0); } else { AVChatManager.getInstance().setupRemoteVideoRender(user1, null, false, 0); } if (DemoCache.getAccount().equals(user2)) { AVChatManager.getInstance().setupLocalVideoRender(null, false, 0); } else { AVChatManager.getInstance().setupRemoteVideoRender(user2, null, false, 0); } //交换画布 //如果存在多个用户,建议用Map维护account,render关系. //目前只有两个用户,并且认为这两个account肯定是对的 AVChatVideoRender render1; AVChatVideoRender render2; if(user1.equals(smallAccount)) { render1 = largeRender; render2 = smallRender; } else { render1 = smallRender; render2 = largeRender; } //重新设置上画布 if (user1 == DemoCache.getAccount()) { AVChatManager.getInstance().setupLocalVideoRender(render1, false, AVChatVideoScalingType.SCALE_ASPECT_BALANCED); } else { AVChatManager.getInstance().setupRemoteVideoRender(user1, render1, false, AVChatVideoScalingType.SCALE_ASPECT_BALANCED); } if (user2 == DemoCache.getAccount()) { AVChatManager.getInstance().setupLocalVideoRender(render2, false, AVChatVideoScalingType.SCALE_ASPECT_BALANCED); } else { AVChatManager.getInstance().setupRemoteVideoRender(user2, render2, false, AVChatVideoScalingType.SCALE_ASPECT_BALANCED); } } /** * 是否本地预览图像在小图像(UI上层) * * @return */ public boolean isLocalPreviewInSmallSize() { return localPreviewInSmallSize; } }