Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Android 13]: device with cutout shifting content below bottom of screen #9446

Closed
awbooze opened this issue Aug 18, 2022 · 44 comments · Fixed by koreader/android-luajit-launcher#415 or #10439
Assignees
Milestone

Comments

@awbooze
Copy link

awbooze commented Aug 18, 2022

  • KOReader version: 2022.07 (fdroid)
  • Device: Google Pixel 6a
  • Operating System: Android 13

Issue

The Pixel 6a has a hole-punch camera, while my previous phone did not. After I upgraded to the 6a, I noticed that KOReader was not laying out content beneath the hole punch, but seemed to be laying content out below the bottom of the screen. This does also occur when the screen is rotated - if the camera is on the left, the text is cut off on the right.

After searching the issues, it looks like the problem is similar to #4769 and should have been fixed by koreader/android-luajit-launcher#150. The type of log you had nvvslm do is here.

After looking at the log and doing some digging in the code, it looks like getScreenHeight in android-luajit-launcher would probably correctly detect the notch, while screen:getRawSize() in device.lua is still returning the full screen height. I'd think this would move some content below the screen, although I'm not experienced with Lua.

Steps to reproduce

  1. Have an Android device with a display cutout. If not, you can go to Developer Options, scroll down to the Drawing section, and select Display cutout (or Simulate a display with a cutout).
  2. Open KOReader.
  3. See that content is laid out below the bottom of the screen and not under the display cutout.

Thanks for the great reader app!

@pazos
Copy link
Member

pazos commented Aug 18, 2022

Could you attach a picture?

Hopefully it is an android 13 bug and it gets fixed automagically. It could be the case of a platform change affecting all targetSdk but I don't remember google/android breaking backwards compatibility without a very big warning on dev documentation.

Anyhow, I'm self-assigning for the long run (a year or so), because emulators now don't support the x86 ABI (what a shit movement!)

adb install koreader-android-x86-v2022.07.apk
Performing Streamed Install
adb: failed to install koreader-android-x86-v2022.07.apk: Failure [INSTALL_FAILED_NO_MATCHING_ABIS: Failed to extract native libraries, res=-113]

In the mean time both repro steps for supported platforms other than android 13 (from 9 onwards) and links to relevant documentation about API changes are very welcome.

@pazos pazos self-assigned this Aug 18, 2022
@Frenzie
Copy link
Member

Frenzie commented Aug 18, 2022

I'm on Android 13 and it's under the cutout but definitely not below the bottom of the screen. That's all as it should be, isn't it?

Edit: d'oh, it's 12.

@awbooze
Copy link
Author

awbooze commented Aug 19, 2022

I tried reproducing it on Android 12 on a Pixel 3a and couldn't, even with the simulated punch hole cutout. Log here: sargo-log.txt. On the 6a, if I use the "Hide" or "Render apps below cutout area" setting, it will work fine, but it won't detect any other cutout setting. Log here: bluejay-log.txt. I wonder if WindowInsetsCompat would work any better?

Wanted to include my pictures below tags so it wouldn't take forever to scroll past them.
  • Pixel 6a in vertical orientation - no changes to cutouts.
    Pixel 6a Vertical 1
    Pixel 6a Vertical 2

  • Pixel 6a in horizontal orientation - no changes to cutouts.
    Pixel 6a Horizontal

  • Pixel 6a in hide cutout mode. This is what it should look like (well, minus the navigation bar).
    Pixel 6a Hide Cutout

@Frenzie
Copy link
Member

Frenzie commented Aug 19, 2022

Wanted to include my pictures below tags so it wouldn't take forever to scroll past them.

It's a bit hacky but here on GH I like to use <img src="from upload" width=45%> (or 32%) to stick multiple next to each other.

@pazos
Copy link
Member

pazos commented Aug 19, 2022

I wonder if WindowInsetsCompat would work any better?

It shouldn't. We're doing exactly the same in https://github.com/koreader/android-luajit-launcher/blob/master/app/src/main/java/org/koreader/launcher/MainActivity.kt#L173-L183

What compat methods provide is a way to do the call without guarding it by api level.

If you enable verbose logs in KO FM -> tools -> more tools -> developer options you should see a message: "top x pixels are not available, reason: window inset"

If you don't see the message then safeInsetTop returned 0. That doesn't make much sense for devices with a top cutout. I have no idea what happens if the report is wrong or the pixels are cutted elsewhere (ie: as safeInsetBottom)

@awbooze
Copy link
Author

awbooze commented Aug 22, 2022

Took a look at the verbose logs and found onAttachedToWindow() but no top x pixels not available. Log attached from startup to beginning of rendering the main page: bluejay-verbose-log.txt

A couple things that stuck out to me:

  1. device.needsView was false, so it used the native content implementation.
  2. surfaceChanged logged the correct width and height (width: 1080, height: 2282).
  3. KOReader still initializes at a resolution of 1080 by 2400.
  4. KOReader later refreshes the UI for region 0 0 1080 2400

@Frugipon
Copy link

Hi,
I'm running Koreader on a Pixel 6 and I recently updated to Android 13 which is when the issue described here occured: bottom part of the screen is cut out
Screenshot_20220822-130802
So no bottom status bar, half of bottom settings are not displayed and said settings cannot be navigated as a result.

There was no issue whatsoever when on Android 12.
I'm running the latest stable build v2022.07. I hope this helps narrowing down the root cause but if someone has a suggestion to bypass the problem while waiting for a fix (playing with margins or dpi perhaps), I'm open to suggestions.

Cheers

@pazos
Copy link
Member

pazos commented Aug 22, 2022

@awbooze

Took a look at the verbose logs and found onAttachedToWindow() but no top x pixels not available

Thanks for the feedback!

That's the issue. Until now all android devices reported the cutout pixels when the activity is attached to a window. The rest of the code relies on that premise being true.

surfaceChanged logged the correct width and height (width: 1080, height: 2282).
KOReader still initializes at a resolution of 1080 by 2400.

Yup. Both the initial size and further resizes are expecting proper cutout pixels because topInsetHeight
is only updated on init. So even when we receive a resize event we use old values These functions return on portrait height = height - cutout on landscape width = width - cutout, but that again relies on proper cutout values to begin with.

@Frugipon

if someone has a suggestion to bypass the problem while waiting for a fix (playing with margins or dpi perhaps), I'm open to suggestions.

I'm afraid there's no workaround available. There was one a few years ago (hardcoding height with a lua patch) but that doesn't work now since we added native rotation & buffer resizes.

While a solution shouldn't be too hard to implement relying on remote feedback for testing changes makes everything messier. I'm going to flash an A13 GSI image on my old tablet and see if I can reproduce the issue there.

@pazos
Copy link
Member

pazos commented Aug 22, 2022

I'm going to flash an A13 GSI image on my old tablet and see if I can reproduce the issue there.

Nah. Google GSI doesn't even boot on my samsung A7 :(

@emacsomancer
Copy link

Same issue (unsurprisingly) on a Pixel 6 Pro having updated to Android 13.

@awbooze
Copy link
Author

awbooze commented Aug 26, 2022

As a small comment, if you are okay with only reading books on the front page of KOReader, it is possible to increase the bottom margin of your books until this is fixed. You could also play with hiding the display cutout in developer settings,, which solves the problem completely, but that applies to all of your apps.

If I have time this weekend, I'll also see if I can build android-luajit-launcher.

@emacsomancer
Copy link

@awbooze cheers! "hiding" the display cutout in developer settings didn't help, but changing it to "render apps below cutout area" does, and restores functionality to KOReader (i. e. makes it useable ).

@tzwm
Copy link

tzwm commented Aug 26, 2022

@awbooze cheers! "hiding" the display cutout in developer settings didn't help, but changing it to "render apps below cutout area" does, and restores functionality to KOReader (i. e. makes it useable ).

"render apps below cutout area" is also works to me. thx!

@pazos
Copy link
Member

pazos commented Aug 27, 2022

Thanks for the workaround, @emacsomancer.

Here is a patch against luajit-launcher master, if somebody wants to try

diff --git a/app/src/main/java/org/koreader/launcher/MainActivity.kt b/app/src/main/java/org/koreader/launcher/MainActivity.kt
index d5b24bb..97b006b 100644
--- a/app/src/main/java/org/koreader/launcher/MainActivity.kt
+++ b/app/src/main/java/org/koreader/launcher/MainActivity.kt
@@ -44,7 +44,7 @@ class MainActivity : NativeActivity(), LuaInterface,
     // Path of last file imported
     private var lastImportedPath: String? = null
 
-    // Device cutout - only used on API 28+
+    // Device cutout - It might be > 0 on API28+
     private var topInsetHeight: Int = 0
 
     // Fullscreen - only used on API levels 16-18
@@ -159,9 +159,10 @@ class MainActivity : NativeActivity(), LuaInterface,
     }
 
     override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
+        topInsetHeight = cutoutPixels
         Log.v(TAG_SURFACE, String.format(Locale.US,
-            "surface changed {\n  width:  %d\n  height: %d\n format: %s\n}",
-            width, height, pixelFormatName(format))
+            "surface changed {\n  width:  %d\n  height: %d\n format: %s\n cutout: %dpx}",
+            width, height, pixelFormatName(format), topInsetHeight)
         )
         super.surfaceChanged(holder, format, width, height)
         drawSplashScreen(holder)
@@ -170,17 +171,7 @@ class MainActivity : NativeActivity(), LuaInterface,
     override fun onAttachedToWindow() {
         Log.d(TAG_SURFACE, "onAttachedToWindow()")
         super.onAttachedToWindow()
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
-            val cut: DisplayCutout? = window.decorView.rootWindowInsets.displayCutout
-            if (cut != null) {
-                val cutPixels = cut.safeInsetTop
-                if (topInsetHeight != cutPixels) {
-                    Log.v(TAG_SURFACE,
-                        "top $cutPixels pixels are not available, reason: window inset")
-                    topInsetHeight = cutPixels
-                }
-            }
-        }
+        topInsetHeight = cutoutPixels
     }
 
     /* Called just before the activity is resumed by an intent */
@@ -436,15 +427,11 @@ class MainActivity : NativeActivity(), LuaInterface,
     }
 
     override fun getScreenHeight(): Int {
-        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
-            // We need to handle the notch in Portrait
-            // NOTE: getScreenAvailableHeight does it automatically, but it also excludes the nav bar, when there's one :/
-            if (getOrientationCompat(screenIsLandscape).and(1) == 0) {
-                // getScreenOrientation returns LinuxFB rotation constants, Portrait rotations are always even
-                getHeight() - topInsetHeight
-            } else {
-                getHeight()
-            }
+        // We need to handle the notch in Portrait
+        // NOTE: getScreenAvailableHeight does it automatically, but it also excludes the nav bar, when there's one :/
+        return if (getOrientationCompat(screenIsLandscape).and(1) == 0) {
+            // getScreenOrientation returns LinuxFB rotation constants, Portrait rotations are always even
+            getHeight() - topInsetHeight
         } else {
             getHeight()
         }
@@ -475,15 +462,11 @@ class MainActivity : NativeActivity(), LuaInterface,
     }
 
     override fun getScreenWidth(): Int {
-        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
-            // We need to handle the notch in Landscape
-            // NOTE: getScreenAvailableWidth does it automatically, but it also excludes the nav bar, when there's one :/
-            if (getOrientationCompat(screenIsLandscape).and(1) == 1) {
-                // getScreenOrientation returns LinuxFB rotation constants, Landscape rotations are always odd
-                getWidth() - topInsetHeight
-            } else {
-                getWidth()
-            }
+        // We need to handle the notch in Landscape
+        // NOTE: getScreenAvailableWidth does it automatically, but it also excludes the nav bar, when there's one :/
+        return if (getOrientationCompat(screenIsLandscape).and(1) == 1) {
+            // getScreenOrientation returns LinuxFB rotation constants, Landscape rotations are always odd
+            getWidth() - topInsetHeight
         } else {
             getWidth()
         }
diff --git a/app/src/main/java/org/koreader/launcher/extensions/ActivityExtensions.kt b/app/src/main/java/org/koreader/launcher/extensions/ActivityExtensions.kt
index f651c5d..19db075 100644
--- a/app/src/main/java/org/koreader/launcher/extensions/ActivityExtensions.kt
+++ b/app/src/main/java/org/koreader/launcher/extensions/ActivityExtensions.kt
@@ -14,6 +14,7 @@ import android.os.Build
 import android.os.Environment
 import android.provider.Settings
 import android.util.DisplayMetrics
+import android.view.DisplayCutout
 import android.view.Surface
 import android.view.View
 import android.view.WindowManager
@@ -31,6 +32,14 @@ val Activity.platform: String
     } else {
         "android"
     }
+
+val Activity.cutoutPixels: Int
+    get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+        window.decorView.rootWindowInsets.displayCutout?.let {
+            it.safeInsetTop
+        } ?: 0
+    } else 0
+
 /* Haptic feedback */
 fun Activity.hapticFeedback(constant: Int, force: Boolean, view: View) {
     runOnUiThread {

@pazos pazos changed the title Android device with cutout shifting content below bottom of screen [Android 13]: device with cutout shifting content below bottom of screen Aug 27, 2022
@mahlzahn
Copy link

mahlzahn commented Sep 5, 2022

@pazos Thank you! I’d like to test your patch with my Pixel 4a but I don’t manage to build KOReader (See #9480). If you send me an apk I could test it, too.

@Nfanja
Copy link

Nfanja commented Sep 6, 2022

Pixel 6a, Android 13, same issue.

As a workaround I'm using GitHub release version with the following patch:

/storage/emulated/0/koreader/patches/10-screen-offset.lua

local screen = require("android").screen
screen.height = screen.height - 120

@ewencb
Copy link

ewencb commented Sep 10, 2022

Same issue here running grapheneos on pixel 6 pro
Screenshot_20220910-132047

@magnus-ISU
Copy link

magnus-ISU commented Sep 26, 2022

Can someone please upload an apk of the patched koreader for android 13?

I was going to myself, but I have ncurses installed but it fails to build because libncurses.so.5 is missing.

Creating a file /storage/emulated/0/koreader/patches/10-screen-offset.lua does nothing.
EDIT: apparently the patch only works when you install straight from github? Anyway I guess it is working now. Still feel a patched apk would be better

@pazos
Copy link
Member

pazos commented Sep 26, 2022

EDIT: apparently the patch only works when you install straight from github?

Yes, the fdroid version doesn't allow changing the runtime behaviour of the app.

Anyway I guess it is working now. Still feel a patched apk would be better.

I don't think so. Is better to keep your APK with a signature that comes from a reputable source and a little lua file that you can remove yourself when a proper fix arrives.

@mahlzahn
Copy link

@Nfanja Thanks! This works very well with - 140 on my Pixel 4a.

Pixel 6a, Android 13, same issue.

As a workaround I'm using GitHub release version with the following patch:

/storage/emulated/0/koreader/patches/10-screen-offset.lua

local screen = require("android").screen
screen.height = screen.height - 120

@ghost
Copy link

ghost commented Oct 25, 2022

On corvus os 4.0, (which is android 12.1) there is no "render apps below cutout area" in developer options. It's in Settings > Display > Display Cutout. But, this option doesn't help much in this situation. The 4th "hide" option worked for me in this case.
Screenshot_20221025-075026_Settings

@ghost
Copy link

ghost commented Oct 25, 2022

UPDATE: This doesn't seem to work for long. After using the 2022.10 version for some time, the top menu returns in the notch area. Tried 2022.08 (github and fdroid version) but it doesn't work. Hopefully the devs will look into it.

@1over137
Copy link

I have the Pixel 5a and the patch doesn't seem to work, even with the Github version. I am using GrapheneOS rom if this matters.

@everdred
Copy link

@Nfanja Thanks! This works very well with - 140 on my Pixel 4a.

Pixel 6a, Android 13, same issue.
As a workaround I'm using GitHub release version with the following patch:
/storage/emulated/0/koreader/patches/10-screen-offset.lua

local screen = require("android").screen
screen.height = screen.height - 120

Can also confirm - 140 works well on Pixel 4a Android 13, with no additional modifications to a freshly-installed KOreader 2022.10.

@tudos4
Copy link

tudos4 commented Dec 4, 2022

Pixel 6a, Android 13, same issue.

As a workaround I'm using GitHub release version with the following patch:

/storage/emulated/0/koreader/patches/10-screen-offset.lua

local screen = require("android").screen
screen.height = screen.height - 120

Can confirm this works for now.
In my case i set it to 109

And as stated in the 3rd comment before mine if i change orientation it messes this up and i need to restart koreader for it to appear properly again

But i only use koreader in vertical orientation so its good for me for now

Edit: using the latest 2022.11 version

@tinkerpunktheprol
Copy link

For Android 13 on Galaxy S21+, forcing show camera cutout with KOReader through Android display settings (mostly) fixed the issue. The bottom corners of books and book display settings are cut off slightly but not unusable.

@tidux
Copy link

tidux commented Dec 10, 2022

Can confirm tweak provided by @tinkerpunktheprol works on S21+ running 13.x.

@yparitcher
Copy link
Member

Here is a patch against luajit-launcher master, if somebody wants to try

@pazos

Patch is not working on Pixel 6a: GrapheneOS: android 13
cutout is 0

Surface : surface changed {
Surface :   width:  1080
Surface :   height: 2282
Surface :  format: OPAQUE
Surface :  cutout: 0px}

@pazos
Copy link
Member

pazos commented Dec 12, 2022

Here is a patch against luajit-launcher master, if somebody wants to try

@pazos

Patch is not working on Pixel 6a: GrapheneOS: android 13 cutout is 0

Surface : surface changed {
Surface :   width:  1080
Surface :   height: 2282
Surface :  format: OPAQUE
Surface :  cutout: 0px}

Thanks for testing, @yparitcher!

I'm afraid I'm going to need a device myself to see what happens and fix it.

Probably the best way is to get rid of static window height + width and just return the last one picked by SurfaceChanged. It is a bit bothersome as we need to test it in a bunch of legacy devices (ie: emulators) and some old broken ereaders.

Let me know if you're available for more tests in the future!

@yparitcher
Copy link
Member

Let me know if you're available for more tests in the future!

Now that i finally built for android, 6hrs and 15GB of my drive later, after failing, arm64 debug, then arm64 release signing, then finally arm7 debug, for sure :)

Don't worry i used a separate tree so i can nuke the build and TC's when this is done. :)

Btw even with decent internet speed (300mbit) a majority of a clean build time is spent being stuck waiting to download sources

@yparitcher
Copy link
Member

The easiest way now is if you push patches to git and i can just checkout your branch

@williamb2021
Copy link

For Android 13 on Galaxy S21+, forcing show camera cutout with KOReader through Android display settings (mostly) fixed the issue. The bottom corners of books and book display settings are cut off slightly but not unusable.

Thank you! This worked for me

@NiLuJe
Copy link
Member

NiLuJe commented Jan 12, 2023

LineageOS 20 is out, so I might be able to join the fray soon (-ish).

@pazos
Copy link
Member

pazos commented Jan 12, 2023

LineageOS 20 is out, so I might be able to join the fray soon (-ish).

Yep. Not available for my phone/tablet yet. Hopefully soon.

But I wouldn't mind if you take care of it entirely ;)

@NiLuJe
Copy link
Member

NiLuJe commented Jan 12, 2023

If you have a few pointers for me to look into, it'll help once I finally make the jump ;).

(I have a compatible device, just waiting for the dust to settle a bit, and to have a bit of free time reserved for it, in case shit happens during the upgrade ;p).

@pazos
Copy link
Member

pazos commented Jan 16, 2023

If you have a few pointers for me to look into.

Sure. We can make both width and height nullable integers and their getters to return current getScreenWidth & getScreenHeight if they're null.

If they're not null then just return the value they hold, which should be updated on each new onSurfaceChanged.
The tricky thing to remember is that we need to test that in both NativeContent (all phones and tablets) and NativeSurfaceView (some eink devices, chromebooks & android tv), it should be a matter of changing the if block in https://github.com/koreader/android-luajit-launcher/blob/master/app/src/main/java/org/koreader/launcher/MainActivity.kt#L115-L123 to match the test requirements.

@zephyros-dev
Copy link

zephyros-dev commented Jan 19, 2023

The cutoff also affect the page content (the last few lines of the pages). The "render apps below cutout area" settings is a system-wide settings, so it will affect other apps. If you're like me and don't need to change the bottom settings and just want to see the whole page content, it's possible to shift the content of the page up by changing the Status bar > Settings > Page 2 > Container height to a very high value (I used maximum which is 98) so that it is no longer being cutoff.

@DireMunchkin
Copy link

I'm using a Pixel 7 and I'm affected by this bug - I tried the workaround Developer options > Display cutout > Render apps below cutout area. However this causes another issue with the under screen fingerprint reader. The on screen symbol for where to put your finger will be slightly off from the sensor in the phone. You then need to hit your finger slightly above it which is annoying and confusing.

If I instead set Display Cutout > Hide it fixes the UI cutoff in KOReader and shows the on screen fingerprint correctly, but it shrinks my screen size by the height of the cutout. I'll use this for now but a fix on KOReader end would be appreciated!

@grock304
Copy link

grock304 commented May 9, 2023

I also encountered the same problem, using Samsung S20+, running OneUI 5.1 (Android 13).

@vurtomatic
Copy link

For Android 13 on Galaxy S21+, forcing show camera cutout with KOReader through Android display settings (mostly) fixed the issue. The bottom corners of books and book display settings are cut off slightly but not unusable.

Is this setting special to the Galaxy or it's available in stock Android? Trying to find the name of the setting.

yparitcher added a commit to yparitcher/koreader that referenced this issue May 14, 2023
Fix cutout on android 13

Closes: koreader#9446
yparitcher added a commit that referenced this issue May 14, 2023
Fix cutout on android 13

Closes: #9446
@Frenzie Frenzie added this to the 2023.05 milestone May 15, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet