agora SDKを利用し、Android Studioで通話アプリを作成

Vcubeで通話処理をするためのサンプルアプリを作成したので、記録に残します。

# 動作環境

* Android studio 4.1.2
* Windows 10 Pro 20H2

# agora consoleにてApp idを取得
1. [agora console](https://console.agora.io/)　にアクセスする
1. 会員登録を行い、consoleを表示する
![画像2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/957268/3e246b2e-6aa4-e01b-651f-3596d9895259.png)
1. Project Managementをクリックし、Createをクリック
![画像3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/957268/e1d317d3-0675-509d-16fc-ae46352f662e.png)

1. プロジェクト名を入力し、認証方法をApp　idのみを選択し「submit」をクリック
            <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/957268/d1e55aad-2c2b-fe81-fb60-c7cd444047d6.png" width="500">
1. プロジェクトが作成されるので、目のマークをクリックしapp idを取得する
![キャプチャ (2).PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/957268/15533624-752e-f8f9-6926-e7f44f3c58fa.png)

# 実装
### agora SDKを利用するための準備

1. [ダウンロードページ](https://docs.agora.io/en/All/downloads?platform=All%20Platforms)からAndroid VideoSDKをダウンロードし、libsフォルダをプロジェクト内にコピーする
1. AndroidStudioでプロジェクトを開き、app/src/main/build.gradleファイルにsourceSetsとfileTreeを追記する

    ```ruby:app/build.gradle
    android {
        sourceSets{
              main{
               jniLibs.srcDirs = ['../../../libs']
              }
        }
    }

    dependencies {
          implementation fileTree(dir: '../../../libs', includes: ['*.jar'])
    }
    ```

1. カメラ/マイクの利用を許可する為、AndroidManifest.xmlを修正する

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

1. VideoSDKをプロジェクトに統合する

    ```ruby:app/build.gradle
    allprojects {
        repositories {
            ...
            maven { url 'https://www.jitpack.io' }
        }
    }
    ```
### 画面の作成

1. UIの作成

    ```ruby:activity_main.xml
    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">


    <FrameLayout
        android:id="@+id/local_video_view_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/white" />

    <FrameLayout
        android:id="@+id/remote_video_view_container"
        android:layout_width="160dp"
        android:layout_height="160dp"
        android:layout_alignParentEnd="true"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:layout_marginEnd="16dp"
        android:layout_marginRight="16dp"
        android:layout_marginTop="16dp"
        android:background="@android:color/darker_gray" />

    </androidx.constraintlayout.widget.ConstraintLayout>
    ```

### 機能の作成

1. Androidクラスをインポート

    ```ruby:MainActivity.kt
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import android.Manifest;
import android.content.pm.PackageManager;
import android.view.SurfaceView;
import android.widget.FrameLayout;
    ```

1. パーミッションの設定

    ```MainActivity.kt
    private val PERMISSION_REQ_ID_RECORD_AUDIO = 22
    private val PERMISSION_REQ_ID_CAMERA = PERMISSION_REQ_ID_RECORD_AUDIO + 1

    private fun checkSelfPermission(permission: String, requestCode: Int): Boolean {
  if (ContextCompat.checkSelfPermission(this, permission) != 
      PackageManager.PERMISSION_GRANTED) {
    ActivityCompat.requestPermissions(this,
                                     arrayOf(permission),
                                     requestCode)
    return false
  }
  return true
}
    ```

1. Agoraクラスをインポートします。

    ```ruby:MainActivity.kt
 import io.agora.rtc.IRtcEngineEventHandler;
 import io.agora.rtc.RtcEngine;
 import io.agora.rtc.video.VideoCanvas;
    ```

1. ビデオハングアウトチャネルのコールバックを作成

    ```ruby:MainActivity.kt
    /Fill the App ID of your project generated on Agora Console./
    private val APP_ID = "取得したapp id"
    /Fill the channel name./
    private val CHANNEL = "任意のチャンネル名"
    /Fill the temp token generated on Agora Console./
    private val TOKEN = ""

    private var mRtcEngine: RtcEngine ?= null

    private val mRtcEventHandler = object : IRtcEngineEventHandler() {
    / Listen for the remote user joining the channel to get the uid of the user. /

        override fun onUserJoined(uid: Int, elapsed: Int) {
            runOnUiThread {
                / Call setupRemoteVideo to set the remote video view after getting uid from the onUserJoined callback. /
                setupRemoteVideo(uid) 
            }
        }
    }
    ```

1. チャネルに参加するためのコアメソッドを呼び出しを作成

    ```ruby:MainActivity.kt
    private fun initializeAndJoinChannel() {
       try {
           mRtcEngine = RtcEngine.create(baseContext, APP_ID, mRtcEventHandler)
       } catch (e: Exception) {

    }

    / By default, video is disabled, and you need to call enableVideo to start a video stream. /
    mRtcEngine!!.enableVideo()

    val localContainer = findViewById(R.id.local_video_view_container) as FrameLayout
   / Call CreateRendererView to create a SurfaceView object and add it as a child to the FrameLayout. /
   val localFrame = RtcEngine.CreateRendererView(baseContext)
   localContainer.addView(localFrame)
   / Pass the SurfaceView object to Agora so that it renders the local video. /
   mRtcEngine!!.setupLocalVideo(VideoCanvas(localFrame, VideoCanvas.RENDER_MODE_FIT, 0))

    / Join the channel with a token. /
    mRtcEngine!!.joinChannel(TOKEN, CHANNEL, "", 0)
 }
    ```

1. リモートユーザーがチャネルに参加するときに、リモートインターフェイスを追加する

    ```ruby:MainActivity.kt
    private fun setupRemoteVideo(uid: Int) {
         val remoteContainer = findViewById(R.id.remote_video_view_container) as FrameLayout

         val remoteFrame = RtcEngine.CreateRendererView(baseContext)
         remoteFrame.setZOrderMediaOverlay(true)
         remoteContainer.addView(remoteFrame)
     
         mRtcEngine!!.setupRemoteVideo(VideoCanvas(remoteFrame, VideoCanvas.RENDER_MODE_FIT, uid))
    }
    ```

1. アプリを開始処理を作成

    ```ruby:MainActivity.kt
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // If all the permissions are granted, initialize the RtcEngine object and join a channel.
        if (checkSelfPermission(Manifest.permission.RECORD_AUDIO, PERMISSION_REQ_ID_RECORD_AUDIO) && checkSelfPermission(
                Manifest.permission.CAMERA,
                PERMISSION_REQ_ID_CAMERA
            )) {
            initializeAndJoinChannel()
        }
    }
    ```

1. アプリを閉じたら通話を終了するように作成

    ```ruby:MainActivity.kt
    override fun onDestroy() {
       super.onDestroy()

       mRtcEngine?.leaveChannel()
       RtcEngine.destroy()
    }
    ```

# 参照URL

https://docs.agora.io/en/Video/start_call_android

https://qiita.com/v-cube/items/c43dd8b0241f03fedc1d