Skip to content

A standalone Android container that can be used to display Contextual Cards that are rendered using JSON from an API

License

Notifications You must be signed in to change notification settings

akri16/ContextualCards

Repository files navigation

ContextualCards

API     Figma Designs     APK     Proposal

A standalone Plug-and-Play container that can be used to display Contextual Cards that are rendered using JSON from an API

Screenshots

Motivation

With the traditional development process the user interface is embedded in the app which makes it inflexible and difficult to update. But the data is fetched from a remote server. The data displayed in the app is always up-to-date and can be modified any time through a backend system. What if we could apply the same technique we use for data to the user interface itself?
Enter server-driven UI. In a SDUI implementation the user interface in the app is a blank slate. The app knows it will be rendering a listing screen but makes no assumptions about how that screen will look. The app makes a request to the server which returns both the UI and the data together.
The response from the server is some form of proprietary markup that the app understands. Instead of fetching a list of products, the app fetches a list view, which contains a set of row views, each of which contains text and image views with information about spacing, alignment, color and typography. The app renders the response from the server and the result is identical to the version using traditional development techniques.

Features

  • A standalone container, that displays a list of Contextual Cards
  • A Contextual Card is used to refer to a view that is rendered using json from an API. These views are dynamic and their properties like images, color, texts, buttons (CTAs) etc. can be changed from backend at anytime.
  • This container works completely independently of everything else, such that, we can add this to container to any fragment/activity and it should work. (Plug-and-Play component)
  • App renders contextual cards in a list based on the API response that you get from the API
  • Shows loading states and errors

Architecture

The project has 2 module:

  1. app
  2. cardcomponent

App

It deals with fetching and deserializing the JSON from the API and passing it down to the UI layer and to the cardcomponent as List<CardGroup> It uses Model View ViewModel (MVVM) architecture

Architecture Diagram

Card Component

It is the custom view that deals with actually rendering the List<CardGroup> passed down from app. It converts it to List<RenderableCardGroup> and then renders it.

Card from the API

data class Card(
    val name: String,
    val formattedTitle: FormattedText? = null,
    val title: String?,
    val formattedDescription: FormattedText? = null,
    val description: String? = null,
    val icon: CardImage? = null,
    val bgImage: CardImage? = null,
    val gradient: Gradient,
    val url: String? = null,
    val bgColor: String? = null,
    val cta: List<CTA>? = null
)

This is simplified into RenderableCard after:

  1. Processing the Entities,
  2. Formatting the text with spans
  3. Creating the final background from bgColor, gradient, and url
data class RenderableCard(
    val name: String,
    val title: CharSequence,
    val desp: CharSequence,
    val icon: String?,
    val bg: RenderableBG,
    val cta: CTA?,
    val url: String?
)

Usage

Plug this Component into your XML and play with it in your Activity/Fragment 😄

<com.akribase.cardcomponent.ui.CardComponent
            android:id="@+id/component"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
private fun initComponent(component: CardComponent) {
        component.onFetch = { viewModel.fetchUISpec() }
        component.onHC3Remove = {it:H3Remove -> viewModel.remove(it) }
        viewModel.isFetching.observe(this) {it:Boolean -> component.isLoading = it }
        viewModel.uiSpec.observe(this) {it:List<CardGroup> -> component.render(it) }
}

Dependencies

  1. Timber: Logging
  2. Hilt: Dependency Injection
  3. Datastore: Preferences Storage
  4. Glide: Image Loading
  5. Retrofit: Networking
  6. Coroutines: Concurrency
  7. AAC: Viewmodel, Livedata

Todo

  • Init the app
  • Design different Cards
    • HC3
    • HC6
    • HC5
    • HC1
    • HC9
  • Build models
  • Build Simple Renderables
  • Link the Renderables to the design
  • Build Recycler Views for Card Groups
  • Convert models to renderables
  • Add Styling
  • Connect to API
  • Bug-Fixes
  • Create Repository and Push to Github
  • Create README
  • Add Swipe to Refresh
  • Add HC3 long click effect
    • Implement Animation
    • Make Rv use ListAdapter with DiffUtil
    • Dismiss on click dismiss
    • Save on click remind later
  • Make the Container Stand-alone
  • Add error handling

Future Work

This can be also be built using Jetpack Compose (JC), the new declarative UI framework. This might make the code a bit more scalable and adaptable.

During the development of this project I faced difficulties in building the nested lists as I had to create the Viewgroups as well as the Views and associate them dynamically at runtime. With Compose, you can just declare the views without the need to handle how they are rendered internally.

However, I have used the normal imperative UI here due to time constraints. I am interested to modify this project to use JC in the future.

Download

  1. This github repository can be cloned using git clone https://github.com/Quizzie-SE/Quizzie-Android-Companion command
  2. Android Studio has to be installed (Version > 4.0)
  3. The cloned repo in the local PC should be opened using Android Studio

Building

It is recommended that you run Gradle with the --daemon option, as starting up the tool from scratch often takes at least a few seconds. You can kill the java process that it leaves running once you are done running your commands.

Tasks work much like Make targets, so you may concatenate them. Tasks are not re-done if multiple targets in a single command require them. For example, running assemble install will not compile the apk twice even though install depends on assemble.

Clean

gradle clean

Debug

This compiles a debugging apk in build/outputs/apk/ signed with a debug key, ready to be installed for testing purposes.

gradle assembleDebug

You can also install it on your attached device:

gradle installDebug

Release

This compiles an unsigned release (non-debugging) apk in build/outputs/apk/. It's not signed, you must sign it before it can be installed by any users.

gradle assembleRelease

Test

Were you to add automated java tests, you could configure them in your build.gradle file and run them within gradle as well.

gradle test

Lint

This analyses the code and produces reports containing warnings about your application in build/outputs/lint/.

gradle lint

Further reading

License

MIT License

Copyright (c) 2021 Amit Krishna A

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

About

A standalone Android container that can be used to display Contextual Cards that are rendered using JSON from an API

Topics

Resources

License

Stars

Watchers

Forks

Languages