Repository: corona-warn-app/cwa-app-android
File: Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/detectiontracker/ExposureDetectionTrackerStorage.kt (Kotlin)

Code before:
) {
    private val gson by lazy {
        baseGson.newBuilder()
            .registerTypeAdapter(Instant::class.java, Instant::class.getDefaultGsonTypeAdapter())
            .create()
    }

    private val mutex = Mutex()
    private val storageDir by lazy {
        File(context.filesDir, "calcuation_tracker").apply {
            if (mkdirs()) Timber.v("Created %s", this)
        }
    }
    private val storageFile by lazy { File(storageDir, "calculations.json") }
    private var lastCalculationData: Map<String, TrackedExposureDetection>? = null

    init {
        Timber.v("init()")
    }

    suspend fun load(): Map<String, TrackedExposureDetection> = mutex.withLock {
        loadTrackingData().getOrNull() ?: loadLegacyTrackingData().getOrNull() ?: emptyMap()
    }

    private fun loadTrackingData() = runCatching {
        baseGson.parseTracking()
    }.onFailure {
        Timber.e(it, "loadTrackingData() failed to load tracked detections.")
    }

    private fun loadLegacyTrackingData() = runCatching {
        gson.parseTracking()
    }.onFailure {
        if (storageFile.delete()) Timber.w("Storage file was deleted.")
        Timber.e(it, "loadLegacyTrackingData() failed to load tracked detections.")
    }

    private fun Gson.parseTracking() =
        fromJson<Map<String, TrackedExposureDetection>>(storageFile)?.also {
            require(it.size >= 0)
            Timber.v("Loaded detection data: %s", it)
            lastCalculationData = it
        }

    suspend fun save(data: Map<String, TrackedExposureDetection>) = mutex.withLock {
        if (lastCalculationData == data) {
            Timber.v("Data didn't change, skipping save.")
            return@withLock
        }
        Timber.v("Storing detection data: %s", data)
        try {

Review comment (suggestion):
On my Pixel 6, Android 13, after installing this pr branch, baseGson parsing works here. 

calculations.json contains:
`{"40625f24-bf39-49c2-9124-326d8fdf636b":{"enfVersion":"V2_WINDOW_MODE","finishedAt":{"iMillis":1665489480787},"identifier":"40625f24-bf39-49c2-9124-326d8fdf636b","result":"NO_MATCHES","startedAt":{"iMillis":1665489480225}}}`

Parsed with the adapter registered in `SerializationModule` 
`reader.nextLong()` returns 0 which is  converted to `1970-01-01T00:00:00Z` resulting in:
![Screenshot_20221011-115614](https://user-images.githubusercontent.com/22813752/195093790-ea85a144-a7be-41a2-919d-1ccc585ff00f.png)


Code after:
) {
    private val gson by lazy {
        baseGson.newBuilder()
            .registerTypeAdapter(Instant::class.java, LegacyInstantDeserializer())
            .create()
    }

    private val mutex = Mutex()
    private val storageDir by lazy {
        File(context.filesDir, "calcuation_tracker").apply {
            if (mkdirs()) Timber.v("Created %s", this)
        }
    }
    private val storageFile by lazy { File(storageDir, "calculations.json") }
    private var lastCalculationData: Map<String, TrackedExposureDetection>? = null

    init {
        Timber.v("init()")
    }

    suspend fun load(): Map<String, TrackedExposureDetection> = mutex.withLock {
        loadTrackingData().getOrNull() ?: loadLegacyTrackingData().getOrNull() ?: emptyMap()
    }

    private fun loadTrackingData() = runCatching {
        baseGson.parseTracking()?.also {
            lastCalculationData = it
        }
    }.onFailure {
        Timber.d(it, "loadTrackingData() failed to load tracked detections.")
    }

    private fun loadLegacyTrackingData() = runCatching {
        gson.parseTracking()
    }.onFailure {
        if (storageFile.delete()) Timber.w("Storage file was deleted.")
        Timber.d(it, "loadLegacyTrackingData() failed to load tracked detections.")
    }

    private fun Gson.parseTracking() =
        fromJson<Map<String, TrackedExposureDetection>>(storageFile)?.also {
            require(it.size >= 0)
            Timber.v("Loaded detection data: %s", it)
        }

    suspend fun save(data: Map<String, TrackedExposureDetection>) = mutex.withLock {
        if (lastCalculationData == data) {
            Timber.v("Data didn't change, skipping save.")
            return@withLock
        }
        Timber.v("Storing detection data: %s", data)