【追記アリ】Androidのツイキャスクライアントを作る

この記事は [第2のドワンゴ Advent Calendar 2016](http://qiita.com/advent-calendar/2016/dwango2) の14日目の記事です。
[2日目のyonexさんの記事](http://qiita.com/yonex/items/dd8f815f54a27f8c8690)にもある通り、ツイキャスの新しいAPIが公開されていたためこれを利用した簡単なAndroidアプリを作ろうと思います。

## はじめに
今回新しいAPIが公開されましたが、古いAPIであれば使ったことがありました。
ちなみに古いAPIを使ったアプリはGooglePlayで公開しています。
[Casee-シンプルなツイキャス視聴アプリ](https://play.google.com/store/apps/details?id=com.kuwapp.casviewer)

1年以上前に作ったのですが、当時私は転職活動をしていましたがAndroidの業務経験がなくAndroidエンジニアとして転職するなら実際にアプリ作って見せるしかないと考えて作ったもので非常に思い入れがあります。
内部的には1000行近いActivityや各所で変更されるインスタンス変数の山、再利用性のないコードなどいろいろとひどいのですが、新しいAPIが公開されたのでこれを機に作り直そうと思い開発中です。
そのついでに他にも作りたい人がいれば助けになるかなーと思い軽いサンプルを作ったので記事にします。

## 作るもの

アクセストークンを取得しておすすめリストを表示し放送が見れるところまで実現できるものです。
サンプルはGitHubにあげてます。
https://github.com/YusukeHata/TwitcastingViewerAndroidSample

ちなみにエラーハンドリングしてなかったりOkHttpのインスタンス都度生成してたりViewHolderパターン使ってなかったりいろいろと問題はありますが、あくまで簡易サンプルとしての位置づけなのでそこは許容してもらえると・・・。
(本当はRxゴリゴリ使いたい)

## 事前準備

事前に[ここ](https://ssl.twitcasting.tv/developer.php)からアプリケーションを登録し以下3つを取得しておきます。
ちなみにCallbackURLは`http`か`https`ではじめる必要があります。

* ClientID
* ClientSecret
* CallbackURL

[yonexさんの記事が詳しいです](http://qiita.com/yonex/items/dd8f815f54a27f8c8690#%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%AE%E7%99%BB%E9%8C%B2)

## アクセストークンを取得する

各種APIを使用するためにはAccessTokenが必要なので取得してみます。
流れとしては以下です。

1. 外部ブラウザ上でツイキャスにログインして連携アプリを許可する
1. CallbackUrlにAccessToken取得に必要なパラメーターが付与されてリダイレクトされるので暗黙的Intentでアプリに戻す
1. アプリでパラメーターを取得してAPIを叩きAccessTokenを取得する

[ドキュメントはここ](http://apiv2-doc.twitcasting.tv/#step1)

### 外部ブラウザを立ち上げる

```java
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_oauth);
    openBrowser();
}

private static final String OAUTH_URL = "https://apiv2.twitcasting.tv/oauth2/authorize?client_id=%s&response_type=code";

// 外部ブラウザでツイキャスにログインして連携アプリを許可するため、暗黙的Intentで外部ブラウザを立ち上げる
private void openBrowser() {
    Uri uri = Uri.parse(String.format(OAUTH_URL, BuildConfig.CLIENT_ID));
    Intent intent = new Intent(Intent.ACTION_VIEW, uri);
    startActivity(intent);
}
```

### 連携アプリを許可後に外部ブラウザから戻ってこれるようにする

連携アプリを許可するとアプリケーション登録時に指定したCallbackURLにリダイレクトされるので暗黙的Intentでアプリが起動するようにAndroidManifestのacitivty要素内に以下を例に設定を追加しておきます。
ちなみに以下はCallbackUrlが`https://kuwapp.com/callback`のときの例です。

```xml
<activity
    android:name=".OAuthActivity"
    android:launchMode="singleTask">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.LAUNCHER" />

        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />

        <data
            android:host="kuwapp.com"
            android:path="/callback"
            android:scheme="https" />

    </intent-filter>
</activity>
```

### パラメーターを取り出し、AccessTokenを取得するAPIを叩く

Activityの`onNewIntent`内でintentからクエリパラメーターを取り出します。

```java
@Override
protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);
    String code = intent.getData().getQueryParameter("code");
}
```

取り出したcodeを使ってAPIを叩きます。以下はOkHttpを使った例です。
[ドキュメントだとここ](http://apiv2-doc.twitcasting.tv/#step2)

```java
@Override
protected void onNewIntent(Intent intent) {
    // いろいろ略
    fetchAccessToken(code, new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                // TODO
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                // レスポンスをパースしてaccessTokenを得る
                AccessToken accessToken = new Gson().fromJson(response.body().charStream(), AccessToken.class);
                String accessToken = accessToken.getAccessToken()
            }
        });
}

private void fetchAccessToken(String code, Callback callback) {
    Request request = new Request.Builder()
            .post(createRequestBody(code))
            .url("https://apiv2.twitcasting.tv/oauth2/access_token")
            .build();
    OkHttpClient client = new OkHttpClient.Builder().build();
    client.newCall(request).enqueue(callback);
}

private RequestBody createRequestBody(String code) {
    return new FormBody.Builder()
            .add("code", code)
            .add("grant_type", "authorization_code")
            .add("client_id", BuildConfig.CLIENT_ID)
            .add("client_secret", BuildConfig.CLIENT_SECRET)
            .add("redirect_uri", BuildConfig.CALLBACK_URL)
            .build();
}
```

これでAccessTokenを取得できたので他のAPIを叩く準備ができました。
[このActivityの全体像はここ](https://github.com/YusukeHata/TwitcastingViewerAndroidSample/blob/master/app/src/main/java/com/kuwapp/twitcastingviewersample/OAuthActivity.java)

## おすすめリストを取得する

AccessTokenが取得できたのでおすすめリストを取得してみます。
[Search Live Movies API](http://apiv2-doc.twitcasting.tv/#search-live-movies)のクエリパラメーターにrecommendと指定するものがあるのでこれでいけそうです。
APIを叩くには以下のようにAccessTokenとAPIのバージョンをリクエストヘッダに指定する必要があります。
(ちなみにAccessTokenでなくともClientIDとClientSecretをBase64エンコードしたものでも可能です)

```java
private void fetchRecommendList(Callback callback) {
    String accessToken = getIntent().getStringExtra(KEY_ACCESS_TOKEN);
    Request request = new Request.Builder()
            .get()
            .url("https://apiv2.twitcasting.tv/search/lives?type=recommend&lang=ja")
            // AccessTokenとAPIバージョンをリクエストヘッダに指定
            .addHeader("Authorization", "Bearer " + accessToken)
            .addHeader("X-Api-Version", BuildConfig.API_VERSION)
            .build();
    OkHttpClient client = new OkHttpClient.Builder().build();
    client.newCall(request).enqueue(callback);
}
```

これでAPIを叩いた後にresponseをパースしてMovieオブジェクトをListViewやRecyclerView等で表示すればおすすめリストが表示できます。
サンプルではおすすめリストをListViewに表示しています。
[このActivityの全体像](https://github.com/YusukeHata/TwitcastingViewerAndroidSample/blob/master/app/src/main/java/com/kuwapp/twitcastingviewersample/RecommendListActivity.java)

## 放送を見れるようにする

おすすめリストを取得するときに叩いたAPIでMovieオブジェクトがとれるのでそこに含まれるHLS URLを使用します。
VideoView等を使っておけば再生は容易です。(今ならExoPlayer使う方が良いかも。)

```java
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_live);
    String hlsUrl = getIntent().getStringExtra(KEY_HLS);
    VideoView videoView = (VideoView) findViewById(R.id.video_view);
    videoView.setVideoPath(hlsUrl);
    videoView.setOnPreparedListener(mediaPlayer -> mediaPlayer.start());
}
```

[このActivityの全体像](https://github.com/YusukeHata/TwitcastingViewerAndroidSample/blob/master/app/src/main/java/com/kuwapp/twitcastingviewersample/LiveActivity.java)

これらを応用して他のAPIも使うとリッチなアプリが作れると思います。

## サンプルアプリについて

ClientID、ClientSecretを公開できない都合上、これらの情報はlocal.propertiesから読み込むようにしています。
そのため、サンプルアプリを実行するにはlocal.propertiesに以下の追加が必要です。(中括弧不要)

```:local.properties
client_id={自身のClientId}
client_secret={自身のClientSecret}
callback_url={自身のCallbackURL}
```

[サンプルアプリ](https://github.com/YusukeHata/TwitcastingViewerAndroidSample)


## ツイキャスAPIに希望すること

**~~Comments APIでUserオブジェクトを返してほしい~~**

※ Userオブジェクト返るようになりました。

[コメントを取得するAPI](http://apiv2-doc.twitcasting.tv/#comments)でUserオブジェクトが返らないのでユーザーのサムネが表示できず、とても寂しい見た目になります。
[User Info API](http://apiv2-doc.twitcasting.tv/#user-info)を叩けばサムネをとれなくはないですが、APIを叩く数は極力減らしたいですしコメントが多いとすぐにAPI制限に達してしまいそうです。

**~~Callback URLのschemeがhttp、https以外も指定できるようにしてほしい~~**

※ schemeにhttp,https以外も指定できるようになっていました

外部ブラウザからアプリに戻す際にCallbackURLがhttp等ではじまっていると関係のないアプリも暗黙的Intentの候補として表示されてしまいます。
全く関係がないアプリが表示されてしまうのはユーザーにとってただのノイズなため、早急な対応が望まれます。

**~~サポーター追加と取得、コメントの投稿APIが待たれる~~**

※ サポータ追加、取得、コメント投稿API追加されてました

いずれ追加されるとは思いますが、これらのAPIがあればやれることが非常に広がるので追加が楽しみです。
（以前作ったアプリでは独自にお気に入りユーザー機能を追加していた）

## 終わりに
今回ツイキャスの新しいAPIを使ってみましたが、以前のAPIと比べるとカテゴリが取得できたりライブを検索できたりやれることはかなり増えていました。今後も新たなAPIが提供されていくと思うので非常に楽しみです。
ちなみに今回のサンプルとは別の現在開発しているものはKotlinやRxを使ってゴリゴリ書いてます。
MaterialDesignもふんだんに取り入れる予定です。[リポジトリはここ](https://github.com/YusukeHata/TwitcastingViewer-v2)

## 参考
[Twitcasting API Documentation](http://apiv2-doc.twitcasting.tv/#introduction)

## 追記

**2016/12/17**
APIが追加されてコメントの投稿ができるようになっていました。
http://apiv2-doc.twitcasting.tv/#post-comment

コメント取得APIでUserオブジェクトが返るようになり、サムネイルが表示できるようになっていました。
http://apiv2-doc.twitcasting.tv/#get-comments

**2016/02/10**

サポーター関連のAPIが追加されていました。
http://apiv2-doc.twitcasting.tv/#supporter

CallbackURLにカスタムスキーマが使えるようになっていました
https://ssl.twitcasting.tv/developernewapp.php