项目中使用EaseUI可以快速的实现简单的聊天功能,但是由于EaseUI中没有实现音视频功能,因此如果需要实现音视频功能,需要自己手动实现,不过由于EaseUI实现了大部分的功能,所以,只需要在此基础上修改即可
1.首先需要音视频的的界面
VideoCall布局:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/root_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true" >
<com.hyphenate.media.EMOppositeSurfaceView
android:id="@+id/opposite_surface"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<RelativeLayout
android:id="@+id/ll_btns"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingLeft="20dp"
android:paddingRight="20dp" >
<LinearLayout
android:id="@+id/ll_top_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_marginTop="5dp"
android:gravity="center_horizontal"
android:orientation="vertical" >
<TextView
android:id="@+id/tv_call_state"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="12dp"
android:paddingRight="12dp"
android:textColor="@color/voip_interface_text_color"
android:textSize="22sp"
android:visibility="visible" />
<com.hyphenate.easeui.widget.MyChronometer
android:id="@+id/chronometer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Chronometer"
android:textColor="#fff"
android:visibility="invisible"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/tv_is_p2p"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#fff" />
<TextView
android:id="@+id/tv_nick"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="张三"
android:textColor="@android:color/white"
android:textSize="20sp"
tools:ignore="HardcodedText" />
</LinearLayout>
<!-- 演示视频录制功能 -->
<Button
android:id="@+id/btn_record_video"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/recording_video"
android:layout_below="@id/ll_top_container"
android:visibility="gone"
/>
<Button
android:id="@+id/btn_switch_camera"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/switch_camera"
android:layout_below="@id/btn_record_video"
/>
<Button
android:id="@+id/btn_capture_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/capture_image"
android:layout_below="@id/btn_switch_camera"
/>
<SeekBar
android:id="@+id/seekbar_y_detal"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:layout_below="@id/btn_capture_image"
android:max="200"
android:progress="100"
/>
<!-- <Button
android:layout_marginTop="3dp"
android:id="@+id/btn_toggle_video_stream"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="暂停视频"
android:layout_below="@id/btn_record_video"
/> -->
<TextView
android:id="@+id/tv_call_monitor"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@+id/local_surface"
android:layout_alignParentRight="true"
android:textColor="#afff"
android:textSize="12sp"
android:layout_marginBottom="6dp"
/>
<com.hyphenate.media.EMLocalSurfaceView
android:id="@+id/local_surface"
android:layout_width="100dp"
android:layout_height="120dp"
android:layout_marginTop="10dp"
android:layout_alignParentRight="true" />
<!--android:layout_above="@+id/ll_surface_baseline"-->
<LinearLayout
android:id="@+id/ll_surface_baseline"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:orientation="vertical"
android:paddingTop="8dp" >
<View
android:layout_width="match_parent"
android:layout_height="1px" />
<LinearLayout
android:id="@+id/ll_bottom_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingBottom="18dp" >
<LinearLayout
android:id="@+id/ll_voice_control"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
tools:ignore="DisableBaselineAlignment">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:orientation="vertical" >
<ImageView
android:id="@+id/iv_mute"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="fitCenter"
android:src="@drawable/em_icon_mute_normal" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="3dp"
android:gravity="center"
android:text="@string/mute"
android:textColor="#666167" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:orientation="vertical" >
<ImageView
android:id="@+id/iv_handsfree"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="fitCenter"
android:src="@drawable/em_icon_speaker_normal" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="3dp"
android:gravity="center"
android:text="@string/Hands_free"
android:textColor="#666167" />
</LinearLayout>
</LinearLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp" >
<Button
android:id="@+id/btn_hangup_call"
android:layout_width="fill_parent"
android:layout_height="55dp"
android:background="@drawable/em_call_hangup_bg"
android:gravity="center"
android:text="@string/hang_up"
android:textColor="@android:color/white"
android:textSize="20sp"
android:visibility="invisible" />
<LinearLayout
android:id="@+id/ll_coming_call"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<Button
android:id="@+id/btn_refuse_call"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:layout_weight="1"
android:background="@drawable/em_call_hangup_bg"
android:gravity="center"
android:text="@string/hang_up"
android:textColor="@android:color/white"
android:textSize="20sp" />
<Button
android:id="@+id/btn_answer_call"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:layout_marginLeft="50dp"
android:layout_weight="1"
android:background="@drawable/em_call_answer_bg"
android:gravity="center"
android:text="@string/answer"
android:textColor="@android:color/white"
android:textSize="20sp" />
</LinearLayout>
</FrameLayout>
</LinearLayout>
</LinearLayout>
</RelativeLayout>
<TextView
android:id="@+id/tv_network_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:textColor="@android:color/white"
android:layout_centerInParent="true"
/>
</RelativeLayout>
VioceCall布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#252C39"
android:orientation="vertical"
android:id="@+id/root_layout"
android:paddingLeft="20dp"
android:paddingRight="20dp" >
<LinearLayout
android:id="@+id/topLayout"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="5dp"
android:layout_weight="1"
android:gravity="center_horizontal"
android:orientation="vertical" >
<TextView
android:id="@+id/tv_call_state"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/voip_interface_text_color"
android:textSize="22sp"
android:paddingLeft="12dp"
android:paddingRight="12dp"
android:visibility="visible" />
<com.hyphenate.easeui.widget.MyChronometer
android:visibility="invisible"
android:id="@+id/chronometer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#fff"
android:text="Chronometer" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/tv_is_p2p"
android:textColor="#fff"
/>
<TextView
android:id="@+id/tv_calling_duration"
android:layout_width="wrap_content"
android:layout_height="25dp"
android:textColor="@color/voip_interface_text_color"
android:textSize="15sp"
android:visibility="visible" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:gravity="center_horizontal"
android:orientation="vertical"
android:layout_weight="2" >
<ImageView
android:id="@+id/swing_card"
android:layout_width="60dp"
android:layout_height="60dp"
android:scaleType="fitXY"
android:layout_marginTop="10dp"
android:src="@drawable/em_default_avatar" />
<TextView
android:id="@+id/tv_nick"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:textColor="@android:color/white"
android:textSize="20sp"
android:text="张三"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/tv_network_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:textColor="@android:color/white" />
</LinearLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="2"
android:orientation="vertical" >
<LinearLayout
android:id="@+id/ll_voice_control"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
tools:ignore="DisableBaselineAlignment">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:orientation="vertical" >
<ImageView
android:id="@+id/iv_mute"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="fitCenter"
android:src="@drawable/em_icon_mute_normal" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="3dp"
android:gravity="center"
android:textColor="#666167"
android:text="@string/mute" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:orientation="vertical" >
<ImageView
android:id="@+id/iv_handsfree"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="fitCenter"
android:src="@drawable/em_icon_speaker_normal" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="3dp"
android:gravity="center"
android:textColor="#666167"
android:text="@string/Hands_free" />
</LinearLayout>
</LinearLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp" >
<Button
android:id="@+id/btn_hangup_call"
android:layout_width="fill_parent"
android:layout_height="60dp"
android:background="@drawable/em_call_hangup_bg"
android:gravity="center"
android:text="@string/hang_up"
android:textColor="@android:color/white"
android:textSize="20sp"
android:visibility="invisible" />
<LinearLayout
android:id="@+id/ll_coming_call"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<Button
android:id="@+id/btn_refuse_call"
android:layout_width="wrap_content"
android:layout_height="60dp"
android:layout_weight="1"
android:background="@drawable/em_call_hangup_bg"
android:gravity="center"
android:text="@string/hang_up"
android:textColor="@android:color/white"
android:textSize="20sp" />
<Button
android:id="@+id/btn_answer_call"
android:layout_width="wrap_content"
android:layout_height="60dp"
android:layout_marginLeft="30dp"
android:layout_weight="1"
android:background="@drawable/em_call_answer_bg"
android:gravity="center"
android:text="@string/answer"
android:textColor="@android:color/white"
android:textSize="20sp" />
</LinearLayout>
</FrameLayout>
</LinearLayout>
</LinearLayout>
视频界面VideoCallActivity:
public class VideoCallActivity extends CallActivity implements OnClickListener {
private boolean isMuteState;
private boolean isHandsfreeState;
private boolean isAnswered;
private boolean endCallTriggerByMe = false;
private boolean monitor = true;
private TextView callStateTextView;
private LinearLayout comingBtnContainer;
private Button refuseBtn;
private Button answerBtn;
private Button hangupBtn;
private ImageView muteImage;
private ImageView handsFreeImage;
private TextView nickTextView;
private Chronometer chronometer;
private LinearLayout voiceContronlLayout;
private RelativeLayout rootContainer;
private LinearLayout topContainer;
private LinearLayout bottomContainer;
private TextView monitorTextView;
private TextView netwrokStatusVeiw;
private Handler uiHandler;
private boolean isInCalling;
boolean isRecording = false;
// private Button recordBtn;
private EMVideoCallHelper callHelper;
private Button toggleVideoBtn;
private BrightnessDataProcess dataProcessor = new BrightnessDataProcess();
// dynamic adjust brightness
class BrightnessDataProcess implements EMCameraDataProcessor {
byte yDelta = 0;
synchronized void setYDelta(byte yDelta) {
Log.d("VideoCallActivity", "brigntness uDelta:" + yDelta);
this.yDelta = yDelta;
}
// data size is width*height*2
// the first width*height is Y, second part is UV
// the storage layout detailed please refer 2.x demo CameraHelper.onPreviewFrame
@Override
public synchronized void onProcessData(byte[] data, Camera camera, final int width, final int height, final int rotateAngel) {
int wh = width * height;
for (int i = 0; i < wh; i++) {
int d = (data[i] & 0xFF) + yDelta;
d = d < 16 ? 16 : d;
d = d > 235 ? 235 : d;
data[i] = (byte)d;
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(savedInstanceState != null){
finish();
return;
}
setContentView(R.layout.em_activity_video_call);
DemoHelper.getInstance().isVideoCalling = true;
callType = 1;
getWindow().addFlags(
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
uiHandler = new Handler();
callStateTextView = (TextView) findViewById(R.id.tv_call_state);
comingBtnContainer = (LinearLayout) findViewById(R.id.ll_coming_call);
rootContainer = (RelativeLayout) findViewById(R.id.root_layout);
refuseBtn = (Button) findViewById(R.id.btn_refuse_call);
answerBtn = (Button) findViewById(R.id.btn_answer_call);
hangupBtn = (Button) findViewById(R.id.btn_hangup_call);
muteImage = (ImageView) findViewById(R.id.iv_mute);
handsFreeImage = (ImageView) findViewById(R.id.iv_handsfree);
callStateTextView = (TextView) findViewById(R.id.tv_call_state);
nickTextView = (TextView) findViewById(R.id.tv_nick);
chronometer = (Chronometer) findViewById(R.id.chronometer);
voiceContronlLayout = (LinearLayout) findViewById(R.id.ll_voice_control);
RelativeLayout btnsContainer = (RelativeLayout) findViewById(R.id.ll_btns);
topContainer = (LinearLayout) findViewById(R.id.ll_top_container);
bottomContainer = (LinearLayout) findViewById(R.id.ll_bottom_container);
monitorTextView = (TextView) findViewById(R.id.tv_call_monitor);
netwrokStatusVeiw = (TextView) findViewById(R.id.tv_network_status);
// recordBtn = (Button) findViewById(R.id.btn_record_video);
Button switchCameraBtn = (Button) findViewById(R.id.btn_switch_camera);
Button captureImageBtn = (Button) findViewById(R.id.btn_capture_image);
SeekBar YDeltaSeekBar = (SeekBar) findViewById(R.id.seekbar_y_detal);
refuseBtn.setOnClickListener(this);
answerBtn.setOnClickListener(this);
hangupBtn.setOnClickListener(this);
muteImage.setOnClickListener(this);
handsFreeImage.setOnClickListener(this);
rootContainer.setOnClickListener(this);
// recordBtn.setOnClickListener(this);
switchCameraBtn.setOnClickListener(this);
captureImageBtn.setOnClickListener(this);
YDeltaSeekBar.setOnSeekBarChangeListener(new YDeltaSeekBarListener());
msgid = UUID.randomUUID().toString();
isInComingCall = getIntent().getBooleanExtra("isComingCall", false);
username = getIntent().getStringExtra("username");
nickTextView.setText(username);
// local surfaceview
localSurface = (EMLocalSurfaceView) findViewById(R.id.local_surface);
localSurface.setZOrderMediaOverlay(true);
localSurface.setZOrderOnTop(true);
// remote surfaceview
oppositeSurface = (EMOppositeSurfaceView) findViewById(R.id.opposite_surface);
// set call state listener
addCallStateListener();
if (!isInComingCall) {// outgoing call
soundPool = new SoundPool(1, AudioManager.STREAM_RING, 0);
outgoing = soundPool.load(this, R.raw.em_outgoing, 1);
comingBtnContainer.setVisibility(View.INVISIBLE);
hangupBtn.setVisibility(View.VISIBLE);
String st = getResources().getString(R.string.Are_connected_to_each_other);
callStateTextView.setText(st);
EMClient.getInstance().callManager().setSurfaceView(localSurface, oppositeSurface);
handler.sendEmptyMessage(MSG_CALL_MAKE_VIDEO);
handler.postDelayed(new Runnable() {
public void run() {
streamID = playMakeCallSounds();
}
}, 300);
} else { // incoming call
callStateTextView.setText("Ringing");
if(EMClient.getInstance().callManager().getCallState() == EMCallStateChangeListener.CallState.IDLE
|| EMClient.getInstance().callManager().getCallState() == EMCallStateChangeListener.CallState.DISCONNECTED) {
// the call has ended
finish();
return;
}
voiceContronlLayout.setVisibility(View.INVISIBLE);
localSurface.setVisibility(View.INVISIBLE);
Uri ringUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE);
audioManager.setMode(AudioManager.MODE_RINGTONE);
audioManager.setSpeakerphoneOn(true);
ringtone = RingtoneManager.getRingtone(this, ringUri);
ringtone.play();
EMClient.getInstance().callManager().setSurfaceView(localSurface, oppositeSurface);
}
final int MAKE_CALL_TIMEOUT = 50 * 1000;
handler.removeCallbacks(timeoutHangup);
handler.postDelayed(timeoutHangup, MAKE_CALL_TIMEOUT);
// get instance of call helper, should be called after setSurfaceView was called
callHelper = EMClient.getInstance().callManager().getVideoCallHelper();
EMClient.getInstance().callManager().setCameraDataProcessor(dataProcessor);
}
class YDeltaSeekBarListener implements SeekBar.OnSeekBarChangeListener {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
dataProcessor.setYDelta((byte)(20.0f * (progress - 50) / 50.0f));
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
}
/**
* set call state listener
*/
void addCallStateListener() {
callStateListener = new EMCallStateChangeListener() {
@Override
public void onCallStateChanged(final CallState callState, final CallError error) {
switch (callState) {
case CONNECTING: // is connecting
runOnUiThread(new Runnable() {
@Override
public void run() {
callStateTextView.setText(R.string.Are_connected_to_each_other);
}
});
break;
case CONNECTED: // connected
runOnUiThread(new Runnable() {
@Override
public void run() {
callStateTextView.setText(R.string.have_connected_with);
}
});
break;
case ACCEPTED: // call is accepted
handler.removeCallbacks(timeoutHangup);
runOnUiThread(new Runnable() {
@Override
public void run() {
try {
if (soundPool != null)
soundPool.stop(streamID);
EMLog.d("EMCallManager", "soundPool stop ACCEPTED");
} catch (Exception e) {
}
openSpeakerOn();
((TextView)findViewById(R.id.tv_is_p2p)).setText(EMClient.getInstance().callManager().isDirectCall()
? R.string.direct_call : R.string.relay_call);
handsFreeImage.setImageResource(R.drawable.em_icon_speaker_on);
isHandsfreeState = true;
isInCalling = true;
chronometer.setVisibility(View.VISIBLE);
chronometer.setBase(SystemClock.elapsedRealtime());
// call durations start
chronometer.start();
nickTextView.setVisibility(View.INVISIBLE);
callStateTextView.setText(R.string.In_the_call);
// recordBtn.setVisibility(View.VISIBLE);
callingState = CallingState.NORMAL;
startMonitor();
}
});
break;
case NETWORK_DISCONNECTED:
runOnUiThread(new Runnable() {
public void run() {
netwrokStatusVeiw.setVisibility(View.VISIBLE);
netwrokStatusVeiw.setText(R.string.network_unavailable);
}
});
break;
case NETWORK_UNSTABLE:
runOnUiThread(new Runnable() {
public void run() {
netwrokStatusVeiw.setVisibility(View.VISIBLE);
if(error == CallError.ERROR_NO_DATA){
netwrokStatusVeiw.setText(R.string.no_call_data);
}else{
netwrokStatusVeiw.setText(R.string.network_unstable);
}
}
});
break;
case NETWORK_NORMAL:
runOnUiThread(new Runnable() {
public void run() {
netwrokStatusVeiw.setVisibility(View.INVISIBLE);
}
});
break;
case VIDEO_PAUSE:
runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(getApplicationContext(), "VIDEO_PAUSE", Toast.LENGTH_SHORT).show();
}
});
break;
case VIDEO_RESUME:
runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(getApplicationContext(), "VIDEO_RESUME", Toast.LENGTH_SHORT).show();
}
});
break;
case VOICE_PAUSE:
runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(getApplicationContext(), "VOICE_PAUSE", Toast.LENGTH_SHORT).show();
}
});
break;
case VOICE_RESUME:
runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(getApplicationContext(), "VOICE_RESUME", Toast.LENGTH_SHORT).show();
}
});
break;
case DISCONNECTED: // call is disconnected
handler.removeCallbacks(timeoutHangup);
@SuppressWarnings("UnnecessaryLocalVariable")final CallError fError = error;
runOnUiThread(new Runnable() {
private void postDelayedCloseMsg() {
uiHandler.postDelayed(new Runnable() {
@Override
public void run() {
removeCallStateListener();
saveCallRecord();
Animation animation = new AlphaAnimation(1.0f, 0.0f);
animation.setDuration(1200);
rootContainer.startAnimation(animation);
finish();
}
}, 200);
}
@Override
public void run() {
chronometer.stop();
callDruationText = chronometer.getText().toString();
String s1 = getResources().getString(R.string.The_other_party_refused_to_accept);
String s2 = getResources().getString(R.string.Connection_failure);
String s3 = getResources().getString(R.string.The_other_party_is_not_online);
String s4 = getResources().getString(R.string.The_other_is_on_the_phone_please);
String s5 = getResources().getString(R.string.The_other_party_did_not_answer);
String s6 = getResources().getString(R.string.hang_up);
String s7 = getResources().getString(R.string.The_other_is_hang_up);
String s8 = getResources().getString(R.string.did_not_answer);
String s9 = getResources().getString(R.string.Has_been_cancelled);
String s10 = getResources().getString(R.string.Refused);
if (fError == CallError.REJECTED) {
callingState = CallingState.BEREFUSED;
callStateTextView.setText(s1);
} else if (fError == CallError.ERROR_TRANSPORT) {
callStateTextView.setText(s2);
} else if (fError == CallError.ERROR_UNAVAILABLE) {
callingState = CallingState.OFFLINE;
callStateTextView.setText(s3);
} else if (fError == CallError.ERROR_BUSY) {
callingState = CallingState.BUSY;
callStateTextView.setText(s4);
} else if (fError == CallError.ERROR_NORESPONSE) {
callingState = CallingState.NO_RESPONSE;
callStateTextView.setText(s5);
}else if (fError == CallError.ERROR_LOCAL_SDK_VERSION_OUTDATED || fError == CallError.ERROR_REMOTE_SDK_VERSION_OUTDATED){
callingState = CallingState.VERSION_NOT_SAME;
callStateTextView.setText(R.string.call_version_inconsistent);
} else {
if (isRefused) {
callingState = CallingState.REFUSED;
callStateTextView.setText(s10);
}
else if (isAnswered) {
callingState = CallingState.NORMAL;
if (endCallTriggerByMe) {
// callStateTextView.setText(s6);
} else {
callStateTextView.setText(s7);
}
} else {
if (isInComingCall) {
callingState = CallingState.UNANSWERED;
callStateTextView.setText(s8);
} else {
if (callingState != CallingState.NORMAL) {
callingState = CallingState.CANCELLED;
callStateTextView.setText(s9);
} else {
callStateTextView.setText(s6);
}
}
}
}
Toast.makeText(VideoCallActivity.this, callStateTextView.getText(), Toast.LENGTH_SHORT).show();
postDelayedCloseMsg();
}
});
break;
default:
break;
}
}
};
EMClient.getInstance().callManager().addCallStateChangeListener(callStateListener);
}
void removeCallStateListener() {
EMClient.getInstance().callManager().removeCallStateChangeListener(callStateListener);
}
@Override
public void onClick(View v) {
int i = v.getId();
if (i == R.id.btn_refuse_call) {
isRefused = true;
refuseBtn.setEnabled(false);
handler.sendEmptyMessage(MSG_CALL_REJECT);
} else if (i == R.id.btn_answer_call) {
EMLog.d(TAG, "btn_answer_call clicked");
answerBtn.setEnabled(false);
openSpeakerOn();
if (ringtone != null)
ringtone.stop();
callStateTextView.setText("answering...");
handler.sendEmptyMessage(MSG_CALL_ANSWER);
handsFreeImage.setImageResource(R.drawable.em_icon_speaker_on);
isAnswered = true;
isHandsfreeState = true;
comingBtnContainer.setVisibility(View.INVISIBLE);
hangupBtn.setVisibility(View.VISIBLE);
voiceContronlLayout.setVisibility(View.VISIBLE);
localSurface.setVisibility(View.VISIBLE);
} else if (i == R.id.btn_hangup_call) {
hangupBtn.setEnabled(false);
chronometer.stop();
endCallTriggerByMe = true;
callStateTextView.setText(getResources().getString(R.string.hanging_up));
if (isRecording) {
callHelper.stopVideoRecord();
}
EMLog.d(TAG, "btn_hangup_call");
handler.sendEmptyMessage(MSG_CALL_END);
} else if (i == R.id.iv_mute) {
if (isMuteState) {
// resume voice transfer
muteImage.setImageResource(R.drawable.em_icon_mute_normal);
try {
EMClient.getInstance().callManager().resumeVoiceTransfer();
} catch (HyphenateException e) {
e.printStackTrace();
}
isMuteState = false;
} else {
// pause voice transfer
muteImage.setImageResource(R.drawable.em_icon_mute_on);
try {
EMClient.getInstance().callManager().pauseVoiceTransfer();
} catch (HyphenateException e) {
e.printStackTrace();
}
isMuteState = true;
}
} else if (i == R.id.iv_handsfree) {
if (isHandsfreeState) {
// turn off speaker
handsFreeImage.setImageResource(R.drawable.em_icon_speaker_normal);
closeSpeakerOn();
isHandsfreeState = false;
} else {
handsFreeImage.setImageResource(R.drawable.em_icon_speaker_on);
openSpeakerOn();
isHandsfreeState = true;
}
/*
case R.id.btn_record_video: //record the video
if(!isRecording){
// callHelper.startVideoRecord(PathUtil.getInstance().getVideoPath().getAbsolutePath());
callHelper.startVideoRecord("/sdcard/");
EMLog.d(TAG, "startVideoRecord:" + PathUtil.getInstance().getVideoPath().getAbsolutePath());
isRecording = true;
recordBtn.setText(R.string.stop_record);
}else{
String filepath = callHelper.stopVideoRecord();
isRecording = false;
recordBtn.setText(R.string.recording_video);
Toast.makeText(getApplicationContext(), String.format(getString(R.string.record_finish_toast), filepath), Toast.LENGTH_LONG).show();
}
break;
*/
} else if (i == R.id.root_layout) {
if (callingState == CallingState.NORMAL) {
if (bottomContainer.getVisibility() == View.VISIBLE) {
bottomContainer.setVisibility(View.GONE);
topContainer.setVisibility(View.GONE);
oppositeSurface.setScaleMode(VideoView.EMCallViewScaleMode.EMCallViewScaleModeAspectFill);
} else {
bottomContainer.setVisibility(View.VISIBLE);
topContainer.setVisibility(View.VISIBLE);
oppositeSurface.setScaleMode(VideoView.EMCallViewScaleMode.EMCallViewScaleModeAspectFit);
}
}
} else if (i == R.id.btn_switch_camera) {
handler.sendEmptyMessage(MSG_CALL_SWITCH_CAMERA);
} else if (i == R.id.btn_capture_image) {
DateFormat df = DateFormat.getDateTimeInstance();
Date d = new Date();
final String filename = Environment.getExternalStorageDirectory() + df.format(d) + ".jpg";
EMClient.getInstance().callManager().getVideoCallHelper().takePicture(filename);
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(VideoCallActivity.this, "saved image to:" + filename, Toast.LENGTH_SHORT).show();
}
});
} else {
}
}
@Override
protected void onDestroy() {
DemoHelper.getInstance().isVideoCalling = false;
stopMonitor();
if(isRecording){
callHelper.stopVideoRecord();
isRecording = false;
}
localSurface.getRenderer().dispose();
localSurface = null;
oppositeSurface.getRenderer().dispose();
oppositeSurface = null;
super.onDestroy();
}
@Override
public void onBackPressed() {
callDruationText = chronometer.getText().toString();
super.onBackPressed();
}
/**
* for debug & testing, you can remove this when release
*/
void startMonitor(){
monitor = true;
new Thread(new Runnable() {
public void run() {
while(monitor){
runOnUiThread(new Runnable() {
public void run() {
monitorTextView.setText("WidthxHeight:"+callHelper.getVideoWidth()+"x"+callHelper.getVideoHeight()
+ "\nDelay:" + callHelper.getVideoLatency()
+ "\nFramerate:" + callHelper.getVideoFrameRate()
+ "\nLost:" + callHelper.getVideoLostRate()
+ "\nLocalBitrate:" + callHelper.getLocalBitrate()
+ "\nRemoteBitrate:" + callHelper.getRemoteBitrate());
((TextView)findViewById(R.id.tv_is_p2p)).setText(EMClient.getInstance().callManager().isDirectCall()
? R.string.direct_call : R.string.relay_call);
}
});
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
}
}
}
}, "CallMonitor").start();
}
void stopMonitor(){
monitor = false;
}
@Override
protected void onUserLeaveHint() {
super.onUserLeaveHint();
if(isInCalling){
try {
EMClient.getInstance().callManager().pauseVideoTransfer();
} catch (HyphenateException e) {
e.printStackTrace();
}
}
}
@Override
protected void onResume() {
super.onResume();
if(isInCalling){
try {
EMClient.getInstance().callManager().resumeVideoTransfer();
} catch (HyphenateException e) {
e.printStackTrace();
}
}
}
}
音频界面:
/**
* 语音通话页面
*
*/
public class VoiceCallActivity extends CallActivity implements OnClickListener {
private LinearLayout comingBtnContainer;
private Button hangupBtn;
private Button refuseBtn;
private Button answerBtn;
private ImageView muteImage;
private ImageView handsFreeImage;
private boolean isMuteState;
private boolean isHandsfreeState;
private TextView callStateTextView;
private boolean endCallTriggerByMe = false;
private Chronometer chronometer;
String st1;
private LinearLayout voiceContronlLayout;
private TextView netwrokStatusVeiw;
private boolean monitor = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(savedInstanceState != null){
finish();
return;
}
setContentView(R.layout.em_activity_voice_call);
DemoHelper.getInstance().isVoiceCalling = true;
callType = 0;
comingBtnContainer = (LinearLayout) findViewById(R.id.ll_coming_call);
refuseBtn = (Button) findViewById(R.id.btn_refuse_call);
answerBtn = (Button) findViewById(R.id.btn_answer_call);
hangupBtn = (Button) findViewById(R.id.btn_hangup_call);
muteImage = (ImageView) findViewById(R.id.iv_mute);
handsFreeImage = (ImageView) findViewById(R.id.iv_handsfree);
callStateTextView = (TextView) findViewById(R.id.tv_call_state);
TextView nickTextView = (TextView) findViewById(R.id.tv_nick);
TextView durationTextView = (TextView) findViewById(R.id.tv_calling_duration);
chronometer = (Chronometer) findViewById(R.id.chronometer);
voiceContronlLayout = (LinearLayout) findViewById(R.id.ll_voice_control);
netwrokStatusVeiw = (TextView) findViewById(R.id.tv_network_status);
refuseBtn.setOnClickListener(this);
answerBtn.setOnClickListener(this);
hangupBtn.setOnClickListener(this);
muteImage.setOnClickListener(this);
handsFreeImage.setOnClickListener(this);
getWindow().addFlags(
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
addCallStateListener();
msgid = UUID.randomUUID().toString();
username = getIntent().getStringExtra("username");
isInComingCall = getIntent().getBooleanExtra("isComingCall", false);
nickTextView.setText(username);
if (!isInComingCall) {// outgoing call
soundPool = new SoundPool(1, AudioManager.STREAM_RING, 0);
outgoing = soundPool.load(this, R.raw.em_outgoing, 1);
comingBtnContainer.setVisibility(View.INVISIBLE);
hangupBtn.setVisibility(View.VISIBLE);
st1 = getResources().getString(R.string.Are_connected_to_each_other);
callStateTextView.setText(st1);
handler.sendEmptyMessage(MSG_CALL_MAKE_VOICE);
handler.postDelayed(new Runnable() {
public void run() {
streamID = playMakeCallSounds();
}
}, 300);
} else { // incoming call
voiceContronlLayout.setVisibility(View.INVISIBLE);
Uri ringUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE);
audioManager.setMode(AudioManager.MODE_RINGTONE);
audioManager.setSpeakerphoneOn(true);
ringtone = RingtoneManager.getRingtone(this, ringUri);
ringtone.play();
}
final int MAKE_CALL_TIMEOUT = 50 * 1000;
handler.removeCallbacks(timeoutHangup);
handler.postDelayed(timeoutHangup, MAKE_CALL_TIMEOUT);
}
/**
* set call state listener
*/
void addCallStateListener() {
callStateListener = new EMCallStateChangeListener() {
@Override
public void onCallStateChanged(CallState callState, final CallError error) {
// Message msg = handler.obtainMessage();
EMLog.d("EMCallManager", "onCallStateChanged:" + callState);
switch (callState) {
case CONNECTING:
runOnUiThread(new Runnable() {
@Override
public void run() {
callStateTextView.setText(st1);
}
});
break;
case CONNECTED:
runOnUiThread(new Runnable() {
@Override
public void run() {
String st3 = getResources().getString(R.string.have_connected_with);
callStateTextView.setText(st3);
}
});
break;
case ACCEPTED:
handler.removeCallbacks(timeoutHangup);
runOnUiThread(new Runnable() {
@Override
public void run() {
try {
if (soundPool != null)
soundPool.stop(streamID);
} catch (Exception e) {
}
if(!isHandsfreeState)
closeSpeakerOn();
//show relay or direct call, for testing purpose
((TextView)findViewById(R.id.tv_is_p2p)).setText(EMClient.getInstance().callManager().isDirectCall()
? R.string.direct_call : R.string.relay_call);
chronometer.setVisibility(View.VISIBLE);
chronometer.setBase(SystemClock.elapsedRealtime());
// duration start
chronometer.start();
String str4 = getResources().getString(R.string.In_the_call);
callStateTextView.setText(str4);
callingState = CallingState.NORMAL;
startMonitor();
}
});
break;
case NETWORK_UNSTABLE:
runOnUiThread(new Runnable() {
public void run() {
netwrokStatusVeiw.setVisibility(View.VISIBLE);
if(error == CallError.ERROR_NO_DATA){
netwrokStatusVeiw.setText(R.string.no_call_data);
}else{
netwrokStatusVeiw.setText(R.string.network_unstable);
}
}
});
break;
case NETWORK_NORMAL:
runOnUiThread(new Runnable() {
public void run() {
netwrokStatusVeiw.setVisibility(View.INVISIBLE);
}
});
break;
case VOICE_PAUSE:
runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(getApplicationContext(), "VOICE_PAUSE", Toast.LENGTH_SHORT).show();
}
});
break;
case VOICE_RESUME:
runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(getApplicationContext(), "VOICE_RESUME", Toast.LENGTH_SHORT).show();
}
});
break;
case DISCONNECTED:
handler.removeCallbacks(timeoutHangup);
@SuppressWarnings("UnnecessaryLocalVariable") final CallError fError = error;
runOnUiThread(new Runnable() {
private void postDelayedCloseMsg() {
handler.postDelayed(new Runnable() {
@Override
public void run() {
runOnUiThread(new Runnable() {
@Override
public void run() {
Log.d("AAA", "CALL DISCONNETED");
removeCallStateListener();
saveCallRecord();
Animation animation = new AlphaAnimation(1.0f, 0.0f);
animation.setDuration(800);
findViewById(R.id.root_layout).startAnimation(animation);
finish();
}
});
}
}, 200);
}
@Override
public void run() {
chronometer.stop();
callDruationText = chronometer.getText().toString();
String st1 = getResources().getString(R.string.Refused);
String st2 = getResources().getString(R.string.The_other_party_refused_to_accept);
String st3 = getResources().getString(R.string.Connection_failure);
String st4 = getResources().getString(R.string.The_other_party_is_not_online);
String st5 = getResources().getString(R.string.The_other_is_on_the_phone_please);
String st6 = getResources().getString(R.string.The_other_party_did_not_answer_new);
String st7 = getResources().getString(R.string.hang_up);
String st8 = getResources().getString(R.string.The_other_is_hang_up);
String st9 = getResources().getString(R.string.did_not_answer);
String st10 = getResources().getString(R.string.Has_been_cancelled);
String st11 = getResources().getString(R.string.hang_up);
if (fError == CallError.REJECTED) {
callingState = CallingState.BEREFUSED;
callStateTextView.setText(st2);
} else if (fError == CallError.ERROR_TRANSPORT) {
callStateTextView.setText(st3);
} else if (fError == CallError.ERROR_UNAVAILABLE) {
callingState = CallingState.OFFLINE;
callStateTextView.setText(st4);
} else if (fError == CallError.ERROR_BUSY) {
callingState = CallingState.BUSY;
callStateTextView.setText(st5);
} else if (fError == CallError.ERROR_NORESPONSE) {
callingState = CallingState.NO_RESPONSE;
callStateTextView.setText(st6);
} else if (fError == CallError.ERROR_LOCAL_SDK_VERSION_OUTDATED || fError == CallError.ERROR_REMOTE_SDK_VERSION_OUTDATED){
callingState = CallingState.VERSION_NOT_SAME;
callStateTextView.setText(R.string.call_version_inconsistent);
} else {
if (isRefused) {
callingState = CallingState.REFUSED;
callStateTextView.setText(st1);
}
else if (isAnswered) {
callingState = CallingState.NORMAL;
if (endCallTriggerByMe) {
// callStateTextView.setText(st7);
} else {
callStateTextView.setText(st8);
}
} else {
if (isInComingCall) {
callingState = CallingState.UNANSWERED;
callStateTextView.setText(st9);
} else {
if (callingState != CallingState.NORMAL) {
callingState = CallingState.CANCELLED;
callStateTextView.setText(st10);
}else {
callStateTextView.setText(st11);
}
}
}
}
postDelayedCloseMsg();
}
});
break;
default:
break;
}
}
};
EMClient.getInstance().callManager().addCallStateChangeListener(callStateListener);
}
void removeCallStateListener() {
EMClient.getInstance().callManager().removeCallStateChangeListener(callStateListener);
}
@Override
public void onClick(View v) {
int i = v.getId();
if (i == R.id.btn_refuse_call) {
isRefused = true;
refuseBtn.setEnabled(false);
handler.sendEmptyMessage(MSG_CALL_REJECT);
} else if (i == R.id.btn_answer_call) {
answerBtn.setEnabled(false);
closeSpeakerOn();
callStateTextView.setText("正在接听...");
comingBtnContainer.setVisibility(View.INVISIBLE);
hangupBtn.setVisibility(View.VISIBLE);
voiceContronlLayout.setVisibility(View.VISIBLE);
handler.sendEmptyMessage(MSG_CALL_ANSWER);
} else if (i == R.id.btn_hangup_call) {
hangupBtn.setEnabled(false);
chronometer.stop();
endCallTriggerByMe = true;
callStateTextView.setText(getResources().getString(R.string.hanging_up));
handler.sendEmptyMessage(MSG_CALL_END);
} else if (i == R.id.iv_mute) {
if (isMuteState) {
muteImage.setImageResource(R.drawable.em_icon_mute_normal);
try {
EMClient.getInstance().callManager().resumeVoiceTransfer();
} catch (HyphenateException e) {
e.printStackTrace();
}
isMuteState = false;
} else {
muteImage.setImageResource(R.drawable.em_icon_mute_on);
try {
EMClient.getInstance().callManager().pauseVoiceTransfer();
} catch (HyphenateException e) {
e.printStackTrace();
}
isMuteState = true;
}
} else if (i == R.id.iv_handsfree) {
if (isHandsfreeState) {
handsFreeImage.setImageResource(R.drawable.em_icon_speaker_normal);
closeSpeakerOn();
isHandsfreeState = false;
} else {
handsFreeImage.setImageResource(R.drawable.em_icon_speaker_on);
openSpeakerOn();
isHandsfreeState = true;
}
} else {
}
}
@Override
protected void onDestroy() {
DemoHelper.getInstance().isVoiceCalling = false;
stopMonitor();
super.onDestroy();
}
@Override
public void onBackPressed() {
callDruationText = chronometer.getText().toString();
}
/**
* for debug & testing, you can remove this when release
*/
void startMonitor(){
monitor = true;
new Thread(new Runnable() {
public void run() {
runOnUiThread(new Runnable() {
public void run() {
((TextView)findViewById(R.id.tv_is_p2p)).setText(EMClient.getInstance().callManager().isDirectCall()
? R.string.direct_call : R.string.relay_call);
}
});
while(monitor){
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
}
}
}
}, "CallMonitor").start();
}
void stopMonitor() {
}
}
2.在有两个页面之后,需要注册一个广播,来实时监听音视频通话
接下来实现CallReceiver:
public class CallReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
if(!DemoHelper.getInstance().isLoggedIn())
return;
//username
String from = intent.getStringExtra("from");
//call type
String type = intent.getStringExtra("type");
if("video".equals(type)){ //video call
context.startActivity(new Intent(context, VideoCallActivity.class).
putExtra("username", from).putExtra("isComingCall", true).
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
}else{ //voice call
context.startActivity(new Intent(context, VoiceCallActivity.class).
putExtra("username", from).putExtra("isComingCall", true).
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
}
EMLog.d("CallReceiver", "app received a incoming call");
}
}
在清单文件中注册广播:
<receiver android:name="com.hyphenate.easeui.receiver.CallReceiver"
<intent-filter>
<action android:name="com.hyphenate.action.incomingcall"/>
</intent-filter>
</receiver>
3.广播注册好之后,需要在EaseUI的fragment中添加音视频按钮
找到EaseChatFragment,在initView方法中找到inputMenu添加:
inputMenu.registerExtendMenuItem(R.string.attach_voice_call, R.drawable.em_chat_voice_call_selector, ITEM_VOICE_CALL, extendMenuItemClickListener);
inputMenu.registerExtendMenuItem(R.string.attach_video_call, R.drawable.em_chat_video_call_selector, ITEM_VIDEO_CALL, extendMenuItemClickListener);
4.在MyItemClickListener中添加两个方法
5.实现startVoiceCall和startVideoCall两个方法。当点击按钮时,调用方法,跳转到音视频界面,实现通话功能
/**
* make a voice call
*/
protected void startVoiceCall() {
if (!EMClient.getInstance().isConnected()) {
Toast.makeText(getActivity(), R.string.not_connect_to_server, Toast.LENGTH_SHORT).show();
} else {
startActivity(new Intent(getActivity(), VoiceCallActivity.class).putExtra("username", toChatUsername)
.putExtra("isComingCall", false));
// voiceCallBtn.setEnabled(false);
inputMenu.hideExtendMenuContainer();
}
}
/**
* make a video call
*/
protected void startVideoCall() {
if (!EMClient.getInstance().isConnected())
Toast.makeText(getActivity(), R.string.not_connect_to_server, Toast.LENGTH_SHORT).show();
else {
startActivity(new Intent(getActivity(), VideoCallActivity.class).putExtra("username", toChatUsername)
.putExtra("isComingCall", false));
// videoCallBtn.setEnabled(false);
inputMenu.hideExtendMenuContainer();
}
}
到此为止,就可以实现音视频通话了。