Simplified MainScreen bottom bar implementation
This commit is contained in:
@@ -223,7 +223,7 @@ private fun MainScreen(
|
|||||||
val ua = UserAgent.ofAor(viewModel.selectedAor.value)
|
val ua = UserAgent.ofAor(viewModel.selectedAor.value)
|
||||||
if (ua != null) {
|
if (ua != null) {
|
||||||
showCall(ctx, viewModel, ua)
|
showCall(ctx, viewModel, ua)
|
||||||
updateIcons(viewModel, ua.account)
|
viewModel.triggerAccountUpdate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Lifecycle.Event.ON_PAUSE -> {
|
Lifecycle.Event.ON_PAUSE -> {
|
||||||
@@ -447,8 +447,7 @@ private fun MainScreen(
|
|||||||
LaunchedEffect(currentRoute, viewModel.selectedAor.collectAsState()) {
|
LaunchedEffect(currentRoute, viewModel.selectedAor.collectAsState()) {
|
||||||
if (currentRoute == "main") {
|
if (currentRoute == "main") {
|
||||||
Log.d(TAG, "Updating icons for AOR: ${viewModel.selectedAor.value}")
|
Log.d(TAG, "Updating icons for AOR: ${viewModel.selectedAor.value}")
|
||||||
val account = Account.ofAor(viewModel.selectedAor.value)
|
viewModel.triggerAccountUpdate()
|
||||||
updateIcons(viewModel, account)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -673,41 +672,56 @@ private fun TopAppBar(
|
|||||||
@Composable
|
@Composable
|
||||||
private fun BottomBar(ctx: Context, viewModel: ViewModel, navController: NavController) {
|
private fun BottomBar(ctx: Context, viewModel: ViewModel, navController: NavController) {
|
||||||
|
|
||||||
val vmIcon by viewModel.vmIcon.collectAsState()
|
// ... (the top part of your function is the same) ...
|
||||||
val showVmIcon by viewModel.showVmIcon.collectAsState()
|
|
||||||
val messagesIcon by viewModel.messagesIcon.collectAsState()
|
|
||||||
val callsIcon by viewModel.callsIcon.collectAsState()
|
|
||||||
val dialpadIcon by viewModel.dialpadIcon.collectAsState()
|
val dialpadIcon by viewModel.dialpadIcon.collectAsState()
|
||||||
|
val aor by viewModel.selectedAor.collectAsState()
|
||||||
|
val accountUpdate by viewModel.accountUpdate.collectAsState()
|
||||||
|
|
||||||
|
val showVmIcon = remember(aor, accountUpdate) {
|
||||||
|
if (aor.isNotEmpty()) Account.ofAor(aor)?.vmUri?.isNotEmpty() ?: false else false
|
||||||
|
}
|
||||||
|
val hasNewVoicemail = remember(aor, accountUpdate) {
|
||||||
|
if (aor.isNotEmpty()) (Account.ofAor(aor)?.vmNew ?: 0) > 0 else false
|
||||||
|
}
|
||||||
|
val hasUnreadMessages = remember(aor, accountUpdate) {
|
||||||
|
if (aor.isNotEmpty()) Account.ofAor(aor)?.unreadMessages ?: false else false
|
||||||
|
}
|
||||||
|
val hasMissedCalls = remember(aor, accountUpdate) {
|
||||||
|
if (aor.isNotEmpty()) Account.ofAor(aor)?.missedCalls ?: false else false
|
||||||
|
}
|
||||||
|
|
||||||
val buttonSize = 48.dp
|
val buttonSize = 48.dp
|
||||||
|
|
||||||
Row( modifier = Modifier
|
Row(
|
||||||
.fillMaxWidth()
|
modifier = Modifier
|
||||||
.navigationBarsPadding()
|
.fillMaxWidth()
|
||||||
.padding(bottom = 16.dp),
|
.navigationBarsPadding()
|
||||||
|
.padding(bottom = 16.dp),
|
||||||
horizontalArrangement = Arrangement.SpaceEvenly,
|
horizontalArrangement = Arrangement.SpaceEvenly,
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
|
|
||||||
if (showVmIcon)
|
if (showVmIcon)
|
||||||
IconButton(
|
IconButton(
|
||||||
|
// Disable the button if no account is selected
|
||||||
|
enabled = aor.isNotEmpty(),
|
||||||
onClick = {
|
onClick = {
|
||||||
if (viewModel.selectedAor.value != "") {
|
// No need for an 'if' check here anymore
|
||||||
val ua = UserAgent.ofAor(viewModel.selectedAor.value)!!
|
val ua = UserAgent.ofAor(aor)!!
|
||||||
val acc = ua.account
|
val acc = ua.account
|
||||||
if (acc.vmUri != "") {
|
if (acc.vmUri.isNotEmpty()) {
|
||||||
dialogTitle.value = ctx.getString(R.string.voicemail_messages)
|
dialogTitle.value = ctx.getString(R.string.voicemail_messages)
|
||||||
dialogMessage.value = acc.vmMessages(ctx)
|
dialogMessage.value = acc.vmMessages(ctx)
|
||||||
positiveText.value = ctx.getString(R.string.listen)
|
positiveText.value = ctx.getString(R.string.listen)
|
||||||
onPositiveClicked.value = {
|
onPositiveClicked.value = {
|
||||||
val intent = Intent(ctx, MainActivity::class.java)
|
val intent = Intent(ctx, MainActivity::class.java)
|
||||||
intent.putExtra("uap", ua.uap)
|
intent.putExtra("uap", ua.uap)
|
||||||
intent.putExtra("peer", acc.vmUri)
|
intent.putExtra("peer", acc.vmUri)
|
||||||
handleIntent(ctx, viewModel, intent, "call")
|
handleIntent(ctx, viewModel, intent, "call")
|
||||||
}
|
|
||||||
negativeText.value = ctx.getString(R.string.cancel)
|
|
||||||
onNegativeClicked.value = {}
|
|
||||||
showDialog.value = true
|
|
||||||
}
|
}
|
||||||
|
negativeText.value = ctx.getString(R.string.cancel)
|
||||||
|
onNegativeClicked.value = {}
|
||||||
|
showDialog.value = true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@@ -715,13 +729,10 @@ private fun BottomBar(ctx: Context, viewModel: ViewModel, navController: NavCont
|
|||||||
.size(buttonSize)
|
.size(buttonSize)
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = ImageVector.vectorResource(vmIcon),
|
imageVector = ImageVector.vectorResource(R.drawable.voicemail),
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
Modifier.size(buttonSize),
|
Modifier.size(buttonSize),
|
||||||
tint = if (vmIcon == R.drawable.voicemail)
|
tint = if (hasNewVoicemail) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.secondary
|
||||||
MaterialTheme.colorScheme.secondary
|
|
||||||
else
|
|
||||||
MaterialTheme.colorScheme.error
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -740,52 +751,49 @@ private fun BottomBar(ctx: Context, viewModel: ViewModel, navController: NavCont
|
|||||||
}
|
}
|
||||||
|
|
||||||
IconButton(
|
IconButton(
|
||||||
|
// Disable the button if no account is selected
|
||||||
|
enabled = aor.isNotEmpty(),
|
||||||
onClick = {
|
onClick = {
|
||||||
if (viewModel.selectedAor.value != "")
|
// No need for an 'if' check here anymore
|
||||||
navController.navigate("chats/${viewModel.selectedAor.value}")
|
navController.navigate("chats/$aor")
|
||||||
},
|
},
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.weight(1f)
|
.weight(1f)
|
||||||
.size(buttonSize)
|
.size(buttonSize)
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = ImageVector.vectorResource(messagesIcon),
|
imageVector = ImageVector.vectorResource(R.drawable.messages),
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
Modifier.size(buttonSize),
|
Modifier.size(buttonSize),
|
||||||
tint = if (messagesIcon == R.drawable.messages)
|
tint = if (hasUnreadMessages) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.secondary
|
||||||
MaterialTheme.colorScheme.secondary
|
|
||||||
else
|
|
||||||
MaterialTheme.colorScheme.error
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
IconButton(
|
IconButton(
|
||||||
|
// Disable the button if no account is selected
|
||||||
|
enabled = aor.isNotEmpty(),
|
||||||
onClick = {
|
onClick = {
|
||||||
if (viewModel.selectedAor.value != "")
|
// No need for an 'if' check here anymore
|
||||||
navController.navigate("calls/${viewModel.selectedAor.value}")
|
navController.navigate("calls/$aor")
|
||||||
},
|
},
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.weight(1f)
|
.weight(1f)
|
||||||
.size(buttonSize)
|
.size(buttonSize)
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = ImageVector.vectorResource(callsIcon),
|
imageVector = ImageVector.vectorResource(R.drawable.calls),
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
Modifier.size(buttonSize),
|
Modifier.size(buttonSize),
|
||||||
tint = if (callsIcon == R.drawable.calls)
|
tint = if (hasMissedCalls) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.secondary
|
||||||
MaterialTheme.colorScheme.secondary
|
|
||||||
else
|
|
||||||
MaterialTheme.colorScheme.error
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = { viewModel.updateDialpadIcon(
|
onClick = {
|
||||||
if (dialpadIcon == R.drawable.dialpad_off)
|
viewModel.updateDialpadIcon(
|
||||||
R.drawable.dialpad_on
|
if (dialpadIcon == R.drawable.dialpad_off) R.drawable.dialpad_on else R.drawable.dialpad_off
|
||||||
else
|
)
|
||||||
R.drawable.dialpad_off
|
},
|
||||||
) },
|
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.weight(1f)
|
.weight(1f)
|
||||||
.size(buttonSize),
|
.size(buttonSize),
|
||||||
@@ -795,14 +803,10 @@ private fun BottomBar(ctx: Context, viewModel: ViewModel, navController: NavCont
|
|||||||
imageVector = ImageVector.vectorResource(dialpadIcon),
|
imageVector = ImageVector.vectorResource(dialpadIcon),
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
modifier = Modifier.size(buttonSize),
|
modifier = Modifier.size(buttonSize),
|
||||||
tint = if (dialpadIcon == R.drawable.dialpad_off)
|
tint = if (dialpadIcon == R.drawable.dialpad_off) MaterialTheme.colorScheme.secondary else MaterialTheme.colorScheme.error
|
||||||
MaterialTheme.colorScheme.secondary
|
|
||||||
else
|
|
||||||
MaterialTheme.colorScheme.error
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private val callUri = mutableStateOf("")
|
private val callUri = mutableStateOf("")
|
||||||
@@ -973,8 +977,7 @@ private fun AccountSpinner(ctx: Context, viewModel: ViewModel, navController: Na
|
|||||||
}
|
}
|
||||||
|
|
||||||
showCall(ctx, viewModel, UserAgent.ofAor(selected))
|
showCall(ctx, viewModel, UserAgent.ofAor(selected))
|
||||||
|
viewModel.triggerAccountUpdate()
|
||||||
updateIcons(viewModel, Account.ofAor(selected))
|
|
||||||
|
|
||||||
if (selected == "") {
|
if (selected == "") {
|
||||||
OutlinedButton(
|
OutlinedButton(
|
||||||
@@ -1103,7 +1106,7 @@ private fun AccountSpinner(ctx: Context, viewModel: ViewModel, navController: Na
|
|||||||
expanded = false
|
expanded = false
|
||||||
viewModel.updateSelectedAor(acc.aor)
|
viewModel.updateSelectedAor(acc.aor)
|
||||||
showCall(ctx, viewModel, ua)
|
showCall(ctx, viewModel, ua)
|
||||||
updateIcons(viewModel, acc)
|
viewModel.triggerAccountUpdate()
|
||||||
},
|
},
|
||||||
text = { Text(
|
text = { Text(
|
||||||
text = acc.text(),
|
text = acc.text(),
|
||||||
@@ -1831,7 +1834,7 @@ private fun OnHoldNotice() {
|
|||||||
private fun spinToAor(viewModel: ViewModel, aor: String) {
|
private fun spinToAor(viewModel: ViewModel, aor: String) {
|
||||||
if (aor != viewModel.selectedAor.value)
|
if (aor != viewModel.selectedAor.value)
|
||||||
viewModel.updateSelectedAor(aor)
|
viewModel.updateSelectedAor(aor)
|
||||||
updateIcons(viewModel, Account.ofAor(aor))
|
viewModel.triggerAccountUpdate()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun callClick(ctx: Context, viewModel: ViewModel) {
|
private fun callClick(ctx: Context, viewModel: ViewModel) {
|
||||||
@@ -2193,7 +2196,7 @@ fun handleServiceEvent(ctx: Context, viewModel: ViewModel, event: String, params
|
|||||||
when (ev[0]) {
|
when (ev[0]) {
|
||||||
"call rejected" -> {
|
"call rejected" -> {
|
||||||
if (aor == viewModel.selectedAor.value)
|
if (aor == viewModel.selectedAor.value)
|
||||||
viewModel.updateCallsIcon(R.drawable.calls_missed)
|
viewModel.triggerAccountUpdate()
|
||||||
}
|
}
|
||||||
"call incoming", "call outgoing" -> {
|
"call incoming", "call outgoing" -> {
|
||||||
val callp = params[1] as Long
|
val callp = params[1] as Long
|
||||||
@@ -2336,7 +2339,7 @@ fun handleServiceEvent(ctx: Context, viewModel: ViewModel, event: String, params
|
|||||||
ua.account.resumeUri = ""
|
ua.account.resumeUri = ""
|
||||||
showCall(ctx, viewModel, ua)
|
showCall(ctx, viewModel, ua)
|
||||||
if (acc.missedCalls)
|
if (acc.missedCalls)
|
||||||
viewModel.updateCallsIcon(R.drawable.calls_missed)
|
viewModel.triggerAccountUpdate()
|
||||||
}
|
}
|
||||||
//if (kgm.isDeviceLocked)
|
//if (kgm.isDeviceLocked)
|
||||||
// this.setShowWhenLocked(false)
|
// this.setShowWhenLocked(false)
|
||||||
@@ -2356,13 +2359,8 @@ fun handleServiceEvent(ctx: Context, viewModel: ViewModel, event: String, params
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (aor == viewModel.selectedAor.value) {
|
if (aor == viewModel.selectedAor.value)
|
||||||
viewModel.updateVmIcon(if (acc.vmNew > 0)
|
viewModel.triggerAccountUpdate()
|
||||||
R.drawable.voicemail_new
|
|
||||||
else
|
|
||||||
R.drawable.voicemail
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else -> Log.e(TAG, "Unknown event '${ev[0]}'")
|
else -> Log.e(TAG, "Unknown event '${ev[0]}'")
|
||||||
}
|
}
|
||||||
@@ -2536,36 +2534,6 @@ private fun acceptTransfer(ctx: Context, viewModel: ViewModel, ua: UserAgent, ca
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateIcons(viewModel: ViewModel, acc: Account?) {
|
|
||||||
if (acc == null) {
|
|
||||||
viewModel.updateShowVmIcon(false)
|
|
||||||
viewModel.updateMessagesIcon(R.drawable.messages)
|
|
||||||
viewModel.updateCallsIcon(R.drawable.calls)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
|
|
||||||
if (acc.vmUri != "") {
|
|
||||||
viewModel.updateShowVmIcon(true)
|
|
||||||
viewModel.updateVmIcon(if (acc.vmNew > 0)
|
|
||||||
R.drawable.voicemail_new
|
|
||||||
else
|
|
||||||
R.drawable.voicemail
|
|
||||||
)
|
|
||||||
} else
|
|
||||||
viewModel.updateShowVmIcon(false)
|
|
||||||
|
|
||||||
viewModel.updateMessagesIcon(if (acc.unreadMessages)
|
|
||||||
R.drawable.messages_unread
|
|
||||||
else
|
|
||||||
R.drawable.messages)
|
|
||||||
|
|
||||||
viewModel.updateCallsIcon(if (acc.missedCalls)
|
|
||||||
R.drawable.calls_missed
|
|
||||||
else
|
|
||||||
R.drawable.calls)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun backup(ctx: Context, password: String) {
|
private fun backup(ctx: Context, password: String) {
|
||||||
val files = arrayListOf("accounts", "config", "contacts", "call_history",
|
val files = arrayListOf("accounts", "config", "contacts", "call_history",
|
||||||
"messages", "uuid", "gzrtp.zid", "cert.pem", "ca_cert", "ca_certs.crt")
|
"messages", "uuid", "gzrtp.zid", "cert.pem", "ca_cert", "ca_certs.crt")
|
||||||
|
|||||||
@@ -30,6 +30,13 @@ class ViewModel(application: Application) : AndroidViewModel(application) {
|
|||||||
_selectedAor.value = newValue
|
_selectedAor.value = newValue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val _accountUpdate = MutableStateFlow(0)
|
||||||
|
val accountUpdate: StateFlow<Int> = _accountUpdate
|
||||||
|
|
||||||
|
fun triggerAccountUpdate() {
|
||||||
|
_accountUpdate.value++
|
||||||
|
}
|
||||||
|
|
||||||
private val _aorPeerMessage = MutableStateFlow(mutableMapOf<AorPeer, String>())
|
private val _aorPeerMessage = MutableStateFlow(mutableMapOf<AorPeer, String>())
|
||||||
|
|
||||||
fun updateAorPeerMessage(aor: String, peerUri: String, message: String) {
|
fun updateAorPeerMessage(aor: String, peerUri: String, message: String) {
|
||||||
@@ -73,13 +80,6 @@ class ViewModel(application: Application) : AndroidViewModel(application) {
|
|||||||
_micIcon.value = iconResId
|
_micIcon.value = iconResId
|
||||||
}
|
}
|
||||||
|
|
||||||
private val _vmIcon = MutableStateFlow(R.drawable.voicemail)
|
|
||||||
val vmIcon: StateFlow<Int> = _vmIcon.asStateFlow()
|
|
||||||
|
|
||||||
fun updateVmIcon(newIcon: Int) {
|
|
||||||
_vmIcon.value = newIcon
|
|
||||||
}
|
|
||||||
|
|
||||||
private val _showVmIcon = MutableStateFlow(false)
|
private val _showVmIcon = MutableStateFlow(false)
|
||||||
val showVmIcon: StateFlow<Boolean> = _showVmIcon.asStateFlow()
|
val showVmIcon: StateFlow<Boolean> = _showVmIcon.asStateFlow()
|
||||||
|
|
||||||
@@ -87,20 +87,6 @@ class ViewModel(application: Application) : AndroidViewModel(application) {
|
|||||||
_showVmIcon.value = show
|
_showVmIcon.value = show
|
||||||
}
|
}
|
||||||
|
|
||||||
private val _messagesIcon = MutableStateFlow(R.drawable.messages)
|
|
||||||
val messagesIcon: StateFlow<Int> = _messagesIcon.asStateFlow()
|
|
||||||
|
|
||||||
fun updateMessagesIcon(newIcon: Int) {
|
|
||||||
_messagesIcon.value = newIcon
|
|
||||||
}
|
|
||||||
|
|
||||||
private val _callsIcon = MutableStateFlow(R.drawable.calls)
|
|
||||||
val callsIcon: StateFlow<Int> = _callsIcon.asStateFlow()
|
|
||||||
|
|
||||||
fun updateCallsIcon(newIcon: Int) {
|
|
||||||
_callsIcon.value = newIcon
|
|
||||||
}
|
|
||||||
|
|
||||||
private val _dialpadIcon = MutableStateFlow(R.drawable.dialpad_off)
|
private val _dialpadIcon = MutableStateFlow(R.drawable.dialpad_off)
|
||||||
val dialpadIcon: StateFlow<Int> = _dialpadIcon.asStateFlow()
|
val dialpadIcon: StateFlow<Int> = _dialpadIcon.asStateFlow()
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
<vector android:height="48dp"
|
|
||||||
android:viewportHeight="24.0" android:viewportWidth="24.0"
|
|
||||||
android:width="48dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<path android:fillColor="#FF000000" android:pathData="M13,3c-4.97,0 -9,4.03 -9,9L1,12l3.89,3.89 0.07,0.14L9,12L6,12c0,-3.87 3.13,-7 7,-7s7,3.13 7,7 -3.13,7 -7,7c-1.93,0 -3.68,-0.79 -4.94,-2.06l-1.42,1.42C8.27,19.99 10.51,21 13,21c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zM12,8v5l4.28,2.54 0.72,-1.21 -3.5,-2.08L13.5,8L12,8z"/>
|
|
||||||
</vector>
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
<vector android:height="44dp"
|
|
||||||
android:viewportHeight="24.0" android:viewportWidth="24.0"
|
|
||||||
android:width="44dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<path android:fillColor="#FF000000" android:pathData="M20,2L4,2c-1.1,0 -1.99,0.9 -1.99,2L2,22l4,-4h14c1.1,0 2,-0.9 2,-2L22,4c0,-1.1 -0.9,-2 -2,-2zM6,9h12v2L6,11L6,9zM14,14L6,14v-2h8v2zM18,8L6,8L6,6h12v2z"/>
|
|
||||||
</vector>
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
<vector android:height="48dp"
|
|
||||||
android:viewportHeight="24.0" android:viewportWidth="24.0"
|
|
||||||
android:width="48dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<path android:fillColor="#FF000000" android:pathData="M18.5,6C15.46,6 13,8.46 13,11.5c0,1.33 0.47,2.55 1.26,3.5L9.74,15c0.79,-0.95 1.26,-2.17 1.26,-3.5C11,8.46 8.54,6 5.5,6S0,8.46 0,11.5 2.46,17 5.5,17h13c3.04,0 5.5,-2.46 5.5,-5.5S21.54,6 18.5,6zM5.5,15C3.57,15 2,13.43 2,11.5S3.57,8 5.5,8 9,9.57 9,11.5 7.43,15 5.5,15zM18.5,15c-1.93,0 -3.5,-1.57 -3.5,-3.5S16.57,8 18.5,8 22,9.57 22,11.5 20.43,15 18.5,15z"/>
|
|
||||||
</vector>
|
|
||||||
Reference in New Issue
Block a user