The glorious hour's gone forth, I finally managed to display the splash screen in a
Marmalade-based android application in a proper way.
As you may know there is a problem with splash screens in Marmalade SDK. That problem is not new - it has been around a year since it first emerged
link to the marmalade forum.
Basically this problem manifests itself (at least on Android) with a long blank screen period after program launch. After this a splashscreen is shown (either yours or standard) followed by the short blank screen period, and only after that your main() function is triggered. This might be due to initialization of glContext, however, it takes longer for the apps with more resources.
Some people suggest to show splashscreen manually from main(), but I cannot agree with this proposal because it is always too late to launch fireworks from main()
see this. I've decided to approach this issue in a different way. For Android it is possible to override
standard Marmalade's LoaderActivity with your own custom activity. I think you have already guessed what it means, and yes this approach works.
Now for the details and sample code. My custom activity is based on the "examples\AndroidJNI\s3eAndroidLVL" source provided with Marmalade.
package com.android.mainactivity;
import com.ideaworks3d.marmalade.LoaderActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.content.Context;
import android.widget.ImageView;
import android.view.ViewGroup.LayoutParams;
import java.util.Timer;
import java.util.TimerTask;
import android.view.ViewGroup;
public class MainActivity extends LoaderActivity {
private static MainActivity m_Activity;
private ImageView m_ImgView;
private final Runnable m_UiThreadStartLogo = new Runnable(){
public void run(){
m_ImgView = new ImageView((Context)LoaderActivity.m_Activity);
m_ImgView.setBackgroundResource(0x7f020001);
m_ImgView.setVisibility(View.VISIBLE);
LoaderActivity.m_Activity.addContentView(m_ImgView, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
}
};
private final Runnable m_UiThreadStopLogo = new Runnable(){
public void run(){
if (m_ImgView != null){
//m_ImgView.setVisibility(View.INVISIBLE);
ViewGroup vg = (ViewGroup)(m_ImgView.getParent());
vg.removeView(m_ImgView);
}
}
};
public class CustomTimerTask extends TimerTask {
public void run() {
LoaderActivity.m_Activity.runOnUiThread(m_UiThreadStopLogo);
}
}
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
m_Activity = this;
LoaderActivity.m_Activity.runOnUiThread(m_UiThreadStartLogo);
Timer timer = new Timer();
TimerTask updateProfile = new CustomTimerTask();
timer.schedule(updateProfile, 4000);
}
protected void onDestroy() {
super.onDestroy();
}
}
After building the jar file do not forget to mention it in mkb file, like this:
deployments
{
android-custom-activity='com.android.mainactivity.MainActivity'
android-external-jars='NativeExtensions/SplashScreenFix/java/mainactivity.jar'
...
}
In the "onCreate" method of the custom MainActivity I launch the ImageView. This ImageView is set to show the splash image. In the above case for simplicity I have put a numerical id for an image that is included in my Marmalade project through Gallery Icon setting (it doesn't have to be 170x170 if you would check the "Allow non-standart icon sizes checkbox). When the project is built for android target this image will be located in: "...\android\release\intermediate_files\res\drawable"
and you can find its numerical id in:
"...\android\release \intermediate_files\src\com\proj\myproj\R.java".
Now the splash image will be shown immediately after launch. The only trouble it woudn't go away automatically, effectively covering all your app) In the above code I hide it on timer, that is set to 4 seconds delay. The better approach would be to use jni interface and call stopLogo as the first instruction in your main(). Anyhow, I would still prefer to have timer as a last resort, to be sure that the logo screen will hide eventually.
I have used Maramalde 6.0.5 SDK.
Update:
Q:Can you give the c++ code + java modification to call stopLogo as JNI call instead of timer?
A: In your MainActivity declare "static MainActivity m_Activity" field and initialize it in OnCreate() to "this", also declare "public boolean StopMyLogo()" method, that would actually hide th logo view. To call this method from c++ take as a reference \Marmalade\6.0\examples\AndroidJNI\s3eAndroidLVL\source\s3eAndroidLVL.cpp. You should write something like this (error checking omitted):
void StopMyLogo()
{
if (!s3eAndroidJNIAvailable()) return;
JavaVM* jvm = (JavaVM*)s3eAndroidJNIGetVM();
JNIEnv* env = NULL;
jvm->GetEnv((void**)&env, JNI_VERSION_1_6);
jclass Activity = env->FindClass("com/android/mainactivity/MainActivity");
jfieldID fid = env->GetStaticFieldID(Activity, "m_Activity", "Lcom/android/mainactivity/MainActivity;");
jobject m_Activity = env->GetStaticObjectField(MainActivity, fid);
jmethodID pMethod = env->GetMethodID(MainActivity, "StopMyLogo", "()Z");
env->CallVoidMethod(m_Activity, pMethod); //Or CallBooleanMethod()
}
Update 2 (as of September 2013) :
If you will follow
Google's optimization advices for tablets and change targetSdkVersion to "14", your activity will be restarted on screen rotation event, triggering onCreate() method again! You don't want the splash screen to reappear when the user rotates a phone. Make sure you run m_UiThreadStartLogo just once. For example, you can add to activity's settings in the manifest android:configChanges="orientation|screenSize". This way activity would not be restarted when screen is about to rotate.