Text Monitoring Guide
This guide covers best practices for monitoring text content in your application using the Cirvia Parental SDK. Learn when, what, and how to monitor text effectively while maintaining good user experience.
Overview
Text monitoring helps protect children from:
- Cyberbullying and harassment
- Inappropriate conversations with strangers
- Sexual predation attempts
- Exposure to mature content through chat
- Self-harm discussions and dangerous challenges
When to Monitor Text
High-Priority Scenarios
Monitor text in these situations for maximum child safety:
// ✅ Chat messages and comments
public void onMessageSent(String message) {
ParentalAI.sendTextIncident("chat", message);
sendMessageToChat(message);
}
// ✅ Direct/private messages
public void onPrivateMessageSent(String message, String recipientId) {
ParentalAI.sendTextIncident("private-messaging", message);
sendPrivateMessage(message, recipientId);
}
// ✅ Public posts and comments
public void onPostCreated(String postContent) {
ParentalAI.sendTextIncident("social-posts", postContent);
publishPost(postContent);
}
// ✅ User-generated content (bios, usernames, etc.)
public void onProfileUpdated(String bio, String displayName) {
ParentalAI.sendTextIncident("profile", bio);
ParentalAI.sendTextIncident("profile", displayName);
updateUserProfile(bio, displayName);
}
Lower-Priority Scenarios
Consider monitoring these for comprehensive coverage:
// Search queries (may indicate concerning interests)
public void onSearchPerformed(String query) {
if (query.length() > 3) { // Avoid monitoring very short queries
ParentalAI.sendTextIncident("search", query);
}
performSearch(query);
}
// Game-specific content (usernames, custom objects)
public void onCustomItemNamed(String itemName) {
ParentalAI.sendTextIncident("game-content", itemName);
createCustomItem(itemName);
}
Implementation Patterns
Real-Time Chat Monitoring
public class ChatManager {
private final EditText messageInput;
private final Button sendButton;
public void setupChatMonitoring() {
sendButton.setOnClickListener(v -> {
String message = messageInput.getText().toString().trim();
if (!message.isEmpty()) {
// Monitor before sending
monitorMessage(message);
// Send message normally
sendChatMessage(message);
// Clear input
messageInput.setText("");
}
});
}
private void monitorMessage(String message) {
try {
ParentalAI.sendTextIncident("game-chat", message);
} catch (Exception e) {
Log.e("Chat", "Failed to monitor message", e);
// Don't block chat functionality if monitoring fails
}
}
}
Comment System Integration
public class CommentSystem {
public void submitComment(String postId, String commentText) {
// Validate comment
if (commentText == null || commentText.trim().isEmpty()) {
showError("Comment cannot be empty");
return;
}
// Monitor comment content
ParentalAI.sendTextIncident("comments", commentText);
// Submit to backend
apiService.postComment(postId, commentText)
.enqueue(new Callback<Comment>() {
@Override
public void onResponse(Call<Comment> call, Response<Comment> response) {
if (response.isSuccessful()) {
onCommentPosted(response.body());
} else {
onCommentError("Failed to post comment");
}
}
@Override
public void onFailure(Call<Comment> call, Throwable t) {
onCommentError("Network error: " + t.getMessage());
}
});
}
}
Social Media Post Monitoring
public class PostCreator {
public void createPost(String content, List<String> hashtags) {
// Monitor main post content
if (content != null && !content.trim().isEmpty()) {
ParentalAI.sendTextIncident("social-post", content);
}
// Monitor hashtags (they can contain inappropriate content)
for (String hashtag : hashtags) {
ParentalAI.sendTextIncident("hashtags", hashtag);
}
// Create the post
Post newPost = new Post(content, hashtags);
uploadPost(newPost);
}
}
Content Preprocessing
Text Sanitization
Clean text before monitoring to improve accuracy:
public class TextProcessor {
public static String preprocessText(String rawText) {
if (rawText == null) return null;
return rawText
.trim() // Remove leading/trailing whitespace
.replaceAll("\\s+", " ") // Normalize whitespace
.replaceAll("[\\p{Cntrl}]", "") // Remove control characters
.toLowerCase(); // Normalize case for better analysis
}
public static boolean shouldMonitor(String text) {
if (text == null || text.trim().isEmpty()) {
return false;
}
// Skip very short text (may be accidental)
if (text.trim().length() < 2) {
return false;
}
// Skip pure punctuation or numbers
if (text.matches("[\\p{Punct}\\d\\s]+")) {
return false;
}
return true;
}
}
Usage with Preprocessing
public void monitorUserText(String rawText, String platform) {
String processedText = TextProcessor.preprocessText(rawText);
if (TextProcessor.shouldMonitor(processedText)) {
ParentalAI.sendTextIncident(platform, processedText);
}
}
Platform-Specific Monitoring
Gaming Platforms
public class GameChatMonitor {
// Monitor in-game chat
public void onPlayerChat(String playerId, String message) {
ParentalAI.sendTextIncident("roblox-chat", message);
}
// Monitor custom game creations
public void onGameObjectNamed(String objectName) {
ParentalAI.sendTextIncident("roblox-creation", objectName);
}
// Monitor player usernames when they join
public void onPlayerJoined(String username) {
ParentalAI.sendTextIncident("roblox-username", username);
}
}
Social Media Apps
public class SocialMediaMonitor {
// Monitor stories and posts
public void onStoryPosted(String storyText) {
ParentalAI.sendTextIncident("instagram-story", storyText);
}
// Monitor direct messages
public void onDirectMessage(String message, String recipientId) {
ParentalAI.sendTextIncident("instagram-dm", message);
}
// Monitor profile changes
public void onBioUpdated(String newBio) {
ParentalAI.sendTextIncident("instagram-bio", newBio);
}
}
Messaging Apps
public class MessagingMonitor {
public void onMessageSent(Message message) {
String platform = "discord";
// Monitor message content
if (message.getContent() != null) {
ParentalAI.sendTextIncident(platform + "-message", message.getContent());
}
// Monitor message embeds/links
for (String embed : message.getEmbeds()) {
ParentalAI.sendTextIncident(platform + "-embed", embed);
}
}
public void onChannelCreated(String channelName, String channelTopic) {
ParentalAI.sendTextIncident("discord-channel", channelName);
if (channelTopic != null && !channelTopic.isEmpty()) {
ParentalAI.sendTextIncident("discord-topic", channelTopic);
}
}
}
Performance Optimization
Batching for High-Volume Text
For apps with very frequent text input, consider batching:
public class BatchTextMonitor {
private final List<String> pendingTexts = new ArrayList<>();
private final Handler batchHandler = new Handler(Looper.getMainLooper());
private final Runnable processBatch = this::processPendingTexts;
private final String platform;
public BatchTextMonitor(String platform) {
this.platform = platform;
}
public void queueText(String text) {
synchronized (pendingTexts) {
pendingTexts.add(text);
}
// Process batch after 1 second of inactivity
batchHandler.removeCallbacks(processBatch);
batchHandler.postDelayed(processBatch, 1000);
}
private void processPendingTexts() {
List<String> textsToProcess;
synchronized (pendingTexts) {
if (pendingTexts.isEmpty()) return;
textsToProcess = new ArrayList<>(pendingTexts);
pendingTexts.clear();
}
// Combine texts and send as single incident
String combinedText = String.join(" ", textsToProcess);
ParentalAI.sendTextIncident(platform, combinedText);
}
}
Debouncing Text Input
For real-time typing scenarios:
public class DebouncedTextMonitor {
private final Handler debounceHandler = new Handler(Looper.getMainLooper());
private final String platform;
private Runnable pendingMonitorTask;
public DebouncedTextMonitor(String platform) {
this.platform = platform;
}
public void onTextChanged(String currentText) {
// Cancel previous monitoring task
if (pendingMonitorTask != null) {
debounceHandler.removeCallbacks(pendingMonitorTask);
}
// Schedule new monitoring task
pendingMonitorTask = () -> {
if (currentText != null && !currentText.trim().isEmpty()) {
ParentalAI.sendTextIncident(platform, currentText);
}
};
// Execute after 2 seconds of no typing
debounceHandler.postDelayed(pendingMonitorTask, 2000);
}
}
Error Handling
Robust Monitoring Implementation
public class RobustTextMonitor {
private static final String TAG = "TextMonitor";
private final String platform;
public RobustTextMonitor(String platform) {
this.platform = platform;
}
public void monitorText(String text) {
try {
// Validate input
if (text == null || text.trim().isEmpty()) {
Log.d(TAG, "Skipping empty text monitoring");
return;
}
// Preprocess text
String processedText = TextProcessor.preprocessText(text);
if (!TextProcessor.shouldMonitor(processedText)) {
Log.d(TAG, "Text doesn't meet monitoring criteria");
return;
}
// Monitor with retry logic
monitorWithRetry(processedText, 0);
} catch (Exception e) {
Log.e(TAG, "Error in text monitoring", e);
// Don't let monitoring errors affect app functionality
}
}
private void monitorWithRetry(String text, int attempt) {
try {
ParentalAI.sendTextIncident(platform, text);
Log.d(TAG, "Text monitoring successful");
} catch (IllegalStateException e) {
// SDK not initialized - retry after delay
if (attempt < 3) {
Handler handler = new Handler(Looper.getMainLooper());
handler.postDelayed(() ->
monitorWithRetry(text, attempt + 1),
1000 * (attempt + 1)
);
} else {
Log.e(TAG, "Failed to monitor text after 3 attempts", e);
}
}
}
}
Privacy Considerations
Selective Monitoring
public class PrivacyAwareMonitor {
public void monitorWithConsent(String text, String platform) {
// Check if user has consented to monitoring
if (!hasMonitoringConsent()) {
Log.d("Privacy", "Monitoring consent not granted");
return;
}
// Check if this content type should be monitored
if (!shouldMonitorPlatform(platform)) {
Log.d("Privacy", "Platform monitoring disabled: " + platform);
return;
}
ParentalAI.sendTextIncident(platform, text);
}
private boolean hasMonitoringConsent() {
SharedPreferences prefs = getSharedPreferences("privacy", Context.MODE_PRIVATE);
return prefs.getBoolean("monitoring_consent", false);
}
private boolean shouldMonitorPlatform(String platform) {
SharedPreferences prefs = getSharedPreferences("privacy", Context.MODE_PRIVATE);
return prefs.getBoolean("monitor_" + platform, true);
}
}
Testing Text Monitoring
Unit Testing
@RunWith(AndroidJUnit4.class)
public class TextMonitoringTest {
@Test
public void testTextPreprocessing() {
String rawText = " Hello World! \n\t ";
String processed = TextProcessor.preprocessText(rawText);
assertEquals("hello world!", processed);
}
@Test
public void testShouldMonitorCriteria() {
// Should monitor normal text
assertTrue(TextProcessor.shouldMonitor("Hello world"));
// Should not monitor empty text
assertFalse(TextProcessor.shouldMonitor(""));
assertFalse(TextProcessor.shouldMonitor(" "));
// Should not monitor very short text
assertFalse(TextProcessor.shouldMonitor("a"));
// Should not monitor pure punctuation
assertFalse(TextProcessor.shouldMonitor("!!!"));
}
}
Integration Testing
@Test
public void testChatMonitoring() {
// Mock SDK initialization
ParentalAI.init(context, config, () -> {}, () -> {});
// Test chat monitoring
ChatManager chatManager = new ChatManager();
// Should monitor normal messages
chatManager.monitorMessage("Hello everyone!");
// Should handle empty messages gracefully
chatManager.monitorMessage("");
// Should handle null messages gracefully
chatManager.monitorMessage(null);
}
Common Pitfalls
Avoid These Mistakes
// ❌ DON'T: Monitor every keystroke
editText.addTextChangedListener(new TextWatcher() {
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
ParentalAI.sendTextIncident("typing", s.toString()); // Too frequent!
}
});
// ✅ DO: Use debouncing for real-time monitoring
DebouncedTextMonitor monitor = new DebouncedTextMonitor("typing");
editText.addTextChangedListener(new TextWatcher() {
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
monitor.onTextChanged(s.toString()); // Debounced
}
});
// ❌ DON'T: Ignore monitoring errors
public void sendMessage(String text) {
ParentalAI.sendTextIncident("chat", text); // May throw exceptions
sendToChat(text);
}
// ✅ DO: Handle errors gracefully
public void sendMessage(String text) {
try {
ParentalAI.sendTextIncident("chat", text);
} catch (Exception e) {
Log.e("Chat", "Monitoring failed", e);
// Continue with sending message
}
sendToChat(text);
}
Best Practices Summary
- Monitor at the right time - When content is created/sent, not during typing
- Preprocess text - Clean and validate before monitoring
- Handle errors gracefully - Don't let monitoring break app functionality
- Respect privacy - Only monitor what's necessary for child safety
- Optimize performance - Use batching/debouncing for high-frequency text
- Use meaningful platform identifiers - Help parents understand context
- Test thoroughly - Ensure monitoring works in all scenarios
Next Steps
- Image Monitoring Guide → - Monitor visual content
- Platform Integration → - Platform-specific examples
- Error Handling → - Advanced error handling strategies
- Examples → - Complete implementation examples
Ready to protect children from text-based threats? Continue to Image Monitoring to learn about visual content protection.