Skip to content

Commit ca60a34

Browse files
committed
Blinking fixes from leonardoxh#141
1 parent 9d178b3 commit ca60a34

File tree

7 files changed

+174
-29
lines changed

7 files changed

+174
-29
lines changed

app/src/main/java/fr/groggy/racecontrol/tv/kv/credentials/SharedPreferencesF1CredentialsRepository.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,12 @@ class SharedPreferencesF1CredentialsRepository @Inject constructor(
2222
override fun find(): F1Credentials? {
2323
val login = store.findString(LOGIN_KEY)
2424
val password = store.findString(PASSWORD_KEY)
25-
val loginToken = store.findString(LOGIN_TOKEN)
26-
return if (login != null && password != null && loginToken != null) {
25+
// val loginToken = store.findString(LOGIN_TOKEN)
26+
return if (login != null && password != null /*&& loginToken != null*/) {
2727
F1Credentials(
2828
login = login,
2929
password = password,
30-
subToken = loginToken
30+
// subToken = loginToken
3131
)
3232
} else {
3333
null
@@ -53,8 +53,8 @@ class SharedPreferencesF1CredentialsRepository @Inject constructor(
5353

5454
override fun delete() =
5555
store.update {
56-
remove(LOGIN_KEY)
57-
remove(PASSWORD_KEY)
56+
//remove(LOGIN_KEY)
57+
//remove(PASSWORD_KEY)
5858
remove(LOGIN_TOKEN)
5959
}
6060
}

app/src/main/java/fr/groggy/racecontrol/tv/ui/home/HomeActivity.kt

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import fr.groggy.racecontrol.tv.f1tv.Archive
1717
import fr.groggy.racecontrol.tv.ui.season.browse.SeasonBrowseActivity
1818
import fr.groggy.racecontrol.tv.ui.settings.SettingsActivity
1919
import fr.groggy.racecontrol.tv.utils.coroutines.schedule
20+
import kotlinx.coroutines.Job
21+
import kotlinx.coroutines.launch
2022
import org.threeten.bp.Duration
2123
import org.threeten.bp.Year
2224
import javax.inject.Inject
@@ -34,6 +36,8 @@ class HomeActivity : FragmentActivity(R.layout.activity_home) {
3436
@Inject internal lateinit var seasonService: SeasonService
3537
private var teaserImage: ImageView? = null
3638

39+
private var syncJob: Job? = null
40+
3741
override fun onCreate(savedInstanceState: Bundle?) {
3842
super.onCreate(savedInstanceState)
3943

@@ -50,6 +54,7 @@ class HomeActivity : FragmentActivity(R.layout.activity_home) {
5054
findViewById<View>(R.id.settings).setOnClickListener {
5155
startActivity(SettingsActivity.intent(this))
5256
}
57+
5358
}
5459

5560
override fun onStart() {
@@ -58,7 +63,7 @@ class HomeActivity : FragmentActivity(R.layout.activity_home) {
5863

5964
teaserImage?.requestFocus()
6065

61-
lifecycleScope.launchWhenStarted {
66+
syncJob = lifecycleScope.launch {
6267
schedule(Duration.ofMinutes(1)) {
6368
Log.d("Fetching new data", "Lifecycle state is ${lifecycle.currentState}")
6469
try {
@@ -72,10 +77,16 @@ class HomeActivity : FragmentActivity(R.layout.activity_home) {
7277

7378
if (supportFragmentManager.findFragmentByTag(TAG) !is HomeFragment) {
7479
supportFragmentManager.commit {
75-
replace(R.id.fragment_container, HomeFragment())
80+
replace(R.id.fragment_container, HomeFragment(), TAG)
7681
}
7782
}
7883
}
7984
}
8085
}
86+
87+
override fun onPause() {
88+
syncJob?.cancel()
89+
syncJob = null
90+
super.onPause()
91+
}
8192
}

app/src/main/java/fr/groggy/racecontrol/tv/ui/home/HomeFragment.kt

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,14 +82,25 @@ class HomeFragment : RowsSupportFragment(), OnItemViewClickedListener {
8282
homeEntriesAdapter.add(sessionsListRow)
8383
homeEntriesAdapter.add(archivesRow)
8484
} else {
85-
/* Makes the adapter blink :| */
86-
homeEntriesAdapter.replace(0, sessionsListRow)
85+
86+
/* Compare the old list to the new to see if it needs updating */
87+
if (!hasMatchingSessions(existingListRow, sessionsListRow)) {
88+
homeEntriesAdapter.replace(0, sessionsListRow)
89+
}
8790
}
8891
} else {
8992
onEmptySeason()
9093
}
9194
}
9295

96+
private fun hasMatchingSessions(
97+
existingListRow: ListRow,
98+
sessionsListRow: ListRow
99+
) = (existingListRow.adapter.size() == sessionsListRow.adapter.size() // Do we have the same number of items
100+
|| (0 until existingListRow.adapter.size()).all { index -> // If so, do the sessions in each match in order?
101+
existingListRow.adapter[index] as Session == sessionsListRow.adapter[index] as Session
102+
})
103+
93104
private fun onEmptySeason() {
94105
/* Session wasn't started yet, just add the archive */
95106
homeEntriesAdapter.add(archivesRow)
@@ -157,4 +168,4 @@ class HomeFragment : RowsSupportFragment(), OnItemViewClickedListener {
157168
}
158169
startActivity(activity)
159170
}
160-
}
171+
}

app/src/main/java/fr/groggy/racecontrol/tv/ui/season/browse/SeasonBrowseActivity.kt

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import fr.groggy.racecontrol.tv.core.season.SeasonService
1414
import fr.groggy.racecontrol.tv.f1tv.Archive
1515
import fr.groggy.racecontrol.tv.utils.coroutines.schedule
1616
import org.threeten.bp.Duration
17+
import org.threeten.bp.Year
1718
import javax.inject.Inject
1819

1920
@AndroidEntryPoint
@@ -50,18 +51,28 @@ class SeasonBrowseActivity : FragmentActivity(R.layout.activity_season_browse) {
5051
override fun onStart() {
5152
super.onStart()
5253
lifecycleScope.launchWhenStarted {
53-
schedule(Duration.ofMinutes(1)) {
54-
Log.d("Fetching new data", "Lifecycle state is ${lifecycle.currentState}")
55-
val archive = SeasonBrowseFragment.findArchive(this@SeasonBrowseActivity)
56-
seasonService.loadSeason(archive)
57-
58-
if (supportFragmentManager.findFragmentByTag(TAG) !is SeasonBrowseFragment) {
59-
supportFragmentManager.commit {
60-
replace(R.id.fragment_container, SeasonBrowseFragment(), TAG)
61-
}
54+
val archive = SeasonBrowseFragment.findArchive(this@SeasonBrowseActivity)
55+
// Only refresh the season data if it is the current year, as older content should not be changing
56+
if (archive.year == Year.now().value) {
57+
loadSeasonContent()
58+
} else {
59+
schedule(Duration.ofMinutes(1)) {
60+
loadSeasonContent()
6261
}
6362
}
6463
}
6564
}
6665

67-
}
66+
private suspend fun loadSeasonContent() {
67+
Log.d("Fetching new data", "Lifecycle state is ${lifecycle.currentState}")
68+
val archive = SeasonBrowseFragment.findArchive(this@SeasonBrowseActivity)
69+
seasonService.loadSeason(archive)
70+
71+
if (supportFragmentManager.findFragmentByTag(TAG) !is SeasonBrowseFragment) {
72+
supportFragmentManager.commit {
73+
replace(R.id.fragment_container, SeasonBrowseFragment(), TAG)
74+
}
75+
}
76+
}
77+
78+
}

app/src/main/java/fr/groggy/racecontrol/tv/ui/season/browse/SeasonBrowseFragment.kt

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -117,9 +117,20 @@ class SeasonBrowseFragment : BrowseSupportFragment(), OnItemViewClickedListener
117117
val events = season.events
118118
.filter { it.sessions.isNotEmpty() }
119119
.map { toListRow(it, existingListRows) }
120-
eventsAdapter.setItems(events, eventListRowDiffCallback)
120+
if (existingListRows.size != events.size ||
121+
(0 until existingListRows.size).any { index -> !hasMatchingSessions(existingListRows[index], events[index]) }) {
122+
eventsAdapter.setItems(events, eventListRowDiffCallback)
123+
}
121124
}
122125

126+
private fun hasMatchingSessions(
127+
existingListRow: ListRow,
128+
sessionsListRow: ListRow
129+
) = (existingListRow.adapter.size() == sessionsListRow.adapter.size() // Do we have the same number of items
130+
|| (0 until existingListRow.adapter.size()).all { index -> // If so, do the sessions in each match in order?
131+
existingListRow.adapter[index] as Session == sessionsListRow.adapter[index] as Session
132+
})
133+
123134
private fun toListRow(event: Event, existingListRows: List<ListRow>): ListRow {
124135
val existingListRow = existingListRows.find { it.headerItem.name == event.name }
125136
val (listRow, sessionsAdapter) = if (existingListRow == null) {
@@ -130,7 +141,9 @@ class SeasonBrowseFragment : BrowseSupportFragment(), OnItemViewClickedListener
130141
val sessionsAdapter = existingListRow.adapter as ArrayObjectAdapter
131142
existingListRow to sessionsAdapter
132143
}
133-
sessionsAdapter.setItems(event.sessions, Session.diffCallback)
144+
if (existingListRow == null || !hasMatchingSessions(existingListRow, listRow)) {
145+
sessionsAdapter.setItems(event.sessions, Session.diffCallback)
146+
}
134147
return listRow
135148
}
136149

@@ -164,4 +177,4 @@ class SeasonBrowseFragment : BrowseSupportFragment(), OnItemViewClickedListener
164177
return Archive(year)
165178
}
166179
}
167-
}
180+
}

app/src/main/java/fr/groggy/racecontrol/tv/ui/signin/SignInActivity.kt

Lines changed: 99 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,17 @@ import android.util.Log
77
import android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN
88
import android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE
99
import android.webkit.CookieManager
10+
import android.webkit.JavascriptInterface
1011
import android.webkit.WebView
1112
import android.webkit.WebViewClient
1213
import androidx.activity.ComponentActivity
14+
import androidx.lifecycle.lifecycleScope
1315
import dagger.hilt.android.AndroidEntryPoint
1416
import fr.groggy.racecontrol.tv.R
1517
import fr.groggy.racecontrol.tv.core.credentials.CredentialsService
18+
import fr.groggy.racecontrol.tv.f1.F1Credentials
1619
import fr.groggy.racecontrol.tv.ui.home.HomeActivity
20+
import kotlinx.coroutines.launch
1721
import javax.inject.Inject
1822

1923
@AndroidEntryPoint
@@ -39,20 +43,112 @@ class SignInActivity : ComponentActivity() {
3943
setContentView(R.layout.activity_signin)
4044
window.setSoftInputMode(SOFT_INPUT_STATE_VISIBLE or SOFT_INPUT_ADJUST_PAN)
4145

46+
// Check if the user is already logged in using hasValidF1Credentials
47+
checkCredentialsAndNavigate()
48+
}
49+
50+
private fun checkCredentialsAndNavigate() {
51+
lifecycleScope.launch {
52+
if (credentialsService.hasValidF1Credentials()) {
53+
navigateToHome()
54+
} else {
55+
setupWebView()
56+
}
57+
}
58+
}
59+
60+
private fun setupWebView() {
4261
loginWebView.settings.domStorageEnabled = true
4362
loginWebView.settings.useWideViewPort = true
4463
loginWebView.settings.javaScriptEnabled = true
64+
65+
// Add JavaScript interface to capture credentials
66+
loginWebView.addJavascriptInterface(object {
67+
@JavascriptInterface
68+
fun saveCredentials(username: String, password: String) {
69+
lifecycleScope.launch {
70+
val credentials = F1Credentials(username, password, "")
71+
credentialsService.saveCredentials(credentials)
72+
Log.d(TAG, "Credentials saved for: $username")
73+
}
74+
}
75+
}, "Android")
76+
4577
loginWebView.webViewClient = object : WebViewClient() {
4678
override fun onPageFinished(view: WebView?, url: String?) {
4779
super.onPageFinished(view, url)
4880

49-
if (credentialsService.storeToken(CookieManager.getInstance().getCookie(url))) {
50-
startActivity(HomeActivity.intent(this@SignInActivity))
51-
finish()
81+
// Check if the user is logged in by verifying cookies
82+
val cookies = CookieManager.getInstance().getCookie(url)
83+
if (credentialsService.storeToken(cookies)) {
84+
navigateToHome()
85+
} else {
86+
autoFillCredentials()
87+
captureCredentials()
5288
}
5389
}
5490
}
91+
5592
loginWebView.loadUrl("https://account.formula1.com/#/en/login")
5693
}
5794

95+
private fun captureCredentials() {
96+
val jsCode = """
97+
javascript:(function() {
98+
// Listen for when sign in is clicked to capture the credentials
99+
var loginButton = document.querySelector('button.btn.btn-primary[type="submit"]');
100+
101+
if (loginButton && !loginButton.hasAttribute('listener')) {
102+
loginButton.setAttribute('listener', 'true');
103+
loginButton.addEventListener('click', function() {
104+
var login = document.querySelector('.txtLogin').value;
105+
var password = document.querySelector('.txtPassword').value;
106+
if (username && password) {
107+
window.Android.saveCredentials(login, password);
108+
}
109+
});
110+
}
111+
})();
112+
"""
113+
loginWebView.evaluateJavascript(jsCode, null)
114+
}
115+
116+
private fun autoFillCredentials() {
117+
lifecycleScope.launch {
118+
val savedCredentials = credentialsService.getSavedCredentials()
119+
120+
if (savedCredentials != null) {
121+
val jsCode = """
122+
javascript:(function() {
123+
// Fill in the email field
124+
var loginField = document.querySelector('.txtLogin');
125+
if (loginField) {
126+
loginField.value = '${savedCredentials.login}';
127+
}
128+
129+
// Fill in the password field
130+
var passwordField = document.querySelector('.txtPassword');
131+
if (passwordField) {
132+
passwordField.value = '${savedCredentials.password}';
133+
}
134+
135+
// Click the "Sign In" button after a 1s delay
136+
setTimeout(function() {
137+
var loginButton = document.querySelector('button.btn.btn-primary[type="submit"]');
138+
if (loginButton) {
139+
loginButton.click();
140+
}
141+
}, 1000);
142+
})();
143+
"""
144+
loginWebView.evaluateJavascript(jsCode, null)
145+
}
146+
}
147+
}
148+
149+
150+
private fun navigateToHome() {
151+
startActivity(HomeActivity.intent(this))
152+
finish()
153+
}
58154
}
Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
package fr.groggy.racecontrol.tv.utils.coroutines
22

3+
import android.util.Log
4+
import kotlinx.coroutines.CoroutineScope
35
import kotlinx.coroutines.delay
6+
import kotlinx.coroutines.isActive
47
import org.threeten.bp.Duration
58

6-
suspend fun schedule(duration: Duration, f: suspend () -> Any) {
7-
while (true) {
9+
suspend fun CoroutineScope.schedule(duration: Duration, f: suspend () -> Any) {
10+
while (isActive) {
811
f()
912
delay(duration.toMillis())
1013
}
11-
}
14+
}

0 commit comments

Comments
 (0)