SDK overviewAndroidiOSDemoChangelog

Getting started on Android

Android SDK is written in Kotlin and uses Jetpack Compose for Views. For UI we use customizable Material components. Networking is done by Kotlin Multiplatform Mobile and this part of SDK is same for Android and iOS. You can check external dependencies for this module here. Our tests show that SDK will increase your app approximately by 5MB in download size and 36MB in install size (apps prior not using Kotlin, so this might be lower in real usage).

The SDK supports minimum version of Android API 23.

However, technically it should be possible to use even 21. Older versions are not supported due to technical restrictions.

It is possible to use SDK for Kotlin projects and for Java with XML layouts too.

Minimal supported version of Kotlin is 1.7.0, for AGP it's 7.2.0.

Recommended versions are follows:

  • AGP 7.3.0+ as apps should target Android version 33.
  • Kotlin 1.8.0+ as this prevent possible error described bellow.

If you use Kotlin 1.7.0 you will probably end up with Duplicate classes between kotlin-stdlib and kotlin-stdlib-jdk8(they were merged in Kotlin 1.8.0 into kotlin-stdlib). This issue is caused by the fact that the SDK have to use Paging Compose 3.2.1+ as older versions contain breaking issues.

To solve this, you will need to add:

dependencies {
....
 constraints {
        implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.0") {
            because("kotlin-stdlib-jdk7 is now a part of kotlin-stdlib")
        }
        implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.0") {
            because("kotlin-stdlib-jdk8 is now a part of kotlin-stdlib")
        }
    }
....
}

For builds and development we use these versions: AGP 7.2.0 - 8.2.0, Gradle 7.3.3 - 8.2, Kotlin 1.7.21 - 1.9.10, Android Studio Hedgehog | 2023.1.1. We might test the exactly same versions as you use to verify compatibility if needed.

Adding SDK to project

After you download SDK from Dateio SFTP, add it to your project via Maven Local Repository by adding the SDK to /user/.m2/repository.

Then, if you don't use Maven Local Repository already, you will need to add mavenLocal() to your build file to the repositories section.

If using Maven Local Repository is not feasible from your side, please let us know what solution would you prefer, so we can test this solution and prepare a demo for you.

Next, import SDK into your project by following:

implementation("dateio.clo:android:1.7.0")
implementation("dateio.clo:shared:1.7.0")

Compose approach only - you will have to add KotlinCompiler 1.4.0 (this version might be different depending on your Kotlin version) and also enable Compose by compose = true. Plus enable the Desugaring library if you are targeting API <26. These steps are not required for XML approach.

See example of build.gradle.kts below:

plugins { 
    id("com.android.application") 
    kotlin("android") 
} 

repositories {
    mavenLocal()
}
 
android { 
    compileSdk = 33 
    defaultConfig { 
        applicationId = "eu.dateio.dateiosdkmutliplatform.android.demo" 
        minSdk = 23 
        targetSdk = 33 
        versionCode = 1 
        versionName = "1.0" 
    } 
    compileOptions { 
        isCoreLibraryDesugaringEnabled = true 
    } 
    buildFeatures { 
        compose = true 
    } 
    composeOptions { 
        kotlinCompilerExtensionVersion = "1.4.0" 
    } 
}

dependencies {
    implementation("dateio.clo:android:1.7.0")
    implementation("dateio.clo:shared:1.7.0")
    ....
}

After sync, you will be able to use SDK components.

Adding screens to the App

The SDK provides set of screens which are not directly accessible as UI components but rather as destinations which can be added to your apps navigation graph. This allows you to use the SDK in both XML-based and Compose-based applications.

XML view-based application

For XML Views based apps you will need to:

  1. Add SDK screens via res/navigation.
  2. Create new Fragment (or Activity, depends on your use case and architecture).
  3. Create an XML holder androidx.fragment.app.FragmentContainerView that holds androidx.navigation.fragment.NavHostFragment.
  4. Create function that switches screens in Fragment holder. You also might use ViewPager2 and bottom bar as shown in our Android demo.
  5. Synchronize back button with SDK navigation.

As SDK contains multiple bottom sheets and popups, it should be used as an edge-to-edge for best UI experience. You will need to set fitsSystemWindows = false for the SDK holder and ideally set the status bar transparent. SDK contains NavigationBarsPadding and other paddings inside it so you won't need to set them.

If you are using bottom bar, you will need to make sure that your configuration has set dateio_add_navigation_bars_padding to false.

Adding screens to navigation graph

Add navigation graphs from SDK to res/navigation (or create new navigation file). In our example, it's named nav_graph_dateio_sdk.xml.

<navigation
    xmlns:app="http://schemas.android.com/apk/res-auto"
    app:startDestination="@id/dateio_offers_list">

    <include app:graph="@navigation/dateio_offers_list" />
    <include app:graph="@navigation/dateio_cashback_history" />
    <include app:graph="@navigation/dateio_offers_search" />
    <include app:graph="@navigation/dateio_offers_map" />

</navigation>

Note: We found an issue with include element in navgraph when your app uses the SDK in another module than your main app. In that case it might end up with Manifest merger failed: Referenced navigation file with navigationXmlId = … not found error. In that case, please use direct declaration of screens instead of include. E.g.:

<navigation ...>

    <fragment
        android:id="@+id/offer_detail_destination"
        android:name="eu.dateio.dateiosdkmutliplatform.android.modules.offer.ui.detail.OfferDetailFragment"
        android:label="Detail">
        <argument
            android:name="offerId"
            app:argType="long" />
    </fragment>

    <fragment
        android:id="@+id/offers_list_destination"
        android:name="eu.dateio.dateiosdkmutliplatform.android.modules.offer.ui.list.OffersListFragment"
        android:label="List" />

    <fragment
        android:id="@+id/cashback_history_destination"
        android:name="eu.dateio.dateiosdkmutliplatform.android.modules.offer.ui.history.CashbackHistoryFragment"
        android:label="History" />

    <fragment
        android:id="@+id/offers_map_destination"
        android:name="eu.dateio.dateiosdkmutliplatform.android.modules.offer.ui.map.OffersMapFragment"
        android:label="Map" />

        <fragment
            android:id="@+id/offer_map_detail_destination"
            android:name="eu.dateio.dateiosdkmutliplatform.android.modules.offer.ui.detail.OfferDetailFragment"
            android:label="Detail">
            <argument
                android:name="offerId"
                app:argType="long" />
        </fragment>
    </fragment>    

</navigation>

If this is your case, Dateio will provide you further details and assistance.

In earlier SDK versions (< 1.7.0), the dateio_offers_list destination was named dateio_offers_list_cashback.

Add Fragment and XML Fragment holder

For Fragment manager navigation app you will need to add a new activity or fragment which will hold our SDK. This Activity/Fragment needs to use an XML view with androidx.fragment.app.FragmentContainerView. Also set navGraph to this holder accordingly. But this part is not necessary when you don't use Jetpack Navigation already.

See example below:

<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        app:navGraph="@navigation/nav_graph_dateio_sdk"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:defaultNavHost="true" />

</FrameLayout>

Switching to screen with SDK

You can switch to a screen with SDK by navigating to the newly created navigation graph like this:

childFragmentManager.fragments.find { it is NavHostFragment }?.let {
        val inflater = it.findNavController().navInflater
        // This is sole SDK entry point. Navigation inside the SDK handle SDK itself.
        val graph = inflater.inflate(R.navigation.nav_graph_dateio_sdk)
        it.findNavController().graph = graph
    }  
}

If you use Jetpack Navigation already, you can also use navigate function.

You can also use Configuration.processNotification(...) if you want to redirect user to another SDK page. This might be useful for push notifications or in-app banners.

Synchronize back button

Lastly if you use fragment switching and you want SDK in new window, you will need to implement back button functionality to synchronize navigation between SDK and your app:

class DemosSDKFragment : Fragment(R.layout.fragment_sdk) {    
    override fun onCreate (savedInstanceState: Bundle?) {
        super.onCreate (savedInstanceState)

        val onBackPressedCallback = object : OnBackPressedCallback(enabled: true) {
        override fun handleOnBackPressed() {
            val fragmentHolder = activity?.supportFragmentManager?.findFragment8yId(R.id.fragmentHolder)
            fragmentHolder?.let { fh ->
                fh.childFrogmentManager.primaryNavigotionFragment?.let{fragment ->
                    val nhf = fragment as NavHostFragment;
                    val navcontroller: NavController = nht.NavController;

                    val popSuccess = navController.popBackStack();
                    if (!popSuccess)
                        activity?supportFragmentManager?.popBackStack();
                    }
                }
            }
        }
            
        requireActivity().getOnBackPressedDispatcher().addCallback(this, onBackPressedCallback)
    }
}

Or use this code when you want to use bottom bar:

.....
override fun handleOnBackPressed() {
    var sdkPopSuccess: Boolean? = false
    if (isVisible && isResumed) {
        val navController = activity?.findNavController(R.id.nav_host_fragment);
        if (navController?.previousBackStackEntry != null) {
            sdkPopSuccess = navController.popBackStack()
        }
    }

    if (sdkPopSuccess == false) {
        // top state host app back button action
    }
}

Compose-based application

For implementation of our screens by Compose use this code:

import eu.dateio.dateiosdkmutliplatform.android.modules.offer.ui.history.dateioCashbackHistoryGraph
import eu.dateio.dateiosdkmutliplatform.android.modules.offer.ui.list.dateioOffersListGraph
import eu.dateio.dateiosdkmutliplatform.android.modules.offer.ui.search.dateioOffersSearchGraph
import eu.dateio.dateiosdkmutliplatform.android.modules.offer.ui.shops.dateioOfferShopsGraph
import eu.dateio.dateiosdkmutliplatform.android.modules.offer.ui.map.dateioOffersMapGraph

...

NavHost( 
    navController = navController, 
    startDestination = "eu.bank", 
) {

    dateioCashbackHistoryGraph(
        "offers/history/dropin",
        nestedRoutesPrefix = "eu.bank/"
    )
    dateioOffersListGraph(
        "offers/list/dropin",
        navController,
        activity = activity,
        nestedRoutesPrefix = "eu.bank/"
    )
    dateioOfferShopsGraph(
        "offers/shops/dropin",
        activity = activity,
        nestedRoutesPrefix = "eu.bank/"
    )
    dateioOffersSearchGraph(
        "offers/search/dropin",
        navController,
        activity = activity,
        nestedRoutesPrefix = "eu.bank/"
    )
    dateioOffersMapGraph(
        "offers/map/dropin",
        navController,
        activity = activity,
        nestedRoutesPrefix = "eu.bank/"
    )
...
}

Where you will just simply include SDK component (in this case Offers List screen and History screen) to your navigation graph through NavHost.

When you want to show SDK screen, call navController.navigate("offers/list/dropin") and etc.

In earlier SDK versions (< 1.7.0), the dateioOffersListGraph destination was named dateioOffersListCashbackGraph.

ProGuard rules

If you use ProGuard to make a release obfuscated build, you will need to add following ProGuard rules. Otherwise build will end up with an error.

-repackageclasses ''

-if @kotlinx.serialization.Serializable class eu.dateio.dateiosdkmutliplatform.** {
   static **$* *;
}
-keepclassmembers class <2>$<3> {
   kotlinx.serialization.KSerializer serializer(...);
}

-keepattributes *Annotation*, InnerClasses
-dontnote kotlinx.serialization.SerializationKt

-keep,includedescriptorclasses class eu.dateio.dateiosdkmutliplatform.**$$serializer { *; }
-keepclassmembers class eu.dateio.dateiosdkmutliplatform.** {
    *** Companion;
}
-keepclasseswithmembers class eu.dateio.dateiosdkmutliplatform.** {
    kotlinx.serialization.KSerializer serializer(...);
}

If you use another obfuscation tool, please let us know.

Where to head next

By this point, you will be able to run the application and navigate into screens within the SDK. Yet, it won't load any data. In the next chapters you will find how to resolve this and how to customize your UI.