package io.agora.demo.agora;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Bundle;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.SurfaceView;
import android.view.View;
import android.view.WindowManager;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
import io.agora.demo.agora.util.NetworkConnectivityUtils;
import io.agora.rtc.IRtcEngineEventHandler;
import io.agora.rtc.RtcEngine;
import io.agora.rtc.video.VideoCanvas;
/**
* some refs:
* <p/>
* 1. http://stackoverflow.com/questions/6026625/layout-design-surfaceview-doesnt-display
* 2. http://stackoverflow.com/questions/1096618/android-surfaceview-scrolling/2216788#2216788
*/
/**
* Created by apple on 15/9/9.
*/
public class ChannelActivity extends BaseEngineEventHandlerActivity {
public final static int CALLING_TYPE_VIDEO = 0x100;
public final static int CALLING_TYPE_VOICE = 0x101;
public final static String EXTRA_CALLING_TYPE = "EXTRA_CALLING_TYPE";
public final static String EXTRA_VENDOR_KEY = "EXTRA_VENDOR_KEY";
public final static String EXTRA_CHANNEL_ID = "EXTRA_CHANNEL_ID";
private int mCallingType;
private SurfaceView mLocalView;
private String vendorKey = "";
private String channelId = "";
private TextView mDuration;
private TextView mByteCounts;
private View mCameraEnabler;
private View mCameraSwitcher;
private LinearLayout mRemoteUserContainer;
private AlertDialog alertDialog;
private int time = 0;
private int mLastRxBytes = 0;
private int mLastTxBytes = 0;
private int mLastDuration = 0;
private int mRemoteUserViewWidth = 0;
RtcEngine rtcEngine;
@Override
public void onCreate(Bundle savedInstance) {
super.onCreate(savedInstance);
setContentView(R.layout.activity_room);
// keep screen on - turned on
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
mRemoteUserViewWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 80, getResources().getDisplayMetrics());
mCallingType = getIntent().getIntExtra(EXTRA_CALLING_TYPE, CALLING_TYPE_VOICE /*default is voice call*/);
setupRtcEngine();
initViews();
setupTime();
if (CALLING_TYPE_VIDEO == mCallingType) {
// video call
View simulateClick = new View(getApplicationContext());
simulateClick.setId(R.id.wrapper_action_video_calling);
this.onUserInteraction(simulateClick);
} else if (CALLING_TYPE_VOICE == mCallingType) {
// voice call
View simulateClick = new View(getApplicationContext());
simulateClick.setId(R.id.wrapper_action_voice_calling);
this.onUserInteraction(simulateClick);
}
// check network
if (!NetworkConnectivityUtils.isConnectedToNetwork(getApplicationContext())) {
onError(104);
}
}
void setupChannel() {
String channelId = getIntent().getStringExtra(EXTRA_CHANNEL_ID);
this.channelId = channelId;
this.rtcEngine.joinChannel(
this.vendorKey,
this.channelId,
"" /*optionalInfo*/,
new Random().nextInt(Math.abs((int) System.currentTimeMillis()))/*optionalUid*/);
((TextView) findViewById(R.id.channel_id)).setText(String.format(getString(R.string.title_channel), channelId));
}
void setupRtcEngine() {
String vendorKey = getIntent().getStringExtra(EXTRA_VENDOR_KEY);
this.vendorKey = vendorKey;
// setup engine
((AgoraApplication) getApplication()).setRtcEngine(vendorKey);
rtcEngine = ((AgoraApplication) getApplication()).getRtcEngine();
// LogUtil.log.d(getApplicationContext().getExternalFilesDir(null).toString() + "/agorasdk.log");
rtcEngine.setLogFile(getApplicationContext().getExternalFilesDir(null).toString() + "/agorasdk.log");
// setup engine event activity
((AgoraApplication) getApplication()).setEngineEventHandlerActivity(this);
rtcEngine.enableVideo();
}
void ensureLocalViewIsCreated() {
if (this.mLocalView == null) {
// local view has not been added before
FrameLayout localViewContainer = (FrameLayout) findViewById(R.id.user_local_view);
SurfaceView localView = rtcEngine.CreateRendererView(getApplicationContext());
this.mLocalView = localView;
localViewContainer.addView(localView, new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
rtcEngine.enableVideo();
rtcEngine.setupLocalVideo(new VideoCanvas(this.mLocalView));
}
}
/**
* Initialize views and its listeners
*/
void initViews() {
// muter
CheckBox muter = (CheckBox) findViewById(R.id.action_muter);
muter.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean mutes) {
rtcEngine.muteLocalAudioStream(mutes);
compoundButton.setBackgroundResource(mutes ? R.drawable.ic_room_mute_pressed:R.drawable.ic_room_mute);
}
});
// speaker
CheckBox speaker = (CheckBox) findViewById(R.id.action_speaker);
speaker.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean usesSpeaker) {
rtcEngine.setEnableSpeakerphone(usesSpeaker);
compoundButton.setBackgroundResource(usesSpeaker ? R.drawable.ic_room_loudspeaker : R.drawable.ic_room_loudspeaker_pressed);
}
});
// camera enabler
CheckBox cameraEnabler = (CheckBox) findViewById(R.id.action_camera_enabler);
cameraEnabler.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean disablesCamera) {
rtcEngine.muteLocalVideoStream(disablesCamera);
if (disablesCamera) {
findViewById(R.id.user_local_voice_bg).setVisibility(View.VISIBLE);
rtcEngine.muteLocalVideoStream(true);
} else {
findViewById(R.id.user_local_voice_bg).setVisibility(View.GONE);
rtcEngine.muteLocalVideoStream(false);
}
compoundButton.setBackgroundResource(disablesCamera ? R.drawable.ic_room_button_close_pressed : R.drawable.ic_room_button_close);
}
});
// camera switcher
CheckBox cameraSwitch = (CheckBox) findViewById(R.id.action_camera_switcher);
cameraSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean switches) {
rtcEngine.switchCamera();
compoundButton.setBackgroundResource(switches ? R.drawable.ic_room_button_change_pressed : R.drawable.ic_room_button_change);
}
});
// setup states of action buttons
muter.setChecked(false);
speaker.setChecked(true);
cameraEnabler.setChecked(false);
cameraSwitch.setChecked(false);
findViewById(R.id.wrapper_action_video_calling).setOnClickListener(getViewClickListener());
findViewById(R.id.wrapper_action_voice_calling).setOnClickListener(getViewClickListener());
findViewById(R.id.action_hung_up).setOnClickListener(getViewClickListener());
findViewById(R.id.action_back).setOnClickListener(getViewClickListener());
mDuration = (TextView) findViewById(R.id.stat_time);
mByteCounts = (TextView) findViewById(R.id.stat_bytes);
mCameraEnabler = findViewById(R.id.wrapper_action_camera_enabler);
mCameraSwitcher = findViewById(R.id.wrapper_action_camera_switcher);
mRemoteUserContainer = (LinearLayout) findViewById(R.id.user_remote_views);
setRemoteUserViewVisibility(false);
}
void setRemoteUserViewVisibility(boolean isVisible) {
findViewById(R.id.user_remote_views).getLayoutParams().height =
isVisible ? (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 80, getResources().getDisplayMetrics())
: 0;
}
void removeBackgroundOfCallingWrapper() {
findViewById(R.id.wrapper_action_video_calling).setBackgroundResource(R.drawable.shape_transparent);
findViewById(R.id.wrapper_action_voice_calling).setBackgroundResource(R.drawable.shape_transparent);
}
void setupTime() {
TimerTask task = new TimerTask() {
@Override
public void run() {
runOnUiThread(new Runnable() {
@Override
public void run() {
time++;
if (time >= 3600) {
mDuration.setText(String.format("%d:%02d:%02d", time / 3600, (time % 3600) / 60, (time % 60)));
} else {
mDuration.setText(String.format("%02d:%02d", (time % 3600) / 60, (time % 60)));
}
}
});
}
};
Timer timer = new Timer();
timer.schedule(task, 1000, 1000);
}
/**
* 切换视频音频通话时,更新 view 的显示。只是更新重用的 view,并不新添加。
*
* @param callingType
*/
void updateRemoteUserViews(int callingType) {
int visibility = View.GONE;
if (CALLING_TYPE_VIDEO == callingType) {
visibility = View.GONE;
} else if (CALLING_TYPE_VOICE == callingType) {
visibility = View.VISIBLE;
}
for (int i = 0, size = mRemoteUserContainer.getChildCount(); i < size; i++) {
View singleRemoteView = mRemoteUserContainer.getChildAt(i);
singleRemoteView.findViewById(R.id.remote_user_voice_container).setVisibility(visibility);
if (CALLING_TYPE_VIDEO == callingType) {
// re-setup remote video
FrameLayout remoteVideoUser = (FrameLayout) singleRemoteView.findViewById(R.id.viewlet_remote_video_user);
// ensure remote video view setup
if(remoteVideoUser.getChildCount()>0) {
final SurfaceView remoteView = (SurfaceView) remoteVideoUser.getChildAt(0);
if(remoteView!=null) {
remoteView.setZOrderOnTop(true);
remoteView.setZOrderMediaOverlay(true);
int savedUid = (Integer) remoteVideoUser.getTag();
log("saved uid: " + savedUid);
rtcEngine.setupRemoteVideo(new VideoCanvas(remoteView, VideoCanvas.RENDER_MODE_ADAPTIVE, savedUid));
}
}
}
}
}
@Override
public void onUserInteraction(View view) {
switch (view.getId()) {
default:
super.onUserInteraction(view);
break;
case R.id.wrapper_action_video_calling: {
mCallingType = CALLING_TYPE_VIDEO;
mCameraEnabler.setVisibility(View.VISIBLE);
mCameraSwitcher.setVisibility(View.VISIBLE);
removeBackgroundOfCallingWrapper();
findViewById(R.id.wrapper_action_video_calling).setBackgroundResource(R.drawable.ic_room_button_yellow_bg);
findViewById(R.id.user_local_voice_bg).setVisibility(View.GONE);
// enable video call
ensureLocalViewIsCreated();
rtcEngine.enableVideo();
rtcEngine.muteLocalVideoStream(false);
rtcEngine.muteLocalAudioStream(false);
rtcEngine.muteAllRemoteVideoStreams(false);
// join video call
if (mRemoteUserContainer.getChildCount() == 0) {
this.setupChannel();
}
new android.os.Handler().postDelayed(new Runnable() {
@Override
public void run() {
updateRemoteUserViews(CALLING_TYPE_VIDEO);
}
},500);
// ensure video camera enabler states
CheckBox cameraEnabler = (CheckBox) findViewById(R.id.action_camera_enabler);
cameraEnabler.setChecked(false);
}
break;
case R.id.wrapper_action_voice_calling: {
mCallingType = CALLING_TYPE_VOICE;
mCameraEnabler.setVisibility(View.GONE);
mCameraSwitcher.setVisibility(View.GONE);
removeBackgroundOfCallingWrapper();
findViewById(R.id.wrapper_action_voice_calling).setBackgroundResource(R.drawable.ic_room_button_yellow_bg);
// show background for voice call
findViewById(R.id.user_local_voice_bg).setVisibility(View.VISIBLE);
ensureLocalViewIsCreated();
// disable video call when necessary
rtcEngine.disableVideo();
rtcEngine.muteLocalVideoStream(true);
rtcEngine.muteAllRemoteVideoStreams(true);
// join voice call
if (mRemoteUserContainer.getChildCount() == 0) {
this.setupChannel();
}
new android.os.Handler().postDelayed(new Runnable() {
@Override
public void run() {
updateRemoteUserViews(CALLING_TYPE_VOICE);
}
},500);
}
break;
case R.id.action_hung_up:
case R.id.action_back: {
onBackPressed();
}
break;
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
return true;
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
}
@Override
public void onBackPressed() {
new Thread(new Runnable() {
@Override
public void run() {
rtcEngine.leaveChannel();
}
}).run();
// keep screen on - turned off
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
public void onUpdateSessionStats(final IRtcEngineEventHandler.RtcStats stats) {
runOnUiThread(new Runnable() {
@Override
public void run() {
// bytes
mByteCounts.setText(((stats.txBytes + stats.rxBytes - mLastTxBytes - mLastRxBytes) / 1024 / (stats.totalDuration - mLastDuration + 1)) + "KB/s");
// remember data from this call back
mLastRxBytes = stats.rxBytes;
mLastTxBytes = stats.txBytes;
mLastDuration = stats.totalDuration;
}
});
}
public synchronized void onFirstRemoteVideoDecoded(final int uid, int width, int height, final int elapsed) {
log("onFirstRemoteVideoDecoded: uid: " + uid + ", width: " + width + ", height: " + height);
runOnUiThread(new Runnable() {
@Override
public void run() {
View remoteUserView = mRemoteUserContainer.findViewById(Math.abs(uid));
// ensure container is added
if (remoteUserView == null) {
LayoutInflater layoutInflater = getLayoutInflater();
View singleRemoteUser = layoutInflater.inflate(R.layout.viewlet_remote_user, null);
singleRemoteUser.setId(Math.abs(uid));
TextView username = (TextView) singleRemoteUser.findViewById(R.id.remote_user_name);
username.setText(String.valueOf(uid));
mRemoteUserContainer.addView(singleRemoteUser, new LinearLayout.LayoutParams(mRemoteUserViewWidth, mRemoteUserViewWidth));
remoteUserView = singleRemoteUser;
}
FrameLayout remoteVideoUser = (FrameLayout) remoteUserView.findViewById(R.id.viewlet_remote_video_user);
remoteVideoUser.removeAllViews();
remoteVideoUser.setTag(uid);
// ensure remote video view setup
final SurfaceView remoteView = RtcEngine.CreateRendererView(getApplicationContext());
remoteVideoUser.addView(remoteView, new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
remoteView.setZOrderOnTop(true);
remoteView.setZOrderMediaOverlay(true);
rtcEngine.enableVideo();
int successCode = rtcEngine.setupRemoteVideo(new VideoCanvas(remoteView, VideoCanvas.RENDER_MODE_ADAPTIVE, uid));
if (successCode < 0) {
new android.os.Handler().postDelayed(new Runnable() {
@Override
public void run() {
rtcEngine.setupRemoteVideo(new VideoCanvas(remoteView, VideoCanvas.RENDER_MODE_ADAPTIVE, uid));
remoteView.invalidate();
}
}, 500);
}
if (remoteUserView != null && CALLING_TYPE_VIDEO == mCallingType) {
remoteUserView.findViewById(R.id.remote_user_voice_container).setVisibility(View.GONE);
} else {
remoteUserView.findViewById(R.id.remote_user_voice_container).setVisibility(View.VISIBLE);
}
// app hints before you join
TextView appNotification = (TextView) findViewById(R.id.app_notification);
appNotification.setText("");
setRemoteUserViewVisibility(true);
}
});
}
public synchronized void onUserJoined(final int uid, int elapsed) {
log("onUserJoined: uid: " + uid);
View existedUser = mRemoteUserContainer.findViewById(Math.abs(uid));
if (existedUser != null) {
// user view already added
return;
}
runOnUiThread(new Runnable() {
@Override
public void run() {
// Handle the case onFirstRemoteVideoDecoded() is called before onUserJoined()
View singleRemoteUser = mRemoteUserContainer.findViewById(Math.abs(uid));
if (singleRemoteUser != null) {
return;
}
LayoutInflater layoutInflater = getLayoutInflater();
singleRemoteUser = layoutInflater.inflate(R.layout.viewlet_remote_user, null);
singleRemoteUser.setId(Math.abs(uid));
TextView username = (TextView) singleRemoteUser.findViewById(R.id.remote_user_name);
username.setText(String.valueOf(uid));
mRemoteUserContainer.addView(singleRemoteUser, new LinearLayout.LayoutParams(mRemoteUserViewWidth, mRemoteUserViewWidth));
// app hints before you join
TextView appNotification = (TextView) findViewById(R.id.app_notification);
appNotification.setText("");
setRemoteUserViewVisibility(true);
}
});
}
public void onUserOffline(final int uid) {
log("onUserOffline: uid: " + uid);
if(isFinishing()){
return;
}
if(mRemoteUserContainer==null){
return;
}
runOnUiThread(new Runnable() {
@Override
public void run() {
View userViewToRemove = mRemoteUserContainer.findViewById(Math.abs(uid));
mRemoteUserContainer.removeView(userViewToRemove);
// no joined users any more
if (mRemoteUserContainer.getChildCount() == 0) {
setRemoteUserViewVisibility(false);
TextView appNotification = (TextView) findViewById(R.id.app_notification);
appNotification.setText(R.string.room_prepare);
}
}
});
}
@Override
public void finish() {
if(alertDialog!=null){
alertDialog.dismiss();
}
super.finish();
}
@Override
public void onLeaveChannel(IRtcEngineEventHandler.RtcStats stats) {
try {
finish();
}catch (Exception e){
e.printStackTrace();
}
}
public void onUserMuteVideo(final int uid, final boolean muted) {
log("onUserMuteVideo uid: " + uid + ", muted: " + muted);
if(isFinishing()){
return;
}
if(mRemoteUserContainer==null){
return;
}
runOnUiThread(new Runnable() {
@Override
public void run() {
View remoteView = mRemoteUserContainer.findViewById(Math.abs(uid));
remoteView.findViewById(R.id.remote_user_voice_container).setVisibility(
(CALLING_TYPE_VOICE==mCallingType || (CALLING_TYPE_VIDEO==mCallingType && muted))
? View.VISIBLE
: View.GONE);
remoteView.invalidate();
}
});
}
@Override
public synchronized void onError(int err) {
if(isFinishing()){
return;
}
// incorrect vendor key
if(101==err){
runOnUiThread(new Runnable() {
@Override
public void run() {
if(alertDialog!=null){
return;
}
alertDialog= new AlertDialog.Builder(ChannelActivity.this).setCancelable(false)
.setMessage(getString(R.string.error_101))
.setPositiveButton(getString(R.string.error_confirm), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// Go to login
Intent toLogin = new Intent(ChannelActivity.this, LoginActivity.class);
toLogin.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivity(toLogin);
rtcEngine.leaveChannel();
}
}).setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialogInterface) {
dialogInterface.dismiss();
}
})
.create();
alertDialog.show();
}
});
}
// no network connection
if (104 == err) {
runOnUiThread(new Runnable() {
@Override
public void run() {
TextView appNotification = (TextView) findViewById(R.id.app_notification);
appNotification.setText(R.string.network_error);
}
});
}
}
public static String humanReadableByteCount(long bytes, boolean si) {
int unit = si ? 1000 : 1024;
if (bytes < unit) return bytes + " B";
int exp = (int) (Math.log(bytes) / Math.log(unit));
String pre = (si ? "kMGTPE" : "KMGTPE").charAt(exp - 1) + (si ? "" : "i");
return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre);
}
@Override
protected void onDestroy() {
super.onDestroy();
((AgoraApplication) getApplication()).setEngineEventHandlerActivity(null);
}
}