This commit is contained in:
PoliEcho 2026-01-28 21:55:22 +01:00
parent 93148fcf36
commit d1314a1b32

View File

@ -50,8 +50,14 @@ import java.io.File
import java.io.FileInputStream import java.io.FileInputStream
import java.io.FileOutputStream import java.io.FileOutputStream
import java.net.URL import java.net.URL
import kotlinx.coroutines.*
import java.util.concurrent.Executors
import android.os.StrictMode
// Thread pool for tile downloading to prevent blocking UI thread
private val tileDownloadExecutor = Executors.newFixedThreadPool(4)
// MapState configuration for OpenStreetMap // MapState configuration for OpenStreetMap
// Max zoom level is 18, tile size is 256x256 // Max zoom level is 18, tile size is 256x256
// At zoom level 18, there are 2^18 = 262144 tiles in each dimension // At zoom level 18, there are 2^18 = 262144 tiles in each dimension
@ -67,6 +73,7 @@ val mapState = MapState(
* Creates a cached tile stream provider for OpenStreetMap tiles * Creates a cached tile stream provider for OpenStreetMap tiles
* Tiles are cached in the app's cache directory for offline access and faster loading * Tiles are cached in the app's cache directory for offline access and faster loading
* Cache expires after 30 days to ensure map updates are fetched * Cache expires after 30 days to ensure map updates are fetched
* Uses background thread pool to prevent ANR (Application Not Responding)
*/ */
fun createCachedTileStreamProvider(context: Context, cacheExpiryDays: Int = 30): TileStreamProvider { fun createCachedTileStreamProvider(context: Context, cacheExpiryDays: Int = 30): TileStreamProvider {
val cacheDir = File(context.cacheDir, "map_tiles") val cacheDir = File(context.cacheDir, "map_tiles")
@ -89,12 +96,16 @@ fun createCachedTileStreamProvider(context: Context, cacheExpiryDays: Int = 30):
// Use cached tile // Use cached tile
FileInputStream(tileFile) FileInputStream(tileFile)
} else { } else {
// Download tile from OpenStreetMap (either new or expired) // Download tile from OpenStreetMap in background thread
val future = tileDownloadExecutor.submit<FileInputStream?> {
try {
val url = URL("https://tile.openstreetmap.org/$zoomLvl/$col/$row.png") val url = URL("https://tile.openstreetmap.org/$zoomLvl/$col/$row.png")
val connection = url.openConnection() val connection = url.openConnection()
// Set User-Agent header as required by OSM tile usage policy // Set User-Agent header as required by OSM tile usage policy
val userAgent = "${BuildConfig.APPLICATION_ID}/${BuildConfig.VERSION_NAME} (Android) (Contact: poliecho@pupes.org)" val userAgent = "${BuildConfig.APPLICATION_ID}/${BuildConfig.VERSION_NAME} (Android) (Contact: poliecho@pupes.org)"
connection.setRequestProperty("User-Agent", userAgent) connection.setRequestProperty("User-Agent", userAgent)
connection.connectTimeout = 5000 // 5 second timeout
connection.readTimeout = 10000 // 10 second timeout
val inputStream = connection.getInputStream() val inputStream = connection.getInputStream()
@ -108,23 +119,36 @@ fun createCachedTileStreamProvider(context: Context, cacheExpiryDays: Int = 30):
// Return the cached file's input stream // Return the cached file's input stream
FileInputStream(tileFile) FileInputStream(tileFile)
} catch (e: Exception) {
e.printStackTrace()
null
}
}
// Wait for download with timeout
try {
future.get() ?: run {
// If download failed but we have a cached tile (even if expired), use it
if (tileFile.exists()) {
FileInputStream(tileFile)
} else {
null
}
} }
} catch (e: Exception) { } catch (e: Exception) {
// If download fails but we have a cached tile (even if expired), use it // If download timed out or failed, try using expired cache
val tileFile = File(cacheDir, "tile_${zoomLvl}_${col}_${row}.png")
if (tileFile.exists()) { if (tileFile.exists()) {
try {
FileInputStream(tileFile) FileInputStream(tileFile)
} catch (fallbackError: Exception) {
e.printStackTrace()
null
}
} else { } else {
e.printStackTrace()
null null
} }
} }
} }
} catch (e: Exception) {
e.printStackTrace()
null
}
}
} }
@ -132,6 +156,21 @@ class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
// Configure StrictMode to detect threading violations (debug mode only)
if (BuildConfig.DEBUG) {
StrictMode.setThreadPolicy(
StrictMode.ThreadPolicy.Builder()
.detectAll()
.penaltyLog() // Log violations instead of crashing
.build()
)
StrictMode.setVmPolicy(
StrictMode.VmPolicy.Builder()
.detectAll()
.penaltyLog()
.build()
)
}
setContent { setContent {
MaterialTheme { MaterialTheme {