mirror of
https://github.com/openlibrecommunity/olcng.git
synced 2026-07-03 14:05:17 +02:00
refactor(ui): improve connection testing, dialog styling, and status indicators
- major release, update app
This commit is contained in:
@@ -73,3 +73,5 @@ material-design-icons
|
||||
.kiro/steering/kirograph.md
|
||||
.kirograph/config.json
|
||||
.kirograph/kirograph.db
|
||||
.kirograph/.session-id
|
||||
.kirograph/token-savings.jsonl
|
||||
|
||||
@@ -305,20 +305,28 @@ object V2RayServiceManager {
|
||||
var time = -1L
|
||||
var errorStr = ""
|
||||
|
||||
try {
|
||||
time = coreController.measureDelay(SettingsManager.getDelayTestUrl())
|
||||
} catch (e: Exception) {
|
||||
Log.e(AppConfig.TAG, "StartCore-Manager: Failed to measure delay", e)
|
||||
errorStr = e.message?.substringAfter("\":") ?: "empty message"
|
||||
}
|
||||
if (time == -1L) {
|
||||
val urls = listOf(
|
||||
SettingsManager.getDelayTestUrl(),
|
||||
SettingsManager.getDelayTestUrl(true)
|
||||
)
|
||||
for (url in urls) {
|
||||
if (time >= 0) break
|
||||
try {
|
||||
time = coreController.measureDelay(SettingsManager.getDelayTestUrl(true))
|
||||
time = coreController.measureDelay(url)
|
||||
} catch (e: Exception) {
|
||||
Log.e(AppConfig.TAG, "StartCore-Manager: Failed to measure delay", e)
|
||||
errorStr = e.message?.substringAfter("\":") ?: "empty message"
|
||||
}
|
||||
}
|
||||
// One more retry after a brief pause to reduce false negatives
|
||||
if (time == -1L) {
|
||||
kotlinx.coroutines.delay(500)
|
||||
try {
|
||||
time = coreController.measureDelay(urls[0])
|
||||
} catch (e: Exception) {
|
||||
errorStr = e.message?.substringAfter("\":") ?: "empty message"
|
||||
}
|
||||
}
|
||||
|
||||
val result = if (time >= 0) {
|
||||
service.getString(R.string.connection_test_available, time)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package xyz.zarazaex.olc.ui
|
||||
|
||||
import android.animation.ArgbEvaluator
|
||||
import android.animation.ValueAnimator
|
||||
import android.content.Intent
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Color
|
||||
@@ -203,7 +205,13 @@ class MainActivity : HelperBaseActivity(), NavigationView.OnNavigationItemSelect
|
||||
}
|
||||
|
||||
private fun setupViewModel() {
|
||||
mainViewModel.updateTestResultAction.observe(this) { setTestState(it) }
|
||||
mainViewModel.updateTestResultAction.observe(this) { result ->
|
||||
setTestState(result)
|
||||
if (result != null && mainViewModel.isRunning.value == true) {
|
||||
val isSuccess = result.contains(Regex("\\d+\\s*(ms|мс|毫秒)"))
|
||||
setStatusDot(if (isSuccess) DotState.CONNECTED else DotState.FAILURE)
|
||||
}
|
||||
}
|
||||
|
||||
mainViewModel.isTesting.observe(this) { testing ->
|
||||
if (testing) {
|
||||
@@ -218,13 +226,13 @@ class MainActivity : HelperBaseActivity(), NavigationView.OnNavigationItemSelect
|
||||
binding.btnSummaryLite.isEnabled = true
|
||||
binding.btnSummaryLite.alpha = 1.0f
|
||||
binding.btnSummaryLite.setIconResource(R.drawable.ic_stop_24dp)
|
||||
binding.btnSummaryLite.backgroundTintList = ColorStateList.valueOf(
|
||||
com.google.android.material.color.MaterialColors.getColor(this, com.google.android.material.R.attr.colorPrimaryContainer, 0)
|
||||
animateButtonTint(binding.btnSummaryLite,
|
||||
com.google.android.material.color.MaterialColors.getColor(this, com.google.android.material.R.attr.colorSecondaryContainer, 0)
|
||||
)
|
||||
} else {
|
||||
setButtonsEnabled(true)
|
||||
binding.btnSummaryLite.setIconResource(R.drawable.bolt_24)
|
||||
binding.btnSummaryLite.backgroundTintList = ColorStateList.valueOf(
|
||||
animateButtonTint(binding.btnSummaryLite,
|
||||
com.google.android.material.color.MaterialColors.getColor(this, com.google.android.material.R.attr.colorSecondaryContainer, 0)
|
||||
)
|
||||
if (!isLiteTesting) {
|
||||
@@ -383,7 +391,7 @@ class MainActivity : HelperBaseActivity(), NavigationView.OnNavigationItemSelect
|
||||
showStatus("Остановлено")
|
||||
setButtonsEnabled(true)
|
||||
binding.btnSummaryLite.setIconResource(R.drawable.bolt_24)
|
||||
binding.btnSummaryLite.backgroundTintList = ColorStateList.valueOf(
|
||||
animateButtonTint(binding.btnSummaryLite,
|
||||
com.google.android.material.color.MaterialColors.getColor(this, com.google.android.material.R.attr.colorSecondaryContainer, 0)
|
||||
)
|
||||
hideLoading()
|
||||
@@ -490,9 +498,9 @@ class MainActivity : HelperBaseActivity(), NavigationView.OnNavigationItemSelect
|
||||
delay(1000)
|
||||
} catch (e: Exception) {
|
||||
Log.e(AppConfig.TAG, "Error in restartV2Ray", e)
|
||||
applyRunningState(isLoading = false, isRunning = mainViewModel.isRunning.value == true)
|
||||
} finally {
|
||||
isFabOperationInProgress = false
|
||||
applyRunningState(isLoading = false, isRunning = mainViewModel.isRunning.value == true)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -530,6 +538,16 @@ class MainActivity : HelperBaseActivity(), NavigationView.OnNavigationItemSelect
|
||||
return ColorStateList.valueOf(color)
|
||||
}
|
||||
|
||||
private fun animateButtonTint(view: com.google.android.material.button.MaterialButton, toColor: Int, duration: Long = 300L) {
|
||||
val from = view.backgroundTintList?.defaultColor ?: toColor
|
||||
if (from == toColor) { view.backgroundTintList = ColorStateList.valueOf(toColor); return }
|
||||
ValueAnimator.ofArgb(from, toColor).apply {
|
||||
this.duration = duration
|
||||
addUpdateListener { view.backgroundTintList = ColorStateList.valueOf(it.animatedValue as Int) }
|
||||
start()
|
||||
}
|
||||
}
|
||||
|
||||
private fun applyRunningState(isLoading: Boolean, isRunning: Boolean) {
|
||||
val secContainer = ColorStateList.valueOf(
|
||||
com.google.android.material.color.MaterialColors.getColor(this, com.google.android.material.R.attr.colorSecondaryContainer, 0)
|
||||
@@ -562,7 +580,7 @@ class MainActivity : HelperBaseActivity(), NavigationView.OnNavigationItemSelect
|
||||
binding.fab.alpha = 1.0f
|
||||
binding.fab.backgroundTintList = accentColor()
|
||||
binding.fab.iconTint = onPrimary
|
||||
binding.btnSummaryLite.backgroundTintList = secContainer
|
||||
animateButtonTint(binding.btnSummaryLite, secContainer.defaultColor)
|
||||
binding.fab.contentDescription = getString(R.string.action_stop_service)
|
||||
setTestState(getString(R.string.connection_connected))
|
||||
binding.layoutTest.isFocusable = true
|
||||
@@ -571,7 +589,7 @@ class MainActivity : HelperBaseActivity(), NavigationView.OnNavigationItemSelect
|
||||
setButtonsEnabled(true)
|
||||
binding.fab.backgroundTintList = secContainer
|
||||
binding.fab.iconTint = onSecContainer
|
||||
binding.btnSummaryLite.backgroundTintList = secContainer
|
||||
animateButtonTint(binding.btnSummaryLite, secContainer.defaultColor)
|
||||
binding.fab.contentDescription = getString(R.string.tasker_start_service)
|
||||
if (mainViewModel.isTesting.value != true && statusResetJob?.isActive != true) {
|
||||
setTestState(getString(R.string.connection_not_connected))
|
||||
@@ -581,7 +599,7 @@ class MainActivity : HelperBaseActivity(), NavigationView.OnNavigationItemSelect
|
||||
}
|
||||
}
|
||||
|
||||
private enum class DotState { IDLE, CONNECTED, LOADING }
|
||||
private enum class DotState { IDLE, CONNECTED, LOADING, FAILURE }
|
||||
|
||||
private fun setStatusDot(state: DotState) {
|
||||
val dot = binding.statusDot
|
||||
@@ -589,6 +607,7 @@ class MainActivity : HelperBaseActivity(), NavigationView.OnNavigationItemSelect
|
||||
dot.alpha = 1f; dot.scaleX = 1f; dot.scaleY = 1f
|
||||
dot.backgroundTintList = ColorStateList.valueOf(when (state) {
|
||||
DotState.CONNECTED -> ContextCompat.getColor(this, R.color.status_connected)
|
||||
DotState.FAILURE -> ContextCompat.getColor(this, R.color.status_failure)
|
||||
DotState.LOADING -> com.google.android.material.color.MaterialColors.getColor(this, androidx.appcompat.R.attr.colorPrimary, 0)
|
||||
DotState.IDLE -> com.google.android.material.color.MaterialColors.getColor(this, com.google.android.material.R.attr.colorOutline, 0)
|
||||
})
|
||||
@@ -894,20 +913,26 @@ class MainActivity : HelperBaseActivity(), NavigationView.OnNavigationItemSelect
|
||||
copyToClipboard(btcValue.text.toString())
|
||||
}
|
||||
|
||||
MaterialAlertDialogBuilder(this)
|
||||
.setTitle(R.string.donate_dialog_title)
|
||||
val titleStr = getString(R.string.donate_dialog_title)
|
||||
val dialog = MaterialAlertDialogBuilder(this)
|
||||
.setView(view)
|
||||
.setNegativeButton(R.string.donate_btn_dont_show) { dialog, _ ->
|
||||
.setNegativeButton(R.string.donate_btn_dont_show) { d, _ ->
|
||||
MmkvManager.encodeSettings(AppConfig.PREF_DONATE_DIALOG_DISMISSED, true)
|
||||
dialog.dismiss()
|
||||
}
|
||||
.setPositiveButton(R.string.donate_btn_postpone) { dialog, _ ->
|
||||
val postponeUntil = System.currentTimeMillis() + 24L * 60 * 60 * 1000
|
||||
MmkvManager.encodeSettings(AppConfig.PREF_DONATE_DIALOG_POSTPONE_UNTIL, postponeUntil)
|
||||
dialog.dismiss()
|
||||
d.dismiss()
|
||||
}
|
||||
.setCancelable(true)
|
||||
.show()
|
||||
.create()
|
||||
// Closing (X / outside / back) postpones for 24h
|
||||
val postpone = {
|
||||
val postponeUntil = System.currentTimeMillis() + 24L * 60 * 60 * 1000
|
||||
MmkvManager.encodeSettings(AppConfig.PREF_DONATE_DIALOG_POSTPONE_UNTIL, postponeUntil)
|
||||
}
|
||||
dialog.setOnCancelListener { postpone() }
|
||||
dialog.setCustomTitle(buildDialogTitleWithClose(titleStr) {
|
||||
postpone()
|
||||
dialog.dismiss()
|
||||
})
|
||||
dialog.show()
|
||||
}
|
||||
|
||||
private fun copyToClipboard(text: String) {
|
||||
@@ -1117,7 +1142,6 @@ class MainActivity : HelperBaseActivity(), NavigationView.OnNavigationItemSelect
|
||||
}
|
||||
|
||||
val dialog = MaterialAlertDialogBuilder(this@MainActivity)
|
||||
.setTitle("Исключить страны")
|
||||
.setAdapter(adapter, null)
|
||||
.setPositiveButton("Применить") { _, _ ->
|
||||
val excluded = codes.filterIndexed { i, _ -> checked[i] }.toSet()
|
||||
@@ -1131,8 +1155,8 @@ class MainActivity : HelperBaseActivity(), NavigationView.OnNavigationItemSelect
|
||||
showStatus("Показаны все страны")
|
||||
}
|
||||
.create()
|
||||
dialog.show()
|
||||
dialog.setCustomTitle(buildDialogTitleWithClose("Исключить страны") { dialog.dismiss() })
|
||||
dialog.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1158,7 +1182,6 @@ class MainActivity : HelperBaseActivity(), NavigationView.OnNavigationItemSelect
|
||||
val message = result.releaseNotes?.let { xyz.zarazaex.olc.util.MarkdownUtil.parseBasic(it) } ?: ""
|
||||
val titleStr = getString(R.string.update_new_version_found, result.latestVersion)
|
||||
val dialog = MaterialAlertDialogBuilder(this)
|
||||
.setTitle(titleStr)
|
||||
.setMessage(message)
|
||||
.setPositiveButton(R.string.update_now) { _, _ ->
|
||||
result.downloadUrl?.let {
|
||||
@@ -1166,8 +1189,8 @@ class MainActivity : HelperBaseActivity(), NavigationView.OnNavigationItemSelect
|
||||
}
|
||||
}
|
||||
.create()
|
||||
dialog.show()
|
||||
dialog.setCustomTitle(buildDialogTitleWithClose(titleStr) { dialog.dismiss() })
|
||||
dialog.show()
|
||||
}
|
||||
|
||||
private fun buildDialogTitleWithClose(title: String, onClose: () -> Unit): View {
|
||||
|
||||
@@ -166,10 +166,14 @@ class MainRecyclerAdapter(
|
||||
if (addressText.isEmpty()) View.GONE else View.VISIBLE
|
||||
|
||||
// TestResult
|
||||
val aff = MmkvManager.decodeServerAffiliationInfo(guid)
|
||||
holder.itemMainBinding.tvTestResult.text = aff?.getTestDelayString().orEmpty()
|
||||
val delayMillis = data[position].testDelayMillis
|
||||
holder.itemMainBinding.tvTestResult.text = when {
|
||||
delayMillis == 0L -> "—ms"
|
||||
delayMillis < 0L -> "-ms"
|
||||
else -> "${delayMillis}ms"
|
||||
}
|
||||
holder.itemMainBinding.tvTestResult.setTextColor(
|
||||
getPingColor(context, aff?.testDelayMillis)
|
||||
getPingColor(context, delayMillis)
|
||||
)
|
||||
(holder.itemMainBinding.tvTestResult.layoutParams as? ViewGroup.MarginLayoutParams)?.marginStart =
|
||||
if (addressText.isEmpty()) 0 else 6.dpToPx(context)
|
||||
@@ -264,9 +268,7 @@ class MainRecyclerAdapter(
|
||||
|
||||
private fun recomputePingRange() {
|
||||
val delays = data.mapNotNull { item ->
|
||||
MmkvManager.decodeServerAffiliationInfo(item.guid)
|
||||
?.testDelayMillis
|
||||
?.takeIf { it > 0L }
|
||||
item.testDelayMillis.takeIf { it > 0L }
|
||||
}
|
||||
minReachablePing = delays.minOrNull()
|
||||
maxReachablePing = delays.maxOrNull()
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:paddingStart="14dp"
|
||||
android:paddingEnd="6dp"
|
||||
android:paddingEnd="10dp"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingBottom="10dp">
|
||||
|
||||
@@ -56,15 +56,16 @@
|
||||
android:typeface="monospace" />
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
<ImageButton
|
||||
android:id="@+id/donate_card_open"
|
||||
style="@style/Widget.Material3.Button.IconButton"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/donate_open_link"
|
||||
app:icon="@drawable/ic_share_24dp"
|
||||
app:iconSize="20dp"
|
||||
app:iconTint="?attr/colorOnSurfaceVariant" />
|
||||
android:padding="10dp"
|
||||
android:scaleType="centerInside"
|
||||
android:src="@drawable/ic_share_24dp"
|
||||
app:tint="?attr/colorOnSurfaceVariant" />
|
||||
</LinearLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
@@ -84,7 +85,7 @@
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:paddingStart="14dp"
|
||||
android:paddingEnd="6dp"
|
||||
android:paddingEnd="10dp"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingBottom="10dp">
|
||||
|
||||
@@ -114,15 +115,16 @@
|
||||
android:typeface="monospace" />
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
<ImageButton
|
||||
android:id="@+id/donate_ton_copy"
|
||||
style="@style/Widget.Material3.Button.IconButton"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/donate_copy"
|
||||
app:icon="@drawable/ic_copy"
|
||||
app:iconSize="20dp"
|
||||
app:iconTint="?attr/colorOnSurfaceVariant" />
|
||||
android:padding="10dp"
|
||||
android:scaleType="centerInside"
|
||||
android:src="@drawable/ic_copy"
|
||||
app:tint="?attr/colorOnSurfaceVariant" />
|
||||
</LinearLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
@@ -142,7 +144,7 @@
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:paddingStart="14dp"
|
||||
android:paddingEnd="6dp"
|
||||
android:paddingEnd="10dp"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingBottom="10dp">
|
||||
|
||||
@@ -172,15 +174,16 @@
|
||||
android:typeface="monospace" />
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
<ImageButton
|
||||
android:id="@+id/donate_trc_copy"
|
||||
style="@style/Widget.Material3.Button.IconButton"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/donate_copy"
|
||||
app:icon="@drawable/ic_copy"
|
||||
app:iconSize="20dp"
|
||||
app:iconTint="?attr/colorOnSurfaceVariant" />
|
||||
android:padding="10dp"
|
||||
android:scaleType="centerInside"
|
||||
android:src="@drawable/ic_copy"
|
||||
app:tint="?attr/colorOnSurfaceVariant" />
|
||||
</LinearLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
@@ -199,7 +202,7 @@
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:paddingStart="14dp"
|
||||
android:paddingEnd="6dp"
|
||||
android:paddingEnd="10dp"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingBottom="10dp">
|
||||
|
||||
@@ -229,15 +232,16 @@
|
||||
android:typeface="monospace" />
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
<ImageButton
|
||||
android:id="@+id/donate_btc_copy"
|
||||
style="@style/Widget.Material3.Button.IconButton"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/donate_copy"
|
||||
app:icon="@drawable/ic_copy"
|
||||
app:iconSize="20dp"
|
||||
app:iconTint="?attr/colorOnSurfaceVariant" />
|
||||
android:padding="10dp"
|
||||
android:scaleType="centerInside"
|
||||
android:src="@drawable/ic_copy"
|
||||
app:tint="?attr/colorOnSurfaceVariant" />
|
||||
</LinearLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
</LinearLayout>
|
||||
|
||||
@@ -1,42 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
android:paddingStart="24dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingBottom="8dp">
|
||||
|
||||
<FrameLayout
|
||||
<TextView
|
||||
android:id="@+id/dialog_title_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="24dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingTop="20dp"
|
||||
android:paddingBottom="16dp">
|
||||
android:layout_gravity="center_vertical"
|
||||
android:gravity="start"
|
||||
android:paddingEnd="32dp"
|
||||
android:textAppearance="@style/TextAppearance.Material3.TitleMedium"
|
||||
android:textColor="?attr/colorOnSurface" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/dialog_title_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:gravity="center"
|
||||
android:paddingEnd="32dp"
|
||||
android:textAppearance="@style/TextAppearance.Material3.HeadlineSmall"
|
||||
android:textColor="?attr/colorOnSurface" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/dialog_close_btn"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_gravity="end|center_vertical"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:contentDescription="Закрыть"
|
||||
android:src="@drawable/ic_close_24dp"
|
||||
android:tint="?attr/colorOnSurfaceVariant" />
|
||||
</FrameLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="?attr/colorOutline" />
|
||||
|
||||
</LinearLayout>
|
||||
<ImageButton
|
||||
android:id="@+id/dialog_close_btn"
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="36dp"
|
||||
android:layout_gravity="end|center_vertical"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:contentDescription="Закрыть"
|
||||
android:src="@drawable/ic_close_24dp"
|
||||
android:tint="?attr/colorOnSurfaceVariant" />
|
||||
</FrameLayout>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
<color name="colorPingGood">#86D993</color>
|
||||
<color name="colorPingMedium">#F1C76A</color>
|
||||
<color name="status_connected">#66BB6A</color>
|
||||
<color name="status_failure">#EF5350</color>
|
||||
<color name="colorPingRed">#FFB4AB</color>
|
||||
<color name="color_fab_active">@color/md_theme_primary</color>
|
||||
<color name="color_fab_inactive">@color/md_theme_secondaryContainer</color>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
<color name="colorPingGood">#2E7D32</color>
|
||||
<color name="colorPingMedium">#9A6700</color>
|
||||
<color name="status_connected">#4CAF50</color>
|
||||
<color name="status_failure">#E53935</color>
|
||||
<color name="colorPingRed">#BA1A1A</color>
|
||||
<color name="colorConfigType">#1565C0</color>
|
||||
<color name="colorWhite">#FFFFFF</color>
|
||||
|
||||
Reference in New Issue
Block a user