# Demo App

## Initialization

{% tabs %}
{% tab title="Application.java" %}

```
public class Application extends android.app.Application {
    @Override
    public void onCreate() {
        super.onCreate();
        HuddleClient.initApp(this);
    }
}
```

{% endtab %}
{% endtabs %}

## UI&#x20;

### Java

{% tabs %}
{% tab title="MeView\.java" %}

```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());
    }
}
```

{% endtab %}

{% tab title="PeerView\.java" %}

```java
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);
    }
}

```

{% endtab %}

{% tab title="PeerAdapter.java" %}

```java
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);
        }
    }
}
```

{% endtab %}

{% tab title="BindingAdapter.java" %}

```java
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);
        }
    }
}
```

{% endtab %}
{% endtabs %}

### XML

{% tabs %}
{% tab title="activity\_room.xml" %}

```markup
<?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>
```

{% endtab %}

{% tab title="view\_me.xml" %}

```markup
<?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>

```

{% endtab %}

{% tab title="view\_peer.xml" %}

```markup
<?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>
```

{% endtab %}

{% tab title="item\_remote\_peer.xml" %}

```markup
<?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" />
```

{% endtab %}

{% tab title="view\_peer\_view\.xml" %}

```markup
<?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>

```

{% endtab %}
{% endtabs %}

## Room

{% tabs %}
{% tab title="RoomActivity.java" %}

```java
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);
        }
    };
}
```

{% endtab %}
{% endtabs %}

## State

{% tabs %}
{% tab title="StateStore.java" %}

```java
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;
    }

```

{% endtab %}
{% endtabs %}

For any help, reach out to us on Slack. We are available 24\*7 at: [Huddle01 Community](https://bit.ly/3AsIsT7).


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://huddle01-2.gitbook.io/huddle01/android-native/demo-app.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
