diff --git a/AndroidManifest.xml b/AndroidManifest.xml index fbf530f..5c15724 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -2,12 +2,15 @@ + android:versionCode="6" + android:versionName="1.2" > + android:targetSdkVersion="18" /> + + + + android:versionCode="6" + android:versionName="1.2" > + android:targetSdkVersion="18" /> + + + + + + + UA-44695784-1 + + + true + + + true + \ No newline at end of file diff --git a/res/values/strings.xml b/res/values/strings.xml index 0845712..e05245e 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -16,6 +16,10 @@ Random starting player If selected, the starting player will be selected at random for each game. Gameplay - Settings + Settings + Clear the Board? + Cancel + Reset + Clear the board? \ No newline at end of file diff --git a/src/com/exposuresoftware/xsandos/Game.java b/src/com/exposuresoftware/xsandos/Game.java index 0fecf75..163e727 100644 --- a/src/com/exposuresoftware/xsandos/Game.java +++ b/src/com/exposuresoftware/xsandos/Game.java @@ -5,8 +5,11 @@ import android.annotation.SuppressLint; import android.app.Activity; import android.app.Dialog; +import android.content.Context; import android.content.Intent; import android.graphics.Typeface; +import android.hardware.Sensor; +import android.hardware.SensorManager; import android.os.Bundle; import android.preference.PreferenceManager; import android.util.Log; @@ -18,6 +21,8 @@ import android.widget.Toast; import com.exposuresoftware.xsandos.GameBoard.Mark; +import com.google.analytics.tracking.android.EasyTracker; + public class Game extends Activity { @@ -31,8 +36,13 @@ public class Game extends Activity { GameBoard board = null; int[][] buttons = new int[3][3]; + private SensorManager sensorManager; + private ShakeEventListener sensorListener; + Toast toast_player_x = null, toast_player_o = null; + boolean dialog_up = false; + // TODO Add shake to clear // TODO Move switchPlayer() out of handleButtonForSpace(View) @@ -45,16 +55,74 @@ public void onCreate(Bundle icicle) { solo_player = getIntent().getExtras().getBoolean("solo"); chalkduster = Typeface.createFromAsset(getAssets(), "fonts/Chalkduster.ttf"); + sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); + sensorListener = new ShakeEventListener(); + + sensorListener.setOnShakeListener(new ShakeEventListener.OnShakeListener() { + + public void onShake() { + if ( !dialog_up ) { + Typeface chalkduster = Typeface.createFromAsset(getAssets(), + "fonts/Chalkduster.ttf"); + final Dialog dialog = new Dialog(Game.this, R.style.CleanDialog); + dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); + dialog.setContentView( R.layout.dialog_base ); + dialog.setCancelable( false ); + TextView title = (TextView) dialog.findViewById( R.id.dialog_header ); + title.setTypeface(chalkduster); + TextView message = (TextView) dialog.findViewById(R.id.dialog_message); + message.setTypeface(chalkduster); + Button ok_button = (Button) dialog.findViewById(R.id.dialog_new_game); + Button title_button = (Button) dialog.findViewById(R.id.dialog_title_screen); + ok_button.setTypeface( chalkduster ); + title_button.setTypeface(chalkduster); + // Now, change what they say! + title.setText(R.string.dialog_shake_header); + ok_button.setText(R.string.new_game); + title_button.setText(R.string.cancel); + message.setText(R.string.clear_board); + ok_button.setOnClickListener( new OnClickListener() { + public void onClick(View view) { + Log.d( TAG, "Restarting game" ); + Intent intentToRestart = getIntent(); + startActivity(intentToRestart); + dialog.dismiss(); + finish(); + } + }); + title_button.setOnClickListener( new OnClickListener() { + public void onClick(View view) { + Log.d( TAG, "Shake cancelled" ); + dialog_up = false; + dialog.dismiss(); + } + }); + dialog.show(); + dialog_up = true; + } + } + }); board = new GameBoard(SIZE); Log.d(TAG, "Game board created."); this.toast_player_x = Toast.makeText(this, R.string.msg_player_one, Toast.LENGTH_SHORT); this.toast_player_o = Toast.makeText(this, R.string.msg_player_two, Toast.LENGTH_SHORT); } + @Override + public void onStart() { + super.onStart(); + EasyTracker.getInstance(this).activityStart(this); + } + public void onResume() { super.onResume(); Log.d( TAG, "Building button ID array" ); buildButtonIds(); + // Register shake listener + sensorManager.registerListener(sensorListener, + sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), + SensorManager.SENSOR_DELAY_UI); + // TODO Make sure orientation change doesn't restart game with this! boolean random_start = PreferenceManager.getDefaultSharedPreferences( this ) .getBoolean( "pref_key_random_start", true ); Log.d( TAG, "Random starting player: " + Boolean.toString( random_start ) ); @@ -72,6 +140,18 @@ public void onResume() { } } + @Override + public void onPause() { + sensorManager.unregisterListener(sensorListener); + super.onPause(); + } + + @Override + public void onStop() { + EasyTracker.getInstance(this).activityStop(this); + super.onStop(); + } + private void buildButtonIds() { buttons[0][0] = R.id.button_0_0; buttons[0][1] = R.id.button_0_1; @@ -138,7 +218,9 @@ public void onClick(View view) { case IN_PROGRESS: } } - switchPlayer(); + if ( board.gameState == GameBoard.State.IN_PROGRESS ) { + switchPlayer(); + } } private void switchPlayer() { diff --git a/src/com/exposuresoftware/xsandos/MainMenu.java b/src/com/exposuresoftware/xsandos/MainMenu.java index cba2516..4f3249c 100644 --- a/src/com/exposuresoftware/xsandos/MainMenu.java +++ b/src/com/exposuresoftware/xsandos/MainMenu.java @@ -1,5 +1,7 @@ package com.exposuresoftware.xsandos; +import com.google.analytics.tracking.android.EasyTracker; + import android.app.Activity; import android.content.Intent; import android.graphics.Typeface; @@ -28,6 +30,18 @@ protected void onCreate(Bundle savedInstanceState) { button_one_player.setTypeface(chalkduster); button_two_player.setTypeface(chalkduster); } + + @Override + public void onStart() { + super.onStart(); + EasyTracker.getInstance(this).activityStart(this); + } + + @Override + public void onStop() { + super.onStop(); + EasyTracker.getInstance(this).activityStop(this); + } @Override public boolean onCreateOptionsMenu(Menu menu) { diff --git a/src/com/exposuresoftware/xsandos/SettingsActivity.java b/src/com/exposuresoftware/xsandos/SettingsActivity.java index cd85e26..383482e 100644 --- a/src/com/exposuresoftware/xsandos/SettingsActivity.java +++ b/src/com/exposuresoftware/xsandos/SettingsActivity.java @@ -1,13 +1,27 @@ package com.exposuresoftware.xsandos; +import com.google.analytics.tracking.android.EasyTracker; + import android.app.Activity; import android.os.Bundle; public class SettingsActivity extends Activity { - private static String TAG = "Xs And Os - Settings"; + + //private static String TAG = "Xs And Os - Settings"; + public void onCreate( Bundle savedInstanceState ) { super.onCreate( savedInstanceState ); getFragmentManager().beginTransaction().replace( android.R.id.content, new SettingsFragment() ) .commit(); } + + public void onStart() { + super.onStart(); + EasyTracker.getInstance(this).activityStart(this); + } + + public void onStop() { + super.onStop(); + EasyTracker.getInstance(this).activityStop(this); + } } diff --git a/src/com/exposuresoftware/xsandos/ShakeEventListener.java b/src/com/exposuresoftware/xsandos/ShakeEventListener.java new file mode 100644 index 0000000..0b7bc33 --- /dev/null +++ b/src/com/exposuresoftware/xsandos/ShakeEventListener.java @@ -0,0 +1,94 @@ +package com.exposuresoftware.xsandos; + +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; +import android.util.Log; + +public class ShakeEventListener implements SensorEventListener { + + private static final String TAG = "Xs And Os - Shake"; + + private static final int MIN_FORCE = 40; + private static final int MIN_DIRECTION_CHANGE = 2; + private static final int MAX_PAUSE_BETWEEN_DIRECTION_CHANGE = 200; + private static final int MAX_TOTAL_DURATION_OF_SHAKE = 400; + private long firstDirectionChangeTime = 0; + private long lastDirectionChangeTime; + private int directionChangeCount = 0; + private float lastX = 0; + private float lastY = 0; + private float lastZ = 0; + private OnShakeListener shakeListener; + + public interface OnShakeListener { + void onShake(); + } + + public void setOnShakeListener(OnShakeListener listener) { + shakeListener = listener; + } + + @Override + public void onAccuracyChanged(Sensor sensor, int amount) { + // TODO Auto-generated method stub + + } + + @Override + public void onSensorChanged(SensorEvent event) { + // Get our position + float x = event.values[SensorManager.DATA_X]; + float y = event.values[SensorManager.DATA_Y]; + float z = event.values[SensorManager.DATA_Z]; + Log.d( TAG, "New position: " + x + ", " + y + ", " + z ); + + // Get the movement + // TODO Break out paper and see why this works! + float moved = Math.abs(x + y + z - lastX - lastY - lastZ); + + // If moved more then our threshold + if ( moved > MIN_FORCE ) { + // Note the time for durations + long eventOccuredTime = System.currentTimeMillis(); + + // Is this the first movement? + if ( 0 == firstDirectionChangeTime ) { + // Yes, set our comparison values + firstDirectionChangeTime = eventOccuredTime; + lastDirectionChangeTime = eventOccuredTime; + } + + long changeFrequency = eventOccuredTime - lastDirectionChangeTime; + if ( MAX_PAUSE_BETWEEN_DIRECTION_CHANGE > changeFrequency ) { + lastDirectionChangeTime = eventOccuredTime; + directionChangeCount++; + lastX = x; + lastY = y; + lastZ = z; + if ( MIN_DIRECTION_CHANGE <= directionChangeCount ) { + // How long has this been going on? + long totalDuration = eventOccuredTime - firstDirectionChangeTime; + if ( MAX_TOTAL_DURATION_OF_SHAKE > totalDuration ) { + // We have a shake! + shakeListener.onShake(); + resetShakes(); + } + } + } else { + resetShakes(); + } + } + } + + private void resetShakes() { + firstDirectionChangeTime = 0; + directionChangeCount = 0; + lastDirectionChangeTime = 0; + lastX = 0; + lastY = 0; + lastZ = 0; + } + +}