How to use or opt out in Android 12 SplashScreen - android

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!

Related

How to test the Android Dark Mode using Espresso

What is the best way to test the different DayNight themes on Android using Espresso? (or something better?) I haven't found anything on the internet. I thought this must be something big because of everything migrating to DayNight now.
I want to know things like: "when I click this button, has my activity theme changed" or "I have this background and this text, is the contrast right".
Thanks in advance.
I have found this to work:
#get:Rule
val activityTestRule = ActivityScenarioRule(...Activity::class.java)
private fun createActivityScenarioRule(withNightMode: Boolean = false) =
activityTestRule.scenario.apply {
onActivity {
AppCompatDelegate.setDefaultNightMode(
if (withNightMode) AppCompatDelegate.MODE_NIGHT_YES
else AppCompatDelegate.MODE_NIGHT_NO
)
}
}

How to check the current Activity in a UI test

For tests I use Espresso and Barista
I have a test in which I need to open another screen by pressing a button. How can I check if this screen opens? Did the screen I need open?
Can I somehow check the chain of screens? To understand that the screens open in the order I need?
If someone throws links to good tutorials on UI tests in Android, I will be very grateful.
An easy solution would be to just check for an element of the new screen to be shown like this:
onView(withId(R.id.id_of_element_in_your_new_screen)).check(matches(isDisplayed()))
If you really want to check out for the current activity that is shown, you could try something like this:
Gather the current activity via InstrumentationRegistry and check for the activity in stage RESUMED.
fun getTopActivity(): Activity? {
InstrumentationRegistry.getInstrumentation().runOnMainSync {
val resumedActivities = ActivityLifecycleMonitorRegistry.getInstance().getActivitiesInStage(Stage.RESUMED)
if (resumedActivities.iterator().hasNext()) {
resumedActivities.iterator().next()?.let {
activity = it
}
}
}
return activity
}
You could then check this in a test like this:
#Test
fun checkForActivity() {
val currentActivity = getTopActivity()
assertTrue(currentActivity?.javaClass == YourActivityToCheckAgainst::class.java)
}
I personally use intended(hasComponent(YourActivityToCheckAgainst::class.java.name)), which checks if the last intent was done with a desired activity, set as its component.
I also wrote an extensive Android UI testing tutorial using Espresso + Barista libraries.

How can I detect programmatically if the Android Device is in Dark Mode?

I'm trying to support the Android Q Dark theme for my Android app and I can't figure out how to import different assets based on the theme I'm currently in.
Im using the official DayNight theme for making the dark/light versions and for drawables is very easy to just point to the XML and it will choose the correct value either from values or values-night depending on what is enabled.
I wanted to do something similar where depending on the theme it would load either the asset "priceTag_light.png" or "priceTag_dark.png".
val inputStream = if(darkIsEnabled) {
assets.open("priceTag_dark.png")
} else {
assets.open("priceTag_light.png")
}
Is there a way I get that flag?
Okay finally found the solution I was looking for. As #deepak-s-gavkar points out the parameter that gives us that information is on the Configuration. So, after a small search I found this article that gives this example method that has worked perfectly for what I wanted:
fun isDarkTheme(activity: Activity): Boolean {
return activity.resources.configuration.uiMode and
Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES
}
You first need to do this changes in manifest
<activity
android:name=".MyActivity"
android:configChanges="uiMode" />
then onConfigurationChanged of activity
val currentNightMode = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
when (currentNightMode) {
Configuration.UI_MODE_NIGHT_NO -> {} // Night mode is not active, we're using the light theme
Configuration.UI_MODE_NIGHT_YES -> {} // Night mode is active, we're using dark theme
}
Use the following code:
boolean isDarkThemeOn = (getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;

Trouble with DayNight mode status bar/controls inconsistently themed after recreate

I'm having quite a bit of trouble getting the DayNight theme to play nicely after a preference change and activity.recreate() call. Depending on the value (and which SDK I'm on), I get inconsistent theming (icons are dark on dark or light on light).
In my PreferenceFragment, I have a preference that allows the user to set one of three values: Light, Dark, or Auto, which correspond with AppCompatDelegates MODE_NIGHT_NO, MODE_NIGHT_YES, or MODE_NIGHT_AUTO, respectively. Here's what the implementation looks like:
PreferenceFragment.kt
override fun onPreferenceChange(preference: Preference, value: Any): Boolean {
setSummary(preference, value.toString())
return when (preference.key) {
themePreference.key -> consume {
AppCompatDelegate.setDefaultNightMode(appSharedPreferences.string(R.string.preference_theme_key).toInt())
activity?.recreate()
}
}
}
BaseActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
AppCompatDelegate.setDefaultNightMode(appSharedPreferences.string(R.string.preference_theme_key).toInt())
super.onCreate(savedInstanceState)
}
I've also tried combinations of:
delegate.setLocalNightMode(appSharedPreferences.string(R.string.preference_theme_key).toInt())
and
AppCompatDelegate.setDefaultNightMode(appSharedPreferences.string(R.string.preference_theme_key).toInt()
peppered into different places in the activity/application lifecycle with no success.
What I'm seeing mostly is dark icons on a dark status bar, and inconsistencies on pre-P app switcher (the theme is Night, but the app switcher toolbar shows up as a light theme). I don't appear to have issues with the theme of my own app controls, like tabs or text - it's mainly on the android system views like the status bar and app switcher toolbar).
If I kill the app and re-launch, I have no issues whatsoever. It's only after an activity.recreate call do I see these issues.
After searching for a simple solution for a long while, I found an attribute that conveniently handles this scenario. I'm not sure how proper this approach is, but from my testing it works as expected.
Given that you're using Theme.MaterialComponents.DayNight or some similar variant in your style XML, you can use ?attr/isLightTheme as a true/false flag.
<item name="android:windowLightStatusBar">?attr/isLightTheme</item>
The value returned by ?attr/isLightTheme is handled by the system. You can check the current value with:
AppCompatDelegate.getDefaultNightMode();
// or
Configuration configuration = getResources().getConfiguration();
int currentNightMode = configuration.uiMode & Configuration.UI_MODE_NIGHT_MASK;

Activity Background change dynamic

I have just started developing an android weather app and I was wondering how to change activity background automatically. For example, in daytime it should show day time or in the night it should show night photos.
This is the app of Sony which has a feature (mentioned above)
Check the screenshots.
Okay Credit goes to SteD;so for you check this(beginner's guide)
Follow this
//set an ID for Relative Layout in content_main.xml(Android Studio)
RelativeLayout rlayout=(RelativeLayout)findViewById(R.id.rlayout);
if(something){Drawable drawble=getResource().getDrawable(R.drawable.your_image);rlayout.setBackgroundDrawable(drawable);}
//If it works,destroy the upvote
The only automatic way is the newly released (Day/Night theme for android app)
For finer control you check the condition yourself and call the normal Java methods, like this:
if(something) {
getWindow()
.setBackgroundDrawable(ContextCompat.getDrawable(this, R.drawable.image));
}
of if you don't care about the newly introduced context themed styling, you just call the deprecated method (which will keep working without issues for all the foreseeable future)
if(something) {
getWindow()
.setBackgroundDrawable(
getResources().getDrawable(R.drawable.image));
}

Categories

Resources