diff --git a/examples/android/.cvsignore b/examples/android/.cvsignore
new file mode 100644
index 00000000..6a4fdcdf
--- /dev/null
+++ b/examples/android/.cvsignore
@@ -0,0 +1,3 @@
+bin
+gen
+libs
diff --git a/examples/android/AndroidManifest.xml b/examples/android/AndroidManifest.xml
new file mode 100644
index 00000000..e6e4448f
--- /dev/null
+++ b/examples/android/AndroidManifest.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/android/assets/activity.lua b/examples/android/assets/activity.lua
new file mode 100644
index 00000000..e5e893b3
--- /dev/null
+++ b/examples/android/assets/activity.lua
@@ -0,0 +1,152 @@
+-- Simple lua script that is executed when the activity start.
+--
+-- Arguments are the activity and the view.
+local activity, view = ...
+print('activity', activity, 'view', view)
+
+-- forward declarations of local functions and variables
+local animate,render
+local chars = {}
+local W, H = 600, 800
+
+-- called on key down event
+function onKeyDown(keyCode, event)
+ print('onKeyDown', keyCode, event)
+end
+
+-- called on key up event
+function onKeyUp(keyCode, event)
+ print('onKeyUp', keyCode, event)
+end
+
+-- called on touch event
+function onTouchEvent(event)
+ -- print('onTouchEvent', event)
+ local x1, y1 = event:getX(), event:getY()
+ local i,j = math.floor(x1*256/W), math.floor(y1*256/H)
+ local u = i + j * 256
+ local c = u < 0x80 and string.char(u) or
+ u < 0x800 and string.char(0xc0 + math.floor(u/0x80) % 0x20,
+ u % 0x80 ) or
+ string.char(0xe0 + math.floor(u/0x2000) % 0x8,
+ 0x80 + math.floor(u/0x80) % 0x80,
+ u % 0x80)
+ while #chars > 4 do table.remove(chars, 1) end
+ table.insert(chars, {
+ n=255,
+ x=-4+12*math.random(),
+ y=-8+12*math.random(),
+ text=c})
+ return true
+end
+
+-- called on trackball event
+function onTrackballEvent(event)
+ print('onTrackballEvent', event)
+ return true
+end
+
+function onWindowFocusChanged(hasWindowFocus)
+ print('onWindowFocusChanged', hasWindowFocus)
+end
+
+function onWindowSystemUiVisibilityChanged(visible)
+ print('onWindowSystemUiVisibilityChanged', visible)
+end
+
+-- called to draw the lua view
+local prev, interval = os.time(), 1/60
+local Thread = luajava.bindClass('java.lang.Thread')
+function draw(canvas)
+ view:invalidate()
+ local curr = os.time()
+ local diff = curr - prev
+ if diff >= interval then
+ pcall(animate, canvas)
+ local wait = math.floor(1000 * (prev + interval - os.time()))
+ if wait > 0 then pcall(Thread.sleep, Thread, wait) end
+ prev = os.time()
+ end
+ pcall(render, canvas)
+end
+
+-- the animation step moves the line endpoints
+local x1,y1,x2,y2,xi,yi = 160,240,480,240,0,0
+local vx1,vy1,vx2,vy2,vxi,vyi = -5,-6,7,8,3,1
+local w, h = W, H
+local advance = function(x,vx,max,rnd)
+ x = x + vx
+ if x < 0 then
+ return 0, math.random(2,10)
+ elseif x > max then
+ return max, math.random(-10,-2)
+ end
+ return x, vx
+end
+animate = function(canvas)
+ x1,y1,x2,y2 = x1+1,y1+1,x2-1,y2-1
+ x1,vx1 = advance(x1,vx1,w)
+ y1,vy1 = advance(y1,vy1,h)
+ x2,vx2 = advance(x2,vx2,w)
+ y2,vy2 = advance(y2,vy2,h)
+ xi,vxi = advance(xi,vxi,w-100)
+ yi,vyi = advance(yi,vyi,h-100)
+ while #chars > 0 and chars[1].n <= 1 do
+ table.remove(chars, 1)
+ end
+ for i,c in pairs(chars) do
+ c.n = c.n - 1
+ end
+end
+
+-- classes that we need for rendering
+local Color = luajava.bindClass('android.graphics.Color')
+local Paint = luajava.bindClass('android.graphics.Paint')
+local Style = luajava.bindClass('android.graphics.Paint$Style')
+print('Color, Paint, Style', Color, Paint, Style)
+
+-- colors that we use
+local text_color, line_color = Paint.new(), Paint.new()
+text_color:setColor(0xffffff33)
+text_color:setTextSize(8.0)
+line_color:setColor(0xffffaa33)
+line_color:setStrokeWidth(1.5)
+line_color:setStyle(Style.STROKE)
+print('text_color, line_color', text_color, line_color)
+
+-- load the logo image
+local istream = view:findResource('logo.gif')
+print('istream', istream)
+local BitmapFactory = luajava.bindClass('android.graphics.BitmapFactory')
+local logo = BitmapFactory:decodeStream(istream)
+print('logo', logo)
+
+-- the render step draws the scene
+render = function(canvas)
+ -- scale the drawing to approximagely 600 x 800
+ W, H = canvas:getWidth(), canvas:getHeight();
+ local scale = (W + H) / (600 + 800)
+ canvas:scale(scale, scale)
+ w, h = W / scale, H / scale
+
+ -- redraw the canvas
+ canvas:drawColor(0xff112244)
+
+ -- line
+ canvas:drawLine(x1, y1, x2, y2, line_color)
+
+ -- text
+ canvas:translate(w/2,h/2)
+ for i,c in pairs(chars) do
+ local s = 200 / (256-c.n)
+ canvas:scale(s, s)
+ canvas:drawText(c.text, c.x-4, c.y+6, text_color)
+ canvas:scale(1/s, 1/s)
+ end
+ canvas:translate(-w/2,-h/2)
+
+ -- image
+ canvas:drawBitmap(logo,xi,yi)
+end
+
+
diff --git a/examples/android/assets/logo.gif b/examples/android/assets/logo.gif
new file mode 100644
index 00000000..193e6920
Binary files /dev/null and b/examples/android/assets/logo.gif differ
diff --git a/examples/android/build.xml b/examples/android/build.xml
new file mode 100644
index 00000000..bb698dca
--- /dev/null
+++ b/examples/android/build.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+ sdk.dir: ${sdk.dir}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/android/res/drawable-hdpi/ic_launcher.png b/examples/android/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 00000000..2711c476
Binary files /dev/null and b/examples/android/res/drawable-hdpi/ic_launcher.png differ
diff --git a/examples/android/res/drawable-mdpi/ic_launcher.png b/examples/android/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 00000000..ec2c092f
Binary files /dev/null and b/examples/android/res/drawable-mdpi/ic_launcher.png differ
diff --git a/examples/android/res/drawable-xhdpi/ic_launcher.png b/examples/android/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 00000000..5f1f38c1
Binary files /dev/null and b/examples/android/res/drawable-xhdpi/ic_launcher.png differ
diff --git a/examples/android/res/drawable-xxhdpi/ic_launcher.png b/examples/android/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 00000000..449575c2
Binary files /dev/null and b/examples/android/res/drawable-xxhdpi/ic_launcher.png differ
diff --git a/examples/android/res/menu/luaj.xml b/examples/android/res/menu/luaj.xml
new file mode 100644
index 00000000..c0020282
--- /dev/null
+++ b/examples/android/res/menu/luaj.xml
@@ -0,0 +1,9 @@
+
diff --git a/examples/android/res/values/dimens.xml b/examples/android/res/values/dimens.xml
new file mode 100644
index 00000000..55c1e590
--- /dev/null
+++ b/examples/android/res/values/dimens.xml
@@ -0,0 +1,7 @@
+
+
+
+ 16dp
+ 16dp
+
+
diff --git a/examples/android/res/values/strings.xml b/examples/android/res/values/strings.xml
new file mode 100644
index 00000000..68c087d4
--- /dev/null
+++ b/examples/android/res/values/strings.xml
@@ -0,0 +1,8 @@
+
+
+
+ Luaj
+ Settings
+ Hello world!
+
+
diff --git a/examples/android/res/values/styles.xml b/examples/android/res/values/styles.xml
new file mode 100644
index 00000000..6ce89c7b
--- /dev/null
+++ b/examples/android/res/values/styles.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
diff --git a/examples/android/src/android/LuajActivity.java b/examples/android/src/android/LuajActivity.java
new file mode 100644
index 00000000..0a6380e8
--- /dev/null
+++ b/examples/android/src/android/LuajActivity.java
@@ -0,0 +1,30 @@
+package org.luaj.vm2.android;
+
+import org.luaj.vm2.LuaValue;
+import org.luaj.vm2.lib.jse.CoerceJavaToLua;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.Menu;
+
+public class LuajActivity extends Activity {
+
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ LuajView view = new LuajView(this);
+ setContentView(view);
+ try {
+ LuaValue activity = CoerceJavaToLua.coerce(this);
+ LuaValue viewobj = CoerceJavaToLua.coerce(view);
+ view.globals.loadfile("activity.lua").call(activity, viewobj);
+ } catch ( Exception e ) {
+ e.printStackTrace();
+ }
+ }
+
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.luaj, menu);
+ return true;
+ }
+
+}
diff --git a/examples/android/src/android/LuajView.java b/examples/android/src/android/LuajView.java
new file mode 100644
index 00000000..ea5af0c0
--- /dev/null
+++ b/examples/android/src/android/LuajView.java
@@ -0,0 +1,122 @@
+package org.luaj.vm2.android;
+
+import java.io.InputStream;
+
+import org.luaj.vm2.Globals;
+import org.luaj.vm2.LuaValue;
+import org.luaj.vm2.lib.ResourceFinder;
+import org.luaj.vm2.lib.jse.CoerceJavaToLua;
+import org.luaj.vm2.lib.jse.JsePlatform;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
+
+public class LuajView extends View implements ResourceFinder {
+
+ public final Globals globals;
+
+ public LuajView(Context context) {
+ super(context);
+ this.globals = JsePlatform.standardGlobals();
+ this.globals.FINDER = this;
+ }
+
+ // Implement a finder that loads from the assets directory.
+ public InputStream findResource(String name) {
+ try {
+ return getContext().getAssets().open(name);
+ } catch (java.io.IOException ioe) {
+ return null;
+ }
+ }
+
+
+ public void draw(Canvas canvas) {
+ LuaValue f = globals.get("draw");
+ if (!f.isnil())
+ try {
+ f.call(CoerceJavaToLua.coerce(canvas));
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ else
+ super.draw(canvas);
+ }
+
+ public boolean f(int keyCode, KeyEvent event) {
+ LuaValue f = globals.get("onKeyDown");
+ if (!f.isnil())
+ try {
+ return f.call(CoerceJavaToLua.coerce(keyCode),
+ CoerceJavaToLua.coerce(event)).toboolean();
+ } catch (Exception e) {
+ e.printStackTrace();
+ return true;
+ }
+ else
+ return super.onKeyDown(keyCode, event);
+ }
+
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ LuaValue f = globals.get("onKeyUp");
+ if (!f.isnil())
+ try {
+ return f.call(CoerceJavaToLua.coerce(keyCode),
+ CoerceJavaToLua.coerce(event)).toboolean();
+ } catch (Exception e) {
+ e.printStackTrace();
+ return true;
+ }
+ else
+ return super.onKeyUp(keyCode, event);
+ }
+
+ public boolean onTouchEvent(MotionEvent event) {
+ LuaValue f = globals.get("onTouchEvent");
+ if (!f.isnil())
+ try {
+ return f.call(CoerceJavaToLua.coerce(event)).toboolean();
+ } catch (Exception e) {
+ e.printStackTrace();
+ return true;
+ }
+ else
+ return super.onTouchEvent(event);
+ }
+
+ public boolean onTrackballEvent(MotionEvent event) {
+ LuaValue f = globals.get("onTrackballEvent");
+ if (!f.isnil())
+ try {
+ return f.call(CoerceJavaToLua.coerce(event)).toboolean();
+ } catch (Exception e) {
+ e.printStackTrace();
+ return true;
+ }
+ else
+ return super.onTrackballEvent(event);
+ }
+
+ public void onWindowFocusChanged(boolean hasWindowFocus) {
+ LuaValue f = globals.get("onWindowFocusChanged");
+ if (!f.isnil())
+ try {
+ f.call(CoerceJavaToLua.coerce(hasWindowFocus));
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void onWindowSystemUiVisibilityChanged(int visible) {
+ LuaValue f = globals.get("onWindowSystemUiVisibilityChanged");
+ if (!f.isnil())
+ try {
+ f.call(CoerceJavaToLua.coerce(visible));
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}