Skip to content

Commit 9b249ba

Browse files
committed
Add fast black overlay activity after power wake
1 parent 9388067 commit 9b249ba

6 files changed

Lines changed: 408 additions & 3 deletions

File tree

1.06 MB
Binary file not shown.

app/build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ plugins {
66
alias(libs.plugins.secrets)
77
}
88

9-
val appVersionCode = 4
10-
val appVersionName = "1.3"
9+
val appVersionCode = 5
10+
val appVersionName = "1.4"
1111

1212
android {
1313
namespace = "com.example"

app/src/main/AndroidManifest.xml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,15 @@
3737
</intent-filter>
3838
</activity>
3939

40+
<activity
41+
android:name=".BlackOverlayActivity"
42+
android:excludeFromRecents="true"
43+
android:exported="false"
44+
android:launchMode="singleTask"
45+
android:showWhenLocked="true"
46+
android:theme="@style/Theme.BlackOverlay"
47+
android:turnScreenOn="true" />
48+
4049
<service
4150
android:name=".TimerService"
4251
android:enabled="true"
Lines changed: 363 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,363 @@
1+
package com.example
2+
3+
import android.app.Activity
4+
import android.app.KeyguardManager
5+
import android.content.Context
6+
import android.content.Intent
7+
import android.graphics.Color
8+
import android.os.Build
9+
import android.os.Bundle
10+
import android.os.Handler
11+
import android.os.Looper
12+
import android.os.VibrationEffect
13+
import android.os.Vibrator
14+
import android.view.Gravity
15+
import android.view.KeyEvent
16+
import android.view.MotionEvent
17+
import android.view.View
18+
import android.view.WindowInsets
19+
import android.view.WindowManager
20+
import android.widget.FrameLayout
21+
import android.widget.GridLayout
22+
import android.widget.LinearLayout
23+
import android.widget.TextView
24+
import android.widget.Toast
25+
26+
class BlackOverlayActivity : Activity() {
27+
private val handler = Handler(Looper.getMainLooper())
28+
private var immersiveRunnable: Runnable? = null
29+
30+
override fun onCreate(savedInstanceState: Bundle?) {
31+
super.onCreate(savedInstanceState)
32+
33+
if (!isTimerRunning()) {
34+
finish()
35+
return
36+
}
37+
38+
configureLockscreenWindow()
39+
40+
val rootLayout = FrameLayout(this).apply {
41+
setBackgroundColor(Color.BLACK)
42+
isFocusable = true
43+
isFocusableInTouchMode = true
44+
}
45+
46+
var clickCount = 0
47+
var lastClickTime = 0L
48+
rootLayout.setOnTouchListener { _, event ->
49+
applyImmersiveSystemUi(rootLayout)
50+
if (event.action == MotionEvent.ACTION_DOWN) {
51+
val scale = resources.displayMetrics.density
52+
val limitX = 200f * scale
53+
val limitY = 250f * scale
54+
55+
if (event.x < limitX && event.y < limitY) {
56+
val currentTime = System.currentTimeMillis()
57+
if (currentTime - lastClickTime < 1200) {
58+
clickCount++
59+
if (clickCount == 6) {
60+
Toast.makeText(this, "Kurang 1 ketukan lagi!", Toast.LENGTH_SHORT).show()
61+
} else if (clickCount >= 7) {
62+
showPinInputPad(rootLayout)
63+
clickCount = 0
64+
}
65+
} else {
66+
clickCount = 1
67+
}
68+
lastClickTime = currentTime
69+
} else {
70+
clickCount = 0
71+
}
72+
}
73+
true
74+
}
75+
76+
setContentView(rootLayout)
77+
rootLayout.requestFocus()
78+
rootLayout.post {
79+
applyImmersiveSystemUi(rootLayout)
80+
rootLayout.requestFocus()
81+
}
82+
startImmersiveGuard(rootLayout)
83+
}
84+
85+
override fun onResume() {
86+
super.onResume()
87+
if (!isTimerRunning()) {
88+
finish()
89+
return
90+
}
91+
closeSystemDialogs()
92+
window.decorView.post { applyImmersiveSystemUi(window.decorView) }
93+
}
94+
95+
override fun onWindowFocusChanged(hasFocus: Boolean) {
96+
super.onWindowFocusChanged(hasFocus)
97+
applyImmersiveSystemUi(window.decorView)
98+
if (!hasFocus) {
99+
closeSystemDialogs()
100+
handler.postDelayed({ applyImmersiveSystemUi(window.decorView) }, 30)
101+
}
102+
}
103+
104+
override fun dispatchKeyEvent(event: KeyEvent): Boolean {
105+
if (isBlockedHardwareKey(event.keyCode)) return true
106+
return super.dispatchKeyEvent(event)
107+
}
108+
109+
override fun onDestroy() {
110+
immersiveRunnable?.let { handler.removeCallbacks(it) }
111+
immersiveRunnable = null
112+
super.onDestroy()
113+
}
114+
115+
private fun configureLockscreenWindow() {
116+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
117+
setShowWhenLocked(true)
118+
setTurnScreenOn(true)
119+
(getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager)
120+
.requestDismissKeyguard(this, null)
121+
}
122+
123+
@Suppress("DEPRECATION")
124+
window.addFlags(
125+
WindowManager.LayoutParams.FLAG_FULLSCREEN or
126+
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON or
127+
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or
128+
WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD or
129+
WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
130+
)
131+
window.setBackgroundDrawableResource(android.R.color.black)
132+
133+
val attrs = window.attributes
134+
attrs.screenBrightness = WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_OFF
135+
attrs.buttonBrightness = WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_OFF
136+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
137+
attrs.layoutInDisplayCutoutMode =
138+
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
139+
}
140+
window.attributes = attrs
141+
}
142+
143+
private fun showPinInputPad(rootLayout: FrameLayout) {
144+
applyImmersiveSystemUi(rootLayout)
145+
val existingPinPad = rootLayout.findViewWithTag<View>("pin_pad_tag")
146+
if (existingPinPad != null) return
147+
148+
val correctPin = getSharedPreferences(TimerService.PREFS_NAME, Context.MODE_PRIVATE)
149+
.getString(TimerService.KEY_PIN, "1234") ?: "1234"
150+
151+
val pinPanel = LinearLayout(this).apply {
152+
tag = "pin_pad_tag"
153+
orientation = LinearLayout.VERTICAL
154+
gravity = Gravity.CENTER
155+
setBackgroundColor(Color.parseColor("#121212"))
156+
elevation = 50f
157+
setPadding(60, 60, 60, 60)
158+
layoutParams = FrameLayout.LayoutParams(650, LinearLayout.LayoutParams.WRAP_CONTENT, Gravity.CENTER)
159+
}
160+
161+
val labelHeader = TextView(this).apply {
162+
text = "KONTROL ORANG TUA"
163+
setTextColor(Color.WHITE)
164+
textSize = 14f
165+
gravity = Gravity.CENTER
166+
setPadding(0, 0, 0, 10)
167+
}
168+
val labelSub = TextView(this).apply {
169+
text = "Masukkan PIN untuk membuka HP"
170+
setTextColor(Color.GRAY)
171+
textSize = 12f
172+
gravity = Gravity.CENTER
173+
setPadding(0, 0, 0, 30)
174+
}
175+
pinPanel.addView(labelHeader)
176+
pinPanel.addView(labelSub)
177+
178+
val pinIndicatorLayout = LinearLayout(this).apply {
179+
orientation = LinearLayout.HORIZONTAL
180+
gravity = Gravity.CENTER
181+
setPadding(0, 0, 0, 40)
182+
}
183+
val indicators = Array(4) {
184+
TextView(this).apply {
185+
text = ""
186+
setTextColor(Color.WHITE)
187+
textSize = 28f
188+
setPadding(15, 0, 15, 0)
189+
}
190+
}
191+
indicators.forEach { pinIndicatorLayout.addView(it) }
192+
pinPanel.addView(pinIndicatorLayout)
193+
194+
var enteredPin = ""
195+
196+
fun updatePinIndicators() {
197+
for (i in 0 until 4) {
198+
indicators[i].text = if (i < enteredPin.length) "" else ""
199+
}
200+
}
201+
202+
val gridLayout = GridLayout(this).apply {
203+
columnCount = 3
204+
rowCount = 4
205+
alignmentMode = GridLayout.ALIGN_BOUNDS
206+
layoutParams = LinearLayout.LayoutParams(
207+
LinearLayout.LayoutParams.MATCH_PARENT,
208+
LinearLayout.LayoutParams.WRAP_CONTENT
209+
)
210+
}
211+
212+
val digitClickListener = View.OnClickListener { v ->
213+
if (v is TextView && enteredPin.length < 4) {
214+
enteredPin += v.text.toString()
215+
updatePinIndicators()
216+
217+
if (enteredPin.length == 4) {
218+
if (enteredPin == correctPin) {
219+
Toast.makeText(this, "Akses Terbuka", Toast.LENGTH_SHORT).show()
220+
stopTimerService()
221+
finishAndRemoveTask()
222+
} else {
223+
vibrateWrongPin()
224+
labelSub.text = "PIN SALAH! Coba lagi..."
225+
labelSub.setTextColor(Color.RED)
226+
enteredPin = ""
227+
updatePinIndicators()
228+
}
229+
}
230+
}
231+
}
232+
233+
fun createKeypadButton(value: String, clickListener: View.OnClickListener): TextView {
234+
return TextView(this).apply {
235+
text = value
236+
setTextColor(Color.WHITE)
237+
textSize = 22f
238+
gravity = Gravity.CENTER
239+
isClickable = true
240+
setBackgroundColor(Color.parseColor("#222222"))
241+
setOnClickListener(clickListener)
242+
setPadding(0, 30, 0, 30)
243+
layoutParams = GridLayout.LayoutParams().apply {
244+
width = 150
245+
height = 130
246+
columnSpec = GridLayout.spec(GridLayout.UNDEFINED, 1f)
247+
rowSpec = GridLayout.spec(GridLayout.UNDEFINED, 1f)
248+
setMargins(10, 10, 10, 10)
249+
}
250+
}
251+
}
252+
253+
for (i in 1..9) {
254+
gridLayout.addView(createKeypadButton(i.toString(), digitClickListener))
255+
}
256+
gridLayout.addView(createKeypadButton("X") { rootLayout.removeView(pinPanel) }.apply {
257+
setTextColor(Color.RED)
258+
textSize = 14f
259+
})
260+
gridLayout.addView(createKeypadButton("0", digitClickListener))
261+
gridLayout.addView(createKeypadButton("") {
262+
if (enteredPin.isNotEmpty()) {
263+
enteredPin = enteredPin.substring(0, enteredPin.length - 1)
264+
updatePinIndicators()
265+
}
266+
}.apply { setTextColor(Color.YELLOW) })
267+
268+
pinPanel.addView(gridLayout)
269+
rootLayout.addView(pinPanel)
270+
}
271+
272+
private fun stopTimerService() {
273+
val intent = Intent(this, TimerService::class.java).apply {
274+
action = TimerService.ACTION_STOP
275+
}
276+
startService(intent)
277+
}
278+
279+
private fun vibrateWrongPin() {
280+
val vibrator = getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
281+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
282+
vibrator.vibrate(VibrationEffect.createOneShot(300, VibrationEffect.DEFAULT_AMPLITUDE))
283+
} else {
284+
@Suppress("DEPRECATION")
285+
vibrator.vibrate(300)
286+
}
287+
}
288+
289+
private fun isTimerRunning(): Boolean {
290+
return getSharedPreferences(TimerService.PREFS_NAME, Context.MODE_PRIVATE)
291+
.getBoolean(TimerService.KEY_IS_RUNNING, false)
292+
}
293+
294+
private fun startImmersiveGuard(view: View) {
295+
immersiveRunnable?.let { handler.removeCallbacks(it) }
296+
immersiveRunnable = object : Runnable {
297+
override fun run() {
298+
if (!isTimerRunning()) {
299+
finish()
300+
return
301+
}
302+
closeSystemDialogs()
303+
applyImmersiveSystemUi(view)
304+
view.requestFocus()
305+
handler.postDelayed(this, 200)
306+
}
307+
}
308+
handler.post(immersiveRunnable!!)
309+
}
310+
311+
private fun closeSystemDialogs() {
312+
try {
313+
@Suppress("DEPRECATION")
314+
sendBroadcast(Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS))
315+
} catch (e: Exception) {
316+
// Some Android versions ignore or block this for third-party apps.
317+
}
318+
}
319+
320+
private fun applyImmersiveSystemUi(view: View) {
321+
@Suppress("DEPRECATION")
322+
view.systemUiVisibility = (
323+
View.SYSTEM_UI_FLAG_FULLSCREEN
324+
or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
325+
or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
326+
or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
327+
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
328+
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
329+
)
330+
331+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
332+
view.windowInsetsController?.let { controller ->
333+
controller.hide(WindowInsets.Type.statusBars() or WindowInsets.Type.navigationBars())
334+
controller.systemBarsBehavior =
335+
android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
336+
}
337+
}
338+
}
339+
340+
private fun isBlockedHardwareKey(keyCode: Int): Boolean {
341+
return when (keyCode) {
342+
KeyEvent.KEYCODE_BACK,
343+
KeyEvent.KEYCODE_HOME,
344+
KeyEvent.KEYCODE_APP_SWITCH,
345+
KeyEvent.KEYCODE_ASSIST,
346+
KeyEvent.KEYCODE_MENU,
347+
KeyEvent.KEYCODE_SEARCH,
348+
KeyEvent.KEYCODE_CAMERA,
349+
KeyEvent.KEYCODE_FOCUS,
350+
KeyEvent.KEYCODE_POWER,
351+
KeyEvent.KEYCODE_SLEEP,
352+
KeyEvent.KEYCODE_WAKEUP,
353+
KeyEvent.KEYCODE_VOLUME_UP,
354+
KeyEvent.KEYCODE_VOLUME_DOWN,
355+
KeyEvent.KEYCODE_VOLUME_MUTE,
356+
KeyEvent.KEYCODE_MUTE,
357+
KeyEvent.KEYCODE_NOTIFICATION,
358+
KeyEvent.KEYCODE_BRIGHTNESS_UP,
359+
KeyEvent.KEYCODE_BRIGHTNESS_DOWN -> true
360+
else -> false
361+
}
362+
}
363+
}

0 commit comments

Comments
 (0)