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

GDG DevFest Kansas city - Connect your Phone and Home 
with 
Firebase and Android Things

GDG DevFest Kansas city - Connect your Phone and Home 
with 
Firebase and Android Things

Slides of the presentation about Android Things and Firebase done at the GDG DevFest Kansas City

James Coggan

February 17, 2018
Tweet

More Decks by James Coggan

Other Decks in Technology

Transcript

  1. James Coggan Connect your Phone and Home 
 with 


    Firebase and Android Things https://jamescoggan.com @mad_team
  2. Android vs Android Things ! Main app started automatically !

    Android Things support Library ! Permissions are free (reboot required for dangerous ones) ! Google play services: ◦ Firebase ▪ Realtime database ▪ Cloud messaging ▪ Etc ◦ Google cloud platform ◦ Google cloud IoT ◦ ...
  3. Android vs Android Things ! AdMob ! Android Pay !

    Full screen display (no status bar, notifications or navigation buttons) ! Sign-In ! System apps (Calendar, Telephony, Settings..) ! Display is optional
  4. Why Android Things? ! Kotlin :) ! OS and Framework

    Maintained by Google ! OTA updates ! Android community ! Android libraries ! Reusable code/infrastructure
  5. Why Android Things? A bundle is a zip file that

    contains the following: ! bootanimation.zip: Boot animation located in the root directory ! <user-space driver>.apk:User-space driver as a service (action=BOOT_COMPLETED) ! <main>.apk: (Required) The apk for the main entry point (action=MAIN, category=IOT_LAUNCHER) ! <sub>.apk:One of any number of apks that is launched by the main apk
  6. Update policy ! POLICY_CHECKS_ONLY - Only check for an available

    update. ! POLICY_APPLY_ONLY - Download and apply an available update. ! POLICY_APPLY_AND_REBOOT - Download and apply an available update. Reboot the device automatically. This is the default policy.
  7. Getting started <!—- Things manifest file —-> <activity android:name=".MainActivity"> <intent-filter>


    <!-- Launch activity as default from Android Studio --> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
  8. Getting started <activity android:name=".MainActivity"> <intent-filter>
 <!-- Launch activity as default

    from Android Studio --> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> <intent-filter> <!-- Launch activity automatically on boot --> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.IOT_LAUNCHER" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity>
  9. 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)
  10. val gpioForLED: String // Pin 31 get() { return when

    (Build.DEVICE) { DEVICE_RPI3 -> "BCM6" } }
  11. val gpioForLED: String // Pin 31 get() { return when

    (Build.DEVICE) { DEVICE_RPI3 -> "BCM6" DEVICE_IMX6UL_PICO -> "GPIO4_IO22" DEVICE_IMX7D_PICO -> "GPIO2_IO02" } }
  12. 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) } }
  13. 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) } }
  14. @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); 
 PeripheralManagerService service

    = new PeripheralManagerService(); try { bus = service.openI2cDevice(I2C_ADDRESS, BMP280_TEMPERATURE_SENSOR_SLAVE); calibrationData[0] = bus.readRegWord(REGISTER_TEMPERATURE_CALIBRATION_1); calibrationData[1] = bus.readRegWord(REGISTER_TEMPERATURE_CALIBRATION_2); calibrationData[2] = bus.readRegWord(REGISTER_TEMPERATURE_CALIBRATION_3); …. private static int readSample(byte[] data) { // msb[7:0] lsb[7:0] xlsb[7:4] int msb = data[0] & 0xff; int lsb = data[1] & 0xff; int xlsb = data[2] & 0xf0; // Convert to 20bit integer return (msb << 16 | lsb << 8 | xlsb) >> 4; } } https://github.com/blundell/androidthings-i2c-input
  15. sensor = RainbowHat.openSensor() sensor?.setTemperatureOversampling(Bmx280.OVERSAMPLING_1X) sensor?.setPressureOversampling(Bmx280.OVERSAMPLING_1X) val temperature = sensor?.readTemperature() ?:

    0f val pressure = sensor?.readPressure() ?: 0f val value = TemperatureAndPressure(temperature, pressure) saveTemperatureAndPressure(value)
  16. 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 • Enable anonymous login on (temporarily) 

  17. 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 • Enable anonymous login on (temporarily) 

  18. • Setup a database 
 // Database
 { "home" :

    { "light" : false, "pressure" : 0, "temperature" : 0 } }
  19. // Base build.gradle for all modules
 buildscript {
 ...
 dependencies

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


    implementation "com.google.firebase:firebase-database:$playServices"
 implementation "com.google.firebase:firebase-auth:$playServices"
 implementation "android.arch.lifecycle:extensions$archComponentsVersion"
 }
 
 apply plugin: "com.google.gms.google-services"
  21. // MainActivity.kt (Both modules - mobile and things)
 companion object

    { private val HOME = "home" } override fun onCreate(savedInstanceState: Bundle?) { }
  22. // MainActivity.kt (Both modules - mobile and things)
 companion object

    { private val HOME = "home" } override fun onCreate(savedInstanceState: Bundle?) { val firebaseAuth = FirebaseAuth.getInstance() }
  23. // MainActivity.kt (Both modules - mobile and things)
 companion object

    { private val HOME = "home" } override fun onCreate(savedInstanceState: Bundle?) { val firebaseAuth = FirebaseAuth.getInstance() val reference = FirebaseDatabase .getInstance() .reference .child(HOME) }
  24. // Mobile Activity companion object { val LIGHT = "light"

    } override fun onCreate(savedInstanceState: Bundle?) { ... }
  25. // Mobile Activity companion object { val LIGHT = "light"

    } override fun onCreate(savedInstanceState: Bundle?) { ... lightToggle.setOnCheckedChangeListener({ _, state: Boolean -> }) }
  26. // Mobile Activity companion object { val LIGHT = "light"

    } override fun onCreate(savedInstanceState: Bundle?) { ... lightToggle.setOnCheckedChangeListener({ _, state: Boolean -> databaseReference.child(LIGHT).setValue(state) }) }
  27. // Things Activity companion object { val PRESSURE = "pressure"

    val TEMPERATURE = "temperature" } private val temperatureAndPressureListener = object : Sensor.OnStateChangeListener<TemperatureAndPressure> { }
  28. // Things Activity companion object { val PRESSURE = "pressure"

    val TEMPERATURE = "temperature" } private val temperatureAndPressureListener = object : Sensor.OnStateChangeListener<TemperatureAndPressure> { override fun onStateChanged(state: TemperatureAndPressure) { } }
  29. // Things Activity companion object { val PRESSURE = "pressure"

    val TEMPERATURE = "temperature" } private val temperatureAndPressureListener = object : Sensor.OnStateChangeListener<TemperatureAndPressure> { override fun onStateChanged(state: TemperatureAndPressure) { reference.child(TEMPERATURE).setValue(state.temperature) reference.child(PRESSURE).setValue(state.pressure) } }
  30. class HomeInformationLiveData (val dbReference : DatabaseReference) : LiveData<HomeInformation>() { private

    val valueEventListener = object : ValueEventListener { override fun onDataChange(snapshot: DataSnapshot) { } } }
  31. class HomeInformationLiveData (val dbReference : DatabaseReference) : LiveData<HomeInformation>() { private

    val valueEventListener = object : ValueEventListener { override fun onDataChange(snapshot: DataSnapshot) { val newValue = snapshot.getValue(HomeInformation::class.java) value = newValue } } }
  32. class HomeInformationLiveData (val dbReference : DatabaseReference) : LiveData<HomeInformation>() { private

    val valueEventListener = object : ValueEventListener { override fun onDataChange(snapshot: DataSnapshot) { val newValue = snapshot.getValue(HomeInformation::class.java) value = newValue } override fun onCancelled(error: DatabaseError) { Timber.w(error.toException(), "onCancelled") } } }
  33. class HomeInformationLiveData (val dbReference : DatabaseReference) : LiveData<HomeInformation>() { …

    override fun onActive() { dbReference.addValueEventListener(valueEventListener) } override fun onInactive() { } }
  34. class HomeInformationLiveData (val dbReference : DatabaseReference) : LiveData<HomeInformation>() { …

    override fun onActive() { dbReference.addValueEventListener(valueEventListener) } override fun onInactive() { dbReference.removeEventListener(valueEventListener) } }
  35. // Mobile Activity private val lightsDataObserver = Observer<HomeInformation> { homeInformation

    -> pressureText.text =homeInformation?.pressure ?: 0f temperatureText.text = homeInformation?.temperature ?: 0f) }
  36. companion object { val HOME = "home" } private val

    reference = FirebaseDatabase.getInstance().reference.child(HOME)
  37. companion object { val HOME = "home" } private val

    reference = FirebaseDatabase.getInstance().reference.child(HOME) private val homeLiveData = HomeInformationLiveData(reference)
  38. // Mobile and Things Activity override fun onCreate(savedInstanceState: Bundle?) {

    firebaseAuth.signInAnonymously() .addOnCompleteListener { } }
  39. // Mobile and Things Activity override fun onCreate(savedInstanceState: Bundle?) {

    firebaseAuth.signInAnonymously() .addOnCompleteListener { task -> if (task.isSuccessful) { homeLiveData.observe(this, lightsDataObserver) } }
 }
  40. // Mobile and Things Activity override fun onStop() { super.onStop()

    lightsLiveData.removeObserver { lightsDataObserver } }
  41. Nearby ! Pub/Sub ! Send messages, files or stream data

    ! No need for server ! Peer to peer ! Wifi or BLE
  42. // Android Things and Mobile val googleApiClient = GoogleApiClient.Builder(this) .addApi(Nearby.CONNECTIONS_API)

    .addConnectionCallbacks(object : GoogleApiClient.ConnectionCallbacks { ) .build()
  43. // Android Things and Mobile val googleApiClient = GoogleApiClient.Builder(this) .addApi(Nearby.CONNECTIONS_API)

    .addConnectionCallbacks(object : GoogleApiClient.ConnectionCallbacks { override fun onConnected(connectionHint: Bundle?) { } }) .build()
  44. // Android Things and Mobile val googleApiClient = GoogleApiClient.Builder(this) .addApi(Nearby.CONNECTIONS_API)

    .addConnectionCallbacks(object : GoogleApiClient.ConnectionCallbacks { override fun onConnected(connectionHint: Bundle?) { startListening() } }) .build()
  45. // Android Things and Mobile val googleApiClient = GoogleApiClient.Builder(this) .addApi(Nearby.CONNECTIONS_API)

    .addConnectionCallbacks(object : GoogleApiClient.ConnectionCallbacks { override fun onConnected(connectionHint: Bundle?) { startService() } override fun onConnectionSuspended(cause: Int) { Timber.e(“Failed”): } }) .build()
  46. // Mobile private fun startService() { Nearby.Connections.startDiscovery( googleApiClient, SERVICE_ID, endpointDiscoveryCallback,

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

    onEndpointFound(endpointId: String?, info: DiscoveredEndpointInfo?) { } }
  48. private val endpointDiscoveryCallback = object : EndpointDiscoveryCallback() { override fun

    onEndpointFound(endpointId: String?, info: DiscoveredEndpointInfo?) { endpointId?.let { } } }
  49. private val endpointDiscoveryCallback = object : EndpointDiscoveryCallback() { override fun

    onEndpointFound(endpointId: String?, info: DiscoveredEndpointInfo?) { endpointId?.let { requestConnection(endpointId) } } }
  50. private fun sendDataPayload(endpointId : String) { val credentials = getCredentials()

    val adapter = moshi.adapter(Credentials::class.java) }
  51. private fun sendDataPayload(endpointId : String) { val credentials = getCredentials()

    val adapter = moshi.adapter(Credentials::class.java) val dataToBeSent = adapter.toJson(credentials) }
  52. private fun sendDataPayload(endpointId : String) { val credentials = getCredentials()

    val adapter = moshi.adapter(Credentials::class.java) val dataToBeSent = adapter.toJson(credentials) dataToBeSent?.let { } }
  53. private fun sendDataPayload(endpointId : String) { val credentials = getCredentials()

    val adapter = moshi.adapter(Credentials::class.java) val dataToBeSent = adapter.toJson(credentials) dataToBeSent?.let { val bytes = dataToBeSent?.toByteArray() } }
  54. private fun sendDataPayload(endpointId : String) { val credentials = getCredentials()

    val adapter = moshi.adapter(Credentials::class.java) val dataToBeSent = adapter.toJson(credentials) dataToBeSent?.let { val bytes = dataToBeSent?.toByteArray() Nearby.Connections.sendPayload(googleApiClient, endpointId, Payload.fromBytes(bytes)) } }
  55. // Android Things private fun startListening() { Nearby.Connections.startAdvertising( googleApiClient, appName,

    SERVICE_ID, connectionLifecycleCallback, AdvertisingOptions(Strategy.P2P_STAR))
  56. // Android Things private fun startListening() { Nearby.Connections.startAdvertising( googleApiClient, appName,

    SERVICE_ID, connectionLifecycleCallback, AdvertisingOptions(Strategy.P2P_STAR)) .setResultCallback {})
 }
  57. // Android Things private fun startListening() { Nearby.Connections.startAdvertising( googleApiClient, appName,

    SERVICE_ID, connectionLifecycleCallback, AdvertisingOptions(Strategy.P2P_STAR)) .setResultCallback { result -> when { result.status.isSuccess -> Timber.d("SUCCESS") else -> Timber.w("STATE_READY") })
 }
  58. // Android Things private val connectionLifecycleCallback = object : ConnectionLifecycleCallback()

    { override fun onConnectionResult(endpointId: String?, result: ConnectionResolution?) { Timber.d("connectionResult from " + endpointId, result) } }
  59. // Android Things private val connectionLifecycleCallback = object : ConnectionLifecycleCallback()

    { override fun onConnectionResult(endpointId: String?, result: ConnectionResolution?) { Timber.d("connectionResult from " + endpointId, result) } override fun onDisconnected(endpointId: String?) { Timber.w("onDisconnected from " + endpointId) } }
  60. // Android Things private val connectionLifecycleCallback = object : ConnectionLifecycleCallback()

    { … override fun onConnectionInitiated(endpointId: String?, connectionInfo: ConnectionInfo?) { } }
  61. // Android Things private val connectionLifecycleCallback = object : ConnectionLifecycleCallback()

    { … override fun onConnectionInitiated(endpointId: String?, connectionInfo: ConnectionInfo?) { Timber.d("onConnectionInitiated from " + endpointId, connectionInfo) Nearby.Connections.acceptConnection( googleApiClient, endpointId, payloadCallback ) } }
  62. // Android Things private val payloadCallback = object : PayloadCallback()

    { override fun onPayloadReceived(endpointId: String?, payload: Payload?) { } }
  63. // Android Things private val payloadCallback = object : PayloadCallback()

    { override fun onPayloadReceived(endpointId: String?, payload: Payload?) { payload?.let { onDataReceived(payload.asBytes()) } } }
  64. private fun onDataReceived(data: ByteArray) { val jsonString = String(data) val

    adapter = moshi.adapter(Credentials::class.java) }
  65. private fun onDataReceived(data: ByteArray) { val jsonString = String(data) val

    adapter = moshi.adapter(Credentials::class.java) val credentials = adapter.fromJson(jsonString) }
  66. private fun onDataReceived(data: ByteArray) { val jsonString = String(data) val

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

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