Android app dev: Waterfall management for the app - android

I would like my Android app to manage waterfall displays automatically if present, in that I do not wish for the texts, buttons, etc to be displayed all the way to the screen's border if the screen is waterfall, as I find this not to be user friendly.
I've tried to explicitely state the WindowCompat true, but that doesn't seem to help
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
context = this
sharedPref = getSharedPreferences("app_settings", Context.MODE_PRIVATE)
WindowCompat.setDecorFitsSystemWindows(window, true)
super.onCreate(savedInstanceState)
setContent {
I've also tried to to put the WindowCompat to false and manually add an instet padding to WindowInsets.waterfall without any success
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
context = this
sharedPref = getSharedPreferences("app_settings", Context.MODE_PRIVATE)
WindowCompat.setDecorFitsSystemWindows(window, false)
super.onCreate(savedInstanceState)
setContent {
myTheme(chosenTheme = themeSelection.value) {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier
.windowInsetsPadding(WindowInsets.waterfall),
Any clue as to what I am doing wrong ?
Is there a way to fix this at the same level as WindowCompat, so that it's valid for the full app?
Thanks!

Related

Show different content (layouts) depending on available width/height in Android Compose

I have an Activity and MyApp composable function. In the function I need to show either list with details or just list screen depending on the available width. How to determine available width for the content I want to show using Jetpack Compose? What is a good practice for this using Compose?
class MyActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ProjectTheme {
MyApp()
}
}
}
}
#Composable
fun MyApp() {
val isLarge: Boolean = ...// how to determine whether the available width is large enough?
if (isLarge) {
ListScreenWithDetails()
} else {
ListScreen()
}
}
#Composable
fun ProjectTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: #Composable () -> Unit) {
// ... project theme
}
You can use :
val configuration = LocalConfiguration.current
Then:
val isLargeWidth = configuration.screenWidthDp > 840
For a generic solution, consider using the Window Size Classes, see this and this for reference.
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val windowSizeClass = calculateWindowSizeClass(this)
MyApp(windowSizeClass)
}
}

How to set a custom Context in Jetpack Compose?

I have run into an issue with Jetpack Compose. In my application's old classes (activity and fragment) I am using a custom Context wrapper for my custom Resources wrapper. This is because string resources values are downloaded from a server; the Strings resource files do contain string IDs, their values are empty and are replaced by the server's values.
In activities and fragments I can simply replace the context in getContext() or attachBaseContext(newBase: Context?) methods, but how do I do this in Jetpack Compose?
Jetpack Compose does have these Context methods: LocalConfiguration.current and LocalContext.current. I love how straightforward this is, but I was not able to find where this Context object is inited. At first, I thought it was in the parent Activity in attachBaseContext() method when I set content, but this code does not seem to be useful:
#AndroidEntryPoint
class MyComposeActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ProvideWindowInsets(windowInsetsAnimationsEnabled = true) {
MyComposeActivityScreen()
}
}
}
override fun attachBaseContext(newBase: Context?) {
val language = Locale(Utils.getLanguage(newBase))
super.attachBaseContext(MyCustomContextWrapper.wrap(MyCustomResourcesContextWrapper(newBase), language))
}
You can try specifying the context manually with CompositionLocalProvider:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
updateComposeView(this)
}
override fun attachBaseContext(newBase: Context?) {
super.attachBaseContext(newBase)
updateComposeView(newBase ?: this)
}
fun updateComposeView(context: Context) {
setContent {
CompositionLocalProvider(
LocalContext provides context
) {
ProvideWindowInsets(windowInsetsAnimationsEnabled = true) {
MyComposeActivityScreen()
}
}
}
}
Also a more Compose way to solve the original problem is to create your own string holder, something like LocalStrings, kind of as shown in this answer.

Kotlin / Migration to View Binding

I was following YT video to make Quiz App, but in the end I got this error with binding:
The 'kotlin-android-extensions' Gradle plugin is deprecated.
Please use this migration guide
(https://goo.gle/kotlin-android-extensions-deprecation) to start working with View Binding (https://developer.android.com/topic/libraries/view-binding) and the 'kotlin-parcelize' plugin.
But when I add binding for instance to tv_name.text, i do get error that text is expecting Variable and everything the same with other binding parts.
Libraries part
import android.content.Intent
import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.WindowCompat
import com.example.quizapp.databinding.ActivityResultBinding
Code part
class ResultActivity : AppCompatActivity() {
private val binding by viewBinding(FragmentResultActivity::bind)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_result)
// Hide the status bar.
//window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_FULLSCREEN
val userName = intent.getStringExtra(Constants.USER_NAME)
binding.tv_name.text = userName
val totalQuestions = intent.getIntExtra(Constants.TOTAL_QUESTIONS, 0)
val correctAnswers = intent.getIntExtra(Constants.CORRECT_ANSWERS, 0)
binding.tv_score.text = "Your Score is $correctAnswers out of $totalQuestions."
binding.btn_finish.setOnClickListener {
startActivity(Intent(this#ResultActivity, MainActivity::class.java))
}
}
}
You need to initialize the binding variable properly. Please use the below code:
class ResultActivity : AppCompatActivity() {
private lateinit var binding: ActivityResultBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityResultBinding.inflate(layoutInflater)
setContentView(binding.root)
// Hide the status bar.
//window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_FULLSCREEN
val userName = intent.getStringExtra(Constants.USER_NAME)
binding.tv_name.text = userName
val totalQuestions = intent.getIntExtra(Constants.TOTAL_QUESTIONS, 0)
val correctAnswers = intent.getIntExtra(Constants.CORRECT_ANSWERS, 0)
binding.tv_score.text = "Your Score is $correctAnswers out of $totalQuestions."
binding.btn_finish.setOnClickListener {
startActivity(Intent(this#ResultActivity, MainActivity::class.java))
}
}
}
Since I do not have your resources with me available, I think you might have to change the naming of ActivityResultBinding. However, I am pretty sure this is it.
Happy Coding! :)
First you need to let android know that you are using view binding. so go to "Gradle Scripts" folder and open app level build.gradle(Module:nameOfProject) file and inside android property add this:
android {
// ------ VIEW BINDING SETTING ------
// this creates the binding object
buildFeatures{
viewBinding true
}
// after set up, click on "Sync Now"
}
then in MainActivity.kt:
class MainActivity : AppCompatActivity() {
// Initialize binding object. if ActivityMainBinding is not ready in menu click "Build/Make project"
private lateinit var binding:ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// view Bindig
// binding default layout inflater
binding= ActivityResultBinding.inflate(layoutInflater)
// inflate the root views which is Linear Layout, we access with .root
setContentView(binding.root)
// Set click listeners
....
}

Navigation component: Retain same fragment after orientation change

as I said in the title, I would like to keep my current fragment after rotating the phone. I could actually make it happen by adding
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
in the Manifest. The problem is that my layout configuration is lost every time.
What am I doing wrong? How can I fix it?
I'm using Jetpack Navigation component. Exist a way to navigateToCurrentFragment()?
Edit: Part of onCreate() from Activity()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityBinding.inflate(layoutInflater)
setContentView(binding.root)
val graphInflater = (supportFragmentManager.findFragmentById(R.id.mNavHost) as NavHostFragment).navController.navInflater
val navGraph = graphInflater.inflate(R.navigation.services_nav_graph)
navGraph.startDestination = R.id.FirstFragment
(supportFragmentManager.findFragmentById(R.id.mNavHost) as NavHostFragment).navController.graph = navGraph
}

Using View Binding displays blank white screen

I was experimenting View Binding in my new app. Suddenly today when I run the app it displays blank white screen instead of UI. If I remove binding and use setContentView, it works!
Any idea why this is happening?
Code:
build.gradle
viewBinding {
enabled = true
}
MainActivity
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
}
You need to call setContentView with ViewBinding as:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root) // add this
}

Categories

Resources