Skip to content

Commit 761d93b

Browse files
fix: serve cached images when device has no internet (utkarshdalal#635)
1 parent 08cc7a5 commit 761d93b

1 file changed

Lines changed: 59 additions & 1 deletion

File tree

app/src/main/java/app/gamenative/MainActivity.kt

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import androidx.compose.ui.platform.LocalContext
3030
import coil.ImageLoader
3131
import coil.disk.DiskCache
3232
import coil.memory.MemoryCache
33+
import coil.intercept.Interceptor
3334
import coil.request.CachePolicy
3435
import app.gamenative.events.AndroidEvent
3536
import app.gamenative.service.SteamService
@@ -48,15 +49,50 @@ import com.winlator.core.AppUtils
4849
import com.winlator.inputcontrols.ControllerManager
4950
import dagger.hilt.android.AndroidEntryPoint
5051
import kotlinx.coroutines.launch
52+
import java.util.Collections
5153
import java.util.EnumSet
54+
import java.util.concurrent.atomic.AtomicBoolean
55+
import android.net.ConnectivityManager
56+
import android.net.Network
57+
import android.net.NetworkCapabilities
58+
import android.net.NetworkRequest
5259
import kotlin.math.abs
5360
import okio.Path.Companion.toOkioPath
5461
import timber.log.Timber
5562

5663
@AndroidEntryPoint
5764
class MainActivity : ComponentActivity() {
5865

66+
// ignore VPN and mesh transports — they don't reliably indicate internet
67+
private val networkCallback = object : ConnectivityManager.NetworkCallback() {
68+
private val validated = Collections.synchronizedSet(mutableSetOf<Network>())
69+
70+
private fun skip(caps: NetworkCapabilities) =
71+
caps.hasTransport(NetworkCapabilities.TRANSPORT_VPN) ||
72+
caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI_AWARE) ||
73+
caps.hasTransport(NetworkCapabilities.TRANSPORT_LOWPAN)
74+
75+
override fun onCapabilitiesChanged(network: Network, caps: NetworkCapabilities) {
76+
if (skip(caps)) return
77+
if (caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)) {
78+
validated.add(network)
79+
} else {
80+
validated.remove(network)
81+
}
82+
_hasInternet.set(validated.isNotEmpty())
83+
}
84+
85+
override fun onLost(network: Network) {
86+
validated.remove(network)
87+
_hasInternet.set(validated.isNotEmpty())
88+
}
89+
}
90+
5991
companion object {
92+
// updated by NetworkCallback, read by Coil interceptor
93+
private val _hasInternet = AtomicBoolean(false)
94+
val hasInternet: Boolean get() = _hasInternet.get()
95+
6096
private var totalIndex = 0
6197

6298
private var currentOrientationChangeValue: Int = 0
@@ -141,6 +177,15 @@ class MainActivity : ComponentActivity() {
141177

142178
handleLaunchIntent(intent)
143179

180+
// track real network state (callback filters out VPN/mesh transports)
181+
val cm = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
182+
cm.registerNetworkCallback(
183+
NetworkRequest.Builder()
184+
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
185+
.build(),
186+
networkCallback,
187+
)
188+
144189
// Prevent device from sleeping while app is open
145190
AppUtils.keepScreenOn(this)
146191

@@ -184,10 +229,20 @@ class MainActivity : ComponentActivity() {
184229
.diskCachePolicy(CachePolicy.ENABLED)
185230
.diskCache(diskCache)
186231
.components {
232+
// serve cached images when device has no internet
233+
add(Interceptor { chain ->
234+
val request = if (!hasInternet) {
235+
chain.request.newBuilder()
236+
.networkCachePolicy(CachePolicy.DISABLED)
237+
.build()
238+
} else {
239+
chain.request
240+
}
241+
chain.proceed(request)
242+
})
187243
add(IconDecoder.Factory())
188244
add(AnimatedPngDecoder.Factory())
189245
}
190-
// .logger(logger)
191246
.build()
192247
}
193248

@@ -238,6 +293,9 @@ class MainActivity : ComponentActivity() {
238293
override fun onDestroy() {
239294
super.onDestroy()
240295

296+
val cm = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
297+
cm.unregisterNetworkCallback(networkCallback)
298+
241299
PluviaApp.events.emit(AndroidEvent.ActivityDestroyed)
242300

243301
PluviaApp.events.off<AndroidEvent.SetSystemUIVisibility, Unit>(onSetSystemUi)

0 commit comments

Comments
 (0)