Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

integrate kronos-android ntp clock library into project #150

Open
wants to merge 10 commits into
base: develop
Choose a base branch
from

Conversation

rasprague
Copy link
Collaborator

@rasprague rasprague commented Sep 2, 2020

integrate ntp clock library into app, make "Share a Positive Diagnosis" date picker use ntp time

add Kronos ntp clock lib

change DiagnosisKeysToken to use ntp time
make app context injectable, make NTPTime use it

removed app context access hack from BaseCovidWatchApplicaiton
@rasprague
Copy link
Collaborator Author

Copy link
Member

@Apisov Apisov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's nice start but according to README from https://github.com/lyft/Kronos-Android
we need to init this lib in the onCreate of the Application class. So when the wrappers is done we would need to inject it in the Application class to call syncInBackground method.

Also, to fix the problem we need to limit the date pickers on the verification screen to real dates.

So we would to rework it here:

val twoWeeksAgo = LocalDate.now().plusDays(-14).toInstant()
.toEpochMilli()
// Day in the past or 14 days back
val fromWhen = dayInPast ?: twoWeeksAgo
val untilNow = LocalDate.now().toInstant().toEpochMilli()

import org.koin.core.KoinComponent
import org.koin.core.inject

class NTPTime {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A separate wrapper is nice but having a static reference to a context is a bad thing that leads to memory leaks.

In Koin there is androidApplication() that returns applicationContext, have a look at any wrapper that use androidApplication() in appModule. For example Notifications -- we need something like that and then we will be injecting NtpTime anywhere we need it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, to make it clear -- I suggest to rename this class to NtpTime because of the guidelines

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed static reference to context, changed so it's passed into the constructor and never stored

renamed file

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made NtpClock injectable. Injected into BaseCovidWatchApplication and started properly from onCreate()

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

modified VerifyPositiveDiagnosisFragment to use NtpTime for date pickers

import org.covidwatch.android.data.converter.ExposureConfigurationConverter

@Entity(tableName = "diagnosis_keys_token")
@TypeConverters(ExposureConfigurationConverter::class)
data class DiagnosisKeysToken(
@PrimaryKey val token: String,
val exposureConfiguration: CovidExposureConfiguration,
val providedTime: Long = System.currentTimeMillis(),
val providedTime: Long = NTPTime.currentTimeMillis(),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We would to remove default value for this property and use injected NtpTime to use a proper time. But having here a proper world time is not critical, it's not even used anywhere for now.

So we could leave the System time here because this class might be deleted in the near future when we update EN version.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

change reverted back to System call

import android.content.ContextWrapper

// exists solely to make the Android applicationContext injectable
class AppContext(context : Context) : ContextWrapper(context) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove it please since we won't use the implementation of NTPTime with companion object.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

file removed

…ment to use NtpTime for date pickers

refactor so NtpTime is injectable, change VerifyPositiveDiagnosisFragment to use NtpTime for date pickers

remove AppContext class

properly start ntpTime in Application onCreate()
remove unused function from NtpTime
remove unused import
removed trailing newline (the only change to this file) to remove from this commit
)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like the github editor enforces the final newline haha

remove DiagnonsisKeysToken.kt from commit
Copy link
Member

@Apisov Apisov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for changes now it's better but still some issues need to be resolved.

I double checked the library and it's quite old but the version of it still still super alpha which suggest Lyft doesn't really support it. There are a few issues in their repo but we don't really need a high accuracy from the lib -- we need good enough to sync the days not minutes.
Another old lib that I remember is https://github.com/instacart/truetime-android
but it looks even more abandoned so no big difference I guess.

Also, I realized that introducing NTP time, in the ideal case, would need more changes than here...
We probably need to change most of Instant.now() or any now() requests but this is too big changes that would need to replace all java.Time API. We need to think on this before changing everything.

app/build.gradle Outdated
@@ -288,6 +288,10 @@ dependencies {
testImplementation "io.mockk:mockk:1.10.0"
// Koin
testImplementation "org.koin:koin-test:$koin_version"

// Kronos ntp clock
def kronos_latest_version = "0.0.1-alpha09"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Last dependencies are preserved for the test flavored so let's move this before the test dependencies.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dependency line moved

fun syncInBackground() = kronosClock.syncInBackground()

fun nowAsLocalDate() : LocalDate =
Instant.ofEpochMilli(currentTimeMillis()).atZone(ZoneId.systemDefault()).toLocalDate()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ZoneId.systemDefault() introduces the same problem that we tried to eliminate -- it tights the time of the app to the users' settings. I was able to reproduce a one day shift with a timezone that have the next day relatively to mine. It's not that big as before but still a problem.

One way to resolve is just to remove atZone(ZoneId.systemDefault()).

Copy link
Collaborator Author

@rasprague rasprague Sep 4, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed notion of LocalDate from NtpTime to avoid confusion / simplify the code. Sticking to returning Instants instead

@@ -119,13 +121,13 @@ class VerifyPositiveDiagnosisFragment :
val builder = MaterialDatePicker.Builder.datePicker()
val constraints = CalendarConstraints.Builder()

val twoWeeksAgo = LocalDate.now().plusDays(-14).toInstant()
val twoWeeksAgo = ntpTime.nowAsLocalDate().plusDays(-14).toInstant()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a problem with this solution.
Even if the dates that can be selected are correct the date picker shows the current month from the device's settings.

I tried to use selection to current day when we open the dialog but it doesn't help.
@rasprague Could you try to investigate if that somehow possible to fix with MaterialDatePicker?

Copy link
Collaborator Author

@rasprague rasprague Sep 4, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

adding a setOpenAt() to the date picker constraints fixes this

…nstant

removed "LocalTime" from NtpTime

add plus/minus days extenstion to Instant
force date picker to open on today's month
Copy link
Member

@Apisov Apisov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Almost done, just a few suggestions. But the concern about this PR is still valid -- should we use this NTP adjusted time across the whole app or only in the date pickers.

app/build.gradle Outdated
apply plugin: 'com.google.gms.google-services'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use the same formatter from AS in the future to avoid this kind of changes.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed


import java.time.Instant

// 1d * (24h/d) * (60m/h) * (60s/m)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Creating extensions is nice but why then don't use existing API inside of them?
Like this:

minus(15, ChronoUnit.DAYS)
add(Duration.ofDays(15))

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed

constraints.setValidator(BaseDateValidator { it in fromWhen..untilNow })
constraints
.setValidator(BaseDateValidator { it in fromWhen..untilNow })
.setOpenAt(untilNow)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice 👍


// Day in the past or 14 days back
val fromWhen = dayInPast ?: twoWeeksAgo

val untilNow = LocalDate.now().toInstant().toEpochMilli()
val untilNow = ntpTime.nowAsInstant().toEpochMilli()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like the conversion is redundant because we use the milliseconds anyway. We could make NtpTime.currentTimeMillis public and use it directly here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

calling currentTimeMillis() directly

minor changes / cleanup
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants