I'm trying to learn android studio, android, and kotlin. Mostly this has been straightforward but with a few glitches. One such glitch is the following for which I could use some help:
I'm trying to create a phone view in immersive mode. According to the documentation (https://developer.android.com/training/system-ui/immersive) the following function should do the trick.
private fun hideSystemBars() {
val windowInsetsController =
ViewCompat.getWindowInsetsController(window.decorView) ?: return
// Configure the behavior of the hidden system bars
windowInsetsController.systemBarsBehavior =
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
// Hide both the status bar and the navigation bar
windowInsetsController.hide(WindowInsetsCompat.Type.systemBars())
}// end hideSystemBars function
I set this in the main Activity onCreate function as follows
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
requestWindowFeature(Window.FEATURE_NO_TITLE)
supportActionBar?.hide()
setContentView(binding.root)
hideSystemBars()
}// end onCreate function
Unfortunately I seem to missing something basic as this always fails because
ViewCompat.getWindowInsetsController(window.decorView) always returns null (I ran several tests to verify this)
I've verified that window.decorView is not null and looks okay. As a precaution I replaced the hideSystemBars() function call with window.decorView.doOnLayout {hideSystemBars()} in case the layout wasn't complete but this doesn't help. In fact, from some addition tests I ran use of the window.decorView.doOnLayout {} doesn't seem to work either no matter what function is used in the brackets (another glitch I don't understand)
I think I may be missing something basic (and possibly trivial) but cannot seem to discover how to make this work. Any help will be appreciated.
Maybe you should check your API Level on the device. The following solution works for me.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
val windowInsetsController = ViewCompat.getWindowInsetsController(window.decorView) ?: return
// Configure the behavior of the hidden system bars
windowInsetsController.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
// Hide both the status bar and the navigation bar
windowInsetsController.hide(WindowInsetsCompat.Type.systemBars())
}else{
val flags =
(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY)
window!!.decorView.setSystemUiVisibility(flags)
}
Related
I am trying to draw over notch and want to manage screen view. If there is notch available then it'll be set otherwise not here is the code written in onCreate() method this works fine.
super.onCreate(savedInstanceState)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
window.attributes.layoutInDisplayCutoutMode =
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
WindowCompat.setDecorFitsSystemWindows(window, false)
hasNotch = true
}
Now need to manage my control and want to get safeInsetTop but window.decorView.rootWindowInsets return always null. I tried my code to put under oncreate(), onResume() and onAttachedToWindow() but from all this area I am getting null. I even tried to put code under runnable with delay time but no success. I don't know what the best way
Thanks
I'm not sure when or what I changed, but all of the sudden the systemUiController has stopped affecting the status bar color in my app. For context, I'm using the accompanist Insets library in combination with system UI controller to get rid of the status and nav bar, and this was working fine until a build or two ago, and now its only working on the nav/gesture bar.
I do this by putting all composables into ProvideWindowInsets, and by setting the bar colors to transparent. For some reason this only works on the nav bar.
To debug, I've stripped my app of everything but the setContent in onCreate of the mainActivity, including all services, and have temporarily removed everything within setContent but an empty theme provider.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
WindowCompat.setDecorFitsSystemWindows(window, false)
setContent {
val systemUiController = rememberSystemUiController()
val useDarkIcons = !isSystemInDarkTheme()
SideEffect {
systemUiController.setSystemBarsColor(
color = Color.Transparent,
darkIcons = useDarkIcons
)
}
MicCheckTheme {
ProvideWindowInsets() {
}
}
}
}
When I run this, only the nav bar is transparent. I believe this isn't the fault of the Insets library, as while the status bar retains its color, app content goes underneath the bar, so Insets works as intended.
For further context, I'm using a MainActivity inheriting ComponentActivity with Material3, Compose 1.2.x, and accompanist 0.24.9-beta, although I've tried many different releases in debugging this, none working. I've even created a new test app project and pasted the Insets + SystemUIController sample app from the accompanist github, and even that didn't work.
Please help me! Thanks.
That is because in Material3 compose Theme.kt overrides systemuicontroller changes as follows:
val view = LocalView.current
if (!view.isInEditMode) {
SideEffect {
(view.context as Activity).window.statusBarColor = colorScheme.primary.toArgb()
ViewCompat.getWindowInsetsController(view)?.isAppearanceLightStatusBars = darkTheme
}
}
As a matter of fact, you do not need systemuicontroller libary to update system bars
. You can directly utilise the above code from Theme.kt in Material3 Compose template.
Note: ViewCompat.getWindowInsetsController(view) is now depricated. You should use WindowCompat.getInsetsController(window, view)
Per the android docs, we can make an activity go full-screen by adding the following block of code in the onCreate method (inside setContent{...}, specifically, if you are using Compose)
val windowInsetsController = ViewCompat.getWindowInsetsController(window.decorView)
windowInsetsController.systemBarsBehavior =
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
windowInsetsController.hide(WindowInsetsCompat.Type.systemBars())
However, this seems to only hide the information displayed on the statusbar, replacing it with just a black stripe.
Now, my question is - How can we modify the color of this stripe so that the UI of the app seems to extend to the entire display?
I am not sure where to start but I've heard accompanist MAY have something related to this, but I thought it better to post this question here, so that if anyone already knows a way around, they may share since it will be helpful to the community.
Other than that, solutions that do not involve accompanist are also welcome, and may be even preferred.
For reference, here's the output as of now
Notice the black bar at the top? That's the target.
copy and paste it in onCreate method after result you can understand
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
val window: Window = window
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
val decorView: View = window.getDecorView()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
decorView.systemUiVisibility =
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
} else {
decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
}
window.setStatusBarColor(Color.TRANSPARENT)
}
I implemented dark mode for my app. In the settings you can set it. Then it loads your option from sharedprefs and applies it using AppCompatDelegate.setDefaultNightMode. I made a function for this that I call at start of every activity.
fun setAppTheme(context: Context) {
AppCompatDelegate.setDefaultNightMode(when(PreferenceManager.getDefaultSharedPreferences(context).getString("theme", "default")) {
"light" -> AppCompatDelegate.MODE_NIGHT_NO
"dark" -> AppCompatDelegate.MODE_NIGHT_YES
"default" -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
else -> AppCompatDelegate.MODE_NIGHT_YES
})
}
It works great when i set it to follow system, all colors correct, but when I set it to force night mode, and change system mode to light, recycler view items are all light (should be dark!)
I read this, this, tried using all the different contexts, but nothing helped.
I figured it out. The problem was I was calling the function setAppTheme right before super.onCreate() in main activity. My bad. This works now:
...
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setAppTheme(this)
...
I have a webview-based Android app full-screen, running in COSU mode. Each time I pick a color using an HTML5 <input type="color"/>, the hidden action bars are shown again, and I can't hide them, unless I close the app and open it again.
Is there any way of avoiding the bars showing up each time I use the color picker? The only solution I came up to is using the javascript interface to call a function/method in Android to hide the bars each time the color picker is used, but seems too tricky/elaborate. There should be a better way... Or not?
Any ideas?
Thanks in advance
EDITED:
How I hide bars? I call this function I defined inside the Activity:
private fun hideBars() {
actionBar?.hide()
this.supportActionBar?.hide()
}
Also, use this one to set the activity to full screen
private fun setFullScreen() {
window.decorView.systemUiVisibility =
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or
View.SYSTEM_UI_FLAG_FULLSCREEN or
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
}
I've been waiting a few days for a response, but looks like it is not a very common problem, or there is no cleaner solution than using the javascript interface.
Finally, I opted to call to a function using the javascript interface when the <input type="color"/> is clicked. This is the function I used:
class WebAppInterface {
private val context : Context
...
constructor(context : Context) {
this.context = context
...
}
#JavascriptInterface
fun resetScreen() {
val activity = context as MainActivity
activity.runOnUiThread(Runnable {
fun run() {
activity.hideBars()
activity.setFullScreen()
}
});
}
}
Note I had to call the hiding bar functions using Activity.runOnUiThread(). Otherwise, Android kindly answered I was not running the code in the UI thread.
I mark this as the answer, but if someone in the future finds a cleaner way to solve this problem, I will review it. Otherwise, I hope this helps somebody