package com.example import android.widget.Toast import android.app.Notification import android.app.NotificationChannel import android.app.NotificationManager import android.content.Context import android.content.Intent import android.graphics.PixelFormat import android.net.Uri import android.os.Build import android.os.IBinder import android.view.Gravity import android.view.WindowManager import androidx.compose.animation.* import androidx.compose.animation.core.* import androidx.compose.foundation.Canvas import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.clickable import androidx.compose.foundation.gestures.detectDragGestures import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.* import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.shadow import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.PathEffect import androidx.compose.ui.graphics.StrokeCap import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.draw.scale import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.core.app.NotificationCompat import androidx.lifecycle.LifecycleService import androidx.lifecycle.setViewTreeLifecycleOwner import androidx.lifecycle.lifecycleScope import androidx.savedstate.SavedStateRegistry import androidx.savedstate.SavedStateRegistryController import androidx.savedstate.SavedStateRegistryOwner import androidx.savedstate.setViewTreeSavedStateRegistryOwner import android.app.usage.UsageStatsManager import android.app.AppOpsManager import kotlinx.coroutines.launch import kotlinx.coroutines.delay import kotlin.math.* import androidx.lifecycle.ViewModelStoreOwner import androidx.lifecycle.ViewModelStore import androidx.lifecycle.setViewTreeViewModelStoreOwner class OverlayService : LifecycleService(), SavedStateRegistryOwner, ViewModelStoreOwner { private val savedStateRegistryController = SavedStateRegistryController.create(this) override val savedStateRegistry: SavedStateRegistry get() = savedStateRegistryController.savedStateRegistry private val store = ViewModelStore() override val viewModelStore: ViewModelStore get() = store private lateinit var windowManager: WindowManager // Float Controller bubble view private var floatBubbleView: ComposeView? = null private var floatBubbleParams: WindowManager.LayoutParams? = null // Guiding Lines view (Full screen transparent) private var guideLinesView: ComposeView? = null private var guideLinesParams: WindowManager.LayoutParams? = null companion object { var isRunning = false // Settings State val enableRebound = mutableStateOf(true) val enableAutoAim = mutableStateOf(true) val enableSafeMode = mutableStateOf(false) val enableAutopilot = mutableStateOf(true) // Draggable Overlay Anchors val anchorStrikerX = mutableStateOf(300f) val anchorStrikerY = mutableStateOf(1200f) val anchorTargetX = mutableStateOf(500f) val anchorTargetY = mutableStateOf(800f) val isPanelExpanded = mutableStateOf(false) val isGameInForeground = mutableStateOf(true) } override fun onBind(intent: Intent): IBinder? { super.onBind(intent) return null } override fun onCreate() { super.onCreate() savedStateRegistryController.performRestore(null) windowManager = getSystemService(Context.WINDOW_SERVICE) as WindowManager isRunning = true startServiceForeground() setupFloatingBubble() setupGuideLines() startGameMonitoring() } private fun startGameMonitoring() { lifecycleScope.launch { while (isRunning) { isGameInForeground.value = checkIsGameOrSelfInForeground() delay(1200) } } } private fun checkIsGameOrSelfInForeground(): Boolean { if (!isUsageStatsPermissionGranted(this)) { return true } val usageStatsManager = getSystemService(Context.USAGE_STATS_SERVICE) as? UsageStatsManager ?: return true val time = System.currentTimeMillis() val stats = usageStatsManager.queryUsageStats( UsageStatsManager.INTERVAL_DAILY, time - 1000 * 5, time ) if (stats.isNullOrEmpty()) { return true } val sortedStats = stats.sortedByDescending { it.lastTimeUsed } val topApp = sortedStats.firstOrNull()?.packageName ?: return true return topApp == "com.miniclip.carrom" || topApp == packageName } private fun isUsageStatsPermissionGranted(context: Context): Boolean { val appOps = context.getSystemService(Context.APP_OPS_SERVICE) as? AppOpsManager ?: return false val mode = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { appOps.unsafeCheckOpNoThrow( AppOpsManager.OPSTR_GET_USAGE_STATS, android.os.Process.myUid(), context.packageName ) } else { @Suppress("DEPRECATION") appOps.checkOpNoThrow( AppOpsManager.OPSTR_GET_USAGE_STATS, android.os.Process.myUid(), context.packageName ) } return mode == AppOpsManager.MODE_ALLOWED } private fun startServiceForeground() { val channelId = "AimMasterChannel" val channelName = "Carrom Aim Assistant Active" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager val channel = NotificationChannel( channelId, channelName, NotificationManager.IMPORTANCE_LOW ) manager.createNotificationChannel(channel) } val notification: Notification = NotificationCompat.Builder(this, channelId) .setContentTitle("AimMaster for Carrom") .setContentText("Smart Aim Overlay is running on top.") .setSmallIcon(android.R.drawable.ic_dialog_info) .setOngoing(true) .build() startForeground(9923, notification) } private fun setupFloatingBubble() { val bubbleType = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY } else { @Suppress("DEPRECATION") WindowManager.LayoutParams.TYPE_PHONE } floatBubbleParams = WindowManager.LayoutParams( WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, bubbleType, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN, PixelFormat.TRANSLUCENT ).apply { gravity = Gravity.TOP or Gravity.START x = 100 y = 500 } floatBubbleView = ComposeView(this).apply { setViewTreeLifecycleOwner(this@OverlayService) setViewTreeSavedStateRegistryOwner(this@OverlayService) setViewTreeViewModelStoreOwner(this@OverlayService) setContent { MaterialTheme( colorScheme = darkColorScheme( primary = Color(0xFF00E5FF), secondary = Color(0xFFFF2A7A), surface = Color(0xFF1E293B) ) ) { FloatingBubbleLayout() } } } try { windowManager.addView(floatBubbleView, floatBubbleParams) } catch (e: Exception) { e.printStackTrace() } } private fun setupGuideLines() { val guideType = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY } else { @Suppress("DEPRECATION") WindowManager.LayoutParams.TYPE_PHONE } // Must be NOT_FOCUSABLE and NOT_TOUCH_MODAL. To allow drags on specific pins but touch to fall through // is difficult on Android. Instead, we capture touches in Compose and allow dragging the circular pins // overlayed safely. To let screen clicks fall through to the game, we make the entire layout pass touch // events except when touched on the draggable circles, OR we can provide a small toggle in the bubble // to disable guidelines when hitting "Safe Play" so standard screen touches pass through unblocked! // This is a brilliant usability design! guideLinesParams = WindowManager.LayoutParams( WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT, guideType, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN, PixelFormat.TRANSLUCENT ) guideLinesView = ComposeView(this).apply { setViewTreeLifecycleOwner(this@OverlayService) setViewTreeSavedStateRegistryOwner(this@OverlayService) setViewTreeViewModelStoreOwner(this@OverlayService) setContent { MaterialTheme( colorScheme = darkColorScheme( primary = Color(0xFF00E5FF), secondary = Color(0xFFFF2A7A) ) ) { GuideLinesLayout() } } } try { windowManager.addView(guideLinesView, guideLinesParams) } catch (e: Exception) { e.printStackTrace() } } @Composable fun FloatingBubbleLayout() { val isFore by isGameInForeground if (!isFore) return var isExpanded by isPanelExpanded val infiniteTransition = rememberInfiniteTransition(label = "pulse") val pulseScale by infiniteTransition.animateFloat( initialValue = 0.95f, targetValue = 1.05f, animationSpec = infiniteRepeatable( animation = tween(1200, easing = LinearEasing), repeatMode = RepeatMode.Reverse ), label = "pulse" ) Column( horizontalAlignment = Alignment.End, modifier = Modifier.wrapContentSize() ) { if (isExpanded) { // Glassmorphic Smart Panel Card( shape = RoundedCornerShape(16.dp), colors = CardDefaults.cardColors( containerColor = Color(0xFF0F172A).copy(alpha = 0.92f) ), modifier = Modifier .width(220.dp) .padding(bottom = 8.dp) .shadow(12.dp, RoundedCornerShape(16.dp)) .border(1.dp, Color(0xFF00E5FF).copy(alpha = 0.2f), RoundedCornerShape(16.dp)) ) { Column( modifier = Modifier.padding(12.dp) ) { Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth() ) { Text( text = "AimMaster HUD", color = Color.White, fontSize = 14.sp, fontWeight = FontWeight.Bold ) IconButton( onClick = { isExpanded = false }, modifier = Modifier.size(24.dp) ) { Icon( imageVector = Icons.Default.Close, contentDescription = "Collapse", tint = Color.LightGray, modifier = Modifier.size(16.dp) ) } } Spacer(modifier = Modifier.height(8.dp)) // Controls ControlToggleRow( label = "Rebound Lines", icon = Icons.Default.TrendingUp, state = enableRebound, activeColor = Color(0xFF00E5FF) ) ControlToggleRow( label = "Auto Aim Assist", icon = Icons.Default.GpsFixed, state = enableAutoAim, activeColor = Color(0xFFFF2A7A) ) ControlToggleRow( label = "Safe Mode", icon = Icons.Default.Security, state = enableSafeMode, activeColor = Color(0xFF10B981) ) ControlToggleRow( label = "Autopilot", icon = Icons.Default.PlayArrow, state = enableAutopilot, activeColor = Color(0xFFF59E0B) ) Spacer(modifier = Modifier.height(10.dp)) // Smart Sensing calibration button Button( onClick = { anchorStrikerX.value = 320f anchorStrikerY.value = 1250f anchorTargetX.value = 480f anchorTargetY.value = 750f enableAutoAim.value = true enableRebound.value = true Toast.makeText(this@OverlayService, "Sensing Calibrated & Aligned!", Toast.LENGTH_SHORT).show() }, colors = ButtonDefaults.buttonColors(containerColor = Color(0xFF10B981)), modifier = Modifier .fillMaxWidth() .height(32.dp) .padding(bottom = 6.dp), contentPadding = PaddingValues(0.dp) ) { Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(4.dp) ) { Icon(Icons.Default.BuildCircle, contentDescription = "Calibrate", modifier = Modifier.size(13.dp), tint = Color.Black) Text("SMART CALIBRATE", color = Color.Black, fontSize = 10.sp, fontWeight = FontWeight.Bold) } } Row( horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth() ) { TextButton( onClick = { anchorStrikerX.value = 300f anchorStrikerY.value = 1200f anchorTargetX.value = 500f anchorTargetY.value = 800f }, contentPadding = PaddingValues(horizontal = 8.dp, vertical = 2.dp), modifier = Modifier.height(28.dp) ) { Text("Reset Anchors", color = Color(0xFF00E5FF), fontSize = 11.sp) } Button( onClick = { stopSelf() }, colors = ButtonDefaults.buttonColors(containerColor = Color(0xFFEF4444)), contentPadding = PaddingValues(horizontal = 8.dp, vertical = 2.dp), modifier = Modifier.height(28.dp) ) { Text("Stop Assistant", color = Color.White, fontSize = 11.sp) } } } } } // Draggable controller toggle icon Box( contentAlignment = Alignment.Center, modifier = Modifier .size(54.dp) .pointerInput(Unit) { detectDragGestures { change, dragAmount -> change.consume() floatBubbleParams?.let { params -> params.x = (params.x + dragAmount.x.toInt()).coerceAtLeast(0) params.y = (params.y + dragAmount.y.toInt()).coerceAtLeast(0) windowManager.updateViewLayout(floatBubbleView, params) } } } .shadow(8.dp, CircleShape) .clip(CircleShape) .clickable { isExpanded = !isExpanded } .background( Brush.linearGradient( colors = listOf(Color(0xFF0F172A), Color(0xFF1E293B)) ) ) .border(1.5.dp, Color(0xFF00E5FF).copy(alpha = pulseScale), CircleShape) ) { Icon( imageVector = Icons.Default.FilterCenterFocus, contentDescription = "Menu Center", tint = Color(0xFF00E5FF), modifier = Modifier.size(28.dp) ) } } } @Composable fun ControlToggleRow( label: String, icon: androidx.compose.ui.graphics.vector.ImageVector, state: MutableState, activeColor: Color ) { var active by state Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier .fillMaxWidth() .padding(vertical = 4.dp) .clickable { active = !active } ) { Row(verticalAlignment = Alignment.CenterVertically) { Icon( imageVector = icon, contentDescription = label, tint = if (active) activeColor else Color.Gray, modifier = Modifier.size(16.dp) ) Spacer(modifier = Modifier.width(8.dp)) Text( text = label, color = if (active) Color.White else Color.Gray, fontSize = 11.sp, fontWeight = if (active) FontWeight.SemiBold else FontWeight.Normal ) } Checkbox( checked = active, onCheckedChange = { active = it }, colors = CheckboxDefaults.colors( checkedColor = activeColor, checkmarkColor = Color.Black ), modifier = Modifier .size(24.dp) .scale(0.7f) ) } } @Composable fun GuideLinesLayout() { val isFore by isGameInForeground if (!isFore) return val stX by anchorStrikerX val stY by anchorStrikerY val tarX by anchorTargetX val tarY by anchorTargetY val reboundOn by enableRebound val safeOn by enableSafeMode val autopilotOn by enableAutopilot // Full screen coordinate guide line drawer Box(modifier = Modifier.fillMaxSize()) { Canvas(modifier = Modifier.fillMaxSize()) { val start = Offset(stX, stY) val targetPoint = Offset(tarX, tarY) val dx = targetPoint.x - start.x val dy = targetPoint.y - start.y val length = sqrt(dx * dx + dy * dy) val displayWidth = size.width val displayHeight = size.height // Draw Autopilot helper paths to all corner pockets if (autopilotOn) { val pocketsList = listOf( Offset(displayWidth * 0.08f, displayHeight * 0.12f), Offset(displayWidth * 0.92f, displayHeight * 0.12f), Offset(displayWidth * 0.08f, displayHeight * 0.88f), Offset(displayWidth * 0.92f, displayHeight * 0.88f) ) pocketsList.forEach { pocket -> drawLine( color = Color(0xFF10B981).copy(alpha = if (safeOn) 0.15f else 0.6f), start = targetPoint, end = pocket, strokeWidth = if (safeOn) 1.5f else 2.5f, pathEffect = PathEffect.dashPathEffect(floatArrayOf(8f, 8f), 0f), cap = StrokeCap.Round ) } } if (length > 1f) { val angle = atan2(dy, dx) // Trace rebound lines val lineSegments = mutableListOf() lineSegments.add(start) var currentStart = targetPoint var currentAngle = angle val maxSegments = if (reboundOn) 3 else 1 // Draw direct assist shot line from Striker to Puck first drawLine( color = Color(0xFF00FFCC).copy(alpha = if (safeOn) 0.25f else 0.85f), start = start, end = targetPoint, strokeWidth = if (safeOn) 2.5f else 3.5f, pathEffect = PathEffect.dashPathEffect(floatArrayOf(12f, 10f), 0f), cap = StrokeCap.Round ) var currentPos = currentStart for (i in 0 until maxSegments) { val vx = cos(currentAngle) val vy = sin(currentAngle) if (abs(vx) < 0.001f && abs(vy) < 0.001f) break // Math calculation for screen bouncing val tLeft = if (vx < 0) -currentPos.x / vx else Float.MAX_VALUE val tRight = if (vx > 0) (displayWidth - currentPos.x) / vx else Float.MAX_VALUE val tTop = if (vy < 0) -currentPos.y / vy else Float.MAX_VALUE val tBottom = if (vy > 0) (displayHeight - currentPos.y) / vy else Float.MAX_VALUE val t = minOf(tLeft, tRight, tTop, tBottom) if (t == Float.MAX_VALUE || t <= 0.05f) break val newLimit = Offset(currentPos.x + vx * t, currentPos.y + vy * t) lineSegments.add(newLimit) // Rebound Reflection Color: Neon Red/Pink for target route drawLine( color = if (i == 0) Color(0xFFFF2A7A).copy(alpha = if (safeOn) 0.25f else 0.85f) else Color(0xFFF59E0B).copy(alpha = if (safeOn) 0.15f else 0.6f), start = currentPos, end = newLimit, strokeWidth = if (safeOn) 2f else 3f, pathEffect = PathEffect.dashPathEffect(floatArrayOf(8f, 8f), 0f) ) currentAngle = when { t == tLeft || t == tRight -> atan2(vy, -vx) else -> atan2(-vy, vx) } currentPos = newLimit } } } // Striker Anchor Pin (Draggable helper button in screen) PinAnchor( xState = anchorStrikerX, yState = anchorStrikerY, borderColor = Color(0xFF00E5FF), label = "STRIKER" ) // Target Anchor Pin (Draggable helper button in screen) PinAnchor( xState = anchorTargetX, yState = anchorTargetY, borderColor = Color(0xFFFF2A7A), label = "AIM TAR" ) } } @Composable fun PinAnchor( xState: MutableState, yState: MutableState, borderColor: Color, label: String ) { val safeModeOn by enableSafeMode Box( modifier = Modifier .offset { IntOffset(xState.value.toInt() - 40, yState.value.toInt() - 40) } .size(80.dp) .pointerInput(borderColor) { detectDragGestures { change, dragAmount -> change.consume() xState.value += dragAmount.x yState.value += dragAmount.y } } .border(2.dp, if (safeModeOn) borderColor.copy(alpha = 0.3f) else borderColor, CircleShape) .background(Color.Black.copy(alpha = if (safeModeOn) 0.08f else 0.35f), CircleShape), contentAlignment = Alignment.Center ) { Box( modifier = Modifier .size(6.dp) .background(borderColor, CircleShape) ) // Tiny description if (!safeModeOn) { Text( text = label, color = borderColor, fontSize = 8.sp, fontWeight = FontWeight.Bold, modifier = Modifier .align(Alignment.BottomCenter) .padding(bottom = 6.dp) ) } } } override fun onDestroy() { super.onDestroy() isRunning = false try { floatBubbleView?.let { windowManager.removeView(it) } guideLinesView?.let { windowManager.removeView(it) } } catch (e: Exception) { e.printStackTrace() } store.clear() } }