Skip to content

Fix AndroidManifest.xml parsing, implement debouncing logic in access…#1

Open
moawizsipra80 wants to merge 1 commit into
poc/KAN-98-overlayfrom
KAN-96
Open

Fix AndroidManifest.xml parsing, implement debouncing logic in access…#1
moawizsipra80 wants to merge 1 commit into
poc/KAN-98-overlayfrom
KAN-96

Conversation

@moawizsipra80

Copy link
Copy Markdown
Collaborator

…ibility services, and update category map

Copilot AI review requested due to automatic review settings March 7, 2026 20:54

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds Android accessibility-based URL/app foreground detection to drive category-based time limits and overlays, while updating the Android manifest/resources to wire up the new services.

Changes:

  • Added JS category mapping + timer logic and a JS service that subscribes to native foreground-change events.
  • Added Android AccessibilityServices (website blocker + category tracker), an overlay activity, and a React Native native module bridge for events/triggering blocks.
  • Updated AndroidManifest/resources to register services and provide accessibility service configuration/strings.

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
src/services/categoryTimer.js Introduces in-memory per-category time tracking and block checks.
src/services/categoryService.js Subscribes to native foreground-change events and runs per-category timers.
src/services/categoryMap.js Defines the package/domain → category mapping and the global category limit.
android/app/src/main/res/xml/website_blocker_config.xml Accessibility service config for browser URL capture + blocking.
android/app/src/main/res/xml/category_tracker_config.xml Accessibility service config for category tracking broadcast.
android/app/src/main/res/values/strings.xml Adds accessibility service description string resource.
android/app/src/main/java/com/appguard2/WebsiteBlockerService.kt Adds debounced URL detection and overlay triggering for Google.
android/app/src/main/java/com/appguard2/WebsiteBlockerOverlayActivity.kt Adds a full-screen “blocked” overlay activity.
android/app/src/main/java/com/appguard2/LimitterPackage.kt Registers the new CategoryTrackerModule with React Native.
android/app/src/main/java/com/appguard2/CategoryTrackerModule.kt Bridges accessibility broadcasts to JS events and exposes triggerBlock().
android/app/src/main/java/com/appguard2/CategoryTrackerAccessibilityService.kt Broadcasts foreground package/URL changes for JS consumption.
android/app/src/main/AndroidManifest.xml Registers accessibility services + overlay activity; adjusts MainActivity entry.
Limitter-app Updates the subproject commit pointer.
App.tsx Starts the new category tracking service on app startup.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +8 to +12
if (categoryTimeS[categoryName] !== undefined) {
categoryTimeS[categoryName] += 1;
return categoryTimeS[categoryName];
}
return 0;

Copilot AI Mar 7, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

incrementCategoryTime() never starts counting for categories that aren’t pre-seeded in categoryTimeS (it returns 0 forever). Initialize missing categories on first use (e.g., set to 0 then increment), or pre-populate from the set of categories you support so time is tracked for all mapped categories.

Suggested change
if (categoryTimeS[categoryName] !== undefined) {
categoryTimeS[categoryName] += 1;
return categoryTimeS[categoryName];
}
return 0;
if (categoryTimeS[categoryName] === undefined) {
categoryTimeS[categoryName] = 0;
}
categoryTimeS[categoryName] += 1;
return categoryTimeS[categoryName];

Copilot uses AI. Check for mistakes.
@@ -0,0 +1,21 @@
import { categoryMap, CATEGORY_LIMIT } from './categoryMap';

Copilot AI Mar 7, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

categoryMap is imported but not used in this file. Either remove the unused import or use it to initialize/derive supported categories (which would also help avoid missed categories in the timer state).

Suggested change
import { categoryMap, CATEGORY_LIMIT } from './categoryMap';
import { CATEGORY_LIMIT } from './categoryMap';

Copilot uses AI. Check for mistakes.
Comment on lines +5 to +6
const { CategoryTrackerModule } = NativeModules;
const eventEmitter = new NativeEventEmitter(CategoryTrackerModule);

Copilot AI Mar 7, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NativeEventEmitter is constructed unconditionally at module load time, but startCategoryService() explicitly handles CategoryTrackerModule being missing. If CategoryTrackerModule is undefined/null, creating the emitter can throw or warn before your guard runs. Create the emitter lazily inside startCategoryService() after confirming the native module exists.

Copilot uses AI. Check for mistakes.
Comment on lines +11 to +15
export const startCategoryService = () => {
if (!CategoryTrackerModule) {
console.error("CategoryTrackerModule not found. Ensure Native code is integrated.");
return;
}

Copilot AI Mar 7, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NativeEventEmitter is constructed unconditionally at module load time, but startCategoryService() explicitly handles CategoryTrackerModule being missing. If CategoryTrackerModule is undefined/null, creating the emitter can throw or warn before your guard runs. Create the emitter lazily inside startCategoryService() after confirming the native module exists.

Copilot uses AI. Check for mistakes.
Comment on lines +19 to +40
eventEmitter.addListener('onForegroundChange', (event) => {
const { packageName, url } = event;

let detectedItem = null;
if (url && (packageName === 'com.android.chrome' || packageName === 'com.microsoft.emmx')) {
for (const key in categoryMap) {
if (url.includes(key)) {
detectedItem = key;
break;
}
}
}

if (!detectedItem && categoryMap[packageName]) {
detectedItem = packageName;
}

if (detectedItem !== currentItem) {
currentItem = detectedItem;
handleTimerUpdate();
}
});

Copilot AI Mar 7, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The listener subscription returned by addListener isn’t stored/removed, so re-initialization paths (e.g., app-level re-mounts, dev reloads, or calling startCategoryService() again) can accumulate listeners/timers. Consider returning a cleanup function (or exposing stopCategoryService) that removes the listener and clears any active interval.

Copilot uses AI. Check for mistakes.
}

@ReactMethod
fun triggerBlock(type: String) {

Copilot AI Mar 7, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The type parameter is unused, which makes the API misleading for JS callers. Either remove it (if not needed) or use it (e.g., choose overlay text/behavior based on category), or rename to _type to clearly indicate it’s intentionally unused.

Suggested change
fun triggerBlock(type: String) {
fun triggerBlock(_type: String) {

Copilot uses AI. Check for mistakes.
}
for (i in 0 until node.childCount) {
val child = node.getChild(i) ?: continue
val result = findUrlByRecursion(child)

Copilot AI Mar 7, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AccessibilityNodeInfo objects obtained via getChild(i) should be recycled when you’re done with them to avoid resource leaks. Wrap child processing in try/finally (recycling the child in finally), and similarly ensure any root node retrieved from rootInActiveWindow is recycled when no longer needed.

Suggested change
val result = findUrlByRecursion(child)
val result = try {
findUrlByRecursion(child)
} finally {
child.recycle()
}

Copilot uses AI. Check for mistakes.
Comment on lines +77 to +82
for (i in 0 until node.childCount) {
val child = node.getChild(i) ?: continue
val result = findUrlByRecursion(child)
if (result != null) return result
}
return null

Copilot AI Mar 7, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same AccessibilityNodeInfo recycling issue as in WebsiteBlockerService: child nodes from getChild(i) should be recycled, and any root node obtained from rootInActiveWindow should also be recycled. Not doing so can increase memory pressure in frequently-called accessibility callbacks.

Copilot uses AI. Check for mistakes.
Comment on lines 25 to +33
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
android:launchMode="singleTask"
android:windowSoftInputMode="adjustResize"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

Copilot AI Mar 7, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The MainActivity declaration dropped several React Native template attributes (android:configChanges, android:windowSoftInputMode, and android:label). Removing these commonly causes unwanted Activity recreation on rotation/config changes and can regress keyboard/resizing behavior. Consider restoring the previous attributes unless there’s a specific reason to remove them.

Copilot uses AI. Check for mistakes.
Comment thread App.tsx
Comment on lines 18 to +22

const { LimitterModule, TimerEventModule } = NativeModules;

import { startCategoryService } from './src/services/categoryService';

Copilot AI Mar 7, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This import is placed after executable code. While still top-level, many TS/ESLint configurations enforce imports at the top for consistency and to avoid tooling edge-cases. Move import { startCategoryService ... } up with the other imports.

Suggested change
const { LimitterModule, TimerEventModule } = NativeModules;
import { startCategoryService } from './src/services/categoryService';
import { startCategoryService } from './src/services/categoryService';
const { LimitterModule, TimerEventModule } = NativeModules;

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants