Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Connect your Phone and Home with Firebase and Android Things

James Coggan
December 28, 2017

Connect your Phone and Home with Firebase and Android Things

Presentation done at Droidcon Tel Aviv 2017

James Coggan

December 28, 2017
Tweet

More Decks by James Coggan

Other Decks in Programming

Transcript

  1. James Coggan, MyDrive Solutions Connect your Phone and Home with

    Firebase and Android Things We are hiring! https://www.mydrivesolutions.com/jobs Q&A sli.do #thingstelaviv
  2. A little bit about me.. James Coggan Android tech lead

    https://jamescoggan.com @mad_team
  3. Good to know • Main application started on boot •

    Permissions are free and set on reboot • adb connect 192.168.1.111:5555
  4. Why Android Things? • Kotlin :) • Maintained by Google

    • OTA updates • Android community • Hardware agnostic • Relatively cheap hardware • Reusable code
  5. Supported I/Os • General Purpose Input/Output (GPIO): Binary • Pulse

    Width Modulation (PWM): Servo motors, DC motors • Inter-Integrated Circuit(I2C) • Serial Peripheral Interface (SPI) • Universal Asynchronous Receiver Transmitter (UART)
  6. val pioService = PeripheralManagerService() try { val pinName = "BCM6"

    ledGpio = pioService.openGpio(pinName) ledGpio?.setDirection(Gpio.DIRECTION_OUT_INITIALLY_LOW) } catch (e: IOException) { e.printStackTrace() }
  7. val pioService = PeripheralManagerService() try { val pinName = "BCM21"

    button = ButtonInputDriver( pinName, Button.LogicState.PRESSED_WHEN_LOW, KeyEvent.KEYCODE_SPACE) button?.register() } catch (e: IOException) { e.printStackTrace() }
  8. override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean { if (keyCode

    == KeyEvent.KEYCODE_SPACE) { setLedValue(true) return true } return super.onKeyDown(keyCode, event) }
  9. override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean { if (keyCode

    == KeyEvent.KEYCODE_SPACE) { setLedValue(false) return true } return super.onKeyUp(keyCode, event) }
  10. val gpioForLED: String // Pin 31 get() { return when

    (Build.DEVICE) { DEVICE_RPI3 -> "BCM6" DEVICE_IMX6UL_PICO -> "GPIO4_IO22" DEVICE_IMX7D_PICO -> "GPIO2_IO02" else -> throw IllegalStateException("Unknown Build.DEVICE " + Build.DEVICE) } }
  11. val gpioForButton: String // Pin 40 get() { return when

    (Build.DEVICE) { DEVICE_RPI3 -> "BCM21" DEVICE_IMX6UL_PICO -> "GPIO2_IO03" DEVICE_IMX7D_PICO -> "GPIO6_IO14" else -> throw IllegalStateException("Unknown Build.DEVICE " + Build.DEVICE) } }
  12. val pinName = gpioForLED ledGpio = pioService.openGpio(pinName) ... val pinName

    = gpioForButton button = ButtonInputDriver( pinName, Button.LogicState.PRESSED_WHEN_LOW, KeyEvent.KEYCODE_SPACE) ...
  13. Setting up Firebase https://console.firebase.google.com • Create a new Firebase project

    • Add a new Android app to the project with your package name: ie: com.jamescoggan.thingspresentation • Enable anonymous login on (temporarily)
  14. • Setup a database // Database { "light" : true

    } • Add the rules // Rules - don’t expose your data for the world { "rules": { ".write": "auth != null", ".read": "auth != null", } }
  15. // Base build.gradle for all modules buildscript { ... dependencies

    { classpath "com.google.gms:google-services:$googleServicesClassVersion" } }
  16. // mobile & things build.gradle dependencies { ... implementation "com.google.firebase:firebase-core:$playServicesVersion"

    implementation "com.google.firebase:firebase-database:$playServicesVersion" implementation "com.google.firebase:firebase-auth:$playServicesVersion" implementation "android.arch.lifecycle:extensions:$androidArchComponentsVersion" } apply plugin: "com.google.gms.google-services"
  17. // MainActivity.kt (Both modules - mobile and things) override fun

    onCreate(savedInstanceState: Bundle?) { FirebaseApp.initializeApp(this) // Move me to the Application class val firebaseAuth = FirebaseAuth.getInstance() val databaseReference = FirebaseDatabase.getInstance().reference }
  18. // MainActivity.kt override fun onCreate(savedInstanceState: Bundle?) { ... val databaseReference

    = FirebaseDatabase.getInstance().reference firebaseAuth.signInAnonymously() .addOnCompleteListener { task -> if (task.isSuccessful) { observeLightsData() } else { Timber.e(task.exception, "FirebaseAuth:failed") } } }
  19. // Light.kt class FirebaseTables { companion object { val LIGHTS_BASE

    = "light" } } data class Light(val light : Boolean)
  20. class LightLiveData(val firebase: DatabaseReference) : LiveData<Light>() { private val valueEventListener

    = object : ValueEventListener { override fun onDataChange(snapshot: DataSnapshot) { val newValue = snapshot.getValue(Boolean::class.java) ?: false value = Light(newValue) } override fun onCancelled(error: DatabaseError) { } } override fun onActive() { firebase.child(LIGHTS_BASE).addValueEventListener(valueEventListener) } override fun onInactive() { firebase.child(LIGHTS_BASE).removeEventListener(valueEventListener) } }
  21. // Things Activity private val lightsDataObserver = Observer<Light> { lightState

    -> Timber.d("LightState changed: ${lightState?.isOn}") led.setValue(lightState?.isOn ?: false) } override fun onCreate(savedInstanceState: Bundle?) { ... firebaseAuth.signInAnonymously() .addOnCompleteListener { task -> if (task.isSuccessful) { lightsLiveData.observe(this, lightsDataObserver) } else { Timber.e(task.exception, "FirebaseAuth:failed")
  22. Nearby • Pub/Sub • Send messages, files or stream data

    • No need for server • Peer to peer • Wifi or BLE
  23. googleApiClient = GoogleApiClient.Builder(this) .addApi(Nearby.CONNECTIONS_API) .addConnectionCallbacks(object : GoogleApiClient.ConnectionCallbacks { override fun

    onConnected(connectionHint: Bundle?) { startService() } override fun onConnectionSuspended(cause: Int) { failed() } }) .build()
  24. private fun startService() { // Android Things Nearby.Connections.startAdvertising( googleApiClient, "appName",

    "serviceId", connectionLifecycleCallback, AdvertisingOptions(Strategy.P2P_STAR)) .setResultCallback { result -> when { result.status.isSuccess -> Timber.d("startAdvertising:onResult: SUCCESS") else -> Timber.w("STATE_READY")
  25. private fun startService() { // Phone Nearby.Connections.startDiscovery( googleApiClient, "serviceId", endpointDiscoveryCallback,

    DiscoveryOptions(Strategy.P2P_STAR)) .setResultCallback { result -> when { result.status.isSuccess -> Timber.d("startDiscovery:SUCCESS") else -> { Timber.w("startDiscovery:FAILURE ${result.statusMessage}")
  26. private val connectionLifecycleCallback = object : ConnectionLifecycleCallback() { override fun

    onConnectionResult(endpointId: String?, result: ConnectionResolution?) { Timber.d("connectionResult from " + endpointId, result) sendDataPayload() } }
  27. // Phone private fun sendDataPayload(email : String, password: String) {

    val credentials = Credentials(email, password) val adapter = moshi.adapter(Credentials::class.java) val json = adapter.toJson(credentials) Nearby.Connections.sendPayload( googleApiClient, currentEndpointId, Payload.fromBytes(json.toByteArray()) ) }
  28. private val payloadCallback = object : PayloadCallback() { override fun

    onPayloadReceived(endpointId: String?, payload: Payload?) { val adapter = moshi.adapter(Credentials::class.java) val credentials = adapter.fromJson(jsonString) credentials?.let { saveCredentials(credentials) } } }
  29. https://github.com/jamescoggan/thingspresentationn • Common code in shared module • Nearby •

    RainbowHat: ◦ Sensors ◦ Led ◦ Button • Philips Hue (in progress)