Enable Zendrive SDK
This section describes how to enable Zendrive SDK for the IQL program on Android devices. This section comprises the following topics:
1.0. Add Zendrive SDK Dependencies
Use the following code snippet to update the gradle
file of the project or module.
// build.gradle
dependencies {
...
api 'com.zendrive.sdk.android:ZendriveSDK:9.1.0'
...
}
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")
}
}
}
}
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:
Integrate the IQL missing heartbeat webhook with your application, and send a silent push notification to send periodic heartbeat updates to Zendrive SDK.
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.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)