Creating animated in Splash in Maui (setting MainActivity OnCreate and other things)?

3.2k Views Asked by At

This question started from an Android article I tried to implement in Maui. Mostly it was about implementing custom Android resource XML files but I got stuck in implementing things on OnCreate. Hence that's where this started, but then escalated other potential solutions.

Then @user2153142 chimed in with Xamarin sample that unlocked the issue. Hence that's that accepted answer and this question is revised a bit to show it. Like how it started, how it went.

So the old question was like this:

I was following tutorial at https://www.droidcon.com/2022/10/04/splash-screen-in-android/ to set up animated Android Splash Screen in Maui.

But there is a "Step 5" I do not know how to set up the actual animation. In the code it is described as

class SplashScreenActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        startActivity(Intent(this@SplashScreenActivity, MainActivity::class.java))
        finish()
    }
}

I was thinking it could be something like

[Activity(Theme = "@style/SplashTheme", MainLauncher = true)]
public class SplashScreenActivity: MauiAppCompatActivity
{
    protected override void OnCreate(Bundle savedInstanceState)
    {
        base.OnCreate(savedInstanceState);

        //So, passing this intent doesn't work here at all now. Argh! How should this be done?
        StartActivity(new Android.Content.Intent(this.SplashScreen, typeof(MainActivity)));
        Finish();
    }
}

in .NET Maui, but I fail to realize how to create the intent so I could pass MainActivity and whatever is that SplashScreenActivity in this as parameters here. That activity is the default activity, basically.

I learned elsewhere that SplashScreenActivity should be set as MainLauncher = true and in .NET Maui it automatically generated a section to AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <application
    android:allowBackup="true"
    android:icon="@mipmap/appicon"
    android:supportsRtl="true">

    <!--
      This activity is generated by SplasScreenActivity,
      the contents may differ as they depend on that activity
      definition. This can be verified by looking at
    \obj\Debug\net7.0-android\android\manifest\AndroidManifest.xml
    -->
    <!--
    <activity
            android:name=".SplashScreenActivity"
            android:exported="true"
            android:theme="@style/SplashTheme">
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
    </activity>
    -->

  </application>  
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="33" />
</manifest>

It appears one can't just set <item name="postSplashScreenTheme">@style/MainTheme</item> in styles.xml either to declare the main theme as it one cannot set Parent="Theme.SplashScreen" in the styles describing the Splash theme (probably the values could be copied from somewhere?).

Further in that same tutorial there is a more advanced way of setting up the activity. It might be better, from the tutorial

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // Enable support for Splash Screen API for
        // proper Android 12+ support
        installSplashScreen()
        
        setContentView(R.layout.activity_main)
    }
}

but there doesn't seem to be InstallSplashScreen in .NET Maui. Also I would not know how to call setContentView(R.layout.activity_main) in .NET (there is SetContentView function, however).

There are other tutorials too that might allow one to bypass some of these issues. One is at https://www.truiton.com/2022/10/implementing-the-splash-activity-in-android-the-right-way/, but it appears there'll be similar issues with AppCompatActivity functions.

But it got complicated. The provided answer was close to the price, and Maui builds on Android (and old) Xamarin bits. Or that what I believe it does.

Here are a few modifications I did:

In .csproj there is

 <ItemGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">
    <PackageReference Include="Xamarin.AndroidX.Core.SplashScreen" Version="1.0.0" />
  </ItemGroup>

 <MauiIcon Condition="$(TargetFramework.Contains('-android')) != true" Include="Resources\AppIcon\appicon.svg" ForegroundFile="Resources\AppIcon\appiconfg.svg" Color="#512BD4" />
    <MauiIcon Include="Resources\AppIcon\appiconfg.svg" />

    <!-- Splash Screen -->
    <MauiSplashScreen Condition="$(TargetFramework.Contains('-android')) != true" Include="Resources\Splash\splash.svg" Color="#512BD4" BaseSize="128,128" />

(Maybe they all should be conditionals, but this is how it is now and it works.)

The resource are laid out like this: enter image description here

AndroidManifest.xml is defined like this

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <application android:allowBackup="true" android:icon="@mipmap/appicon" android:supportsRtl="true"></application>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="33" />
</manifest>

It's important to target a SDK over 31, I think.

styles.xml is defined like

<?xml version="1.0" encoding="utf-8" ?>
<resources>
  <style name="AppTheme.Starting" parent="Theme.SplashScreen.IconBackground">
    <item name="windowSplashScreenAnimatedIcon">@mipmap/appicon_foreground</item>
    <item name="android:windowSplashScreenAnimatedIcon">@drawable/animated_face</item>
    <item name="windowSplashScreenBackground">?android:colorBackground</item>
    <item name="postSplashScreenTheme">@style/AppTheme</item>
  </style>

  <style name="AppTheme" parent="Maui.SplashTheme">
    <item name="android:windowSplashScreenBackground">@color/colorPrimary</item>
    <item name="android:windowLightStatusBar">true</item>
  </style>
</resources>

Here AppTheme is the main theme, AppTheme.Starting is the "splash theme". In my mind this means chaining these actions together. Notice that unlike in the comments in the answer, I think this is what it set in MainActivity.cs and it seems to work. Setting the AppTheme does not, there's just the application background showing. That probably is because there isn't the windowSplashScreenAnimatedIcon isn't set there. I have not tried if it could, maybe. Also it doesn't have the android: prefix, maybe works with it too. Or all others without also.

Nothing remarkable in colors.xml, I think.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#ffff1c</color>
    <color name="colorPrimaryDark">#00c3ff</color>
    <color name="colorAccent">#3b3d3b</color>
</resources>

That file `animated_face.xml" was copied from https://yggr.medium.com/exploring-android-12-splash-screen-21f88cc8e8f8 and modified so it blinks (I needed to find something I can understand and test with)

<animated-vector
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:aapt="http://schemas.android.com/aapt">
  <aapt:attr name="android:drawable">
    <vector
        android:name="vector"
        android:width="24dp"
        android:height="24dp"
        android:viewportWidth="24"
        android:viewportHeight="24">
      <path
          android:name="path"
          android:pathData="M 9 11.75 C 8.31 11.75 7.75 12.31 7.75 13 C 7.75 13.69 8.31 14.25 9 14.25 C 9.69 14.25 10.25 13.69 10.25 13 C 10.25 12.31 9.69 11.75 9 11.75 Z M 15 11.75 C 14.31 11.75 13.75 12.31 13.75 13 C 13.75 13.69 14.31 14.25 15 14.25 C 15.69 14.25 16.25 13.69 16.25 13 C 16.25 12.31 15.69 11.75 15 11.75 Z M 12 2 C 6.48 2 2 6.48 2 12 C 2 17.52 6.48 22 12 22 C 17.52 22 22 17.52 22 12 C 22 6.48 17.52 2 12 2 Z M 12 20 C 7.59 20 4 16.41 4 12 C 4 11.71 4.02 11.42 4.05 11.14 C 6.41 10.09 8.28 8.16 9.26 5.77 C 11.07 8.33 14.05 10 17.42 10 C 18.2 10 18.95 9.91 19.67 9.74 C 19.88 10.45 20 11.21 20 12 C 20 16.41 16.41 20 12 20 Z"
          android:fillColor="#000"
          android:strokeWidth="1"/>
    </vector>
  </aapt:attr>
  <target android:name="path">
    <aapt:attr name="android:animation">
      <objectAnimator
          android:propertyName="fillColor"
          android:duration="2000"
          android:valueFrom="#bb86fc"
          android:valueTo="#ffffff"
          android:valueType="colorType"
          android:repeatCount="-1"
          android:repeatMode="reverse"
          android:interpolator="@android:interpolator/fast_out_slow_in"/>
    </aapt:attr>
  </target>
</animated-vector>

and finally MainActivity.cs looks like this

using Android.App;
using Android.Content.PM;
using Android.OS;
using Android.Views;


namespace SomeApp;

[Activity(Theme = "@style/AppTheme.Starting", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)]
public class MainActivity: MauiAppCompatActivity
{
    protected override void OnCreate(Bundle savedInstanceState)
    {
        AndroidX.Core.SplashScreen.SplashScreen.InstallSplashScreen(this);

        base.OnCreate(savedInstanceState);

        // Include this if using the Splash Screen API - rotate to Landscape on a device with a notch to see why - with this code commented.
        if(Build.VERSION.SdkInt >= BuildVersionCodes.P)
        {
            Window.Attributes.LayoutInDisplayCutoutMode = LayoutInDisplayCutoutMode.Default;
        }

        // If you want to get a good look at the icon to check it. Don't forget remove from production code
        System.Threading.Thread.Sleep(1000);

        // Set our view from the "main" layout resource
        //SetContentView(Resource.Layout.activity_main);
    }
}

So, almost like the application. When I run this, the face is blinking when the app is loading (also while it's stopped in the debugger is there's a breakpoint there) and then switches over to the application screen.

I don't know how AndroidManifest.xml should look like so it's correct, but one can check it at \obj\Debug\net7.0-android\AndroidManifest.xml. I'll include this bit information here as it's something I learned while doing this.

I think this was it. The actual code is at https://github.com/gmck/XamarinBasicSplashScreen and here with slight Maui related modifications to make it work.

If you know this can be improved or perfected, please do chime in. There seem to be something related e.g. at https://github.com/dotnet/maui/issues/3300.

3

There are 3 best solutions below

4
On BEST ANSWER

@Veksi

To get an understanding of how a splash screen should be implemented in Xamarin.Android you can use the following example https://github.com/gmck/XamarinBasicSplashScreen.

One thing you can't do with Android 12 and above is use the two activity technique that you refer to above. There is only one additional line of code that is added to the standard blank app template, the rest is done via an additional theme. Works for Android 13 down to Android 5.0

I can't help you with Maui as I've never used it, but if you read the thread https://github.com/xamarin/xamarin-android/issues/7239 you will see how this helped out developer9969 with his xamarin.forms app, therefore I'm assuming you could do something similar with Maui.

For more detailed Android info refer to https://developer.android.com/develop/ui/views/launch/splash-screen.

I could also convert it from xamarin.android to a net7.0-android app if you would find that more helpful.

4
On

I've got no real clue about Maui as I never used it. I just had a quick look at a standard Maui app, build a standard app and checked the generated manifest in the obj folder. The manifest file inputs the theme as android:theme="@style/Maui.SplashTheme", so that would be what you would normally use. However, you probably don't want that so then continue searching the obj folder. The path where I found styles.xml was C:\ProjectsVS2022Preview\MauiApp1\obj\Debug\net6.0-android\lp\154\jl\res\values\styles.xml

Open that file in VS and reformat the text to make it easier to read. There appears to be two styles Maui.MainTheme and Maui.MainTheme.NoActionBar. Which one you choose depends on your android app type. Where my app had @style/AppTheme change postSplashScreenTheme to one of those Maui theme names and then delete any theme in the Activity attribute.

Remember this is only an Android solution - not going to work for the other platforms.

1
On

@Veksi I've reread your original post and have only now realised that you had already seen my XamarinBasicSplashScreen app. I can see in your original post where there is style that contains errors.

name="android:windowSplashScreenAnimatedIcon">@drawable/animated_face The android: is wrong if you are using androidx.core.splashscreen

if you check my style it is

<item name="windowSplashScreenAnimatedIcon">@mipmap/ic_launcher_foreground</item>

when using XamarinSplashScreenBasic

<item name="windowSplashScreenAnimatedIcon">@mipmap/appicon_foreground</item>

and now in xamarinSplashScreenBasicNet7

Basically anytime you see android: something on the left side that is the framework therefore Android 12+ only. So to work with devices less than 12 we need androidX.

Your example is a case of mixing up the android framework way rather than using the syntax of androidx.core.splashscreen. So that was never going to work.

As Xamarin users using Xamarin.AndroidX.Core.SplashScreen we must use androidX syntax.

The SomeApp namespace example is pretty much right, except it doesn't need the theme in the Activity Attribute as it should already be in the manifest. Looks like it came from my app when you check the comments.

If you do want to do animation of the icon, you then will need to follow the explorer Android 12 link.

I'd suggest getting XamarinSplashScreenBasicNet7 to work in a Maui .Net7 app just as a basic Android 12 splash screen before attempting to get animation working.

but there doesn't seem to be InstallSplashScreen in .NET Maui. Well there is in Xamarin but the C# syntax is completely different

AndroidX.Core.SplashScreen.SplashScreen.InstallSplashScreen(this);

When you do get it right with Maui can you please post a link?