package se.nekotronic.satelliterushtest; import java.util.ArrayList; import java.util.Timer; import java.util.TimerTask; import android.app.Activity; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.drawable.ShapeDrawable; import android.graphics.drawable.shapes.OvalShape; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.os.PowerManager; import android.os.PowerManager.WakeLock; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.Log; import android.view.Display; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.Surface; import android.view.View; import android.view.WindowManager; class Brick { public int x, y; public int radius; public int color; } public class RushView extends View implements SensorEventListener { // private ShapeDrawable my_drawable; private ArrayList bricks = new ArrayList(); public int MAX_BRICKS = 100; Timer ticker = new Timer(); public final int NOT_READY = 1, // Not started RUNNING = 2, PAUSED = 3, // Will continue as soon as visible STOPPED = 4; // Will not continue until user commands it private int status = NOT_READY; private WindowManager the_window_manager; private SensorManager the_sensor_manager; private Display the_display; private Sensor the_accelerometer; private PowerManager the_power_manager; private WakeLock the_wake_lock; // private SensorManager the_sensor_manager; private void init(Context context) { screen_density = -1; // Get an instance of the SensorManager // the_sensor_manager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); // mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); // Get an instance of the WindowManager the_window_manager = (WindowManager) context.getSystemService(context.WINDOW_SERVICE); the_display = the_window_manager.getDefaultDisplay(); DisplayMetrics dm = new DisplayMetrics(); the_display.getMetrics(dm); // Get an instance of the SensorManager the_sensor_manager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); the_accelerometer = the_sensor_manager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); // Get an instance of the PowerManager the_power_manager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); // Create a bright wake lock the_wake_lock = the_power_manager.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, getClass().getName()); } // init public RushView(Context context) { super(context); init(context); } public RushView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public RushView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(context); } private int n = 0; private int width_pixels = -1; private int height_pixels = -1; private float screen_density = -1; // Measured in SCREENS private float ball_x = 0.5f; private float ball_y = 0.5f; // Measured in SCREENS PER SECOND private float ball_dx = 0.1f; private float ball_dy = 0.1f; // Measured in SCREENS PER SECOND-SQUARED private float gravity_x = 0.02f; private float gravity_y = 0.02f; protected void onDraw(Canvas canvas) { // my_drawable.draw(canvas); super.onDraw(canvas); //int h = canvas.getHeight(); //int w = canvas.getWidth(); //height_pixels = h; //width_pixels = w; int h = height_pixels; if (h == -1) h = canvas.getHeight(); int w = width_pixels; if (w == -1) w = canvas.getWidth(); float d = screen_density; if (d == -1) d = 1.0f; Paint p = new Paint(); p.setColor(Color.RED); canvas.drawText("n = " + ++n + ", x = " + roundDouble(ball_x, 2) + ", y = " + roundDouble(ball_y, 2) + ", dx = " + roundDouble(ball_dx, 2) + ", dy = " + roundDouble(ball_dy, 2), 20.0f, 20.0f, p); canvas.drawText("h = " + h + " (" + height_pixels + ")" + ", w = " + w + " (" + width_pixels + ")", 20.0f, 40.0f, p); canvas.drawText("d = " + d + " (" + screen_density + ")", 20.0f, 60.0f, p); p.setColor(Color.BLUE); canvas.drawCircle(ball_x * w, ball_y * h, 10 * d, p); long now_nanos = java.lang.System.nanoTime(); if (latest_tilt_nanos == -1) { // No tilt } else if (latest_tilt_nanos > now_nanos) { // Overflow! latest_tilt_nanos = -1; } else if (now_nanos - latest_tilt_nanos < 1e9) { // Less than a second since latest tilt // p.setColor(Color.WHITE); p.setARGB(192, 255, 255, 255); canvas.drawCircle(0.5f * w, 0.5f * h, 100 * d, p); p.setColor(Color.RED); p.setTextSize(100.0f); canvas.drawText("TILT", 0.25f * w, 0.60f * h, p); } else { // There was a tilt, but more than a second ago latest_tilt_nanos = -1; } } // onDraw // Called when the view is to be hidden or destroyed public void pause() { if (status == RUNNING) { status = PAUSED; if (ticker != null) { ticker.cancel(); ticker = null; } nanos_when_paused = java.lang.System.nanoTime(); } the_sensor_manager.unregisterListener(this); the_wake_lock.release(); } // User command to pause the game public void stop() { if (status == RUNNING) { status = STOPPED; if (ticker != null) { ticker.cancel(); ticker = null; } nanos_when_paused = java.lang.System.nanoTime(); } else if (status == PAUSED) { // Shouldn't happen status = STOPPED; } } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { width_pixels = w; height_pixels = h; // screen_density = 1.0f; Context context = getContext(); if (context != null && context instanceof Activity) { Activity parent = (Activity)getContext(); DisplayMetrics dm = new DisplayMetrics(); parent.getWindowManager().getDefaultDisplay().getMetrics(dm); screen_density = dm.density; } } private void start_ticker() { ticker = new Timer(); TimerTask task = new TimerTask() { @Override public void run() { update_simulation(); Runnable r = new Runnable() { @Override public void run() { // print("Tick!"); invalidate(); } }; getHandler().post(r); } }; ticker.schedule(task, 10, 10); } /* @Override public boolean onKeyDown(int keyCode, KeyEvent event) { // TODO Auto-generated method stub // return super.onKeyDown(keyCode, event); ball_x = 0.5f; ball_y = 0.5f; return false; // So the rest works } */ private long previous_touch_nanos = -1; private int previous_touch_x = -1; private int previous_touch_y = -1; @Override public boolean onTouchEvent(MotionEvent event) { // Set position of ball int x = (int)event.getX(); int y = (int)event.getY(); ball_x = 1.0f*x/width_pixels; ball_y = 1.0f*y/height_pixels; // Set velocity of ball long now_nanos = java.lang.System.nanoTime(); if (event.getAction() == MotionEvent.ACTION_MOVE && previous_touch_nanos != -1 && now_nanos > previous_touch_nanos) { long nanos = now_nanos - previous_touch_nanos; double seconds = nanos / 1e9; int xdiff = x - previous_touch_x; int ydiff = y - previous_touch_y; ball_dx = (float)((double)xdiff/width_pixels/seconds); ball_dy = (float)((double)ydiff/width_pixels/seconds); Log.d("SRT", "xdiff = " + xdiff); Log.d("SRT", "ydiff = " + ydiff); Log.d("SRT", "ball_dx = " + ball_dx); Log.d("SRT", "ball_dy = " + ball_dy); } previous_touch_nanos = now_nanos; previous_touch_x = x; previous_touch_y = y; return true; } // Called when the view is to be shown public void resume() { if (status == PAUSED) { status = RUNNING; start_ticker(); the_wake_lock.acquire(); the_sensor_manager.registerListener(this, the_accelerometer, SensorManager.SENSOR_DELAY_UI); } } // resume // -1 is not guaranteed to never happen, but we ignore that private long previous_nanos = -1; private long nanos_when_paused = -1; private long latest_tilt_nanos = -1; private void update_simulation() { long now_nanos = java.lang.System.nanoTime(); if (previous_nanos == -1 || now_nanos < previous_nanos) { // First time, or overflow, so don't update the game previous_nanos = now_nanos; return; } long nanos = now_nanos - previous_nanos; if (nanos_when_paused != -1) { // We have been paused! nanos = nanos_when_paused - previous_nanos; nanos_when_paused = -1; } previous_nanos = now_nanos; double seconds = nanos / 1e9; ball_dx += gravity_x * seconds; ball_dy += gravity_y * seconds; ball_dx *= 1 - (0.5 * seconds); ball_dy *= 1 - (0.5 * seconds); ball_x += ball_dx * seconds; ball_y += ball_dy * seconds; if (ball_x < 0) { ball_x = 0; ball_dx = -ball_dx; } if (ball_x >= 1) { ball_x = 1; ball_dx = -ball_dx; } if (ball_y < 0) { ball_y = 0; ball_dy = -ball_dy; } if (ball_y >= 1) { ball_y = 1; ball_dy = -ball_dy; } } // update_simulation // User command to start or continue public void start() { if (status == NOT_READY || status == STOPPED) { status = PAUSED; resume(); } } public static final double roundDouble(double d, int places) { return Math.round(d * Math.pow(10, (double) places)) / Math.pow(10, (double) places); } public void set_gravity(float gx, float gy) { gravity_x = gx / 10; gravity_y = gy / 10; } @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { // TODO Auto-generated method stub } @Override public void onSensorChanged(SensorEvent event) { if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) { float mSensorX = 0; float mSensorY = 0; switch (the_display.getRotation()) { case Surface.ROTATION_0: mSensorX = event.values[0]; mSensorY = event.values[1]; break; case Surface.ROTATION_90: mSensorX = -event.values[1]; mSensorY = event.values[0]; break; case Surface.ROTATION_180: mSensorX = -event.values[0]; mSensorY = -event.values[1]; break; case Surface.ROTATION_270: mSensorX = event.values[1]; mSensorY = -event.values[0]; break; } set_gravity(-mSensorX, mSensorY); float mSensorZ = event.values[2]; if (Math.abs(mSensorX) > 10 || Math.abs(mSensorY) > 10 || Math.abs(mSensorZ) > 10) latest_tilt_nanos = java.lang.System.nanoTime(); } } // onSensorChanged } // class RushView