Maintaining state through screen rotation in Kotlin - android

There's really no code snippet for this. I'm building an Android app in Android Studio in Kotlin with a main activity in both default portrait and lanscape modes. I'm using Android Studio auto-generated layout\activity_main.xml and layout-land\activity_main.xml layouts.
PROBLEM: When I rotate screens in the app, all widgets reset state. For example, I have a TextView, tvTitle, with a default hardcoded string, "Hello World". tvTitle normally changed to "News" while using the app in portrait mode. I then switched to landscape mode and tvTitle reverted to it's hardcoded default string, Hello. How can I retain the state of the app through rotations?

You might find the solution to your particular problem after understanding the Android mechanisms for saving the application state.
I suggest that you check out the official guide.

Got it. The simple answer that works for me here is:
class MainActivity : AppCompatActivity() {
private var SavedVar = "Hello"
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putString("SavedVar_KEY" ,SavedVar)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
if(savedInstanceState != null) {
SavedVar = savedInstanceState.getString("SavedVar_KEY")
}
...
...
}

Related

Android Espresso: How do I inspect elements inside a web view?

In the app is a Checkout.com 3DS screen and when I use Layout Inspector (Android Studio), I only see the web view but it has no subtrees. Do I need to use separate software to inspect elements in chrome web view?
I believe I have the correct code to interact with the elements but I'm just guessing the attributes at the moment and not having much luck. Using text/placeholder hasn't worked so far.
private val webView = withId(R.id.verifyByThreeDsWebView)
override fun isDisplayed() {
support.waitForViewDisplayed(webView, 20000)
}
fun enterPassword(password: String = "Checkout1!") {
onWebView(webView).forceJavascriptEnabled()
.withElement(findElement(Locator.XPATH,"//*[#placeholder='Hint: Checkout1!']"))
.perform(webKeys(password))
}
fun clickSubmit() {
onWebView(webView)
.withElement(findElement(Locator.XPATH,"\"//button[contains(text(),'Continue')]\""))
.perform(webClick())
}

Is it possible to change configuration ui mode programmatically?

I am developing an Android application that should work on both types of devices: phone, tv and tv box. To do this, I created resources with a TV modifier (for example, textSizes in dimens that will change automatically depending on the uimode), but the problem is that TV boxes use mobile insead of TV. How to change the uimode on app start in order to use dimens with tv modifer?
I've already tried this, but it doesn't work.
config.uiMode = Configuration.UI_MODE_TYPE_TELEVISION
resources.updateConfiguration(config, resources.displayMetrics)
Finally, I've found a solution! Maybe it would be helpful for someone!
So, I must put it into attachBaseContext instead of onCreate
override fun attachBaseContext(context: Context?) {
super.attachBaseContext(context)
if (context != null) {
SettingsData.initDeviceType(context)
if (SettingsData.deviceType == DeviceType.TV) {
resources.configuration.uiMode = uiMode
resources.configuration.setTo(resources.configuration)
}
}
}
Also, having switched to another device mode, you have to reload an activity. I've tried finish() StartActivity(intent), but it doesn't work properly. So to fix it up I use ProcessPhoenix lib:
fun refreshActivity() {
ProcessPhoenix.triggerRebirth(this)
}
Warning! If you are using setRequestedOrientation in your MainActivity, it could trigger configuration to change with old uimode.

'cameraView.addFrameProcessor' but using realtime image from the screen instead of the Camera

I've been looking for days on google, but I guess I'm out of luck.
I'm looking for similar functionality to cameraView.addFrameProcessor but using real-time onscreen images instead of the camera. basically, I want to implement a feature similar to Google Assistant feature "Google lens" or "What's on my screen",
below the example for frameProcessor:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Fritz.configure(this)
cameraView.addFrameProcessor { frame ->
//This returns a frame, which cann be used to retrieve the preview
}
}
}
Is this even exist?

How to use or opt out in Android 12 SplashScreen

The new API SplashScreen in Android 12 seems good but just like before the sample code in the documentation does not really help explaining the whole and proper implementation. There is also some cases where you might do some task during splash screen in our case this is to launch Firebase Auth so probably the best way is just to opt out on using this new featured API but according to lint warning it seems like it is mandatory and there is no way to opt out.
The application should not provide its own launch screen
Application-defined Launch Screen Starting in Android 12 (API 31+),
the application's Launch Screen is provided by the system and the
application should not create its own, otherwise the user will see two
splashscreen. Please check the SplashScreen class to check how the
Splash Screen can be controlled and customized.
How about the backward compatibility for older devices, how to handle it? Is there any codelab project to play and test with?
Can we opt out of SplashScreen?
It looks like we can't opt out as Android Team is trying to unify the app loading experience: https://9to5google.com/2021/04/21/android-12-dp3-all-apps-now-show-the-same-splash-screen-while-loading-gallery/
How to use it?
If you don't do anything then it will use windowBackground of the theme & your launcher icon & dismissed as soon as your app draws its first frame.
There are bunch of properties that you can modify like background, icon etc: https://developer.android.com/about/versions/12/features/splash-screen#set-theme
What if I want splash to stay longer? Like fetching a local DataBase.
You can use ViewTreeObserver.OnPreDrawListener & make a blocking call from your viewmodel return if it's ready to go ahead.
Activity:
// My Launcher Activity
class MainActivity : AppCompatActivity() {
private val viewModel : JustDelayViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val content: View = findViewById(android.R.id.content)
content.viewTreeObserver.addOnPreDrawListener(
object : ViewTreeObserver.OnPreDrawListener {
override fun onPreDraw(): Boolean {
// Check if the initial data is ready.
return if (viewModel.getIsReady()) {
// The content is ready; start drawing.
content.viewTreeObserver.removeOnPreDrawListener(this)
true
} else {
// The content is not ready; suspend.
false
}
}
}
)
}
}
ViewModel:
class JustDelayViewModel : ViewModel() {
fun getIsReady(): Boolean {
val result = viewModelScope.runCatching {
runBlocking {
//do some blocking call check for Firebase result or something
delay(5000)
}
true //return the result
}
return result.isSuccess
}
}
You can read more about this: https://developer.android.com/about/versions/12/features/splash-screen#suspend-drawing
To complement Mayur's answer for older device support.
The new windowSplashScreen* attributes need to be added in the res/values-v31/style.xml file.
Then for the legacy splashscreen it depend of the current implementation of the app.
If the application simply uses a starting theme with a custom windowBackground there is nothing to do since the windowBackground isn't used for the new splash screen (only if it's a simple color).
If the application has some visible splash screen Activity, there will be a double splash screen on Android 12. To solve this, the application can migrate to the windowBackground solution.
If the application really need to keep its splash screen Activity, it can update the layout to match the system splash screen on Android 12 and/or create a smooth transition from the system splash screen to the app splash screen using the SplashScreen.setOnExitAnimationListener()
We can also use android's splash screen library - link
android {
compileSdk 31
...
}
dependencies {
...
implementation 'androidx.core:core-splashscreen:1.0.0-alpha02'
}
This will give splash screen options in style.xml, you just need to create 2 style.xmls 1 for android api 31 and above and one of below api 31
<style name="Theme.CustomSplashScreenTheme" parent="Theme.SplashScreen">
<item name="windowSplashScreenBackground">#color/white</item>
<item name="windowSplashScreenAnimatedIcon">#drawable/logo</item>
<item name="windowSplashScreenAnimationDuration">300</item>
<item name="postSplashScreenTheme">#style/Theme.YourAppTheme</item>
</style>
Learn more about this library using this example
you can add this line:
<item name="android:windowIsTranslucent">true</item>
in your style.xml file before close style tag. it`s make your default android splash transparent!

Night mode color values not applying to recycler view items

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)
...

Categories

Resources