Knowledge Base
  • Welcome to Huddle01 SYNC
  • Web-SDK
    • Client
    • Demo App
  • Flutter SDK
    • Client
    • Demo App
  • Android-Native
    • Client
    • Demo App
  • React-Native
    • Client
    • Demo App
Powered by GitBook
On this page
  • Initialization
  • UI
  • Java
  • XML
  • Room
  • State

Was this helpful?

  1. Android-Native

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());
    }
}
public class PeerView extends RelativeLayout {

    public PeerView(@NonNull Context context) {
        super(context);
        init(context);
    }

    public PeerView(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public PeerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public PeerView(
            @NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context);
    }

    com.rohg007.android.huddle01_android_sdk.databinding.ViewPeerBinding mBinding;

    private void init(Context context) {
        mBinding =
                DataBindingUtil.inflate(LayoutInflater.from(context), R.layout.view_peer, this, true);
        mBinding.peerView.videoRenderer.init(PeerConnectionUtils.getEglContext(), null);
    }

    public void setProps(PeerProps props) {
        // set view model into included layout
        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);
                });

        // set view model
        mBinding.setPeerProps(props);
    }
}
public class PeerAdapter extends RecyclerView.Adapter<PeerAdapter.PeerViewHolder> {

    private static final String TAG = "PeerAdapter";

    @NonNull private StateStore mStore;
    @NonNull private LifecycleOwner mLifecycleOwner;

    private List<Peer> mPeers = new LinkedList<>();

    private int containerHeight;

    public PeerAdapter(
            @NonNull StateStore store,
            @NonNull LifecycleOwner lifecycleOwner) {
        mStore = store;
        mLifecycleOwner = lifecycleOwner;
    }

    public void replacePeers(@NonNull List<Peer> peers) {
        mPeers = peers;
        notifyDataSetChanged();
    }

    @NonNull
    @Override
    public PeerViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        containerHeight = parent.getHeight();
        Context context = parent.getContext();
        View view = LayoutInflater.from(context).inflate(R.layout.item_remote_peer, parent, false);
        return new PeerViewHolder(
                view, new PeerProps(((AppCompatActivity) context).getApplication(), mStore));
    }

    @Override
    public void onBindViewHolder(@NonNull PeerViewHolder holder, int position) {
        // update height
        ViewGroup.LayoutParams layoutParams = holder.mPeerView.getLayoutParams();
        layoutParams.height = getItemHeight();
        holder.mPeerView.setLayoutParams(layoutParams);
        // bind
        holder.bind(mLifecycleOwner, mPeers.get(position));
    }

    @Override
    public int getItemCount() {
        return mPeers.size();
    }

    private int getItemHeight() {
        int itemCount = getItemCount();
        if (itemCount <= 1) {
            return containerHeight;
        } else if (itemCount <= 3) {
            return containerHeight / itemCount;
        } else {
            return (int) (containerHeight / 3.2);
        }
    }

    static class PeerViewHolder extends RecyclerView.ViewHolder {

        @NonNull
        final PeerView mPeerView;
        @NonNull final PeerProps mPeerProps;

        PeerViewHolder(@NonNull View view, @NonNull PeerProps peerProps) {
            super(view);
            mPeerView = view.findViewById(R.id.remote_peer);
            mPeerProps = peerProps;
        }

        void bind(LifecycleOwner owner, @NonNull Peer peer) {
            mPeerProps.connect(owner, peer.getId());
            mPeerView.setProps(mPeerProps);
        }
    }
}
public class BindingAdapters {

    private static final String TAG = "BindingAdapters";

    @BindingAdapter({"bind:edias_state", "bind:edias_state_animation"})
    public static void roomState(
            ImageView view, ConnectionState state, Animation animation) {
        if (state == null) {
            return;
        }
        if (ConnectionState.CONNECTING.equals(state)) {
            view.setImageResource(R.drawable.ic_state_connecting);
            view.startAnimation(animation);
        } else if (ConnectionState.CONNECTED.equals(state)) {
            view.setImageResource(R.drawable.ic_state_connected);
            animation.cancel();
            view.clearAnimation();
        } else {
            view.setImageResource(R.drawable.ic_state_new_close);
            animation.cancel();
            view.clearAnimation();
        }
    }

    @BindingAdapter({"bind:edias_link"})
    public static void inviteLink(TextView view, String inviteLink) {
        view.setVisibility(TextUtils.isEmpty(inviteLink) ? View.INVISIBLE : View.VISIBLE);
    }

    @SuppressLint("SetTextI18n")
    @BindingAdapter({"bind:edias_device"})
    public static void deviceInfo(TextView view, DeviceInfo deviceInfo) {
        if (deviceInfo == null) {
            return;
        }

        int deviceIcon = R.drawable.ic_unknown;
        if (!TextUtils.isEmpty(deviceInfo.getFlag())) {
            switch (deviceInfo.getFlag().toLowerCase()) {
                case "chrome":
                    deviceIcon = R.mipmap.chrome;
                    break;
                case "firefox":
                    deviceIcon = R.mipmap.firefox;
                    break;
                case "safari":
                    deviceIcon = R.mipmap.safari;
                    break;
                case "opera":
                    deviceIcon = R.mipmap.opera;
                    break;
                case "edge":
                    deviceIcon = R.mipmap.edge;
                    break;
                case "android":
                    deviceIcon = R.mipmap.android;
                    break;
            }
            view.setText(deviceInfo.getName() + " " + deviceInfo.getVersion());
        } else {
            view.setText("");
        }
        view.setCompoundDrawablesWithIntrinsicBounds(deviceIcon, 0, 0, 0);
    }

    @BindingAdapter({"edias_mic_state"})
    public static void deviceMicState(ImageView imageView, MeProps.DeviceState state) {
        if (state == null) {
            return;
        }
        if (MeProps.DeviceState.ON.equals(state)) {
            imageView.setBackgroundResource(R.drawable.bg_media_box_on);
        } else {
            imageView.setBackgroundResource(R.drawable.bg_media_box_off);
        }

        switch (state) {
            case ON:
                imageView.setImageResource(R.drawable.icon_mic_black_on);
                break;
            case OFF:
                imageView.setImageResource(R.drawable.icon_mic_white_off);
                break;
            case UNSUPPORTED:
                imageView.setImageResource(R.drawable.icon_mic_white_unsupported);
                break;
        }
    }

    @BindingAdapter({"edias_cam_state"})
    public static void deviceCamState(ImageView imageView, MeProps.DeviceState state) {
        if (state == null) {
            return;
        }
        if (MeProps.DeviceState.ON.equals(state)) {
            imageView.setBackgroundResource(R.drawable.bg_media_box_on);
        } else {
            imageView.setBackgroundResource(R.drawable.bg_media_box_off);
        }

        switch (state) {
            case ON:
                imageView.setImageResource(R.drawable.icon_webcam_black_on);
                break;
            case OFF:
                imageView.setImageResource(R.drawable.icon_webcam_white_off);
                break;
            case UNSUPPORTED:
                imageView.setImageResource(R.drawable.icon_webcam_white_unsupported);
                break;
        }
    }

    @BindingAdapter({"edias_change_came_state"})
    public static void changeCamState(View view, MeProps.DeviceState state) {
        if (state == null) {
            return;
        }
        view.setEnabled(MeProps.DeviceState.ON.equals(state));
    }

    @BindingAdapter({"edias_share_state"})
    public static void shareState(View view, MeProps.DeviceState state) {
        if (state == null) {
            return;
        }
        view.setEnabled(MeProps.DeviceState.ON.equals(state));
    }

    @BindingAdapter({"edias_render"})
    public static void render(SurfaceViewRenderer renderer, VideoTrack track) {
        if (track != null) {
            track.addSink(renderer);
            renderer.setVisibility(View.VISIBLE);
        } else {
            renderer.setVisibility(View.GONE);
        }
    }

    @BindingAdapter({"edias_render_empty"})
    public static void renderEmpty(View renderer, VideoTrack track) {
        if (track == null) {
            renderer.setVisibility(View.VISIBLE);
        } else {
            renderer.setVisibility(View.GONE);
        }
    }
}

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>
<?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">

    <data>

        <import type="android.view.View" />

        <variable
            name="meProps"
            type="com.rohg007.android.huddle01_android_sdk.viewmodels.MeProps" />

    </data>

    <androidx.cardview.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        bind:cardBackgroundColor="@color/peer_bg_color"
        bind:cardCornerRadius="16dp"
        bind:cardUseCompatPadding="true">

        <include
            android:id="@+id/peer_view"
            layout="@layout/view_peer_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            bind:peerViewProps="@{meProps}" />

        <LinearLayout
            android:id="@+id/controls"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="right"
            android:gravity="center_vertical"
            android:padding="5dp"
            android:visibility="@{meProps.connected ? View.VISIBLE : View.GONE}">

            <ImageView
                android:id="@+id/mic"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginEnd="2dp"
                android:background="@drawable/bg_media_box"
                android:padding="5dp"
                android:src="@drawable/icon_mic"
                bind:edias_mic_state="@{meProps.micState}" />

            <ImageView
                android:id="@+id/cam"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginEnd="2dp"
                android:background="@drawable/bg_media_box"
                android:clickable="@{!(meProps.me.camInProgress || meProps.me.shareInProgress)}"
                android:padding="5dp"
                android:src="@drawable/icon_webcam"
                bind:edias_cam_state="@{meProps.camState}" />

            <ImageView
                android:id="@+id/change_cam"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginEnd="2dp"
                android:background="@drawable/bg_media_box"
                android:clickable="@{!(meProps.me.camInProgress || meProps.me.shareInProgress)}"
                android:padding="5dp"
                android:src="@drawable/icon_change_cam"
                bind:edias_change_came_state="@{meProps.camState}" />

        </LinearLayout>

    </androidx.cardview.widget.CardView>
</layout>
<?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">

    <data>

        <import type="android.view.View" />

        <variable
            name="peerProps"
            type="com.rohg007.android.huddle01_android_sdk.viewmodels.PeerProps" />
    </data>

    <androidx.cardview.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        bind:cardBackgroundColor="@color/peer_bg_color"
        bind:cardCornerRadius="16dp"
        bind:cardElevation="5dp"
        bind:cardUseCompatPadding="true">


        <include
            android:id="@+id/peer_view"
            layout="@layout/view_peer_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            bind:peerViewProps="@{peerProps}" />

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="right"
            android:padding="5dp">

            <ImageView
                android:id="@+id/mic_off"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="4dp"
                android:layout_marginEnd="5dp"
                android:clickable="false"
                android:src="@drawable/ic_microphone_slash"
                android:visibility="@{!peerProps.audioEnabled ? View.VISIBLE : View.GONE}" />

            <ImageView
                android:id="@+id/cam_off"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="4dp"
                android:clickable="false"
                android:src="@drawable/ic_videocamera_slash"
                android:visibility="@{!peerProps.videoVisible ? View.VISIBLE : View.GONE}" />

        </LinearLayout>

    </androidx.cardview.widget.CardView>
</layout>
<?xml version="1.0" encoding="utf-8"?>

<com.rohg007.android.huddle01_android_sdk.views.PeerView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/remote_peer"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
<?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">

    <data>

        <import type="androidx.core.util.Consumer" />

        <import type="android.text.TextUtils" />

        <import type="android.view.View" />

        <variable
            name="peerViewProps"
            type="com.rohg007.android.huddle01_android_sdk.viewmodels.PeerViewProps" />
    </data>

    <FrameLayout
        android:background="@color/peer_bg_color"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <org.webrtc.SurfaceViewRenderer
            android:id="@+id/video_renderer"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:visibility="gone"
            bind:edias_render="@{peerViewProps.videoTrack}" />

        <LinearLayout
            android:id="@+id/video_hidden"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical"
            bind:edias_render_empty="@{peerViewProps.videoTrack}">

            <View
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_weight="1" />

            <ImageView
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_weight="5"
                android:scaleType="fitCenter"
                android:src="@drawable/buddy" />
        </LinearLayout>
        
        <LinearLayout
            android:id="@+id/peer"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom"
            android:gravity="start"
            android:orientation="vertical"
            android:background="@color/peer_display_name_bg_color"
            android:padding="5dp">

            <EditText
                android:id="@+id/me_display_name"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@null"
                android:textSize="14sp"
                android:textStyle="bold"
                android:textColor="@color/peer_display_name_text_color"
                android:imeOptions="actionDone"
                android:singleLine="true"
                android:text="@{peerViewProps.peer.displayName}"
                android:visibility="@{peerViewProps.me ? View.VISIBLE : View.GONE}"
                android:layout_marginHorizontal="8dp"/>

            <TextView
                android:id="@+id/peer_display_name"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="5dp"
                android:singleLine="true"
                android:text="@{peerViewProps.peer.displayName}"
                android:textColor="@color/peer_display_name_text_color"
                android:textSize="14sp"
                android:textStyle="bold"
                android:visibility="@{!peerViewProps.me ? View.VISIBLE : View.GONE}"
                android:layout_marginHorizontal="8dp"/>

        </LinearLayout>

    </FrameLayout>
</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;
    }
PreviousClientNextClient

Last updated 3 years ago

Was this helpful?

For any help, reach out to us on Slack. We are available 24*7 at: .

Huddle01 Community