Kynetics/uf-android-client

View on GitHub
uf-client-service/src/main/kotlin/com/kynetics/uf/android/configuration/TargetAttributesHandlerImpl.kt

Summary

Maintainability
A
0 mins
Test Coverage
/*
 * Copyright © 2017-2023  Kynetics  LLC
 *
 * This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License 2.0
 * which is available at https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 */

package com.kynetics.uf.android.configuration

import android.os.Build
import com.kynetics.uf.android.BuildConfig
import com.kynetics.uf.android.api.UFServiceConfigurationV2
import com.kynetics.uf.android.content.SharedPreferencesWithObject
import com.kynetics.uf.android.md5.toMD5
import com.kynetics.uf.android.update.system.SystemUpdateType
import org.eclipse.hara.ddiclient.api.ConfigDataProvider
import java.text.SimpleDateFormat
import java.time.OffsetDateTime
import java.util.*
import kotlin.time.DurationUnit
import kotlin.time.toDuration

class TargetAttributesHandlerImpl(
    private val sharedPreferences: SharedPreferencesWithObject,
    private val keys: SharedPreferencesKeys,
    private val timeWindows: UFServiceConfigurationV2.TimeWindows,
): TargetAttributesHandler {
    private val systemUpdateType: SystemUpdateType = SystemUpdateType.getSystemUpdateType()

    override fun saveConfigurationTargetAttributes(targetAttributes: Map<String, String>) =
        saveMap(keys.sharedPreferencesTargetAttributesFromConfiguration, targetAttributes)


    override fun saveAddTargetAttributes(targetAttributes: Map<String, String>, merge:Boolean){
        if(merge){
            getAddTargetAttributes().apply {
                putAll(targetAttributes)
            }
        } else {
            targetAttributes
        }.let {
            saveMap(keys.sharedPreferencesAddTargetAttributes, it)
        }
    }

    override fun getConfigurationTargetAttributes(): MutableMap<String, String> =
        getMap (keys.sharedPreferencesTargetAttributesFromConfiguration)
            .toMutableMap()

    override fun newConfigDataProvider(): ConfigDataProvider {
        return object : ConfigDataProvider {
            override fun configData(): Map<String, String> {
                return decorateTargetAttribute()
            }

            override fun isUpdated(): Boolean {
                val md5 = decorateTargetAttribute().toMD5()
                return md5 == sharedPreferences.getString(LAST_TARGET_ATTRIBUTES_MD5_SENT_KEY, "")
            }

            override fun onConfigDataUpdate() {
                val md5 = decorateTargetAttribute().toMD5()
                sharedPreferences.edit().putString(LAST_TARGET_ATTRIBUTES_MD5_SENT_KEY, md5)
                    .apply()
            }
        }
    }

    private fun saveMap(key:String, map:Map<String,String>) =
        sharedPreferences.putAndCommitObject(key, map)

    private fun getMap(key:String):MutableMap<String,String>{
        val map: MutableMap<String, String>? = sharedPreferences.getObject(key)
        return map ?: mutableMapOf()
    }
    private fun mergeThirdPartyAppTargetAttributes():MutableMap<String,String>{
        return getConfigurationTargetAttributes()
            .apply {
                putAll(getAddTargetAttributes())
            }.filterNot { (key,_) -> key.startsWith("UF_", true)}
            .toMutableMap()
    }

    private fun getAddTargetAttributes():MutableMap<String, String> =
        getMap (keys.sharedPreferencesAddTargetAttributes)

    private fun decorateTargetAttribute(): Map<String, String> {
        val targetAttributes = mergeThirdPartyAppTargetAttributes()
        targetAttributes[CLIENT_TYPE_TARGET_TOKEN_KEY] = "Android"
        targetAttributes[CLIENT_VERSION_TARGET_ATTRIBUTE_KEY] = BuildConfig.VERSION_NAME
        targetAttributes[CLIENT_VERSION_CODE_ATTRIBUTE_KEY] = BuildConfig.VERSION_CODE.toString()
        val buildDate = Date(Build.TIME)
        val dateFormat = SimpleDateFormat("yyyyMMdd", Locale.UK)
        targetAttributes[ANDROID_BUILD_DATE_TARGET_ATTRIBUTE_KEY] = dateFormat.format(buildDate)
        targetAttributes[ANDROID_BUILD_TYPE_TARGET_ATTRIBUTE_KEY] = Build.TYPE
        targetAttributes[ANDROID_FINGERPRINT_TARGET_ATTRIBUTE_KEY] = Build.FINGERPRINT
        targetAttributes[ANDROID_KEYS_TARGET_ATTRIBUTE_KEY] = Build.TAGS
        targetAttributes[ANDROID_VERSION_TARGET_ATTRIBUTE_KEY] = Build.VERSION.RELEASE
        targetAttributes[DEVICE_NAME_TARGET_ATTRIBUTE_KEY] = Build.DEVICE
        targetAttributes[SYSTEM_UPDATE_TYPE] = systemUpdateType.name
        targetAttributes[DEVICE_TIME_ZONE_TARGET_ATTRIBUTE_KEY] = TimeZone.getDefault().id
        targetAttributes[DEVICE_UTC_TARGET_ATTRIBUTE_KEY] = "${OffsetDateTime.now().offset}"
        targetAttributes[TIME_WINDOWS_CRON_EXPRESSION_ATTRIBUTE_KEY] = timeWindows.cronExpression
        targetAttributes[TIME_WINDOWS_DURATION_ATTRIBUTE_KEY] = timeWindows.duration.toDuration(
            DurationUnit.SECONDS
        ).toString()
        return targetAttributes
    }

    private fun Map<String, String>.toMD5(): String {
        return entries
            .sortedBy { it.key }
            .joinToString("-") { "${it.key}_${it.value}" }
            .toMD5()
    }

    companion object {
        private const val LAST_TARGET_ATTRIBUTES_MD5_SENT_KEY = "LAST_TARGET_ATTRIBUTES_MD5_SET_KEY"
        private const val CLIENT_VERSION_TARGET_ATTRIBUTE_KEY = "client_version"
        private const val CLIENT_VERSION_CODE_ATTRIBUTE_KEY = "client_version_code"
        private const val ANDROID_BUILD_DATE_TARGET_ATTRIBUTE_KEY = "android_build_date"
        private const val ANDROID_BUILD_TYPE_TARGET_ATTRIBUTE_KEY = "android_build_type"
        private const val ANDROID_FINGERPRINT_TARGET_ATTRIBUTE_KEY = "android_fingerprint"
        private const val ANDROID_KEYS_TARGET_ATTRIBUTE_KEY = "android_keys"
        private const val ANDROID_VERSION_TARGET_ATTRIBUTE_KEY = "android_version"
        private const val DEVICE_NAME_TARGET_ATTRIBUTE_KEY = "device_name"
        private const val SYSTEM_UPDATE_TYPE = "system_update_type"
        private const val DEVICE_UTC_TARGET_ATTRIBUTE_KEY = "utc_offset"
        private const val DEVICE_TIME_ZONE_TARGET_ATTRIBUTE_KEY = "time_zone"
        private const val TIME_WINDOWS_CRON_EXPRESSION_ATTRIBUTE_KEY = "time_windows_cron_expression"
        private const val TIME_WINDOWS_DURATION_ATTRIBUTE_KEY = "time_windows_duration"
        private const val CLIENT_TYPE_TARGET_TOKEN_KEY = "client"
    }
}