Skip to content

Commit 63c6345

Browse files
committed
robofix
1 parent 6ec01d2 commit 63c6345

24 files changed

Lines changed: 2429 additions & 244 deletions

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@
1616
</p>
1717

1818
<p align="center">
19-
<h3 align="center">Watch the Watchers.</h3>
19+
<h3 align="center">Watch the Watchers 🥰</h3>
2020

21-
<p align="center"><b style="color: #eb2a2a;">FUCK</b> Palantir, <b style="color: #eb2a2a;">FUCK</b> ICE, <b style="color: #eb2a2a;">FUCK</b> ANY FASCIST PRICK.</p>
21+
<p align="center"><b style="color: #eb2a2a;">CHAOS ANTI-ICE-ING TOOL</b></p>
2222
<p align="center">WE SEE YOU TOO, <b>WE WONT FORGET <b style="color: #eb2a2a;">YOUR ACTIONS</b></b></p>
2323

2424
<p align="center"><b>Brought to you by <span style="color: #0883f6">CHAOS.CORP</span></b></p>

app/src/main/AndroidManifest.xml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@
5555
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
5656
<uses-permission android:name="android.permission.USE_EXACT_ALARM" />
5757

58+
<!-- Draw over other apps for emergency popup alerts (CMAS/WEA style) -->
59+
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
60+
5861
<!-- ================================================================== -->
5962
<!-- SYSTEM/OEM PRIVILEGED PERMISSIONS -->
6063
<!-- These permissions are only granted when app is installed as: -->
@@ -158,6 +161,17 @@
158161
<category android:name="android.intent.category.LAUNCHER" />
159162
</intent-filter>
160163
</activity>
164+
165+
<!-- CMAS/WEA-style emergency alert popup - displays above lock screen -->
166+
<activity
167+
android:name=".ui.EmergencyAlertActivity"
168+
android:exported="false"
169+
android:excludeFromRecents="true"
170+
android:launchMode="singleTop"
171+
android:showOnLockScreen="true"
172+
android:showWhenLocked="true"
173+
android:turnScreenOn="true"
174+
android:theme="@style/Theme.FlockYou.EmergencyAlert" />
161175

162176
<!-- Scanning service with multiple foreground types for robust background operation -->
163177
<service

app/src/main/java/com/flockyou/FlockYouApplication.kt

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
package com.flockyou
22

33
import android.app.Application
4+
import android.app.NotificationChannel
5+
import android.app.NotificationManager
6+
import android.os.Build
47
import androidx.hilt.work.HiltWorkerFactory
58
import androidx.work.Configuration
69
import com.flockyou.ai.DetectionAnalyzer
710
import com.flockyou.data.AiSettingsRepository
811
import com.flockyou.data.OuiSettingsRepository
912
import com.flockyou.data.repository.OuiRepository
13+
import com.flockyou.util.NotificationChannelIds
1014
import com.flockyou.worker.OuiUpdateWorker
1115
import dagger.hilt.android.HiltAndroidApp
1216
import kotlinx.coroutines.CoroutineScope
@@ -44,6 +48,9 @@ class FlockYouApplication : Application(), Configuration.Provider {
4448
override fun onCreate() {
4549
super.onCreate()
4650

51+
// Create all notification channels at app startup
52+
createNotificationChannels()
53+
4754
// Initialize OUI database updates
4855
applicationScope.launch {
4956
initializeOuiUpdates()
@@ -55,6 +62,78 @@ class FlockYouApplication : Application(), Configuration.Provider {
5562
}
5663
}
5764

65+
private fun createNotificationChannels() {
66+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
67+
val notificationManager = getSystemService(NotificationManager::class.java)
68+
69+
// Scanning service channel (low priority, always-on)
70+
val scanningChannel = NotificationChannel(
71+
NotificationChannelIds.SCANNING,
72+
"Scanning Service",
73+
NotificationManager.IMPORTANCE_LOW
74+
).apply {
75+
description = "Background surveillance device detection service"
76+
setShowBadge(false)
77+
}
78+
79+
// Detection alerts channel (high priority, bypasses DND)
80+
val detectionAlertsChannel = NotificationChannel(
81+
NotificationChannelIds.DETECTION_ALERTS,
82+
"Detection Alerts",
83+
NotificationManager.IMPORTANCE_HIGH
84+
).apply {
85+
description = "Alerts when surveillance devices are detected"
86+
enableVibration(true)
87+
setShowBadge(true)
88+
setBypassDnd(true)
89+
}
90+
91+
// Dead man's switch warning channel (high priority, bypasses DND)
92+
val deadManSwitchChannel = NotificationChannel(
93+
NotificationChannelIds.DEAD_MAN_SWITCH,
94+
"Dead Man's Switch Warning",
95+
NotificationManager.IMPORTANCE_HIGH
96+
).apply {
97+
description = "Warning before automatic data wipe"
98+
enableVibration(true)
99+
setShowBadge(true)
100+
setBypassDnd(true)
101+
}
102+
103+
// Critical alerts channel (max priority, bypasses DND)
104+
val criticalAlertsChannel = NotificationChannel(
105+
NotificationChannelIds.CRITICAL_ALERTS,
106+
"Critical Alerts",
107+
NotificationManager.IMPORTANCE_HIGH
108+
).apply {
109+
description = "Critical security alerts requiring immediate attention"
110+
enableVibration(true)
111+
setShowBadge(true)
112+
setBypassDnd(true)
113+
}
114+
115+
// Updates channel (default priority)
116+
val updatesChannel = NotificationChannel(
117+
NotificationChannelIds.UPDATES,
118+
"App Updates",
119+
NotificationManager.IMPORTANCE_DEFAULT
120+
).apply {
121+
description = "Notifications about app updates and new features"
122+
setShowBadge(false)
123+
}
124+
125+
notificationManager.createNotificationChannels(
126+
listOf(
127+
scanningChannel,
128+
detectionAlertsChannel,
129+
deadManSwitchChannel,
130+
criticalAlertsChannel,
131+
updatesChannel
132+
)
133+
)
134+
}
135+
}
136+
58137
private suspend fun initializeOuiUpdates() {
59138
val settings = ouiSettingsRepository.settings.first()
60139

app/src/main/java/com/flockyou/ai/DetectionAnalyzer.kt

Lines changed: 116 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ class DetectionAnalyzer @Inject constructor(
5757
private val aiSettingsRepository: AiSettingsRepository,
5858
private val detectionRepository: DetectionRepository,
5959
private val geminiNanoClient: GeminiNanoClient,
60-
private val mediaPipeLlmClient: MediaPipeLlmClient
60+
private val mediaPipeLlmClient: MediaPipeLlmClient,
61+
private val falsePositiveAnalyzer: FalsePositiveAnalyzer
6162
) {
6263
companion object {
6364
private const val TAG = "DetectionAnalyzer"
@@ -186,12 +187,14 @@ class DetectionAnalyzer @Inject constructor(
186187
* Uses mutex to prevent race conditions during concurrent initialization attempts.
187188
*/
188189
suspend fun initializeModel(): Boolean = modelStateMutex.withLock {
190+
Log.i(TAG, "=== initializeModel START ===")
189191
withContext(Dispatchers.IO) {
190192
try {
191193
val settings = aiSettingsRepository.settings.first()
194+
Log.d(TAG, "initializeModel settings: enabled=${settings.enabled}, selectedModel=${settings.selectedModel}")
192195

193196
if (!settings.enabled) {
194-
Log.d(TAG, "AI analysis is disabled")
197+
Log.w(TAG, "AI analysis is disabled in settings - returning false")
195198
return@withContext false
196199
}
197200

@@ -349,17 +352,33 @@ class DetectionAnalyzer @Inject constructor(
349352
*/
350353
suspend fun analyzeDetection(detection: Detection): AiAnalysisResult = withContext(Dispatchers.IO) {
351354
val startTime = System.currentTimeMillis()
355+
Log.i(TAG, "=== analyzeDetection START ===")
356+
Log.d(TAG, "Detection: ${detection.id} (${detection.deviceType})")
352357

353358
// Lazy initialization: ensure model is loaded before analysis
354359
// This handles cases where model was downloaded but not yet initialized
355360
val settings = aiSettingsRepository.settings.first()
356361
val modelFromSettings = AiModel.fromId(settings.selectedModel)
362+
363+
Log.d(TAG, "Settings check:")
364+
Log.d(TAG, " - enabled: ${settings.enabled}")
365+
Log.d(TAG, " - analyzeDetections: ${settings.analyzeDetections}")
366+
Log.d(TAG, " - selectedModel: ${settings.selectedModel}")
367+
Log.d(TAG, " - modelFromSettings: ${modelFromSettings.id} (${modelFromSettings.displayName})")
368+
Log.d(TAG, " - currentModel (in-memory): ${currentModel.id}")
369+
Log.d(TAG, " - isModelLoaded: $isModelLoaded")
370+
Log.d(TAG, " - mediaPipeLlmClient.isReady(): ${mediaPipeLlmClient.isReady()}")
371+
357372
if (settings.enabled && modelFromSettings != AiModel.RULE_BASED && !isModelLoaded) {
358-
Log.d(TAG, "Model not loaded, attempting lazy initialization for: ${modelFromSettings.displayName}")
373+
Log.i(TAG, "Model not loaded, attempting lazy initialization for: ${modelFromSettings.displayName}")
359374
val initialized = initializeModel()
375+
Log.d(TAG, "Lazy initialization result: $initialized")
376+
Log.d(TAG, "After init - isModelLoaded: $isModelLoaded, mediaPipeReady: ${mediaPipeLlmClient.isReady()}")
360377
if (!initialized) {
361378
Log.w(TAG, "Lazy initialization failed, will use rule-based fallback")
362379
}
380+
} else {
381+
Log.d(TAG, "Skipping lazy init: enabled=${settings.enabled}, modelFromSettings=${modelFromSettings.id}, isModelLoaded=$isModelLoaded")
363382
}
364383

365384
// Check if already analyzing - return early with informative message
@@ -391,6 +410,7 @@ class DetectionAnalyzer @Inject constructor(
391410

392411
// Settings already loaded above for lazy initialization
393412
if (!settings.enabled || !settings.analyzeDetections) {
413+
Log.w(TAG, "Returning 'AI analysis is disabled': enabled=${settings.enabled}, analyzeDetections=${settings.analyzeDetections}")
394414
return@withContext AiAnalysisResult(
395415
success = false,
396416
error = "AI analysis is disabled"
@@ -432,8 +452,24 @@ class DetectionAnalyzer @Inject constructor(
432452

433453
// Generate analysis
434454
val result = generateAnalysis(detection, contextualInsights, settings)
455+
456+
// Check for cancellation before FP analysis
457+
coroutineContext.ensureActive()
458+
459+
// Run false positive analysis if enabled
460+
val fpResult = if (settings.enableFalsePositiveFiltering) {
461+
val contextInfo = buildFpContextInfo(detection, contextualInsights)
462+
falsePositiveAnalyzer.analyzeForFalsePositive(detection, contextInfo)
463+
} else null
464+
435465
val processingTime = System.currentTimeMillis() - startTime
436-
val finalResult = result.copy(processingTimeMs = processingTime)
466+
val finalResult = result.copy(
467+
processingTimeMs = processingTime,
468+
isFalsePositive = fpResult?.isFalsePositive ?: false,
469+
falsePositiveConfidence = fpResult?.confidence ?: 0f,
470+
falsePositiveBanner = fpResult?.bannerMessage,
471+
falsePositiveReasons = fpResult?.allReasons?.map { it.description } ?: emptyList()
472+
)
437473

438474
// Check for cancellation before caching
439475
coroutineContext.ensureActive()
@@ -489,6 +525,48 @@ class DetectionAnalyzer @Inject constructor(
489525
Log.d(TAG, "Analysis cancellation requested")
490526
}
491527

528+
// ==================== FALSE POSITIVE ANALYSIS ====================
529+
530+
/**
531+
* Check a single detection for false positive likelihood.
532+
* Returns the FP result with banner message if applicable.
533+
*/
534+
suspend fun checkForFalsePositive(detection: Detection): FalsePositiveResult {
535+
return falsePositiveAnalyzer.analyzeForFalsePositive(detection)
536+
}
537+
538+
/**
539+
* Filter a list of detections, removing likely false positives.
540+
* Returns filtered results with FP explanations.
541+
*
542+
* @param detections List of detections to filter
543+
* @param confidenceThreshold Minimum FP confidence to filter (0.0-1.0, default 0.6)
544+
*/
545+
suspend fun filterFalsePositives(
546+
detections: List<Detection>,
547+
confidenceThreshold: Float = 0.6f
548+
): FilteredDetections {
549+
return falsePositiveAnalyzer.filterFalsePositives(
550+
detections = detections,
551+
threshold = confidenceThreshold
552+
)
553+
}
554+
555+
/**
556+
* Batch analyze detections for false positives.
557+
* Returns a map of detection ID to FP result.
558+
*/
559+
suspend fun batchCheckFalsePositives(
560+
detections: List<Detection>
561+
): Map<String, FalsePositiveResult> {
562+
return falsePositiveAnalyzer.analyzeMultiple(detections)
563+
}
564+
565+
/**
566+
* Get the false positive analyzer for direct access if needed.
567+
*/
568+
fun getFalsePositiveAnalyzer(): FalsePositiveAnalyzer = falsePositiveAnalyzer
569+
492570
private suspend fun gatherContextualInsights(detection: Detection): ContextualInsights {
493571
val allDetections = detectionRepository.getAllDetectionsSnapshot()
494572

@@ -561,12 +639,46 @@ class DetectionAnalyzer @Inject constructor(
561639
return r * c
562640
}
563641

642+
/**
643+
* Build context information for false positive analysis.
644+
* Uses contextual insights and detection location to determine user context.
645+
*/
646+
private fun buildFpContextInfo(
647+
detection: Detection,
648+
contextualInsights: ContextualInsights?
649+
): FpContextInfo {
650+
// Determine time of day
651+
val currentHour = java.util.Calendar.getInstance().get(java.util.Calendar.HOUR_OF_DAY)
652+
val isNightTime = currentHour < 6 || currentHour >= 22
653+
654+
// Check if at a known/familiar location
655+
val isKnownLocation = contextualInsights?.isKnownLocation ?: false
656+
657+
return FpContextInfo(
658+
isAtHome = isKnownLocation && detection.latitude != null,
659+
homeLatitude = if (isKnownLocation) detection.latitude else null,
660+
homeLongitude = if (isKnownLocation) detection.longitude else null,
661+
isAtWork = false, // Would need user preference storage
662+
isKnownSafeArea = isKnownLocation,
663+
isNightTime = isNightTime,
664+
recentlyTraveled = false // Would need location history
665+
)
666+
}
667+
564668
@Suppress("UNUSED_PARAMETER")
565669
private suspend fun generateAnalysis(
566670
detection: Detection,
567671
contextualInsights: ContextualInsights?,
568672
settings: AiSettings // Reserved for future use with inference configuration
569673
): AiAnalysisResult {
674+
Log.i(TAG, "=== generateAnalysis START ===")
675+
Log.d(TAG, "State at generateAnalysis:")
676+
Log.d(TAG, " - currentModel: ${currentModel.id} (${currentModel.displayName})")
677+
Log.d(TAG, " - isModelLoaded: $isModelLoaded")
678+
Log.d(TAG, " - geminiNanoInitialized: $geminiNanoInitialized")
679+
Log.d(TAG, " - mediaPipeLlmClient.isReady(): ${mediaPipeLlmClient.isReady()}")
680+
Log.d(TAG, " - mediaPipeLlmClient.getStatus(): ${mediaPipeLlmClient.getStatus()}")
681+
570682
// Use Gemini Nano if available and selected
571683
if (currentModel == AiModel.GEMINI_NANO && geminiNanoInitialized) {
572684
val geminiResult = geminiNanoClient.analyzeDetection(detection)

0 commit comments

Comments
 (0)