Lets make a Twitch Android app. Part 2. The secret to implementing chat
Tags: android, mobile, kotlin, tristan
Published: 2023-07-24 20:31:38

### Table of contents
1. [A quick disclaimer](#quick)
2. [Embedding the video](#video)
3. [Embedding the chat](#chat)
4. [What is next?](#next)


### The code
- [GitHub](https://github.com/thePlebDev/Clicker)


### Introduction <a name="intro"></a>
- I have embarked on my next app, a Twitch client app. This series will be all my notes and problems faced when creating this app.

### A quick disclaimer <a name="quick"></a>
- This tutorial is not a finished and polished product, it is simply me stating my current progress on implementing the Twitch video and chat. I am writing this before I have a polished product because I believe there is a better way to implement the chat then the documentation initially suggests. I want to point people down the path I am heading, so they don't get lost like I was

### Embedding the video <a name="video"></a>
- To embed a twitch video is fairly straight forward. However, since Android has multiple design systems, there are multiple ways to implement it. Theses are the main two:

**1) WebView :** Using a [WebView](https://developer.android.com/develop/ui/views/layout/webapps) is the proper way to show a user videos. It will allow our application show a twitch stream directly . We can tackle this problem through XML or Compose. 

- The XML is pretty simple, just define a WebView and give it any needed constraints:

```
<WebView
     android:id="@+id/webView"
     android:layout_width="match_parent"
     android:layout_height="200dp"
     />

```
- We then need to find it in the `onCreateView()` method (I am using fragments) and enable javascript and load the url:

```
val url="https://player.twitch.tv/?channel=$channelName&parent=applicationsName"
val myWebView: WebView = view.findViewById(R.id.webView)
        myWebView.settings.javaScriptEnabled = true
        myWebView.loadUrl(url)

```
- It is worth pointing out that we will be given a warning about enabling JavaScript. However, the embedded twitch stream will not work without it.

- **2) AndroidView :** The Compose version is a little more complex as we need to use the [AndroidView](https://developer.android.com/jetpack/compose/migrate/interoperability-apis/views-in-compose) class. To create a Composable we would do something like this:
```
@Composable
fun AnotherTesting(){
    val context = LocalContext.current
    val html = "<iframe src=\"?channel=ChannelYouWantToVisit&parent=NameOfYourApplication\" height=\"360\" width=\"640\" allowfullscreen/>"
    AndroidView(

        factory = {
            WebView(context).apply {
                webViewClient = WebViewClient()
                webChromeClient = WebChromeClient()
                settings.loadsImagesAutomatically = true
                settings.javaScriptEnabled = true
                settings.allowFileAccess = true
                settings.javaScriptCanOpenWindowsAutomatically = true
                settings.mediaPlaybackRequiresUserGesture = false
                settings.domStorageEnabled = true
                settings.cacheMode = WebSettings.LOAD_NO_CACHE

                loadDataWithBaseURL("https://player.twitch.tv/", html, "text/html", "UTF-8", null)

            }
        }
    )
}

```
- Now I would like to point out that with this compose version I was only able to get the audio version working. This is most like do to my lack of experience of working with the `AndroidView`. So if your mind is set on using the compose system, know that you may have to mess around with the `AndroidView` a little longer. However, due to the simplicity of it `just working`, I have decided to stick with the simple XML implementation


### Embedding the chat <a name="chat"></a>
- This is a little trickier, mainly because the [documentation](https://dev.twitch.tv/docs/embed/everything/) tells us that we just need to embed the video and chat together. However, this does not result in the best user experience. Also, the user has to sign in through the WebView to chat(Which is considered an anti practice and a attack vector for hackers)

**The secret :** I believe the secret to creating a better twitch chat experience than the embedded version is to treat our application as a chatbot and to implement [Twitch’s IRC service](https://dev.twitch.tv/docs/irc/). The downside being that we will have to entirely recreate the Twitch chat experience. However, we will get the freedom to recreate the UI as we see fit. There is another downside of [Rate Limits](https://dev.twitch.tv/docs/irc/#rate-limits). At this moment I am uncertain if the each individual user would be treated as a separate bot or if all users get treated collectively as one bot. But once I find supporting documentation I will post it here.

- We can implement [Twitch’s IRC service](https://dev.twitch.tv/docs/irc/) with the help of [WebSockets](https://square.github.io/okhttp/4.x/okhttp/okhttp3/-web-socket/) provided to us by the engineering team at square.

- Technically the first thing we need to do to get the chat up and running is to authenticate our application and get a `user access token`. You can see how to do that through [my previous post](https://dev.to/theplebdev/lets-make-a-twitch-android-app-part-1-app-access-tokens-51pm/edit)

- Once you have a [user access token](https://dev.to/theplebdev/lets-make-a-twitch-android-app-part-1-app-access-tokens-51pm#grant), we can move on to implementing a Websocket and connecting to the twitch IRC servers. We need to create a new class and have it implement the [WebSocketListener](https://square.github.io/okhttp/4.x/okhttp/okhttp3/-web-socket-listener/) class.
- You can check out my github version [HERE](https://github.com/thePlebDev/Clicker/blob/bfa5dfc301766cd44a80ec3fb0ee4ceccdcaa84d/app/src/main/java/com/example/clicker/network/websockets/TwitchWebSocket.kt#L14), although with the current version you have to hard code your `user access token`.
But to get us started, it will look something like this:
```
class TwitchWebSocket(): WebSocketListener() {


    val webSocketURL = "wss://irc-ws.chat.twitch.tv:443"

    init {
        run()
    }

    private fun run() {
        val client: OkHttpClient = OkHttpClient.Builder()
            .readTimeout(1000,TimeUnit.MILLISECONDS)
            .writeTimeout(1000,TimeUnit.MILLISECONDS)
            .build()

        val request: Request = Request.Builder()
            .url(webSocketURL)
            .build()
        client.newWebSocket(request, this)

        // Trigger shutdown of the dispatcher's executor so this process can exit cleanly.
        client.dispatcher.executorService.shutdown()
    }
```
- Then we need to override the onOpen() method and [send the PASS and NICK messages](https://dev.twitch.tv/docs/irc/authenticate-bot/#sending-the-pass-and-nick-messages). Implemented like so:
```
override fun onOpen(webSocket: WebSocket, response: Response) {
        super.onOpen(webSocket, response)
        webSocket.send("CAP REQ :twitch.tv/tags twitch.tv/commands");
        //todo: add the User access tokens after oauth:
        webSocket.send("PASS oauth:yourToken");
        webSocket.send("NICK username");
    }

```
- Also, we need to log the results:
```
override fun onMessage(webSocket: WebSocket, text: String) {
        
        Log.d("websocketStoof","onMessage: ${text}")
    }

```
- Doing everything correctly should give you back a message of:

```
:tmi.twitch.tv 001 <user> :Welcome, YourUsername!
:tmi.twitch.tv 002 <user> :Your host is tmi.twitch.tv
:tmi.twitch.tv 003 <user> :This server is rather new
:tmi.twitch.tv 004 <user> :-
:tmi.twitch.tv 375 <user> :-
:tmi.twitch.tv 372 <user> :You are in a maze of twisty passages.
:tmi.twitch.tv 376 <user> :>
@badge-info=;badges=;color=;display-name=<user>;emote-sets=0,300374282;user-id=12345678;user-type= :tmi.twitch.tv GLOBALUSERSTATE

```

### What is next?  <a name="next"></a>
- Next tutorial I will clean up the code and we will walk through actually joining a chat room. We might even be able to send our first messages through our app!!!!!!!

### Conclusion
- Thank you for taking the time out of your day to read this blog post of mine. If you have any questions or concerns please comment below or reach out to me on [Twitter](https://twitter.com/AndroidTristan).