基于Bmob的即时通信(15)音视频通话界面

废话

今天我们来编写视频语音通话界面

在即时通信app中,除了基本的文本、语音、图片聊天外,还要有实时语音和视频聊天功能,这个功能是每个即时通信app需要有的,下面我们就来看看如何编写

创建Activity

public class MediaActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_media);
        fbv();
        init();
        event();
    }
}

这个是音视频通话的Activity
我们可以看到,在onCreate中加载了布局R.layout.activity_media,我们看看这个布局是怎么写的
activity_media.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.yk.mchat.app.widget.VideoLayout
        android:id="@+id/media_video_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <com.yk.mchat.app.widget.VoiceLayout
        android:id="@+id/media_voice_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</RelativeLayout>

可以看到,这是一个相对布局,里面有两个子View

  • VideoLayout
  • VoiceLayout
    这两个子View分别是视频和音频通话的布局,并且这两个View不是原生的,需要我们自己编写,后面我们会详细讲解这两个布局,我们先进行下一步

接下来是setContentView后面的三个方法

  • fbv
  • init
  • event

fbv

private void fbv() {
    mVideoLayout = findViewById(R.id.media_video_layout);
    mVoiceLayout = findViewById(R.id.media_voice_layout);
}

init

private void init() {
    initUser();
    initMedia();
}

initUser

private void initUser() {
    if (null != getIntent().getSerializableExtra("to_user")) {
        mToUser = (User) getIntent().getSerializableExtra("to_user");
        mVoiceLayout.setUserInfo(mToUser);
    } else {
        mToUsername = getIntent().getStringExtra("from");
        loadToUser();
    }
}

loadToUser

private void loadToUser() {
    BmobQuery<User> query = new BmobQuery<>();
    query.addWhereEqualTo("username", mToUsername);
    query.include("friends");
    query.findObjects(new FindListener<User>() {
        @Override
        public void done(List<User> list, BmobException e) {
            if (e == null) {
                if (list.size() != 0) {
                    mToUser = list.get(0);
                    mVoiceLayout.setUserInfo(mToUser);
                }
            }
        }
    });
}

initMedia

private void initMedia() {
    EMClient.getInstance().callManager().addCallStateChangeListener(mEmCallStateChangeListener);
    mType = getIntent().getStringExtra("call_type");
    if (mType.equals("call")) {
        call();
    } else if (mType.equals("answer")) {
        answer();
    }
}

call

private void call() {
    mVoiceLayout.setType(VoiceLayout.TYPE_CALL);
    mMediaType = getIntent().getStringExtra("media_type");
    if (mMediaType.equals("voice")) {
        callVoice();
        mVideoLayout.setVisibility(View.GONE);
    } else if (mMediaType.equals("video")) {
        callVideo();
        setSurfaceView();
    }
}

callVoice

private void callVoice() {
    try {
        EMClient.getInstance().callManager().makeVoiceCall(mToUsername);
    } catch (EMServiceNotReadyException e) {
        e.printStackTrace();
    }
}

callVideo

private void callVideo() {
    try {
        EMClient.getInstance().callManager().makeVideoCall(mToUsername);
    } catch (EMServiceNotReadyException e) {
        e.printStackTrace();
    }
}

answer

private void answer() {
    mVoiceLayout.setType(VoiceLayout.TYPE_ANSWER);
    mMediaType = getIntent().getStringExtra("media_type");
    if (mMediaType.equals("voice")) {
        mVideoLayout.setVisibility(View.GONE);
    } else if (mMediaType.equals("video")) {
        setSurfaceView();
    }
}

setSurfaceView

private void setSurfaceView() {
    mLocalSurfaceView = new EMLocalSurfaceView(MediaActivity.this);
    mRemoteSurfaceView = new EMOppositeSurfaceView(MediaActivity.this);
    EMClient.getInstance().callManager().setSurfaceView(mLocalSurfaceView, mRemoteSurfaceView);
    mVideoLayout.addView(mLocalSurfaceView);
}

上面的方法基本上可以从名字上看出功能,最后还有一个就是通话状态监听

EMCallStateChangeListener mEmCallStateChangeListener = new EMCallStateChangeListener() {
    @Override
    public void onCallStateChanged(CallState callState, CallError error) {
        switch (callState) {
            case CONNECTING: // 正在连接对方
                break;
            case CONNECTED: // 双方已经建立连接
                break;
            case ACCEPTED: // 电话接通成功
                mVideoLayout.addView(mRemoteSurfaceView, 0);
                mVoiceLayout.connect();
                break;
            case DISCONNECTED: // 电话断了
                finish();
                break;
            case NETWORK_UNSTABLE: //网络不稳定
                if (error == CallError.ERROR_NO_DATA) {
                    //无通话数据
                } else {
                }
                break;
            case NETWORK_NORMAL: //网络恢复正常
                break;
            default:
                break;
        }
    }
};

基本代码就这些,下面是音视频通话界面的完整代码

package com.yk.mchat.app.activity;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;

import com.hyphenate.chat.EMCallStateChangeListener;
import com.hyphenate.chat.EMClient;
import com.hyphenate.exceptions.EMNoActiveCallException;
import com.hyphenate.exceptions.EMServiceNotReadyException;
import com.hyphenate.media.EMLocalSurfaceView;
import com.hyphenate.media.EMOppositeSurfaceView;
import com.yk.mchat.R;
import com.yk.mchat.app.widget.VideoLayout;
import com.yk.mchat.app.widget.VoiceLayout;
import com.yk.mchat.model.contacts.User;

import java.util.List;

import cn.bmob.v3.BmobQuery;
import cn.bmob.v3.exception.BmobException;
import cn.bmob.v3.listener.FindListener;

public class MediaActivity extends AppCompatActivity {

    private String mType;

    private String mMediaType;

    private User mToUser;

    private String mToUsername;

    private VideoLayout mVideoLayout;

    private VoiceLayout mVoiceLayout;

    private EMLocalSurfaceView mLocalSurfaceView;

    private EMOppositeSurfaceView mRemoteSurfaceView;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_media);
        fbv();
        init();
        event();
    }

    private void fbv() {
        mVideoLayout = findViewById(R.id.media_video_layout);
        mVoiceLayout = findViewById(R.id.media_voice_layout);
    }

    private void init() {
        initUser();
        initMedia();
    }

    private void initUser() {
        if (null != getIntent().getSerializableExtra("to_user")) {
            mToUser = (User) getIntent().getSerializableExtra("to_user");
            mVoiceLayout.setUserInfo(mToUser);
        } else {
            mToUsername = getIntent().getStringExtra("from");
            loadToUser();
        }
    }

    private void loadToUser() {
        BmobQuery<User> query = new BmobQuery<>();
        query.addWhereEqualTo("username", mToUsername);
        query.include("friends");
        query.findObjects(new FindListener<User>() {
            @Override
            public void done(List<User> list, BmobException e) {
                if (e == null) {
                    if (list.size() != 0) {
                        mToUser = list.get(0);
                        mVoiceLayout.setUserInfo(mToUser);
                    }
                }
            }
        });
    }

    private void initMedia() {
        EMClient.getInstance().callManager().addCallStateChangeListener(mEmCallStateChangeListener);

        mType = getIntent().getStringExtra("call_type");
        if (mType.equals("call")) {
            call();
        } else if (mType.equals("answer")) {
            answer();
        }
    }

    private void setSurfaceView() {
        mLocalSurfaceView = new EMLocalSurfaceView(MediaActivity.this);
        mRemoteSurfaceView = new EMOppositeSurfaceView(MediaActivity.this);

        EMClient.getInstance().callManager().setSurfaceView(mLocalSurfaceView, mRemoteSurfaceView);

        mVideoLayout.addView(mLocalSurfaceView);
    }

    private void call() {
        mVoiceLayout.setType(VoiceLayout.TYPE_CALL);
        mMediaType = getIntent().getStringExtra("media_type");
        if (mMediaType.equals("voice")) {
            callVoice();
            mVideoLayout.setVisibility(View.GONE);
        } else if (mMediaType.equals("video")) {
            callVideo();
            setSurfaceView();
        }
    }

    private void callVoice() {
        try {
            EMClient.getInstance().callManager().makeVoiceCall(mToUsername);
        } catch (EMServiceNotReadyException e) {
            e.printStackTrace();
        }
    }

    private void callVideo() {
        try {
            EMClient.getInstance().callManager().makeVideoCall(mToUsername);
        } catch (EMServiceNotReadyException e) {
            e.printStackTrace();
        }
    }

    private void answer() {
        mVoiceLayout.setType(VoiceLayout.TYPE_ANSWER);
        mMediaType = getIntent().getStringExtra("media_type");
        if (mMediaType.equals("voice")) {
            mVideoLayout.setVisibility(View.GONE);
        } else if (mMediaType.equals("video")) {
            setSurfaceView();
        }
    }

    private void event() {

    }

    EMCallStateChangeListener mEmCallStateChangeListener = new EMCallStateChangeListener() {
        @Override
        public void onCallStateChanged(CallState callState, CallError error) {
            switch (callState) {
                case CONNECTING: // 正在连接对方

                    break;
                case CONNECTED: // 双方已经建立连接

                    break;
                case ACCEPTED: // 电话接通成功
                    mVideoLayout.addView(mRemoteSurfaceView, 0);
                    mVoiceLayout.connect();
                    break;
                case DISCONNECTED: // 电话断了
                    finish();
                    break;
                case NETWORK_UNSTABLE: //网络不稳定
                    if (error == CallError.ERROR_NO_DATA) {
                        //无通话数据
                    } else {

                    }
                    break;
                case NETWORK_NORMAL: //网络恢复正常

                    break;
                default:
                    break;
            }
        }
    };
}

今天就到这里

猜你喜欢

转载自blog.csdn.net/weixin_33762130/article/details/88261529