Android Open Source - tetravex-android Puzzle Activity






From Project

Back to project page tetravex-android.

License

The source code is released under:

GNU General Public License

If you think the Android project tetravex-android 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) 2014 John Hunt <john.alma.hunt@gmail.com>
 *//from  ww  w . j  a v  a  2  s  . c om
 * This file is part of Tetravex Android.
 *
 * Tetravex Android 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.
 *
 * Tetravex Android 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 Tetravex Android.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.github.tetravex_android.activity;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.ClipData;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.os.Bundle;
import android.os.SystemClock;
import android.preference.PreferenceManager;
import android.view.DragEvent;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.Chronometer;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Toast;

import com.github.tetravex_android.Constants;
import com.github.tetravex_android.R;
import com.github.tetravex_android.TetravexApp;
import com.github.tetravex_android.data.HiScoreManager;
import com.github.tetravex_android.game.Puzzle;
import com.github.tetravex_android.game.Puzzle.PuzzleState;
import com.github.tetravex_android.game.Tile;
import com.github.tetravex_android.data.BoardAdapter;

/**
 * This activity is where the Tetravex gameplay takes place
 */
public class PuzzleActivity extends Activity
        implements View.OnTouchListener, View.OnDragListener {

    static final int PAUSE_REQUEST = 1;  // The request code

    private Puzzle mPuzzle;

    private GridView mTargetGridView;
    private GridView mSourceGridView;

    // fields to support drag 'n drop
    private Tile mDraggedTile = null;
    private int mDraggedTileStartingX = -1;
    private int mDraggedTilePosition = -1;

    // used for the puzzle timer
    private Chronometer mTimer;
    private long mPausedTime = 0;

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

        // Full screen
        this.getWindow().setFlags(
                WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);
        this.requestWindowFeature(Window.FEATURE_NO_TITLE);

        setContentView(R.layout.activity_puzzle);
        TetravexApp.injectActivity(this);
        mTimer = (Chronometer) findViewById(R.id.timer);

        // get the size of the board from the saved setting
        SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);
        String boardSize = settings.getString(getString(R.string.pref_size_key),
                Constants.DEFAULT_SIZE);
        // access the setting to decide whether or not to color the tiles
        boolean colorTiles = settings.getBoolean(getString(R.string.pref_color_key), true);

        mPuzzle = new Puzzle(Integer.valueOf(boardSize));
        mPuzzle.setColor(colorTiles);

        mTargetGridView = (GridView) findViewById(R.id.target_board);
        mSourceGridView = (GridView) findViewById(R.id.source_board);

        formatTargetBoard();
        formatSourceBoard();
    }

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

        if (mPuzzle.getState().equals(PuzzleState.PAUSED)) {
            mPuzzle.setState(PuzzleState.IN_PROGRESS);
            resumeTimer();
        }
    }

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

        if (mPuzzle.getState().equals(PuzzleState.IN_PROGRESS)) {
            mPuzzle.setState(PuzzleState.PAUSED);
            pauseTimer();
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        // Check which request we're responding to
        if (requestCode == PAUSE_REQUEST) {
            // handle result codes from the PauseActivity
            if (resultCode == PauseActivity.RESULT_NEW_GAME) {
                Intent intent = new Intent(this, PuzzleActivity.class);
                startActivity(intent);
                finish();
            } else if (resultCode == PauseActivity.RESULT_QUIT_GAME) {
                // Go back to the dashboard
                finish();
            }
        }
    }

    /**
     * Sets the adapter and number of columns for the target board GridView
     */
    private void formatTargetBoard() {
        mTargetGridView.setAdapter(new BoardAdapter(this, false, mPuzzle));
        mTargetGridView.setNumColumns(mPuzzle.getSize());
        mTargetGridView.setOnTouchListener(this);
    }

    /**
     * Set the adapter and number of columns for the source board GridView
     */
    private void formatSourceBoard() {
        mSourceGridView.setAdapter(new BoardAdapter(this, true, mPuzzle));
        mSourceGridView.setNumColumns(mPuzzle.getSize());
        mSourceGridView.setOnTouchListener(this);
    }

    /**
     * Handle events when the user touches the screen
     *
     * @param view        the View that was clicked
     * @param motionEvent the system event associated with the touch
     * @return {@code true} if the event is consumed, {@code false} otherwise
     */
    public boolean onTouch(View view, MotionEvent motionEvent) {

        if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
            GridView parent = (GridView) view;

            int x = (int) motionEvent.getX();
            int y = (int) motionEvent.getY();
            int position = parent.pointToPosition(x, y);
            int relativePosition = position - parent.getFirstVisiblePosition();
            final View target = parent.getChildAt(relativePosition);

            // drag only numbered tiles
            if (target != null &&
                    target.getTag().equals(Constants.TAG_NUMBERED_TILE)) {
                // start a drag event - on a separate thread to not interfere with the
                // touch event already in progress

                target.post(new Runnable() {
                    @Override
                    public void run() {
                        ClipData data = ClipData.newPlainText("DragData", "");
                        View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(target);
                        target.startDrag(data, shadowBuilder, target, 0);
                        target.setVisibility(View.INVISIBLE);
                    }
                });

                mDraggedTilePosition = parent.getPositionForView(target);
                if (parent.equals(mSourceGridView)) {
                    mDraggedTileStartingX = mPuzzle.getSize();
                } else {
                    mDraggedTileStartingX = 0;
                }
                mDraggedTile = mPuzzle.getTileByPosition(mDraggedTileStartingX, mDraggedTilePosition);
                mPuzzle.setTile(mDraggedTileStartingX, mDraggedTilePosition, null);

                // draw an empty slot where the dragged tile just was
                ((BoardAdapter) parent.getAdapter()).notifyDataSetChanged();

                // start the timer the first time a tile is touched
                if (mPuzzle.getState().equals(PuzzleState.NEW)) {
                    mPuzzle.setState(PuzzleState.IN_PROGRESS);
                    mTimer.setBase(SystemClock.elapsedRealtime());
                    mTimer.start();
                }
            }
            // report that the event was consumed
            return true;
        } else {
            return false;
        }
    }

    @Override
    public boolean onDrag(View view, DragEvent event) {

        // Handles each of the expected events
        switch (event.getAction()) {

            //signal for the start of a drag and drop operation.
            case DragEvent.ACTION_DRAG_STARTED:
                break;

            //the drag point has entered the bounding box of the View
            case DragEvent.ACTION_DRAG_ENTERED:
                ((ImageView) view).setColorFilter(Color.YELLOW);
                break;

            //the user has moved the drag shadow outside the bounding box of the View
            case DragEvent.ACTION_DRAG_EXITED:
                ((ImageView) view).clearColorFilter();
                break;

            //drag shadow has been released,the drag point is within the bounding box of the View
            case DragEvent.ACTION_DROP:
                GridView targetGrid = (GridView) view.getParent();
                int index = targetGrid.indexOfChild(view);

                int droppedStartIndex;
                if (targetGrid.equals(mSourceGridView)) {
                    droppedStartIndex = mPuzzle.getSize();
                } else {
                    droppedStartIndex = 0;
                }
                mPuzzle.setTile(droppedStartIndex, index, mDraggedTile);
                ((BoardAdapter) targetGrid.getAdapter()).notifyDataSetChanged();

                // validate the board now the tile has been dropped
                if (mPuzzle.isSolved()) {
                    puzzleSolvedActions();
                }
                break;

            //the drag and drop operation has concluded.
            case DragEvent.ACTION_DRAG_ENDED:
                if (!event.getResult()) {
                    // tile dropped in an invalid area of the screen; put it back
                    mPuzzle.setTile(mDraggedTileStartingX, mDraggedTilePosition, mDraggedTile);
                    ((BoardAdapter) mTargetGridView.getAdapter()).notifyDataSetChanged();
                    ((BoardAdapter) mSourceGridView.getAdapter()).notifyDataSetChanged();
                }
                break;
            default:
                break;
        }
        return true;
    }

    @Override
    public void onBackPressed() {
        // confirm quitting the game
        if (mPuzzle.getState().equals(PuzzleState.IN_PROGRESS)) {
            pauseTimer();
            DialogInterface.OnClickListener dialogClickListener =
                    new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int selection) {
                            switch (selection) {
                                case DialogInterface.BUTTON_POSITIVE:
                                    finish();
                                    break;
                                case DialogInterface.BUTTON_NEGATIVE: // fall-through
                                default:
                                    resumeTimer();
                                    break;
                            }
                        }
                    };

            AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setMessage(getResources().getString(R.string.dialog_quit_game_prompt));
            builder.setPositiveButton(getResources().getString(R.string.dialog_ok),
                    dialogClickListener);
            builder.setNegativeButton(getResources().getString(R.string.dialog_cancel),
                    dialogClickListener);
            builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
                @Override
                public void onCancel(DialogInterface dialog) {
                    resumeTimer();
                }
            });
            builder.show();
        } else {
            finish();
        }
    }

    /**
     * Perform necessary actions when puzzle is solved.
     */
    private void puzzleSolvedActions() {
        mTimer.stop();
        mPuzzle.setState(PuzzleState.COMPLETED);

        // set the tiles to not be draggable
        mTargetGridView.setOnTouchListener(null);

        showPuzzleSolvedToast();
        updateHighScores();
        hideStartingBoard();
        showButtonsOnCompleted();
    }

    /**
     * Show the buttons for new game and high scores
     */
    private void showButtonsOnCompleted() {
        LinearLayout buttons = (LinearLayout) findViewById(R.id.puzzle_buttons);
        Animation fadeIn = AnimationUtils.loadAnimation(this, android.R.anim.fade_in);
        fadeIn.setDuration(Constants.BUTTONS_FADE_IN_MS);
        buttons.startAnimation(fadeIn);
        buttons.setVisibility(View.VISIBLE);
    }

    /**
     * Hide the arrow and the starting grid
     */
    private void hideStartingBoard() {
        ImageView arrowImg = (ImageView) findViewById(R.id.arrow);
        Animation out = AnimationUtils.loadAnimation(this, R.anim.fade_out);
        arrowImg.startAnimation(out);
        arrowImg.setVisibility(View.INVISIBLE);
        GridView sourceGrid = (GridView) findViewById(R.id.source_board);
        sourceGrid.startAnimation(out);
        sourceGrid.setVisibility(View.INVISIBLE);
    }

    /**
     * Show a Toast notification that the puzzle has been solved
     */
    private void showPuzzleSolvedToast() {
        Toast toast = new Toast(getApplicationContext());
        toast.setGravity(Gravity.CENTER_VERTICAL, 0, 0);
        toast.setDuration(Toast.LENGTH_SHORT);
        toast.setView(getLayoutInflater().inflate(R.layout.victory_toast, null));
        toast.show();
    }

    /**
     * Add a new score to the scores database if it meets the criteria for a high score.
     * Uses the time from the timer as a basis for the score.
     */
    private void updateHighScores() {
        HiScoreManager highScores = new HiScoreManager();
        highScores.setContext(this);

        TetravexApp.getApplication(this).inject(highScores);
        String timerValue = mTimer.getText().toString();
        highScores.addScore(mPuzzle.getSize(), timerValue);
    }

    /**
     * Perform the necessary actions when the pause button is clicked
     *
     * @param view the View that was clicked
     */
    public void pauseButtonClicked(View view) {
        if (mPuzzle.getState().equals(PuzzleState.IN_PROGRESS)) {
            pauseTimer();
        }
        Intent intent = new Intent(this, PauseActivity.class);
        startActivityForResult(intent, 1);
    }

    /**
     * Stop the timer and save its state to resume later
     */
    private void pauseTimer() {
        mTimer.stop();
        mPausedTime = SystemClock.elapsedRealtime() - mTimer.getBase();
        mPuzzle.setState(PuzzleState.PAUSED);
    }

    /**
     * Re-start the timer from where it was left
     */
    private void resumeTimer() {
        mPuzzle.setState(PuzzleState.IN_PROGRESS);
        mTimer.setBase(SystemClock.elapsedRealtime() - mPausedTime);
        mTimer.start();
    }

    /**
     * Start a new game at the user's request. Removes the current activity from the back stack.
     *
     * @param view the View that was clicked
     */
    public void puzzleNewGameButtonClicked(View view) {
        Intent intent = new Intent(this, PuzzleActivity.class);
        startActivity(intent);
        finish();
    }

    /**
     * Show the high scores at the user's request
     *
     * @param view the View that was clicked
     */
    public void puzzleScoresButtonClicked(View view) {
        Intent intent = new Intent(this, HiScoreActivity.class);
        startActivity(intent);
    }
}




Java Source Code List

com.github.tetravex_android.ApplicationTest.java
com.github.tetravex_android.Constants.java
com.github.tetravex_android.TetravexApp.java
com.github.tetravex_android.TetravexModule.java
com.github.tetravex_android.activity.DashboardActivity.java
com.github.tetravex_android.activity.HiScoreActivity.java
com.github.tetravex_android.activity.HowToActivity.java
com.github.tetravex_android.activity.LegalActivity.java
com.github.tetravex_android.activity.PauseActivity.java
com.github.tetravex_android.activity.PuzzleActivity.java
com.github.tetravex_android.activity.SettingsActivity.java
com.github.tetravex_android.data.BoardAdapter.java
com.github.tetravex_android.data.HiScoreManager.java
com.github.tetravex_android.domain.DatabaseBaseManager.java
com.github.tetravex_android.domain.DatabaseManager.java
com.github.tetravex_android.domain.five.FiveBaseManager.java
com.github.tetravex_android.domain.five.FiveBaseRecord.java
com.github.tetravex_android.domain.five.FiveManager.java
com.github.tetravex_android.domain.five.Five.java
com.github.tetravex_android.domain.four.FourBaseManager.java
com.github.tetravex_android.domain.four.FourBaseRecord.java
com.github.tetravex_android.domain.four.FourManager.java
com.github.tetravex_android.domain.four.Four.java
com.github.tetravex_android.domain.three.ThreeBaseManager.java
com.github.tetravex_android.domain.three.ThreeBaseRecord.java
com.github.tetravex_android.domain.three.ThreeManager.java
com.github.tetravex_android.domain.three.Three.java
com.github.tetravex_android.domain.two.TwoBaseManager.java
com.github.tetravex_android.domain.two.TwoBaseRecord.java
com.github.tetravex_android.domain.two.TwoManager.java
com.github.tetravex_android.domain.two.Two.java
com.github.tetravex_android.fragment.HiScoreTableFragment.java
com.github.tetravex_android.fragment.HowToFragment.java
com.github.tetravex_android.fragment.PageFragmentFactory.java
com.github.tetravex_android.game.Puzzle.java
com.github.tetravex_android.game.Score.java
com.github.tetravex_android.game.Tile.java