Skip to content

Commit 745f009

Browse files
author
alex-hermes-agent
committed
feat(v1.20): overlay label + best-effort force-stop under blackout + 120ms poll + detection-lag logs
1 parent 586f52a commit 745f009

5 files changed

Lines changed: 49 additions & 10 deletions

File tree

app/build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ android {
1515
applicationId = "com.alexk.tvlock"
1616
minSdk = 21
1717
targetSdk = 34
18-
versionCode = 20
19-
versionName = "1.19"
18+
versionCode = 21
19+
versionName = "1.20"
2020
}
2121

2222
if (!releaseKeystore.isNullOrBlank()) {

app/src/main/java/com/alexk/tvlock/GateOverlay.kt

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,23 @@ object GateOverlay {
4848
}
4949
try {
5050
wm = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
51-
val black = object : View(context) {
51+
val root = object : android.widget.FrameLayout(context) {
5252
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean = true
5353
override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean = true
5454
override fun onTouchEvent(event: MotionEvent): Boolean = true
5555
}
56-
black.isFocusable = true
57-
black.isFocusableInTouchMode = true
56+
val label = android.widget.TextView(context).apply {
57+
text = context.getString(R.string.overlay_locked)
58+
setTextColor(Color.WHITE)
59+
textSize = 28f
60+
gravity = android.view.Gravity.CENTER
61+
}
62+
root.addView(label, android.widget.FrameLayout.LayoutParams(
63+
android.widget.FrameLayout.LayoutParams.WRAP_CONTENT,
64+
android.widget.FrameLayout.LayoutParams.WRAP_CONTENT,
65+
android.view.Gravity.CENTER))
66+
root.isFocusable = true
67+
root.isFocusableInTouchMode = true
5868
val type = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
5969
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
6070
else
@@ -69,10 +79,10 @@ object GateOverlay {
6979
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS or
7080
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON,
7181
PixelFormat.OPAQUE)
72-
black.setBackgroundColor(Color.BLACK)
73-
wm!!.addView(black, lp)
74-
black.requestFocus()
75-
view = black
82+
root.setBackgroundColor(Color.BLACK)
83+
wm!!.addView(root, lp)
84+
root.requestFocus()
85+
view = root
7686
shownAt = now
7787
android.util.Log.i("TVLOCK", "blackout overlay shown")
7888
} catch (e: Exception) {

app/src/main/java/com/alexk/tvlock/LockPollService.kt

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,10 @@ class LockPollService : Service() {
139139
}
140140
unlockUntil = Prefs(this).unlockUntil()
141141
if (t in blocked && foregroundChanged && now >= unlockUntil) {
142+
// evTs = the event's own timestamp; now-evTs = how late UsageStats
143+
// surfaced it to us. This is the detection gap the overlay has to
144+
// beat on a fast dedicated-button press.
145+
android.util.Log.i("TVLOCK", "detect lag=${now - evTs}ms pkg=$t")
142146
raiseGate(t, now)
143147
}
144148
}
@@ -147,6 +151,8 @@ class LockPollService : Service() {
147151
gateUp = true
148152
gateRaisedAt = now
149153
targetPkg = t
154+
// detection latency log: evTs->now tells us how late the UsageStats
155+
// event itself is; (now - gateRaisedAt) at the hide tells gate launch lag
150156
android.util.Log.i("TVLOCK", "gate raising for $t")
151157
try {
152158
// instant blackout FIRST: paints in <100 ms from the service and
@@ -159,6 +165,27 @@ class LockPollService : Service() {
159165
Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS or
160166
Intent.FLAG_ACTIVITY_SINGLE_TOP)
161167
})
168+
// Kill the app underneath the blackout: even if the profile picker
169+
// rendered before the overlay landed, taps cannot reach it and no
170+
// video can start. Safe to do because onUnlocked() relaunches the
171+
// app by package after a correct PIN. Guarded by gateUp so a fast
172+
// unlock (gateUp=false before this fires) never kills the app the
173+
// user is legitimately opening.
174+
handler.postDelayed({
175+
if (gateUp && targetPkg == t) {
176+
try {
177+
val am = Class.forName("android.app.ActivityManager")
178+
.getMethod("getService").invoke(null)
179+
am.javaClass.getMethod("forceStopPackage", String::class.java, Int::class.java)
180+
.invoke(am, t, 0)
181+
android.util.Log.i("TVLOCK", "force-stopped $t under blackout")
182+
} catch (e: Exception) {
183+
// hidden API blocked on this ROM (likely on API 34) — the
184+
// blackout + gate still protect; this is best-effort only
185+
android.util.Log.w("TVLOCK", "force-stop unavailable: ${e.javaClass.simpleName}")
186+
}
187+
}
188+
}, 500)
162189
// Cooldown window opens now (gate visible on the launch path / or when
163190
// the watchdog re-arms after seeing it on top, see tick()).
164191
recoveryReadyAt = now + RECOVERY_COOLDOWN_MS
@@ -237,7 +264,7 @@ class LockPollService : Service() {
237264
override fun onBind(intent: Intent?): IBinder? = null
238265

239266
companion object {
240-
const val POLL_MS = 250L
267+
const val POLL_MS = 120L
241268
const val GATE_CONFIRM_MS = 2_500L
242269
// Cooldown window for the watchdog recovery re-raise, measured from the
243270
// last raise. The 2.5 s watchdog check paces it to >= 2.5 s anyway; this

app/src/main/res/values-uk/strings.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<resources>
33
<string name="locked_title">🔒 Захищений додаток</string>
4+
<string name="overlay_locked">⏳ Завантажую екран блокування…</string>
45
<string name="pin_hint">Введіть PIN з пульта (4 цифри)</string>
56
<string name="wrong_pin">Невірний PIN</string>
67
<string name="unlocked">Розблоковано</string>

app/src/main/res/values/strings.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
<string name="app_title">TV App Lock</string>
44
<string name="service_description">TV App Lock</string>
55
<string name="locked_title">🔒 Protected app</string>
6+
<string name="overlay_locked">⏳ Loading lock screen…</string>
67
<string name="pin_hint">Enter the 4-digit PIN with your remote</string>
78
<string name="wrong_pin">Wrong PIN</string>
89
<string name="unlocked">Unlocked</string>

0 commit comments

Comments
 (0)