Keep open positions carousel stable
This commit is contained in:
@@ -10,7 +10,7 @@ android {
|
|||||||
applicationId = "xyz.kusoft.tradebotmonitor"
|
applicationId = "xyz.kusoft.tradebotmonitor"
|
||||||
minSdk = 26
|
minSdk = 26
|
||||||
targetSdk = 36
|
targetSdk = 36
|
||||||
versionCode = 16
|
versionCode = 17
|
||||||
versionName = "0.2.13"
|
versionName = "0.2.14"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+60
-6
@@ -62,10 +62,12 @@ class MainActivity : Activity() {
|
|||||||
var positionsScroller: HorizontalScrollView? = null,
|
var positionsScroller: HorizontalScrollView? = null,
|
||||||
var positionsList: LinearLayout? = null,
|
var positionsList: LinearLayout? = null,
|
||||||
var closedTradesContainer: LinearLayout? = null,
|
var closedTradesContainer: LinearLayout? = null,
|
||||||
|
val positionOrder: MutableList<String> = mutableListOf(),
|
||||||
val positions: MutableMap<String, PositionBinding> = linkedMapOf(),
|
val positions: MutableMap<String, PositionBinding> = linkedMapOf(),
|
||||||
)
|
)
|
||||||
|
|
||||||
private data class PositionBinding(
|
private data class PositionBinding(
|
||||||
|
var index: TextView? = null,
|
||||||
var value: TextView? = null,
|
var value: TextView? = null,
|
||||||
var quantity: TextView? = null,
|
var quantity: TextView? = null,
|
||||||
var entryPrice: TextView? = null,
|
var entryPrice: TextView? = null,
|
||||||
@@ -165,7 +167,7 @@ class MainActivity : Activity() {
|
|||||||
trainingStatusSignature = newTrainingStatusSignature
|
trainingStatusSignature = newTrainingStatusSignature
|
||||||
lastError = ""
|
lastError = ""
|
||||||
ensureSelectedSymbol()
|
ensureSelectedSymbol()
|
||||||
if (silent && hadSnapshot && !hadError && updateVisibleScreen(data)) {
|
if (silent && hadSnapshot && updateVisibleScreen(data)) {
|
||||||
return@post
|
return@post
|
||||||
}
|
}
|
||||||
if (shouldRenderAfterRefresh(silent, hadSnapshot, hadError, trainingChanged)) {
|
if (shouldRenderAfterRefresh(silent, hadSnapshot, hadError, trainingChanged)) {
|
||||||
@@ -178,6 +180,9 @@ class MainActivity : Activity() {
|
|||||||
val hadError = lastError.isNotBlank()
|
val hadError = lastError.isNotBlank()
|
||||||
lastError = error.message ?: "ошибка подключения"
|
lastError = error.message ?: "ошибка подключения"
|
||||||
if (!silent) toast(lastError)
|
if (!silent) toast(lastError)
|
||||||
|
if (silent && hadSnapshot) {
|
||||||
|
return@post
|
||||||
|
}
|
||||||
if (shouldRenderAfterRefresh(silent, hadSnapshot, hadError, trainingChanged = false)) {
|
if (shouldRenderAfterRefresh(silent, hadSnapshot, hadError, trainingChanged = false)) {
|
||||||
render()
|
render()
|
||||||
}
|
}
|
||||||
@@ -258,10 +263,8 @@ class MainActivity : Activity() {
|
|||||||
val binding = assetsBindings ?: return false
|
val binding = assetsBindings ?: return false
|
||||||
val positionSignature = positionsSignature(data.positions)
|
val positionSignature = positionsSignature(data.positions)
|
||||||
if (binding.positionSignature != positionSignature) {
|
if (binding.positionSignature != positionSignature) {
|
||||||
binding.positionsContainer?.let { container ->
|
if (!syncPositionsList(binding, data.positions)) return false
|
||||||
rebuildPositionsContent(container, data.positions, binding.positionsScroller?.scrollX ?: 0)
|
binding.positionSignature = positionSignature
|
||||||
binding.positionSignature = positionSignature
|
|
||||||
} ?: return false
|
|
||||||
}
|
}
|
||||||
val closedSignature = closedTradesSignature(data.closedTrades, data.closedTradesSummary)
|
val closedSignature = closedTradesSignature(data.closedTrades, data.closedTradesSummary)
|
||||||
if (binding.closedTradesSignature != closedSignature) {
|
if (binding.closedTradesSignature != closedSignature) {
|
||||||
@@ -837,6 +840,7 @@ class MainActivity : Activity() {
|
|||||||
private fun rebuildPositionsContent(container: LinearLayout, positions: List<PositionData>, preserveScrollX: Int = 0) {
|
private fun rebuildPositionsContent(container: LinearLayout, positions: List<PositionData>, preserveScrollX: Int = 0) {
|
||||||
container.removeAllViews()
|
container.removeAllViews()
|
||||||
assetsBindings?.positions?.clear()
|
assetsBindings?.positions?.clear()
|
||||||
|
assetsBindings?.positionOrder?.clear()
|
||||||
assetsBindings?.positionsScroller = null
|
assetsBindings?.positionsScroller = null
|
||||||
assetsBindings?.positionsList = null
|
assetsBindings?.positionsList = null
|
||||||
if (positions.isEmpty()) {
|
if (positions.isEmpty()) {
|
||||||
@@ -868,13 +872,61 @@ class MainActivity : Activity() {
|
|||||||
cardWidth: Int = (resources.displayMetrics.widthPixels - dp(64)).coerceAtLeast(dp(260)),
|
cardWidth: Int = (resources.displayMetrics.widthPixels - dp(64)).coerceAtLeast(dp(260)),
|
||||||
) {
|
) {
|
||||||
list.removeAllViews()
|
list.removeAllViews()
|
||||||
|
assetsBindings?.positionOrder?.clear()
|
||||||
positions.forEachIndexed { index, position ->
|
positions.forEachIndexed { index, position ->
|
||||||
|
assetsBindings?.positionOrder?.add(positionKey(position))
|
||||||
list.addView(positionCard(position, index + 1, positions.size), LinearLayout.LayoutParams(cardWidth, ViewGroup.LayoutParams.WRAP_CONTENT).apply {
|
list.addView(positionCard(position, index + 1, positions.size), LinearLayout.LayoutParams(cardWidth, ViewGroup.LayoutParams.WRAP_CONTENT).apply {
|
||||||
if (index < positions.lastIndex) setMargins(0, 0, dp(12), 0)
|
if (index < positions.lastIndex) setMargins(0, 0, dp(12), 0)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun syncPositionsList(binding: AssetsBindings, positions: List<PositionData>): Boolean {
|
||||||
|
val container = binding.positionsContainer ?: return false
|
||||||
|
val list = binding.positionsList
|
||||||
|
if (list == null || positions.isEmpty() || binding.positionOrder.isEmpty()) {
|
||||||
|
rebuildPositionsContent(container, positions, binding.positionsScroller?.scrollX ?: 0)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
val byKey = positions.associateBy(::positionKey)
|
||||||
|
var index = 0
|
||||||
|
while (index < binding.positionOrder.size) {
|
||||||
|
val key = binding.positionOrder[index]
|
||||||
|
if (!byKey.containsKey(key)) {
|
||||||
|
binding.positionOrder.removeAt(index)
|
||||||
|
binding.positions.remove(key)
|
||||||
|
list.removeViewAt(index)
|
||||||
|
} else {
|
||||||
|
index += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val cardWidth = (resources.displayMetrics.widthPixels - dp(64)).coerceAtLeast(dp(260))
|
||||||
|
positions.forEach { position ->
|
||||||
|
val key = positionKey(position)
|
||||||
|
if (!binding.positionOrder.contains(key)) {
|
||||||
|
binding.positionOrder.add(key)
|
||||||
|
list.addView(positionCard(position, binding.positionOrder.size, positions.size), LinearLayout.LayoutParams(cardWidth, ViewGroup.LayoutParams.WRAP_CONTENT))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updatePositionCardChrome(binding)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updatePositionCardChrome(binding: AssetsBindings) {
|
||||||
|
val total = binding.positionOrder.size
|
||||||
|
binding.positionOrder.forEachIndexed { index, key ->
|
||||||
|
setText(binding.positions[key]?.index, "${index + 1}/$total")
|
||||||
|
}
|
||||||
|
val list = binding.positionsList ?: return
|
||||||
|
for (i in 0 until list.childCount) {
|
||||||
|
val params = (list.getChildAt(i).layoutParams as? LinearLayout.LayoutParams) ?: continue
|
||||||
|
params.setMargins(0, 0, if (i < list.childCount - 1) dp(12) else 0, 0)
|
||||||
|
list.getChildAt(i).layoutParams = params
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun positionCard(position: PositionData, index: Int, total: Int): View =
|
private fun positionCard(position: PositionData, index: Int, total: Int): View =
|
||||||
LinearLayout(this).apply {
|
LinearLayout(this).apply {
|
||||||
orientation = LinearLayout.VERTICAL
|
orientation = LinearLayout.VERTICAL
|
||||||
@@ -884,7 +936,9 @@ class MainActivity : Activity() {
|
|||||||
orientation = LinearLayout.HORIZONTAL
|
orientation = LinearLayout.HORIZONTAL
|
||||||
gravity = Gravity.CENTER_VERTICAL
|
gravity = Gravity.CENTER_VERTICAL
|
||||||
addView(text(position.symbol, 20f, Typeface.BOLD), LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f))
|
addView(text(position.symbol, 20f, Typeface.BOLD), LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f))
|
||||||
addView(text("$index/$total", 12f, Typeface.BOLD, palette.muted))
|
val indexText = text("$index/$total", 12f, Typeface.BOLD, palette.muted)
|
||||||
|
assetsBindings?.positions?.getOrPut(positionKey(position)) { PositionBinding() }?.index = indexText
|
||||||
|
addView(indexText)
|
||||||
})
|
})
|
||||||
val valueText = text("${money(position.marketValue)} · ${signedPercent(position.unrealizedPnlPercent, 2)}", 15f, Typeface.BOLD, colorForSigned(position.unrealizedPnlPercent))
|
val valueText = text("${money(position.marketValue)} · ${signedPercent(position.unrealizedPnlPercent, 2)}", 15f, Typeface.BOLD, colorForSigned(position.unrealizedPnlPercent))
|
||||||
addView(valueText.top(dp(8)))
|
addView(valueText.top(dp(8)))
|
||||||
|
|||||||
Reference in New Issue
Block a user