Enable Zendrive SDK

This section describes how to enable Zendrive SDK for the IQL program on Android devices. This section comprises the following topics:

Enable Zendrive SDK

Enable Zendrive SDK

1.0. Add Zendrive SDK Dependencies

Use the following code snippet to update the gradle file of the project or module.

2.0. Integrate Zendrive SDK

The following sections describe how to integrate Zendrive SDK with your application.

2.1. Set Up Zendrive SDK

Use the following code snippet to define a function to set up Zendrive SDK:

class ZendriveManager(
    database: IQLDatabase,
    private val sdkKey: String,
    private val loginRepository: LoginRepository,
    private val tripRepository: TripRepository
) {
    ...
    ...
    fun setup(context: Context) {
        runBlocking {
            val loginRepository = ZWLCore.loginRepository()
            val emailID = loginRepository.retrieveLoginRequestEmail(context)
            val driverID = loginRepository.getDriverId()
            if (emailID != null && driverID != null) {
                val configuration =
                    ZendriveConfiguration(
                        sdkKey,
                        driverID,
                        ZendriveDriveDetectionMode.AUTO_ON
                    )

                val driverAttributes = ZendriveDriverAttributes().apply {
                    alias = emailID
                }
                configuration.driverAttributes = driverAttributes
                Zendrive.setup(
                    context,
                    configuration,
                    ZendriveReceiver::class.java,
                    NotificationProvider::class.java
                ) {
                    if (it.isSuccess) {
                        Timber.d("Zendrive setup succeeded")
                    } else {
                        Timber.d("Zendrive setup failed errorCode: ${it.errorCode} errorMessage: ${it.errorMessage}")
                    }
                }
            } else {
                Timber.d("Skipping zendrive initialization as email id: $emailID or driver id: $driverID does not exist")
            }
        }
    }
    ...
    ...
}

class ZWLCore private constructor(val context: Context) {
    ...
    ...
    private lateinit var zendriveManager: ZendriveManager
    ...
    ...
    companion object {
        ...
        ...
        fun setupZendrive() {
            val context = INSTANCE!!.context
            if (Zendrive.isSDKSetup(context).not()) {
                INSTANCE!!.zendriveManager.setup(context)
            } else {
                Timber.d("ZendriveSDK already setup")
            }
        }
    }
}

When thesetupZendrive() method is called, it will create a driver entry in the Zendrive backend, if one doesn’t already exist.

2.2. Set Up Zendrive SDK At Opt-in Stage

Use the following code snippet to set up Zendrive SDK when the user opts in to the IQL program.

// Setup ZendriveSDK when user opts in
class ExplainerFragment : Fragment(R.layout.fragment_explainer) {
    private lateinit var binding: FragmentExplainerBinding
    private val viewModel: OnboardingViewModel by activityViewModels()

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        binding = FragmentExplainerBinding.bind(view)
        ...
        binding.apply {
            this.materialCardView.viewFaq.setOnClickListener {
                ...
            }
            this.textTerms.setOnClickListener {
                ...
            }
            // When the user opts in setup Zendrive
            activateZenSaver.setOnClickListener {
                val currentDateString =
                    getActivatedDateFormatString(Date(Calendar.getInstance().timeInMillis))
                saveActivatedDate(currentDateString)
                ZWLCore.setupZendrive()
                viewModel.navigateTo(NavigationIntention.EnterZipCode())
            }
        }
    }
    ...
    ...
}

2.3. Set Up Zendrive SDK At Application Launch Stage

Use the following code snippet to set up Zendrive SDK at the application launch stage:

// Setup ZendriveSDK on app launch
class MainApplication : Application() {

    private var numRunningActivities = 0

    override fun onCreate() {
        super.onCreate()
        initZendriveSDK()
        ...
        ...
    }

    fun initZendriveSDK() {
        var level = HttpLoggingInterceptor.Level.NONE
        if (needsLogging()) {
            level = HttpLoggingInterceptor.Level.BODY
            Timber.plant(Timber.DebugTree())
        }

        ZWLCore.init(
            this,
            ZWLConfig(
                appName = applicationContext.getString(R.string.app_name),
                versionName = BuildConfig.VERSION_NAME,
                versionCode = BuildConfig.VERSION_CODE,
                sdkKey = BuildConfig.ZENDRIVE_SDK_KEY,
                apiBaseUrl = BuildConfig.API_BASE_URL,
                frConfig = FRConfig(
                    BuildConfig.FR_DYNAMIC_LINKS_DOMAIN,
                    BuildConfig.FR_APP_DOMAIN
                ),
                sharedPreferences = getSharedPreferences(temporary_shared_preference, MODE_PRIVATE),
                okHttpDebugLevel = level,
                onBoardingActivityIntentClass = OnboardingActivity::class.java
            ),
            HomeActivity::class.java
        ) {
            Timber.d("401 Response, unauthorized client.")
        }

        ZWLCore.setupZendrive()
        ...
        ...
    }
    ...
    ...
}

2.4. Integrate Zendrive SDK Callbacks

Integrate Zendrive SDK callbacks to ensure better user experience. This is especially useful while handling errors and trip detection updates. For example, you can use SDK callbacks to display notifications during trip start, or display SDK errors in the application interface.

// Integrate ZendriveSDK callbacks
class ZendriveReceiver : ZendriveBroadcastReceiver() {
    override fun onDriveStart(context: Context?, startInfo: DriveStartInfo?) {
        Timber.d("onDriveStart $startInfo")
    }

    override fun onZendriveSettingsConfigChanged(
        context: Context?,
        errorsFound: Boolean,
        warningsFound: Boolean
    ) {
        if (errorsFound || warningsFound) {
            ZWLCore.zendriveManager()
                .onZendriveSettingsConfigChanged(context, errorsFound, warningsFound)
        } else {
            NotificationProvider.dismissPermissionErrorNotifications()
        }
    }

    override fun onAccident(context: Context?, accidentInfo: AccidentInfo?) {
        Timber.d("onAccident $accidentInfo")
    }

    override fun onDriveAnalyzed(context: Context?, analyzedDriveInfo: AnalyzedDriveInfo?) {
        Timber.d("onDriveAnalyzed $analyzedDriveInfo")
    }

    override fun onDriveResume(context: Context?, resumeInfo: DriveResumeInfo?) {
        Timber.d("onDriveResume $resumeInfo")
    }

    override fun onDriveEnd(context: Context?, estimatedDriveInfo: EstimatedDriveInfo?) {
        Timber.d("onDriveEnd $estimatedDriveInfo")
    }
}

2.5. Integrate Zendrive SDK Heartbeat Service

Integrate the Zendrive SDK heartbeat service to resolve setup and permission issues in the backend.

To do this, you need to:

  1. Integrate the IQL missing heartbeat webhook with your application, and send a silent push notification to send periodic heartbeat updates to Zendrive SDK.

  2. Create a new endpoint similar to driver.views.heartbeat_webhook and configure it with the IQL backend. The IQL backend will call this endpoint for each driver for whom the heartbeat data is not received for a given number of hours.

  3. Using the driver details, send a silent push notification to the user's device to wake up the application. Zendrive SDK automatically collects heartbeat information once the application is woken up.

@api_view(['POST'])
def heartbeat_webhook(request):
   try:
       driver_id = request.data['info']['driver_id']
       driver = User.objects.get(driver_id=driver_id)
       push_service = PushService()
       push_service.send_heartbeat_push(driver)

       return Response(status=status.HTTP_200_OK)

   except User.DoesNotExist:
       logger.error(
           "Missing heartbeat web-hook was sent for a non-existent user " +
           driver_id)
       return Response(status=status.HTTP_400_BAD_REQUEST)
   except PushService.Error as e:
       logger.exception(e)
       return Response(status=status.HTTP_424_FAILED_DEPENDENCY)