Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ android {
minSdk = 23
targetSdk = 36
versionCode = 48
versionName = "1.0.48.0"
versionName = "1.0.48.2"
}

buildTypes {
Expand Down Expand Up @@ -86,4 +86,4 @@ dependencies {
androidTestImplementation(libs.androidx.ui.test.junit4)
debugImplementation(libs.androidx.ui.tooling)
debugImplementation(libs.androidx.ui.test.manifest)
}
}
6 changes: 4 additions & 2 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

git ls-files | grep -E "(build\.gradle|AndroidManifest|flavor)" | head -20

Repository: immichFrame/ImmichFrame_Android

Length of output: 145


🏁 Script executed:

fd -t f "AndroidManifest" -o "build.gradle"

Repository: immichFrame/ImmichFrame_Android

Length of output: 218


🏁 Script executed:

cd app && find . -name "AndroidManifest.xml" -o -name "build.gradle*" | head -20

Repository: immichFrame/ImmichFrame_Android

Length of output: 124


🏁 Script executed:

cat -n app/build.gradle.kts | head -100

Repository: immichFrame/ImmichFrame_Android

Length of output: 3658


🏁 Script executed:

cat -n app/src/main/AndroidManifest.xml

Repository: immichFrame/ImmichFrame_Android

Length of output: 4149


🏁 Script executed:

rg "WRITE_SETTINGS" -B 3 -A 3

Repository: immichFrame/ImmichFrame_Android

Length of output: 663


🏁 Script executed:

rg "WRITE_SETTINGS\|Settings\.ACTION_MANAGE_WRITE_SETTINGS" app/src -B 2 -A 2

Repository: immichFrame/ImmichFrame_Android

Length of output: 57


🏁 Script executed:

grep -r "flavors\|productFlavors\|flavorDimensions" app/build.gradle.kts

Repository: immichFrame/ImmichFrame_Android

Length of output: 57


🏁 Script executed:

find app/src -name "*.kt" -o -name "*.java" | xargs grep -l "WRITE_SETTINGS\|Settings\|ACTION_MANAGE_WRITE_SETTINGS" 2>/dev/null

Repository: immichFrame/ImmichFrame_Android

Length of output: 456


🏁 Script executed:

cat app/src/main/java/com/immichframe/immichframe/SettingsActivity.kt

Repository: immichFrame/ImmichFrame_Android

Length of output: 494


🏁 Script executed:

cat app/src/main/java/com/immichframe/immichframe/SettingsFragment.kt

Repository: immichFrame/ImmichFrame_Android

Length of output: 7814


🏁 Script executed:

rg "ACTION_MANAGE_WRITE_SETTINGS\|canWrite\|WRITE_SETTINGS" app/src/main/java --type kt -B 3 -A 3

Repository: immichFrame/ImmichFrame_Android

Length of output: 101


🏁 Script executed:

rg "ACTION_MANAGE_WRITE_SETTINGS|canWrite|WRITE_SETTINGS" app/src/main/java

Repository: immichFrame/ImmichFrame_Android

Length of output: 57


🏁 Script executed:

rg "keepScreenOn|screenTimeout|dim" app/src/main/java --type-add "kt:*.kt" -B 1 -A 1

Repository: immichFrame/ImmichFrame_Android

Length of output: 11002


🏁 Script executed:

cat app/src/main/java/com/immichframe/immichframe/ScreenSaverService.kt

Repository: immichFrame/ImmichFrame_Android

Length of output: 23994


Remove unused WRITE_SETTINGS permission.

WRITE_SETTINGS is declared in the manifest but not used anywhere in the codebase. Screen timeout management is handled via app-level WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, not system Settings. This unused permission declaration will likely trigger Play Store policy review or rejection. Either remove it or implement and document its actual usage.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/AndroidManifest.xml` at line 9, Remove the unused WRITE_SETTINGS
permission declaration from AndroidManifest.xml: delete the line declaring
android.permission.WRITE_SETTINGS since the app uses
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON for screen timeout and does not
interact with system Settings; if you actually need to modify system settings
instead, implement that logic where appropriate and add the corresponding
rationale and documentation, otherwise simply remove the <uses-permission
android:name="android.permission.WRITE_SETTINGS" /> entry.


<uses-feature
android:name="android.hardware.touchscreen"
Expand Down Expand Up @@ -65,7 +66,8 @@
</activity>
<activity android:name=".WidgetConfigActivity"
android:label="Widget Configuration"
android:theme="@style/Theme.WidgetConfigDialog" />
android:theme="@style/Theme.WidgetConfigDialog"
android:exported="true" />
Comment on lines 67 to +70
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n -C3 'EXTRA_APPWIDGET_ID|INVALID_APPWIDGET_ID|RESULT_CANCELED|RESULT_OK|widgetBackground' app/src/main/java/com/immichframe/immichframe/WidgetConfigActivity.kt

Repository: immichFrame/ImmichFrame_Android

Length of output: 1048


🏁 Script executed:

cat app/src/main/java/com/immichframe/immichframe/WidgetConfigActivity.kt

Repository: immichFrame/ImmichFrame_Android

Length of output: 2214


Reject invalid appWidgetId before accessing preferences.

WidgetConfigActivity reads and writes widget preferences without validating EXTRA_APPWIDGET_ID. If called by an external app without providing a valid widget ID, the activity defaults to INVALID_APPWIDGET_ID and proceeds to access shared preferences anyway, returning RESULT_OK regardless. Add an early check to return RESULT_CANCELED and finish if the widget ID is invalid:

if (appWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {
    setResult(RESULT_CANCELED)
    finish()
    return
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/AndroidManifest.xml` around lines 67 - 70, WidgetConfigActivity
currently proceeds to read/write widget preferences without validating the
incoming EXTRA_APPWIDGET_ID; add an early guard in WidgetConfigActivity (after
extracting appWidgetId from the intent) that checks if appWidgetId ==
AppWidgetManager.INVALID_APPWIDGET_ID and if so calls
setResult(RESULT_CANCELED), finish(), and returns to prevent further preference
access and accidental RESULT_OK for invalid requests.

<receiver android:name=".WidgetProvider"
android:exported="true">
<intent-filter>
Expand All @@ -80,4 +82,4 @@
</receiver>
</application>

</manifest>
</manifest>
196 changes: 161 additions & 35 deletions app/src/main/java/com/immichframe/immichframe/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,17 @@ package com.immichframe.immichframe
import android.animation.ObjectAnimator
import android.animation.PropertyValuesHolder
import android.annotation.SuppressLint
import android.app.KeyguardManager
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.Color
import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.os.PowerManager
import android.provider.Settings
import android.text.SpannableString
import android.text.Spanned
import android.text.style.RelativeSizeSpan
Expand Down Expand Up @@ -75,6 +79,8 @@ class MainActivity : AppCompatActivity() {
private var previousImage: Helpers.ImageResponse? = null
private var currentImage: Helpers.ImageResponse? = null
private var portraitCache: Helpers.ImageResponse? = null
private var originalScreenTimeout: Int = -1
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Make timeout backup lifecycle-safe and idempotent.

Line 82 keeps backup only in memory, and Lines 795-797 can overwrite it on repeated screenDim(true) calls (e.g., duplicate RPCs). That risks restoring 3000 instead of the user’s real value.

Proposed fix (idempotent backup/restore)
     private fun screenDim(dim: Boolean) {
+        val isCurrentlyDimmed = dimOverlay.isVisible
+        if (dim == isCurrentlyDimmed) return
+
         if (dim) {
             // save user's screen timeout and set to 3s to show "going to sleep" message
-            originalScreenTimeout = Settings.System.getInt(
-                contentResolver, Settings.System.SCREEN_OFF_TIMEOUT, 30000
-            )
+            if (originalScreenTimeout == -1) {
+                originalScreenTimeout = Settings.System.getInt(
+                    contentResolver, Settings.System.SCREEN_OFF_TIMEOUT, 30000
+                )
+            }
             setScreenTimeout(3000)
@@
             if (originalScreenTimeout != -1) {
                 setScreenTimeout(originalScreenTimeout)
+                originalScreenTimeout = -1
             }

Also persist the backup (e.g., SharedPreferences) so it can be restored after process death/crash.

Also applies to: 795-799, 829-831

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/java/com/immichframe/immichframe/MainActivity.kt` at line 82,
The current in-memory backup originalScreenTimeout is overwritten on repeated
screenDim(true) calls and lost on process death; make the backup lifecycle-safe
and idempotent by: only assign originalScreenTimeout when it is the unset
sentinel (e.g., -1) inside the method handling screenDim(true) so repeated calls
do not overwrite the real user value, persist the backup to SharedPreferences
when first saving it, and on restore (screenDim(false)) read and apply the saved
value only if a valid backup exists then clear both the in-memory sentinel and
the SharedPreferences key; also load any persisted backup into
originalScreenTimeout during startup (e.g., in onCreate) so crashes/process
death can still restore the user’s timeout.

private var isSnoozing = false
private val imageRunnable = object : Runnable {
override fun run() {
if (isImageTimerRunning) {
Expand All @@ -97,6 +103,12 @@ class MainActivity : AppCompatActivity() {
handler.postDelayed(this, 30000)
}
}
private val redimRunnable = object : Runnable {
override fun run() {
isSnoozing = false
screenDim(true)
}
}
private var isShowingFirst = true
private var zoomAnimator: ObjectAnimator? = null

Expand Down Expand Up @@ -488,6 +500,7 @@ class MainActivity : AppCompatActivity() {
attemptFetch()
}

// called when app starts and when user returns from settings screen
@SuppressLint("SetJavaScriptEnabled")
private fun loadSettings() {
val prefs = PreferenceManager.getDefaultSharedPreferences(applicationContext)
Expand Down Expand Up @@ -518,7 +531,7 @@ class MainActivity : AppCompatActivity() {
handler.post(dimCheckRunnable)
} else {
handler.removeCallbacks(dimCheckRunnable)
dimOverlay.visibility = View.GONE
removeDimOverlay()
val lp = WindowManager.LayoutParams()
lp.copyFrom(window.attributes)
lp.screenBrightness = 1f
Expand Down Expand Up @@ -607,6 +620,7 @@ class MainActivity : AppCompatActivity() {
}
)
}
applyScreenTimeout()
}

private fun onSettingsLoaded() {
Expand Down Expand Up @@ -770,39 +784,137 @@ class MainActivity : AppCompatActivity() {
}
}

private fun screenDim(dim: Boolean) {
val lp = window.attributes
if (dim) {
lp.screenBrightness = 0.01f
window.attributes = lp
if (dimOverlay.visibility != View.VISIBLE) {
dimOverlay.apply {
visibility = View.VISIBLE
alpha = 0f
if (useWebView) {
webView.loadUrl("about:blank")
} else {
stopImageTimer()
stopWeatherTimer()
// set screen timeout in Android settings in microseconds
private fun setScreenTimeout(timeout: Int) {
try {
Settings.System.putInt(
contentResolver,
Settings.System.SCREEN_OFF_TIMEOUT,
timeout
)
} catch (e: Exception) {
Log.e("Settings", "Could not set screen timeout: ${e.message}")
}
Comment on lines +788 to +797
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify timeout writes and permission patterns
echo "=== Checking SCREEN_OFF_TIMEOUT and Settings API usage ==="
rg -n -C3 'SCREEN_OFF_TIMEOUT|Settings\.System\.putInt|Settings\.System\.canWrite|ACTION_MANAGE_WRITE_SETTINGS' app/src/main/java/com/immichframe/immichframe/MainActivity.kt

echo ""
echo "=== Checking AndroidManifest.xml for WRITE_SETTINGS permission ==="
rg -n 'WRITE_SETTINGS' app/src/main/AndroidManifest.xml

Repository: immichFrame/ImmichFrame_Android

Length of output: 953


🌐 Web query:

Android Settings.System.putInt WRITE_SETTINGS permission canWrite requirement documentation

💡 Result:

Settings.System.putInt() modifies values in the system settings table, so on Android 6.0 (API 23) and higher you must have the special WRITE_SETTINGS authorization (not a normal runtime permission prompt).

What the official docs say:

  • Declare the permission in the manifest: android.permission.WRITE_SETTINGS. If your app targets API 23+, the user must explicitly grant this via a management screen (Settings.ACTION_MANAGE_WRITE_SETTINGS). You can verify access with Settings.System.canWrite(context). [1]
  • Settings.System.canWrite(Context) “checks if the specified app can modify system settings” and reiterates the requirement: manifest declaration plus user grant via ACTION_MANAGE_WRITE_SETTINGS. [2]
  • This is considered a special permission that’s granted from Settings > Special app access, not via requestPermissions(). [3]

Minimal reference flow (API 23+):

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.System.canWrite(context)) {
  Intent i = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS);
  i.setData(Uri.parse("package:" + context.getPackageName()));
  context.startActivity(i);
} else {
  Settings.System.putInt(context.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS, 100);
}

Sources:
[1] Android Manifest.permission.WRITE_SETTINGS reference (note about API 23+, manage screen, and canWrite()). (developer.android.com)
[2] Android Settings.System.canWrite() reference (requirements and ACTION_MANAGE_WRITE_SETTINGS). (developer.android.com)
[3] Android “Request special permissions” guide (special app access workflow). (developer.android.com)

Citations:


Guard SCREEN_OFF_TIMEOUT writes behind Settings.System.canWrite(...).

The WRITE_SETTINGS permission requires explicit user grant through device Settings on Android 6.0+. Without checking Settings.System.canWrite() before calling putInt(), these writes will fail when the user hasn't granted the permission, breaking the screen timeout feature.

Proposed fix
-    private fun setScreenTimeout(timeout: Int) {
+    private fun setScreenTimeout(timeout: Int): Boolean {
+        if (!Settings.System.canWrite(this)) {
+            Log.w("Settings", "WRITE_SETTINGS not granted; cannot set screen timeout")
+            return false
+        }
         try {
-            Settings.System.putInt(
+            return Settings.System.putInt(
                 contentResolver,
                 Settings.System.SCREEN_OFF_TIMEOUT,
                 timeout
             )
         } catch (e: Exception) {
             Log.e("Settings", "Could not set screen timeout: ${e.message}")
+            return false
         }
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/java/com/immichframe/immichframe/MainActivity.kt` around lines
779 - 788, The setScreenTimeout function calls Settings.System.putInt without
checking Settings.System.canWrite; before attempting the write, check
Settings.System.canWrite(contentResolver) and if it returns false, avoid calling
putInt and instead trigger the WRITE_SETTINGS flow (e.g., launch an Intent with
ACTION_MANAGE_WRITE_SETTINGS for the app) or surface a user-facing prompt to
request the permission; keep the existing try/catch for putInt but only execute
it when canWrite is true to prevent silent failures and log or handle the
denied-permission path accordingly.

}

// apply user-entered screen timeout value
private fun applyScreenTimeout() {
val prefs = PreferenceManager.getDefaultSharedPreferences(applicationContext)

if (keepScreenOn) {
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
} else {
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
val timeoutMinutes = prefs.getString("screenTimeout", "10")?.toIntOrNull() ?: 10

// capture original system timeout once if we haven't yet
if (originalScreenTimeout == -1) {
originalScreenTimeout = Settings.System.getInt(
contentResolver, Settings.System.SCREEN_OFF_TIMEOUT, 30000
)
}
setScreenTimeout(timeoutMinutes * 60 * 1000)
}
}

// show the black overlay and pause activity
private fun applyDimOverlay() {
if (dimOverlay.visibility != View.VISIBLE || dimOverlay.alpha < 0.5f) {
dimOverlay.apply {
visibility = View.VISIBLE
alpha = 0f
if (useWebView) {
webView.loadUrl("about:blank")
} else {
stopImageTimer()
stopWeatherTimer()
}
animate()
.alpha(0.99f)
.setDuration(500L)
.start()
}

// display message to user that screen is going to sleep
Toast.makeText(
this@MainActivity,
"Going to sleep",
Toast.LENGTH_LONG
).show()
}
}

// hide the black overlay and resume activity
private fun removeDimOverlay() {
// remove black overlay and restore webview settings
handler.removeCallbacks(redimRunnable)
if (dimOverlay.isVisible) {
dimOverlay.animate()
.alpha(0f)
.setDuration(500L)
.withEndAction {
dimOverlay.visibility = View.GONE
loadSettings() // Restores WebView and timers
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
setShowWhenLocked(false)
}
animate()
.alpha(0.99f)
.setDuration(500L)
.start()
}
.start()
}
}

@Suppress("DEPRECATION")
private fun screenDim(dim: Boolean) {
if (dim) {
// save user's screen timeout and set to 3s to show "going to sleep" message
if (originalScreenTimeout == -1) {
originalScreenTimeout = Settings.System.getInt(
contentResolver, Settings.System.SCREEN_OFF_TIMEOUT, 30000
)
}
setScreenTimeout(3000)
Comment on lines 868 to +875
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, let's find the MainActivity.kt file
find . -name "MainActivity.kt" -type f

Repository: immichFrame/ImmichFrame_Android

Length of output: 138


🏁 Script executed:

#!/bin/bash
# Get file size first
wc -l ./app/src/main/java/com/immichframe/immichframe/MainActivity.kt

Repository: immichFrame/ImmichFrame_Android

Length of output: 142


🏁 Script executed:

#!/bin/bash
# Read the context around lines 817-824
sed -n '800,840p' ./app/src/main/java/com/immichframe/immichframe/MainActivity.kt

Repository: immichFrame/ImmichFrame_Android

Length of output: 1786


🏁 Script executed:

#!/bin/bash
# Read the context around lines 850-910
sed -n '850,910p' ./app/src/main/java/com/immichframe/immichframe/MainActivity.kt

Repository: immichFrame/ImmichFrame_Android

Length of output: 2914


🏁 Script executed:

#!/bin/bash
# Search for screenDim calls and how they're scheduled
rg "screenDim" ./app/src/main/java/com/immichframe/immichframe/MainActivity.kt -A 2 -B 2

Repository: immichFrame/ImmichFrame_Android

Length of output: 1104


🏁 Script executed:

#!/bin/bash
# Search for dimCheckRunnable definition
rg "dimCheckRunnable" ./app/src/main/java/com/immichframe/immichframe/MainActivity.kt -A 10 -B 2

Repository: immichFrame/ImmichFrame_Android

Length of output: 1004


🏁 Script executed:

#!/bin/bash
# Let me verify the complete flow by checking when setScreenTimeout(3000) actually lets device sleep
# and whether there's any other wake-up mechanism
rg "setScreenTimeout|AlarmManager|WakeLock" ./app/src/main/java/com/immichframe/immichframe/MainActivity.kt -B 2 -A 2

Repository: immichFrame/ImmichFrame_Android

Length of output: 1369


Replace Handler-based scheduling with AlarmManager for reliable undim triggering.

The dim-to-undim flow relies on Handler.postDelayed() to periodically call checkDimTime() and trigger screenDim(false). However, once the device sleeps after 3 seconds (line 824 sets setScreenTimeout(3000)), the Handler loop stops executing—it is not a wake source. The device will remain asleep until something else wakes it, making the end-of-window auto-undim unreliable.

While the screenDim(false) code path (lines 863-869) correctly uses WakeLock with ACQUIRE_CAUSES_WAKEUP to wake the device, this code never executes if screenDim(false) is never called. To ensure reliable undim at the scheduled end time, use AlarmManager to schedule a wake-up alarm at the end of the dim window instead of relying on the Handler loop.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/java/com/immichframe/immichframe/MainActivity.kt` around lines
817 - 824, The current dim flow uses Handler.postDelayed in checkDimTime()/dim
loop and reduces the timeout via setScreenTimeout(3000), but the Handler stops
when the device sleeps so screenDim(false) may never run; replace the periodic
Handler scheduling with an AlarmManager RTC_WAKEUP/ELAPSED_REALTIME_WAKEUP
scheduled for the exact end of the dim window so the system will wake and run
your undim logic. Specifically: when entering dim mode (where
originalScreenTimeout is captured and setScreenTimeout(3000) is called),
schedule an AlarmManager alarm for the dim-end timestamp; cancel that alarm when
leaving dim early; in the alarm receiver or PendingIntent invoke the same code
path currently used for undimming (call screenDim(false) or the existing
wake-and-undim logic) and ensure any WakeLock acquisition remains in that undim
path; remove or stop relying on Handler.postDelayed/checkDimTime for the final
wake-up.


// create black overlay, set webview to blank to reduce activity, and
// wait for screen to go to sleep after inactivity
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
applyDimOverlay()

} else {
lp.screenBrightness = WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_NONE
window.attributes = lp
if (dimOverlay.isVisible) {
dimOverlay.animate()
.alpha(0f)
.setDuration(500L)
.withEndAction {
dimOverlay.visibility = View.GONE
loadSettings()
}
.start()
// restore user's screen timeout value
if (originalScreenTimeout != -1) {
setScreenTimeout(originalScreenTimeout)
originalScreenTimeout = -1
}

// acquire WakeLock to turn screen on for short time, just to wake device
// FLAG_KEEP_SCREEN_ON will keep it on afterwards
val powerManager = getSystemService(Context.POWER_SERVICE) as PowerManager
val wakeLock = powerManager.newWakeLock(
PowerManager.SCREEN_BRIGHT_WAKE_LOCK or PowerManager.ACQUIRE_CAUSES_WAKEUP,
"ImmichFrame:WakeLockTag"
)
wakeLock.acquire(10 * 1000L) // 10 second timeout
wakeLock.release()

window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
removeDimOverlay()

// turn screen back on, dismissing keyguard lockscreen
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
setShowWhenLocked(true)
setTurnScreenOn(true)
val keyguardManager = getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
keyguardManager.requestDismissKeyguard(this, null)
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val keyguardManager = getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
keyguardManager.requestDismissKeyguard(this, null)
window.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON or
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED)

} else {
window.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON or
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or
WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD)
}
}
}
Expand All @@ -827,9 +939,11 @@ class MainActivity : AppCompatActivity() {
}

val isOverlayVisible = dimOverlay.isVisible
if (shouldDim && !isOverlayVisible) {
if (shouldDim && !isOverlayVisible && !isSnoozing) {
isSnoozing = false
screenDim(true)
} else if (!shouldDim && isOverlayVisible) {
isSnoozing = false
screenDim(false)
}
}
Expand All @@ -841,21 +955,33 @@ class MainActivity : AppCompatActivity() {
}
}

// called when user returns to app
override fun onResume() {
super.onResume()
if (keepScreenOn) {
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)

// if we are already in dim state, but screen just woke up,
// hide overlay and schedule screen to re-dim in 30 seconds
if (dimOverlay.isVisible) {
isSnoozing = true
setScreenTimeout(40000)
removeDimOverlay()
handler.postDelayed(redimRunnable, 30000)
} else {
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
applyScreenTimeout()
}
hideSystemUI()

}

// called when app is closed
override fun onDestroy() {
super.onDestroy()
rcpServer.stop()
handler.removeCallbacksAndMessages(null)

// restore timeout if we changed it
if (originalScreenTimeout != -1) {
setScreenTimeout(originalScreenTimeout)
}
}

private fun loadWebViewWithRetry(
Expand Down
Loading