UnityとPrime31 SocialNetworkingPluginでFacebookに投稿

UnityとPrime31 SocialNetworking PluginでFacebookに投稿するまでの手順をまとめます。
思ったよりも情報がまとまっていなかったので、助けになれば幸いです。

ここに書かれている内容は、2015年1月時点のもので、プラグインのバージョンはiOS版2.4、Android版2.2を基にしています。

# 準備

ダウンロードした.unitypackageファイルを、Unityでインポートしておきます。
また、Facebook Developersにアクセスし、Facebookアプリを作成して下さい。
[Facebook Developers](https://developers.facebook.com/)

# iOS版の手順

## Facebook Developersの設定

Settingsメニューで「Add Platform」を行い、iOS向けの設定シートを追加します。
設定シートには、最低限「Bundle ID」を指定して下さい。（検証不足なのですが、指定が無くても動くかもしれません）
また、AppIDとDisplay Nameは後で使いますので控えておいて下さい。

## info.plist の設定

Unityを起動し、Tools ＞ prime[31] ＞ info.plist additions... を選択します。

CFBundle URLSchemes を開き、「Size」を __1__ に、「Element 0」を __"fb" + AppID__ とします。（"fb01234567890" のようにする）
「Facebook App ID」には __AppID__ を入力します。こちらには "fb" は不要です。
「Facebook Display Name」には先程控えた __Display Name__ を入力します。間違いがないようにして下さい。
入力が終わったら、Save Changes ボタンで保存します。

## コードを記述

```SnsiOS.cs
// 初期化
FacebookBinding.init();

// ログイン
FacebookBinding.loginWithReadPermissions( new string[] { "public_profile" } );

// 投稿
var parameters = new Dictionary<string, object> {
	{ "link", url },
	{ "description", description },
	{ "display", "touch" }		// for mobile
};
FacebookBinding.showFacebookShareDialog(parameters);
```

`FacebookBinding.init()` は初期化です。見たままですね。

`FacebookBinding.loginWithReadPermissions(...)` は、Facebookにログインを促すと共に、指定したパーミッションの承認をユーザーに促します。
public_profile を指定しているのは、Facebook Developersで作ったばかりのアプリでは、使用出来るパーミッションが email / public_profile / user_friends しか無いからです。どうせ使わないのでは？と思われるかもしれませんが、ログインを行わないと問題が出る箇所があります。こちらは後述します。

`FacebookBinding.showFacebookShareDialog(...)` はFacebookのタイムラインへ投稿するためのダイアログを表示します。"link" はシェアするURL、"description" はその概要、 "display" はダイアログの表示形式で、モバイル端末向けなら "touch" を指定することがお約束のようです。
他にも指定出来るパラメータはありますが、詳しくはFacebookのページを参照して下さい。

他にも、よく使うのは以下の関数です。

```SnsiOS.cs
// ログイン中かどうかをチェック
FacebookBinding.isSessionValid();

// ログアウトする
FacebookBinding.logout();
```

# Android版の手順

## Facebook Developersの設定

Settingsメニューで「Add Platform」を行い、Android向けの設定シートを追加します。
「Google Play Package Name」には__パッケージ名__を指定します。
「Class Name」には __"com.prime31.UnityPlayerProxyActivity"__ と指定します。
「Key Hashes」には __Keystore から出力したキーハッシュ__を指定します。詳しくはPrime31の公式ページを。
また、AppIDは後で使いますので控えておいて下さい。

## AndroidManifest の設定

以下の内容を追加して下さい。

> \<meta-data android:name="com.facebook.sdk.ApplicationId" android:value="\ 99999999999"/>
> \<meta-data android:name="com.prime31.FacebookPlugin" android:value="UnityPlayerActivityProxy"/>

99999999999 の部分にはAppIDを入れて下さい。
また、起動するアクティビティを __"com.prime31.UnityPlayerNativeActivity"__ として下さい。

## コードを記述

クラスが違うだけで、iOSと同じです。
詳しくはiOSの説明をご覧下さい。

```SnsAndroid.cs
// 初期化
FacebookAndroid.init();

// ログイン
FacebookAndroid.loginWithReadPermissions( new string[] { "public_profile" } );

// 投稿
var parameters = new Dictionary<string, object> {
	{ "link", url },
	{ "description", description },
	{ "display", "touch" }		// for mobile
};
FacebookAndroid.showFacebookShareDialog(parameters);
```

```SnsAndroid.cs
// ログイン中かどうかをチェック
FacebookAndroid.isSessionValid();

// ログアウトする
FacebookAndroid.logout();
```

# ハマりどころ

こう見ると簡単そうに見えますが、かなり多くのハマりどころがあります。

## ログイン出来ない

> アプリが設定されていません。
> このアプリでのFacebookログインの仕様はまだアプリ開発者により設定されていません。

というようなエラーが出た場合、Facebook Developersで公開設定をON（少し前の表現だと、SandboxをOFF）にする必要があります。
Facebook Developers で、Status & Review のページにある「Do you want to make this app and all its live features available to the general public?」のボタンをYESにして下さい。

また、Display Nameが info.plist の内容と Facebook Developers の内容とで違っている（iOS版のみ）と、ログイン出来ない可能性があります。

## ログインの結果を受け取りたい

ログインに成功したかどうかは、以下のコールバックを設定することで受け取ることが出来ます。

```FacebookManager.cs
// セッションオープン時（ログイン成功時）
public static event Action sessionOpenedEvent;

// ログイン失敗時
public static event Action<P31Error> loginFailedEvent;
```

## 投稿の結果を受け取りたい

投稿が成功したかどうかは、以下のコールバックを設定することで受け取ることが出来ます。

```FacebookManager.cs
// カスタムダイアログでの投稿成功
public static event Action<string> dialogCompletedWithUrlEvent;

// カスタムダイアログでの投稿失敗
public static event Action<P31Error> dialogFailedEvent;

// シェアダイアログでの投稿失敗
public static event Action<P31Error> shareDialogFailedEvent;

// シェアダイアログでの投稿成功
public static event Action<Dictionary<string,object>> shareDialogSucceededEvent;
```

ここで注意して欲しいのは、起動したダイアログのタイプによって、返されるコールバックが異なるという点です。
例えばiOSでは、Facebookアプリがインストールされていると `shareDialogFailedEvent` / `shareDialogSucceededEvent` で返りますが、インストールされていないと `dialogCompletedWithUrlEvent` / `dialogFailedEvent` で返ります。

また、投稿成功時に返される内容も異なります。
`shareDialogSucceededEvent` では "com.facebook.platform.extra.DID_COMPLETE" と "com.facebook.platform.extra.COMPLETION_GESTURE" が返されますが、`dialogCompletedWithUrlEvent` では "fbconnect://success" が返されます。（2015年1月現在）

更に、ログインをしていないと、com.facebook.platform.extra.COMPLETION_GESTURE が返されないことがあります。投稿が成功したかどうかはこの内容でしか判断出来ません。（前述の、ログインしないと問題が出る、と言った点はここです）

## 投稿に失敗する

* 前述の、Facebook公開設定がNOになっている
* 投稿しようとしたURLに特殊な文字が含まれている（例えば "#" はダメなようです）
* Display Nameが info.plist の内容と Facebook Developers の内容とで違っている（iOS版のみ）

## Android版でダイアログが表示されない

Facebookのアプリをインストールしていない場合、ダイアログはPrime31側が表示しますが、Unityの起動Activityが "com.prime31.UnityPlayerNativeActivity" でない場合、ダイアログが表示されません。

自前のActivityを使いつつ、Facebookアプリがインストールされていない時にダイアログを出す方法は見つけられませんでした。

## Android版でダイアログのコールバックが返ってこない

Facebookのアプリをインストールしている場合、ダイアログのクローズ時にコールバックが返って来ますが、Unityの起動Activityが "com.prime31.UnityPlayerNativeActivity" でない場合、コールバックが返って来ません。

どうしても自前のActivityで起動する必要がある場合、以下のような方法があります。
[Unity Android Activity Sharing](https://gist.github.com/prime31/0908e6100d7e228f1add)

自前Activityで発生したイベントをPrime31の方に横流しする、という手法です。
Activity側で

```MyActivity.java
// Prime31のFacebookプラグインを取得しておく
try {
	facebookPlugin = Class.forName("com.prime31.FacebookPlugin");
} catch (Exception e) {
	Log.e("MyActivity", "Error : " + e);
}
```

としておき、

```MyActivity.java
public void onActivityResult(int requestCode, int resultCode, Intent data) {
	// 中略
	
    if (facebookPlugin != null) {
	    try {
	    	Method	methodOnActivityResult = facebookPlugin.getDeclaredMethod("onActivityResult", new Class[] { int.class, int.class, Intent.class });
	    	if (methodOnActivityResult != null) {
	    		methodOnActivityResult.invoke(facebookPlugin, requestCode, resultCode, data);
	    	}
	    } catch (Exception e) {
	    	Log.e("MyActivity", "Error : " + e);
	    }
    }
}
```

というように FacebookPlugin に onActivityResult の結果を横流ししてあげればコールバックか返ってくるようになります。