Skip to content

Commit e7cacb6

Browse files
committed
fix(privacy): improve background playback and ad blocking
- Prevent blocking of media streams from googlevideo.com - Enhance WebView background playback reliability by overriding visibility and focus state dispatchers - Introduce user interaction tracking in injection script to distinguish between explicit and background pauses
1 parent f00ead3 commit e7cacb6

5 files changed

Lines changed: 246 additions & 63 deletions

File tree

app/src/main/java/com/example/privacy/ContentBlocker.kt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,13 +93,18 @@ object ContentBlocker {
9393
fun shouldBlock(uri: Uri, isGlobalBlockerEnabled: Boolean, isSiteWhitelisted: Boolean): Boolean {
9494
if (!isGlobalBlockerEnabled || isSiteWhitelisted) return false
9595

96+
val host = uri.host?.lowercase() ?: return false
97+
98+
// NEVER block googlevideo.com media streams
99+
if (host.contains("googlevideo.com")) {
100+
return false
101+
}
102+
96103
// Check specialized YouTube ad endpoints
97104
if (YouTubeAdBlocker.isYouTubeAdRequest(uri)) {
98105
_totalBlockedCount.value += 1
99106
return true
100107
}
101-
102-
val host = uri.host?.lowercase() ?: return false
103108
val pathAndQuery = (uri.path ?: "") + (uri.query?.let { "?$it" } ?: "")
104109

105110
// Check host suffix match (e.g. ad.doubleclick.net endsWith doubleclick.net)

app/src/main/java/com/example/privacy/FingerprintScriptGenerator.kt

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,20 @@ object FingerprintScriptGenerator {
152152
window.IntersectionObserver.prototype = OrigIO.prototype;
153153
}
154154
155-
// 5. Media Session API hooking for notification media player
155+
// 5. Track genuine user interactions so we differentiate user pauses from backgrounding pauses
156+
window.__feather_explicit_pause = false;
157+
let lastUserTouchTime = 0;
158+
const recordUserTouch = function(e) {
159+
if (e && e.isTrusted) {
160+
lastUserTouchTime = Date.now();
161+
}
162+
};
163+
['pointerdown', 'mousedown', 'touchstart', 'click', 'keydown'].forEach(function(evt) {
164+
window.addEventListener(evt, recordUserTouch, true);
165+
document.addEventListener(evt, recordUserTouch, true);
166+
});
167+
168+
// 6. Media Session API hooking for notification media player
156169
window.__feather_actions = window.__feather_actions || {};
157170
158171
function getMediaThumbnail() {
@@ -246,18 +259,21 @@ object FingerprintScriptGenerator {
246259
}
247260
} catch(e) {}
248261
249-
// 6. Direct media element tracking & action triggers
262+
// 7. Direct media element tracking & action triggers
250263
window.__feather_media_play = function() {
264+
window.__feather_explicit_pause = false;
251265
try {
252266
if (window.__feather_actions && typeof window.__feather_actions['play'] === 'function') {
253267
window.__feather_actions['play']();
268+
notifyMediaBridge(true);
254269
return;
255270
}
256271
} catch(e) {}
257272
try {
258273
const moviePlayer = document.getElementById('movie_player');
259274
if (moviePlayer && typeof moviePlayer.playVideo === 'function') {
260275
moviePlayer.playVideo();
276+
notifyMediaBridge(true);
261277
return;
262278
}
263279
} catch(e) {}
@@ -267,6 +283,7 @@ object FingerprintScriptGenerator {
267283
mediaEls.forEach(function(m) {
268284
m.play().catch(function() {});
269285
});
286+
notifyMediaBridge(true);
270287
}
271288
} catch(e) {}
272289
try {
@@ -276,16 +293,19 @@ object FingerprintScriptGenerator {
276293
};
277294
278295
window.__feather_media_pause = function() {
296+
window.__feather_explicit_pause = true;
279297
try {
280298
if (window.__feather_actions && typeof window.__feather_actions['pause'] === 'function') {
281299
window.__feather_actions['pause']();
300+
notifyMediaBridge(false);
282301
return;
283302
}
284303
} catch(e) {}
285304
try {
286305
const moviePlayer = document.getElementById('movie_player');
287306
if (moviePlayer && typeof moviePlayer.pauseVideo === 'function') {
288307
moviePlayer.pauseVideo();
308+
notifyMediaBridge(false);
289309
return;
290310
}
291311
} catch(e) {}
@@ -295,6 +315,7 @@ object FingerprintScriptGenerator {
295315
mediaEls.forEach(function(m) {
296316
m.pause();
297317
});
318+
notifyMediaBridge(false);
298319
}
299320
} catch(e) {}
300321
try {
@@ -334,24 +355,33 @@ object FingerprintScriptGenerator {
334355
}
335356
};
336357
337-
// Hook HTMLMediaElement prototype play and pause
358+
// 8. Hook HTMLMediaElement prototype play and pause with background auto-pause defense
338359
try {
339360
const origPlay = HTMLMediaElement.prototype.play;
340361
HTMLMediaElement.prototype.play = function() {
362+
window.__feather_explicit_pause = false;
341363
notifyMediaBridge(true);
342364
return origPlay.apply(this, arguments);
343365
};
344366
345367
const origPause = HTMLMediaElement.prototype.pause;
346368
HTMLMediaElement.prototype.pause = function() {
369+
// Check if the pause call was triggered by genuine user action or explicitly by media session bridge
370+
const isExplicitUserPause = window.__feather_explicit_pause || (Date.now() - lastUserTouchTime < 800);
371+
if (!isExplicitUserPause) {
372+
// Suppress unwanted auto-pause triggered by YouTube visibility listeners or background state!
373+
return;
374+
}
347375
notifyMediaBridge(false);
348376
return origPause.apply(this, arguments);
349377
};
350378
} catch(e) {}
351379
352-
// Periodic monitor for HTML media elements
380+
// 9. Periodic monitor & watchdog for HTML media elements
353381
let lastReportedState = null;
354382
let lastReportedTitle = '';
383+
let wasActivePlayback = false;
384+
355385
const monitorMedia = function() {
356386
try {
357387
const els = document.querySelectorAll('video, audio');
@@ -364,11 +394,14 @@ object FingerprintScriptGenerator {
364394
el.__feather_monitored = true;
365395
366396
el.addEventListener('play', function() {
397+
window.__feather_explicit_pause = false;
367398
notifyMediaBridge(true);
368399
});
369400
370401
el.addEventListener('pause', function() {
371-
notifyMediaBridge(false);
402+
if (window.__feather_explicit_pause || (Date.now() - lastUserTouchTime < 800)) {
403+
notifyMediaBridge(false);
404+
}
372405
});
373406
374407
el.addEventListener('ended', function() {
@@ -378,6 +411,17 @@ object FingerprintScriptGenerator {
378411
});
379412
});
380413
414+
if (anyPlaying) {
415+
wasActivePlayback = true;
416+
} else if (wasActivePlayback && !window.__feather_explicit_pause && (Date.now() - lastUserTouchTime > 1500)) {
417+
// Video unexpectedly paused while in the background without user touch!
418+
// Auto-resume background playback
419+
const video = document.querySelector('video');
420+
if (video && video.paused && !video.ended) {
421+
video.play().catch(function() {});
422+
}
423+
}
424+
381425
const currentTitle = getMediaTitle();
382426
if (anyPlaying !== lastReportedState || (anyPlaying && currentTitle !== lastReportedTitle)) {
383427
lastReportedState = anyPlaying;
@@ -386,7 +430,7 @@ object FingerprintScriptGenerator {
386430
}
387431
} catch(e) {}
388432
};
389-
setInterval(monitorMedia, 1500);
433+
setInterval(monitorMedia, 800);
390434
monitorMedia();
391435
} catch(e) {}
392436
})();

0 commit comments

Comments
 (0)