com.ezeeideas.wordjam.BaseGameActivity.java Source code

Java tutorial

Introduction

Here is the source code for com.ezeeideas.wordjam.BaseGameActivity.java

Source

/******************************************************************************
*
* The MIT License (MIT)
*
* Copyright (c) 2013 - Present, Gaurav Jain
*
* (@gauravdelphinus, github.com/gauravdelphinus)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*******************************************************************************/
package com.ezeeideas.wordjam;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashSet;
import java.util.Set;

import com.ezeeideas.wordjam.GameDialog.GameDialogListener;
import com.ezeeideas.wordjam.db.GamesDbAdapter;

import android.app.Dialog;
import android.app.ProgressDialog;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.FragmentActivity;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.TableRow;
import android.widget.TextView;
import android.widget.Toast;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.drawable.Drawable;

import com.google.ads.*;
import com.google.analytics.tracking.android.EasyTracker;

/**
 * BaseGameActivity - the base class for ALL Game Activities
 */
/**
 * Starting a Game
 * 
 * A: A Game can be started by creating an Intent object and pass the corresponding *GameActivity with these intent values:
 * 
 * 1) KEY_GAME_NUM must be set to the game number (if existing), or -1 if new game
 * 2) KEY_IS_PLAY must be true for Play and false for Practice
 * 3) KEY_PLAY_TYPE must be set to one of Constants.PLAY_TYPE_*
 * 4) KEY_LEVEL must be set to the level
 * For Practice type, you must pass these additional values:
 * 5) KEY_PRACTICE_TYPE must be set to one of Constants.PRACTICE_TYPE_*
 * 6) PRACTICE_GAMES_COMPLETED must be set to total no. of practice games completed so far in the session (0 for first game)
 * 7) PRACTICE_TIME_SPENT must be set to the total time spent in practice session so far (0 for first game)
 * 8) PRACTICE_HINTS_USED must be set to the total no. of hints used in the practice session so far (0 for first game)
 * 9) PRACTICE_POINTS_EARNED must be set to the total no. of points earned in the practice session so far (0 for first game)
 *
 * B: Game Restored after Orientation Change.  This *could* be a game that is new (i.e., not stored in DB)
 * 
 * C: Game Restored from the DB.  This *cannot* be a new game.  All values must be restored from the DB.
 *
 */
public abstract class BaseGameActivity extends FragmentActivity
        implements OnClickListener, GameDialogListener, AdListener {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(getLayoutID());

        //Initialize Everything!
        initAll(savedInstanceState);

        honorTheme();
    }

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

        if (mSetupGameAsyncTask != null && mSetupGameAsyncTask.getStatus() != AsyncTask.Status.FINISHED) {
            mSetupGameAsyncTask.cancel(true);
            mSetupGameAsyncTask = null;
        }
    }

    @Override
    public void onStart() {
        super.onStart();
        EasyTracker.getInstance(this).activityStart(this);
    }

    @Override
    public void onStop() {
        super.onStop();
        EasyTracker.getInstance(this).activityStop(this);
    }

    @Override
    public void onPause() {
        super.onPause();
    }

    @Override
    public void onResume() {
        super.onResume();
    }

    @Override
    public void onBackPressed() {
        Analytics.trackEvent(this, Analytics.getAnalyticsCategoryFromGame(mGameType),
                Analytics.ANALYTICS_ACTION_BUTTON_PRESS, Analytics.ANALYTICS_LABEL_BACK_BUTTON, null);

        onStopButtonClicked();
    }

    private void honorTheme() {
        //honor theme for common elements
        LinearLayout mainLayout = (LinearLayout) findViewById(R.id.game_main_layout);
        mainLayout.setBackgroundColor(Themes.getColorFor(Themes.COLOR_BACKGROUND));

        View ruleHeader = (View) findViewById(R.id.rule_header);
        ruleHeader.setBackgroundColor(Themes.getColorFor(Themes.COLOR_LINE_HEADING));

        View ruleFooter = (View) findViewById(R.id.rule_footer);
        ruleFooter.setBackgroundColor(Themes.getColorFor(Themes.COLOR_LINE_HEADING));

        View ruleFooter2 = (View) findViewById(R.id.rule_footer2);
        if (ruleFooter2 != null) {
            ruleFooter2.setBackgroundColor(Themes.getColorFor(Themes.COLOR_LINE_HEADING));
        }

        //honor theme for game specfic elements
        doHonorTheme();
    }

    /**
     * The Uber Initialization routine.  Call this, and only this, from all games
     * to ensure correct order of initialization.
     */
    private void initAll(Bundle savedInstanceState) {
        commonInit();

        doInitializeGame();

        if (savedInstanceState != null && !savedInstanceState.isEmpty()) {
            restoreSavedInstance(savedInstanceState);
        } else if (getIntent().getExtras() != null) {
            initializeFromExtras(getIntent().getExtras());
        }

        setupViews();

        setupInterstitialAds();

        setupGame();

        setupConnectors();
    }

    /**
     * Common Initialization Routine.
     */
    private void commonInit() {
        //open databases
        mApp = (WordJamApp) getApplication();

        mWordDbAdapter = mApp.getWordDbAdapter();
        mGamesDbAdapter = mApp.getGamesDbAdapter();

        //TODO: Fix this
        //mLevelPoints = mGamesDbAdapter.getEarnedPoints(mPlayType, mLevel);

        mGameTimer = new GameTimer();
        mGameTimerHandler = new Handler();
    }

    /**
     * Restore the instance of the game from the saved Bundle.  This is usually
     * when the device screen was rotated while in the middle of the game.
     * 
     * Note: The game may or may not have been stored in the DB.
     * 
     * @param savedInstanceState The bundle to restore state from
     */
    private void restoreSavedInstance(Bundle savedInstanceState) {
        if (savedInstanceState != null && !savedInstanceState.isEmpty()) {
            mIsPlay = savedInstanceState.getBoolean(Constants.KEY_IS_PLAY);
            mPlayType = savedInstanceState.getInt(Constants.KEY_PLAY_TYPE);
            mGameNumber = savedInstanceState.getInt(Constants.KEY_GAME_NUM);
            mIsNewGame = savedInstanceState.getBoolean(Constants.KEY_IS_NEW_GAME);

            if (mIsPlay) {
                mLevel = savedInstanceState.getInt(Constants.KEY_LEVEL);
                mNumGamesInLevel = savedInstanceState.getInt(Constants.KEY_LEVEL_NUM_GAMES);

                mLevelPoints = savedInstanceState.getInt(Constants.KEY_LEVEL_POINTS);
                mLevelState = savedInstanceState.getInt(Constants.KEY_LEVEL_STATE);
                mLevelGamesRemaining = savedInstanceState.getInt(Constants.KEY_LEVEL_GAMES_REMAINING);
                mLevelHintsRemaining = savedInstanceState.getInt(Constants.KEY_LEVEL_HINTS_REMAINING);
                mLevelHintsEarned = savedInstanceState.getInt(Constants.KEY_LEVEL_HINTS_EARNED);
            } else {
                mPracticeType = savedInstanceState.getInt(Constants.KEY_PRACTICE_TYPE);
                mPracticeGamesCompleted = savedInstanceState.getInt(Constants.PRACTICE_GAMES_COMPLETED);
                mPracticeTimeSpent = savedInstanceState.getInt(Constants.PRACTICE_TIME_SPENT);
                mPracticeHintsUsed = savedInstanceState.getInt(Constants.PRACTICE_HINTS_USED);
                mPracticePointsEarned = savedInstanceState.getInt(Constants.PRACTICE_POINTS_EARNED);
            }

            mGameState = savedInstanceState.getInt(Constants.KEY_GAME_STATE);
            mGameHintsRemaining = savedInstanceState.getInt(Constants.KEY_GAME_HINTS_REMAINING);
            mGameHintsUsed = GameUtils.getPointChart(mPlayType, mIsPlay, mLevel,
                    mPracticeType)[GameUtils.GAME_HINTS_TOTAL][mGameType] - mGameHintsRemaining;
            mGamePoints = savedInstanceState.getInt(Constants.KEY_GAME_POINTS);
            mGameTime = savedInstanceState.getLong(Constants.KEY_GAME_DURATION);
            mGameData = savedInstanceState.getString(Constants.KEY_GAME_DATA);
        }
    }

    /**
     * Initialize the game from the bundle extras.  Note that the extras may be sent in these cases:
     * 
     * 1) A new game was started by the user (in this case, KEY_GAME_NUM is passed as -1 in case of Play, but actual no. incase of Practice)
     * 2) An existing game was started/resumed by the user (in this case, KEY_GAME_NUM is the actual game number)
     * 
     * @param extras The bundle extras to initialize from
     */
    private void initializeFromExtras(Bundle extras) {
        if (extras != null) {
            //Get the mandatory fields
            mIsPlay = extras.getBoolean(Constants.KEY_IS_PLAY);
            mPlayType = extras.getInt(Constants.KEY_PLAY_TYPE);
            mGameNumber = extras.getInt(Constants.KEY_GAME_NUM);
            mIsNewGame = false;

            //Not get the play or practice related values
            if (mIsPlay) {
                mLevel = extras.getInt(Constants.KEY_LEVEL);
                mNumGamesInLevel = mGamesDbAdapter.getNumGames(mPlayType, mLevel);
                if (mGameNumber == -1) {
                    mGameNumber = mNumGamesInLevel + 1;
                    mIsNewGame = true;
                    mNumGamesInLevel++;
                }

                mLevelPoints = mGamesDbAdapter.getEarnedPoints(mPlayType, mLevel);
                mLevelState = mGamesDbAdapter.getLevelState(mPlayType, mLevel);
                mLevelGamesRemaining = mGamesDbAdapter.getNumGamesRemaining(mPlayType, mLevel);
                mLevelHintsRemaining = mGamesDbAdapter.getNumHintsRemaining(mPlayType, mLevel);
                mLevelHintsEarned = mGamesDbAdapter.getNumHintsEarned(mPlayType, mLevel);
            } else {
                mIsNewGame = true;
                if (mGameNumber == -1) {
                    mGameNumber = 1;
                }

                mPracticeType = extras.getInt(Constants.KEY_PRACTICE_TYPE);
                mPracticeGamesCompleted = extras.getInt(Constants.PRACTICE_GAMES_COMPLETED);
                mPracticeTimeSpent = extras.getInt(Constants.PRACTICE_TIME_SPENT);
                mPracticeHintsUsed = extras.getInt(Constants.PRACTICE_HINTS_USED);
                mPracticePointsEarned = extras.getInt(Constants.PRACTICE_POINTS_EARNED);
            }
        }

        if (mIsNewGame) {
            mGameState = Constants.GAME_STATE_NOT_STARTED;
            Utils.Log("mGameHintsRemaining, mPlayType = " + mPlayType + ", mIsPlay = " + mIsPlay + ", mLevel = "
                    + mLevel + ", mPracticeType = " + mPracticeType + ", mGameType = " + mGameType);
            mGameHintsRemaining = GameUtils.getPointChart(mPlayType, mIsPlay, mLevel,
                    mPracticeType)[GameUtils.GAME_HINTS_TOTAL][mGameType];
            Utils.Log("initializing mGameHintsRemaining to " + mGameHintsRemaining);
            mGameHintsUsed = 0;
            mGamePoints = 0;
            mGameTime = 0;
            mGameData = null;
        } else {
            //always for a Play game, as Practice is always NEW
            GamesDbAdapter.GameInfo gameInfo = mGamesDbAdapter.getGameInformation(mPlayType, mLevel, mGameNumber);
            mGameState = gameInfo.gameShortInfo.gameState;
            mGameHintsRemaining = gameInfo.gameLongInfo.gameHintsRemaining;
            mGameHintsUsed = GameUtils.getPointChart(mPlayType, mIsPlay, mLevel,
                    mPracticeType)[GameUtils.GAME_HINTS_TOTAL][mGameType] - mGameHintsRemaining;
            mGamePoints = gameInfo.gameShortInfo.gamePointsEarned;
            mGameTime = gameInfo.gameLongInfo.gameDuration;
            mGameData = gameInfo.gameLongInfo.gameData;
        }

        //game hints cannot be larger than the total level hints remaining, in case of Play
        if (mIsPlay) {
            mGameHintsRemaining = Math.min(mGameHintsRemaining, mLevelHintsRemaining);
        }
    }

    /**
     * Print various key game attributes - for debugging purposes only.
     */
    private void print() {
        Utils.Log("####################################################");
        Utils.Log("mGameNumber = " + mGameNumber);
        Utils.Log("mGameType = " + mGameType);
        Utils.Log("mGameState = " + mGameState);
        Utils.Log("mIsPlay = " + mIsPlay);
        Utils.Log("mPlayType = " + mPlayType);
        Utils.Log("mIsNewGame = " + mIsNewGame);
        Utils.Log("mLevel = " + mLevel);
        Utils.Log("mGameData = [" + mGameData + "]");
        Utils.Log("mGameTime = " + mGameTime);
        Utils.Log("####################################################");
    }

    /**
     * Save the instance state of the current game to the bundle.  This is usually called when the 
     * user rotates the device screen while playing the game.  It can be called at any time the system
     * decides to "kill" the activity without user interaction where the activity is expected to
     * "restore" itself at some later point.
     */
    @Override
    protected void onSaveInstanceState(Bundle savedInstanceState) {
        super.onSaveInstanceState(savedInstanceState);

        //cancel the async task, if it's running
        if (mSetupGameAsyncTask != null && mSetupGameAsyncTask.getStatus() != AsyncTask.Status.FINISHED) {
            mSetupGameAsyncTask.cancel(true);

            mGameData = null;
        }

        savedInstanceState.putBoolean(Constants.KEY_IS_PLAY, mIsPlay);
        savedInstanceState.putBoolean(Constants.KEY_IS_NEW_GAME, mIsNewGame);
        savedInstanceState.putInt(Constants.KEY_GAME_NUM, mGameNumber);
        savedInstanceState.putInt(Constants.KEY_PLAY_TYPE, mPlayType);

        savedInstanceState.putInt(Constants.KEY_GAME_TYPE, mGameType);
        savedInstanceState.putInt(Constants.KEY_GAME_POINTS, mGamePoints);
        savedInstanceState.putInt(Constants.KEY_GAME_STATE, mGameState);
        savedInstanceState.putInt(Constants.KEY_GAME_HINTS_REMAINING, mGameHintsRemaining);
        savedInstanceState.putLong(Constants.KEY_GAME_DURATION, mGameTime);
        savedInstanceState.putLong(Constants.KEY_GAME_LAST_PLAYED, System.currentTimeMillis());

        if (mGameData != null) {
            //the game setup (via AsyncTask) already happened once, so generate current game state and save
            savedInstanceState.putString(Constants.KEY_GAME_DATA, generateGameData());
        } else {
            //the game setup never happened, so set game data to null so it is setup the next time it is restored
            savedInstanceState.putString(Constants.KEY_GAME_DATA, null);
        }

        if (mIsPlay) {
            savedInstanceState.putInt(Constants.KEY_LEVEL, mLevel);
            savedInstanceState.putInt(Constants.KEY_LEVEL_STATE, mLevelState);
            savedInstanceState.putInt(Constants.KEY_LEVEL_GAMES_REMAINING, mLevelGamesRemaining);
            savedInstanceState.putInt(Constants.KEY_LEVEL_HINTS_REMAINING, mLevelHintsRemaining);
            savedInstanceState.putInt(Constants.KEY_LEVEL_HINTS_EARNED, mLevelHintsEarned);
            savedInstanceState.putInt(Constants.KEY_LEVEL_NUM_GAMES, mNumGamesInLevel);
        } else {
            savedInstanceState.putInt(Constants.KEY_PRACTICE_TYPE, mPracticeType);
            savedInstanceState.putInt(Constants.PRACTICE_GAMES_COMPLETED, mPracticeGamesCompleted);
            savedInstanceState.putInt(Constants.PRACTICE_TIME_SPENT, mPracticeTimeSpent);
            savedInstanceState.putInt(Constants.PRACTICE_HINTS_USED, mPracticeHintsUsed);
            savedInstanceState.putInt(Constants.PRACTICE_POINTS_EARNED, mPracticePointsEarned);
        }
    }

    private void setupConnectors() {

    }

    /**
     * Set up the game data, UI, etc. depending on whether it's a new game or a
     * restored game.
     */
    private void setupGame() {
        if (mGameData != null) {
            //restore an existing game
            doRestoreGame();
            loadBannerAd();
            if (mGameState == Constants.GAME_STATE_IN_PROGRESS) {
                startTimer();
            }
            refreshViews();
            return;
        }

        //start a new game!
        mSetupGameAsyncTask = new SetupGameAsyncTask(this);
        mSetupGameAsyncTask.execute((Void[]) null);
    }

    /**
     * The Game was successfully completed.  Do the points updates, etc. and then
     * congratulate the user on completing the game!
     * 
     * This function is called from the actual game class that knows when the game
     * is completed.
     */
    protected void onGameCompleted() {
        Utils.Log("onGameCompleted, this is " + this.getClass());
        stopTimer();

        //do the math for the points/hints
        mGameState = Constants.GAME_STATE_COMPLETED;

        mGamePoints += GameUtils.getPointChart(mPlayType, mIsPlay, mLevel,
                mPracticeType)[GameUtils.GAME_POINTS_ON_COMPLETION][mGameType];
        mLevelPoints += GameUtils.getPointChart(mPlayType, mIsPlay, mLevel,
                mPracticeType)[GameUtils.GAME_POINTS_ON_COMPLETION][mGameType];
        mPracticePointsEarned += GameUtils.getPointChart(mPlayType, mIsPlay, mLevel,
                mPracticeType)[GameUtils.GAME_POINTS_ON_COMPLETION][mGameType];

        if (mGameTime < 1000 * GameUtils.getPointChart(mPlayType, mIsPlay, mLevel,
                mPracticeType)[GameUtils.GAME_FAST_COMPLETION_TIME][mGameType]) {
            mGamePoints += GameUtils.getPointChart(mPlayType, mIsPlay, mLevel,
                    mPracticeType)[GameUtils.GAME_BONUS_POINTS_ON_FAST_COMPLETION][mGameType];
            mLevelPoints += GameUtils.getPointChart(mPlayType, mIsPlay, mLevel,
                    mPracticeType)[GameUtils.GAME_BONUS_POINTS_ON_FAST_COMPLETION][mGameType];
            mPracticePointsEarned += GameUtils.getPointChart(mPlayType, mIsPlay, mLevel,
                    mPracticeType)[GameUtils.GAME_BONUS_POINTS_ON_FAST_COMPLETION][mGameType];
        }

        mLevelGamesRemaining--;
        mPracticeGamesCompleted++;

        mLevelHintsRemaining += GameUtils.getPointChart(mPlayType, mIsPlay, mLevel,
                mPracticeType)[GameUtils.GAME_HINTS_ON_COMPLETION][mGameType];
        mLevelHintsEarned += GameUtils.getPointChart(mPlayType, mIsPlay, mLevel,
                mPracticeType)[GameUtils.GAME_HINTS_ON_COMPLETION][mGameType];

        if (mIsPlay) {
            //save to the DB
            saveCurrentGame();
        }

        refreshViews();

        Utils.Log("showing the dialog");
        //show the dialog      
        GameCompletedDialog dialog = new GameCompletedDialog(this, mGamePoints);
        dialog.setCanceledOnTouchOutside(true);
        dialog.show();
        Utils.Log("after dialog.show");
    }

    /**
     * The Game was unsuccessfully completed (failed).  Do the points updates and then
     * alert the user about the fact that he failed the game!
     * 
     * Called from the actual game class that knows exactly when the user failed the game.
     */
    protected void onGameFailed() {
        stopTimer();

        //do the math for the points/hints
        mGameState = Constants.GAME_STATE_FAILED;
        mGamePoints += GameUtils.getPointChart(mPlayType, mIsPlay, mLevel,
                mPracticeType)[GameUtils.GAME_POINTS_ON_LOSING_GAME][mGameType];
        mLevelPoints += GameUtils.getPointChart(mPlayType, mIsPlay, mLevel,
                mPracticeType)[GameUtils.GAME_POINTS_ON_LOSING_GAME][mGameType];
        mPracticePointsEarned += GameUtils.getPointChart(mPlayType, mIsPlay, mLevel,
                mPracticeType)[GameUtils.GAME_POINTS_ON_LOSING_GAME][mGameType];

        if (mIsPlay) {
            //save to the DB
            saveCurrentGame();
        }

        refreshViews();

        //show the dialog
        GameFailedDialog dialog = new GameFailedDialog(this);
        dialog.show();
    }

    /**
     * Show a little point animation that makes the user like to earn more points!
     */
    private void showPointsBubble() {

    }

    /**
     * A correct Word or Letter was chosen, not leading to game completion.
     * 
     * Called from the game class that knows when the right word or letter was selected.
     */
    protected void onRightWordOrLetterFound() {
        Utils.Log("onRightWordOrLetterFound, mGameType = " + mGameType + ", mLevel = " + mLevel);
        //do the math for the points/hints
        mGamePoints += GameUtils.getPointChart(mPlayType, mIsPlay, mLevel,
                mPracticeType)[GameUtils.GAME_POINTS_ON_CORRECT_WORD_OR_LETTER][mGameType];
        mLevelPoints += GameUtils.getPointChart(mPlayType, mIsPlay, mLevel,
                mPracticeType)[GameUtils.GAME_POINTS_ON_CORRECT_WORD_OR_LETTER][mGameType];
        mPracticePointsEarned += GameUtils.getPointChart(mPlayType, mIsPlay, mLevel,
                mPracticeType)[GameUtils.GAME_POINTS_ON_CORRECT_WORD_OR_LETTER][mGameType];

        refreshViews();

        showPointsBubble();
    }

    /**
     * A wrong Word or Letter was chosen, not leading to game completion.
     * 
     * Called from the game class that knows when the right word or letter wasn't successfuly found.
     */
    protected void onWrongWordOrLetterFound() {
        Utils.Log("onWrongWordOrLetterFound");
        //do the math for the points/hints
        mGamePoints += GameUtils.getPointChart(mPlayType, mIsPlay, mLevel,
                mPracticeType)[GameUtils.GAME_POINTS_ON_INCORRECT_WORD_OR_LETTER][mGameType];
        mLevelPoints += GameUtils.getPointChart(mPlayType, mIsPlay, mLevel,
                mPracticeType)[GameUtils.GAME_POINTS_ON_INCORRECT_WORD_OR_LETTER][mGameType];
        mPracticePointsEarned += GameUtils.getPointChart(mPlayType, mIsPlay, mLevel,
                mPracticeType)[GameUtils.GAME_POINTS_ON_INCORRECT_WORD_OR_LETTER][mGameType];

        refreshViews();

        showPointsBubble();
    }

    /**
     * Call this when you're ready to handle a game completion
     */
    private void handleGameCompleted() {
        Utils.Log(LOG_TAG + "handleGameCompleted");
        doHandleGivenUp();

        refreshViews();

        //now move on to the next game directly            
        startNextGame(true);
    }

    private boolean checkLevelCompleted() {
        int levelGamesRemaining = mGamesDbAdapter.getNumGamesRemaining(mPlayType, mLevel);
        if (mGameState == Constants.GAME_STATE_COMPLETED) {
            levelGamesRemaining--;
        }

        if (levelGamesRemaining <= 0
                && mLevelPoints >= GameUtils.sLevelValues[mLevel - 1][GameUtils.LEVEL_TOTAL_POINTS]) {
            //Completed the level!
            return true;
        }

        return false;
    }

    /**
     * Save the current game to the DB.  Only relevant for Play, not Practice.
     */
    protected void saveCurrentGame() {
        GamesDbAdapter.GameShortInfo gameShortInfo = mGamesDbAdapter.new GameShortInfo();
        gameShortInfo.gameNum = mGameNumber;
        gameShortInfo.gameType = mGameType;
        gameShortInfo.gamePointsEarned = mGamePoints;
        gameShortInfo.gameState = mGameState;

        GamesDbAdapter.GameLongInfo gameLongInfo = mGamesDbAdapter.new GameLongInfo();
        gameLongInfo.gameLevel = mLevel;
        gameLongInfo.gameHintsRemaining = mGameHintsRemaining;
        gameLongInfo.gameDuration = mGameTime;
        gameLongInfo.gameLastPlayed = System.currentTimeMillis();
        gameLongInfo.gameData = generateGameData();

        mGamesDbAdapter.addOrUpdateGameData(mPlayType, gameShortInfo, gameLongInfo);

        GamesDbAdapter.LevelInfo levelInfo = mGamesDbAdapter.new LevelInfo();
        levelInfo.levelNum = mLevel;
        levelInfo.levelState = mLevelState;
        levelInfo.levelGamesRemaining = mLevelGamesRemaining;
        levelInfo.levelHintsRemaining = mLevelHintsRemaining;
        levelInfo.levelHintsEarned = mLevelHintsEarned;

        mGamesDbAdapter.addOrUpdateLevelData(mPlayType, levelInfo);
    }

    /**
     * The next game needs to be opened (could be saved or a new game).
     */
    protected void startNextGame(boolean checkAd) {
        if (checkAd && shouldShowInterstitialAd() && canShowInterstitialAd()) {
            showInterstitialAd(AD_DISMISS_ACTION_START_NEXT_GAME);
            return;
        }

        Intent i = null;

        SharedPreferences settings = getSharedPreferences(Constants.PREFS_NAME, Context.MODE_PRIVATE);
        int newGamePref = settings.getInt(Constants.PREFS_NEW_GAME_CHOICE, Constants.PREFS_NEW_GAME_SAME_TYPE);
        if (newGamePref == Constants.PREFS_NEW_GAME_SAME_TYPE) {
            i = new Intent(this, mGameClass);
        } else if (newGamePref == Constants.PREFS_NEW_GAME_RANDOM_TYPE) {
            int gameType = GameUtils.getRandomGameType();
            Class gameClass = GameUtils.getGameClassForGameType(gameType);

            i = new Intent(this, gameClass);
        } else //if (newGamePref == Constants.PREFS_NEW_GAME_ASK_ME)
        {
            i = new Intent(this, AdvancedGameSelectionActivity.class);
        }

        //in case this is Play type and not the last game in the level, the next game needs
        //to be restored from the DB, so retrieve the game class
        if (mIsPlay && (mGameNumber < mNumGamesInLevel)) {
            int gameType = mGamesDbAdapter.getGameTypeForGame(mPlayType, mLevel, mGameNumber + 1);
            i = new Intent(this, GameUtils.getGameClassForGameType(gameType));
        }

        i.putExtra(Constants.KEY_IS_PLAY, mIsPlay);
        i.putExtra(Constants.KEY_PLAY_TYPE, mPlayType);
        if (mIsPlay) {
            if (mGameNumber == mNumGamesInLevel) //this is the last game, so start a new game
            {
                i.putExtra(Constants.KEY_GAME_NUM, -1);
            } else //this is not the last game and there are more saved games ahead of it
            {
                i.putExtra(Constants.KEY_GAME_NUM, mGameNumber + 1);
            }
        } else {
            i.putExtra(Constants.KEY_GAME_NUM, mGameNumber + 1);
        }

        if (mIsPlay) {
            i.putExtra(Constants.KEY_LEVEL, mLevel);
        } else {
            i.putExtra(Constants.KEY_PRACTICE_TYPE, mPracticeType);
            i.putExtra(Constants.PRACTICE_GAMES_COMPLETED, mPracticeGamesCompleted);
            i.putExtra(Constants.PRACTICE_TIME_SPENT, mPracticeTimeSpent);
            i.putExtra(Constants.PRACTICE_HINTS_USED, mPracticeHintsUsed);
            i.putExtra(Constants.PRACTICE_POINTS_EARNED, mPracticePointsEarned);
        }

        //finally, start the activity
        //Whenever the game activity is dismissed, we'd like to go directly to the Level Home
        Utils.Log("setting result in Activity to 1");
        setResult(Constants.ACTION_GO_TO_LEVEL_HOME);
        this.finish();

        startActivity(i);
    }

    /**
     * The previous game needs to be opened.  This is only relevant for Play, not Practice.
     */
    protected void startPreviousGame() {
        Intent i = null;

        int gameNum = mGameNumber - 1;
        int gameType = mGamesDbAdapter.getGameType(mPlayType, mLevel, gameNum);
        if (gameType != -1) {
            i = new Intent(this, GameUtils.getGameClassForGameType(gameType));
        }

        i.putExtra(Constants.KEY_IS_PLAY, mIsPlay);
        i.putExtra(Constants.KEY_PLAY_TYPE, mPlayType);
        i.putExtra(Constants.KEY_GAME_NUM, gameNum);
        i.putExtra(Constants.KEY_LEVEL, mLevel);

        //finally, start the activity
        Utils.Log("setting result in Activity to 1");
        setResult(Constants.ACTION_GO_TO_LEVEL_HOME);
        this.finish();
        startActivity(i);
    }

    /**
     * Set up Views that are common to all Games
     */
    protected void setupViews() {
        //Header
        TextView headerTextView = (TextView) findViewById(R.id.game_header_text_view);
        String headerText = String.format(getResources().getString(R.string.game_header),
                GameUtils.getPlayTypeName(mPlayType, this), mLevel);
        headerTextView.setText(headerText);

        //Game Name
        TextView gameHeadingTextView = (TextView) findViewById(R.id.game_title_text_view);
        String gameHeadingText = String.format(getResources().getString(R.string.game_heading),
                GameUtils.getGameNameFromGameType(mGameType, this), mGameNumber);
        gameHeadingTextView.setText(gameHeadingText);

        //Points Text
        mPointsTextView = (TextView) findViewById(R.id.game_points_text_view);
        int points = (mIsPlay ? mLevelPoints : mPracticePointsEarned);
        String pointsText = String.format(getResources().getString(R.string.game_points_label), points);
        mPointsTextView.setText(pointsText);

        //Timer Text
        mGameTimeTextView = (TextView) findViewById(R.id.game_time_text_view);
        mGameTimeTextView.setText(Utils.convertTime(mGameTime));

        //Buttons at the bottom
        mStopButton = (ImageButton) findViewById(R.id.stop_button);
        mStopButton.setOnClickListener(this);

        mPauseButton = (ImageButton) findViewById(R.id.pause_button);
        mPauseButton.setOnClickListener(this);

        mGiveUpButton = (ImageButton) findViewById(R.id.giveup_button);
        mGiveUpButton.setOnClickListener(this);

        mHintButton = (ImageButton) findViewById(R.id.hint_button);
        mHintButton.setOnClickListener(this);

        mHelpButton = (ImageButton) findViewById(R.id.help_button);
        mHelpButton.setOnClickListener(this);

        //mShareButton = (ImageButton) findViewById(R.id.share_button);
        //mShareButton.setOnClickListener(this);

        mPreviousButton = (ImageButton) findViewById(R.id.previous_button);
        mPreviousButton.setOnClickListener(this);
        if (mGameNumber == 1) //disable previous in case of first game
        {
            mPreviousButton.setEnabled(false);
        }
        if (!mIsPlay) //Practice games don't have a Previous button
        {
            LinearLayout buttonLayout = (LinearLayout) findViewById(R.id.game_button_layout);
            buttonLayout.removeView(mPreviousButton);
            //mPreviousButton.setVisibility(ImageButton.INVISIBLE);
            //TableRow.LayoutParams params = new TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT, TableRow.LayoutParams.WRAP_CONTENT, 0f);
            //mPreviousButton.setLayoutParams(params);
        }

        mNextButton = (ImageButton) findViewById(R.id.next_button);
        mNextButton.setOnClickListener(this);

        //set up hint button with values, as required
        //setupHintButton();

        //set up the ads
        mBannerAdView = (AdView) findViewById(R.id.adView);
        mBannerAdView.setAdListener(this);

        //set up game specific views
        doSetupViews();
    }

    /**
     * Refresh the views at run-time based on updated game state, such as hints
     * used, points earned, time passed, etc.
     */
    private void refreshViews() {
        //Points Text
        String pointsText = String.format(getResources().getString(R.string.game_points_label),
                (mIsPlay ? mLevelPoints : mPracticePointsEarned));
        mPointsTextView.setText(pointsText);

        if (mGameState == Constants.GAME_STATE_NOT_STARTED) {
            mStopButton.setEnabled(true);
            mPauseButton.setEnabled(false);
            mGiveUpButton.setEnabled(false);
            mHintButton.setEnabled(false);
            mHelpButton.setEnabled(true);
        } else if (mGameState == Constants.GAME_STATE_COMPLETED) {
            mStopButton.setEnabled(true);
            mPauseButton.setEnabled(false);
            mGiveUpButton.setEnabled(false);
            mHintButton.setEnabled(false);
            mHelpButton.setEnabled(true);
        } else if (mGameState == Constants.GAME_STATE_IN_PROGRESS) {
            mStopButton.setEnabled(true);
            mPauseButton.setEnabled(true);
            mGiveUpButton.setEnabled(true);
            mHintButton.setEnabled(true);
            mHelpButton.setEnabled(true);
        } else if (mGameState == Constants.GAME_STATE_PAUSED) {
            mStopButton.setEnabled(false);
            mPauseButton.setEnabled(false);
            mGiveUpButton.setEnabled(false);
            mHintButton.setEnabled(false);
            mHelpButton.setEnabled(true);
        } else if (mGameState == Constants.GAME_STATE_GIVEN_UP) {
            mStopButton.setEnabled(true);
            mPauseButton.setEnabled(false);
            mGiveUpButton.setEnabled(false);
            mHintButton.setEnabled(false);
            mHelpButton.setEnabled(true);
        } else if (mGameState == Constants.GAME_STATE_FAILED) {
            mStopButton.setEnabled(true);
            mPauseButton.setEnabled(false);
            mGiveUpButton.setEnabled(false);
            mHintButton.setEnabled(false);
            mHelpButton.setEnabled(true);
        }

        //setupHintButton();

        doRefreshViews();
    }

    /**
     * Set up the hint button to show the count of remaining hints in the game.
     * 
     * If there are > 1 hints remaining in the game, don't show a count
     * If there are 1 or 0 hints remaining in the game, show a count
     */
    private void setupHintButton() {
        if (mGameHintsRemaining == 1) {
            mHintButton.setBackgroundResource(R.drawable.hint_button1);
        } else if (mGameHintsRemaining == 0) {
            mHintButton.setBackgroundResource(R.drawable.hint_button0);
        } else {
            mHintButton.setBackgroundResource(R.drawable.hint_button);
        }
    }

    /**
     * The stop button was clicked
     */
    private void onStopButtonClicked() {
        Analytics.trackEvent(this, Analytics.getAnalyticsCategoryFromGame(mGameType),
                Analytics.ANALYTICS_ACTION_BUTTON_PRESS, Analytics.ANALYTICS_LABEL_STOP_BUTTON, null);

        stopTimer();

        //Update various values

        if (mIsPlay) // for play, save the game and show the entire level point summary
        {
            //save to the DB
            saveCurrentGame();

            //show the dialog
            GameUtils.LevelInfo levelInfo = new GameUtils.LevelInfo();
            levelInfo.levelNum = mLevel;
            levelInfo.levelGamesRemaining = mLevelGamesRemaining;
            levelInfo.levelHintsRemaining = mLevelHintsRemaining;
            levelInfo.levelHintsEarned = mLevelHintsEarned;
            levelInfo.levelPointsRemaining = GameUtils.sLevelValues[mLevel - 1][GameUtils.LEVEL_TOTAL_POINTS]
                    - mLevelPoints;
            levelInfo.levelTimeSpent = mGamesDbAdapter.getTotalTimeSpent(mPlayType, mLevel);
            levelInfo.levelTotalGames = GameUtils.sLevelValues[mLevel - 1][GameUtils.LEVEL_TOTAL_GAMES];
            levelInfo.levelTotalHints = GameUtils.sLevelValues[mLevel - 1][GameUtils.LEVEL_TOTAL_HINTS];
            levelInfo.levelTotalPoints = GameUtils.sLevelValues[mLevel - 1][GameUtils.LEVEL_TOTAL_POINTS];

            Utils.Log("passing to LevelPointSummaryDialog, levelInfo = " + levelInfo);
            LevelPointSummaryDialog dialog = new LevelPointSummaryDialog(this, levelInfo);
            dialog.setCanceledOnTouchOutside(true);
            dialog.show();
            levelInfo = null;
        } else {
            // for practice, show the practice summary
            GameUtils.PracticeInfo practiceInfo = new GameUtils.PracticeInfo();
            //practiceInfo.practiceGamesCompleted = mPracticeGamesCompleted;
            practiceInfo.practiceGamesCompleted = mGameNumber;
            practiceInfo.practiceHintsUsed = mPracticeHintsUsed;
            practiceInfo.practicePointsEarned = mPracticePointsEarned;
            practiceInfo.practiceTimeSpent = mPracticeTimeSpent;

            PracticePointSummaryDialog dialog = new PracticePointSummaryDialog(this, practiceInfo);
            dialog.setCanceledOnTouchOutside(true);
            dialog.show();
            practiceInfo = null;
        }
    }

    /**
     * The pause button was clicked
     */
    private void onPauseButtonClicked() {
        Analytics.trackEvent(this, Analytics.getAnalyticsCategoryFromGame(mGameType),
                Analytics.ANALYTICS_ACTION_BUTTON_PRESS, Analytics.ANALYTICS_LABEL_PAUSE_BUTTON, null);

        //stop the timer
        stopTimer();

        GamePauseDialog dialog = new GamePauseDialog(this);
        dialog.setCanceledOnTouchOutside(true);
        dialog.show();
    }

    /**
     * The user opted to resume the current game
     */
    private void onResumeGameSelected() {
        //resume the timer
        startTimer();
    }

    /**
     * The give up button was clicked
     */
    private void onGiveUpButtonClicked() {
        Analytics.trackEvent(this, Analytics.getAnalyticsCategoryFromGame(mGameType),
                Analytics.ANALYTICS_ACTION_BUTTON_PRESS, Analytics.ANALYTICS_LABEL_REVEAL_BUTTON, null);

        GameGiveUpDialog dialog = new GameGiveUpDialog(this, -GameUtils.getPointChart(mPlayType, mIsPlay, mLevel,
                mPracticeType)[GameUtils.GAME_POINTS_ON_GIVING_UP][mGameType], mGameHintsRemaining);
        dialog.setCanceledOnTouchOutside(true);
        dialog.show();
    }

    /**
     * Consume a hint for the current game
     */
    private void consumeHint() {
        Analytics.trackEvent(this, Analytics.getAnalyticsCategoryFromGame(mGameType),
                Analytics.ANALYTICS_ACTION_BUTTON_PRESS, Analytics.ANALYTICS_LABEL_HINT_USED, null);

        doHandleHintUsed();

        mGameHintsRemaining--;
        mGameHintsUsed++;
        mLevelHintsRemaining--;
        mPracticeHintsUsed++;

        mGamePoints += GameUtils.getPointChart(mPlayType, mIsPlay, mLevel,
                mPracticeType)[GameUtils.GAME_POINTS_ON_HINT_USED][mGameType];
        mLevelPoints += GameUtils.getPointChart(mPlayType, mIsPlay, mLevel,
                mPracticeType)[GameUtils.GAME_POINTS_ON_HINT_USED][mGameType];
        mPracticePointsEarned += GameUtils.getPointChart(mPlayType, mIsPlay, mLevel,
                mPracticeType)[GameUtils.GAME_POINTS_ON_HINT_USED][mGameType];
    }

    /**
     * The hint button was clicked
     */
    private void onHintButtonClicked() {
        Analytics.trackEvent(this, Analytics.getAnalyticsCategoryFromGame(mGameType),
                Analytics.ANALYTICS_ACTION_BUTTON_PRESS, Analytics.ANALYTICS_LABEL_HINT_BUTTON, null);

        Utils.Log("onHintbutton, mGameHintsRemaining = " + mGameHintsRemaining);
        if (mGameHintsRemaining > 0) {
            consumeHint();

            refreshViews();
        } else //hints exhausted!
        {
            GameHintsExhaustedDialog dialog = new GameHintsExhaustedDialog(this);
            dialog.setCanceledOnTouchOutside(true);
            dialog.show();
        }
    }

    /**
     * The Help button was clicked
     */
    private void onHelpButtonClicked() {
        Analytics.trackEvent(this, Analytics.getAnalyticsCategoryFromGame(mGameType),
                Analytics.ANALYTICS_ACTION_BUTTON_PRESS, Analytics.ANALYTICS_LABEL_HELP_BUTTON, null);

        Intent intent = new Intent(this, HelpDialog.class);
        intent.putExtra(Constants.KEY_PLAY_TYPE, mPlayType);
        intent.putExtra(Constants.KEY_GAME_TYPE, mGameType);
        intent.putExtra(Constants.KEY_IS_PLAY, mIsPlay);
        intent.putExtra(Constants.KEY_GAME_LEVEL, mLevel);
        intent.putExtra(Constants.KEY_PRACTICE_TYPE, mPracticeType);

        startActivity(intent);
    }

    private void onShareButtonClicked() {
        Analytics.trackEvent(this, Analytics.getAnalyticsCategoryFromGame(mGameType),
                Analytics.ANALYTICS_ACTION_BUTTON_PRESS, Analytics.ANALYTICS_LABEL_SHARE_BUTTON, null);

        GameShareDialog dialog = new GameShareDialog(this);
        dialog.setCanceledOnTouchOutside(true);
        dialog.show();
    }

    /**
     * The previous game button was clicked
     */
    private void onPreviousButtonClicked() {
        stopTimer();

        saveCurrentGame();
        startPreviousGame();
    }

    /**
     * The next game button was clicked
     */
    private void onNextButtonClicked() {
        Analytics.trackEvent(this, Analytics.getAnalyticsCategoryFromGame(mGameType),
                Analytics.ANALYTICS_ACTION_BUTTON_PRESS, Analytics.ANALYTICS_LABEL_NEXT_GAME_BUTTON, null);

        stopTimer();

        if (mIsPlay) {
            saveCurrentGame();
        }

        startNextGame(false);
    }

    /**
     * A button was clicked
     */
    public void onClick(View v) {
        if (v.getId() == R.id.stop_button) {
            onStopButtonClicked();
        } else if (v.getId() == R.id.pause_button) {
            onPauseButtonClicked();
        } else if (v.getId() == R.id.giveup_button) {
            onGiveUpButtonClicked();
        } else if (v.getId() == R.id.hint_button) {
            onHintButtonClicked();
        } else if (v.getId() == R.id.help_button) {
            onHelpButtonClicked();
        } else if (v.getId() == R.id.previous_button) {
            onPreviousButtonClicked();
        } else if (v.getId() == R.id.next_button) {
            onNextButtonClicked();
        } else if (v.getId() == R.id.share_button) {
            onShareButtonClicked();
        } else {
            doHandleButtonClicked(v.getId());
        }
    }

    /**
     * One of the options in the GameDialog instance was selected
     */
    public void onDialogOptionSelected(Dialog dialog, int option) {
        if (dialog.getClass() == GamePauseDialog.class) {
            if (option == GameDialog.GAME_DIALOG_ACTION_POSITIVE_1) {
                //Resume the game
                onResumeGameSelected();
            }
        } else if (dialog.getClass() == LevelPointSummaryDialog.class) {
            if (option == GameDialog.GAME_DIALOG_ACTION_POSITIVE_1) {
                setResult(Constants.ACTION_GO_TO_LEVEL_HOME);
                this.finish();
            }
        } else if (dialog.getClass() == PracticePointSummaryDialog.class) {
            if (option == GameDialog.GAME_DIALOG_ACTION_POSITIVE_1) {
                Analytics.trackEvent(this, Analytics.getAnalyticsCategoryFromGame(mGameType),
                        Analytics.ANALYTICS_ACTION_BUTTON_PRESS,
                        Analytics.ANALYTICS_LABEL_GAME_SUMMARY_SHARE_BUTTON, null);

                String storyTitle = String.format(
                        getResources().getString(R.string.practice_point_summary_share_story_title), mGameNumber,
                        GameUtils.getGameNameFromGameType(mGameType, this));
                String storyCaption = getResources().getString(R.string.practice_point_summary_share_story_caption);
                String storyDescription = String.format(
                        getResources().getString(R.string.practice_point_summary_share_story_description),
                        GameUtils.getGameNameFromGameType(mGameType, this), mGameNumber, mPracticePointsEarned,
                        mPracticeTimeSpent / 1000, mPracticeHintsUsed);

            } else {
                this.finish();
            }
            /*
             * Dont' show ads for now
             */
            /*
               if (canShowInterstitialAd())
               {
                  showInterstitialAd(AD_DISMISS_ACTION_DISMISS_ACTIVITY);
               }
               else
               {
                  this.finish();
               }
             */
            //this.finish();
        } else if (dialog.getClass() == GameShareDialog.class) {
            if (option == GameDialog.GAME_DIALOG_ACTION_POSITIVE_1) //Share Game
            {
                Analytics.trackEvent(this, Analytics.getAnalyticsCategoryFromGame(mGameType),
                        Analytics.ANALYTICS_ACTION_BUTTON_PRESS, Analytics.ANALYTICS_LABEL_SHARE_GAME_BUTTON, null);
            } else if (option == GameDialog.GAME_DIALOG_ACTION_POSITIVE_2) //Invite Friends
            {
                Analytics.trackEvent(this, Analytics.getAnalyticsCategoryFromGame(mGameType),
                        Analytics.ANALYTICS_ACTION_BUTTON_PRESS, Analytics.ANALYTICS_LABEL_INVITE_FRIENDS_BUTTON,
                        null);

                String storyTitle = getResources().getString(R.string.facebook_share_story_title);
                String storyCaption = getResources().getString(R.string.facebook_share_story_caption);
                String storyDescription = getResources().getString(R.string.facebook_share_story_description);
            }
        } else if (dialog.getClass() == GameGiveUpDialog.class) {
            if (option == GameDialog.GAME_DIALOG_ACTION_POSITIVE_1) {
                Analytics.trackEvent(this, Analytics.getAnalyticsCategoryFromGame(mGameType),
                        Analytics.ANALYTICS_ACTION_BUTTON_PRESS, Analytics.ANALYTICS_LABEL_REVEAL_GAME_BUTTON,
                        null);

                stopTimer();

                doHandleGivenUp();

                mGamePoints += GameUtils.getPointChart(mPlayType, mIsPlay, mLevel,
                        mPracticeType)[GameUtils.GAME_POINTS_ON_GIVING_UP][mGameType];
                mLevelPoints += GameUtils.getPointChart(mPlayType, mIsPlay, mLevel,
                        mPracticeType)[GameUtils.GAME_POINTS_ON_GIVING_UP][mGameType];
                mPracticePointsEarned += GameUtils.getPointChart(mPlayType, mIsPlay, mLevel,
                        mPracticeType)[GameUtils.GAME_POINTS_ON_GIVING_UP][mGameType];

                mGameState = Constants.GAME_STATE_GIVEN_UP;

                refreshViews();
            } else if (option == GameDialog.GAME_DIALOG_ACTION_NEGATIVE_1) {
                //cancel giving up.  Do nothing
                Analytics.trackEvent(this, Analytics.getAnalyticsCategoryFromGame(mGameType),
                        Analytics.ANALYTICS_ACTION_BUTTON_PRESS, Analytics.ANALYTICS_LABEL_REVEAL_CANCELLED, null);
            } else if (option == GameDialog.GAME_DIALOG_ACTION_POSITIVE_2) {
                Analytics.trackEvent(this, Analytics.getAnalyticsCategoryFromGame(mGameType),
                        Analytics.ANALYTICS_ACTION_BUTTON_PRESS, Analytics.ANALYTICS_LABEL_REVEAL_SHARE_BUTTON,
                        null);
            }
        } else if (dialog.getClass() == GameCompletedDialog.class) {
            if (option == GameDialog.GAME_DIALOG_ACTION_POSITIVE_1) {

            } else {
                handleGameCompleted();
            }
        } else if (dialog.getClass() == GameFailedDialog.class) {
            if (option == GameDialog.GAME_DIALOG_ACTION_POSITIVE_1) {
                doHandleGivenUp();

                //Update values
                mGamePoints += GameUtils.getPointChart(mPlayType, mIsPlay, mLevel,
                        mPracticeType)[GameUtils.GAME_POINTS_ON_LOSING_GAME][mGameType];
                mLevelPoints += GameUtils.getPointChart(mPlayType, mIsPlay, mLevel,
                        mPracticeType)[GameUtils.GAME_POINTS_ON_LOSING_GAME][mGameType];
                mPracticePointsEarned += GameUtils.getPointChart(mPlayType, mIsPlay, mLevel,
                        mPracticeType)[GameUtils.GAME_POINTS_ON_LOSING_GAME][mGameType];

                refreshViews();
            }
        } else if (dialog.getClass() == GameHintsExhaustedDialog.class) {
            if (option == GameDialog.GAME_DIALOG_ACTION_POSITIVE_1) {
                Analytics.trackEvent(this, Analytics.getAnalyticsCategoryFromGame(mGameType),
                        Analytics.ANALYTICS_ACTION_BUTTON_PRESS, Analytics.ANALYTICS_LABEL_HINT_SHARE_BUTTON, null);

            }
        } else if (dialog.getClass() == GameHintConfirmationDialog.class) {
            if (option == GameDialog.GAME_DIALOG_ACTION_POSITIVE_1) {
                consumeHint();

                refreshViews();
            } else if (option == GameDialog.GAME_DIALOG_ACTION_NEGATIVE_1) {
                //status quo (don't use hint)
            }
        }

    }

    /************* Ads **************/

    /**
     * Load the banner ad.  This should be done after
     * the game data has been loaded so we can target
     * ads based on game content
     */
    private void loadBannerAd() {
        AdRequest adRequest = new AdRequest();
        Set<String> keywords = new HashSet<String>();
        Utils.addKeywords(keywords);
        adRequest.setKeywords(keywords);

        Utils.Log("gauravad: loadBannerAd with keywords");
        mBannerAdView.loadAd(adRequest);
    }

    /**
     * Show a full-screen rich Interstitial Ad
     */
    private void setupInterstitialAds() {
        Utils.Log("gauravad: setupInterstitialAds");
        // Create the interstitial
        interstitial = new InterstitialAd(this, "a15281ef463ae86");

        // Set Ad Listener to use the callbacks below
        interstitial.setAdListener(this);

        // Initial Refresh
        refreshInterstitialAds();
    }

    private void refreshInterstitialAds() {
        Utils.Log("gauravad: refreshInterstitialAds");
        // Create ad request
        AdRequest adRequest = new AdRequest();

        // Begin loading your interstitial
        interstitial.loadAd(adRequest);
    }

    public void onReceiveAd(Ad ad) {
        if (ad == mBannerAdView) {
            Utils.Log("gauravad: onReceiveAd for Banner ad");
        } else if (ad == interstitial) {
            Utils.Log("gauravad: onReceiveAd for Interstitial ad");
        }
    }

    public void onFailedToReceiveAd(Ad ad, AdRequest.ErrorCode error) {
        if (ad == mBannerAdView) {
            Utils.Log("gauravad: onFailedToReceiveAd for Banner ad");
        } else if (ad == interstitial) {
            Utils.Log("gauravad: onFailedToReceiveAd for Interstitial ad");
        }
    }

    public void onPresentScreen(Ad ad) {
        Utils.Log("gauravad: onPresentScreen");
        mShowingInterstitialAd = true;
        mTimeOfLastAd = System.currentTimeMillis();
    }

    public void onDismissScreen(Ad ad) {
        Utils.Log("gauravad: onDismissScreen");
        mShowingInterstitialAd = false;
        switch (mActionOnAdDismiss) {
        case AD_DISMISS_ACTION_DISMISS_ACTIVITY:
            this.finish();
            break;
        case AD_DISMISS_ACTION_START_NEXT_GAME:
            startNextGame(false);
            break;
        }
    }

    public void onLeaveApplication(Ad ad) {
        Utils.Log("gauravad: onLeaveApplication");
    }

    private boolean shouldShowInterstitialAd() {
        //Don't show interstitials for now!
        /*
        long currentTime = System.currentTimeMillis();
        */
        /**
         * Set the initial value of mTimeOfLastAd if not already set
         * this is to avoid showing the ad to the user as soon as he starts
         * playing.
         */

        /*
        if (mTimeOfLastAd == 0)
        {
           mTimeOfLastAd = currentTime;
        }
        if (currentTime - mTimeOfLastAd > MIN_INTERVAL_BETWEEN_ADS)
        {
           return true;
        }
        */
        return false;
    }

    private boolean canShowInterstitialAd() {
        return interstitial.isReady();
    }

    private void showInterstitialAd(int actionOnDismiss) {
        interstitial.show();
        mActionOnAdDismiss = actionOnDismiss;
    }

    public static Bitmap getBitmapFromAsset(Context context, String strName) {
        AssetManager assetManager = context.getAssets();

        InputStream istr;
        Bitmap bitmap = null;
        try {
            istr = assetManager.open(strName);
            bitmap = BitmapFactory.decodeStream(istr);
        } catch (IOException e) {
            return null;
        }

        return bitmap;
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        Utils.Log(LOG_TAG + "onActivityResult called");
        super.onActivityResult(requestCode, resultCode, data);

    }

    /************* Timer Related **************/

    /**
     * Update the timer in the UI with the game time
     */
    private void updateTimer() {
        mGameTimeTextView.setText(Utils.convertTime(mGameTime));
    }

    /**
     * Start the timer either from the beginning (if new game)
     * or from the point it is being resumed/restored.
     */
    private void startTimer() {
        long currentTime = System.currentTimeMillis();
        if (mGameTime <= 0) {
            mGameTimeStart = currentTime;
            Utils.Log("Timer Started from 0");
        } else {
            mGameTimeStart = currentTime - mGameTime;
            Utils.Log("Timer Resumed");
        }

        //set timer value for practice time calculation
        mLastTimerValue = currentTime;

        mGameTimerHandler.removeCallbacks(mGameTimer);
        mGameTimerHandler.postDelayed(mGameTimer, 0); //start immediately
    }

    /**
     * Stop the timer
     */
    private void stopTimer() {
        mGameTimerHandler.removeCallbacks(mGameTimer);
        Utils.Log("Timer Stopped");
    }

    /********************* Abstract routines - Game implementations must implement these ******************/

    /**
     * Return the main layout's ID that is used to call setContentView() on.
     * 
     * @return The Layout ID
     */
    protected abstract int getLayoutID();

    /**
     * Initialize Game specific values here, such as mGameType and mGameClass
     * as well as allocate member variables and initialize them (e.g., Vectors, lists, etc.)
     */
    protected abstract void doInitializeGame();

    /**
     * Honor theme for game specific UI elements
     */
    protected abstract void doHonorTheme();

    /**
     * Implemented by the Game Activity to restore game-specific attributes.
     */
    protected abstract void doRestoreGame();

    /**
     * Set up game specific views
     */
    protected abstract void doSetupViews();

    /**
     * Refresh game specific views.
     */
    protected abstract void doRefreshViews();

    /**
     * Generate the Game Data for the current game for storing in the DB.
     * Refer the GamesDbAdapter for format details for individual game types.
     * 
     * @return String representing the game data
     */
    protected abstract String generateGameData();

    /**
     * Generate game data from the DB for starting a fresh game.
     * This is called in the context of an AsyncTask as the expectation is
     * that it can take a while to successfully generate a good game
     * from the DB.
     */
    protected abstract String generateGameDataFromDB();

    /**
     * Handle the user Giving up the current game.
     */
    protected abstract void doHandleGivenUp();

    /**
     * Handle the user using a hint in the current game.
     */
    protected abstract void doHandleHintUsed();

    /**
     * Generic button clicked, other than the standard buttons common
     * to all games.
     * 
     * @param id The button ID
     */
    protected abstract void doHandleButtonClicked(int id);

    /****************** Member Variables **********************/

    /**
     * Common protected members
     */
    private WordJamApp mApp;
    protected WordDbAdapter mWordDbAdapter;
    protected GamesDbAdapter mGamesDbAdapter;
    private SetupGameAsyncTask mSetupGameAsyncTask;

    /**
     * Play and Practice - common members
     */
    protected boolean mIsPlay; //This game's part of a Play, not a practice
    protected int mPlayType; //The Play or Challenge type.  One of Constants.PLAY_TYPE_*
    protected int mGameType; //The type of game.  One of Constants.GAME_TYPE_*
    protected int mGameState; //Type state of the game.  One of Constants.GAME_STATE_*
    protected Class mGameClass; //The Class for this game activity
    protected int mGameNumber; //This Game's no.  May be -1 in case of "new" game.  Sent from calling activity.
    protected int mGameHintsRemaining; //No. of hints remaining in this game.  Calculated for new game, looked up for resumed game.
    protected int mGameHintsUsed; //No. of hints consumed so far in the game.
    protected int mGamePoints; //Total points earned in this game so far
    protected boolean mStopGame; //Indication to the dialog interface to stop the game as soon as the dialog returns
    protected boolean mIsNewGame; //This is a new game, not already saved in the DB

    /**
     * Play related members
     */
    protected int mLevel; //This game's level.  Send from calling activity.
    protected int mLevelPoints; //Total points earned in this level, including the current game (i.e., inclusive of mGamePoints)
    protected int mNumGamesInLevel; //Total no. of games in the level excluding the current game
    protected int mLevelState; //State of this level at this very point.  One of Constants.LEVEL_STATE_*
    protected int mLevelGamesRemaining; //No.o f games remaining to be completed at this level
    protected int mLevelHintsRemaining; //No. of hints remaining to be used at this level
    protected int mLevelHintsEarned; //No. of hints earned at this level

    /**
     * Practice (non-Play) related members
     */
    protected int mPracticeType; //Type of practice.  One of Constants.PRACTICE_TYPE_*
    protected int mPracticeGamesCompleted; //Total no. of games successfully completed in this practice session.  Sent from calling activity.
    protected int mPracticeTimeSpent; //Total time spent in this practice session, including the current game.  Passed along to next practice game and added up for the session.
    protected int mPracticeHintsUsed; //Total no. of hints used in this practice session.  Passed along to next practice game and added up for the session.
    protected int mPracticePointsEarned; //Total. no. of points earned in this practice session.  Passed along to next practice game and added up for the session.

    /**
     * Other members used for storing/restoring/status/etc.
     */
    protected String mGameData; //The entire dump of game data in a single string.  For formats, see GamesDbAdapter
    protected boolean mGameDirty; //The game was actually started
    protected long mGameTime; //Time duration of the game before it was stopped/paused

    protected static final String LOG_TAG = "BaseGameActivity: ";

    /**
     * Used to manage Timer values.
     */
    protected long mLastTimerValue; //The "current time" snapshot the last time the timer was invoked.

    /**
     * UI Elements
     */
    protected TextView mPointsTextView; // shows the current total points earned in this level (including for the current game)
    protected TextView mGameTimeTextView; // shows the time elapsed since the beginning of the game
    private ProgressDialog mProgressDialog; //shows while the async task is setting up the game

    /**
     * The Various action buttons at the bottom of each game.  Names are self-explanatory
     */
    protected ImageButton mStopButton, mPauseButton, mGiveUpButton, mHintButton, mHelpButton, mPreviousButton,
            mNextButton, mShareButton;

    /**
     * Interstitial Ad related
     */
    private InterstitialAd interstitial;
    private boolean mShowingInterstitialAd = false; // tracks whether the interstitial ad is showing
    private int mActionOnAdDismiss = 0; // action to be taken after the interstitial ad is dismissed
    private static long mTimeOfLastAd = 0; // absolute time at which the last interstitial ad was shown to the user
    private static final int MIN_INTERVAL_BETWEEN_ADS = 5 * 60 * 1000; // 5 minutes (in milliseconds)
    private static final int AD_DISMISS_ACTION_START_NEXT_GAME = 1;
    private static final int AD_DISMISS_ACTION_DISMISS_ACTIVITY = 2;

    /**
     * Banner Ad related
     */
    private AdView mBannerAdView; // the banner ad view that's shown at the bottom of the game view

    /**
     * The timer that manages the game time.
     */
    private Runnable mGameTimer;
    private Handler mGameTimerHandler;
    private static final int TIMER_REFRESH_RATE = 1000; // milliseconds after which timer is to be refreshed
    private long mGameTimeStart;

    /**
     * This class implements the Timer that is required to measure and display
     * the game time (time spent playing a game)
     */
    private class GameTimer implements Runnable {
        /**
         * Callback called when the timer goes off.  Called every TIMER_REFRESH_RATE
         * milliseconds
         */
        public void run() {
            long currentTime = System.currentTimeMillis();
            mGameTime = currentTime - mGameTimeStart;
            updateTimer();
            mGameTimerHandler.postDelayed(this, TIMER_REFRESH_RATE);

            //increment timer value, and set practice time
            mPracticeTimeSpent += (currentTime - mLastTimerValue);
            mLastTimerValue = currentTime;
        }
    }

    private class SetupGameAsyncTask extends AsyncTask<Void, Void, String> {
        private Context mContext;

        SetupGameAsyncTask(Context context) {
            mContext = context;
        }

        protected void onPreExecute() {
            Utils.Log("onPreExecute");
            //setProgressBarIndeterminateVisibility(true);
            //mProgressDialog = ProgressDialog.show(mContext, getResources().getString(R.string.setupdb_progress_message), getResources().getString(R.string.setup_game_progress_title));
            mProgressDialog = ProgressDialog.show(mContext, "", "");
        }

        protected String doInBackground(Void... types) {
            Utils.Log("doInBackground");
            return generateGameDataFromDB();
        }

        protected void onProgressUpdate(Void... progress) {
        }

        protected void onPostExecute(String result) {
            Utils.Log("onPostExecute, result = [" + result + "]");
            mGameData = result;
            mGameState = Constants.GAME_STATE_IN_PROGRESS;
            startTimer();
            doRestoreGame();
            loadBannerAd();
            refreshViews();

            //setProgressBarIndeterminateVisibility(false);
            if (mProgressDialog.isShowing()) {
                //Log.d(null, "################# dialog was showing, dismissing it");
                mProgressDialog.dismiss();
            }
        }

        protected void onCancelled() {
            mGameData = null;
        }
    }

}