Android Open Source - opensudoku Sudoku Play Activity






From Project

Back to project page opensudoku.

License

The source code is released under:

GNU General Public License

If you think the Android project opensudoku listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/* 
 * Copyright (C) 2009 Roman Masek/*from  w  w  w .  jav a 2 s .c om*/
 * 
 * This file is part of OpenSudoku.
 * 
 * OpenSudoku is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * OpenSudoku is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with OpenSudoku.  If not, see <http://www.gnu.org/licenses/>.
 * 
 */

package org.moire.opensudoku.gui;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.ComponentName;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.view.Display;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.TextView;
import org.moire.opensudoku.R;
import org.moire.opensudoku.db.SudokuDatabase;
import org.moire.opensudoku.game.SudokuGame;
import org.moire.opensudoku.game.SudokuGame.OnPuzzleSolvedListener;
import org.moire.opensudoku.gui.inputmethod.IMControlPanel;
import org.moire.opensudoku.gui.inputmethod.IMControlPanelStatePersister;
import org.moire.opensudoku.gui.inputmethod.IMNumpad;
import org.moire.opensudoku.gui.inputmethod.IMPopup;
import org.moire.opensudoku.gui.inputmethod.IMSingleNumber;
import org.moire.opensudoku.utils.AndroidUtils;

/*
 */
public class SudokuPlayActivity extends Activity {

  public static final String EXTRA_SUDOKU_ID = "sudoku_id";

  public static final int MENU_ITEM_RESTART = Menu.FIRST;
  public static final int MENU_ITEM_CLEAR_ALL_NOTES = Menu.FIRST + 1;
  public static final int MENU_ITEM_FILL_IN_NOTES = Menu.FIRST + 2;
  public static final int MENU_ITEM_UNDO = Menu.FIRST + 3;
  public static final int MENU_ITEM_HELP = Menu.FIRST + 4;
  public static final int MENU_ITEM_SETTINGS = Menu.FIRST + 5;

  public static final int MENU_ITEM_SET_CHECKPOINT = Menu.FIRST + 6;
  public static final int MENU_ITEM_UNDO_TO_CHECKPOINT = Menu.FIRST + 7;

  private static final int DIALOG_RESTART = 1;
  private static final int DIALOG_WELL_DONE = 2;
  private static final int DIALOG_CLEAR_NOTES = 3;
  private static final int DIALOG_UNDO_TO_CHECKPOINT = 4;

  private static final int REQUEST_SETTINGS = 1;

  private long mSudokuGameID;
  private SudokuGame mSudokuGame;


  private SudokuDatabase mDatabase;

  private Handler mGuiHandler;

  private ViewGroup mRootLayout;
  private SudokuBoardView mSudokuBoard;
  private TextView mTimeLabel;

  private IMControlPanel mIMControlPanel;
  private IMControlPanelStatePersister mIMControlPanelStatePersister;
  private IMPopup mIMPopup;
  private IMSingleNumber mIMSingleNumber;
  private IMNumpad mIMNumpad;

  private boolean mShowTime = true;
  private GameTimer mGameTimer;
  private GameTimeFormat mGameTimeFormatter = new GameTimeFormat();
  private boolean mFullScreen;
  private boolean mFillInNotesEnabled = false;

  private HintsQueue mHintsQueue;

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // go fullscreen for devices with QVGA screen (only way I found
    // how to fit UI on the screen)
    Display display = getWindowManager().getDefaultDisplay();
    if ((display.getWidth() == 240 || display.getWidth() == 320)
        && (display.getHeight() == 240 || display.getHeight() == 320)) {
      requestWindowFeature(Window.FEATURE_NO_TITLE);
      getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
          WindowManager.LayoutParams.FLAG_FULLSCREEN);
      mFullScreen = true;
    }

    // theme must be set before setContentView
    AndroidUtils.setThemeFromPreferences(this);

    setContentView(R.layout.sudoku_play);

    mRootLayout = (ViewGroup) findViewById(R.id.root_layout);
    mSudokuBoard = (SudokuBoardView) findViewById(R.id.sudoku_board);
    mTimeLabel = (TextView) findViewById(R.id.time_label);

    mDatabase = new SudokuDatabase(getApplicationContext());
    mHintsQueue = new HintsQueue(this);
    mGameTimer = new GameTimer();

    mGuiHandler = new Handler();

    // create sudoku game instance
    if (savedInstanceState == null) {
      // activity runs for the first time, read game from database
      mSudokuGameID = getIntent().getLongExtra(EXTRA_SUDOKU_ID, 0);
      mSudokuGame = mDatabase.getSudoku(mSudokuGameID);
    } else {
      // activity has been running before, restore its state
      mSudokuGame = new SudokuGame();
      mSudokuGame.restoreState(savedInstanceState);
      mGameTimer.restoreState(savedInstanceState);
    }

    if (mSudokuGame.getState() == SudokuGame.GAME_STATE_NOT_STARTED) {
      mSudokuGame.start();
    } else if (mSudokuGame.getState() == SudokuGame.GAME_STATE_PLAYING) {
      mSudokuGame.resume();
    }

    if (mSudokuGame.getState() == SudokuGame.GAME_STATE_COMPLETED) {
      mSudokuBoard.setReadOnly(true);
    }

    mSudokuBoard.setGame(mSudokuGame);
    mSudokuGame.setOnPuzzleSolvedListener(onSolvedListener);

    mHintsQueue.showOneTimeHint("welcome", R.string.welcome, R.string.first_run_hint);

    mIMControlPanel = (IMControlPanel) findViewById(R.id.input_methods);
    mIMControlPanel.initialize(mSudokuBoard, mSudokuGame, mHintsQueue);

    mIMControlPanelStatePersister = new IMControlPanelStatePersister(this);

    mIMPopup = mIMControlPanel.getInputMethod(IMControlPanel.INPUT_METHOD_POPUP);
    mIMSingleNumber = mIMControlPanel.getInputMethod(IMControlPanel.INPUT_METHOD_SINGLE_NUMBER);
    mIMNumpad = mIMControlPanel.getInputMethod(IMControlPanel.INPUT_METHOD_NUMPAD);
  }

  @Override
  protected void onResume() {
    super.onResume();

    // read game settings
    SharedPreferences gameSettings = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());

    int screenPadding = gameSettings.getInt("screen_border_size", 0);
    mRootLayout.setPadding(screenPadding, screenPadding, screenPadding, screenPadding);

    mFillInNotesEnabled = gameSettings.getBoolean("fill_in_notes_enabled", false);

    mSudokuBoard.setHighlightWrongVals(gameSettings.getBoolean("highlight_wrong_values", true));
    mSudokuBoard.setHighlightTouchedCell(gameSettings.getBoolean("highlight_touched_cell", true));

    mShowTime = gameSettings.getBoolean("show_time", true);
    if (mSudokuGame.getState() == SudokuGame.GAME_STATE_PLAYING) {
      mSudokuGame.resume();

      if (mShowTime) {
        mGameTimer.start();
      }
    }
    mTimeLabel.setVisibility(mFullScreen && mShowTime ? View.VISIBLE : View.GONE);

    mIMPopup.setEnabled(gameSettings.getBoolean("im_popup", true));
    mIMSingleNumber.setEnabled(gameSettings.getBoolean("im_single_number", true));
    mIMNumpad.setEnabled(gameSettings.getBoolean("im_numpad", true));
    mIMNumpad.setMoveCellSelectionOnPress(gameSettings.getBoolean("im_numpad_move_right", false));
    mIMPopup.setHighlightCompletedValues(gameSettings.getBoolean("highlight_completed_values", true));
    mIMPopup.setShowNumberTotals(gameSettings.getBoolean("show_number_totals", false));
    mIMSingleNumber.setHighlightCompletedValues(gameSettings.getBoolean("highlight_completed_values", true));
    mIMSingleNumber.setShowNumberTotals(gameSettings.getBoolean("show_number_totals", false));
    mIMNumpad.setHighlightCompletedValues(gameSettings.getBoolean("highlight_completed_values", true));
    mIMNumpad.setShowNumberTotals(gameSettings.getBoolean("show_number_totals", false));

    mIMControlPanel.activateFirstInputMethod(); // make sure that some input method is activated
    mIMControlPanelStatePersister.restoreState(mIMControlPanel);

    updateTime();
  }

  @Override
  public void onWindowFocusChanged(boolean hasFocus) {
    super.onWindowFocusChanged(hasFocus);

    if (hasFocus) {
      // FIXME: When activity is resumed, title isn't sometimes hidden properly (there is black 
      // empty space at the top of the screen). This is desperate workaround.
      if (mFullScreen) {
        mGuiHandler.postDelayed(new Runnable() {
          @Override
          public void run() {
            getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
            mRootLayout.requestLayout();
          }
        }, 1000);
      }

    }
  }

  @Override
  protected void onPause() {
    super.onPause();

    // we will save game to the database as we might not be able to get back
    mDatabase.updateSudoku(mSudokuGame);

    mGameTimer.stop();
    mIMControlPanel.pause();
    mIMControlPanelStatePersister.saveState(mIMControlPanel);
  }

  @Override
  protected void onDestroy() {
    super.onDestroy();

    mDatabase.close();
  }

  @Override
  protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);

    mGameTimer.stop();

    if (mSudokuGame.getState() == SudokuGame.GAME_STATE_PLAYING) {
      mSudokuGame.pause();
    }

    mSudokuGame.saveState(outState);
    mGameTimer.saveState(outState);
  }

  @Override
  public boolean onCreateOptionsMenu(Menu menu) {
    super.onCreateOptionsMenu(menu);

    menu.add(0, MENU_ITEM_UNDO, 0, R.string.undo)
        .setShortcut('1', 'u')
        .setIcon(android.R.drawable.ic_menu_revert);

    menu.add(0, MENU_ITEM_RESTART, 5, R.string.restart)
        .setShortcut('7', 'r')
        .setIcon(android.R.drawable.ic_menu_rotate);

    menu.add(0, MENU_ITEM_CLEAR_ALL_NOTES, 2, R.string.clear_all_notes)
        .setShortcut('3', 'a')
        .setIcon(android.R.drawable.ic_menu_delete);

    if (mFillInNotesEnabled) {
      menu.add(0, MENU_ITEM_FILL_IN_NOTES, 1, R.string.fill_in_notes)
          .setIcon(android.R.drawable.ic_menu_edit);
    }

        menu.add(0, MENU_ITEM_SET_CHECKPOINT, 3, R.string.set_checkpoint);
        menu.add(0, MENU_ITEM_UNDO_TO_CHECKPOINT, 4, R.string.undo_to_checkpoint);

    menu.add(0, MENU_ITEM_HELP, 7, R.string.help)
        .setShortcut('0', 'h')
        .setIcon(android.R.drawable.ic_menu_help);

    menu.add(0, MENU_ITEM_SETTINGS, 6, R.string.settings)
        .setShortcut('9', 's')
        .setIcon(android.R.drawable.ic_menu_preferences);

    // Generate any additional actions that can be performed on the
    // overall list.  In a normal install, there are no additional
    // actions found here, but this allows other applications to extend
    // our menu with their own actions.
    Intent intent = new Intent(null, getIntent().getData());
    intent.addCategory(Intent.CATEGORY_ALTERNATIVE);
    menu.addIntentOptions(Menu.CATEGORY_ALTERNATIVE, 0, 0,
        new ComponentName(this, SudokuPlayActivity.class), null, intent, 0, null);

    return true;
  }

  @Override
  public boolean onPrepareOptionsMenu(Menu menu) {
    super.onPrepareOptionsMenu(menu);

    if (mSudokuGame.getState() == SudokuGame.GAME_STATE_PLAYING) {
      menu.findItem(MENU_ITEM_CLEAR_ALL_NOTES).setEnabled(true);
      if (mFillInNotesEnabled) {
        menu.findItem(MENU_ITEM_FILL_IN_NOTES).setEnabled(true);
      }
      menu.findItem(MENU_ITEM_UNDO).setEnabled(mSudokuGame.hasSomethingToUndo());
      menu.findItem(MENU_ITEM_UNDO_TO_CHECKPOINT).setEnabled(mSudokuGame.hasUndoCheckpoint());
    } else {
      menu.findItem(MENU_ITEM_CLEAR_ALL_NOTES).setEnabled(false);
      if (mFillInNotesEnabled) {
        menu.findItem(MENU_ITEM_FILL_IN_NOTES).setEnabled(false);
      }
      menu.findItem(MENU_ITEM_UNDO).setEnabled(false);
      menu.findItem(MENU_ITEM_UNDO_TO_CHECKPOINT).setEnabled(false);
    }

    return true;
  }

  @Override
  public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
      case MENU_ITEM_RESTART:
        showDialog(DIALOG_RESTART);
        return true;
      case MENU_ITEM_CLEAR_ALL_NOTES:
        showDialog(DIALOG_CLEAR_NOTES);
        return true;
      case MENU_ITEM_FILL_IN_NOTES:
        mSudokuGame.fillInNotes();
        return true;
      case MENU_ITEM_UNDO:
        mSudokuGame.undo();
        return true;
      case MENU_ITEM_SETTINGS:
        Intent i = new Intent();
        i.setClass(this, GameSettingsActivity.class);
        startActivityForResult(i, REQUEST_SETTINGS);
        return true;
      case MENU_ITEM_HELP:
        mHintsQueue.showHint(R.string.help, R.string.help_text);
        return true;
      case MENU_ITEM_SET_CHECKPOINT:
        mSudokuGame.setUndoCheckpoint();
        return true;
      case MENU_ITEM_UNDO_TO_CHECKPOINT:
        showDialog(DIALOG_UNDO_TO_CHECKPOINT);
        return true;

    }
    return super.onOptionsItemSelected(item);
  }

  @Override
  protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    switch (requestCode) {
      case REQUEST_SETTINGS:
        restartActivity();
        break;
    }
  }

  /**
   * Restarts whole activity.
   */
  private void restartActivity() {
    startActivity(getIntent());
    finish();
  }

  @Override
  protected Dialog onCreateDialog(int id) {
    switch (id) {
      case DIALOG_WELL_DONE:
        return new AlertDialog.Builder(this)
            .setIcon(android.R.drawable.ic_dialog_info)
            .setTitle(R.string.well_done)
            .setMessage(getString(R.string.congrats, mGameTimeFormatter.format(mSudokuGame.getTime())))
            .setPositiveButton(android.R.string.ok, null)
            .create();
      case DIALOG_RESTART:
        return new AlertDialog.Builder(this)
            .setIcon(android.R.drawable.ic_menu_rotate)
            .setTitle(R.string.app_name)
            .setMessage(R.string.restart_confirm)
            .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
              public void onClick(DialogInterface dialog, int whichButton) {
                // Restart game
                mSudokuGame.reset();
                mSudokuGame.start();
                mSudokuBoard.setReadOnly(false);
                if (mShowTime) {
                  mGameTimer.start();
                }
              }
            })
            .setNegativeButton(android.R.string.no, null)
            .create();
      case DIALOG_CLEAR_NOTES:
        return new AlertDialog.Builder(this)
            .setIcon(android.R.drawable.ic_menu_delete)
            .setTitle(R.string.app_name)
            .setMessage(R.string.clear_all_notes_confirm)
            .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
              public void onClick(DialogInterface dialog, int whichButton) {
                mSudokuGame.clearAllNotes();
              }
            })
            .setNegativeButton(android.R.string.no, null)
            .create();
      case DIALOG_UNDO_TO_CHECKPOINT:
        return new AlertDialog.Builder(this)
            .setIcon(android.R.drawable.ic_menu_delete)
            .setTitle(R.string.app_name)
            .setMessage(R.string.undo_to_checkpoint_confirm)
            .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
              public void onClick(DialogInterface dialog, int whichButton) {
                mSudokuGame.undoToCheckpoint();
              }
            })
            .setNegativeButton(android.R.string.no, null)
            .create();

    }
    return null;
  }

  /**
   * Occurs when puzzle is solved.
   */
  private OnPuzzleSolvedListener onSolvedListener = new OnPuzzleSolvedListener() {

    @Override
    public void onPuzzleSolved() {
      mSudokuBoard.setReadOnly(true);
      showDialog(DIALOG_WELL_DONE);
    }

  };

  /**
   * Update the time of game-play.
   */
  void updateTime() {
    if (mShowTime) {
      setTitle(mGameTimeFormatter.format(mSudokuGame.getTime()));
      mTimeLabel.setText(mGameTimeFormatter.format(mSudokuGame.getTime()));
    } else {
      setTitle(R.string.app_name);
    }

  }

  // This class implements the game clock.  All it does is update the
  // status each tick.
  private final class GameTimer extends Timer {

    GameTimer() {
      super(1000);
    }

    @Override
    protected boolean step(int count, long time) {
      updateTime();

      // Run until explicitly stopped.
      return false;
    }

  }
}




Java Source Code List

org.moire.opensudoku.db.DatabaseHelper.java
org.moire.opensudoku.db.FolderColumns.java
org.moire.opensudoku.db.SudokuColumns.java
org.moire.opensudoku.db.SudokuDatabase.java
org.moire.opensudoku.db.SudokuImportParams.java
org.moire.opensudoku.db.SudokuInvalidFormatException.java
org.moire.opensudoku.game.CellCollection.java
org.moire.opensudoku.game.CellGroup.java
org.moire.opensudoku.game.CellNote.java
org.moire.opensudoku.game.Cell.java
org.moire.opensudoku.game.FolderInfo.java
org.moire.opensudoku.game.SudokuGame.java
org.moire.opensudoku.game.command.AbstractCellCommand.java
org.moire.opensudoku.game.command.AbstractCommand.java
org.moire.opensudoku.game.command.ClearAllNotesCommand.java
org.moire.opensudoku.game.command.CommandStack.java
org.moire.opensudoku.game.command.EditCellNoteCommand.java
org.moire.opensudoku.game.command.FillInNotesCommand.java
org.moire.opensudoku.game.command.SetCellValueCommand.java
org.moire.opensudoku.gui.Changelog.java
org.moire.opensudoku.gui.FileImportActivity.java
org.moire.opensudoku.gui.FileListActivity.java
org.moire.opensudoku.gui.FolderDetailLoader.java
org.moire.opensudoku.gui.FolderListActivity.java
org.moire.opensudoku.gui.GameSettingsActivity.java
org.moire.opensudoku.gui.GameTimeFormat.java
org.moire.opensudoku.gui.HintsQueue.java
org.moire.opensudoku.gui.ImportSudokuActivity.java
org.moire.opensudoku.gui.SeekBarPreference.java
org.moire.opensudoku.gui.SudokuBoardView.java
org.moire.opensudoku.gui.SudokuEditActivity.java
org.moire.opensudoku.gui.SudokuExportActivity.java
org.moire.opensudoku.gui.SudokuImportActivity.java
org.moire.opensudoku.gui.SudokuListActivity.java
org.moire.opensudoku.gui.SudokuListFilter.java
org.moire.opensudoku.gui.SudokuPlayActivity.java
org.moire.opensudoku.gui.Timer.java
org.moire.opensudoku.gui.exporting.FileExportTaskParams.java
org.moire.opensudoku.gui.exporting.FileExportTaskResult.java
org.moire.opensudoku.gui.exporting.FileExportTask.java
org.moire.opensudoku.gui.importing.AbstractImportTask.java
org.moire.opensudoku.gui.importing.ExtrasImportTask.java
org.moire.opensudoku.gui.importing.OpenSudokuImportTask.java
org.moire.opensudoku.gui.importing.SdmImportTask.java
org.moire.opensudoku.gui.inputmethod.IMControlPanelStatePersister.java
org.moire.opensudoku.gui.inputmethod.IMControlPanel.java
org.moire.opensudoku.gui.inputmethod.IMNumpad.java
org.moire.opensudoku.gui.inputmethod.IMPopupDialog.java
org.moire.opensudoku.gui.inputmethod.IMPopup.java
org.moire.opensudoku.gui.inputmethod.IMSingleNumber.java
org.moire.opensudoku.gui.inputmethod.InputMethod.java
org.moire.opensudoku.utils.AndroidUtils.java
org.moire.opensudoku.utils.Const.java
org.moire.opensudoku.utils.StringUtils.java