Skip to content

Commit eca89e7

Browse files
committed
SDL2: implement support for loadingscreen
As explained on kivy#465, the previous approach (pure GL, or using window background) doesn't fully work. This approach have a tiny delay until PythonActivity.onCreate is called. It first display an ImageView fitted on the screen. Once SDL is loaded (and that SDL put its own layout), we put again our ImageView. The tricky part is when to remove. On the previous python-for-android+kivy, kivy was explicitely saying that he's ready to receive event. And that's where we knew the loading screen could be removed. Here, there is no absolute way to do it, as SDL itself on the java part doesn't provide any callback... except pollInputDevices() This method is called each X seconds, to check if any joystick has been plugged or not. The PR add a keepActive() method that is overrided in PythonActivity to remove the screen. It wait for the second call of it before removing the loading screen: 1. the first call is done right after the creation of the SDL window in C code. (In kivy, it's just the start, after that we load the app, widgets etc... it can take a long time) 2. the second call is done (at least on kivy) after the first frame rendered. The loading screen might be displayed more than needed (pure SDL2 game // very fast loading). In any case, we can always force to remove the loading screen with: PythonActivity.mActivity.removeLoadingScreen() If anybody have another good approach, feel free :) Closes kivy#465
1 parent 6beac27 commit eca89e7

File tree

2 files changed

+156
-72
lines changed

2 files changed

+156
-72
lines changed

pythonforandroid/bootstraps/sdl2/build/src/org/kivy/android/PythonActivity.java

Lines changed: 88 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@
2525
import android.content.pm.PackageManager;
2626
import android.content.pm.ApplicationInfo;
2727
import android.content.Intent;
28+
import android.widget.ImageView;
29+
import java.io.InputStream;
30+
import android.graphics.Bitmap;
31+
import android.graphics.BitmapFactory;
2832

2933
import org.libsdl.app.SDLActivity;
3034

@@ -38,7 +42,7 @@ public class PythonActivity extends SDLActivity {
3842
private static final String TAG = "PythonActivity";
3943

4044
public static PythonActivity mActivity = null;
41-
45+
4246
private ResourceManager resourceManager = null;
4347
private Bundle mMetaData = null;
4448
private PowerManager.WakeLock mWakeLock = null;
@@ -47,16 +51,18 @@ public class PythonActivity extends SDLActivity {
4751
protected void onCreate(Bundle savedInstanceState) {
4852
Log.v(TAG, "My oncreate running");
4953
resourceManager = new ResourceManager(this);
54+
this.showLoadingScreen();
5055

5156
Log.v(TAG, "Ready to unpack");
5257
unpackData("private", getFilesDir());
5358

5459
Log.v(TAG, "About to do super onCreate");
5560
super.onCreate(savedInstanceState);
5661
Log.v(TAG, "Did super onCreate");
57-
62+
63+
this.showLoadingScreen();
5864
this.mActivity = this;
59-
65+
6066
String mFilesDirectory = mActivity.getFilesDir().getAbsolutePath();
6167
Log.v(TAG, "Setting env vars for start.c and Python to use");
6268
SDLActivity.nativeSetEnv("ANDROID_PRIVATE", mFilesDirectory);
@@ -65,7 +71,7 @@ protected void onCreate(Bundle savedInstanceState) {
6571
SDLActivity.nativeSetEnv("PYTHONHOME", mFilesDirectory);
6672
SDLActivity.nativeSetEnv("PYTHONPATH", mFilesDirectory + ":" + mFilesDirectory + "/lib");
6773

68-
74+
6975
// nativeSetEnv("ANDROID_ARGUMENT", getFilesDir());
7076

7177
try {
@@ -87,11 +93,11 @@ protected void onCreate(Bundle savedInstanceState) {
8793
} catch (PackageManager.NameNotFoundException e) {
8894
}
8995
}
90-
96+
9197
public void loadLibraries() {
9298
PythonUtil.loadLibraries(getFilesDir());
9399
}
94-
100+
95101
public void recursiveDelete(File f) {
96102
if (f.isDirectory()) {
97103
for (File r : f.listFiles()) {
@@ -123,15 +129,15 @@ public void run() {
123129
}
124130
}
125131
}
126-
132+
127133
public void unpackData(final String resource, File target) {
128-
134+
129135
Log.v(TAG, "UNPACKING!!! " + resource + " " + target.getName());
130-
136+
131137
// The version of data in memory and on disk.
132138
String data_version = resourceManager.getString(resource + "_version");
133139
String disk_version = null;
134-
140+
135141
Log.v(TAG, "Data version is " + data_version);
136142

137143
// If no version, no unpacking is necessary.
@@ -180,7 +186,7 @@ public void unpackData(final String resource, File target) {
180186
}
181187
}
182188
}
183-
189+
184190
public static ViewGroup getLayout() {
185191
return mLayout;
186192
}
@@ -277,4 +283,75 @@ public static void stop_service() {
277283
Intent serviceIntent = new Intent(PythonActivity.mActivity, PythonService.class);
278284
PythonActivity.mActivity.stopService(serviceIntent);
279285
}
286+
287+
/** Loading screen implementation
288+
* keepActive() is a method plugged in pollInputDevices in SDLActivity.
289+
* Once it's called twice, the loading screen will be removed.
290+
* The first call happen as soon as the window is created, but no image has been
291+
* displayed first. My tests showed that we can wait one more. This might delay
292+
* the real available of few hundred milliseconds.
293+
* The real deal is to know if a rendering has already happen. The previous
294+
* python-for-android and kivy was having something for that, but this new version
295+
* is not compatible, and would require a new kivy version.
296+
* In case of, the method PythonActivty.mActivity.removeLoadingScreen() can be called.
297+
*/
298+
public static ImageView mImageView = null;
299+
int mLoadingCount = 2;
300+
301+
@Override
302+
public void keepActive() {
303+
Log.v("python", "keepActive from PythonActivity");
304+
if (this.mLoadingCount > 0) {
305+
this.mLoadingCount -= 1;
306+
if (this.mLoadingCount == 0) {
307+
this.removeLoadingScreen();
308+
}
309+
}
310+
}
311+
312+
public void removeLoadingScreen() {
313+
runOnUiThread(new Runnable() {
314+
public void run() {
315+
if (PythonActivity.mImageView != null) {
316+
((ViewGroup)PythonActivity.mImageView.getParent()).removeView(
317+
PythonActivity.mImageView);
318+
PythonActivity.mImageView = null;
319+
}
320+
}
321+
});
322+
}
323+
324+
protected void showLoadingScreen() {
325+
// load the bitmap
326+
// 1. if the image is valid and we don't have layout yet, assign this bitmap
327+
// as main view.
328+
// 2. if we have a layout, just set it in the layout.
329+
if (mImageView == null) {
330+
int presplashId = this.resourceManager.getIdentifier("presplash", "drawable");
331+
InputStream is = this.getResources().openRawResource(presplashId);
332+
Bitmap bitmap = null;
333+
try {
334+
bitmap = BitmapFactory.decodeStream(is);
335+
} finally {
336+
try {
337+
is.close();
338+
} catch (IOException e) {};
339+
}
340+
341+
mImageView = new ImageView(this);
342+
mImageView.setImageBitmap(bitmap);
343+
mImageView.setLayoutParams(new ViewGroup.LayoutParams(
344+
ViewGroup.LayoutParams.FILL_PARENT,
345+
ViewGroup.LayoutParams.FILL_PARENT));
346+
mImageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
347+
}
348+
349+
if (mLayout == null) {
350+
setContentView(mImageView);
351+
} else {
352+
mLayout.addView(mImageView);
353+
}
354+
}
355+
356+
280357
}

0 commit comments

Comments
 (0)