HoloLensでTwitter認証＆投稿

HoloLensからTwitter認証を行い投稿したい！ということで調査してみました。

現状、HoloLensのDirect3Dアプリはアプリ内でブラウザを使用することができません。ですので、OAuthの中でも「PIN認証」と呼ばれる方法を用いて認証を行います。

ちょうどAssetStoreに、「Let's Tweet In Unity」というPIN認証を行ってくれる無料のAssetがありますので、こちらを使用させていただきます。

https://assetstore.unity.com/packages/tools/integration/let-s-tweet-in-unity-536

### HashTableの修正

そのままではコンパイルが通らないので、下記ブログを参考にしてHashTableではなくDictionaryに置き換えました。

http://kazumalab.hatenablog.com/entry/2016/07/09/111722

```
var headers = new Hashtable();
headers["Authorization"] = GetHeaderWithAccessToken("POST", PostTweetURL, consumerKey, consumerSecret, response, parameters);
```

↓

```
var headers = new Dictionary<string, string>();
headers.Add("Authorization", GetHeaderWithAccessToken("POST", PostTweetURL, consumerKey, consumerSecret, response, parameters));
```

### UWP向けの修正

また、HoloLensあるあるですが、UWP向けにコンパイルが通らないので、ハッシュ化の部分を修正します。

http://kotatu99.hateblo.jp/entry/2017/07/12/221321

ヘッダーに追加

```
#if UNITY_WSA && !UNITY_EDITOR
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Security.Cryptography;
using Windows.Security.Cryptography.Core;
using Windows.Storage.Streams;
#endif
```

「// Generate the hash」の部分を書き換え（#ifとUWPのコードを追加）

```
#if UNITY_WSA && !UNITY_EDITOR
 var objMacProv = MacAlgorithmProvider.OpenAlgorithm("HMAC_SHA1");
 var messageBytes = Encoding.UTF8.GetBytes(signatureBaseString);
 var keyBytes = Encoding.UTF8.GetBytes(key);
 CryptographicKey hmacKey = objMacProv.CreateKey(keyBytes.AsBuffer());
 IBuffer buffHMAC = CryptographicEngine.Sign(hmacKey, messageBytes.AsBuffer());
 return Convert.ToBase64String(buffHMAC.ToArray());
#else
 // Generate the hash
 HMACSHA1 hmacsha1 = new HMACSHA1(Encoding.ASCII.GetBytes(key));
 byte[] signatureBytes = 
 hmacsha1.ComputeHash(Encoding.ASCII.GetBytes(signatureBaseString));
 return Convert.ToBase64String(signatureBytes);
#endif
```

### HoloLens向けUIの作成

Let's Tweet in UnityはOnGUIでUIが作られていますが、HoloLensはOnGUIが動作しないため、uGUIに作り変えます。

ここで問題となるのが、HoloLensでの文字入力の方法です。今回は、
 HoloToolKit-Examples > Input > Scenes > KeyboardTest
を複製して、UIオブジェクトを足す形で調整しました。
これでバーチャルキーボードや音声認識を使った文字入力が可能となります。

<img width="453" alt="ui.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/31032/8cec1af6-5709-92d7-0d51-d1e8b91611ad.png">

そして、Demo.csを下記のように書き換えます。
uGUIの中で行っているイベント処理を別の関数に書き出しています。

https://gist.github.com/torikasyu/d219417ec936746ae4c061b27b974e93


### Sceneのセットアップ

空のGameObjectを作成して、Demo.csをアタッチします。

![tempsnip.png](https://qiita-image-store.s3.amazonaws.com/0/31032/10348ca6-f7f0-1275-a727-04d99a0b54eb.png)

* CONSUMER_KEY / CONSUMER_SECRET
  * https://apps.twitter.com/ で取得した値
* Message / PIN Field / Tweet Field
  * それぞれ作成したUI部品を割り当て

#### uGUIボタンのイベント設定
* Get PINボタンのイベント
 * public void onClick_GetRequestToken()
* Enter PINボタンのイベント
 * public void onClick_EnterPIN()
* Tweetボタンのイベント
 * public void onClick_PostTweet()

 <img width="524" alt="button.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/31032/4ce741f5-7428-b3f3-b43c-221ecc622edf.png">

### コンパイル・実行

これで実行すると、「Get PIN」をAirTapした時に別ウインドウでEdgeが起動します。そこでTwitterにログインするとPINが表示されるので、再度作成したアプリに切り替えて、InputFieldのところに入力し、「Enter PIN」をAirTapします。認証が成功すると、Messageのところにアカウント名が入りますので、「Tweet」ボタンで投稿ができるようになります。

Edgeに切り替わってしまうのは利用者から見れば煩雑なフローなので、アプリ内ブラウザの登場が待たれるところですね。