Fixed ANR caused by too many saved games

This commit is contained in:
Yair Morgenstern 2020-12-10 10:42:02 +02:00
parent 4578994adf
commit ab548dda54
2 changed files with 109 additions and 97 deletions

View File

@ -1,10 +1,13 @@
package com.unciv.ui.saves package com.unciv.ui.saves
import com.badlogic.gdx.Gdx import com.badlogic.gdx.Gdx
import com.badlogic.gdx.files.FileHandle
import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.scenes.scene2d.actions.Actions
import com.badlogic.gdx.scenes.scene2d.ui.CheckBox import com.badlogic.gdx.scenes.scene2d.ui.CheckBox
import com.badlogic.gdx.scenes.scene2d.ui.Table import com.badlogic.gdx.scenes.scene2d.ui.Table
import com.badlogic.gdx.scenes.scene2d.ui.TextButton import com.badlogic.gdx.scenes.scene2d.ui.TextButton
import com.badlogic.gdx.utils.Align
import com.unciv.UncivGame import com.unciv.UncivGame
import com.unciv.logic.GameSaver import com.unciv.logic.GameSaver
import com.unciv.logic.UncivShowableException import com.unciv.logic.UncivShowableException
@ -129,10 +132,31 @@ class LoadGameScreen(previousScreen:CameraStageBaseScreen) : PickerScreen() {
private fun updateLoadableGames(showAutosaves:Boolean) { private fun updateLoadableGames(showAutosaves:Boolean) {
saveTable.clear() saveTable.clear()
for (save in GameSaver.getSaves().sortedByDescending { it.lastModified() }) {
val loadImage =ImageGetter.getImage("OtherIcons/Load")
loadImage.setSize(50f,50f) // So the origin sets correctly
loadImage.setOrigin(Align.center)
loadImage.addAction(Actions.rotateBy(360f, 2f))
saveTable.add(loadImage).size(50f)
thread { // Apparently, even jut getting the list of saves can cause ANRs -
// not sure how many saves these guys had but Google Play reports this to have happened hundreds of times
// .toList() because otherwise the lastModified will only be checked inside the postRunnable
val saves = GameSaver.getSaves().sortedByDescending { it.lastModified() }.toList()
Gdx.app.postRunnable {
saveTable.clear()
for (save in saves) {
if (save.name().startsWith("Autosave") && !showAutosaves) continue if (save.name().startsWith("Autosave") && !showAutosaves) continue
val textButton = TextButton(save.name(), skin) val textButton = TextButton(save.name(), skin)
textButton.onClick { textButton.onClick { onSaveSelected(save) }
saveTable.add(textButton).pad(5f).row()
}
}
}
}
private fun onSaveSelected(save: FileHandle) {
selectedSave = save.name() selectedSave = save.name()
copySavedGameToClipboardButton.enable() copySavedGameToClipboardButton.enable()
var textToSet = save.name() var textToSet = save.name()
@ -159,9 +183,6 @@ class LoadGameScreen(previousScreen:CameraStageBaseScreen) : PickerScreen() {
} }
} }
} }
saveTable.add(textButton).pad(5f).row()
}
}
} }

View File

@ -36,9 +36,9 @@ object ImageGetter {
// We then shove all the drawables into a hashmap, because the atlas specifically tells us // We then shove all the drawables into a hashmap, because the atlas specifically tells us
// that the search on it is inefficient // that the search on it is inefficient
val textureRegionDrawables = HashMap<String,TextureRegionDrawable>() val textureRegionDrawables = HashMap<String, TextureRegionDrawable>()
init{ init {
reload() reload()
} }
@ -61,7 +61,7 @@ object ImageGetter {
val tempAtlas = TextureAtlas("$singleImagesFolder.atlas") val tempAtlas = TextureAtlas("$singleImagesFolder.atlas")
for (region in tempAtlas.regions) { for (region in tempAtlas.regions) {
val drawable = TextureRegionDrawable(region) val drawable = TextureRegionDrawable(region)
textureRegionDrawables["$singleImagesFolder/"+region.name] = drawable textureRegionDrawables["$singleImagesFolder/" + region.name] = drawable
} }
} }
@ -77,7 +77,7 @@ object ImageGetter {
// These are from the mods // These are from the mods
for (mod in ruleset.mods) { for (mod in ruleset.mods) {
val modAtlasFile = Gdx.files.local("mods/$mod/game.atlas") val modAtlasFile = Gdx.files.local("mods/$mod/game.atlas")
if (modAtlasFile.exists()) { if (!modAtlasFile.exists()) continue
val modAtlas = TextureAtlas(modAtlasFile) val modAtlas = TextureAtlas(modAtlasFile)
for (region in modAtlas.regions) { for (region in modAtlas.regions) {
val drawable = TextureRegionDrawable(region) val drawable = TextureRegionDrawable(region)
@ -85,7 +85,6 @@ object ImageGetter {
} }
} }
} }
}
/** /**
@ -142,16 +141,10 @@ object ImageGetter {
return layerList return layerList
} }
/*fun refreshAtlas() {
atlas.dispose() // To avoid OutOfMemory exceptions
atlas = TextureAtlas("game.atlas")
setTextureRegionDrawables()
}*/
fun getWhiteDot() = getImage(whiteDotLocation) fun getWhiteDot() = getImage(whiteDotLocation)
fun getDot(dotColor: Color) = getWhiteDot().apply { color = dotColor} fun getDot(dotColor: Color) = getWhiteDot().apply { color = dotColor }
fun getExternalImage(fileName:String): Image { fun getExternalImage(fileName: String): Image {
return Image(TextureRegion(Texture("ExtraImages/$fileName"))) return Image(TextureRegion(Texture("ExtraImages/$fileName")))
} }
@ -160,55 +153,54 @@ object ImageGetter {
} }
fun getDrawable(fileName: String): TextureRegionDrawable { fun getDrawable(fileName: String): TextureRegionDrawable {
if(textureRegionDrawables.containsKey(fileName)) return textureRegionDrawables[fileName]!! if (textureRegionDrawables.containsKey(fileName)) return textureRegionDrawables[fileName]!!
else return textureRegionDrawables[whiteDotLocation]!! else return textureRegionDrawables[whiteDotLocation]!!
} }
fun getRoundedEdgeTableBackground(tintColor: Color?=null): NinePatchDrawable { fun getRoundedEdgeTableBackground(tintColor: Color? = null): NinePatchDrawable {
val drawable = NinePatchDrawable(NinePatch(getDrawable("OtherIcons/buttonBackground").region,25,25,0,0)).apply { val drawable = NinePatchDrawable(NinePatch(getDrawable("OtherIcons/buttonBackground").region, 25, 25, 0, 0)).apply {
setPadding(5f,15f,5f,15f) setPadding(5f, 15f, 5f, 15f)
} }
if(tintColor==null) return drawable if (tintColor == null) return drawable
return drawable.tint(tintColor) return drawable.tint(tintColor)
} }
fun imageExists(fileName:String) = textureRegionDrawables.containsKey(fileName) fun imageExists(fileName: String) = textureRegionDrawables.containsKey(fileName)
fun techIconExists(techName:String) = imageExists("TechIcons/$techName") fun techIconExists(techName: String) = imageExists("TechIcons/$techName")
fun getStatIcon(statName: String): Image { fun getStatIcon(statName: String): Image {
return getImage("StatIcons/$statName") return getImage("StatIcons/$statName")
.apply { setSize(20f,20f)} .apply { setSize(20f, 20f) }
} }
fun getUnitIcon(unitName:String,color:Color= Color.BLACK):Image{ fun getUnitIcon(unitName: String, color: Color = Color.BLACK): Image {
return getImage("UnitIcons/$unitName").apply { this.color=color } return getImage("UnitIcons/$unitName").apply { this.color = color }
} }
fun getNationIndicator(nation: Nation, size:Float): IconCircleGroup { fun getNationIndicator(nation: Nation, size: Float): IconCircleGroup {
val civIconName = if(nation.isCityState()) "CityState" else nation.name val civIconName = if (nation.isCityState()) "CityState" else nation.name
if(nationIconExists(civIconName)){ if (nationIconExists(civIconName)) {
val cityStateIcon = getNationIcon(civIconName) val cityStateIcon = getNationIcon(civIconName)
cityStateIcon.color = nation.getInnerColor() cityStateIcon.color = nation.getInnerColor()
return cityStateIcon.surroundWithCircle(size*0.9f).apply { circle.color = nation.getOuterColor() } return cityStateIcon.surroundWithCircle(size * 0.9f).apply { circle.color = nation.getOuterColor() }
.surroundWithCircle(size,false).apply { circle.color=nation.getInnerColor() } .surroundWithCircle(size, false).apply { circle.color = nation.getInnerColor() }
} } else {
else{
return getCircle().apply { color = nation.getOuterColor() } return getCircle().apply { color = nation.getOuterColor() }
.surroundWithCircle(size).apply { circle.color = nation.getInnerColor() } .surroundWithCircle(size).apply { circle.color = nation.getInnerColor() }
} }
} }
fun nationIconExists(nation:String) = imageExists("NationIcons/$nation") fun nationIconExists(nation: String) = imageExists("NationIcons/$nation")
fun getNationIcon(nation:String) = getImage("NationIcons/$nation") fun getNationIcon(nation: String) = getImage("NationIcons/$nation")
val foodCircleColor = colorFromRGB(129, 199, 132) val foodCircleColor = colorFromRGB(129, 199, 132)
val productionCircleColor = Color.BROWN.cpy().lerp(Color.WHITE,0.5f)!! val productionCircleColor = Color.BROWN.cpy().lerp(Color.WHITE, 0.5f)!!
val goldCircleColor = Color.GOLD.cpy().lerp(Color.WHITE,0.5f)!! val goldCircleColor = Color.GOLD.cpy().lerp(Color.WHITE, 0.5f)!!
val cultureCircleColor = Color.PURPLE.cpy().lerp(Color.WHITE,0.5f)!! val cultureCircleColor = Color.PURPLE.cpy().lerp(Color.WHITE, 0.5f)!!
val scienceCircleColor = Color.BLUE.cpy().lerp(Color.WHITE,0.5f)!! val scienceCircleColor = Color.BLUE.cpy().lerp(Color.WHITE, 0.5f)!!
fun getColorFromStats(stats:Stats) = when { fun getColorFromStats(stats: Stats) = when {
stats.food > 0 -> foodCircleColor stats.food > 0 -> foodCircleColor
stats.production > 0 -> productionCircleColor stats.production > 0 -> productionCircleColor
stats.gold > 0 -> goldCircleColor stats.gold > 0 -> goldCircleColor
@ -218,7 +210,7 @@ object ImageGetter {
} }
fun getImprovementIcon(improvementName:String, size:Float=20f):Actor { fun getImprovementIcon(improvementName: String, size: Float = 20f): Actor {
if (improvementName.startsWith("Remove") || improvementName == Constants.cancelImprovementOrder) if (improvementName.startsWith("Remove") || improvementName == Constants.cancelImprovementOrder)
return getImage("OtherIcons/Stop") return getImage("OtherIcons/Stop")
if (improvementName.startsWith("StartingLocation ")) { if (improvementName.startsWith("StartingLocation ")) {
@ -243,28 +235,28 @@ object ImageGetter {
return getStatIcon(construction) return getStatIcon(construction)
} }
fun getPromotionIcon(promotionName:String): Actor { fun getPromotionIcon(promotionName: String): Actor {
var level = 0 var level = 0
when { when {
promotionName.endsWith(" I") -> level=1 promotionName.endsWith(" I") -> level = 1
promotionName.endsWith(" II") -> level=2 promotionName.endsWith(" II") -> level = 2
promotionName.endsWith(" III") -> level=3 promotionName.endsWith(" III") -> level = 3
} }
val basePromotionName = if(level==0) promotionName val basePromotionName = if (level == 0) promotionName
else promotionName.substring(0, promotionName.length-level-1) else promotionName.substring(0, promotionName.length - level - 1)
if(imageExists("UnitPromotionIcons/$basePromotionName")) { if (imageExists("UnitPromotionIcons/$basePromotionName")) {
val icon = getImage("UnitPromotionIcons/$basePromotionName") val icon = getImage("UnitPromotionIcons/$basePromotionName")
icon.color = colorFromRGB(255,226,0) icon.color = colorFromRGB(255, 226, 0)
val circle = icon.surroundWithCircle(30f) val circle = icon.surroundWithCircle(30f)
circle.circle.color = colorFromRGB(0,12,49) circle.circle.color = colorFromRGB(0, 12, 49)
if(level!=0){ if (level != 0) {
val starTable = Table().apply { defaults().pad(2f) } val starTable = Table().apply { defaults().pad(2f) }
for(i in 1..level) starTable.add(getImage("OtherIcons/Star")).size(8f) for (i in 1..level) starTable.add(getImage("OtherIcons/Star")).size(8f)
starTable.centerX(circle) starTable.centerX(circle)
starTable.y=5f starTable.y = 5f
circle.addActor(starTable) circle.addActor(starTable)
} }
return circle return circle
@ -277,15 +269,15 @@ object ImageGetter {
fun getCircle() = getImage("OtherIcons/Circle") fun getCircle() = getImage("OtherIcons/Circle")
fun getTriangle() = getImage("OtherIcons/Triangle") fun getTriangle() = getImage("OtherIcons/Triangle")
fun getBackground(color:Color): Drawable { fun getBackground(color: Color): Drawable {
val drawable = getDrawable("OtherIcons/TableBackground") val drawable = getDrawable("OtherIcons/TableBackground")
drawable.minHeight=0f drawable.minHeight = 0f
drawable.minWidth=0f drawable.minWidth = 0f
return drawable.tint(color) return drawable.tint(color)
} }
fun getResourceImage(resourceName: String, size:Float): Actor { fun getResourceImage(resourceName: String, size: Float): Actor {
val iconGroup = getImage("ResourceIcons/$resourceName").surroundWithCircle(size) val iconGroup = getImage("ResourceIcons/$resourceName").surroundWithCircle(size)
val resource = ruleset.tileResources[resourceName] val resource = ruleset.tileResources[resourceName]
if (resource == null) return iconGroup // This is the result of a bad modding setup, just give em an empty circle. Their problem. if (resource == null) return iconGroup // This is the result of a bad modding setup, just give em an empty circle. Their problem.
@ -322,7 +314,7 @@ object ImageGetter {
.surroundWithCircle(circleSize) .surroundWithCircle(circleSize)
} }
fun getProgressBarVertical(width:Float,height:Float,percentComplete:Float,progressColor:Color,backgroundColor:Color): Table { fun getProgressBarVertical(width: Float, height: Float, percentComplete: Float, progressColor: Color, backgroundColor: Color): Table {
val advancementGroup = Table() val advancementGroup = Table()
val completionHeight = height * percentComplete val completionHeight = height * percentComplete
advancementGroup.add(getImage(whiteDotLocation).apply { color = backgroundColor }) advancementGroup.add(getImage(whiteDotLocation).apply { color = backgroundColor })
@ -353,7 +345,7 @@ object ImageGetter {
return healthBar return healthBar
} }
fun getLine(startX:Float,startY:Float,endX:Float,endY:Float, width:Float): Image { fun getLine(startX: Float, startY: Float, endX: Float, endY: Float, width: Float): Image {
/** The simplest way to draw a line between 2 points seems to be: /** The simplest way to draw a line between 2 points seems to be:
* A. Get a pixel dot, set its width to the required length (hypotenuse) * A. Get a pixel dot, set its width to the required length (hypotenuse)
* B. Set its rotational center, and set its rotation * B. Set its rotational center, and set its rotation
@ -362,9 +354,9 @@ object ImageGetter {
// A // A
val line = getWhiteDot() val line = getWhiteDot()
val deltaX = (startX-endX).toDouble() val deltaX = (startX - endX).toDouble()
val deltaY = (startY-endY).toDouble() val deltaY = (startY - endY).toDouble()
line.width = Math.sqrt(deltaX*deltaX+deltaY*deltaY).toFloat() line.width = Math.sqrt(deltaX * deltaX + deltaY * deltaY).toFloat()
line.height = width // the width of the line, is the height of the line.height = width // the width of the line, is the height of the
// B // B
@ -373,16 +365,15 @@ object ImageGetter {
line.rotation = (Math.atan2(deltaY, deltaX) * radiansToDegrees).toFloat() line.rotation = (Math.atan2(deltaY, deltaX) * radiansToDegrees).toFloat()
// C // C
line.x = (startX+endX)/2 - line.width/2 line.x = (startX + endX) / 2 - line.width / 2
line.y = (startY+endY)/2 - line.height/2 line.y = (startY + endY) / 2 - line.height / 2
return line return line
} }
fun getSpecialistIcon(color:Color): Image { fun getSpecialistIcon(color: Color): Image {
val specialist = getImage("StatIcons/Specialist") val specialist = getImage("StatIcons/Specialist")
specialist.color = color specialist.color = color
return specialist return specialist
} }
} }