本项目中我用到的第三方框架:evenbus,和ButterKnife
ButterKnife:
@BindView(R.id.tv_call_state) TextView callStateTextView;这句就相当于findViewById
@OnClick(R.id.register)相当于给这个id设置了点击监听
eventbus:
其实很简单,前面有介绍 ,这里就不多说了
第一步就是当环信官网下载jar包并放入自己的项目里面
整个项目需要那几包类和包如图
在Manifest的要声明的权限和service,activity
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.yangzhelin.myvideo30"> <uses-permission android:name="android.permission.VIBRATE" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.GET_TASKS" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <application android:name=".app.App" android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".activities.MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <!-- 设置环信应用的AppKey --> <meta-data android:name="EASEMOB_APPKEY" android:value="9i#110" /> <!-- 声明SDK所需的service SDK核心功能--> <service android:name="com.hyphenate.chat.EMChatService" android:exported="true"/> <!-- 声明SDK所需的receiver --> <receiver android:name="com.hyphenate.chat.EMMonitorReceiver"> <intent-filter> <action android:name="android.intent.action.PACKAGE_REMOVED"/> <data android:scheme="package"/> </intent-filter> <!-- 可选filter --> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED"/> <action android:name="android.intent.action.USER_PRESENT" /> </intent-filter> </receiver> <!-- 视频通话 --> <activity android:name=".activities.VideoCallActivity" android:configChanges="orientation|keyboardHidden|screenSize" android:screenOrientation="portrait" android:launchMode="singleTask" android:theme="@style/horizontal_slide" > </activity> </application> </manifest>
下面是整个项目的的代码:
package com.example.yangzhelin.myvideo30.app; import android.app.Application; import android.content.Context; import android.content.IntentFilter; import com.example.yangzhelin.myvideo30.receiver.CallReceiver; import com.hyphenate.chat.EMClient; import com.hyphenate.chat.EMOptions; /** * User:yangzhelin * Date:2015-09-18 * Time: 11:31 */ public class App extends Application{ public static Context applicationContext; private CallReceiver callReceiver; @Override public void onCreate() { super.onCreate(); applicationContext = this; EMOptions options = new EMOptions(); // 默认添加好友时,是不需要验证的,改成需要验证 options.setAcceptInvitationAlways(false); EMClient.getInstance().init(applicationContext, options); //在做打包混淆时,关闭debug模式,避免消耗不必要的资源 EMClient.getInstance().setDebugMode(true); //注册通话广播接收者 registerReceiver(new CallReceiver(), new IntentFilter(EMClient.getInstance().callManager().getIncomingCallBroadcastAction())); } }
/** * Copyright (C) 2013-2014 EaseMob Technologies. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.example.yangzhelin.myvideo30.receiver; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import com.example.yangzhelin.myvideo30.activities.VideoCallActivity; import com.example.yangzhelin.myvideo30.DemoHelper; import com.hyphenate.util.EMLog; /** * Created by yangzhelin on 2016/7/28. */ 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"); } }
package com.example.yangzhelin.myvideo30; import com.hyphenate.chat.EMClient; public class DemoHelper { private static DemoHelper instance = null; public boolean isVoiceCalling; public boolean isVideoCalling; private DemoHelper() { } public synchronized static DemoHelper getInstance() { if (instance == null) { instance = new DemoHelper(); } return instance; } /** * if ever logged in * * @return */ public boolean isLoggedIn() { return EMClient.getInstance().isLoggedInBefore(); } }
package com.example.yangzhelin.myvideo30; import com.halove.android.framework.event.BaseEvent; /** * Copyright (2012-2016) by 杭州九爱科技有限公司. All rights reserved * Comments: * * @author yining * Created on 2016/7/4 */ public class ToastEvent extends BaseEvent { public String text; public ToastEvent(String text) { this.text = text; } }
package com.example.yangzhelin.myvideo30.util; import android.content.Context; /** * Created by yangzhelin on 2016/7/28. */ public interface IVideoManager { /**注册 * 用户名字 * @param username* * 用户密码 * @param password * @return */ void register(String username, String password); /** * 登入服务器 * @param username * @param password */ void login(String username, String password); /** * 登出服务器 */ void logout(); /** * 视频聊天 * @param username */ void video(String username); /** * 语音聊天 * @param username */ void voice(String username); }
package com.example.yangzhelin.myvideo30.util; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.text.TextUtils; import android.util.Log; import android.widget.Toast; import com.example.yangzhelin.myvideo30.ToastEvent; import com.example.yangzhelin.myvideo30.activities.VideoCallActivity; import com.hyphenate.EMCallBack; import com.hyphenate.chat.EMClient; import com.hyphenate.exceptions.HyphenateException; /** * Created by yangzhelin on 2016/7/28. */ public class VideoManager implements IVideoManager { private static IVideoManager instance = null; private Context context; private VideoManager(Context context){ this.context=context; } public synchronized static IVideoManager getInstance(Context context){ if (instance == null) { instance = new VideoManager(context); } return instance; } @Override public void register(final String username, final String password) { new Thread(new Runnable() { @Override public void run() { //注册失败会抛出HyphenateException try { EMClient.getInstance().createAccount(username, password);//同步方法 } catch (final HyphenateException e) { toast("注册失败"+ e.getMessage()); } } }).start(); } @Override public void login(String username, String password) { if(TextUtils.isEmpty(username)){ toast("请输入用户"); return; }else if(TextUtils.isEmpty(password)){ toast("请输入密码"); return; } EMClient.getInstance().login(username,password,new EMCallBack() {//回调 @Override public void onSuccess() { EMClient.getInstance().groupManager().loadAllGroups(); EMClient.getInstance().chatManager().loadAllConversations(); Log.d("main", "登录聊天服务器成功!"); toast("登入成功"); } @Override public void onProgress(int progress, String status) { } @Override public void onError(int code, String message) { // Log.d("main", "登录聊天服务器失败!"); toast("登入失败"); } }); } @Override public void logout() { new Thread(new Runnable() { @Override public void run() { EMClient.getInstance().logout(true, new EMCallBack() { @Override public void onSuccess() { toast("退出成功"); } @Override public void onProgress(int progress, String status) { } @Override public void onError(int code, String message) { toast("退出失败"); } }); } }).start(); } @Override public void video(String toUser) { if (! EMClient.getInstance().isConnected()) { toast("未连接到服务器"); return; }else{ if (TextUtils.isEmpty(toUser)){ toast("请填写接受方账号"); return ; } Intent intent = new Intent(context, VideoCallActivity.class); intent.putExtra("username", toUser); intent.putExtra("isComingCall", false); context.startActivity(intent); } } @Override public void voice(String username) { } public void toast(final String text){ new ToastEvent(text).fire(); } }
/** * Copyright (C) 2016 Hyphenate Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.example.yangzhelin.myvideo30.activities; import java.util.UUID; import com.example.yangzhelin.myvideo30.DemoHelper; import com.example.yangzhelin.myvideo30.R; import com.hyphenate.chat.EMCallManager.EMCameraDataProcessor; import com.hyphenate.chat.EMCallManager.EMVideoCallHelper; import com.hyphenate.chat.EMCallStateChangeListener; import com.hyphenate.chat.EMClient; import com.hyphenate.media.EMLocalSurfaceView; import com.hyphenate.media.EMOppositeSurfaceView; import com.hyphenate.util.PathUtil; import android.hardware.Camera; import android.media.AudioManager; import android.media.RingtoneManager; import android.media.SoundPool; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.SystemClock; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.view.WindowManager; import android.view.animation.AlphaAnimation; import android.view.animation.Animation; import android.widget.Button; import android.widget.Chronometer; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.SeekBar; import android.widget.TextView; import android.widget.Toast; import butterknife.BindView; import butterknife.ButterKnife; import butterknife.OnClick; public class VideoCallActivity extends CallActivity { @BindView(R.id.tv_call_state) TextView callStateTextView; @BindView(R.id.ll_coming_call) LinearLayout comingBtnContainer; @BindView(R.id.btn_refuse_call) Button refuseBtn; @BindView(R.id.btn_answer_call) Button answerBtn; @BindView(R.id.btn_hangup_call) Button hangupBtn; @BindView(R.id.iv_mute) ImageView muteImage; @BindView(R.id.iv_handsfree) ImageView handsFreeImage; @BindView(R.id.tv_nick) TextView nickTextView; @BindView(R.id.chronometer) Chronometer chronometer; @BindView(R.id.ll_voice_control) LinearLayout voiceContronlLayout; @BindView(R.id.root_layout) RelativeLayout rootContainer; @BindView(R.id.ll_btns) RelativeLayout btnsContainer; @BindView(R.id.rl_Video_function) RelativeLayout videoFunction; @BindView(R.id.ll_top_container) LinearLayout topContainer; @BindView(R.id.ll_bottom_container) LinearLayout bottomContainer; @BindView(R.id.tv_call_monitor) TextView monitorTextView; @BindView(R.id.tv_network_status) TextView netwrokStatusVeiw; @BindView(R.id.btn_record_video) Button recordBtn; @BindView(R.id.btn_switch_camera) Button switchCameraBtn; @BindView(R.id.seekbar_y_detal) SeekBar YDeltaSeekBar; private boolean isMuteState; private boolean isHandsfreeState; private boolean isAnswered; private boolean endCallTriggerByMe = false; private boolean monitor = true; private boolean isInCalling; private boolean isRecording = false; private Handler uiHandler; private EMVideoCallHelper callHelper; 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, int width, int height) { 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); ButterKnife.bind(this); 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(); YDeltaSeekBar.setOnSeekBarChangeListener(new YDeltaSeekBarListener()); msgid = UUID.randomUUID().toString(); isInComingCall = getIntent().getBooleanExtra("isComingCall", false); username = getIntent().getStringExtra("username"); nickTextView.setText(username); localSurface.setZOrderMediaOverlay(true); localSurface.setZOrderOnTop(true); // remote surfaceview // 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); } else { // incoming call 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); } // 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(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); } 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_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: toastByUi("VIDEO_PAUSE"); break; case VIDEO_RESUME: toastByUi("VIDEO_RESUME"); break; case VOICE_PAUSE: toastByUi("VOICE_PAUSE"); break; case VOICE_RESUME: toastByUi("VOICE_RESUME"); break; case DISCONNNECTED: // call is disconnected handler.removeCallbacks(timeoutHangup); final CallError fError = error; runOnUiThread(new Runnable() { private void postDelayedCloseMsg() { uiHandler.postDelayed(new Runnable() { @Override public void run() { saveCallRecord(); Animation animation = new AlphaAnimation(1.0f, 0.0f); animation.setDuration(800); 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); if (fError == CallError.REJECTED) { callingState = CallingState.BEREFUESD; callStateTextView.setText(s1); } else if (fError == CallError.ERROR_TRANSPORT) { callStateTextView.setText(s2); } else if (fError == CallError.ERROR_INAVAILABLE) { 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.NORESPONSE; callStateTextView.setText(s5); }else if (fError == CallError.ERROR_LOCAL_VERSION_SMALLER || fError == CallError.ERROR_PEER_VERSION_SMALLER){ callingState = CallingState.VERSION_NOT_SAME; callStateTextView.setText(R.string.call_version_inconsistent); } 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.CANCED; callStateTextView.setText(s9); } else { callStateTextView.setText(s6); } } } } postDelayedCloseMsg(); } }); break; default: break; } } }; EMClient.getInstance().callManager().addCallStateChangeListener(callStateListener); } public void toastByUi(final String text){ runOnUiThread(new Runnable() { @Override public void run() { Toast.makeText(VideoCallActivity.this, text, Toast.LENGTH_SHORT).show(); } }); } @OnClick(R.id.btn_refuse_call) public void btn_refuse_call(){ refuseBtn.setEnabled(false); handler.sendEmptyMessage(MSG_CALL_REJECT); } @OnClick(R.id.btn_answer_call) public void btn_answer_call(){ 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); } @OnClick(R.id.btn_hangup_call) public void btn_hangup_call(){ hangupBtn.setEnabled(false); chronometer.stop(); endCallTriggerByMe = true; callStateTextView.setText(getResources().getString(R.string.hanging_up)); if(isRecording){ callHelper.stopVideoRecord(); } handler.sendEmptyMessage(MSG_CALL_END); } @OnClick(R.id.iv_mute) public void iv_mute(){ if (isMuteState) { // resume voice transfer muteImage.setImageResource(R.drawable.em_icon_mute_normal); EMClient.getInstance().callManager().resumeVoiceTransfer(); isMuteState = false; } else { // pause voice transfer muteImage.setImageResource(R.drawable.em_icon_mute_on); EMClient.getInstance().callManager().pauseVoiceTransfer(); isMuteState = true; } } @OnClick(R.id.iv_handsfree) public void 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; } } @OnClick(R.id.btn_record_video) public void btn_record_video(){ if(!isRecording){ callHelper.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(); } } @OnClick(R.id.root_layout) public void root_layout(){ if (callingState == CallingState.NORMAL) { if (bottomContainer.getVisibility() == View.VISIBLE) { bottomContainer.setVisibility(View.GONE); topContainer.setVisibility(View.GONE); videoFunction.setVisibility(View.GONE); } else { bottomContainer.setVisibility(View.VISIBLE); topContainer.setVisibility(View.VISIBLE); videoFunction.setVisibility(View.VISIBLE); } } } @OnClick(R.id.btn_switch_camera) public void btn_switch_camera(){ handler.sendEmptyMessage(MSG_CALL_SWITCH_CAMERA); } @Override protected void onDestroy() { DemoHelper.getInstance().isVideoCalling = false; stopMonitor(); if(isRecording){ callHelper.stopVideoRecord(); isRecording = false; } localSurface = null; 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(){ 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.getVideoTimedelay() + "\nFramerate:" + callHelper.getVideoFramerate() + "\nLost:" + callHelper.getVideoLostcnt() + "\nLocalBitrate:" + callHelper.getLocalBitrate() + "\nRemoteBitrate:" + callHelper.getRemoteBitrate()); } }); try { Thread.sleep(1500); } catch (InterruptedException e) { } } } }).start(); } void stopMonitor(){ monitor = false; } @Override protected void onUserLeaveHint() { super.onUserLeaveHint(); if(isInCalling){ EMClient.getInstance().callManager().pauseVideoTransfer(); } } @Override protected void onResume() { super.onResume(); if(isInCalling){ EMClient.getInstance().callManager().resumeVideoTransfer(); } } }
package com.example.yangzhelin.myvideo30.activities; import com.example.yangzhelin.myvideo30.R; import com.hyphenate.chat.EMCallStateChangeListener; import com.hyphenate.chat.EMClient; import com.hyphenate.chat.EMMessage; import com.hyphenate.chat.EMMessage.Status; import com.hyphenate.chat.EMTextMessageBody; import com.hyphenate.exceptions.EMServiceNotReadyException; import com.hyphenate.media.EMLocalSurfaceView; import com.hyphenate.media.EMOppositeSurfaceView; import com.hyphenate.util.EMLog; import com.hyphenate.util.NetUtils; import android.app.Activity; import android.content.Context; import android.media.AudioManager; import android.media.Ringtone; import android.media.SoundPool; import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; import android.os.Message; import android.widget.Toast; import butterknife.BindView; public class CallActivity extends Activity { protected final int MSG_CALL_MAKE_VIDEO = 0; protected final int MSG_CALL_MAKE_VOICE = 1; protected final int MSG_CALL_ANSWER = 2; protected final int MSG_CALL_REJECT = 3; protected final int MSG_CALL_END = 4; protected final int MSG_CALL_RLEASE_HANDLER = 5; protected final int MSG_CALL_SWITCH_CAMERA = 6; protected boolean isInComingCall; protected String username; protected CallingState callingState = CallingState.CANCED; protected String callDruationText; protected String msgid; protected AudioManager audioManager; protected SoundPool soundPool; protected Ringtone ringtone; protected int outgoing; protected EMCallStateChangeListener callStateListener; @BindView(R.id.local_surface) protected EMLocalSurfaceView localSurface; @BindView(R.id.opposite_surface) protected EMOppositeSurfaceView oppositeSurface; protected boolean isAnswered = false; protected int streamID = -1; /** * 0:voice call,1:video call */ protected int callType = 0; @Override protected void onCreate(Bundle arg0) { super.onCreate(arg0); audioManager = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE); } @Override protected void onDestroy() { if (soundPool != null) soundPool.release(); if (ringtone != null && ringtone.isPlaying()) ringtone.stop(); audioManager.setMode(AudioManager.MODE_NORMAL); audioManager.setMicrophoneMute(false); if(callStateListener != null) EMClient.getInstance().callManager().removeCallStateChangeListener(callStateListener); releaseHandler(); super.onDestroy(); } @Override public void onBackPressed() { handler.sendEmptyMessage(MSG_CALL_END); saveCallRecord(); finish(); super.onBackPressed(); } Runnable timeoutHangup = new Runnable() { @Override public void run() { handler.sendEmptyMessage(MSG_CALL_END); } }; HandlerThread callHandlerThread = new HandlerThread("callHandlerThread"); { callHandlerThread.start(); } protected Handler handler = new Handler(callHandlerThread.getLooper()) { @Override public void handleMessage(Message msg) { EMLog.d("EMCallManager CallActivity", "handleMessage ---enter--- msg.what:" + msg.what); switch (msg.what) { case MSG_CALL_MAKE_VIDEO: case MSG_CALL_MAKE_VOICE: try { streamID = playMakeCallSounds(); if (msg.what == MSG_CALL_MAKE_VIDEO) { EMClient.getInstance().callManager().makeVideoCall(username); } else { EMClient.getInstance().callManager().makeVoiceCall(username); } final int MAKE_CALL_TIMEOUT = 50 * 1000; handler.removeCallbacks(timeoutHangup); handler.postDelayed(timeoutHangup, MAKE_CALL_TIMEOUT); } catch (EMServiceNotReadyException e) { e.printStackTrace(); runOnUiThread(new Runnable() { public void run() { final String st2 = getResources().getString(R.string.Is_not_yet_connected_to_the_server); Toast.makeText(CallActivity.this, st2, Toast.LENGTH_SHORT).show(); } }); } break; case MSG_CALL_ANSWER: if (ringtone != null) ringtone.stop(); if (isInComingCall) { try { if (NetUtils.hasDataConnection(CallActivity.this)) { EMClient.getInstance().callManager().answerCall(); isAnswered = true; } else { runOnUiThread(new Runnable() { public void run() { final String st2 = getResources().getString(R.string.Is_not_yet_connected_to_the_server); Toast.makeText(CallActivity.this, st2, Toast.LENGTH_SHORT).show(); } }); throw new Exception(); } } catch (Exception e) { e.printStackTrace(); saveCallRecord(); finish(); return; } } break; case MSG_CALL_REJECT: if (ringtone != null) ringtone.stop(); try { EMClient.getInstance().callManager().rejectCall(); } catch (Exception e1) { e1.printStackTrace(); saveCallRecord(); finish(); } callingState = CallingState.REFUESD; break; case MSG_CALL_END: if (soundPool != null) soundPool.stop(streamID); try { EMClient.getInstance().callManager().endCall(); } catch (Exception e) { saveCallRecord(); finish(); } break; case MSG_CALL_RLEASE_HANDLER: try { EMClient.getInstance().callManager().endCall(); } catch (Exception e) { e.printStackTrace(); } handler.removeCallbacks(timeoutHangup); handler.removeMessages(MSG_CALL_MAKE_VIDEO); handler.removeMessages(MSG_CALL_MAKE_VOICE); handler.removeMessages(MSG_CALL_ANSWER); handler.removeMessages(MSG_CALL_REJECT); handler.removeMessages(MSG_CALL_END); callHandlerThread.quit(); break; case MSG_CALL_SWITCH_CAMERA: EMClient.getInstance().callManager().switchCamera(); break; default: break; } EMLog.d("EMCallManager CallActivity", "handleMessage ---exit--- msg.what:" + msg.what); } }; void releaseHandler() { handler.sendEmptyMessage(MSG_CALL_RLEASE_HANDLER); } /** * play the incoming call ringtone * */ protected int playMakeCallSounds() { try { // max volume float audioMaxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_RING); // current volume float audioCurrentVolume = audioManager.getStreamVolume(AudioManager.STREAM_RING); float volumeRatio = audioCurrentVolume / audioMaxVolume; audioManager.setMode(AudioManager.MODE_RINGTONE); audioManager.setSpeakerphoneOn(false); // play int id = soundPool.play(outgoing, // sound resource 0.3f, // left volume 0.3f, // right volume 1, // priority -1, // loop,0 is no loop,-1 is loop forever 1); // playback rate (1.0 = normal playback, range 0.5 to 2.0) return id; } catch (Exception e) { return -1; } } protected void openSpeakerOn() { try { if (!audioManager.isSpeakerphoneOn()) audioManager.setSpeakerphoneOn(true); audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION); } catch (Exception e) { e.printStackTrace(); } } protected void closeSpeakerOn() { try { if (audioManager != null) { // int curVolume = // audioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL); if (audioManager.isSpeakerphoneOn()) audioManager.setSpeakerphoneOn(false); audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION); // audioManager.setStreamVolume(AudioManager.STREAM_VOICE_CALL, // curVolume, AudioManager.STREAM_VOICE_CALL); } } catch (Exception e) { e.printStackTrace(); } } /** * save call record */ protected void saveCallRecord() { EMMessage message = null; EMTextMessageBody txtBody = null; if (!isInComingCall) { // outgoing call message = EMMessage.createSendMessage(EMMessage.Type.TXT); message.setReceipt(username); } else { message = EMMessage.createReceiveMessage(EMMessage.Type.TXT); message.setFrom(username); } String st1 = getResources().getString(R.string.call_duration); String st2 = getResources().getString(R.string.Refused); String st3 = getResources().getString(R.string.The_other_party_has_refused_to); String st4 = getResources().getString(R.string.The_other_is_not_online); String st5 = getResources().getString(R.string.The_other_is_on_the_phone); String st6 = getResources().getString(R.string.The_other_party_did_not_answer); String st7 = getResources().getString(R.string.did_not_answer); String st8 = getResources().getString(R.string.Has_been_cancelled); switch (callingState) { case NORMAL: txtBody = new EMTextMessageBody(st1 + callDruationText); break; case REFUESD: txtBody = new EMTextMessageBody(st2); break; case BEREFUESD: txtBody = new EMTextMessageBody(st3); break; case OFFLINE: txtBody = new EMTextMessageBody(st4); break; case BUSY: txtBody = new EMTextMessageBody(st5); break; case NORESPONSE: txtBody = new EMTextMessageBody(st6); break; case UNANSWERED: txtBody = new EMTextMessageBody(st7); break; case VERSION_NOT_SAME: txtBody = new EMTextMessageBody(getString(R.string.call_version_inconsistent)); default: txtBody = new EMTextMessageBody(st8); break; } // set message extension if(callType == 0){ message.setAttribute(MESSAGE_ATTR_IS_VOICE_CALL, true); } else { message.setAttribute(MESSAGE_ATTR_IS_VIDEO_CALL, true); } // set message body message.addBody(txtBody); message.setMsgId(msgid); message.setStatus(Status.SUCCESS); // save EMClient.getInstance().chatManager().saveMessage(message); } enum CallingState { CANCED, NORMAL, REFUESD, BEREFUESD, UNANSWERED, OFFLINE, NORESPONSE, BUSY, VERSION_NOT_SAME } public static final String MESSAGE_ATTR_IS_VOICE_CALL = "is_voice_call"; public static final String MESSAGE_ATTR_IS_VIDEO_CALL = "is_video_call"; }package com.example.yangzhelin.myvideo30.activities; import android.os.Bundle; import android.widget.EditText; import android.widget.Toast; import com.example.yangzhelin.myvideo30.R; import com.example.yangzhelin.myvideo30.ToastEvent; import com.example.yangzhelin.myvideo30.util.IVideoManager; import com.example.yangzhelin.myvideo30.util.VideoManager; import com.halove.android.framework.base.BaseActivity; import org.greenrobot.eventbus.Subscribe; import butterknife.BindView; import butterknife.ButterKnife; import butterknife.OnClick; public class MainActivity extends BaseActivity { @BindView(R.id.username) EditText username; @BindView(R.id.password) EditText password; @BindView(R.id.to) EditText to; private IVideoManager videoManger; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); videoManger = VideoManager.getInstance(this); } @OnClick(R.id.register) void register() { String u = username.getText().toString().trim(); String p = password.getText().toString().trim(); videoManger.register(u, p); } @OnClick(R.id.login) void login() { String u = username.getText().toString().trim(); String p = password.getText().toString().trim(); videoManger.login(u, p); } @OnClick(R.id.logout) public void logout() { videoManger.logout(); } @OnClick(R.id.voice) public void voice() { toast("未开通此功能"); } @OnClick(R.id.video) public void video() { String toUser = to.getText().toString(); videoManger.video(toUser); } @Override protected void onDestroy() { logout(); super.onDestroy(); } @Subscribe public void showMessageToUser(ToastEvent event) { final String msg=event.text; runOnUiThread(new Runnable() { @Override public void run() { Toast.makeText(MainActivity.this, msg, Toast.LENGTH_SHORT).show(); } }); } }