AndroidでWebRTCを使った電話アプリを実装する【初心者向け】


AndroidでWebRTCを使った電話アプリの実装方法を解説します。
WebRTCは、リアルタイムでビデオチャットやボイスチャット、テキストなどのデータ送信ができるものです。

今回は、ボイスチャットを実装していきます。ビデオは取り扱いません。

WebRTCを１から実装しようとすると大変なので、今回は、[SkyWay](https://nttcom.github.io/skyway/)というサービスとそのSDKを利用します。
このSDKを使うと、AndroidのみならずiOSやweb(javascript)でWebRTCを簡単に利用するアプリを作ることができます。

また、初心者向けを意識しているので、外部ライブラリは、SkyWaySDK以外は導入せずに実装していきます。
「動いた、すげぇ」を重視しているので、エラーハンドリングや`onPause`などのライフサイクルでの適切な処理は省いています。

実装したソースコードは、[こちらに](https://github.com/kawasin73/WebRTCPhoneSample)あります。

## はじめに

念のため今回開発を行った環境を。

- macOS : 10.11.6 (El Capitan)
- Android Studio : 2.3.3
- 使ったエミュレータ : API24

多少違っても問題ないです。

## 1. アプリのひな形を作る

わかってる人は次の[下準備](#2-下準備skywayの導入)まで飛ばしてください。

1. `Start a new Android Studio project` をクリック

![Welcome_to_Android_Studio_と_1__bash.png](https://qiita-image-store.s3.amazonaws.com/0/65193/5f686f81-601b-14f7-e079-78305b1fc3b5.png "Welcome_to_Android_Studio_と_1__bash.png")
2. アプリ名と、ドメイン(それぞれ適当でいいです)を入力して`Next`

![Create_New_Project_と_Welcome_to_Android_Studio.png](https://qiita-image-store.s3.amazonaws.com/0/65193/378d5102-003d-3f2b-5df1-f255a941e958.png "Create_New_Project_と_Welcome_to_Android_Studio.png")
3. MinimumSDKは適当に決めて`Next`(今回は、API version19にしてみました)

![Create_New_Project_と_Welcome_to_Android_Studio.png](https://qiita-image-store.s3.amazonaws.com/0/65193/89af9e79-77fb-7446-fcc3-0024ed4e6f3e.png "Create_New_Project_と_Welcome_to_Android_Studio.png")
4. [Empty Activity]が選択されていることを確認して、`Next`

![Create_New_Project.png](https://qiita-image-store.s3.amazonaws.com/0/65193/7af5631e-8a51-62f0-10fb-f5cbd0b23ab0.png "Create_New_Project.png")
5. Activity Name はデフォルトの`MainActivity`のままで、`Finish`

![Create_New_Project_と_Welcome_to_Android_Studio.png](https://qiita-image-store.s3.amazonaws.com/0/65193/b1898e1c-cc83-9d04-d96e-e5f7b7b92ead.png "Create_New_Project_と_Welcome_to_Android_Studio.png")

これで、プロジェクトが作成されます。

## 2. 下準備(SkyWayの導入)

1. まず、プロジェクト一覧画面を変更します。画像の矢印のボタンをクリックして、「Project」をクリックしてください。

![MainActivity_java_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample__と_AndroidでWebRTCを使った電話アプリを実装する【初心者向け】.png](https://qiita-image-store.s3.amazonaws.com/0/65193/dabcbb5a-9903-4471-6d07-57315f3cae13.png "MainActivity_java_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample__と_AndroidでWebRTCを使った電話アプリを実装する【初心者向け】.png")
2. デフォルトの「Android」のナビゲーション画面はわかりにくいので、以降「Project」のナビゲーション画面を使います。これは、実際のファイル・フォルダ構造と同じように表示されるのでわかりやすいです。画像のターゲットアイコンを押すと、右側のエディタで開いているファイルの場所を表示してくれます。

![MainActivity_java_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample__と_AndroidでWebRTCを使った電話アプリを実装する【初心者向け】_と_1__bash.png](https://qiita-image-store.s3.amazonaws.com/0/65193/98679b73-31d3-3612-eebf-d5c197a37896.png "MainActivity_java_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample__と_AndroidでWebRTCを使った電話アプリを実装する【初心者向け】_と_1__bash.png")
3. [SkyWayのAndroidのSDK](https://github.com/nttcom/SkyWay-Android-SDK/releases/tag/v0.2.2)をダウンロードし(Source code (zip)をクリック)、解凍して、`SkyWay.aar`をプロジェクトの`app/libs`ディレクトリにコピーしてください。

![MainActivity_java_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample__と_SkyWay-Android-SDK-0_2_2.png](https://qiita-image-store.s3.amazonaws.com/0/65193/884278d0-4d24-13cc-88f9-45adcc0e691f.png "MainActivity_java_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample__と_SkyWay-Android-SDK-0_2_2.png")
4. `SkyWay.aar`をプロジェクトに取り込むために、`app/build.gradle`を編集します。追記が終わったら`Sync Now`を押してください。aarファイルが読み込まれます。

``` gradle:app/build.gradle
dependencies {
    compile(name: 'SkyWay', ext: 'aar') // <- この行
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:24.2.1'
    compile 'com.android.support.constraint:constraint-layout:1.0.2'
    testCompile 'junit:junit:4.12'
}
// この下の行も
repositories {
    flatDir {
        dirs 'libs'
    }
}
```

![app_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample__と_Import_SkyWay_aar_·_kawasin73_webrtc_sample_android_8318afb.png](https://qiita-image-store.s3.amazonaws.com/0/65193/d665a768-37d2-29b5-9e5e-1a0892e9fce7.png "app_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample__と_Import_SkyWay_aar_·_kawasin73_webrtc_sample_android_8318afb.png")
5. 次に、[SkyWayに会員登録してください](https://skyway.io/ds/registration/)。利用するドメインは、ひとまずは、`localhost`としてください。
6. この後表示される「APIキー」と「利用可能ドメイン」はこの後利用するので注意しておいてください
7. `app`ディレクトリを右クリックして、「New」->「File」で、`gradle.properties`ファイルを作成して、先ほど表示された、「APIキー」と「利用可能なドメイン」を入力してください。

```app/gradle.properties
skywayApiKey=xxxxxxxxxxxxxxxxxxxxxxxxxxxx
skywayHost=localhost
```

![gradle_properties_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample__と_Add_API_KEY_·_kawasin73_webrtc_sample_android_6fac224_と_1__bash.png](https://qiita-image-store.s3.amazonaws.com/0/65193/5462bd78-797f-7830-b8a3-9d2293d03911.png "gradle_properties_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample__と_Add_API_KEY_·_kawasin73_webrtc_sample_android_6fac224_と_1__bash.png")
8. `app/.gitignore`に`gradle.properties`を追記してください。これにより、ソースコードをgithubに公開した時に、`app/gradle.properties`は公開されないため、APIキーを他者に知られずにソースコードを公開できます。

![_gitignore_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample__と_AndroidでWebRTCを使った電話アプリを実装する【初心者向け】_と_Add_API_KEY_·_kawasin73_webrtc_sample_android_6fac224_と_1__bash.png](https://qiita-image-store.s3.amazonaws.com/0/65193/1843ef62-666d-bc12-8c05-db11e91e5e4f.png "_gitignore_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample__と_AndroidでWebRTCを使った電話アプリを実装する【初心者向け】_と_Add_API_KEY_·_kawasin73_webrtc_sample_android_6fac224_と_1__bash.png")
9. `app/build.gradle`に以下の追記をします。これにより、javaのコードから、`BuildConfig. SKYWAY_API_KEY` と `BuildConfig. SKYWAY_HOST`でアクセスできるようになります。追記が終わったら、`Sync Now` を押してください。

```gradle:app/build.gradle
    buildTypes {
        debug {
            buildConfigField "String", "SKYWAY_API_KEY", "\"${project.property("skywayApiKey")}\""
            buildConfigField "String", "SKYWAY_HOST", "\"${project.property("skywayHost")}\""
        }
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            buildConfigField "String", "SKYWAY_API_KEY", "\"${project.property("skywayApiKey")}\""
            buildConfigField "String", "SKYWAY_HOST", "\"${project.property("skywayHost")}\""
        }
    }
```

![AndroidでWebRTCを使った電話アプリを実装する【初心者向け】_と_app_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample__と_app_-_WebRTCSample_-____AndroidStudioProjects_WebRTCSample_.png](https://qiita-image-store.s3.amazonaws.com/0/65193/d94c45f6-9b31-b467-d25c-1f6fa26ef651.png "AndroidでWebRTCを使った電話アプリを実装する【初心者向け】_と_app_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample__と_app_-_WebRTCSample_-____AndroidStudioProjects_WebRTCSample_.png")
10. `AndroidManifest.xml`を編集します。これは、Androidアプリが実行されるときに必要な権限を宣言するものです。

```xml:app/src/main/AndroidManifest.xml
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
```

![AndroidManifest_xml_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample__と_1__bash.png](https://qiita-image-store.s3.amazonaws.com/0/65193/6dc228cf-408b-06ae-cfda-74fe6f929e9f.png "AndroidManifest_xml_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample__と_1__bash.png")
11. 以上でSkyWayを使うための下準備は完了です。

## 3. 自分のpeerIDを表示する

まず、自分の`PeerId`を表示する部分の実装を進めていきます。SkyWayによって自動で割り当てられる`PeerId`を確認することができるようになります。

ここでは、

- `Peer` クラスインスタンスの作成
- `Log`でログを出す
- TextViewに動的に値をセットする

を行っていきます。

1. まず、`Peer`クラスのインスタンスを作成します。これが作成されることで通信ができるようになります。以下のコードを入力してください。「Select Classes to Import」が出てきたら`OK`を押してください。

```java:MainActivity.java
// MainActivityのすぐ下
private Peer peer;

// onCreate の中
PeerOption options = new PeerOption();
options.key = BuildConfig.SKYWAY_API_KEY;
options.domain = BuildConfig.SKYWAY_HOST;
peer = new Peer(this, options);
Navigator.initialize(peer);
```

![MainActivity_java_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample_.png](https://qiita-image-store.s3.amazonaws.com/0/65193/e56d9054-aa7a-a178-a2b3-1f4f2f57b142.png "MainActivity_java_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample_.png")

![Select_Classes_to_Import_と_MainActivity_java_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample__と_MainActivity_java_-_WebRTCSample_-____AndroidStudioProjects_WebRTCSample_.png](https://qiita-image-store.s3.amazonaws.com/0/65193/b93337d4-c182-28b0-51b8-a858bd2ec188.png "Select_Classes_to_Import_と_MainActivity_java_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample__と_MainActivity_java_-_WebRTCSample_-____AndroidStudioProjects_WebRTCSample_.png")
2. 次に、自分の`PeerId`を取得して、Logに出力してみましょう。

```java:MainActivity.java
// MainActivityの下
private String TAG = getClass().getSimpleName();
private String currentId;

// onCreateの中
showCurrentPeerId();

// onCreateの下
    private void showCurrentPeerId() {
        peer.on(Peer.PeerEventEnum.OPEN, new OnCallback() {
            @Override
            public void onCallback(Object o) {
                if (o instanceof String) {
                    currentId = (String) o;
                    Log.d(TAG, "currentId: " + currentId);
                }
            }
        });
    }
```

![MainActivity_java_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample_.png](https://qiita-image-store.s3.amazonaws.com/0/65193/562d5f64-7941-ed5c-2acf-7c3106b51dba.png "MainActivity_java_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample_.png")
3. この状態で実際にアプリを起動してみましょう。画面上部の緑色の三角ボタンが実行ボタンです。動かす機体を選んで実行しましょう。ソースコードのコンパイルやアプリのインストールに時間がかかりますが、画面下に実行状況が表示されるので待ちましょう。

![Select_Deployment_Target_と_MainActivity_java_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample__と_AndroidでWebRTCを使った電話アプリを実装する【初心者向け】_と_MainActivity_java_-_WebRTCSample_-____AndroidStudioProjects_WebRTCSample_.png](https://qiita-image-store.s3.amazonaws.com/0/65193/4839d2f7-9a25-f1c9-b930-d41aa19c49ef.png "Select_Deployment_Target_と_MainActivity_java_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample__と_AndroidでWebRTCを使った電話アプリを実装する【初心者向け】_と_MainActivity_java_-_WebRTCSample_-____AndroidStudioProjects_WebRTCSample_.png")
4. 起動されたら、ログを確認しましょう。画面下の「Android Monitor」をクリックするとログ画面が開きます。開かない場合は、「logcat」を選択するとログが流れてきます。すると、確かに、ログで自分の、`PeerId`が表示されているのが確認できます。

![MainActivity_java_-_WebRTCSample_-____AndroidStudioProjects_WebRTCSample__と_MainActivity_java_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample__と_AndroidでWebRTCを使った電話アプリを実装する【初心者向け】.png](https://qiita-image-store.s3.amazonaws.com/0/65193/d074c533-5808-bcf6-a8ba-2dd3a985566c.png "MainActivity_java_-_WebRTCSample_-____AndroidStudioProjects_WebRTCSample__と_MainActivity_java_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample__と_AndroidでWebRTCを使った電話アプリを実装する【初心者向け】.png")
5. 次に、TextViewをjavaから操作するために、TextViewにidをつけます。idの追加などは、GUI画面で設定もできますが、今後のことを考えて、CUI画面で設定していきます。画面下の`Text`タブをクリックすると、CUI画面に切り替えることができます。`Hello World!`とあるデフォルトのTextViewにidを追記してください。

```activity_main.xml
android:id="@+id/id_textview"
```

![MainActivity_java_-_WebRTCSample_-____AndroidStudioProjects_WebRTCSample__と_activity_main_xml_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample__と_LoginActivity_java_-_collegino_android_-____AndroidStudioProjects_collegino_an.png](https://qiita-image-store.s3.amazonaws.com/0/65193/654f7b8c-9d23-7df4-de75-50aee964009d.png "MainActivity_java_-_WebRTCSample_-____AndroidStudioProjects_WebRTCSample__と_activity_main_xml_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample__と_LoginActivity_java_-_collegino_android_-____AndroidStudioProjects_collegino_an.png")
6. 次に、MainActivityから、`PeerId`を表示します。ここまで出来たらもう一度アプリを実行してみてください。Idが表示されているはずです。

```java:MainActivity.java
// MainActivity の下に
private TextView idTextView;

// onCreate の中に
idTextView = (TextView) findViewById(R.id.id_textview);

// showCurrentPeerId の中に
runOnUiThread(new Runnable() {
    @Override
    public void run() {
        idTextView.setText("ID: " + currentId);
    }
});
```

![Android_Emulator_-_Nexus_5X_API_24_5554_と_MainActivity_java_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample_.png](https://qiita-image-store.s3.amazonaws.com/0/65193/9553a155-4412-40d9-528f-29b7d60850f3.png "Android_Emulator_-_Nexus_5X_API_24_5554_と_MainActivity_java_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample_.png")


## 4. 通話できるユーザーの一覧を表示する

通話が出来るユーザーの一覧を表示していきます。
ここでは、

- ListViewでの表示

を行っていきます。

1. まず、ListViewのダミーデータを画面に表示するところから始めていきます。`activity_main.xml`に`LinearLayout`と`ListView`を追加し、`TextView`を、`LinearLayout`の中に移動させます。

```xml:activity_main.xml
    <LinearLayout
        android:id="@+id/head_layout"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <TextView
            android:id="@+id/id_textview"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello World!" />
    </LinearLayout>

    <ListView
        android:id="@+id/listview"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintTop_toBottomOf="@+id/head_layout"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent" />
```


![activity_main_xml_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample_.png](https://qiita-image-store.s3.amazonaws.com/0/65193/9858568b-d2ef-40c4-8e77-7876f6ecef5f.png "activity_main_xml_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample_.png")
2. 次に、ListViewの中の１行１行のViewを作成します。`app/src/res/layout`の中に、`Layout resource file`として`list_item.xml`を作成してください。`root`は、`LinearLayout`で大丈夫です。そして、`TextView`を作成してください。

```xml:list_item.xml
    <TextView
        android:id="@+id/item_textview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="20dp"/>
```

![list_item_xml_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample_.png](https://qiita-image-store.s3.amazonaws.com/0/65193/a93e360e-4854-0e11-0c4c-0de037de9c99.png "list_item_xml_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample_.png")
3. 次に、MainActivityにListViewのロジックを作成していきます。以下を追記して、実行するとListViewが表示されていると思います。書く順番としては、内部クラスである`MyAdapter`を最初に書くとエラーが出ずにスムースに書けます。

```java:MainActivity.java
// MainActivity の最終行の一つ上に
    private class MyAdapter extends ArrayAdapter<String> {
        private LayoutInflater inflater;

        MyAdapter(@NonNull Context context, @LayoutRes int resource, @NonNull List<String> objects) {
            super(context, resource, objects);
            inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        }

        @NonNull
        @Override
        public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
            View view = inflater.inflate(R.layout.list_item, null, false);
            TextView textView = (TextView) view.findViewById(R.id.item_textview);
            String name = getItem(position);
            textView.setText(name);
            return view;
        }
    }

// MainActivityの下に
    private ListView listView;
    private MyAdapter adapter;
    private List<String> idList = new ArrayList<String>();
    
// onCreateの中に
        listView = (ListView) findViewById(R.id.listview);

        adapter = new MyAdapter(this, 0, idList);
        listView.setAdapter(adapter);

        idList.clear();
        idList.add("Hello1");
        idList.add("Hello2");
        idList.add("Hello3");
        idList.add("Hello4");
        adapter.notifyDataSetChanged();

```

![MainActivity_java_-_WebRTCSample_-____AndroidStudioProjects_WebRTCSample_.png](https://qiita-image-store.s3.amazonaws.com/0/65193/b247412f-0ffa-8027-da53-84e586129956.png "MainActivity_java_-_WebRTCSample_-____AndroidStudioProjects_WebRTCSample_.png")


![Android_Emulator_-_Nexus_5X_API_24_5554_と_MainActivity_java_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample_.png](https://qiita-image-store.s3.amazonaws.com/0/65193/eccbbbbf-9df5-25ce-7b8c-6ebec87cc09c.png "Android_Emulator_-_Nexus_5X_API_24_5554_と_MainActivity_java_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample_.png")
4. 最後にダミーデータの代わりに、peerIdを表示させます。実行してみると、自分のidだけがListViewに表示されます。もし、複数端末で同時に起動すると複数のidが表示されます。

```java:MainActivity.java
// onCreate内の以下を削除
        idList.clear();
        idList.add("Hello1");
        idList.add("Hello2");
        idList.add("Hello3");
        idList.add("Hello4");
        adapter.notifyDataSetChanged();

// showCurrentPeerId の下に追記
    private void refreshPeerList() {
        Log.d(TAG, "Refreshing");
        peer.listAllPeers(new OnCallback() {
            @Override
            public void onCallback(Object o) {
                if (o instanceof JSONArray) {
                    JSONArray array = (JSONArray) o;
                    idList.clear();
                    for (int i = 0; i < array.length(); i++) {
                        try {
                            String id = array.getString(i);
                            idList.add(id);
                            Log.d(TAG, "Fetched PeerId: " + id);
                        } catch (JSONException e) {
                            Log.e(TAG, "Parse ListAllPeer", e);
                        }
                    }
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            adapter.notifyDataSetChanged();
                        }
                    });
                }
            }
        });
    }
    
// onCreateに追記
refreshPeerList();
```

![Android_Emulator_-_Nexus_5X_API_24_5554_と_MainActivity_java_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample_.png](https://qiita-image-store.s3.amazonaws.com/0/65193/521a584a-a919-e686-46d5-694f60108480.png "Android_Emulator_-_Nexus_5X_API_24_5554_と_MainActivity_java_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample_.png")


![MainActivity_java_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample_.png](https://qiita-image-store.s3.amazonaws.com/0/65193/cd5a46d7-613d-96db-031e-e3a47b83658a.png "MainActivity_java_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample_.png")
5. 最後に「refresh」ボタンを押すと`ListView`が更新されるように変更します。`activity_main.xml`と`MainActivity.java`にそれぞれ追記します。

```xml:activity_main.xml
        <Button
            android:id="@+id/refresh_btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Refresh" />
```

```java:MainActivity.java
// onCreateの中に追記
        Button refreshBtn = (Button) findViewById(R.id.refresh_btn);
        refreshBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                refreshPeerList();
            }
        });
```

![activity_main_xml_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample_.png](https://qiita-image-store.s3.amazonaws.com/0/65193/1ab7a09b-f6de-2cc4-9e54-b900278bff21.png "activity_main_xml_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample_.png")

![Android_Emulator_-_Nexus_5X_API_24_5554_と_MainActivity_java_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample_.png](https://qiita-image-store.s3.amazonaws.com/0/65193/f9a974a6-4413-febf-b94a-c489e1ffe33a.png "Android_Emulator_-_Nexus_5X_API_24_5554_と_MainActivity_java_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample_.png")

## 5. 通話のための権限を揃える

Androidで使う一部の機能はアプリを利用するユーザーから権限を認められないと利用することができません。

`AndroidManifest.xml`に記載することで権限は利用できるようになるのですが、一部の機能は、`Dangerous Permissions`として、`AndroidManifest.xml`に記載した上でさらに利用するときにダイアログを出して許可してもらう必要があります。

今回のアプリでは、`RECORD_AUDIO`が`Dangerous Permissions`に指定されているので、アプリを起動したときに、権限を要求するダイアログを出す処理を記述します。

なお、`Dangerous Permissions`の概念は、Android6.0(API23)以降に導入されたもので、それ以前のAndroid端末を対象とする場合は不要です。

これらのコードは、[実行時のパーミッション リクエスト](https://developer.android.com/training/permissions/requesting.html?hl=ja)を参考に作成しました。コメントは必要ないので入力する必要はありません。

```java:MainActivity.java
// MainActivity の下に
private static final int RECORD_AUDIO_REQUEST_ID = 1;

// onCreate の下に

    private void checkAudioPermission() {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO)
                != PackageManager.PERMISSION_GRANTED) {
            Log.d(TAG, "Manifest.permission.RECORD_AUDIO is not GRANTED");
            // Should we show an explanation?
            if (ActivityCompat.shouldShowRequestPermissionRationale(this,
                    Manifest.permission.RECORD_AUDIO)) {
                Log.d(TAG, "shouldShowRequestPermissionRationale = false");
                // Show an expanation to the user *asynchronously* -- don't block
                // this thread waiting for the user's response! After the user
                // sees the explanation, try again to request the permission.
            } else {
                // No explanation needed, we can request the permission.
                Log.d(TAG, "request Permission RECORD_AUDIO");
                ActivityCompat.requestPermissions(this,
                        new String[]{Manifest.permission.RECORD_AUDIO},
                        RECORD_AUDIO_REQUEST_ID);
                // MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
                // app-defined int constant. The callback method gets the
                // result of the request.
            }
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        switch (requestCode) {
            case RECORD_AUDIO_REQUEST_ID: {
                // If request is cancelled, the result arrays are empty.
                if (grantResults.length > 0
                        && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    Log.d(TAG, "request Permission RECORD_AUDIO GRANTED!");
                    // permission was granted, yay! Do the
                    // contacts-related task you need to do.
                } else {
                    Log.d(TAG, "request Permission RECORD_AUDIO DENIED!");
                    // permission denied, boo! Disable the
                    // functionality that depends on this permission.
                }
                return;
            }
            // other 'case' lines to check for other
            // permissions this app might request
        }
    }

// onCreate の中に
checkAudioPermission();
```

![MainActivity_java_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample_.png](https://qiita-image-store.s3.amazonaws.com/0/65193/b9811bbc-9cad-969d-6635-7360fce43c7b.png "MainActivity_java_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample_.png")

![MainActivity_java_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample_.png](https://qiita-image-store.s3.amazonaws.com/0/65193/9c511ad7-88c9-12b5-5a4a-6a37d4aa8f11.png "MainActivity_java_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample_.png")


これらのコードを書いた上で、実行すると以下のようなダイアログが表示されます。`許可`を押してください。

![Android_Emulator_-_Nexus_5X_API_24_5554_と_MainActivity_java_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample_.png](https://qiita-image-store.s3.amazonaws.com/0/65193/bebe77e5-03bf-a895-3dd0-f4e9b4eef77e.png "Android_Emulator_-_Nexus_5X_API_24_5554_と_MainActivity_java_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample_.png")


また、本来であれば、許可されなかった時に、許可を再申請する処理などのエラー処理が必要になります。
今回はサンプルアプリなのでその辺りの細かいエラー処理は省いて実装しています。

## 6. 通話(発信)を実装する

ここから、peerを使った発信の処理を行っていきます。
ListViewでクリックされたPeerIdに対して発信をする処理を書きます。

まずは、ListViewのクリックを検知する処理を書きます。
Logを確認すると、クリックが検出できていることがわかります。

```java:MainActivity.java
// onCreate の中に
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
                String selectedPeerId = idList.get(i);
                if (selectedPeerId == null) {
                    Log.d(TAG, "Selected PeerId == null");
                    return;
                }
                Log.d(TAG, "SelectedPeerId: " + selectedPeerId);
            }
        });
```

![Android_Emulator_-_Nexus_5X_API_24_5554_と_MainActivity_java_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample__と_MainActivity_java_-_WebRTCSample_-____AndroidStudioProjects_WebRTCSample_.png](https://qiita-image-store.s3.amazonaws.com/0/65193/88343108-6e8b-9b8c-897e-362b6bb095e5.png "Android_Emulator_-_Nexus_5X_API_24_5554_と_MainActivity_java_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample__と_MainActivity_java_-_WebRTCSample_-____AndroidStudioProjects_WebRTCSample_.png")



次に、実際に発信する処理を実装します。`getMediaStream`の中で、`audioFlag`のみを`true`にしているので、ボイスチャットのみの利用に制限することができます。

```java:MainActivit.java
// MainActivity の下に
private MediaConnection connection;

// refreshPeerList の下に
    private void call(String peerId) {
        Log.d(TAG, "Calling to id:" + peerId);
        if (peer == null) {
            Log.i(TAG, "Call but peer is null");
            return;
        }

        if (peer.isDestroyed || peer.isDisconnected) {
            Log.i(TAG, "Call but peer is not active");
            return;
        }

        if (connection != null) {
            Log.d(TAG, "Call but connection is already created");
            return;
        }

        MediaStream stream = getMediaStream();

        if (stream == null) {
            Log.d(TAG, "Call but media stream is null");
            return;
        }
        CallOption option = new CallOption();
        option.metadata = "test"; // TODO: metadata
        MediaConnection connection = peer.call(peerId, stream, option);

        if (connection == null) {
            Log.d(TAG, "Call but MediaConnection is null");
            return;
        }

        this.connection = connection;
        Log.d(TAG, "connection started!");
    }

    private MediaStream getMediaStream() {
        MediaConstraints constraints = new MediaConstraints();
        constraints.videoFlag = false;
        constraints.audioFlag = true;
        return Navigator.getUserMedia(constraints);
    }
    
// OnItemClickListener の中に
call(selectedPeerId);
```

![MainActivity_java_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample_.png](https://qiita-image-store.s3.amazonaws.com/0/65193/570bae6a-d798-1065-a3a3-74748b0012a2.png "MainActivity_java_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample_.png")

![MainActivity_java_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample_.png](https://qiita-image-store.s3.amazonaws.com/0/65193/121e20ba-331c-0bce-daf9-75d20cd22cca.png "MainActivity_java_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample_.png")

ここまでで、発信をすることができるようになりました。

## 7. 終了処理を実装する

次に、発信した通話を切る処理を実装します。`Button`を追加し、クリックされたら、`connection`を`close()`します。

```activity_main.xml
        <Button
            android:id="@+id/close_btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="CLOSE"/>
```

```java:MainActivity.java
// getMediaStream の下に
    private void closeConnection() {
        if (connection != null) {
            connection.close();
            MainActivity.this.connection = null;
            Log.d(TAG, "Connection is Closed");
        }
    }

// onCreate の中に
        Button closeBtn = (Button) findViewById(R.id.close_btn);
        closeBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                closeConnection();
            }
        });
```

![FollowersActivity_java_-_collegino_android_-____AndroidStudioProjects_collegino_android__と_activity_main_xml_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample_.png](https://qiita-image-store.s3.amazonaws.com/0/65193/d505cdc5-f806-b5f5-e5a9-23f711ba3bf6.png "FollowersActivity_java_-_collegino_android_-____AndroidStudioProjects_collegino_android__と_activity_main_xml_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample_.png")

![MainActivity_java_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample_.png](https://qiita-image-store.s3.amazonaws.com/0/65193/ddb39b96-52fc-950b-add8-f5d77a9996ae.png "MainActivity_java_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample_.png")

![MainActivity_java_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample_.png](https://qiita-image-store.s3.amazonaws.com/0/65193/0827a465-7a1a-640d-b148-8337ca6e19ea.png "MainActivity_java_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample_.png")


これで、こちらから通話を切ることができるようになりました。しかし、相手から通話が切られた時に、こちらの`connection`はつなぎっぱなしになってしまい、次の相手に発信することができません。
`connection`が切れた時には、`CLOSE`イベントが発生するので、それを探知して、`connection`を`close()`する処理を実装します。

```java:MainActivity.java
// closeConnection の上に
    private void setConnectionCallback(MediaConnection connection) {
        connection.on(MediaConnection.MediaEventEnum.CLOSE, new OnCallback() {
            @Override
            public void onCallback(Object o) {
                Log.d(TAG, "Close Event is Received");
                closeConnection();
            }
        });
    }
    
// call の中に
setConnectionCallback(connection);
```

![MainActivity_java_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample_.png](https://qiita-image-store.s3.amazonaws.com/0/65193/af64064d-9cfa-d8d9-77d0-1b61020fca52.png "MainActivity_java_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample_.png")


最後に、アプリが終了した時にも、通話を終了し`Peer`を終了しなくてはなりません。

```java:MainActivity.java
// onCreate の下に
    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (connection != null) {
            closeConnection();
        }
        if (peer != null && !peer.isDestroyed) {
            peer.destroy();
            peer = null;
        }
    }
```

![MainActivity_java_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample_.png](https://qiita-image-store.s3.amazonaws.com/0/65193/afa511e3-344c-999b-63e1-df48592b1bea.png "MainActivity_java_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample_.png")



## 8. 通話(着信)を実装する

ここまでで、発信については実装が完了しました。
次に、着信の時の処理を実装していきます。

着信があった時、`peer`インスタンスに`CALL`イベントが発生します。そのイベントをキャッチして、通話を開始します。

```java:MainActivity.java
        peer.on(Peer.PeerEventEnum.CALL, new OnCallback() {
            @Override
            public void onCallback(Object o) {
                Log.d(TAG, "CALL Event is Received");
                if (o instanceof MediaConnection) {
                    MediaConnection connection = (MediaConnection) o;

                    if (MainActivity.this.connection != null) {
                        Log.d(TAG, "connection is already created");
                        connection.close();
                        return;
                    }

                    // TODO: show dialog

                    MediaStream stream = MainActivity.this.getMediaStream();

                    connection.answer(stream);

                    setConnectionCallback(connection);

                    MainActivity.this.connection = connection;
                    Log.d(TAG, "CALL Event is Received and Set");
                }
            }
        });
```

![MainActivity_java_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample_.png](https://qiita-image-store.s3.amazonaws.com/0/65193/bfbbf1f7-4a83-576c-2325-318e93f60c5d.png "MainActivity_java_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample_.png")

これで、着信を処理することができるようになりました。

## 9. エラーを抑える

実際に通話をしてログを見てみると以下のような、warningが出ていることに気づきました。

```
W/AudioDeviceTemplate: The application should use MODE_IN_COMMUNICATION audio mode!
```

![MainActivity_java_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample_.png](https://qiita-image-store.s3.amazonaws.com/0/65193/7543f673-83c6-71f4-60f1-a1daf36bfd95.png "MainActivity_java_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample_.png")

これを解消するために、以下のコードを付け加えます。

```java:MainActivity.java
// onCreate の中に
        Context context = getApplicationContext();
        AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
        am.setMode(AudioManager.MODE_IN_COMMUNICATION);
```

![MainActivity_java_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample_.png](https://qiita-image-store.s3.amazonaws.com/0/65193/e3c470bb-d09e-cb32-b4fb-04f226ea7eff.png "MainActivity_java_-_WebRTCPhoneSample_-____AndroidStudioProjects_WebRTCPhoneSample_.png")


これで、解消されます。

## 最後に

TODO: 着信のダイアログを出したい