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

Transforming Farmer's Lives Using Android in Kenya

Transforming Farmer's Lives Using Android in Kenya

Imagine if you could change the lives of farmers using Android. We did that! This is the story of how Apollo Agriculture relies on two Android apps, both fully written in Kotlin, are offline first and heavily rely on Server Side Driven UI to render the UI components.

We'll(Harun Wangereka & Juma Allan) take a deep dive into the challenges we face when developing apps for devices with low memory, low internet bandwidth, offline-first approach and some of the key lessons we've learnt so far. This talk will show how our server-side UI approach works, and some of the reasons that made us adapt this. Additionally, the talk covers the tools we use to make this possible: an internal tool called "Choice Expressions" and an implementation of JsonSchema, both used to build UIs from a set of conditions.

Lastly, we'll talk about some of our future plans, including but not limited to adopting Jetpack Compose in the apps.

Session was in Droidcon Berlin Presented by Juma Allan.

Harun Wangereka

July 08, 2022
Tweet

More Decks by Harun Wangereka

Other Decks in Programming

Transcript

  1. apollo Apollo Agriculture Harun Wangereka. Software Engineer - Apollo Agriculture

    @wangerekaharun Juma Allan. Software Engineer - Apollo Agriculture @_jumaallan
  2. apollo Apollo Agriculture What does that mean for the Android

    Team? Apollo is intensely focused on making data driven decisions, and this puts pressure on the Engineering Team. So, how do we make it easier to collect data needed to make these decisions? • By building a simple app used by agents and agro dealers • Build new screens using a config without having to release new version of the app • Build an app that works for low end devices, and works offline
  3. apollo Apollo Agriculture JSON Schema What is JSON Schema? It

    is a vocabulary that allows you to annotate and validate JSON documents. Why we use JSON Schema: • Easy data format description between Android & Backend. • Human and Machine readable data. • Enforce quality of data submitted by Android client. • Easy to test.
  4. apollo Apollo Agriculture JSON Schema at Apollo: Tasks • A

    task represents an action that is carried out by an agent or agro dealer. • We model a task with a list of sub tasks, which represent a single action. • Our apps fetch tasks assigned to an agent or agro dealer, save in Room Database and use this to build out dynamic screens. Fetch a task from the backend Store this in Room database tasks listOf<SubTasks>
  5. { "id": 867865617, "farmer_id": "3506ab-8aa0-4cv29-b5ed-1878222", "agent_id": "3506ab-8aa0-4cv29-b5ed-1878222", "-": null, "requirements":

    [], "-": null, "label": "Droidcon Berlin", "-": {}, "-": null, "-": null, "-": null, "-": null, "-": null, "-": false, "-": true, "-": false, "-": "PENDING", "-": null, "-": null, "-": null, "-": null, "sub_tasks": [] } JSON Schema at Apollo: Tasks We have a ‘task’, with the label Droidcon Berlin. The task can contain a list of sub tasks, which are what we use to build out feature screens. We can additionally send other config keys here too to help configure the task.
  6. { "config": { "form": { "version": 14, "type": "object", "required":

    [ "initial.event" ], "properties": { "initial": { "title": "Initial questions", "type": "object", "properties": { "event": { "type": "string", "title": "How organized was the event?", "items": { "type": "string", "enum": [ "Extremely Organized", "Organized", "Not at all organized" ] ... } JSON Schema at Apollo: Sub Tasks The idea of a sub task, is to build a ‘feature screen.’ In this case, we would have a ‘Form Screen’, with a question that expects a user to pick an answer from the 3 choices/options provided. Using this, we create an Apollo Schema that allows us to map this config to our UI Builders [Form, Photo, Map, Audio]
  7. data class Schema( val version: Int?, val title: String?, val

    description: String?, val type: SchemaType?, val format: String?, val required: List<String>?, val properties: LinkedHashMap<String, Schema>?, val ui: Map<String, Any>?, val items: Schema?, val enum: List<String>?, val pattern: String?, val default: String?, val answer: String?, val minLength: Int?, val maxLength: Int?, val minItems: Int?, val maxItems: Int?, val minimum: Int?, val maximum: Int? ) Apollo Schema: Deep Dive We query the sub tasks from Room, then serialize it into a Schema (Apollo Schema). The Schema has a SchemaType which is a building block that determines which type of UI to render on the app.
  8. apollo Apollo Agriculture Views: Anko and Why, Compose and Why

    Anko was born as a type-safe DSL for building Android layouts. The team at the time decided to use it since it made development faster and easier, clean and easy to read and provided helpers and utilities (Anko Commons) that made development faster. Why are we moving to compose? Anko is deprecated ⚰ Compose offers a reactive view DSL for Kotlin, and works well with our state based approach of building UI components
  9. val field: Field? = fieldFactory.createField(s, key, title, required) field?.widget?.let(this::addView) if

    (field != null) { val validationError = TextView(context) validationError.setTextColor( ContextCompat.getColor( context, R.color.colorError ) ) validationError.visibility = View.GONE field.validationMessage = validationError addView(validationError) if (s.ui != null && s.ui["show_if"] != null) { setupConditional(s.ui["show_if"] as String, field) } val state = TextFieldState( key = key, title = s.title.toString(), type = s.type, fieldType = FieldType.OUTLINED_TEXT_VIEW, inputType = KeyboardOptions.Default, transform = { it.trim().lowercase() }, validators = listOf(Validators.Email()), ) if (s.ui != null && s.ui!!["show_if"] != null) { state.hasConditional = true state.conditional = s.ui!!["show_if"] as String } addState(state)
  10. apollo Apollo Agriculture • A user fills in the form,

    & we call validate which triggers validation on all fields. • When all validations are correct, we read values from the fields in a key-value pair to map answers to their keys • Inside each subtask, we have a URL that we use to post data back to the backend • For image subtasks, the photos are uploaded to AWS S3 bucket via AWS SDK which returns a URL which is linked to a subtask. subtask <result> Responses & Submitting
  11. apollo Apollo Agriculture Choice Expression { "op":"Ask", "prompt":"Are you attending

    droidcon Berlin?", "type":{ "type":"Bool" } } { "op":"Ask", "prompt":"Which?", "type":{ "type":"Labeled", "choices":[ [ "foo", "Foo" ], [ "bar", "Bar" ], [ null, "None" ] ] } } Sample Labelled Sample Boolean
  12. apollo Apollo Agriculture Challenges Devices with low RAM which slows

    down the app. Smalls screens which makes it hard to draw graphs and show reports. Low quality images, this makes it hard for the Data Science team to use the images for accurate analysis. Little or no internet access - hard to push updates. Low storage.
  13. apollo Apollo Agriculture Future Plans Migrate to Jetpack Compose. Migrate

    to Kotlinx Serialization. Migrate to WorkManager. Hiring.