Demo App
An app demonstrating consumption of Huddle01 Android SDK
Initialization
public class Application extends android.app.Application {
@Override
public void onCreate() {
super.onCreate();
HuddleClient.initApp(this);
}
}
UI
Java
public class MeView extends RelativeLayout {
public MeView(@NonNull Context context) {
super(context);
init(context);
}
public MeView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context);
}
public MeView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public MeView(
@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context);
}
com.rohg007.android.huddle01_android_sdk.databinding.ViewMeBindingImpl mBinding;
private void init(Context context) {
mBinding = DataBindingUtil.inflate(LayoutInflater.from(context), R.layout.view_me, this, true);
mBinding.peerView.videoRenderer.init(PeerConnectionUtils.getEglContext(), null);
}
public void setProps(MeProps props, final HuddleClient huddleClient) {
// set view model.
mBinding.peerView.setPeerViewProps(props);
// register click listener.
mBinding.peerView.info.setOnClickListener(
view -> {
Boolean showInfo = props.getShowInfo().get();
props.getShowInfo().set(showInfo != null && showInfo ? Boolean.FALSE : Boolean.TRUE);
});
mBinding.peerView.meDisplayName.setOnEditorActionListener(
(textView, actionId, keyEvent) -> {
if (actionId == EditorInfo.IME_ACTION_DONE) {
huddleClient.changeDisplayName(textView.getText().toString().trim());
return true;
}
return false;
});
mBinding.peerView.videoRenderer.setZOrderMediaOverlay(true);
// set view model.
mBinding.setMeProps(props);
// register click listener.
mBinding.mic.setOnClickListener(
view -> {
if (MeProps.DeviceState.ON.equals(props.getMicState().get())) {
huddleClient.muteMic();
} else {
huddleClient.unmuteMic();
}
});
mBinding.cam.setOnClickListener(
view -> {
if (MeProps.DeviceState.ON.equals(props.getCamState().get())) {
huddleClient.disableCam();
} else {
huddleClient.enableCam();
}
});
mBinding.changeCam.setOnClickListener(view -> huddleClient.changeCam());
}
}
XML
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:bind="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/onboarding_"
android:orientation="vertical"
tools:context=".RoomActivity">
<EditText
android:id="@+id/chat_input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginLeft="10dp"
android:layout_marginBottom="10dp"
android:imeOptions="actionDone"
android:singleLine="true" />
<ImageView
android:id="@+id/room_state"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginTop="5dp"
android:padding="5dp"
android:src="@drawable/ic_state_new_close"
bind:edias_state="@{roomProps.connectionState}"
bind:edias_state_animation="@{roomProps.getConnectingAnimation}" />
<TextView
android:id="@+id/version"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_marginTop="5dp"
android:layout_marginEnd="10dp"
android:padding="5dp"
android:textColor="@color/text_color" />
<TextView
android:id="@+id/invitation_link"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:background="@drawable/bg_area"
android:paddingLeft="15dp"
android:paddingTop="8dp"
android:paddingRight="15dp"
android:paddingBottom="8dp"
android:text="@string/invitation_link"
android:textColor="@color/text_color"
android:textSize="15sp"
bind:edias_link="@{roomProps.invitationLink}" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/remote_peers"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone" />
<com.leinardi.android.speeddial.SpeedDialView
android:id="@+id/reaction_fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@id/chat_input"
android:layout_alignParentEnd="true"
bind:sdMainFabClosedSrc="@drawable/ic_baseline_tag_faces_24" />
<com.rohg007.android.huddle01_android_sdk.views.MeView
android:id="@+id/me"
android:layout_width="220dp"
android:layout_height="200dp"
android:layout_alignParentBottom="true"
android:layout_marginLeft="10dp"
android:layout_marginBottom="60dp" />
</RelativeLayout>
<data>
<variable
name="roomProps"
type="com.rohg007.android.huddle01_android_sdk.viewmodels.RoomProps" />
</data>
</layout>
Room
public class RoomActivity extends AppCompatActivity {
private static final String TAG = RoomActivity.class.getSimpleName();
private static final int REQUEST_CODE_SETTING = 1;
private String mRoomId, mPeerId, mDisplayName;
private ActivityRoomBinding activityRoomBinding;
private HuddleClient huddleClient;
private StateStore stateStore;
private String apiKey = API_KEY_HERE;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
activityRoomBinding = DataBindingUtil.setContentView(this, R.layout.activity_room);
stateStore = new StateStore();
createRoom();
checkPermission();
setupReactions();
}
@SuppressLint("NonConstantResourceId")
private void setupReactions(){
activityRoomBinding.reactionFab.inflate(R.menu.reactions_menu);
activityRoomBinding.reactionFab.setOnActionSelectedListener(actionItem -> {
switch (actionItem.getId()){
case R.id.tears_of_joy: {
huddleClient.sendReaction(Reactions.TEARS_OF_JOY);
activityRoomBinding.reactionFab.close();
return true;
}
case R.id.crying_face: {
huddleClient.sendReaction(Reactions.CRYING_FACE);
activityRoomBinding.reactionFab.close();
return true;
}
case R.id.hundred: {
huddleClient.sendReaction(Reactions.HUNDRED);
activityRoomBinding.reactionFab.close();
return true;
}
case R.id.thumbs_up: {
huddleClient.sendReaction(Reactions.THUMBS_UP);
activityRoomBinding.reactionFab.close();
return true;
}
case R.id.thumbs_down: {
huddleClient.sendReaction(Reactions.THUMBS_DOWN);
activityRoomBinding.reactionFab.close();
return true;
}
case R.id.rocket: {
huddleClient.sendReaction(Reactions.ROCKET);
activityRoomBinding.reactionFab.close();
return true;
}
default: {
Toast.makeText(this, "invalid reaction", Toast.LENGTH_SHORT).show();
activityRoomBinding.reactionFab.close();
return false;
}
}
});
}
private void createRoom(){
loadRoomConfig();
activityRoomBinding.invitationLink.setText("RoomId: "+mRoomId);
huddleClient = new HuddleClient.Builder(getApplicationContext(), apiKey)
.setPeerId(mPeerId)
.setRoomId(mRoomId)
.setDisplayName(mDisplayName)
.setCanConsume(true)
.setCanProduce(true)
.setCanUseDataChannel(true)
.setRoomEventListener(listener)
.setMeListener(meListener)
.setFrontCamEnabledOnInit(true)
.build();
getViewModelStore().clear();
initViewModel();
}
void joinRoom(){
huddleClient.joinRoom();
}
private void loadRoomConfig(){
Intent intent = getIntent();
String roomId = intent.getStringExtra("roomId");
if(roomId!=null){
mRoomId = roomId;
} else {
mRoomId = Utils.getRandomString(8);
}
mPeerId = Utils.getRandomString(8);
mDisplayName = Utils.getRandomString(8);
}
private void initViewModel(){
EdiasProps.Factory factory = new EdiasProps.Factory(getApplication(), stateStore);
RoomProps roomProps = new ViewModelProvider(this, factory).get(RoomProps.class);
roomProps.connect(this);
activityRoomBinding.invitationLink.setOnClickListener(v -> {
String invitationLink = roomProps.getInvitationLink().get();
ClipboardCopy.clipboardCopy(getApplication(), invitationLink, R.string.invite_link_copied);
});
activityRoomBinding.setRoomProps(roomProps);
MeProps meProps = new ViewModelProvider(this, factory).get(MeProps.class);
meProps.connect(this);
activityRoomBinding.me.setProps(meProps, huddleClient);
activityRoomBinding.chatInput.setOnEditorActionListener((v, actionId, event) -> {
if(actionId == EditorInfo.IME_ACTION_DONE){
huddleClient.sendChatMessage(v.getText().toString());
v.setText("");
return true;
} else
return false;
});
PeerAdapter peerAdapter = new PeerAdapter(stateStore, this);
activityRoomBinding.remotePeers.setLayoutManager(new LinearLayoutManager(this));
activityRoomBinding.remotePeers.setAdapter(peerAdapter);
stateStore.getPeers().observe(this, peers -> {
List<Peer> peerList = peers.getAllPeers();
if(peerList.isEmpty()){
activityRoomBinding.remotePeers.setVisibility(View.GONE);
activityRoomBinding.roomState.setVisibility(View.VISIBLE);
} else {
activityRoomBinding.remotePeers.setVisibility(View.VISIBLE);
activityRoomBinding.roomState.setVisibility(View.GONE);
}
peerAdapter.replacePeers(peerList);
});
stateStore.getNotify().observe(this, notify -> {
SpannableStringBuilder text = SpannableStringBuilder.valueOf(notify.getText());
if(notify.getType().equals("error"))
text.setSpan(new ForegroundColorSpan(Color.RED), 0, text.length(), 0);
Toast.makeText(this, text, notify.getTimeout()).show();
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
if(requestCode == REQUEST_CODE_SETTING){
destroyRoom();
createRoom();
joinRoom();
} else super.onActivityResult(requestCode, resultCode, data);
}
private void destroyRoom(){
huddleClient.closeRoom();
}
@Override
protected void onDestroy() {
super.onDestroy();
destroyRoom();
}
private void checkPermission() {
String[] permissions = {
Manifest.permission.INTERNET,
Manifest.permission.RECORD_AUDIO,
Manifest.permission.CAMERA,
Manifest.permission.WRITE_EXTERNAL_STORAGE
};
String rationale = "Please provide permissions";
Permissions.Options options =
new Permissions.Options().setRationaleDialogTitle("Info").setSettingsDialogTitle("Warning");
Permissions.check(this, permissions, rationale, options, permissionHandler);
}
private final PermissionHandler permissionHandler =
new PermissionHandler() {
@Override
public void onGranted() {
joinRoom();
}
};
private final RoomEventListener listener = new RoomEventListener() {
@Override
public void onRoomUrlGenerated(String roomId, String roomUrl) {
stateStore.setRoomUrl(roomId, roomUrl);
}
@Override
public void onRoomStateChanged(ConnectionState state) {
stateStore.setRoomState(state);
}
@Override
public void onError(String message) {
}
@Override
public void onPeerChanged(String actionType, String peerId, @Nullable JSONObject info, @Nullable String displayName) {
switch (actionType){
case Constants.ACTION_ADDED: stateStore.addPeer(peerId, info);
break;
case Constants.ACTION_DISPLAY_NAME_CHANGED: stateStore.setPeerDisplayName(peerId, displayName);
break;
case Constants.ACTION_REMOVED: stateStore.removePeer(peerId);
break;
default: Toast.makeText(getApplicationContext(), "Invalid Action Type", Toast.LENGTH_SHORT).show();
}
}
@Override
public void onProducersChanged(String type, HuddleProducer producer) {
switch (type){
case Constants
.ACTION_ADDED: stateStore.addProducer(producer);
break;
case Constants.ACTION_PAUSED: stateStore.setProducerPaused(producer.getId());
break;
case Constants.ACTION_RESUMED: stateStore.setProducerResumed(producer.getId());
break;
case Constants.ACTION_REMOVED: stateStore.removeProducer(producer.getId());
break;
default: Toast.makeText(getApplicationContext(), "Invalid Producer Action Type", Toast.LENGTH_SHORT).show();
}
}
@Override
public void onConsumerAdded(String peerId, String consumerType, HuddleConsumer consumer, boolean remotelyPaused) {
stateStore.addConsumer(peerId, consumerType, consumer, remotelyPaused);
}
@Override
public void onConsumerRemoved(String peerId, String consumerId) {
stateStore.removeConsumer(peerId, consumerId);
}
@Override
public void onConsumerStateChanged(String actionType, String consumerId, String originator) {
switch (actionType){
case Constants.ACTION_PAUSED: stateStore.setConsumerPaused(consumerId, originator);
break;
case Constants.ACTION_RESUMED: stateStore.setConsumerResumed(consumerId, originator);
break;
default: Toast.makeText(getApplicationContext(), "Invalid Action Type", Toast.LENGTH_SHORT).show();
}
}
@Override
public void onDataProducerChanged(String actionType, HuddleDataProducer dataProducer) {
switch (actionType){
case Constants.ACTION_ADDED: stateStore.addDataProducer(dataProducer);
break;
case Constants.ACTION_REMOVED: stateStore.removeDataProducer(dataProducer.getId());
break;
default: Toast.makeText(getApplicationContext(), "Invalid Action Type", Toast.LENGTH_SHORT).show();
}
}
@Override
public void onDataConsumerChanged(String actionType, String peedId, HuddleDataConsumer dataConsumer) {
switch (actionType) {
case Constants.ACTION_ADDED: stateStore.addDataConsumer(peedId, dataConsumer);
break;
case Constants.ACTION_REMOVED: stateStore.removeDataConsumer(peedId, dataConsumer.getId());
break;
default: Toast.makeText(getApplicationContext(), "Invalid Action Type", Toast.LENGTH_SHORT).show();
}
}
@Override
public void onReactionReceived(Peer peer, String reaction) {
Toast.makeText(getApplicationContext(), peer.getDisplayName()+" reacted: "+reaction, Toast.LENGTH_SHORT).show();
}
@Override
public void onChatReceived(Peer peer, String message) {
Toast.makeText(getApplicationContext(), peer.getDisplayName()+" messaged: "+message, Toast.LENGTH_SHORT).show();
}
@Override
public void onNotification(Notify notify) {
Toast.makeText(getApplicationContext(), notify.getText(), Toast.LENGTH_SHORT).show();
}
};
private final MeListener meListener = new MeListener() {
@Override
public void onMeReceived(String peerId, String displayName, DeviceInfo deviceInfo) {
stateStore.setMe(peerId, displayName, deviceInfo);
}
@Override
public void onMediaCapabilitiesReceived(boolean canSendMic, boolean canSendCam) {
stateStore.setMediaCapabilities(canSendMic, canSendCam);
}
@Override
public void onNewDisplayName(String displayName) {
stateStore.setDisplayName(displayName);
}
};
}
State
public class StateStore {
CustomTypeMutableLiveData<RoomInfo> roomInfo = new CustomTypeMutableLiveData<>(RoomInfo::new);
CustomTypeMutableLiveData<Me> me = new CustomTypeMutableLiveData<>(Me::new);
CustomTypeMutableLiveData<Producers> producers = new CustomTypeMutableLiveData<>(Producers::new);
CustomTypeMutableLiveData<DataProducers> dataProducers = new CustomTypeMutableLiveData<>(DataProducers::new);
CustomTypeMutableLiveData<Peers> peers = new CustomTypeMutableLiveData<>(Peers::new);
CustomTypeMutableLiveData<Consumers> consumers = new CustomTypeMutableLiveData<>(Consumers::new);
CustomTypeMutableLiveData<DataConsumers> dataConsumers = new CustomTypeMutableLiveData<>(DataConsumers::new);
MutableLiveData<Notify> notify = new MutableLiveData<>();
public void setRoomUrl(String roomId, String url){
roomInfo.postValue(roomInfo->{
roomInfo.setRoomId(roomId);
roomInfo.setUrl(url);
});
}
public void setRoomState(ConnectionState state){
roomInfo.postValue(roomInfo->{
roomInfo.setConnectionState(state);
});
if(state == ConnectionState.CLOSED){
peers.postValue(Peers::clear);
me.postValue(Me::clear);
producers.postValue(Producers::clear);
consumers.postValue(Consumers::clear);
}
}
public void setMe(String peerId, String displayName, DeviceInfo device){
me.postValue(me->{
me.setId(peerId);
me.setDisplayName(displayName);
me.setDeviceInfo(device);
});
}
public void setMediaCapabilities(boolean canSendMic, boolean canSendCam){
me.postValue(me-> {
me.setCanSendMic(canSendMic);
me.setCanSendCam(canSendCam);
});
}
public void setDisplayName(String displayName){
me.postValue(me->me.setDisplayName(displayName));
}
public void addProducer(HuddleProducer producer){
producers.postValue(producers->producers.addProducer(producer));
}
public void setProducerPaused(String producerId){
producers.postValue(producers->producers.setProducerPaused(producerId));
}
public void setProducerResumed(String producerId){
producers.postValue(producers->producers.setProducerResumed(producerId));
}
public void removeProducer(String producerId){
producers.postValue(producers->producers.removeProducer(producerId));
}
public void addDataProducer(HuddleDataProducer dataProducer){
dataProducers.postValue(dataProducers->dataProducers.addDataProducer(dataProducer));
}
public void removeDataProducer(String dataProducerId){
dataProducers.postValue(dataProducers->dataProducers.removeDataProducer(dataProducerId));
}
public void addPeer(String peerId, JSONObject info){
peers.postValue(peers-> {
peers.addPeer(peerId, info);
});
}
public void setPeerDisplayName(String peerId, String displayName){
peers.postValue(peers->peers.setPeerDisplayName(peerId, displayName));
}
public void removePeer(String peerId){
roomInfo.postValue(roomInfo->{
if(!TextUtils.isEmpty(peerId) && roomInfo.getActiveSpeakerId().equals(peerId))
roomInfo.setActiveSpeakerId(null);
if(!TextUtils.isEmpty(peerId) && roomInfo.getStatsPeerId().equals(peerId))
roomInfo.setStatsPeerId(null);
});
peers.postValue(peers->peers.removePeer(peerId));
}
public void addConsumer(String peerId, String type, HuddleConsumer consumer, boolean remotelyPaused){
consumers.postValue(consumers->consumers.addConsumer(type, consumer, remotelyPaused));
peers.postValue(peers->peers.addConsumer(peerId, consumer));
}
public void removeConsumer(String peerId, String consumerId){
consumers.postValue(consumers->consumers.removeConsumer(consumerId));
peers.postValue(peers->peers.removeConsumer(peerId, consumerId));
}
public void setConsumerPaused(String consumerId, String originator){
consumers.postValue(consumers->consumers.setConsumerPaused(consumerId, originator));
}
public void setConsumerResumed(String consumerId, String originator){
consumers.postValue(consumers->consumers.setConsumerResumed(consumerId, originator));
}
public void addDataConsumer(String peerId, HuddleDataConsumer dataConsumer){
dataConsumers.postValue(dataConsumers->dataConsumers.addDataConsumer(dataConsumer));
peers.postValue(peers->peers.addDataConsumer(peerId, dataConsumer));
}
public void removeDataConsumer(String peerId, String dataConsumerId){
dataConsumers.postValue(dataConsumers->dataConsumers.removeDataConsumer(dataConsumerId));
peers.postValue(peers->peers.removeDataConsumer(peerId, dataConsumerId));
}
public CustomTypeMutableLiveData<RoomInfo> getRoomInfo() {
return roomInfo;
}
public CustomTypeMutableLiveData<Me> getMe() {
return me;
}
public CustomTypeMutableLiveData<Producers> getProducers() {
return producers;
}
public CustomTypeMutableLiveData<Peers> getPeers() {
return peers;
}
public CustomTypeMutableLiveData<Consumers> getConsumers() {
return consumers;
}
public MutableLiveData<Notify> getNotify() {
return notify;
}
For any help, reach out to us on Slack. We are available 24*7 at: Huddle01 Community.
Last updated
Was this helpful?