Android
Kotlin
SkyWay

kotlinで最低限の実装でandroidでskywayを動かすよ

もくもく会の結果

とりあえず30分くらいで映像通信はできたのでシェアします。

準備

AndroidManifest.xmlをいじる

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="santapj.co.jp.santa">

    <!-- ここから  -->
    <uses-feature android:name="android.hardware.camera" />
    <uses-feature android:name="android.hardware.camera.autofocus" />
    <uses-feature android:glEsVersion="0x00020000" android:required="true" />

    <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.CAMERA" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
    <!-- ここまでを追加  -->

    <application
       ....
    </application>

</manifest>

skyway.arrを設定

スクリーンショット 2017-12-08 14.13.05.png

ここに置いてModuleのgradleを


apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'

android {
    compileSdkVersion 26
    buildToolsVersion "26.0.2"

    defaultConfig {
        applicationId "santapj.co.jp.santa"
        minSdkVersion 19
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    sourceSets {
        main.java.srcDirs += 'src/main/kotlin'
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:26.0.0-alpha1'
    compile (name:'skyway',ext:'aar')
    compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
}
repositories{
    flatDir{
        dirs 'libs'
    }
    mavenCentral()
}

こうじゃ。graldeでリポジトリ指定してimportできなかったけど、きっと何か事情あるんだろう。

実装

activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="santapj.co.jp.santa.MainActivity">

    <view
        android:layout_width="140dp"
        android:layout_height="140dp"
        class="io.skyway.Peer.Browser.Canvas"
        android:id="@+id/remoteStreamView" />

    <view
        android:layout_width="140dp"
        android:layout_height="140dp"
        class="io.skyway.Peer.Browser.Canvas"
        android:id="@+id/localStreamView" />
</LinearLayout>

MainActivity.kt
package santapj.co.jp.santa

import android.content.pm.PackageManager
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import io.skyway.Peer.Browser.Canvas
import io.skyway.Peer.Peer

class MainActivity : AppCompatActivity() {

    lateinit var viewModel:ViewModel
    lateinit var localStreamView:Canvas
    lateinit var remoteStreamView:Canvas

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

        localStreamView = findViewById(R.id.localStreamView) as Canvas
        remoteStreamView = findViewById(R.id.remoteStreamView) as Canvas

        viewModel = ViewModel(this, localStreamView, remoteStreamView)
        viewModel.setup()
    }

    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        when (requestCode) {
            0 -> {
                if (grantResults.count() > 0 && grantResults[0] === PackageManager.PERMISSION_GRANTED) {
                    viewModel.setupPeer()
                } else {
                    print("Error")
                }
            }
        }
    }
}

viewModel.kt
package santapj.co.jp.santa

import android.Manifest
import android.content.Context
import android.content.pm.PackageManager
import android.support.v4.app.ActivityCompat
import android.support.v4.content.ContextCompat
import android.util.Log
import io.skyway.Peer.Browser.Canvas
import io.skyway.Peer.Browser.MediaConstraints
import io.skyway.Peer.Browser.MediaStream
import io.skyway.Peer.Browser.Navigator
import io.skyway.Peer.MediaConnection
import io.skyway.Peer.OnCallback
import io.skyway.Peer.Peer
import io.skyway.Peer.PeerOption

/**
 * Created by kentaro.haneda on 2017/12/06.
 */
class ViewModel (val activity: MainActivity, val localStreamView:Canvas, val remoteStreamView:Canvas){
    companion object {
        val API_KEY = "XXXXXX"
        val DOMAIN  = "XXXXXX"
    }

    var peer:Peer? = null
    var remoteStream:MediaStream? = null
    var localStream:MediaStream? = null
    var mediaConnection:MediaConnection? = null

    fun setup(){
        checkPermission()
    }


    fun setupPeer(){
        val option = PeerOption()
        option.key = API_KEY
        option.domain = DOMAIN
        option.debug = Peer.DebugLevelEnum.ALL_LOGS
        this.peer = Peer(activity, option)

        this.setupPeerCallBack()
    }

    private fun setupPeerCallBack(){
        this.peer?.on(Peer.PeerEventEnum.OPEN, object : OnCallback {
            override fun onCallback(p0: Any?) {
                (p0 as? String)?.let{
                    peerID ->
                    Log.d("debug", "peerID: ${peerID}")
                    startLocalStream()
                }
            }
        })
        this.peer?.on(Peer.PeerEventEnum.ERROR, object : OnCallback {
            override fun onCallback(p0: Any?) {
                Log.d("debug", "peer error ${p0}")
            }
        })
        this.peer?.on(Peer.PeerEventEnum.CALL, object : OnCallback {
            override fun onCallback(p0: Any?) {
                (p0 as? MediaConnection)?.let{
                    this@ViewModel.mediaConnection = it
                    this@ViewModel.setupMediaCallBack()
                    this@ViewModel.mediaConnection?.answer(localStream)
                }
            }
        })

    }

    private fun checkPermission(){
        if (ContextCompat.checkSelfPermission(activity,
                Manifest.permission.CAMERA) !== PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission(activity,
                Manifest.permission.RECORD_AUDIO) !== PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(activity, arrayOf<String>(Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO), 0)
        } else {
            this.setupPeer()
        }
    }

    private fun startLocalStream(){
        val constraints = MediaConstraints()
        constraints.maxWidth = 960
        constraints.maxHeight = 540
        constraints.cameraPosition = MediaConstraints.CameraPositionEnum.FRONT
        Navigator.initialize(peer)
        localStream = Navigator.getUserMedia(constraints)
        localStream?.addVideoRenderer(localStreamView, 0)
    }

    fun setupMediaCallBack(){
        mediaConnection?.on(MediaConnection.MediaEventEnum.STREAM, object : OnCallback {
            override fun onCallback(p0: Any?) {
                (p0 as? MediaStream)?.let{
                    this@ViewModel.remoteStream = it
                    this@ViewModel.remoteStream?.addVideoRenderer(remoteStreamView, 0)
                }
            }
        })
    }
}

でとりあえず動く。イカしたサンプルは後日こちらへプルリク投げナス :meat_on_bone:

https://github.com/skyway/skyway-android-sdk