PageRenderTime 77ms CodeModel.GetById 30ms RepoModel.GetById 1ms app.codeStats 0ms

/src/com/barleysoft/blitzn/Blitzn.java

https://github.com/rconradharris/blitzn
Java | 518 lines | 421 code | 71 blank | 26 comment | 47 complexity | 15678c76c9dc159e45749026ce2723a9 MD5 | raw file
  1. package com.barleysoft.blitzn;
  2. import com.barleysoft.blitzn.chessclock.BlitznChessClock;
  3. import com.barleysoft.blitzn.chessclock.BlitznChessPlayer;
  4. import com.barleysoft.blitzn.chessclock.ChessClock;
  5. import com.barleysoft.blitzn.chessclock.ChessPlayer;
  6. import com.barleysoft.blitzn.chessclock.OnChessClockStopListener;
  7. import com.barleysoft.blitzn.chessclock.ChessClock.DelayMode;
  8. import com.barleysoft.blitzn.chessclock.ChessClock.Player;
  9. import com.barleysoft.motion.PitchFlipListener;
  10. import com.barleysoft.motion.ShakeListener;
  11. import android.view.HapticFeedbackConstants;
  12. import android.app.Activity;
  13. import android.app.AlertDialog;
  14. import android.app.Dialog;
  15. import android.content.DialogInterface;
  16. import android.content.Intent;
  17. import android.content.SharedPreferences;
  18. import android.content.pm.PackageInfo;
  19. import android.content.pm.PackageManager.NameNotFoundException;
  20. import android.media.AudioManager;
  21. import android.os.Bundle;
  22. import android.os.Handler;
  23. import android.os.SystemClock;
  24. import android.preference.PreferenceManager;
  25. import android.util.Log;
  26. import android.view.Menu;
  27. import android.view.MenuInflater;
  28. import android.view.MenuItem;
  29. import android.view.View;
  30. import android.view.View.OnClickListener;
  31. import android.view.Window;
  32. import android.view.WindowManager;
  33. public class Blitzn extends Activity {
  34. // Constants
  35. public static final long CLOCK_RESOLUTION = 100L; // ms
  36. public static final String PREFS_NAME = "BlitznPrefs";
  37. public static final int ACTIVITY_PREFERENCES = 0;
  38. // Member variables
  39. private ChessClock mChessClock;
  40. private BlitznChessClockButton mPlayer1ClockButton;
  41. private BlitznChessClockButton mPlayer2ClockButton;
  42. private ShakeListener mShakeListener;
  43. private PitchFlipListener mPitchFlipListener;
  44. private Handler mTimerHandler = new Handler();
  45. private AlertDialog mPausedDialog;
  46. // Configurations
  47. private long mDuration = 5 * 1 * 1000L;
  48. private boolean mShakeEnabled = true;
  49. private boolean mFlipEnabled = true;
  50. private boolean mSoundEnabled = true;
  51. private boolean mShowIntroDialog = true;
  52. private boolean mTimePressureWarningEnabled = true;
  53. private DelayMode mDelayMode = DelayMode.NODELAY;
  54. private long mDelayTime = 0L;
  55. private void initializeMainWindow() {
  56. // Go full screen
  57. requestWindowFeature(Window.FEATURE_NO_TITLE);
  58. getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
  59. WindowManager.LayoutParams.FLAG_FULLSCREEN);
  60. setContentView(R.layout.main);
  61. mPausedDialog = createPausedDialog();
  62. }
  63. private BlitznChessClockButton initializeClockButton(final Player player,
  64. int resId, boolean isFlipped, ChessClock chessClock,
  65. ChessPlayer chessPlayer) {
  66. BlitznChessClockButton button = (BlitznChessClockButton) findViewById(resId);
  67. button.setIsFlipped(isFlipped);
  68. button.setChessClock(chessClock);
  69. button.setChessPlayer(chessPlayer);
  70. button.setIsSoundEnabled(mSoundEnabled);
  71. button.setClockResolution(CLOCK_RESOLUTION);
  72. button.setIsTimePressureWarningEnabled(mTimePressureWarningEnabled);
  73. OnClickListener clickListener = new OnClickListener() {
  74. public void onClick(View v) {
  75. deactivatePlayer(player);
  76. }
  77. };
  78. button.setOnClickListener(clickListener);
  79. button.initialize();
  80. return button;
  81. }
  82. void initializeChessClock() {
  83. mChessClock = new BlitznChessClock();
  84. ChessPlayer chessPlayer1 = new BlitznChessPlayer();
  85. ChessPlayer chessPlayer2 = new BlitznChessPlayer();
  86. mChessClock.setChessPlayer(Player.ONE, chessPlayer1);
  87. mChessClock.setChessPlayer(Player.TWO, chessPlayer2);
  88. mChessClock.setClockResolution(CLOCK_RESOLUTION);
  89. mChessClock.setDuration(mDuration);
  90. mChessClock.setDelayMode(mDelayMode);
  91. mChessClock.setDelayTime(mDelayTime);
  92. mChessClock.initialize();
  93. mPlayer1ClockButton = initializeClockButton(Player.ONE,
  94. R.id.player1Clock, true, mChessClock, chessPlayer1);
  95. mPlayer2ClockButton = initializeClockButton(Player.TWO,
  96. R.id.player2Clock, false, mChessClock, chessPlayer2);
  97. mChessClock.setOnChessClockStopListener(new OnChessClockStopListener() {
  98. public void onStop() {
  99. stopClock();
  100. }
  101. });
  102. }
  103. @Override
  104. public void onCreate(Bundle savedInstanceState) {
  105. super.onCreate(savedInstanceState);
  106. initializeMainWindow();
  107. restorePreferences();
  108. initializeChessClock();
  109. installShakeListener();
  110. installPitchFlipListener();
  111. if (mShowIntroDialog) {
  112. showIntroDialogBox();
  113. }
  114. // So volume buttons control media volume not ringtone volume
  115. setVolumeControlStream(AudioManager.STREAM_MUSIC);
  116. }
  117. @Override
  118. protected void onResume() {
  119. super.onResume();
  120. if ((mShakeListener != null) && !mShakeListener.isListening()) {
  121. mShakeListener.resume();
  122. }
  123. if ((mPitchFlipListener != null) && !mPitchFlipListener.isListening()) {
  124. mPitchFlipListener.resume();
  125. }
  126. // When a paused game is resumed, we don't want to immediately start the
  127. // clock,
  128. // lest the player is not ready. Rather, we re-emit the paused dialog
  129. // box. Since the
  130. // clock was placed into the PAUSED state in onPause, we're in a
  131. // consistent state.
  132. if (mChessClock.isPaused() && !mPausedDialog.isShowing()) {
  133. mPausedDialog.show();
  134. }
  135. getPreferencesFromActivity();
  136. }
  137. @Override
  138. protected void onPause() {
  139. if (mShakeListener != null) {
  140. mShakeListener.pause();
  141. }
  142. if (mPitchFlipListener != null) {
  143. mPitchFlipListener.pause();
  144. }
  145. // Don't show the dialog box on a system initiated pause
  146. if (mChessClock.isStarted()) {
  147. pauseClock(false);
  148. }
  149. super.onPause();
  150. }
  151. @Override
  152. public void onBackPressed() {
  153. if (mChessClock.isStarted()) {
  154. showExitDialog();
  155. } else {
  156. super.onBackPressed();
  157. }
  158. }
  159. private AlertDialog createPausedDialog() {
  160. PausedDialog pausedDialog = new PausedDialog(this);
  161. pausedDialog
  162. .setOnDismissListener(new DialogInterface.OnDismissListener() {
  163. public void onDismiss(DialogInterface dialog) {
  164. if (mChessClock.isPaused()) {
  165. unPauseClock();
  166. }
  167. }
  168. });
  169. return pausedDialog;
  170. }
  171. private void showIntroDialogBox() {
  172. // Ask the user if they want to quit
  173. new AlertDialog.Builder(this)
  174. // .setIcon(android.R.drawable.ic_dialog_info)
  175. .setTitle(R.string.intro_title).setMessage(R.string.intro_text)
  176. .setNegativeButton(R.string.ok, null).show();
  177. mShowIntroDialog = false;
  178. }
  179. void restorePreferences() {
  180. SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
  181. mDuration = settings.getLong("duration", 5 * 60 * 1000L);
  182. mDelayMode = DelayMode.fromOrdinal(settings.getInt("delayMethod", 0));
  183. mDelayTime = settings.getLong("delay", 0L);
  184. mShakeEnabled = settings.getBoolean("shakeEnabled", true);
  185. mFlipEnabled = settings.getBoolean("flipEnabled", true);
  186. mSoundEnabled = settings.getBoolean("soundEnabled", true);
  187. mShowIntroDialog = settings.getBoolean("showIntroDialog", true);
  188. mTimePressureWarningEnabled = settings.getBoolean(
  189. "mTimePressureWarningEnabled", true);
  190. }
  191. void getPreferencesFromActivity() {
  192. // Fetch Preferences from Activity
  193. SharedPreferences prefs = PreferenceManager
  194. .getDefaultSharedPreferences(this);
  195. setSoundEnabled(prefs.getBoolean("soundEnabledPref", true));
  196. setTimePressureWarningEnabled(prefs.getBoolean(
  197. "timePressureWarningPref", true));
  198. mShakeEnabled = prefs.getBoolean("shakeToResetPref", true);
  199. mFlipEnabled = prefs.getBoolean("flipToPausePref", true);
  200. long duration = Long.parseLong(prefs.getString("durationPref", "5"));
  201. setDuration(duration * 60 * 1000);
  202. int delayMethodId = Integer.parseInt(prefs.getString("delayMethodPref",
  203. "0"));
  204. setDelayMode(DelayMode.fromOrdinal(delayMethodId));
  205. long delayTime = Long.parseLong(prefs.getString("delayTimePref", "1"));
  206. setDelayTime(delayTime * 1000);
  207. }
  208. void savePreferences() {
  209. SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
  210. SharedPreferences.Editor editor = settings.edit();
  211. editor.putLong("duration", mDuration);
  212. editor.putLong("delay", mDelayTime);
  213. editor.putInt("delayMethod", mDelayMode.ordinal());
  214. editor.putBoolean("shakeEnabled", mShakeEnabled);
  215. editor.putBoolean("flipEnabled", mFlipEnabled);
  216. editor.putBoolean("soundEnabled", mSoundEnabled);
  217. editor.putBoolean("timePressureWarningEnabled",
  218. mTimePressureWarningEnabled);
  219. editor.putBoolean("showIntroDialog", mShowIntroDialog);
  220. editor.commit();
  221. }
  222. void vibrate() {
  223. findViewById(R.id.mainLayout).performHapticFeedback(
  224. HapticFeedbackConstants.VIRTUAL_KEY);
  225. }
  226. void installShakeListener() {
  227. try {
  228. mShakeListener = new ShakeListener(this);
  229. } catch (UnsupportedOperationException e) {
  230. return;
  231. }
  232. mShakeListener.setOnShakeListener(new ShakeListener.OnShakeListener() {
  233. public void onShake() {
  234. if (mShakeEnabled && !mChessClock.isPaused()
  235. && !mChessClock.isReady()) {
  236. vibrate();
  237. resetClock();
  238. }
  239. }
  240. });
  241. }
  242. void installPitchFlipListener() {
  243. try {
  244. mPitchFlipListener = new PitchFlipListener(this);
  245. } catch (UnsupportedOperationException e) {
  246. return;
  247. }
  248. mPitchFlipListener
  249. .setOnPitchFlipListener(new PitchFlipListener.OnPitchFlipListener() {
  250. public void onPitchFlip(PitchFlipListener.State state) {
  251. if (mFlipEnabled && mChessClock.isStarted()) {
  252. if ((state == PitchFlipListener.State.UP)
  253. && mChessClock.isPaused()) {
  254. Log.i("Blitzn", "flip detected, unpausing");
  255. vibrate();
  256. unPauseClock();
  257. } else if ((state == PitchFlipListener.State.DOWN)
  258. && !mChessClock.isPaused()) {
  259. Log.i("Blitzn", "flip detected, pausing");
  260. vibrate();
  261. pauseClock(true);
  262. }
  263. }
  264. }
  265. });
  266. }
  267. void startClockTimer() {
  268. mTimerHandler.removeCallbacks(updateTimeTask);
  269. mTimerHandler.postDelayed(updateTimeTask, CLOCK_RESOLUTION);
  270. }
  271. void stopClockTimer() {
  272. mTimerHandler.removeCallbacks(updateTimeTask);
  273. }
  274. void deactivatePlayer(Player player) {
  275. if (mChessClock.isReady()) {
  276. startClockTimer();
  277. setKeepScreenOn(true);
  278. }
  279. mChessClock.deactivatePlayer(player);
  280. if (player == Player.ONE) {
  281. mPlayer2ClockButton.activate();
  282. mPlayer1ClockButton.deactivate();
  283. } else {
  284. mPlayer2ClockButton.deactivate();
  285. mPlayer1ClockButton.activate();
  286. }
  287. }
  288. void resetClock() {
  289. setKeepScreenOn(false);
  290. stopClockTimer();
  291. mChessClock.reset();
  292. mPlayer1ClockButton.reset();
  293. mPlayer2ClockButton.reset();
  294. }
  295. void pauseClock(boolean showDialog) {
  296. // There are two types of pauses:
  297. // a) Player initiated, where the player flips the phone over
  298. // or hits pause from the menu
  299. // b) Where the system pauses the clock because the clock is
  300. // no longer visible (hit the settings page or the home button)
  301. //
  302. // For a) we want to display a paused dialog; however, for b)
  303. // we do not.
  304. stopClockTimer();
  305. mChessClock.pause();
  306. if (showDialog) {
  307. mPausedDialog.show();
  308. }
  309. }
  310. void unPauseClock() {
  311. if (mPausedDialog.isShowing()) {
  312. mPausedDialog.dismiss();
  313. }
  314. mChessClock.unpause();
  315. startClockTimer();
  316. }
  317. void setKeepScreenOn(boolean keepScreenOn) {
  318. findViewById(R.id.mainLayout).setKeepScreenOn(keepScreenOn);
  319. }
  320. void stopClock() {
  321. setKeepScreenOn(false);
  322. stopClockTimer();
  323. mPlayer1ClockButton.stop();
  324. mPlayer2ClockButton.stop();
  325. }
  326. private Runnable updateTimeTask = new Runnable() {
  327. public void run() {
  328. mChessClock.tick();
  329. if (mChessClock.isRunningForPlayer(Player.ONE)) {
  330. mPlayer1ClockButton.tick();
  331. } else if (mChessClock.isRunningForPlayer(Player.TWO)) {
  332. mPlayer2ClockButton.tick();
  333. }
  334. // Reschedule the next tick
  335. long nextUpdate = SystemClock.uptimeMillis() + CLOCK_RESOLUTION;
  336. mTimerHandler.postAtTime(this, nextUpdate);
  337. }
  338. };
  339. private void setDuration(long duration) {
  340. long oldDuration = mDuration;
  341. mDuration = duration;
  342. mChessClock.setDuration(duration);
  343. if (oldDuration != mDuration) {
  344. resetClock();
  345. }
  346. }
  347. private void setDelayTime(long delayTime) {
  348. long oldDelayTime = mDelayTime;
  349. mDelayTime = delayTime;
  350. mChessClock.setDelayTime(delayTime);
  351. if (oldDelayTime != mDelayTime) {
  352. resetClock();
  353. }
  354. }
  355. private void setDelayMode(DelayMode delayMode) {
  356. DelayMode oldDelayMode = mDelayMode;
  357. mDelayMode = delayMode;
  358. mChessClock.setDelayMode(delayMode);
  359. if (oldDelayMode != mDelayMode) {
  360. resetClock();
  361. }
  362. }
  363. private void setSoundEnabled(boolean soundEnabled) {
  364. mSoundEnabled = soundEnabled;
  365. mPlayer1ClockButton.setIsSoundEnabled(soundEnabled);
  366. mPlayer2ClockButton.setIsSoundEnabled(soundEnabled);
  367. }
  368. private void setTimePressureWarningEnabled(boolean timePressure) {
  369. mTimePressureWarningEnabled = timePressure;
  370. mPlayer1ClockButton.setIsTimePressureWarningEnabled(timePressure);
  371. mPlayer2ClockButton.setIsTimePressureWarningEnabled(timePressure);
  372. }
  373. @Override
  374. public boolean onCreateOptionsMenu(Menu menu) {
  375. boolean result = super.onCreateOptionsMenu(menu);
  376. MenuInflater inflater = getMenuInflater();
  377. inflater.inflate(R.menu.main, menu);
  378. return result;
  379. }
  380. private void startPreferencesActivity() {
  381. Intent preferencesActivity = new Intent(getBaseContext(),
  382. Preferences.class);
  383. startActivity(preferencesActivity);
  384. }
  385. @Override
  386. public boolean onOptionsItemSelected(MenuItem item) {
  387. switch (item.getItemId()) {
  388. case R.id.resetMenu:
  389. resetClock();
  390. return true;
  391. case R.id.pauseMenu:
  392. pauseClock(true);
  393. return true;
  394. case R.id.preferencesMenu:
  395. startPreferencesActivity();
  396. return true;
  397. case R.id.aboutMenu:
  398. showAboutDialog();
  399. return true;
  400. }
  401. return super.onOptionsItemSelected(item);
  402. }
  403. @Override
  404. public boolean onPrepareOptionsMenu(Menu menu) {
  405. MenuItem pauseMenu = menu.findItem(R.id.pauseMenu);
  406. pauseMenu
  407. .setEnabled(mChessClock.isStarted() && !mChessClock.isPaused());
  408. MenuItem resetMenu = menu.findItem(R.id.resetMenu);
  409. resetMenu.setEnabled(!mChessClock.isReady() && !mChessClock.isPaused());
  410. return super.onPrepareOptionsMenu(menu);
  411. }
  412. @Override
  413. protected void onStop() {
  414. super.onStop();
  415. savePreferences();
  416. }
  417. private void showAboutDialog() {
  418. Dialog aboutDialog = new Dialog(this);
  419. aboutDialog.setContentView(R.layout.about_dialog);
  420. aboutDialog.setTitle("Blitzn v" + getVersionName());
  421. aboutDialog.show();
  422. }
  423. private String getVersionName() {
  424. try {
  425. PackageInfo manager = getPackageManager().getPackageInfo(
  426. getPackageName(), 0);
  427. return manager.versionName;
  428. } catch (NameNotFoundException e) {
  429. return "0.0";
  430. }
  431. }
  432. private void showExitDialog() {
  433. // Ask the user if they want to quit
  434. new AlertDialog.Builder(this)
  435. .setIcon(android.R.drawable.ic_dialog_alert)
  436. .setTitle(R.string.quit)
  437. .setMessage(R.string.leave_in_progress)
  438. .setPositiveButton(R.string.yes,
  439. new DialogInterface.OnClickListener() {
  440. public void onClick(DialogInterface dialog,
  441. int which) {
  442. // Stop the activity
  443. Blitzn.this.finish();
  444. }
  445. }).setNegativeButton(R.string.no, null).show();
  446. }
  447. }