Created
May 12, 2021 14:59
-
-
Save thenishchalraj/55995075222f057ebe045cd080531034 to your computer and use it in GitHub Desktop.
Sample java class file to show the main code needed to create Instagram LIVE using Agora SDK and socket.io (Note: not everything for view is included but only the agora and socket snippet)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class InstagramLiveAgoraSocket extends AppCompatActivity { | |
private static final int PERMISSION_REQ_ID = 22; | |
// Permission WRITE_EXTERNAL_STORAGE is not mandatory | |
// for Agora RTC SDK, just in case if you wanna save | |
// logs to external sdcard. | |
private static final String[] REQUESTED_PERMISSIONS = { | |
Manifest.permission.RECORD_AUDIO, | |
Manifest.permission.CAMERA, | |
Manifest.permission.WRITE_EXTERNAL_STORAGE | |
}; | |
private RtcEngine mRtcEngine; | |
private RelativeLayout mVideoContainer; | |
private VideoCanvas mVideo; | |
// all other views variable | |
String token; | |
String liveTitle; | |
String channelName; | |
int userRole; | |
int channelProfile; | |
// Socket connection | |
private Socket socket; | |
private Emitter.Listener onConnect = new Emitter.Listener() { | |
@Override | |
public void call(Object... args) { | |
runOnUiThread(() -> { | |
Log.e("SOCKET: ", "connected"); | |
// enable any layout element here | |
// just to show like Instagram that a user has joined | |
socket.emit("joinRoom", "room_" + roomId); | |
// add username or id as per your need | |
}); | |
} | |
}; | |
private Emitter.Listener onDisconnect = args -> runOnUiThread(() -> { | |
Log.e("SOCKET: ", "disconnected"); | |
// disable any layout element | |
}); | |
private Emitter.Listener onConnectError = args -> { | |
Log.e("SOCKET: ", "connection error"); | |
}; | |
private Emitter.Listener onNewMessage = args -> runOnUiThread(() -> { | |
JSONObject msgObj = (JSONObject) args[0]; | |
Log.d("MESSAGE", msgObj.toString()); | |
LiveComment msg = new LiveComment(); | |
msg.setCommentText(msgObj.optString("message")); | |
addNewMessageToChat(msg); | |
}); | |
private Emitter.Listener onLikeEvent = args -> runOnUiThread(() -> { | |
JSONObject msgObj = (JSONObject) args[0]; | |
Log.d("LIKE", msgObj.toString()); | |
LiveComment msg = new LiveComment(); | |
msg.setCommentText(msgObj.optString("message")); | |
mTextViewLike.setText(String.valueOf(msgObj.optInt("likes"))); | |
addNewMessageToChat(msg); | |
}); | |
@Override | |
protected void onCreate(Bundle savedInstanceState) { | |
super.onCreate(savedInstanceState); | |
setContentView(R.layout.activity_live_view); | |
Bundle liveData = getIntent().getExtras(); | |
token = liveData.getString("agora_token"); | |
channelName = liveData.getString("channel_name"); | |
liveTitle = liveData.getString("title"); | |
if (liveData.getString("isUser").equals("host")) { | |
userRole = Constants.CLIENT_ROLE_BROADCASTER; | |
} else { | |
userRole = Constants.CLIENT_ROLE_AUDIENCE; | |
} | |
channelProfile = Constants.CHANNEL_PROFILE_LIVE_BROADCASTING; | |
initSocket(); | |
initUI(); | |
if (checkSelfPermission(REQUESTED_PERMISSIONS[0], PERMISSION_REQ_ID) && | |
checkSelfPermission(REQUESTED_PERMISSIONS[1], PERMISSION_REQ_ID) && | |
checkSelfPermission(REQUESTED_PERMISSIONS[2], PERMISSION_REQ_ID)) { | |
initEngineAndJoinChannel(); | |
} | |
} | |
private void initUI() { | |
// initiate view here for all the layout elements needed | |
if (userRole == Constants.CLIENT_ROLE_BROADCASTER) { | |
// make layout elements visible for broadcaster | |
} else if ( userRole == Constants.CLIENT_ROLE_AUDIENCE) { | |
// make layout elements visible for audience | |
} | |
setUpCommentsAdapter(); | |
} | |
LiveCommentsAdapter mLiveCommentsAdapter; | |
private void setUpCommentsAdapter() { | |
LinearLayoutManager llm = new LinearLayoutManager(this); | |
llm.setStackFromEnd(true); | |
mRecyclerViewComments.setLayoutManager(llm); | |
mLiveCommentsAdapter = new LiveCommentsAdapter(this, liveComments); | |
mRecyclerViewComments.setHasFixedSize(true); | |
mRecyclerViewComments.setAdapter(mLiveCommentsAdapter); | |
} | |
private final IRtcEngineEventHandler mRtcEventHandler = new IRtcEngineEventHandler() { | |
@Override | |
public void onJoinChannelSuccess(String channel, final int uid, int elapsed) { | |
runOnUiThread(new Runnable() { | |
@Override | |
public void run() { | |
Log.e("Join channel success", String.valueOf((uid & 0xFFFFFFFFL))); | |
} | |
}); | |
} | |
@Override | |
public void onUserJoined(final int uid, int elapsed) { | |
super.onUserJoined(uid, elapsed); | |
runOnUiThread(new Runnable() { | |
@Override | |
public void run() { | |
Log.e("inside --->>", " on user joined"); | |
if (userRole == Constants.CLIENT_ROLE_BROADCASTER) | |
setupLocalVideo(); | |
else setupRemoteVideo(uid); | |
} | |
}); | |
} | |
@Override | |
public void onUserOffline(int uid, int reason) { | |
runOnUiThread(new Runnable() { | |
@Override | |
public void run() { | |
if (userRole == Constants.CLIENT_ROLE_BROADCASTER) | |
onRemoteUserLeft(uid); | |
} | |
}); | |
} | |
}; | |
private void setupRemoteVideo(int uid) { | |
SurfaceView mRemoteView; | |
Log.e("inside --->>", "setupremotevideo"); | |
mRemoteView = RtcEngine.CreateRendererView(getBaseContext()); | |
mVideoContainer.addView(mRemoteView); | |
// Set the remote video view. | |
mRtcEngine.setupRemoteVideo(new VideoCanvas(mRemoteView, VideoCanvas.RENDER_MODE_HIDDEN, uid)); | |
} | |
private void setupLocalVideo() { | |
mRtcEngine.enableVideo(); | |
SurfaceView localView = RtcEngine.CreateRendererView(getBaseContext()); | |
localView.setZOrderMediaOverlay(true); | |
mVideoContainer.addView(localView); | |
mRtcEngine.setupLocalVideo(new VideoCanvas(localView, VideoCanvas.RENDER_MODE_HIDDEN, 0)); | |
} | |
private void onRemoteUserLeft(int uid) { | |
if (mRemoteVideo != null && mRemoteVideo.uid == uid) { | |
removeFromParent(mRemoteVideo); | |
// Destroys remote view | |
mRemoteVideo = null; | |
} | |
} | |
private boolean checkSelfPermission(String permission, int requestCode) { | |
if (ContextCompat.checkSelfPermission(this, permission) != | |
PackageManager.PERMISSION_GRANTED) { | |
ActivityCompat.requestPermissions(this, REQUESTED_PERMISSIONS, requestCode); | |
return false; | |
} | |
return true; | |
} | |
@Override | |
public void onRequestPermissionsResult(int requestCode, | |
@NonNull String[] permissions, @NonNull int[] grantResults) { | |
if (requestCode == PERMISSION_REQ_ID) { | |
if (grantResults[0] != PackageManager.PERMISSION_GRANTED || | |
grantResults[1] != PackageManager.PERMISSION_GRANTED || | |
grantResults[2] != PackageManager.PERMISSION_GRANTED) { | |
showLongToast("Need permissions " + Manifest.permission.RECORD_AUDIO + | |
"/" + Manifest.permission.CAMERA + "/" + Manifest.permission.WRITE_EXTERNAL_STORAGE); | |
finish(); | |
return; | |
} | |
initEngineAndJoinChannel(); | |
} | |
} | |
private void showLongToast(final String msg) { | |
this.runOnUiThread(new Runnable() { | |
@Override | |
public void run() { | |
Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_LONG).show(); | |
} | |
}); | |
} | |
private void initEngineAndJoinChannel() { | |
initializeEngine(); | |
mRtcEngine.setChannelProfile(channelProfile); | |
mRtcEngine.setClientRole(userRole); | |
if (userRole == Constants.CLIENT_ROLE_BROADCASTER) setupLocalVideo(); | |
setupVideoConfig(); | |
joinChannel(); | |
} | |
private void initializeEngine() { | |
try { | |
Log.e("Initializing: ", "AgoraEngine"); | |
mRtcEngine = RtcEngine.create(getBaseContext(), getString(R.string.agora_app_id), mRtcEventHandler); | |
} catch (Exception e) { | |
Log.e("Exception: ", Log.getStackTraceString(e)); | |
throw new RuntimeException("NEED TO check rtc sdk init fatal error\n" + Log.getStackTraceString(e)); | |
} | |
} | |
private void setupVideoConfig() { | |
mRtcEngine.setVideoEncoderConfiguration(new VideoEncoderConfiguration( | |
VideoEncoderConfiguration.VD_640x480, | |
VideoEncoderConfiguration.FRAME_RATE.FRAME_RATE_FPS_15, | |
VideoEncoderConfiguration.STANDARD_BITRATE, | |
VideoEncoderConfiguration.ORIENTATION_MODE.ORIENTATION_MODE_FIXED_PORTRAIT)); | |
} | |
private void joinChannel() { | |
if (TextUtils.isEmpty(token) || TextUtils.equals(token, "#YOUR ACCESS TOKEN#")) { | |
token = null; // default, no token | |
} | |
mRtcEngine.joinChannel(token.replace("\"", ""), channelName, "Extra Optional Data", 0); | |
} | |
@Override | |
protected void onDestroy() { | |
super.onDestroy(); | |
if (userRole == Constants.CLIENT_ROLE_AUDIENCE) { | |
leaveChannel(); | |
} else { | |
RtcEngine.destroy(); | |
} | |
tearDownSocket(); | |
} | |
private void leaveChannel() { | |
mRtcEngine.leaveChannel(); | |
} | |
public void onLocalAudioMuteClicked(View view) { | |
// Stops/Resumes sending the local audio stream. | |
mRtcEngine.muteLocalAudioStream(mMuted); | |
// change the icon here according to condition | |
} | |
public void onSwitchCameraClicked(View view) { | |
mRtcEngine.switchCamera(); | |
// change the icon here according to condition | |
} | |
public void onCallClicked(View view) { | |
endCall(); | |
} | |
private void endCall() { | |
removeFromParent(mRemoteVideo); | |
mRemoteVideo = null; | |
leaveChannel(); | |
// request to own server to tell the live has ended | |
} | |
private ViewGroup removeFromParent(VideoCanvas canvas) { | |
if (canvas != null) { | |
ViewParent parent = canvas.view.getParent(); | |
if (parent != null) { | |
ViewGroup group = (ViewGroup) parent; | |
group.removeView(canvas.view); | |
return group; | |
} | |
} | |
return null; | |
} | |
public void onNewMessageSend(View view) { | |
String newMessage = mWriteCommentBox.getText().toString(); | |
if (newMessage.trim().length() == 0) { | |
Toast.makeText(this, "Can't send empty message", Toast.LENGTH_SHORT).show(); | |
} | |
else { | |
attemptToSendMessage(newMessage); | |
mWriteCommentBox.setText(""); | |
} | |
} | |
private LiveComment attemptToSendMessage(String message) { | |
if (!socket.connected()) return null; | |
JSONObject commentObj = new JSONObject(); | |
try { | |
commentObj.put("user", userName); | |
commentObj.put("message", message); | |
// include any other details as per your need | |
socket.emit("sendMessage", commentObj); | |
} catch (JSONException e) { | |
e.printStackTrace(); | |
} | |
LiveComment liveComment = new LiveComment(); | |
liveComment.setUserName(userName); | |
liveComment.setCommentText(message); | |
return liveComment; | |
} | |
private ArrayList<LiveComment> liveComments = new ArrayList<>(); | |
private void addNewMessageToChat(LiveComment newMessage) { | |
liveComments.add(newMessage); | |
onChatScreenDataSetChanged(); | |
fullScrollChatList(); | |
} | |
private void onChatScreenDataSetChanged() { | |
if (liveComments.isEmpty()) { | |
mRecyclerViewComments.setVisibility(View.GONE); | |
} else { | |
mRecyclerViewComments.setVisibility(View.VISIBLE); | |
} | |
mLiveCommentsAdapter.notifyDataSetChanged(); | |
} | |
private void fullScrollChatList() { | |
mRecyclerViewComments.postDelayed(() -> mRecyclerViewComments.scrollToPosition(liveComments.size() - 1), 100); | |
} | |
private void initSocket() { | |
try { | |
String socketUrl = "https://demoapp.com"; | |
socket = IO.socket(socketUrl); | |
} catch (Exception e) { | |
e.printStackTrace(); | |
} | |
socket.on(Socket.EVENT_CONNECT, onConnect); | |
socket.on(Socket.EVENT_DISCONNECT, onDisconnect); | |
socket.on(Socket.EVENT_CONNECT_ERROR, onConnectError); | |
socket.on(Socket.EVENT_CONNECT_TIMEOUT, onConnectError); | |
socket.on("newMessage", onNewMessage); | |
socket.on("likes", onLikeEvent); | |
socket.connect(); | |
} | |
private void tearDownSocket() { | |
socket.disconnect(); | |
socket.off(Socket.EVENT_CONNECT, onConnect); | |
socket.off(Socket.EVENT_DISCONNECT, onDisconnect); | |
socket.off(Socket.EVENT_CONNECT_ERROR, onConnectError); | |
socket.off(Socket.EVENT_CONNECT_TIMEOUT, onConnectError); | |
socket.off("newMessage", onNewMessage); | |
socket.off("likes", onLikeEvent); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
To read the related blog, click here.
If you liked it, do a re-tweet
and share with your connections here.