Last Updated: January 23, 2026
For: Phase 2 Implementation (Notification Monitoring)
Prerequisites: Android Studio, Git, Basic Kotlin knowledge
- ✅ Phase 1 Complete: ONNX phishing detection working
- ✅ Model Accuracy: 99%+ on phishing URLs
- ✅ False Positive Rate: <5%
- 📝 Your Task: Implement notification monitoring (Phase 2)
- Android Studio (latest version)
- Android device or emulator (API 26+)
- Git installed
- 30 minutes for initial setup
# Clone the repository
git clone <repository-url>
cd CyberShield
# Open in Android Studio
# File → Open → Select CyberShield folder1. Wait for Android Studio to open
2. Click "Sync Now" when prompted
3. Wait for Gradle sync to complete (~2-3 minutes)
Note
If Gradle sync fails, try: File → Invalidate Caches → Invalidate and Restart
# In terminal (inside project directory)
./gradlew assembleDebugExpected Output:
BUILD SUCCESSFUL in 2m 30s
41 actionable tasks: 41 executed
Warning
If build fails with "API key not found", check local.properties file exists with Safe Browsing API key.
# Connect Android device via USB
# Enable USB debugging on device
# Install APK
adb install -r app/build/outputs/apk/debug/app-debug.apkTest 1: Whitelisted Domain
1. Open app
2. Go to "Phishing Prevention"
3. Enter: https://www.google.com
4. Click "Check URL"
5. Expected: SAFE (instant, whitelisted)
Test 2: Phishing URL
1. Enter: http://paypal-verify-account.suspicious-domain.tk
2. Click "Check URL"
3. Expected: PHISHING (99%+ confidence)
Test 3: Complex Legitimate URL
1. Enter: https://chatgpt.com/c/6971fb16-bf70-8323-b5a0-b9560c11b679
2. Click "Check URL"
3. Expected: SAFE (whitelisted AI service)
Tip
All tests should complete in <2 seconds. If slower, check network connection.
Location: app/src/main/java/com/droid/cybershield/data/repository/PhishingRepositoryImpl.kt
What it does: Main business logic for phishing detection
Key methods:
// Main entry point - check if URL is phishing
suspend fun checkUrl(url: String, source: String): PhishingResult
// Check if domain is whitelisted (70+ trusted domains)
private fun isWhitelistedDomain(url: String): Boolean
// Calculate adaptive threshold based on URL complexity
private fun getAdaptiveThreshold(url: String, safeBrowsingChecked: Boolean): Float
// Detect complex URLs (UUIDs, session tokens)
private fun isComplexUrl(url: String): BooleanRead this first - it's the heart of the system.
Location: app/src/main/java/com/droid/cybershield/core/inference/PhishingOnnxScanner.kt
What it does: Loads ONNX model and runs ML inference
Key methods:
// Get singleton instance
fun get(ctx: Context): PhishingOnnxScanner
// Predict phishing probability (0.0 = safe, 1.0 = phishing)
suspend fun predict(url: String): Float
// Parse model output (handles 2D arrays, maps)
private fun parseOutput(output: OrtSession.Result): FloatImportant: Model uses phishing_label_value = 0, meaning class 0 = PHISHING.
Location: app/src/main/java/com/droid/cybershield/core/features/PhishingFeatureExtractor.kt
What it does: Extracts 35 URL features for ML model
Key methods:
// Initialize by loading feature_order.json
fun initialize(context: Context)
// Extract 35 features from URL
fun extract(urlString: String): FloatArray
// Calculate Shannon entropy
private fun calculateEntropy(text: String): FloatYou probably won't need to modify this - it's complete and working.
// Simplified flow in PhishingRepositoryImpl.kt
suspend fun checkUrl(url: String, source: String): PhishingResult {
// 1. Sanitize URL
val sanitizedUrl = sanitizeUrl(url)
// 2. Check whitelist (instant return if whitelisted)
if (isWhitelistedDomain(sanitizedUrl)) {
return PhishingResult(isPhishing = false, confidence = 1.0f)
}
// 3. Call Google Safe Browsing API (if online)
val safeBrowsingResult = safeBrowsingClient.checkUrl(sanitizedUrl)
// 4. Run ONNX model
val phishingProb = scanner.predict(sanitizedUrl)
// 5. Calculate adaptive threshold
val threshold = getAdaptiveThreshold(sanitizedUrl, safeBrowsingChecked)
// 6. Make decision using multi-tier logic
val isPhishing = when {
safeBrowsingResult == PHISHING -> true
safeBrowsingResult == SAFE && phishingProb < 0.7f -> false
phishingProb >= 0.9f -> true
phishingProb >= threshold -> true
else -> false
}
// 7. Save to database and return
phishingDao.insertScan(...)
return PhishingResult(isPhishing, probabilities, confidence)
}Monitor all incoming notifications for phishing URLs and alert the user in real-time.
app/src/main/java/com/droid/cybershield/
└── service/
└── NotificationMonitorService.kt ← CREATE THIS
File: app/src/main/java/com/droid/cybershield/service/NotificationMonitorService.kt
package com.droid.cybershield.service
import android.service.notification.NotificationListenerService
import android.service.notification.StatusBarNotification
import android.util.Log
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.*
import javax.inject.Inject
@AndroidEntryPoint
class NotificationMonitorService : NotificationListenerService() {
@Inject
lateinit var phishingRepository: PhishingRepository
private val scope = CoroutineScope(Dispatchers.Default + SupervisorJob())
override fun onNotificationPosted(sbn: StatusBarNotification) {
super.onNotificationPosted(sbn)
try {
// Extract text from notification
val text = extractNotificationText(sbn)
if (text.isNullOrEmpty()) return
// Find URLs in text
val urls = extractUrls(text)
if (urls.isEmpty()) return
Log.d(TAG, "Found ${urls.size} URLs in notification from ${sbn.packageName}")
// Check each URL for phishing
scope.launch {
urls.forEach { url ->
checkUrlAndAlert(url, sbn.packageName)
}
}
} catch (e: Exception) {
Log.e(TAG, "Error processing notification", e)
}
}
private fun extractNotificationText(sbn: StatusBarNotification): String? {
val extras = sbn.notification.extras
val title = extras.getCharSequence("android.title")?.toString() ?: ""
val text = extras.getCharSequence("android.text")?.toString() ?: ""
val bigText = extras.getCharSequence("android.bigText")?.toString() ?: ""
return "$title $text $bigText".trim()
}
private fun extractUrls(text: String): List<String> {
val urlPattern = Regex(
"""https?://[^\s<>\"']+""",
RegexOption.IGNORE_CASE
)
return urlPattern.findAll(text)
.map { it.value.trimEnd('.', ',', ')', ']', '}') }
.distinct()
.toList()
}
private suspend fun checkUrlAndAlert(url: String, packageName: String) {
try {
val result = phishingRepository.checkUrl(url, source = "notification:$packageName")
if (result.isPhishing && result.confidence > 0.7f) {
// TODO: Show alert to user
Log.w(TAG, "⚠️ PHISHING DETECTED in notification: $url")
}
} catch (e: Exception) {
Log.e(TAG, "Error checking URL: $url", e)
}
}
override fun onDestroy() {
super.onDestroy()
scope.cancel()
}
companion object {
private const val TAG = "NotificationMonitor"
}
}Important
This service uses Hilt dependency injection (@Inject lateinit var phishingRepository). Make sure the service is annotated with @AndroidEntryPoint.
File: app/src/main/AndroidManifest.xml
Add this inside <application> tag:
<service
android:name=".service.NotificationMonitorService"
android:label="CyberShield Notification Monitor"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"
android:exported="true">
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>Location in file:
<application
android:name=".CyberShieldApplication"
...>
<!-- Existing activities -->
<activity ... />
<!-- ADD THIS SERVICE HERE -->
<service
android:name=".service.NotificationMonitorService"
...
</service>
</application>File: app/src/main/java/com/droid/cybershield/presentation/settings/SettingsView.kt
Add this composable:
@Composable
fun NotificationAccessCard(
isEnabled: Boolean,
onRequestAccess: () -> Unit
) {
Card(
modifier = Modifier.fillMaxWidth(),
colors = if (isEnabled) {
CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.primaryContainer
)
} else {
CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.errorContainer
)
}
) {
Column(modifier = Modifier.padding(16.dp)) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Column(modifier = Modifier.weight(1f)) {
Text(
"Notification Access",
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.Bold
)
Text(
if (isEnabled) "Enabled ✓" else "Disabled",
style = MaterialTheme.typography.bodySmall
)
}
if (!isEnabled) {
Button(onClick = onRequestAccess) {
Text("Enable")
}
}
}
if (!isEnabled) {
Spacer(modifier = Modifier.height(8.dp))
Text(
"Required for real-time phishing detection in notifications",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onErrorContainer
)
}
}
}
}Helper functions:
// Check if notification access is enabled
fun isNotificationAccessEnabled(context: Context): Boolean {
val enabledListeners = Settings.Secure.getString(
context.contentResolver,
"enabled_notification_listeners"
)
return enabledListeners?.contains(context.packageName) == true
}
// Request notification access
fun requestNotificationAccess(context: Context) {
val intent = Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS")
context.startActivity(intent)
}- Service starts when notification access is granted
- URLs are extracted from notifications correctly
- Phishing detection works for notification URLs
- No crashes when processing notifications
- Performance is acceptable (no lag)
- Works with Gmail, WhatsApp, SMS
1. Enable Notification Access
Settings → Apps → Special app access → Notification access → CyberShield → Enable
2. Send Test Notification
Send yourself an email with a phishing URL:
http://paypal-verify-account.suspicious-domain.tk
3. Check Logs
adb logcat | grep NotificationMonitorExpected logs:
NotificationMonitor: Found 1 URLs in notification from com.google.android.gm
NotificationMonitor: ⚠️ PHISHING DETECTED in notification: http://paypal-verify-account.suspicious-domain.tk
Problem: NotificationMonitorService doesn't receive notifications
Solution:
1. Check notification access is enabled in Settings
2. Verify service is registered in AndroidManifest.xml
3. Restart app after enabling notification access
4. Check logs for any errors
Problem: extractUrls() returns empty list
Solution:
// Debug the regex pattern
private fun extractUrls(text: String): List<String> {
Log.d(TAG, "Extracting URLs from text: $text")
val urlPattern = Regex(
"""https?://[^\s<>\"']+""",
RegexOption.IGNORE_CASE
)
val urls = urlPattern.findAll(text)
.map { it.value.trimEnd('.', ',', ')', ']', '}') }
.distinct()
.toList()
Log.d(TAG, "Found ${urls.size} URLs: $urls")
return urls
}Problem: phishingRepository is null
Solution:
1. Ensure service is annotated with @AndroidEntryPoint
2. Verify PhishingRepository is provided in Hilt module
3. Clean and rebuild project
# Clean build
./gradlew clean
# Build debug APK
./gradlew assembleDebug
# Build and install
./gradlew installDebug# Install APK
adb install -r app/build/outputs/apk/debug/app-debug.apk
# Clear app data
adb shell pm clear com.droid.cybershield
# View logs
adb logcat | grep -E "Phishing|NotificationMonitor"
# Clear logs
adb logcat -c
# Test offline mode
adb shell svc wifi disable
adb shell svc data disable
# Re-enable network
adb shell svc wifi enable
adb shell svc data enable# All phishing-related logs
adb logcat | grep Phishing
# Only notification monitor
adb logcat | grep NotificationMonitor
# Only errors
adb logcat *:E
# Multiple tags
adb logcat -s NotificationMonitor:D PhishingRepositoryImpl:D- PROJECT_STATUS.md - Complete project overview
- PhishingRepositoryImpl.kt - Main logic
- PhishingOnnxScanner.kt - ML inference
- Clone repository
- Build project successfully
- Install and test app
- Read key code files
- Understand architecture
- Create
NotificationMonitorService.kt - Register service in manifest
- Test URL extraction
- Integrate with
PhishingRepository - Test with real notifications
- Handle edge cases
- Add notification access UI
- Request permissions
- Test permission flow
- Test with multiple apps
- Fix bugs
- Document issues
- Code review
Tip
Start Small: Get basic notification listening working first, then add phishing detection.
Tip
Use Logs: Add lots of Log.d() statements to understand what's happening.
Tip
Test Incrementally: Test each method individually before integrating.
Warning
Don't Skip Testing: Always test with real notifications from different apps.
Questions? Contact Muhammad via [Slack/Email/Discord]
Stuck? Check PROJECT_STATUS.md for detailed architecture
Bugs? Create an issue on GitHub with logs and steps to reproduce
Document Version: 1.0
Last Updated: January 23, 2026
Status: Ready for Phase 2 Implementation