Last active
December 9, 2025 06:13
-
-
Save xArieN9x/0d4aa4744a95862506bded9bb308aae7 to your computer and use it in GitHub Desktop.
CB
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| app/src/main/java/com/example/allinoneflushapp/MainActivity.kt | |
| package com.example.allinoneflushapp | |
| import android.app.Activity | |
| import android.content.Intent | |
| import android.net.VpnService | |
| import android.os.Bundle | |
| import android.widget.Button | |
| import android.widget.ImageView | |
| import android.widget.TextView | |
| import androidx.activity.result.contract.ActivityResultContracts | |
| import androidx.appcompat.app.AppCompatActivity | |
| import kotlinx.coroutines.* | |
| import java.net.URL | |
| class MainActivity : AppCompatActivity() { | |
| private lateinit var textViewIP: TextView | |
| private lateinit var textViewDNS: TextView | |
| private lateinit var networkIndicator: ImageView | |
| private lateinit var btnDoAllJob: Button | |
| private lateinit var btnStartVpn: Button | |
| private val pandaPackage = "com.logistics.rider.foodpanda" | |
| private val dnsList = listOf("1.1.1.1", "8.8.8.8", "9.9.9.9", "9.9.9.10") | |
| private val vpnPermissionLauncher = registerForActivityResult( | |
| ActivityResultContracts.StartActivityForResult() | |
| ) { result -> | |
| if (result.resultCode == Activity.RESULT_OK) { | |
| // user granted vpn permission; start service | |
| startService(Intent(this, AppMonitorVPNService::class.java)) | |
| // tell service to establish with current dns | |
| AppMonitorVPNService.rotateDNS(dnsList) | |
| } | |
| } | |
| override fun onCreate(savedInstanceState: Bundle?) { | |
| super.onCreate(savedInstanceState) | |
| AppGlobals.applicationContext = application | |
| setContentView(R.layout.activity_main) | |
| // KEEP ACCESSIBILITY ALIVE | |
| startService(Intent(this, AnchorService::class.java)) | |
| textViewIP = findViewById(R.id.textViewIP) | |
| textViewDNS = findViewById(R.id.textViewDNS) | |
| networkIndicator = findViewById(R.id.networkIndicator) | |
| btnDoAllJob = findViewById(R.id.btnDoAllJob) | |
| btnStartVpn = findViewById(R.id.btnStartVpn) | |
| updateIP() | |
| rotateDNS() | |
| startNetworkMonitor() | |
| btnDoAllJob.setOnClickListener { doAllJobSequence() } | |
| btnStartVpn.setOnClickListener { requestVpnPermission() } | |
| } | |
| private fun requestVpnPermission() { | |
| val intent = VpnService.prepare(this) | |
| if (intent != null) { | |
| vpnPermissionLauncher.launch(intent) | |
| } else { | |
| // already granted | |
| startService(Intent(this, AppMonitorVPNService::class.java)) | |
| AppMonitorVPNService.rotateDNS(dnsList) | |
| } | |
| } | |
| private fun updateIP() { | |
| CoroutineScope(Dispatchers.IO).launch { | |
| val ip = try { | |
| URL("https://api.ipify.org").readText().trim() | |
| } catch (e: Exception) { null } | |
| withContext(Dispatchers.Main) { | |
| textViewIP.text = if (ip.isNullOrEmpty()) "Public IP: —" else "Public IP: $ip" | |
| } | |
| } | |
| } | |
| private fun rotateDNS() { | |
| val selectedDNS = dnsList.random() | |
| textViewDNS.text = "DNS: $selectedDNS" | |
| AppMonitorVPNService.rotateDNS(dnsList = listOf(selectedDNS)) | |
| } | |
| private fun startNetworkMonitor() { | |
| CoroutineScope(Dispatchers.IO).launch { | |
| while (true) { | |
| val connected = AppMonitorVPNService.isPandaActive() | |
| withContext(Dispatchers.Main) { | |
| networkIndicator.setImageResource( | |
| if (connected) R.drawable.green_circle else R.drawable.red_circle | |
| ) | |
| } | |
| delay(1500) | |
| } | |
| } | |
| } | |
| private fun doAllJobSequence() { | |
| // 1. Force stop Panda + clear cache (accessibility) | |
| AccessibilityAutomationService.requestClearAndForceStop(pandaPackage) | |
| // 2. Toggle airplane (after a short delay to allow force-stop) | |
| CoroutineScope(Dispatchers.Main).launch { | |
| delay(3000) | |
| AccessibilityAutomationService.requestToggleAirplane() | |
| // 3. Wait for airplane cycle and network recovery then start/refresh VPN | |
| delay(11000) // 8 sec ON + some buffer | |
| requestVpnPermission() | |
| // 4. After VPN ready, rotate DNS and refresh IP | |
| delay(2000) | |
| rotateDNS() | |
| updateIP() | |
| } | |
| } | |
| } | |
| ========================================================================= | |
| app/src/main/java/com/example/allinoneflushapp/AccessibilityAutomationService.kt | |
| package com.example.allinoneflushapp | |
| import android.accessibilityservice.AccessibilityService | |
| import android.content.Intent | |
| import android.net.Uri | |
| import android.os.Handler | |
| import android.os.Looper | |
| import android.os.SystemClock | |
| import android.provider.Settings | |
| import android.view.accessibility.AccessibilityEvent | |
| import android.view.accessibility.AccessibilityNodeInfo | |
| import androidx.annotation.RequiresApi | |
| class AccessibilityAutomationService : AccessibilityService() { | |
| companion object { | |
| private val handler = Handler(Looper.getMainLooper()) | |
| private const val AIRPLANE_DELAY = 8000L | |
| private val storageKeys = arrayOf("Storage usage", "Storage usage ", "Storage", "Storage & cache", "App storage") | |
| private val forceStopKeys = arrayOf("Force stop", "Force stop ", "Force Stop", "Force Stop ", "Paksa berhenti", "Paksa Hentikan") | |
| private val confirmOkKeys = arrayOf("OK", "Yes", "Confirm", "Ya", "Force stop ", "Force stop") | |
| private val clearCacheKeys = arrayOf("Clear cache", "Clear cache ", "Clear Cache", "Clear Cache ", "Kosongkan cache") | |
| fun requestClearAndForceStop(packageName: String) { | |
| val ctx = AppGlobals.applicationContext | |
| val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) | |
| intent.data = Uri.parse("package:$packageName") | |
| intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK | |
| ctx.startActivity(intent) | |
| handler.postDelayed({ | |
| val svc = AppGlobals.accessibilityService ?: return@postDelayed | |
| // Click Force Stop in app info | |
| val clicked = svc.findAndClick(*forceStopKeys) | |
| if (clicked) { | |
| // Confirm dialog - choose Force stop on dialog | |
| handler.postDelayed({ svc.findAndClick(*confirmOkKeys) }, 700) | |
| } | |
| // After force stop, go to Storage usage then Clear cache | |
| handler.postDelayed({ | |
| svc.findAndClick(*storageKeys) | |
| handler.postDelayed({ svc.findAndClick(*clearCacheKeys) }, 900) | |
| handler.postDelayed({ svc.performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK) }, 1500) | |
| }, 1600) | |
| }, 1200) | |
| } | |
| fun requestToggleAirplane() { | |
| val svc = AppGlobals.accessibilityService ?: return | |
| svc.performGlobalAction(AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS) | |
| handler.postDelayed({ | |
| // Try a few localized labels | |
| val clicked = svc.findAndClick("Airplane mode", "Airplane mode ", "Airplane", "Mod Pesawat", "Mod Penerbangan", "Aeroplane mode") | |
| if (!clicked) { | |
| // fallback: try icon desc scanning | |
| svc.findAndClick("Airplane") | |
| } | |
| // wait ON | |
| SystemClock.sleep(AIRPLANE_DELAY) | |
| // toggle OFF | |
| svc.findAndClick("Airplane mode", "Airplane mode ", "Airplane", "Mod Pesawat", "Mod Penerbangan") | |
| svc.performGlobalAction(AccessibilityService.GLOBAL_ACTION_HOME) | |
| // Launch Foodpanda selepas 2 saat (bagi masa sistem stabil) | |
| handler.postDelayed({ | |
| AppGlobals.accessibilityService?.launchPandaApp() | |
| }, 2000) | |
| }, 700) | |
| } | |
| } | |
| override fun onServiceConnected() { | |
| super.onServiceConnected() | |
| AppGlobals.accessibilityService = this | |
| } | |
| override fun onAccessibilityEvent(event: AccessibilityEvent?) {} | |
| override fun onInterrupt() {} | |
| // Robust search + click using text, content description, viewId | |
| fun findAndClick(vararg keys: Array<String>): Boolean { | |
| // not used; kept for compatibility | |
| return false | |
| } | |
| // =========================================== | |
| // Auto Launch Panda App Helper | |
| // =========================================== | |
| fun launchPandaApp() { | |
| try { | |
| val pkg = "com.logistics.rider.foodpanda" | |
| val launch = packageManager.getLaunchIntentForPackage(pkg) | |
| if (launch != null) { | |
| launch.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) | |
| startActivity(launch) | |
| } | |
| } catch (_: Exception) {} | |
| } | |
| fun findAndClick(vararg keys: String, maxRetries: Int = 6, delayMs: Long = 700L): Boolean { | |
| repeat(maxRetries) { | |
| val root = rootInActiveWindow | |
| if (root != null) { | |
| for (k in keys) { | |
| // exact text matches | |
| val nodes = root.findAccessibilityNodeInfosByText(k) | |
| if (!nodes.isNullOrEmpty()) { | |
| for (n in nodes) { | |
| if (n.isClickable) { n.performAction(AccessibilityNodeInfo.ACTION_CLICK); return true } | |
| var p = n.parent | |
| while (p != null) { | |
| if (p.isClickable) { p.performAction(AccessibilityNodeInfo.ACTION_CLICK); return true } | |
| p = p.parent | |
| } | |
| } | |
| } | |
| // content-desc scan | |
| val desc = findNodeByDescription(root, k) | |
| if (desc != null) { desc.performAction(AccessibilityNodeInfo.ACTION_CLICK); return true } | |
| // viewId fallback | |
| val idNode = findNodeByViewId(root, k) | |
| if (idNode != null) { | |
| if (idNode.isClickable) { idNode.performAction(AccessibilityNodeInfo.ACTION_CLICK); return true } | |
| var p = idNode.parent | |
| while (p != null) { | |
| if (p.isClickable) { p.performAction(AccessibilityNodeInfo.ACTION_CLICK); return true } | |
| p = p.parent | |
| } | |
| } | |
| } | |
| // try scroll to reveal hidden buttons | |
| root.performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD) | |
| } | |
| Thread.sleep(delayMs) | |
| } | |
| return false | |
| } | |
| private fun findNodeByDescription(root: AccessibilityNodeInfo, text: String): AccessibilityNodeInfo? { | |
| val stack = ArrayDeque<AccessibilityNodeInfo>() | |
| stack.add(root) | |
| while (stack.isNotEmpty()) { | |
| val n = stack.removeFirst() | |
| try { | |
| val cd = n.contentDescription | |
| if (cd != null && cd.toString().contains(text, true)) return n | |
| } catch (_: Exception) {} | |
| for (i in 0 until n.childCount) n.getChild(i)?.let { stack.add(it) } | |
| } | |
| return null | |
| } | |
| private fun findNodeByViewId(root: AccessibilityNodeInfo, idPart: String): AccessibilityNodeInfo? { | |
| val stack = ArrayDeque<AccessibilityNodeInfo>() | |
| stack.add(root) | |
| while (stack.isNotEmpty()) { | |
| val n = stack.removeFirst() | |
| try { | |
| val vid = n.viewIdResourceName | |
| if (vid != null && vid.contains(idPart, true)) return n | |
| } catch (_: Exception) {} | |
| for (i in 0 until n.childCount) n.getChild(i)?.let { stack.add(it) } | |
| } | |
| return null | |
| } | |
| } | |
| ========================================================================= | |
| app/src/main/java/com/example/allinoneflushapp/AppMonitorVPNService.kt | |
| package com.example.allinoneflushapp | |
| import android.app.Notification | |
| import android.app.NotificationChannel | |
| import android.app.NotificationManager | |
| import android.app.PendingIntent | |
| import android.content.Intent | |
| import android.net.VpnService | |
| import android.os.Build | |
| import android.os.ParcelFileDescriptor | |
| import androidx.core.app.NotificationCompat | |
| import java.io.FileInputStream | |
| class AppMonitorVPNService : VpnService() { | |
| companion object { | |
| private var pandaActive = false | |
| private var dnsIndex = 0 | |
| private var instance: AppMonitorVPNService? = null | |
| fun isPandaActive() = pandaActive | |
| fun rotateDNS(dnsList: List<String>) { | |
| if (instance == null) return | |
| dnsIndex = (dnsIndex + 1) % dnsList.size | |
| val nextDNS = dnsList[dnsIndex] | |
| instance?.establishVPN(nextDNS) | |
| } | |
| } | |
| private var vpnInterface: ParcelFileDescriptor? = null | |
| private val CHANNEL_ID = "panda_monitor_channel" | |
| private val NOTIF_ID = 1001 | |
| override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { | |
| instance = this | |
| createNotificationChannel() | |
| startForeground(NOTIF_ID, createNotification("Panda Monitor running", connected = false)) | |
| // initial establish with default dns | |
| establishVPN("8.8.8.8") | |
| return START_STICKY | |
| } | |
| private fun createNotificationChannel() { | |
| if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { | |
| val nm = getSystemService(NotificationManager::class.java) | |
| val channel = NotificationChannel(CHANNEL_ID, "Panda Monitor", NotificationManager.IMPORTANCE_LOW) | |
| nm?.createNotificationChannel(channel) | |
| } | |
| } | |
| private fun createNotification(text: String, connected: Boolean): Notification { | |
| val pi = PendingIntent.getActivity( | |
| this, 0, | |
| Intent(this, MainActivity::class.java), | |
| if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) PendingIntent.FLAG_IMMUTABLE else 0 | |
| ) | |
| // use safe built-in icons instead of removed stat_sys_vpn | |
| val smallIcon = if (connected) android.R.drawable.presence_online else android.R.drawable.presence_busy | |
| return NotificationCompat.Builder(this, CHANNEL_ID) | |
| .setContentTitle("Panda Monitor") | |
| .setContentText(text) | |
| .setSmallIcon(smallIcon) | |
| .setContentIntent(pi) | |
| .setOngoing(true) | |
| .build() | |
| } | |
| fun establishVPN(dns: String) { | |
| try { | |
| vpnInterface?.close() | |
| } catch (_: Exception) {} | |
| val builder = Builder() | |
| builder.setSession("PandaMonitor") | |
| .addAddress("10.0.0.2", 32) | |
| .addDnsServer(dns) | |
| .addRoute("1.1.1.1", 32) | |
| .addRoute("0.0.0.0", 0) | |
| try { | |
| builder.addAllowedApplication("com.logistics.rider.foodpanda") | |
| } catch (_: Exception) {} | |
| vpnInterface = try { | |
| builder.establish() | |
| } catch (e: Exception) { | |
| null | |
| } | |
| try { | |
| startForeground(NOTIF_ID, createNotification("Panda Monitor (DNS: $dns)", connected = vpnInterface != null)) | |
| } catch (_: Exception) {} | |
| // start monitoring | |
| monitorTraffic() | |
| } | |
| private fun monitorTraffic() { | |
| Thread { | |
| while (true) { | |
| try { | |
| val fd = vpnInterface?.fileDescriptor | |
| if (fd == null) { | |
| pandaActive = false | |
| Thread.sleep(1000) | |
| continue | |
| } | |
| val input = FileInputStream(fd) | |
| val available = try { input.available() } catch (_: Exception) { 0 } | |
| pandaActive = available > 0 | |
| } catch (e: Exception) { | |
| pandaActive = false | |
| } | |
| try { Thread.sleep(1000) } catch (_: Exception) {} | |
| } | |
| }.start() | |
| } | |
| override fun onDestroy() { | |
| try { vpnInterface?.close() } catch (_: Exception) {} | |
| pandaActive = false | |
| instance = null | |
| super.onDestroy() | |
| } | |
| } | |
| ========================================================================= | |
| app/src/main/java/com/example/allinoneflushapp/AnchorService.kt | |
| package com.example.allinoneflushapp | |
| import android.app.Service | |
| import android.app.Notification | |
| import android.app.NotificationChannel | |
| import android.app.NotificationManager | |
| import android.content.Intent | |
| import android.os.IBinder | |
| class AnchorService : Service() { | |
| override fun onCreate() { | |
| super.onCreate() | |
| val channelId = "cb_anchor_channel" | |
| val nm = getSystemService(NotificationManager::class.java) | |
| val channel = NotificationChannel( | |
| channelId, | |
| "CedokBooster Anchor", | |
| NotificationManager.IMPORTANCE_LOW | |
| ) | |
| nm.createNotificationChannel(channel) | |
| val notif = Notification.Builder(this, channelId) | |
| .setContentTitle("Cedok Booster Active") | |
| .setContentText("Stabilizing accessibility service…") | |
| .setSmallIcon(android.R.drawable.stat_notify_sync) | |
| .setOngoing(true) | |
| .build() | |
| // KEEP ALIVE | |
| startForeground(77, notif) | |
| } | |
| override fun onStartCommand(i: Intent?, f: Int, s: Int): Int { | |
| return START_STICKY | |
| } | |
| override fun onBind(intent: Intent?): IBinder? = null | |
| } | |
| ========================================================================= | |
| codemagic.yaml | |
| workflows: | |
| android-release: | |
| name: Android Release Workflow | |
| max_build_duration: 60 | |
| environment: | |
| java: 17 | |
| scripts: | |
| - name: Build Debug APK using Gradle CLI | |
| script: | | |
| # Guna gradle CLI terus (sedia ada di Codemagic) | |
| gradle clean assembleDebug | |
| - name: Verify APK | |
| script: | | |
| ls -la app/build/outputs/apk/debug/ | |
| artifacts: | |
| - app/build/outputs/**/*.apk | |
| ========================================================================= | |
| app/src/main/res/xml/accessibility_service_config.xml | |
| <?xml version="1.0" encoding="utf-8"?> | |
| <accessibility-service xmlns:android="http://schemas.android.com/apk/res/android" | |
| android:description="@string/accessibility_service_description" | |
| android:packageNames="com.android.settings,com.android.systemui,com.coloros.safecenter,com.coloros.notificationmanager" | |
| android:accessibilityEventTypes="typeWindowStateChanged|typeWindowContentChanged|typeViewClicked" | |
| android:accessibilityFlags="flagDefault|flagIncludeNotImportantViews|flagReportViewIds|flagRetrieveInteractiveWindows|flagRequestTouchExplorationMode" | |
| android:accessibilityFeedbackType="feedbackGeneric" | |
| android:notificationTimeout="50" | |
| android:canRetrieveWindowContent="true" | |
| android:settingsActivity="com.example.allinoneflushapp.MainActivity" /> | |
| ========================================================================= | |
| app/src/main/AndroidManifest.xml | |
| <?xml version="1.0" encoding="utf-8"?> | |
| <manifest xmlns:android="http://schemas.android.com/apk/res/android" | |
| xmlns:tools="http://schemas.android.com/tools"> | |
| <!-- Internet + Network --> | |
| <uses-permission android:name="android.permission.INTERNET" /> | |
| <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> | |
| <!-- Accessibility priority --> | |
| <uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE" /> | |
| <!-- Allow reading package info (helps find nodes in settings) --> | |
| <uses-permission | |
| android:name="android.permission.QUERY_ALL_PACKAGES" | |
| tools:ignore="QueryAllPackagesPermission" /> | |
| <!-- Usage stats improves stability navigating Settings --> | |
| <uses-permission | |
| android:name="android.permission.PACKAGE_USAGE_STATS" | |
| tools:ignore="ProtectedPermissions" /> | |
| <!-- Foreground service for VPN --> | |
| <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> | |
| <uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" /> | |
| <!-- For overlay popups (some devices require this) --> | |
| <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> | |
| <!-- Optional: log scan for debugging --> | |
| <uses-permission android:name="android.permission.READ_LOGS" /> | |
| <application | |
| android:allowBackup="true" | |
| android:theme="@style/Theme.AppCompat.Light" | |
| android:usesCleartextTraffic="false" | |
| android:supportsRtl="true"> | |
| <!-- Main UI --> | |
| <activity | |
| android:name=".MainActivity" | |
| android:exported="true" | |
| android:launchMode="singleTop"> | |
| <intent-filter> | |
| <action android:name="android.intent.action.MAIN"/> | |
| <category android:name="android.intent.category.LAUNCHER"/> | |
| </intent-filter> | |
| </activity> | |
| <!-- VPN service --> | |
| <service | |
| android:name=".AppMonitorVPNService" | |
| android:permission="android.permission.BIND_VPN_SERVICE" | |
| android:exported="true" | |
| android:foregroundServiceType="specialUse"> | |
| <property | |
| android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE" | |
| android:value="vpn_tunnel"/> | |
| </service> | |
| <!-- Accessibility automation --> | |
| <service | |
| android:name=".AccessibilityAutomationService" | |
| android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE" | |
| android:exported="true" | |
| android:label="CB Accessibility Engine"> | |
| <intent-filter> | |
| <action android:name="android.accessibilityservice.AccessibilityService"/> | |
| </intent-filter> | |
| <meta-data | |
| android:name="android.accessibilityservice" | |
| android:resource="@xml/accessibility_service_config"/> | |
| </service> | |
| <!-- AnchorService --> | |
| <service | |
| android:name=".AnchorService" | |
| android:exported="false" | |
| android:foregroundServiceType="specialUse"> | |
| <property | |
| android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE" | |
| android:value="keep_alive"/> | |
| </service> | |
| </application> | |
| </manifest> | |
| ========================================================================= |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment