Moving to Android Studio and Gradle.

This commit is contained in:
2014-12-02 18:55:57 +04:00
parent 6f4d9f2b4d
commit 6c59f740f0
79 changed files with 404 additions and 146 deletions

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.dyndns.vahagn.sokoban"
android:versionCode="0x00010001"
android:versionName="@string/git_version">
<uses-sdk android:minSdkVersion="10"
android:targetSdkVersion="14"/>
<application android:label="@string/app_name"
android:icon="@drawable/icon"
android:name="org.dyndns.vahagn.sokoban.App"
android:theme="@android:style/Theme">
<activity android:name="org.dyndns.vahagn.sokoban.menu.MainActivity"
android:label="@string/app_name"
android:configChanges="keyboardHidden|orientation"
android:screenOrientation="portrait" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="org.dyndns.vahagn.sokoban.play.PlayActivity"
android:label="@string/play_activity"
android:configChanges="keyboardHidden|orientation"
android:screenOrientation="portrait" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.dyndns.vahagn.sokoban.MainMenu" />
</activity>
</application>
</manifest>

View File

@@ -0,0 +1,125 @@
package org.dyndns.vahagn.sokoban;
import android.app.Application;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.util.Log;
public class App extends Application
{
public static final String TAG = "Sokoban";
public static final String PREFS = "org.dyndns.vahagn.sokoban.prefs";
public static final int MIN_LEVEL = 1;
private static App mApp = null;
protected PuzzleContainer pc;
protected int current_level;
protected int achieved_level;
protected int max_level;
protected SharedPreferences prefs;
protected SharedPreferences.Editor prefsEdit;
/*
*
*/
@Override
public void onCreate()
{
super.onCreate();
mApp = this;
pc = new PuzzleContainer();
prefs = getSharedPreferences(PREFS, MODE_PRIVATE);
prefsEdit = prefs.edit();
max_level = pc.getCount();
current_level = prefs.getInt("current_level", MIN_LEVEL);
achieved_level = prefs.getInt("achieved_level", MIN_LEVEL+10);
}
//
// This is a singleton object.
//
public static App theApp()
{
return mApp;
}
//
// Puzzles.
//
public Puzzle getPuzzle( int level )
{
return pc.getPuzzle( level-1 );
}
public Puzzle getCurrentPuzzle()
{
return getPuzzle( getCurrentLevel() );
}
public Puzzle getPrevPuzzle()
{
if ( current_level != MIN_LEVEL )
return getPuzzle( getCurrentLevel()-1 );
else
return null;
}
public Puzzle getNextPuzzle()
{
if ( current_level != achieved_level )
return getPuzzle( getCurrentLevel()+1 );
else
return null;
}
//
// Provide amount of puzzles.
//
public final int getPuzzleCount()
{
return max_level;
}
//
// Provide highest solved puzzle.
//
public final int getAchivedLevel()
{
return achieved_level;
}
//
// Provide last played puzzle.
//
public final int getCurrentLevel()
{
return current_level;
}
//
// Set the current puzzle level.
//
public void setCurrentLevel( int l )
{
if ( l > achieved_level )
l = MIN_LEVEL;
if ( l < MIN_LEVEL )
l = achieved_level;
if ( l != current_level )
{
current_level = l;
prefsEdit.putInt("current_level", current_level);
prefsEdit.apply();
if ( !prefs.edit().commit() )
Log.d(TAG, "prefs.edit().commit() failed.");
}
}
//
// Set achieved level.
//
public void advanceAchivedLevel( int l )
{
if ( l > max_level )
l = max_level;
if ( l > achieved_level )
{
achieved_level = l;
prefsEdit.putInt("achieved_level", achieved_level);
prefsEdit.apply();
if ( !prefs.edit().commit() )
Log.d(TAG, "prefs.edit().commit() failed.");
}
}
}

View File

@@ -0,0 +1,404 @@
package org.dyndns.vahagn.sokoban;
//import java.lang.Exception;
import java.io.*;
import static java.lang.Math.*;
import android.util.Log;
import android.graphics.Point;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import static org.dyndns.vahagn.sokoban.App.TAG;
public class Puzzle
{
public final static int FLOOR = 0x0;
public final static int GOAL = 0x1;
public final static int FLOOR_MASK = ~(GOAL | FLOOR);
public final static int WALL = 0x2;
public final static int BOX = 0x4;
public final static int BINGO = BOX | GOAL;
public final static int WORKER = 0x8;
public final static int WORKER_NORTH = 0x00 | WORKER;
public final static int WORKER_SOUTH = 0x10 | WORKER;
public final static int WORKER_WEST = 0x20 | WORKER;
public final static int WORKER_EAST = 0x30 | WORKER;
public final static int WORKER_MASK = WORKER_EAST | WORKER_WEST | WORKER_SOUTH | WORKER_NORTH;
public final static int SELECTED = 0x40;
public final static int MAX_COUNT = 0x80-1;
protected int columns;
protected int rows;
protected int box_count;
protected int [] board;
protected int worker_x;
protected int worker_y;
protected int selected_x;
protected int selected_y;
protected int bingos;
public Puzzle( InputStream is )
{
load( is );
}
//////////////////////////////////////////////////////////////////////////
//
// Low level helper functions.
//
public final int getColumnCount()
{
return columns;
}
public final int getRowCount()
{
return rows;
}
public static int getSymCount()
{
return MAX_COUNT;
}
public final int getIndex( int x, int y )
{
return y*columns+x;
}
public final int getX( int idx )
{
return idx % columns;
}
public final int getY( int idx )
{
return idx / columns;
}
public final int getSym( int x, int y )
{
return board[getIndex(x,y)];
}
public void setSym( int x, int y, int v )
{
board[getIndex(x,y)]=v;
}
public static boolean isEmpty( int v )
{
return (v & FLOOR_MASK) == 0;
}
public static boolean hasBox( int v )
{
return (v & BOX) != 0;
}
public static boolean hasWorker( int v )
{
return (v & WORKER) != 0;
}
public final int getWorkerX()
{
return worker_x;
}
public final int getWorkerY()
{
return worker_y;
}
public final boolean isSelected()
{
return selected_x != -1;
}
public final boolean isSelected( int x, int y )
{
return selected_x == x && selected_y == y;
}
public final int getSelectedX()
{
return selected_x;
}
public final int getSelectedY()
{
return selected_y;
}
public final boolean isValid( int x, int y )
{
return ( 0 <= x && 0 <= y
&& x < getColumnCount()
&& y < getRowCount() );
}
public final boolean isOneStep( int x, int y )
{
return isValid(x,y)
&& (abs(worker_x-x )+abs(worker_y-y) == 1);
}
public final boolean isDone()
{
return bingos == box_count;
}
//////////////////////////////////////////////////////////////////////////
//
// Loading.
//
private void load( InputStream is )
{try{
columns = is.read();
rows = is.read();
box_count = 0;
bingos = 0;
selected_x = -1;
selected_y = -1;
board = new int[columns*rows];
byte [] b = new byte [columns*rows];
is.read( b, 0, columns*rows);
for( int i = 0; i < b.length; ++i )
{
switch( b[i] )
{
case 0:
board[i] = FLOOR;
break;
case 1:
board[i] = WALL;
break;
case 2:
board[i] = BOX;
++box_count;
break;
case 3:
board[i] = GOAL;
break;
case 4:
board[i] = BINGO;
++box_count;
++bingos;
break;
case 5:
board[i] = WORKER;
worker_x = getX(i);
worker_y = getY(i);
break;
default:
}
}
}
catch ( Exception e )
{
Log.d( TAG, "load()", e);
}}
//////////////////////////////////////////////////////////////////////////
//
// Move worker.
//
public boolean walk( int x, int y)
{
//
// Check that this is one step away.
//
if ( !isOneStep(x,y) )
return false;
//
// Check that this is empty space.
//
int v = getSym(x,y);
if ( !isEmpty(v) )
return false;
//
// If something is selected then unselect first.
//
if ( isSelected() )
unselect();
//
// Find direction to move.
//
int worker;
if ( worker_x < x )
worker = WORKER_WEST;
else if ( worker_x > x )
worker = WORKER_EAST;
else if ( worker_y > y )
worker = WORKER_SOUTH;
else
worker = WORKER_NORTH;
//
// Move worker marker from current position to asked position.
//
setSym(worker_x,worker_y,getSym(worker_x,worker_y)&~WORKER_MASK);
worker_x =x;
worker_y =y;
setSym(worker_x,worker_y,getSym(worker_x,worker_y)|worker);
return true;
}
public boolean select( int x, int y)
{
//
// Check that this is one step away.
//
if ( !isOneStep(x,y) )
return false;
//
// Check that this is empty space.
//
int v = getSym(x,y);
if ( !hasBox(v) )
return false;
//
// If something is selected then unselect first.
//
if ( isSelected() )
unselect();
//
// Find direction to move.
//
int worker;
if ( worker_x < x )
worker = WORKER_WEST;
else if ( worker_x > x )
worker = WORKER_EAST;
else if ( worker_y > y )
worker = WORKER_SOUTH;
else
worker = WORKER_NORTH;
//
// Move worker marker from current position to asked position.
//
selected_x =x;
selected_y =y;
setSym(worker_x,worker_y,getSym(worker_x,worker_y)&~WORKER_MASK|worker|SELECTED);
setSym(selected_x,selected_y,getSym(selected_x,selected_y)|SELECTED);
return true;
}
public boolean unselect()
{
//
// If not selected then do nothing.
//
if ( !isSelected() )
return false;
//
// Move worker marker from current position to asked position.
//
setSym(worker_x,worker_y,getSym(worker_x,worker_y)&~SELECTED);
setSym(selected_x,selected_y,getSym(selected_x,selected_y)&~SELECTED);
selected_x =-1;
selected_y =-1;
return true;
}
public boolean push(int x, int y)
{
//
// If not selected then do nothing.
//
if ( !isSelected() )
return false;
//
// Check that we go to the selected direction.
//
if ( selected_x != x && selected_y != y )
return false;
//
// Check that this is a box that we move.
//
int v = getSym(x,y);
if ( !hasBox(v) )
return false;
//
// Check that the next space to the box is empty.
//w - 2*(w-x) = 2x -w
int next_x = 2*x - worker_x;
int next_y = 2*y - worker_y;
int next_v = getSym(next_x,next_y);
if ( !isEmpty(next_v) )
return false;
//
// Ok, looks we can move the box. Do it actually.
//
unselect();
setSym(x,y,getSym(x,y)&~BOX);
setSym(next_x,next_y,getSym(next_x,next_y)|BOX);
walk(x,y);
select(next_x,next_y);
//
// Keep track of box count in place.
//
if ( (v & GOAL) != 0 )
--bingos;
if ( (next_v & GOAL) != 0 )
++bingos;
return true;
}
//////////////////////////////////////////////////////////////////////////
//
// Undo/Redo.
//
protected class State
{
public int [] board;
public int worker_x;
public int worker_y;
public int selected_x;
public int selected_y;
public int bingos;
public State(Puzzle puzzle)
{
board = puzzle.board.clone();
worker_x = puzzle.worker_x;
worker_y = puzzle.worker_y;
selected_x = puzzle.selected_x;
selected_y = puzzle.selected_y;
bingos = puzzle.bingos;
}
public void restore(Puzzle puzzle)
{
puzzle.board = board;
puzzle.worker_x = worker_x;
puzzle.worker_y = worker_y;
puzzle.selected_x = selected_x;
puzzle.selected_y = selected_y;
puzzle.bingos = bingos;
}
}
protected LinkedList<State> undoStack = new LinkedList<State>();
//
// Save this state to be able to restore later.
//
public void save()
{
State s = new State(this);
undoStack.addFirst(s);
}
//
// Restore state.
//
public void restore()
{
State s = undoStack.removeFirst();
if ( s != null )
s.restore(this);
}
//
// Check if there are items in the undo stack.
//
public final boolean isUndoable()
{
return undoStack.size() != 0;
}
}

View File

@@ -0,0 +1,75 @@
package org.dyndns.vahagn.sokoban;
import java.io.InputStream;
import java.io.IOException;
import static org.dyndns.vahagn.sokoban.App.theApp;
import android.util.Log;
public class PuzzleContainer
{
protected int count;
/*
* Load puzzle from the puzzle.bin.
*/
public Puzzle getPuzzle( int i )
{try{
InputStream is = theApp().getResources().openRawResource(R.raw.puzzles);
//
// Read amount of puzzles.
//
count = read_i32(is);
if ( i >= count )
return null;
//
// Read the offset.
//
is.skip( i*4 );
int offset = read_i32(is);
//
// Jump to the offset and read the puzzle.
//
is.skip( offset - 4 - (i+1)*4);
Puzzle p = new Puzzle( is );
//
// Finally return the puzzle and we are done.
//
return p;
}
catch ( java.lang.Exception e )
{
//Log.d(TAG, "Exception: " + e.getMessage() );
e.printStackTrace();
return null;
}}
//
// Retrive amount of puzzles.
//
public int getCount()
{try{
if ( count == 0 )
{
InputStream is = theApp().getResources().openRawResource(R.raw.puzzles);
count = read_i32(is);
}
return count;
}
catch ( java.lang.Exception e )
{
//Log.d(TAG, "Exception: " + e.getMessage() );
e.printStackTrace();
return 0;
}}
//
// Helper function to read little endian 32bit integer.
//
protected int read_i32( InputStream is ) throws IOException
{
int i = is.read();
i |= is.read() << 8;
i |= is.read() << 16;
i |= is.read() << 24;
return i;
}
}

View File

@@ -0,0 +1,134 @@
package org.dyndns.vahagn.sokoban.menu;
import org.dyndns.vahagn.sokoban.play.PlayActivity;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.view.View;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.util.TypedValue;
import android.view.Display;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.BaseAdapter;
import android.widget.GridView;
import android.widget.ImageView;
import org.dyndns.vahagn.sokoban.R;
import static org.dyndns.vahagn.sokoban.App.theApp;
public class MainActivity extends FragmentActivity
{
protected final String TAG = "SokobanMenu";
protected final String LEVEL= "org.dyndns.vahagn.sokoban.LEVEL";
protected MainFragment mainFragment;
protected PuzzleListFragment puzzleListFragment;
protected float puzzleListXScroll;
protected float puzzleListXScrollMax;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState)
{
requestWindowFeature(Window.FEATURE_NO_TITLE);
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
mainFragment = new MainFragment();
puzzleListFragment = new PuzzleListFragment();
puzzleListFragment.setOnPuzzleListAction(new PuzzleListFragment.OnPuzzleListAction() {
public boolean onPuzzleGridDown() {
return onPuzzleGridDown1();
}
public boolean onPuzzleGridXScroll(float x) {
return onPuzzleGridXScroll1(x);
}
public void onPuzzleLevelClicked(int level) {
onPuzzleClicked1(level);
}
});
puzzleListXScrollMax = getWindowManager().getDefaultDisplay().getWidth();
//
// Set initila fragment.
//
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.fragment_container, mainFragment);
// fragmentTransaction.add(R.id.fragment_container, puzzleListFragment);
fragmentTransaction.commit();
}
//@Override
public void onConfigurationChanged()
{
// super.onConfigurationChanged();
// setContentView(R.layout-l.menu);
}
public void onStartCurrentPuzzle(View v)
{
Intent intent = new Intent(this, PlayActivity.class);
startActivity( intent );
}
public void onPuzzleListShow(View v)
{
getSupportFragmentManager()
.beginTransaction()
.setCustomAnimations(R.animator.fragment_slide_left_enter,
R.animator.fragment_slide_left_exit)
.replace(R.id.fragment_container, puzzleListFragment)
.commit();
}
public boolean onPuzzleGridDown1()
{
puzzleListXScroll = 0;
return true;
}
public boolean onPuzzleGridXScroll1( float x )
{
puzzleListXScroll += x;
/*
getSupportFragmentManager()
.beginTransaction()
.setCustomAnimations(R.animator.fragment_slide_right_enter,
R.animator.fragment_slide_right_exit)
.commit();
*/
if ( -puzzleListXScroll/puzzleListXScrollMax > 0.5 )
{
getSupportFragmentManager()
.beginTransaction()
.setCustomAnimations(R.animator.fragment_slide_right_enter,
R.animator.fragment_slide_right_exit)
.replace(R.id.fragment_container, mainFragment)
.commit();
}
return true;
}
public void onPuzzleClicked1( int level )
{
if ( level <= theApp().getAchivedLevel() )
{
theApp().setCurrentLevel(level);
Intent intent = new Intent(this, PlayActivity.class);
startActivity( intent );
}
}
}

View File

@@ -0,0 +1,39 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.dyndns.vahagn.sokoban.menu;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import static org.dyndns.vahagn.sokoban.App.theApp;
import org.dyndns.vahagn.sokoban.R;
/**
*
* @author vahagnk
*/
public class MainFragment extends Fragment
{
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle icicle)
{
super.onCreate(icicle);
View v = inflater.inflate(R.layout.main_fragment, container, false);
if ( theApp().getAchivedLevel() == 1 )
((Button)v.findViewById(R.id.btn_start)).setText(R.string.btn_start_begin);
//
// Set Footer.
//
String str = getString(R.string.copyright) + "\n"
+getString(R.string.version_tag) + " " + getString(R.string.git_version);
((TextView)v.findViewById(R.id.footer_txt)).setText(str);
return v;
}
}

View File

@@ -0,0 +1,187 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.dyndns.vahagn.sokoban.menu;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.TypedValue;
import android.view.GestureDetector;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.TextView;
import org.dyndns.vahagn.sokoban.R;
import org.dyndns.vahagn.sokoban.R;
import static org.dyndns.vahagn.sokoban.App.theApp;
import org.dyndns.vahagn.sokoban.play.PlayActivity;
/**
*
*/
public class PuzzleListFragment extends Fragment
{
protected GridView puzzleGrid;
protected PuzzlesAdapter puzzleGridAdapter;
protected GestureDetector gridViewXScrollDetector;
protected OnPuzzleListAction actionListener;
/*
* These are events which the fragments delegates to activity.
*/
public interface OnPuzzleListAction
{
public boolean onPuzzleGridDown();
public boolean onPuzzleGridXScroll( float x );
public void onPuzzleLevelClicked( int level );
}
public void setOnPuzzleListAction( OnPuzzleListAction l )
{
actionListener = l;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle icicle)
{
super.onCreate(icicle);
View v = inflater.inflate(R.layout.puzzle_list_fragment, container, false);
//
// Configure GridView.
//
puzzleGrid = (GridView)v.findViewById(R.id.puzzle_grid);
puzzleGridAdapter = new PuzzlesAdapter(getActivity());
puzzleGrid.setAdapter( puzzleGridAdapter );
puzzleGrid.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v, int position, long id)
{
onPuzzleClicked(v,(int)id);
}
});
puzzleGrid.setOnTouchListener( new View.OnTouchListener() {
public boolean onTouch(View view, MotionEvent me) {
gridViewXScrollDetector.onTouchEvent( me );
return false;
}
});
gridViewXScrollDetector = new GestureDetector(new GestureDetector.SimpleOnGestureListener() {
public boolean onDown(MotionEvent e) {
return (actionListener!=null) ? actionListener.onPuzzleGridDown() : false;
}
public boolean onScroll(MotionEvent e1, MotionEvent e2, float dx, float dy) {
return (actionListener!=null) ? actionListener.onPuzzleGridXScroll(dx) : false;
}
});
return v;
}
@Override
public void onResume()
{
super.onResume();
if ( puzzleGrid != null )
puzzleGridAdapter.notifyDataSetChanged();
}
public void onPuzzleClicked(View v, int level )
{
if (actionListener!=null)
actionListener.onPuzzleLevelClicked(level);
}
public class PuzzlesAdapter extends BaseAdapter
{
private Context context;
private int icon_size;
private int text_x;
private int text_y;
private Bitmap lock_icon;
private Bitmap unlock_icon;
private Paint paint;
public PuzzlesAdapter(Context c)
{
context = c;
icon_size = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_MM, 10, getResources().getDisplayMetrics() );
int text_size = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_MM, 4, getResources().getDisplayMetrics() );
text_x = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_MM, 9, getResources().getDisplayMetrics() );
text_y = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_MM, 9, getResources().getDisplayMetrics() );
Bitmap lock_icon_tmp = BitmapFactory.decodeResource( getResources(), R.drawable.lock );
lock_icon = Bitmap.createBitmap(icon_size,icon_size,Bitmap.Config.ARGB_8888);
Canvas lock_canvas = new Canvas(lock_icon);
lock_canvas.drawBitmap(lock_icon_tmp,
new Rect(0,0,lock_icon_tmp.getWidth()-1,lock_icon_tmp.getHeight()-1),
new Rect(0,0,icon_size, icon_size),
null);
Bitmap unlock_icon_tmp = BitmapFactory.decodeResource( getResources(), R.drawable.unlock );
unlock_icon = Bitmap.createBitmap(icon_size,icon_size,Bitmap.Config.ARGB_8888);
Canvas unlock_canvas = new Canvas(unlock_icon);
unlock_canvas.drawBitmap(unlock_icon_tmp,
new Rect(0,0,unlock_icon_tmp.getWidth()-1,unlock_icon_tmp.getHeight()-1),
new Rect(0,0,icon_size, icon_size),
null);
paint = new Paint();
paint.setColor( Color.WHITE );
paint.setTextSize( text_size );
paint.setTextAlign(Paint.Align.RIGHT);
}
public int getCount()
{
return theApp().getPuzzleCount();
}
public Object getItem(int position)
{
return null;
}
public long getItemId(int position)
{
return position+1;
}
// create a new ImageView for each item referenced by the Adapter
public View getView(int position, View convertView, ViewGroup parent)
{
ImageView imageView;
if (convertView == null)
{ // if it's not recycled, initialize some attributes
imageView = new ImageView(context);
imageView.setLayoutParams(new GridView.LayoutParams(icon_size, icon_size));
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
}
else
imageView = (ImageView) convertView;
int id = (int)getItemId(position);
Bitmap icon = ( id <= theApp().getAchivedLevel() )
? unlock_icon.copy(Bitmap.Config.ARGB_8888,true)
: lock_icon.copy(Bitmap.Config.ARGB_8888,true);
Canvas canvas = new Canvas(icon);
canvas.drawText( Integer.toString(id), text_x, text_y, paint);
imageView.setImageBitmap(icon);
return imageView;
}
}
}

View File

@@ -0,0 +1,209 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.dyndns.vahagn.sokoban.play;
import org.dyndns.vahagn.sokoban.play.PuzzleView;
import android.support.v4.view.ViewCompat;
import java.util.LinkedList;
/**
*
* @author vahagnk
*/
public class Animator implements Runnable
{
protected PuzzleView view;
protected LinkedList<Action> actions = new LinkedList<Action>();
protected boolean stop;
protected int step;
protected Action currentAction;
protected AnimationLister theLister;
public interface AnimationLister
{
public void onAnimationEnd();
}
public Animator( PuzzleView v)
{
view = v;
}
public void setAnimationLister( AnimationLister lister )
{
theLister = lister;
}
public void queue( Action a )
{
actions.addLast(a);
}
public void play()
{
//
// If no actions exist then nothing to do.
//
if ( actions.size() == 0 )
return;
//
// Get the first action an play it.
//
stop = false;
step = 0;
currentAction = actions.removeFirst();
ViewCompat.postOnAnimation(view, this);
}
public void stop()
{
stop = true;
view.removeCallbacks(this);
if ( theLister != null )
theLister.onAnimationEnd();
}
public void clear()
{
actions.clear();
currentAction = null;
}
public void run()
{
if ( currentAction == null )
return;
else if ( currentAction.steps > step )
{
currentAction.intermediate(this,step);
step++;
ViewCompat.postOnAnimation(view, this);
}
else
{
currentAction.last(this);
step = 0;
currentAction = null;
if ( !stop && actions.size() > 0 )
{
currentAction = actions.removeFirst();
step = 0;
ViewCompat.postOnAnimation(view, this);
}
else
{
if ( theLister != null )
theLister.onAnimationEnd();
}
}
}
public static abstract class Action
{
public int steps;
public Action()
{
steps = 0;
}
public abstract void intermediate( Animator ap, int step );
public abstract void last( Animator ap );
}
protected static abstract class XYAction extends Action
{
int x;
int y;
public XYAction( int _x, int _y )
{
x = _x;
y = _y;
}
}
public static class Move extends XYAction
{
public Move( int x, int y )
{
super(x,y);
}
public void intermediate( Animator ap, int step )
{}
public void last( Animator ap )
{
ap.view.getPuzzle().walk(x,y);
ap.view.invalidate();
}
}
public static class Push extends XYAction
{
public Push( int x, int y )
{
super(x,y);
}
public void intermediate( Animator ap, int step )
{
}
public void last( Animator ap )
{
ap.view.getPuzzle().push(x,y);
ap.view.invalidate();
}
}
public static class Select extends XYAction
{
public Select(int x, int y )
{
super(x,y);
}
public void intermediate( Animator ap, int step )
{
}
public void last( Animator ap )
{
ap.view.getPuzzle().select(x,y);
ap.view.invalidate();
}
}
public static class Unselect extends Action
{
public Unselect()
{}
public void intermediate( Animator ap, int step )
{}
public void last( Animator ap )
{
ap.view.getPuzzle().unselect();
ap.view.invalidate();
}
}
public static class NoMove extends XYAction
{
public NoMove( int x, int y )
{
super(x,y);
}
public void intermediate( Animator ap, int step )
{}
public void last( Animator ap )
{}
}
}

View File

@@ -0,0 +1,220 @@
package org.dyndns.vahagn.sokoban.play;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentActivity;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
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.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.AbsListView;
import android.widget.FrameLayout;
import android.widget.TextView;
import java.util.Timer;
import java.util.TimerTask;
import java.util.zip.Inflater;
import org.dyndns.vahagn.sokoban.App;
import static org.dyndns.vahagn.sokoban.App.TAG;
import static org.dyndns.vahagn.sokoban.App.theApp;
import org.dyndns.vahagn.sokoban.R;
import org.dyndns.vahagn.sokoban.Puzzle;
import org.dyndns.vahagn.sokoban.menu.MainActivity;
public class PlayActivity extends FragmentActivity
implements PuzzleControl.PuzzleControlLister
{
protected final static int RESET_ITEM = 1;
protected final static int UNDO_ITEM = 2;
public Puzzle puzzle = null;
public PuzzleControl puzzle_view;
View title_view;
public TextView title_text;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
//
// No title.
//
requestWindowFeature(Window.FEATURE_NO_TITLE);
//
// Create and set up the puzzle_view.
//
puzzle_view = new PuzzleControl( this );
setContentView( puzzle_view );
puzzle_view.setPuzzleControlLister( this );
//
// Create action bar.
//
FrameLayout rootLayout = (FrameLayout)findViewById(android.R.id.content);
View.inflate(this, R.layout.puzzle_view_title, rootLayout);
title_text = (TextView)findViewById(R.id.title_text);
title_view = findViewById(R.id.puzzle_view_title_layout);
//
// Load the puzzle.
//
loadCurrentPuzzle();
}
public void onSolved()
{
//
// Advance current level and achieved level.
//
final int nextl = theApp().getCurrentLevel()+1;
theApp().advanceAchivedLevel(nextl);
theApp().setCurrentLevel(nextl);
//
// Bring a dialog for user to choose to continue or stop.
//
DialogFragment df = new DialogFragment()
{
public Dialog onCreateDialog(Bundle savedInstanceState)
{
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle("Congratulations! You Won!");
builder.setMessage( "Would you like to try the next puzzle?" );
builder.setNegativeButton("Enough",
new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface d, int w)
{
finish();
}
});
builder.setPositiveButton("Please",
new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface d, int w)
{
loadCurrentPuzzle();
puzzle_view.invalidate();
}
});
// Create the AlertDialog object and return it
Dialog dlg = builder.create();
dlg.setCancelable(false);
dlg.setCanceledOnTouchOutside(false);
return dlg;
}
};
df.setCancelable(false);
df.show(getSupportFragmentManager(), null);
//dlg.show();
}
public void onLongPress()
{
if ( title_view.getVisibility() != View.VISIBLE )
showTitle();
}
public void onTouch()
{
if ( title_view.getVisibility() == View.VISIBLE )
hideTitle();
}
public void showTitle()
{
Animation anim = AnimationUtils.loadAnimation(this, R.animator.puzzle_action_bar_enter);
title_view.startAnimation(anim);
title_view.setVisibility(View.VISIBLE);
}
public void hideTitle()
{
Animation anim = AnimationUtils.loadAnimation(this, R.animator.puzzle_action_bar_exit);
title_view.startAnimation(anim);
title_view.setVisibility(View.INVISIBLE);
}
public boolean onReset( View v )
{
while ( puzzle.isUndoable() )
puzzle.restore();
puzzle_view.invalidate();
return true;
}
//
// Go to previouse puzzle.
//
public boolean onPrev( View v )
{
//
// If current level is less than achived level the move to next
// puzzle.
//
if ( theApp().getCurrentLevel() > App.MIN_LEVEL )
{
theApp().setCurrentLevel( theApp().getCurrentLevel()-1 );
loadCurrentPuzzle();
puzzle_view.invalidate();
}
return true;
}
//
// Go to next puzzle.
//
public boolean onNext( View v )
{
//
// If current level is less than achived level the move to next
// puzzle.
//
if ( theApp().getCurrentLevel() < theApp().getAchivedLevel() )
{
theApp().setCurrentLevel( theApp().getCurrentLevel()+1 );
loadCurrentPuzzle();
puzzle_view.invalidate();
}
return true;
}
//
// Undo last action.
//
public boolean onUndo( View v )
{
if ( puzzle.isUndoable() )
{
puzzle.restore();
puzzle_view.invalidate();
return true;
}
else
return false;
}
private void loadCurrentPuzzle()
{
puzzle = theApp().getCurrentPuzzle();
puzzle_view.setPuzzle(puzzle);
updateTitle();
}
public void updateTitle()
{
String title = "Level " + new Integer(theApp().getCurrentLevel()).toString();
setTitle(title);
}
public void setTitle(CharSequence title)
{
super.setTitle(title);
if (title_text != null) {
title_text.setText(title);
}
}
}

View File

@@ -0,0 +1,180 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.dyndns.vahagn.sokoban.play;
import org.dyndns.vahagn.sokoban.play.Animator;
import org.dyndns.vahagn.sokoban.play.PuzzleView;
import org.dyndns.vahagn.sokoban.play.PuzzleLogic;
import android.content.Context;
import android.graphics.Point;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import org.dyndns.vahagn.sokoban.Puzzle;
import static org.dyndns.vahagn.sokoban.App.TAG;
//import android.support.v4.view.GestureDetectorCompat;
/**
*
* @author vahagnk
*/
public class PuzzleControl extends PuzzleView
implements GestureDetector.OnGestureListener
, GestureDetector.OnDoubleTapListener
, ScaleGestureDetector.OnScaleGestureListener
, Animator.AnimationLister
{
protected GestureDetector simpled;
protected ScaleGestureDetector scaled;
protected PuzzleLogic logic;
protected Animator animator;
protected PuzzleControlLister lister;
protected float lastSpan;
protected Point singleTapTile = new Point();
public interface PuzzleControlLister
{
public void onSolved();
public void onLongPress();
public void onTouch();
}
public PuzzleControl(Context c)
{
super(c);
logic = new PuzzleLogic();
animator = new Animator( this );
animator.setAnimationLister(this);
simpled = new GestureDetector( c, this );
simpled.setOnDoubleTapListener(this);
scaled = new ScaleGestureDetector( c, this );
}
public void setPuzzle( Puzzle p )
{
super.setPuzzle(p);
logic.setPuzzle(p);
}
public void setPuzzleControlLister( PuzzleControlLister l )
{
lister = l;
}
@Override
public boolean onTouchEvent(MotionEvent event)
{
//
// Stop any pending animations.
//
animator.stop();
animator.clear();
//
// Pass this event to scale and gesture detectors.
//
boolean r = scaled.onTouchEvent(event);
r = simpled.onTouchEvent(event) || r;
//r = super.onTouchEvent(event) || r;
return r;
}
public boolean onDown(MotionEvent e)
{
return true;
}
public void onLongPress(MotionEvent e)
{
if ( lister != null )
lister.onLongPress();
}
public void onShowPress(MotionEvent e)
{
}
public boolean onSingleTapUp(MotionEvent e)
{
return true;
}
public boolean onDoubleTap(MotionEvent e)
{
return true;
}
public boolean onDoubleTapEvent(MotionEvent e)
{
return true;
}
public boolean onSingleTapConfirmed(MotionEvent e)
{
//
// Translate the tap point into tile column and row.
//
getTile( e.getX(), e.getY(), singleTapTile );
//
// If the tap is not on a valid tile then there is not point to
// continue.
//
if ( !puzzle.isValid(singleTapTile.x, singleTapTile.y) )
return true;
//
// Create sequence of steps and then animate it.
//
if ( logic.createSteps(animator, singleTapTile.x, singleTapTile.y) )
{
puzzle.save();
animator.play();
}
//
// Notify that we were touched.
//
if ( lister != null )
lister.onTouch();
//
// This event is processed.
//
return true;
}
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY)
{
return true;
}
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY)
{
scrollViewport( distanceX, distanceY );
return true;
}
public boolean onScaleBegin(ScaleGestureDetector detector)
{
lastSpan = scaled.getCurrentSpan();
return true;
}
public boolean onScale(ScaleGestureDetector detector)
{
float span = scaled.getCurrentSpan();
float focusX = scaled.getFocusX();
float focusY = scaled.getFocusY();
scaleViewport(focusX,focusY,lastSpan/span);
lastSpan = span;
return true;
}
public void onScaleEnd(ScaleGestureDetector detector)
{
scaleViewportDone();
}
public void onAnimationEnd()
{
if ( puzzle.isDone() )
if ( lister != null )
lister.onSolved();
}
}

View File

@@ -0,0 +1,541 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.dyndns.vahagn.sokoban.play;
import static java.lang.Math.*;
import java.util.Arrays;
import org.dyndns.vahagn.sokoban.Puzzle;
import static org.dyndns.vahagn.sokoban.App.TAG;
/**
*
* @author vahagnk
*/
public class PuzzleLogic
{
//protected PlayActivity activity;
protected Puzzle puzzle = null;
protected int [] moves;
protected int [] setOfCells;
public PuzzleLogic()
{}
public void setPuzzle( Puzzle p )
{
puzzle = p;
moves = new int[puzzle.getColumnCount()*puzzle.getRowCount()];
setOfCells = new int[2*puzzle.getColumnCount()*puzzle.getRowCount()];
}
public boolean createSteps(Animator animator, final int x, final int y )
{
//
// Check that the x,y are valid.
//
if ( !puzzle.isValid(x, y) )
return false;
//
// Now check what tile was tapped.
// If the tapped is a floor then ...
//
int tile = puzzle.getSym( x, y);
if ( Puzzle.isEmpty(tile) )
{
//
// Calculate possible moves map.
//
calcMoves();
//
// Check if worker selected a box and can push it to the location?
// If yes then we are done.
//
if ( puzzle.isSelected()
&& tryMoveBox(animator, x, y) )
return true;
//
// Either nothing was selected or the box cannot be moved to
// tapped location. Try move worker alone.
//
if ( tryMoveWorker( animator, x, y ) )
return true;
//
// Show that action is not allowed.
//
undoable( animator, x, y );
}
//
// The tapped is the worker. Try move the box. If not possible
// then unselect if selected.
//
else if ( Puzzle.hasWorker(tile) )
{
if ( !puzzle.isSelected() )
{
buzz( animator );
return true;
}
//
// Calculate possible moves map.
//
calcMoves();
//
// Check if worker selected a box and can push it to the location?
// If yes then we are done.
//
if ( tryMoveBox(animator, x, y) )
return true;
//
// If the box is not movable then unselect it.
//
unselect( animator );
}
//
// The tapped is a box.
//
else if ( Puzzle.hasBox(tile) )
{
//
// Calculate possible moves map.
//
calcMoves();
//
// If the box is selected then unselect it.
//
if ( puzzle.isSelected(x,y) )
{
unselect( animator );
return true;
}
//
// Try move the worker next to the box and select it if the
// box is not selected yet.
//
if ( trySelectBox( animator, x, y ) )
return true;
//
// Show that action is not allowed if reached till here.
//
undoable( animator, x, y );
}
return true;
}
//////////////////////////////////////////////////////////////////////////
//
// Routes to accessible cells from where worker stands.
//
protected boolean tryMoveWorker( Animator animator, int x, int y )
{
//
// If the filed is not accessable then move failed.
//
if ( !isAccessible(x,y) )
return false;
//
// First unselect box.
//
if ( puzzle.isSelected() )
unselect( animator );
//
// Get directions and queue moves accordingly.
//
int dirs[] = getDirections(x, y);
for ( int i = 0; i < dirs.length; i+=2 )
move( animator, dirs[i],dirs[i+1] );
//
// Done.
//
return true;
}
protected boolean tryMoveBox( Animator animator, int x, int y )
{
//
// If no box is selected then we cannot move no box.
//
if ( !puzzle.isSelected() )
return false;
//
// Check that asked move is orthogonal to the slected box.
//
int bx = puzzle.getSelectedX();
int by = puzzle.getSelectedY();
int dx = x-bx;
int dy = y-by;
if ( dx != 0 && dy != 0 )
return false;
//
// There is no point to continue also in case if the asked cell
// is the box.
//
if ( dx == 0 && dy == 0 )
return false;
//
// Now find the desired place for the worker to start push this
// box.
//
int wx = bx;
int wy = by;
if ( x < bx )
++wx;
else if ( x > bx )
--wx;
else if ( y < by )
++wy;
else
--wy;
//
// Check if the desired place for the worker is accessable? If not
// then there is no point to continue.
//
if ( !isAccessible(wx, wy) )
return false;
//
// Now check that all cell till x,y are empty and that we can
// push box till there.
//
int sx = bx-wx;
int sy = by-wy;
int ix = bx;
int iy = by;
while ( x != ix || y != iy )
{
ix+=sx;
iy+=sy;
int v = puzzle.getSym(ix,iy);
if ( !puzzle.isEmpty(v) && !puzzle.hasWorker(v) )
return false;
}
//
// Ok, looks we can do the desired action. Now put instructions on
// what to do. First move worker to desired position if he is not
// there already.
//
if ( wx != puzzle.getWorkerX() || wy != puzzle.getWorkerY() )
{
tryMoveWorker( animator, wx, wy );
select( animator, bx, by );
}
//
// Now create the steps to push the box.
//
ix = bx;
iy = by;
while ( x != ix || y != iy )
{
push( animator, ix, iy );
ix+=sx;
iy+=sy;
}
//
// Done
//
return true;
}
protected boolean trySelectBox( Animator animator, int x, int y )
{
int north = puzzle.getSym(x,y-1);
int south = puzzle.getSym(x,y+1);
int west = puzzle.getSym(x-1,y);
int east = puzzle.getSym(x+1,y);
//
// First check if there is a worker in a nighbour cell. If
// yes then simplly select the box. If the box is already selected
// then do nothing and if othe box is selected then unselect it first
// and then select the box.
//
if ( Puzzle.hasWorker( west )
|| Puzzle.hasWorker( east )
|| Puzzle.hasWorker( north )
|| Puzzle.hasWorker( south ) )
{
if ( !puzzle.isSelected(x,y) )
{
if ( puzzle.isSelected() )
unselect( animator );
select( animator, x, y );
}
}
//
// Otherwise, check which is of the cells is in closes walking
// distance and move worker to that cell, then select.
//
else
{
int pref_x = -1;
int pref_y = -1;
int shortest = Integer.MAX_VALUE;
if ( Puzzle.isEmpty( north )
&& shortest > stepsAway( x, y-1) )
{
shortest = stepsAway( x, y-1);
pref_x = x;
pref_y = y-1;
}
if ( Puzzle.isEmpty( south )
&& shortest > stepsAway( x, y+1) )
{
shortest = stepsAway( x, y+1);
pref_x = x;
pref_y = y+1;
}
if ( Puzzle.isEmpty( west )
&& shortest > stepsAway( x-1, y) )
{
shortest = stepsAway( x-1, y);
pref_x = x-1;
pref_y = y;
}
if ( Puzzle.isEmpty( east )
&& shortest > stepsAway( x+1, y) )
{
shortest = stepsAway( x+1, y);
pref_x = x+1;
pref_y = y;
}
//
// Move the worker to the direction. If we cannot move worker
// next to the box then we cannot select it.
//
if ( !puzzle.isValid(pref_x, pref_y)
|| !tryMoveWorker(animator, pref_x, pref_y) )
return false;
//
// Select the box.
//
select(animator,x,y);
}
//
// Done
//
return true;
}
protected void move( Animator animator, int x, int y )
{
animator.queue( new Animator.Move(x,y) );
}
protected void push( Animator animator, int x, int y )
{
animator.queue( new Animator.Push(x,y) );
}
protected void select( Animator animator, int x, int y )
{
animator.queue( new Animator.Select(x,y) );
}
protected void unselect( Animator animator )
{
animator.queue( new Animator.Unselect() );
}
protected void buzz( Animator animator )
{
//animator.queue( new Animator.NoMove(x,y) );
}
protected void undoable( Animator animator, int x, int y )
{
animator.queue( new Animator.NoMove(x,y) );
}
//////////////////////////////////////////////////////////////////////////
//
// Routes to accessible cells from where worker stands.
//
protected final int getMoves( int x, int y )
{
return moves[puzzle.getIndex(x,y)];
}
protected void setMoves( int x, int y, int v )
{
moves[puzzle.getIndex(x,y)]=v;
}
private boolean setMovesIfGreater( int x, int y, int l )
{
//
// If out of borders then nothing to do.
//
if ( y < 0 || y >= puzzle.getRowCount()
|| x < 0 || x >= puzzle.getColumnCount() )
return false;
//
// Check if the cell is a floor or goal. If yes then set the l
// if current value is greater than l.
//
if ( getMoves(x,y) > l && puzzle.isEmpty(puzzle.getSym(x,y)))
{
setMoves(x,y,l);
return true;
}
else
return false;
}
public void calcMoves()
{
//
// Erase moves array.
//
Arrays.fill(moves,Integer.MAX_VALUE);
//
// For the beginning there is no cell in the list.
//
int front = 0;
int last = 0;
//
// Set the seed.
//
setMoves(puzzle.getWorkerX(),puzzle.getWorkerY(),0);
setOfCells[last++] = puzzle.getWorkerX();
setOfCells[last++] = puzzle.getWorkerY();
//
// Now on each loop pop one cell from the list and calculate the
// distance of cell around that cell.
//
while ( front < last )
{
//
// Pop the cell.
//
int x = setOfCells[front++];
int y = setOfCells[front++];
//
// Increase the length of cells all around given cell and push
// them into the list.
//
int l = getMoves(x,y)+1;
if ( setMovesIfGreater(x-1,y,l) )
{
setOfCells[last++] = x-1;
setOfCells[last++] = y;
}
if ( setMovesIfGreater(x+1,y,l) )
{
setOfCells[last++] = x+1;
setOfCells[last++] = y;
}
if ( setMovesIfGreater(x,y-1,l) )
{
setOfCells[last++] = x;
setOfCells[last++] = y-1;
}
if ( setMovesIfGreater(x,y+1,l) )
{
setOfCells[last++] = x;
setOfCells[last++] = y+1;
}
}
}
public final int stepsAway( int x, int y )
{
return getMoves(x,y);
}
public final boolean isAccessible( int x, int y )
{
return puzzle.isValid(x, y) && stepsAway(x, y) != Integer.MAX_VALUE;
}
public int [] getDirections( int x, int y )
{
int away = stepsAway(x,y);
if (away == Integer.MAX_VALUE)
return null;
//
// Ok looks there is a routh to given cell. Now create an array
// and fill in the step to get to the cell.
//
int [] steps = new int[2*away];
int i = steps.length;
steps[--i] = y;
steps[--i] = x;
while ( i > 0 )
{
x = steps[i];
y = steps[i+1];
int j = stepsAway(x,y);
if ( stepsAway(x-1,y) < j )
{
steps[--i] = y;
steps[--i] = x-1;
}
else if ( stepsAway(x+1,y) < j )
{
steps[--i] = y;
steps[--i] = x+1;
}
else if ( stepsAway(x,y-1) < j )
{
steps[--i] = y-1;
steps[--i] = x;
}
else if ( stepsAway(x,y+1) < j )
{
steps[--i] = y+1;
steps[--i] = x;
}
}
return steps;
}
public int [] getPushDirections( int x, int y )
{
//
// The selected box can be moved only orthogonally.
// Check that worker is on opposite side of the box.
// Statement:
// If scaliar product of selected->x,y and worker->selected is equal
// to manhatten distance of x,y from the selected box then the worker
// push direction is directed to x,y and is not opposit. In other words
// selected->x,y and worker->selected are codirectional.
//
int selx = puzzle.getSelectedX();
int sely = puzzle.getSelectedY();
int xx = x-selx;
int yy = y-sely;
int sx = selx-puzzle.getWorkerX();
int sy = sely-puzzle.getWorkerY();
double scaliar = (double)(xx)*(double)(sx)
+(double)(yy)*(double)(sy);
int len = abs(xx)+abs(yy);
if ( scaliar != (double)len )
return null;
//
// Now check that all cell till x,y are free.
//
int ix = selx;
int iy = sely;
while ( x != ix || y != iy )
{
ix+=sx;
iy+=sy;
if ( !puzzle.isEmpty(puzzle.getSym(ix,iy)) )
return null;
}
//
// Looks we could move the box till x,y. Create the steps array
// and fill it with push steps.
//
int steps[] = new int [2*len];
int i = 0;
ix = selx;
iy = sely;
while ( x != ix || y != iy )
{
steps[i++] = ix;
steps[i++] = iy;
ix+=sx;
iy+=sy;
}
return steps;
}
}

View File

@@ -0,0 +1,442 @@
package org.dyndns.vahagn.sokoban.play;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.RectF;
import android.support.v4.view.ViewCompat;
import android.view.View;
import static java.lang.Math.*;
import static org.dyndns.vahagn.sokoban.App.TAG;
import org.dyndns.vahagn.sokoban.Puzzle;
import static org.dyndns.vahagn.sokoban.Puzzle.*;
import org.dyndns.vahagn.sokoban.R;
/**
*/
public class PuzzleView extends View
{
protected Puzzle puzzle;
private RectF screen = new RectF(0,0,0,0);
private RectF viewport = new RectF();
private RectF board = new RectF();
private Matrix col2scr = new Matrix();
private Matrix scr2col = new Matrix();
private Bitmap[] tile = new Bitmap[Puzzle.getSymCount()];
private Point tile_size = new Point(0,0);
private int rotated;
private final Paint mPaint = new Paint();
private Paint mSelectPaint = new Paint();
private Rect tile_src = new Rect();
private RectF tile_dest = new RectF();
public PuzzleView(Context context)
{
super(context);
setFocusable(true);
//
// Resources r = this.getContext().getResources();
// TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TileView);
//
// mTileSize = a.getInt(R.styleable.TileView_tileSize, 12);
//
// a.recycle();
}
public void setPuzzle( Puzzle p )
{
puzzle = p;
board.set(0,0,puzzle.getColumnCount(),puzzle.getRowCount());
viewport.set(board);
tile_size.set(0,0);
calcTransforms();
initTiles();
}
public Puzzle getPuzzle()
{
return puzzle;
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh)
{
screen.set(0, 0, w, h);
tile_size.set(0,0);
calcTransforms();
initTiles();
}
public void scaleViewport( float focusX, float focusY, float scale )
{
float left = scale*(focusX - screen.left);
float right = scale*(screen.right - focusX);
float top = scale*(focusY - screen.top);
float bottom = scale*(screen.bottom - focusY);
viewport.set( focusX-left, focusY-top, focusX+right, focusY+bottom );
scr2col.mapRect(viewport);
constrainViewport();
calcTransforms();
ViewCompat.postInvalidateOnAnimation(this);
}
public void scaleViewportDone()
{
//initTiles();
ViewCompat.postInvalidateOnAnimation(this);
}
public void scrollViewport( float distanceX, float distanceY )
{
viewport.set( screen.left+distanceX,
screen.top+distanceY,
screen.right+distanceX,
screen.bottom+distanceY );
scr2col.mapRect(viewport);
constrainViewport();
calcTransforms();
ViewCompat.postInvalidateOnAnimation(this);
}
private void constrainViewport()
{
if ( !board.contains(viewport) )
{
if ( board.width() < viewport.width() )
{
viewport.left = board.left;
viewport.right = board.right;
}
if ( viewport.left < board.left )
{
viewport.right += board.left - viewport.left;
viewport.left = board.left;
}
if ( board.right < viewport.right )
{
viewport.left -= viewport.right - board.right;
viewport.right = board.right;
}
if ( board.height() < viewport.height() )
{
viewport.top = board.top;
viewport.bottom = board.bottom;
}
if ( viewport.top < board.top )
{
viewport.bottom += board.top - viewport.top;
viewport.top = board.top;
}
if ( viewport.bottom > board.bottom )
{
viewport.top -= viewport.bottom - board.bottom;
viewport.bottom = board.bottom;
}
}
if ( viewport.width() < 4 )
{
float center = (viewport.left + viewport.right)/2;
viewport.left = center-2;
viewport.right = center+2;
}
if ( viewport.height() < 4 )
{
float center = (viewport.top + viewport.bottom)/2;
viewport.top = center-2;
viewport.bottom = center+2;
}
}
/**
* Enlarge viewport to meet aspect ratio and calculate transforms.
*/
private void calcTransforms()
{
//
// Get the sizes of screen. If it is zero then simplly do nothing.
// Otherwise, make sure that the width is bigger the height.
//
double sw = (double)screen.width();
double sh = (double)screen.height();
if ( sw <= 0 || sh <= 0 || puzzle == null )
return;
else if ( sw < sh )
{
double t = sw;
sw = sh;
sh = t;
rotated = 90;
}
//
// Enlarge the view to have the same aspect ratio as the screen.
//
double wscale = sw/(double)viewport.width();
double hscale = sh/(double)viewport.height();
// double scale = floor(min(wscale,hscale));
double scale = min(wscale,hscale);
float enlarge_vert = (float)(sh / scale - viewport.height())/2;
float enlarge_horiz = (float)(sw / scale - viewport.width())/2;
viewport.top -= enlarge_vert;
viewport.bottom += enlarge_vert;
viewport.left -= enlarge_horiz;
viewport.right += enlarge_horiz;
//
// Calculate the tile sizes which is the scale.
//
tile_size.set((int)scale,(int)scale);
//
// Set transformations from and to screen.
//
col2scr.reset();
col2scr.postTranslate(-viewport.left, -viewport.top);
col2scr.postScale((float)scale,(float)scale);
col2scr.postTranslate(screen.left, screen.top);
if ( screen.width() < screen.height() )
{
float around = screen.width()/2;
col2scr.postRotate(90, around, around);
}
col2scr.invert(scr2col);
}
/**
* Set up tiles and matrices based on puzzle and view sizes.
*/
private void initTiles()
{
//
// Prevent this from being called if tile_size is not set.
//
if ( tile_size.x <= 0 && tile_size.y <= 0 || puzzle == null )
return;
//
// Selection rect.
//
mSelectPaint.setARGB(0xff,0x40,0x40,0xf0);
mSelectPaint.setStrokeWidth((float)3.0);
//
// Create tile bitmaps for given width and height.
//
tile[FLOOR] = createFloorTile();
tile[GOAL] = createGoalTile();
tile[WALL] = createWallTile();
tile[BOX] = createBoxTile();
tile[BOX|SELECTED] = createBoxSelTile();
tile[BINGO] = createBingoTile();
tile[BINGO|SELECTED] = createBingoSelTile();
tile[WORKER] = createWorkerTile();
tile[WORKER|SELECTED] = createWorkerSelTile();
}
/**
* Apply screen to board mapping and if the points are in the
* column,row range then return it. Otherwise return null.
*/
public void getTile( float scr_x, float scr_y, Point /*out*/ tile )
{
float [] scr_p = { scr_x, scr_y };
scr2col.mapPoints( scr_p );
if ( 0 <= scr_p[0] && scr_p[0] < puzzle.getColumnCount()
&& 0 <= scr_p[1] && scr_p[1] < puzzle.getRowCount())
tile.set((int)scr_p[0], (int)scr_p[1] );
else
tile.set(-1, -1);
}
private Bitmap createFloorTile()
{
Bitmap tmp = BitmapFactory.decodeResource( getResources(), R.drawable.floor );
Bitmap bitmap = Bitmap.createBitmap(tile_size.x, tile_size.y, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
//canvas.drawARGB(0xff,0x40,0x40,0x40);
canvas.drawBitmap(tmp,
new Rect(0,0,tmp.getWidth()-1,tmp.getHeight()-1),
new Rect(0,0,tile_size.x, tile_size.y),
mPaint);
return bitmap;
}
private Bitmap createGoalTile()
{
Bitmap tmp = BitmapFactory.decodeResource( getResources(), R.drawable.goal );
Bitmap bitmap = Bitmap.createBitmap(tile_size.x, tile_size.y, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
// canvas.drawARGB(0xff,0x40,0x40,0xF0);
canvas.drawBitmap(tmp,
new Rect(0,0,tmp.getWidth()-1,tmp.getHeight()-1),
new Rect(0,0,tile_size.x, tile_size.y),
mPaint);
return bitmap;
}
private Bitmap createWallTile()
{
Bitmap tmp = BitmapFactory.decodeResource( getResources(), R.drawable.wall );
Bitmap bitmap = Bitmap.createBitmap(tile_size.x, tile_size.y, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
//canvas.drawARGB(0xff,0xF0,0x40,0x40);
canvas.drawBitmap(tmp,
new Rect(1,1,tmp.getWidth()-2,tmp.getHeight()-2),
new Rect(0,0,tile_size.x, tile_size.y),
mPaint);
return bitmap;
}
private Bitmap createBoxTile()
{
Bitmap tmp = BitmapFactory.decodeResource( getResources(), R.drawable.box );
Bitmap bitmap = Bitmap.createBitmap(tile_size.x, tile_size.y, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
// canvas.drawARGB(0xff,0x40,0xF0,0x40);
canvas.drawBitmap(tmp,
new Rect(0,0,tmp.getWidth()-1,tmp.getHeight()-1),
new Rect(0,0,tile_size.x, tile_size.y),
mPaint);
return bitmap;
}
private Bitmap createBoxSelTile()
{
Bitmap bitmap = createBoxTile();
Canvas canvas = new Canvas(bitmap);
canvas.drawLine(0,0,tile_size.x-1, 0, mSelectPaint);
canvas.drawLine(tile_size.x-1, 0,tile_size.x-1, tile_size.y-1, mSelectPaint);
canvas.drawLine(tile_size.x-1, tile_size.y-1, 0, tile_size.y-1, mSelectPaint);
canvas.drawLine(0, tile_size.y-1, 0, 0, mSelectPaint);
return bitmap;
}
private Bitmap createBingoTile()
{
Bitmap tmp = BitmapFactory.decodeResource( getResources(), R.drawable.bingo );
Bitmap bitmap = Bitmap.createBitmap(tile_size.x, tile_size.y, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
// canvas.drawARGB(0xff,0x40,0xF0,0xF0);
canvas.drawBitmap(tmp,
new Rect(0,0,tmp.getWidth()-1,tmp.getHeight()-1),
new Rect(0,0,tile_size.x, tile_size.y),
mPaint);
return bitmap;
}
private Bitmap createBingoSelTile()
{
Bitmap bitmap = createBingoTile();
Canvas canvas = new Canvas(bitmap);
canvas.drawLine(0,0,tile_size.x-1, 0, mSelectPaint);
canvas.drawLine(tile_size.x-1, 0,tile_size.x-1, tile_size.y-1, mSelectPaint);
canvas.drawLine(tile_size.x-1, tile_size.y-1, 0, tile_size.y-1, mSelectPaint);
canvas.drawLine(0, tile_size.y-1, 0, 0, mSelectPaint);
return bitmap;
}
private Bitmap createWorkerTile()
{
Bitmap tmp = BitmapFactory.decodeResource( getResources(), R.drawable.worker );
Bitmap bitmap = Bitmap.createBitmap(tile_size.x, tile_size.y, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
canvas.drawBitmap(tmp,
new Rect(0,0,tmp.getWidth()-1,tmp.getHeight()-1),
new Rect(0,0,tile_size.x, tile_size.y),
mPaint);
return bitmap;
}
private Bitmap createWorkerSelTile()
{
Bitmap tmp = BitmapFactory.decodeResource( getResources(), R.drawable.worker_select );
Bitmap bitmap = Bitmap.createBitmap(tile_size.x, tile_size.y, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
canvas.drawBitmap(tmp,
new Rect(0,0,tmp.getWidth()-1,tmp.getHeight()-1),
new Rect(0,0,tile_size.x, tile_size.y),
mPaint);
return bitmap;
}
@Override
public void onDraw(Canvas canvas)
{
super.onDraw(canvas);
if ( puzzle != null )
{
//int cols = puzzle.getColumnCount();
//int rows = puzzle.getRowCount();
// canvas.concat(col2scr);
int xbgn = Math.max((int)viewport.left,(int)board.left);
int xend = Math.min((int)viewport.right,(int)board.right-1);
int ybgn = Math.max((int)viewport.top,(int)board.top);
int yend = Math.min((int)viewport.bottom,(int)board.bottom-1);
tile_src.set(0,0,tile_size.x,tile_size.y);
for (int y = ybgn; y <= yend; ++y)
{
for (int x = xbgn; x <= xend; ++x)
{
tile_dest.set(x,y,x+1,y+1);
col2scr.mapRect(tile_dest);
//tile_hlpr.roundOut(tile_hlpr);
//tile_hlpr.right -=1;
//tile_hlpr.bottom -=1;
int sym = puzzle.getSym(x,y);
if ( !Puzzle.hasWorker(sym) )
{
canvas.drawBitmap(tile[sym],
tile_src,tile_dest,
mPaint);
}
else
{
//
// Draw floor.
//
canvas.drawBitmap(tile[sym&~(WORKER_MASK|SELECTED)],
tile_src,tile_dest,
mPaint);
//
// Draw worker.
//
canvas.save();
Bitmap normal_worker = ((sym&SELECTED)!=0) ?
tile[WORKER|SELECTED] : tile[WORKER];
int direction = sym & WORKER_MASK;
if ( direction == WORKER_NORTH )
{
canvas.translate(tile_dest.centerX(), tile_dest.centerY());
canvas.rotate(180+rotated);
canvas.translate(-tile_dest.centerX(), -tile_dest.centerY());
}
else if ( direction == WORKER_SOUTH )
{
canvas.translate(tile_dest.centerX(), tile_dest.centerY());
canvas.rotate(0+rotated);
canvas.translate(-tile_dest.centerX(), -tile_dest.centerY());
}
else if ( direction == WORKER_WEST )
{
canvas.translate(tile_dest.centerX(), tile_dest.centerY());
canvas.rotate(90+rotated);
canvas.translate(-tile_dest.centerX(), -tile_dest.centerY());
}
else if ( direction == WORKER_EAST )
{
canvas.translate(tile_dest.centerX(), tile_dest.centerY());
canvas.rotate(-90+rotated);
canvas.translate(-tile_dest.centerX(), -tile_dest.centerY());
}
canvas.drawBitmap(normal_worker,
tile_src,tile_dest,
mPaint);
canvas.restore();
}
}
}
}
}
}

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
** Copyright 2011, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:interpolator="@android:anim/accelerate_interpolator"
android:fromXDelta="100%"
android:toXDelta="0%"
android:fromYDelta="0"
android:toYDelta="0"
android:duration="@android:integer/config_mediumAnimTime" />
</set>

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
** Copyright 2011, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:interpolator="@android:anim/accelerate_interpolator"
android:fromXDelta="0%"
android:toXDelta="-100%"
android:fromYDelta="0"
android:toYDelta="0"
android:duration="@android:integer/config_mediumAnimTime" />
</set>

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
** Copyright 2011, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:interpolator="@android:anim/accelerate_interpolator"
android:fromXDelta="-100%"
android:toXDelta="0"
android:fromYDelta="0"
android:toYDelta="0"
android:duration="@android:integer/config_mediumAnimTime" />
</set>

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
** Copyright 2011, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:interpolator="@android:anim/accelerate_interpolator"
android:fromXDelta="0%"
android:toXDelta="100%"
android:fromYDelta="0"
android:toYDelta="0"
android:duration="@android:integer/config_mediumAnimTime" />
</set>

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
** Copyright 2011, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:interpolator="@android:anim/accelerate_interpolator"
android:fromXDelta="0"
android:toXDelta="0"
android:fromYDelta="-100%"
android:toYDelta="0"
android:duration="@android:integer/config_mediumAnimTime" />
</set>

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
** Copyright 2011, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:interpolator="@android:anim/accelerate_interpolator"
android:fromXDelta="0"
android:toXDelta="0"
android:fromYDelta="0%"
android:toYDelta="-100%"
android:duration="@android:integer/config_mediumAnimTime" />
</set>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 787 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 942 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 982 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 397 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 710 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 558 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 520 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 496 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 692 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 698 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 651 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 802 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fragment_container"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
</LinearLayout>

View File

@@ -0,0 +1,61 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@drawable/splash"
android:gravity="center_vertical">
<View
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_gravity="top"
android:layout_weight="1"
android:gravity="center" />
<Button
android:id="@+id/btn_start"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/main_menu_btn_vmargin"
android:layout_marginBottom="@dimen/main_menu_btn_vmargin"
android:layout_marginLeft="@dimen/main_menu_btn_hmargin"
android:layout_marginRight="@dimen/main_menu_btn_hmargin"
android:layout_gravity="center"
android:layout_weight="0"
android:padding="@dimen/main_menu_btn_padding"
android:background="@color/main_menu_btn_bg"
android:textColor="@color/main_menu_btn_text"
android:textSize="@dimen/main_menu_text_size"
android:text="@string/btn_start_continue"
android:onClick="onStartCurrentPuzzle" />
<Button
android:id="@+id/btn_puzzles"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/main_menu_btn_vmargin"
android:layout_marginBottom="@dimen/main_menu_btn_vmargin"
android:layout_marginLeft="@dimen/main_menu_btn_hmargin"
android:layout_marginRight="@dimen/main_menu_btn_hmargin"
android:layout_gravity="center"
android:layout_weight="0"
android:padding="@dimen/main_menu_btn_padding"
android:background="@color/main_menu_btn_bg"
android:textColor="@color/main_menu_btn_text"
android:textSize="@dimen/main_menu_text_size"
android:text="@string/btn_puzzles"
android:onClick="onPuzzleListShow" />
<View
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_gravity="bottom"
android:layout_weight="1"
android:gravity="center" />
<TextView
android:id="@+id/footer_txt"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/git_version"
android:layout_gravity="bottom"
android:layout_weight="0"
android:gravity="center" />
</LinearLayout>

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<GridView
android:id="@+id/puzzle_grid"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:horizontalSpacing="2mm"
android:verticalSpacing="2mm"
android:columnWidth="10mm"
android:numColumns="auto_fit"
android:stretchMode="columnWidth"
android:gravity="center" />
</LinearLayout>

View File

@@ -0,0 +1,71 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/puzzle_view_title_layout"
android:orientation="horizontal"
android:gravity="center_horizontal"
android:background="#60404040"
android:layout_height="48dp"
android:layout_width="match_parent">
<!--ImageButton
android:src="@drawable/prev"
android:scaleType="fitCenter"
android:layout_width="wrap_content"
android:layout_height="20dp"
android:layout_gravity="center_vertical"
android:layout_weight="0"
android:layout_margin="3dp"
android:background="#00000000"
android:onClick="onPrev"
android:id="@+id/prev_btn"/-->
<TextView
android:text="Level"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="0"
android:textSize="40dp"
android:paddingLeft="5dp"
android:textScaleX="1"
android:textColor="#FFE680"
android:textStyle="bold"
android:typeface="normal"
android:gravity="center_vertical"
android:id="@+id/title_text"/>
<View
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"/>
<ImageButton
android:src="@drawable/undo"
android:scaleType="fitCenter"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:layout_weight="0"
android:layout_margin="3dp"
android:background="#00000000"
android:onClick="onUndo"
android:id="@+id/undo_btn"/>
<ImageButton
android:src="@drawable/reset"
android:scaleType="fitCenter"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:layout_weight="0"
android:layout_margin="3dp"
android:background="#00000000"
android:onClick="onReset"
android:id="@+id/reset_btn"/>
<!--ImageButton
android:src="@drawable/next"
android:scaleType="fitCenter"
android:layout_width="wrap_content"
android:layout_height="20dp"
android:layout_gravity="center_vertical"
android:layout_weight="0"
android:layout_margin="3dp"
android:background="#00000000"
android:onClick="onNext"
android:id="@+id/next_btn"/-->
</LinearLayout>

Binary file not shown.

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
**
** Copyright 2008, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<resources>
<color name="main_menu_btn_bg">#80404040</color>
<color name="main_menu_btn_text">#C0C0C0C0</color>
</resources>

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
**
** Copyright 2008, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<resources>
<dimen name="main_menu_text_size">18pt</dimen>
<dimen name="main_menu_btn_hmargin">10pt</dimen>
<dimen name="main_menu_btn_vmargin">2pt</dimen>
<dimen name="main_menu_btn_padding">10pt</dimen>
</resources>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Free Sokoban</string>
<string name="app_name_debug">Debug Sokoban</string>
<string name="menu">Menu</string>
<string name="play_activity">play_activity</string>
<string name="btn_start_begin">Begin</string>
<string name="btn_start_continue">Continue</string>
<string name="btn_puzzles">Puzzles</string>
<string name="version_tag">Version:</string>
<string name="copyright">(c) 2013 Vahagn Khachatryan</string>
</resources>

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--style name="WindowTitle">
<item name="android:singleLine">true</item-->
<!--item name="android:textAppearance">@style/TextAppearance.WindowTitle</item-->
<!-- item name="android:shadowColor">#BB000000</item>
<item name="android:shadowRadius">2.75</item>
</style-->
<!-- Base application theme is the default theme. -->
<!--style name="Theme.puzzle_view_title" parent="android:Theme"-->
<!--style name="puzzle_view_title_layout">
<item name="android:windowTitleStyle">@style/title_bar</item>
<item name="android:windowTitleSize">10mm</item-->
<!--item name="android:windowTitleBackgroundStyle">@style/title_background</item-->
<!--/style-->
<!--style name="title_bar">
<item name="android:singleLine">false</item>
<item name="android:textAppearance">@android:style/TextAppearance.WindowTitle</item>
<item name="android:shadowColor">#BB0000FF</item>
<item name="android:shadowRadius">2.75</item>
</style-->
<!--style name="title_background">
<item name="android:background">@drawable/background_square</item>
</style-->
<!--style name="PlainText">
<item name="android:textAppearance">@style/TextAppearance.Theme.PlainText</item>
</style-->
<!--style name="ImageView240dpi">
<item name="android:src">@drawable/stylogo240dpi</item>
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
</style-->
</resources>