//核心功能能实现,activity 和 WindowManager 无缝切换 网上搜半天搜不出来,自己摸索环信搞的,
/**
* 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.hyphenate.chatuidemo.ui;
import android.content.Context;
import android.graphics.PixelFormat;
import android.media.AudioManager;
import android.media.RingtoneManager;
import android.media.SoundPool;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.SystemClock;
import android.text.format.DateFormat;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
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.TextView;
import android.widget.Toast;
import com.hyphenate.chat.EMCallStateChangeListener;
import com.hyphenate.chat.EMClient;
import com.hyphenate.chat.EMVideoCallHelper;
import com.hyphenate.chatuidemo.DemoHelper;
import com.hyphenate.chatuidemo.R;
import com.hyphenate.exceptions.HyphenateException;
import com.hyphenate.media.EMCallSurfaceView;
import com.hyphenate.util.EMLog;
import com.superrtc.sdk.VideoView;
import java.io.File;
import java.util.Date;
import java.util.UUID;
import klr.tool.MSCTool;
import klr.tool.MSCViewTool;
import klr.zr.ZRPermission;
public class VideoCallActivity extends CallActivity implements OnClickListener {
private boolean isMuteState;
private boolean isHandsfreeState;
private boolean endCallTriggerByMe = false;
private boolean monitor = true;
protected EMCallSurfaceView oppositeSurface;
// 视频通话画面显示控件,这里在新版中使用同一类型的控件,方便本地和远端视图切换
protected EMCallSurfaceView localSurface;
private int surfaceState = -1;
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;
boolean isRecording = false;
// private Button recordBtn;
private EMVideoCallHelper callHelper;
static RelativeLayout newview;
static int w;
static int h;
public void onClick_xiaochuang(View view) {
if (!issmaillvideo) {
issmaillvideo = ZRPermission.requestPermission();
toast("请开启悬浮窗权限");
return;
}
if (w <= 0) {
w = MSCViewTool.dip2px(108);
h = MSCViewTool.dip2px(192);
}
// getWindow().setLayout(MSCViewTool.dip2px(300), MSCViewTool.dip2px(200));
// getWindow().setGravity(Gravity.TOP|Gravity.RIGHT);
newview = (RelativeLayout) LayoutInflater.from(getApplicationContext()).inflate(R.layout.fulayout, null);
mLayoutParams = new WindowManager.LayoutParams(w, h, 0, 0,
PixelFormat.TRANSPARENT);
mLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
mLayoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
mLayoutParams.gravity = Gravity.CENTER;
Button button = newview.findViewById(R.id.fubt);
button.setOnTouchListener(new View.OnTouchListener() {
int dx;
@Override
public boolean onTouch(View v, MotionEvent event) {
int x = (int) event.getRawX();
int y = (int) event.getRawY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
dx = 0;
break;
}
case MotionEvent.ACTION_MOVE: {
//- w - w / 2
mLayoutParams.x = x - w - w;
mLayoutParams.y = y - h - h;
mWindowManager.updateViewLayout(newview, mLayoutParams);
dx++;
break;
}
case MotionEvent.ACTION_UP: {
if (dx < 10) {
mWindowManager.removeView(newview);
MSCTool.startZrOneActivity(VideoCallActivity.class);
}
break;
}
default:
break;
}
return true;
}
});
EMClient.getInstance().callManager().setSurfaceView(localSurface, (EMCallSurfaceView) newview.findViewById(R.id.fusv));
mWindowManager.addView(newview, mLayoutParams);
MSCTool.startZrOneActivity(ChatActivity.class);
}
static WindowManager.LayoutParams mLayoutParams;
public static WindowManager mWindowManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// if(savedInstanceState != null){
// finish();
// return;
// }
setContentView(R.layout.em_activity_video_call);
if (mWindowManager == null)
mWindowManager = (WindowManager) getApplication().getSystemService(Context.WINDOW_SERVICE);
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);
// 悬浮后下个层次的可点击
// | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
uiHandler = new Handler();
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);
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);
msgid = UUID.randomUUID().toString();
isInComingCall = getIntent().getBooleanExtra("isComingCall", false);
username = getIntent().getStringExtra("username");
nickTextView.setText(username);
// local surfaceview
localSurface = (EMCallSurfaceView) findViewById(R.id.local_surface);
localSurface.setOnClickListener(this);
localSurface.setZOrderMediaOverlay(true);
localSurface.setZOrderOnTop(true);
// remote surfaceview
oppositeSurface = (EMCallSurfaceView) findViewById(R.id.opposite_surface);
// set call state listener
addCallStateListener();
soundPool = new SoundPool(1, AudioManager.STREAM_RING, 0);
outgoing = soundPool.load(this, R.raw.em_outgoing, 1);
if (newview == null) {
if (!isInComingCall) {
// 发出视频请求
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);
}else {
EMClient.getInstance().callManager().setSurfaceView(localSurface, oppositeSurface);
}
// get instance of call helper, should be called after setSurfaceView was called
callHelper = EMClient.getInstance().callManager().getVideoCallHelper();
}
/**
* 切换通话界面,这里就是交换本地和远端画面控件设置,以达到通话大小画面的切换
*/
private void changeCallView() {
if (surfaceState == 0) {
surfaceState = 1;
EMClient.getInstance().callManager().setSurfaceView(oppositeSurface, localSurface);
} else {
surfaceState = 0;
EMClient.getInstance().callManager().setSurfaceView(localSurface, oppositeSurface);
}
}
/**
* 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
surfaceState = 0;
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;
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.local_surface) {
changeCallView();
} else 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);
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 = new DateFormat();
Date d = new Date();
File storage = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
final String filename = storage.getAbsolutePath() + "/" + df.format("MM-dd-yy--h-mm-ss", 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();
// }
// }
// }
// 是否允许小窗
boolean issmaillvideo;
@Override
protected void onResume() {
super.onResume();
try {
EMClient.getInstance().callManager().resumeVideoTransfer();
} catch (HyphenateException e) {
e.printStackTrace();
}
issmaillvideo = ZRPermission.requestPermission();
}
}