Added the Android project and lots of info to README.android
authorSam Lantinga <slouken@libsdl.org>
Thu, 06 Jan 2011 17:12:31 -0800
changeset 4965 91d0085b7560
parent 4964 6c645018741e
child 4966 a130bc2f0a18
Added the Android project and lots of info to README.android
README.android
android-project/AndroidManifest.xml
android-project/build.properties
android-project/build.xml
android-project/default.properties
android-project/jni/Android.mk
android-project/jni/src/Android.mk
android-project/local.properties
android-project/res/drawable-hdpi/icon.png
android-project/res/drawable-ldpi/icon.png
android-project/res/drawable-mdpi/icon.png
android-project/res/layout/main.xml
android-project/res/values/strings.xml
android-project/src/org/libsdl/app/SDLActivity.java
--- a/README.android	Thu Jan 06 16:11:21 2011 -0800
+++ b/README.android	Thu Jan 06 17:12:31 2011 -0800
@@ -2,8 +2,13 @@
 Simple DirectMedia Layer for Android
 ================================================================================
 
-Requirements: Android SDK and Android NDK r4 or later
-http://developer.android.com/
+Requirements:
+
+Android SDK
+http://developer.android.com/sdk/index.html
+
+Android NDK r4 or later
+http://developer.android.com/sdk/ndk/index.html
 
 ================================================================================
  How the port works
@@ -22,7 +27,7 @@
 ================================================================================
 
 Instructions:
-1. Copy the android-project directory wherever you want your Android project to go
+1. Copy the android-project directory wherever you want to keep your projects and rename it to the name of your project.
 2. Move this SDL directory into the <project>/jni directory
 3. Place your application source files in the <project>/jni/src directory
 4. Edit <project>/jni/src/Android.mk to include your source files
@@ -35,16 +40,54 @@
 creates a .apk with the native code embedded
 8. 'ant install' will push the apk to the device or emulator (if connected)
 
+Here's an explanation of the files in the Android project, so you can customize them:
+
+android-project/
+	AndroidManifest.xml	- package manifest, do not modify
+	build.properties	- empty
+	build.xml		- build description file, used by ant
+	default.properties	- holds the ABI for the application, currently android-4 which corresponds to the Android 1.6 system image
+	local.properties	- holds the SDK path, you should change this to the path to your SDK
+	jni/			- directory holding native code and Android.mk
+	jni/Android.mk		- Android makefile that includes all subdirectories
+	jni/SDL/		- directory holding the SDL library files
+	jni/SDL/Android.mk	- Android makefile for creating the SDL shared library
+	jni/src/		- directory holding your application source
+	jni/src/Android.mk	- Android makefile that you should customize to include your source code and any library references
+	res/			- directory holding resources for your application
+	res/drawable-*		- directories holding icons for different phone hardware
+	res/layout/main.xml	- place holder for the main screen layout, overridden by the SDL video output
+	res/values/strings.xml	- strings used in your application, including the application name shown on the phone.
+	src/org/libsdl/app/SDLActivity.java	- the Java class handling the initialization and binding to SDL.  Be very careful changing this, as the SDL library relies on this implementation.
+
+
+================================================================================
+ Additional documentation
+================================================================================
+
+The documentation in the NDK docs directory is very helpful in understanding the build process and how to work with native code on the Android platform.
+
+The best place to start is with docs/OVERVIEW.TXT
+
 
 ================================================================================
  Using Eclipse
 ================================================================================
 
-NEED CONTENT
+First make sure that you've installed Eclipse and the Android extensions as described here:
+	http://developer.android.com/sdk/eclipse-adt.html
+
+Once you've copied the SDL android project and customized it, you can create an Eclipse project from it:
+ * File -> New -> Other
+ * Select the Android -> Android Project wizard and click Next
+ * Enter the name you'd like your project to have
+ * Select "Create project from existing source" and browse for your project directory
+ * Make sure the Build Target is set to Android 1.6
+ * Click Finish
 
 
 ================================================================================
- Loading files
+ Loading files and resources
 ================================================================================
 
 NEED CONTENT
@@ -54,7 +97,20 @@
  Troubleshooting
 ================================================================================
 
-NEED CONTENT
+You can create and run an emulator from the Eclipse IDE:
+ * Window -> Android SDK and AVD Manager
+
+You can see if adb can see any devices with the following command:
+	adb devices
+
+You can see the output of log messages on the default device with:
+	adb logcat
+
+You can push files to the device with:
+	adb push local_file remote_path_and_file
+
+You can push files to the SD Card at /sdcard, for example:
+	adb push moose.dat /sdcard/moose.dat
 
 
 ================================================================================
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/android-project/AndroidManifest.xml	Thu Jan 06 17:12:31 2011 -0800
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+      package="org.libsdl.app"
+      android:versionCode="1"
+      android:versionName="1.0">
+    <application android:label="@string/app_name" android:icon="@drawable/icon">
+        <activity android:name="SDLActivity"
+                  android:label="@string/app_name">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest> 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/android-project/build.properties	Thu Jan 06 17:12:31 2011 -0800
@@ -0,0 +1,17 @@
+# This file is used to override default values used by the Ant build system.
+# 
+# This file must be checked in Version Control Systems, as it is
+# integral to the build system of your project.
+
+# This file is only used by the Ant script.
+
+# You can use this to override default values such as
+#  'source.dir' for the location of your java source folder and
+#  'out.dir' for the location of your output folder.
+
+# You can also use it define how the release builds are signed by declaring
+# the following properties:
+#  'key.store' for the location of your keystore and
+#  'key.alias' for the name of the key to use.
+# The password will be asked during the build when you use the 'release' target.
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/android-project/build.xml	Thu Jan 06 17:12:31 2011 -0800
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project name="SDLApp" default="help">
+
+    <!-- The local.properties file is created and updated by the 'android' tool.
+         It contains the path to the SDK. It should *NOT* be checked in in Version
+         Control Systems. -->
+    <property file="local.properties" />
+
+    <!-- The build.properties file can be created by you and is never touched
+         by the 'android' tool. This is the place to change some of the default property values
+         used by the Ant rules.
+         Here are some properties you may want to change/update:
+
+         application.package
+             the name of your application package as defined in the manifest. Used by the
+             'uninstall' rule.
+         source.dir
+             the name of the source directory. Default is 'src'.
+         out.dir
+             the name of the output directory. Default is 'bin'.
+
+         Properties related to the SDK location or the project target should be updated
+          using the 'android' tool with the 'update' action.
+
+         This file is an integral part of the build system for your application and
+         should be checked in in Version Control Systems.
+
+         -->
+    <property file="build.properties" />
+
+    <!-- The default.properties file is created and updated by the 'android' tool, as well
+         as ADT.
+         This file is an integral part of the build system for your application and
+         should be checked in in Version Control Systems. -->
+    <property file="default.properties" />
+
+    <!-- Custom Android task to deal with the project target, and import the proper rules.
+         This requires ant 1.6.0 or above. -->
+    <path id="android.antlibs">
+        <pathelement path="${sdk.dir}/tools/lib/anttasks.jar" />
+        <pathelement path="${sdk.dir}/tools/lib/sdklib.jar" />
+        <pathelement path="${sdk.dir}/tools/lib/androidprefs.jar" />
+        <pathelement path="${sdk.dir}/tools/lib/apkbuilder.jar" />
+        <pathelement path="${sdk.dir}/tools/lib/jarutils.jar" />
+    </path>
+
+    <taskdef name="setup"
+        classname="com.android.ant.SetupTask"
+        classpathref="android.antlibs" />
+
+    <!-- Execute the Android Setup task that will setup some properties specific to the target,
+         and import the build rules files.
+
+         The rules file is imported from
+            <SDK>/platforms/<target_platform>/templates/android_rules.xml
+
+         To customize some build steps for your project:
+         - copy the content of the main node <project> from android_rules.xml
+         - paste it in this build.xml below the <setup /> task.
+         - disable the import by changing the setup task below to <setup import="false" />
+
+         This will ensure that the properties are setup correctly but that your customized
+         build steps are used.
+    -->
+    <setup />
+
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/android-project/default.properties	Thu Jan 06 17:12:31 2011 -0800
@@ -0,0 +1,11 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+# 
+# This file must be checked in Version Control Systems.
+# 
+# To customize properties used by the Ant build system use,
+# "build.properties", and override values to adapt the script to your
+# project structure.
+
+# Project target.
+target=android-4
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/android-project/jni/Android.mk	Thu Jan 06 17:12:31 2011 -0800
@@ -0,0 +1,1 @@
+include $(call all-subdir-makefiles)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/android-project/jni/src/Android.mk	Thu Jan 06 17:12:31 2011 -0800
@@ -0,0 +1,19 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := main
+
+SDL_PATH := ../SDL
+
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/$(SDL_PATH)/include
+
+# Add your application source files here...
+LOCAL_SRC_FILES := $(SDL_PATH)/src/main/android/SDL_android_main.cpp \
+	YourSourceHere.c
+
+LOCAL_SHARED_LIBRARIES := SDL
+
+LOCAL_LDLIBS := -lGLESv1_CM -llog
+
+include $(BUILD_SHARED_LIBRARY)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/android-project/local.properties	Thu Jan 06 17:12:31 2011 -0800
@@ -0,0 +1,10 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+# 
+# This file must *NOT* be checked in Version Control Systems,
+# as it contains information specific to your local configuration.
+
+# location of the SDK. This is only used by Ant
+# For customization when using a Version Control System, please read the
+# header note.
+sdk.dir=/Users/hercules/eclipse/android-sdk-mac_86
Binary file android-project/res/drawable-hdpi/icon.png has changed
Binary file android-project/res/drawable-ldpi/icon.png has changed
Binary file android-project/res/drawable-mdpi/icon.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/android-project/res/layout/main.xml	Thu Jan 06 17:12:31 2011 -0800
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    >
+<TextView  
+    android:layout_width="fill_parent" 
+    android:layout_height="wrap_content" 
+    android:text="Hello World, SDLActivity"
+    />
+</LinearLayout>
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/android-project/res/values/strings.xml	Thu Jan 06 17:12:31 2011 -0800
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="app_name">SDL App</string>
+</resources>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/android-project/src/org/libsdl/app/SDLActivity.java	Thu Jan 06 17:12:31 2011 -0800
@@ -0,0 +1,389 @@
+package org.libsdl.app;
+
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+import javax.microedition.khronos.egl.*;
+
+import android.app.*;
+import android.content.*;
+import android.view.*;
+import android.os.*;
+import android.util.Log;
+import android.graphics.*;
+import android.text.method.*;
+import android.text.*;
+import android.media.*;
+import android.hardware.*;
+import android.content.*;
+
+import java.lang.*;
+
+
+/**
+    SDL Activity
+*/
+public class SDLActivity extends Activity {
+
+    //Main components
+    private static SDLActivity mSingleton;
+    private static SDLSurface mSurface;
+
+    //Audio
+    private static AudioTrack mAudioTrack;
+    private static boolean bAudioIsEnabled;
+
+    //Sensors
+    private static boolean bAccelIsEnabled;
+
+    //feature IDs. Must match up on the C side as well.
+    private static int FEATURE_AUDIO = 1;
+    private static int FEATURE_ACCEL = 2;
+
+    //Load the .so
+    static {
+        System.loadLibrary("SDL");
+        System.loadLibrary("main");
+    }
+
+    //Setup
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        
+        //So we can call stuff from static callbacks
+        mSingleton = this;
+
+        //Set up the surface
+        mSurface = new SDLSurface(getApplication());
+        setContentView(mSurface);
+        SurfaceHolder holder = mSurface.getHolder();
+        holder.setType(SurfaceHolder.SURFACE_TYPE_GPU);
+        
+    }
+
+    //Audio
+    public static boolean initAudio(){        
+
+        //blah. Hardcoded things are bad. FIXME when we have more sound stuff
+        //working properly. 
+        mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
+                    11025,
+                    AudioFormat.CHANNEL_CONFIGURATION_MONO,
+                    AudioFormat.ENCODING_PCM_8BIT,
+                    2048,
+                    AudioTrack.MODE_STREAM);   
+        bAudioIsEnabled = true;     
+        return true;
+    }
+
+    //Accel
+    public static boolean initAccel(){
+        mSurface.enableSensor(Sensor.TYPE_ACCELEROMETER, true);
+        bAccelIsEnabled = true;
+        return true;
+    }
+    
+    public static boolean closeAccel(){
+        mSurface.enableSensor(Sensor.TYPE_ACCELEROMETER, false);
+        bAccelIsEnabled = false;
+        return true;
+    }
+    
+
+    //Events
+    protected void onPause() {
+        super.onPause();
+    }
+
+    protected void onResume() {
+        super.onResume();
+    }
+
+    
+
+
+
+    //C functions we call
+    public static native void nativeInit();
+    public static native void nativeQuit();
+    public static native void nativeSetScreenSize(int width, int height);
+    public static native void onNativeKeyDown(int keycode);
+    public static native void onNativeKeyUp(int keycode);
+    public static native void onNativeTouch(int action, float x, 
+                                            float y, float p);
+    public static native void onNativeResize(int x, int y, int format);
+    public static native void onNativeAccel(float x, float y, float z);
+
+
+
+    //Java functions called from C
+    private static void createGLContext(){
+        mSurface.initEGL();
+    }
+
+    public static void flipBuffers(){
+        mSurface.flipEGL();
+    }
+
+    public static void updateAudio(byte [] buf){
+    
+        if(mAudioTrack == null){
+            return;
+        }
+        
+        mAudioTrack.write(buf, 0, buf.length);
+        mAudioTrack.play();
+        
+        Log.v("SDL","Played some audio");
+    }
+
+    public static void enableFeature(int featureid, int enabled){
+         Log.v("SDL","Feature " + featureid + " = " + enabled);
+
+        //Yuck. This is all horribly inelegent. If it gets to more than a few
+        //'features' I'll rip this out and make something nicer, I promise :)
+        if(featureid == FEATURE_AUDIO){
+            if(enabled == 1){
+                initAudio();
+            }else{
+                //We don't have one of these yet...
+                //closeAudio(); 
+            }
+        }
+
+        else if(featureid == FEATURE_ACCEL){
+            if(enabled == 1){
+                initAccel();
+            }else{
+                closeAccel();
+            }
+        }
+    }
+
+
+
+
+
+    
+    
+}
+
+/**
+    Simple nativeInit() runnable
+*/
+class SDLRunner implements Runnable{
+    public void run(){
+        //SDLActivity.initAudio();
+        
+        //Runs SDL_main()
+        SDLActivity.nativeInit();
+
+        Log.v("SDL","SDL thread terminated");
+    }
+}
+
+
+/**
+    SDLSurface. This is what we draw on, so we need to know when it's created
+    in order to do anything useful. 
+
+    Because of this, that's where we set up the SDL thread
+*/
+class SDLSurface extends SurfaceView implements SurfaceHolder.Callback, 
+    View.OnKeyListener, View.OnTouchListener, SensorEventListener  {
+
+    //This is what SDL runs in. It invokes SDL_main(), eventually
+    private Thread mSDLThread;    
+    
+    //EGL private objects
+    private EGLContext  mEGLContext;
+    private EGLSurface  mEGLSurface;
+    private EGLDisplay  mEGLDisplay;
+
+    //Sensors
+    private static SensorManager mSensorManager;
+
+    //Startup    
+    public SDLSurface(Context context) {
+        super(context);
+        getHolder().addCallback(this); 
+    
+        setFocusable(true);
+        setFocusableInTouchMode(true);
+        requestFocus();
+        setOnKeyListener(this); 
+        setOnTouchListener(this);   
+        
+        mSensorManager = (SensorManager)context.getSystemService("sensor");  
+    }
+
+    //Called when we have a valid drawing surface
+    public void surfaceCreated(SurfaceHolder holder) {
+        Log.v("SDL","Surface created"); 
+
+        int width = getWidth();
+        int height = getHeight();
+
+        //Set the width and height variables in C before we start SDL so we have
+        //it available on init
+        SDLActivity.nativeSetScreenSize(width, height);
+
+        //Now start up the C app thread
+        mSDLThread = new Thread(new SDLRunner(), "SDLThread"); 
+		mSDLThread.start();       
+    }
+
+    //Called when we lose the surface
+    public void surfaceDestroyed(SurfaceHolder holder) {
+        Log.v("SDL","Surface destroyed");
+        
+        SDLActivity.nativeQuit();
+
+        //Now wait for the SDL thread to quit
+        try{
+            mSDLThread.wait();
+        }catch(Exception e){
+            Log.v("SDL","Problem stopping thread: " + e);
+        }
+    }
+
+    //Called when the surface is resized
+    public void surfaceChanged(SurfaceHolder holder, int format, 
+                                int width, int height) {
+        Log.v("SDL","Surface resized");
+        
+        SDLActivity.onNativeResize(width, height, format);
+    }
+
+    //unused
+    public void onDraw(Canvas canvas) {}
+
+    
+    //EGL functions
+    public boolean initEGL(){
+        Log.v("SDL","Starting up");
+
+        try{
+
+            EGL10 egl = (EGL10)EGLContext.getEGL();
+
+            EGLDisplay dpy = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
+
+            int[] version = new int[2];
+            egl.eglInitialize(dpy, version);
+
+            int[] configSpec = {
+                    //EGL10.EGL_DEPTH_SIZE,   16,
+                    EGL10.EGL_NONE
+            };
+            EGLConfig[] configs = new EGLConfig[1];
+            int[] num_config = new int[1];
+            egl.eglChooseConfig(dpy, configSpec, configs, 1, num_config);
+            EGLConfig config = configs[0];
+
+            EGLContext ctx = egl.eglCreateContext(dpy, config, EGL10.EGL_NO_CONTEXT, null);
+
+            EGLSurface surface = egl.eglCreateWindowSurface(dpy, config, this, null);
+
+            egl.eglMakeCurrent(dpy, surface, surface, ctx);
+
+            mEGLContext = ctx;
+            mEGLDisplay = dpy;
+            mEGLSurface = surface;
+            
+            
+        }catch(Exception e){
+            Log.v("SDL", e + "");
+            for(StackTraceElement s : e.getStackTrace()){
+                Log.v("SDL", s.toString());
+            }
+        }
+        Log.v("SDL","Done making!");
+
+        return true;
+    }
+
+    //EGL buffer flip
+    public void flipEGL(){      
+        try{
+        
+            EGL10 egl = (EGL10)EGLContext.getEGL();
+            GL10 gl = (GL10)mEGLContext.getGL();
+
+            egl.eglWaitNative(EGL10.EGL_NATIVE_RENDERABLE, null);
+
+            //drawing here
+
+            egl.eglWaitGL();
+
+            egl.eglSwapBuffers(mEGLDisplay, mEGLSurface);
+
+            
+        }catch(Exception e){
+            Log.v("SDL", "flipEGL(): " + e);
+
+            for(StackTraceElement s : e.getStackTrace()){
+                Log.v("SDL", s.toString());
+            }
+        }
+    }
+
+
+  
+    //Key events
+    public boolean onKey(View  v, int keyCode, KeyEvent event){
+
+        if(event.getAction() == KeyEvent.ACTION_DOWN){
+            SDLActivity.onNativeKeyDown(keyCode);
+            return true;
+        }
+        
+        else if(event.getAction() == KeyEvent.ACTION_UP){
+            SDLActivity.onNativeKeyUp(keyCode);
+            return true;
+        }
+        
+        return false;
+    }
+
+    //Touch events
+    public boolean onTouch(View v, MotionEvent event){
+    
+        int action = event.getAction();
+        float x = event.getX();
+        float y = event.getY();
+        float p = event.getPressure();
+
+        //TODO: Anything else we need to pass?        
+        SDLActivity.onNativeTouch(action, x, y, p);
+        return true;
+    }
+
+    //Sensor events
+    public void enableSensor(int sensortype, boolean enabled){
+        //TODO: This uses getDefaultSensor - what if we have >1 accels?
+        if(enabled){
+            mSensorManager.registerListener(this, 
+                            mSensorManager.getDefaultSensor(sensortype), 
+                            SensorManager.SENSOR_DELAY_GAME, null);
+        }else{
+            mSensorManager.unregisterListener(this, 
+                            mSensorManager.getDefaultSensor(sensortype));
+        }
+    }
+    
+    public void onAccuracyChanged(Sensor sensor, int accuracy){
+        //TODO
+    }
+
+    public void onSensorChanged(SensorEvent event){
+        if(event.sensor.getType() == Sensor.TYPE_ACCELEROMETER){
+            SDLActivity.onNativeAccel(  event.values[0],
+                                        event.values[1],
+                                        event.values[2] );
+        }
+    }
+
+
+}
+
+