redesign: PurrAura Indicator(thanks to RSR)
This commit is contained in:
@@ -1,24 +1,8 @@
|
||||
package me.eternal.purrfectsnap.core.features.impl.global
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Check
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import kotlinx.coroutines.delay
|
||||
import me.eternal.purrfectsnap.core.features.Feature
|
||||
import me.eternal.purrfectsnap.core.event.events.impl.NetworkApiRequestEvent
|
||||
import me.eternal.purrfectsnap.core.event.events.impl.UnaryCallEvent
|
||||
import me.eternal.purrfectsnap.core.ui.CustomComposable
|
||||
import me.eternal.purrfectsnap.core.util.dataBuilder
|
||||
import me.eternal.purrfectsnap.core.util.hook.HookStage
|
||||
import me.eternal.purrfectsnap.core.util.hook.hook
|
||||
@@ -32,47 +16,6 @@ class EndpointsBlocker : Feature("EndpointsBlocker") {
|
||||
@Volatile
|
||||
private var isInLoginSignup = false
|
||||
|
||||
private fun showBypassStatusIndicator(isWorking: Boolean) {
|
||||
if (context.bridgeClient.getDebugProp("disable_bypass_indicator", "false") == "true") {
|
||||
return
|
||||
}
|
||||
|
||||
lateinit var composable: CustomComposable
|
||||
composable = {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(16.dp)
|
||||
.align(Alignment.TopCenter)
|
||||
.background(
|
||||
color = Color.Black.copy(alpha = 0.8f),
|
||||
shape = RoundedCornerShape(8.dp)
|
||||
)
|
||||
.padding(horizontal = 12.dp, vertical = 8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(
|
||||
imageVector = if (isWorking) Icons.Filled.Check else Icons.Filled.Close,
|
||||
contentDescription = null,
|
||||
tint = if (isWorking) Color.Green else Color.Red,
|
||||
modifier = Modifier.size(16.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(
|
||||
text = "PurrAura",
|
||||
color = Color.White,
|
||||
fontSize = 14.sp
|
||||
)
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
delay(3000)
|
||||
context.inAppOverlay.removeCustomComposable(composable)
|
||||
}
|
||||
}
|
||||
|
||||
context.inAppOverlay.addCustomComposable(composable)
|
||||
}
|
||||
|
||||
override fun init() {
|
||||
val bypassToggleEnabled = context.bridgeClient.getDebugProp("test_mode", "false") == "true"
|
||||
|
||||
@@ -101,7 +44,7 @@ class EndpointsBlocker : Feature("EndpointsBlocker") {
|
||||
context.log.warn("EndpointsBlocker: native self-test failed, indicator set to inactive")
|
||||
context.log.warn("EndpointsBlocker: Possible causes - config not loaded or self-test logic failed")
|
||||
}
|
||||
showBypassStatusIndicator(healthy)
|
||||
context.inAppOverlay.showBypassStatusIndicator(healthy)
|
||||
} else {
|
||||
context.native.setTestMode(false)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import android.widget.FrameLayout
|
||||
import androidx.compose.animation.core.Animatable
|
||||
import androidx.compose.animation.core.LinearEasing
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.animation.core.spring
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.rememberSplineBasedDecay
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
@@ -20,6 +21,8 @@ import androidx.compose.foundation.gestures.anchoredDraggable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Check
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material.icons.outlined.Warning
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
@@ -33,6 +36,7 @@ import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.IntOffset
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -378,4 +382,134 @@ class InAppOverlay(
|
||||
durationMs = durationMs
|
||||
))
|
||||
}
|
||||
|
||||
private enum class BypassAnimation { SLIDE_DOWN, ZOOM_IN }
|
||||
|
||||
fun showBypassStatusIndicator(isWorking: Boolean) {
|
||||
if (context.bridgeClient.getDebugProp("disable_bypass_indicator", "false") == "true") {
|
||||
return
|
||||
}
|
||||
|
||||
val animationType = BypassAnimation.entries.random()
|
||||
|
||||
lateinit var composable: CustomComposable
|
||||
composable = {
|
||||
var visible by remember { mutableStateOf(false) }
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
visible = true
|
||||
delay(3000)
|
||||
visible = false
|
||||
delay(400)
|
||||
context.inAppOverlay.removeCustomComposable(composable)
|
||||
}
|
||||
|
||||
val progress by animateFloatAsState(
|
||||
targetValue = if (visible) 1f else 0f,
|
||||
animationSpec = spring(
|
||||
dampingRatio = 0.7f,
|
||||
stiffness = 70f
|
||||
),
|
||||
label = "progress"
|
||||
)
|
||||
|
||||
val (offsetX, offsetY, scale) = when (animationType) {
|
||||
BypassAnimation.SLIDE_DOWN -> Triple(0f, -80f * (1f - progress), 1f)
|
||||
BypassAnimation.ZOOM_IN -> Triple(0f, 0f, progress)
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.align(Alignment.TopCenter)
|
||||
.padding(top = 16.dp)
|
||||
.graphicsLayer {
|
||||
translationX = offsetX
|
||||
translationY = offsetY
|
||||
scaleX = scale
|
||||
scaleY = scale
|
||||
alpha = progress
|
||||
}
|
||||
) {
|
||||
// Use PurrfectSnap UI colors - gradient for active, red tint for inactive
|
||||
val backgroundColor = if (isWorking) {
|
||||
Brush.linearGradient(
|
||||
listOf(
|
||||
PurrfectOverlayPalette.glowPrimary.copy(alpha = 0.85f),
|
||||
PurrfectOverlayPalette.glowSecondary.copy(alpha = 0.75f)
|
||||
)
|
||||
)
|
||||
} else {
|
||||
Brush.linearGradient(
|
||||
listOf(
|
||||
Color(0xFFB71C1C).copy(alpha = 0.85f),
|
||||
Color(0xFFD32F2F).copy(alpha = 0.75f)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
val shape = RoundedCornerShape(24.dp)
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
.shadow(
|
||||
elevation = 12.dp,
|
||||
shape = shape,
|
||||
spotColor = if (isWorking) PurrfectOverlayPalette.glowPrimary.copy(alpha = 0.3f) else Color(0xFFB71C1C).copy(alpha = 0.25f),
|
||||
ambientColor = if (isWorking) PurrfectOverlayPalette.glowSecondary.copy(alpha = 0.2f) else Color(0xFFD32F2F).copy(alpha = 0.18f)
|
||||
)
|
||||
.clip(shape)
|
||||
.background(backgroundColor, shape)
|
||||
.border(
|
||||
BorderStroke(
|
||||
1.dp,
|
||||
if (isWorking) {
|
||||
Brush.linearGradient(
|
||||
listOf(
|
||||
PurrfectOverlayPalette.glowPrimary.copy(alpha = 0.7f),
|
||||
PurrfectOverlayPalette.glowSecondary.copy(alpha = 0.6f)
|
||||
)
|
||||
)
|
||||
} else {
|
||||
Brush.linearGradient(
|
||||
listOf(
|
||||
Color(0xFFB71C1C).copy(alpha = 0.7f),
|
||||
Color(0xFFD32F2F).copy(alpha = 0.6f)
|
||||
)
|
||||
)
|
||||
}
|
||||
),
|
||||
shape
|
||||
),
|
||||
color = Color.Transparent,
|
||||
tonalElevation = 0.dp,
|
||||
shadowElevation = 0.dp
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.padding(horizontal = 16.dp, vertical = 10.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = if (isWorking) Icons.Filled.Check else Icons.Filled.Close,
|
||||
contentDescription = null,
|
||||
tint = Color.White,
|
||||
modifier = Modifier.size(18.dp)
|
||||
)
|
||||
|
||||
Text(
|
||||
text = if (isWorking)
|
||||
context.translation.getOrNull("manager.sections.bypass_status.active") ?: "Bypass Active"
|
||||
else
|
||||
context.translation.getOrNull("manager.sections.bypass_status.inactive") ?: "Bypass Inactive",
|
||||
color = Color.White,
|
||||
fontSize = 14.sp,
|
||||
fontWeight = FontWeight.Medium
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context.mainActivity?.let { injectOverlay(it) }
|
||||
customComposables.add(composable)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user