package com.adamnickle.deck;
import android.content.Context;
import android.graphics.PointF;
import android.graphics.Shader;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Vibrator;
import android.support.annotation.NonNull;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import com.adamnickle.deck.Game.Card;
import com.adamnickle.deck.Game.CardCollection;
import com.adamnickle.deck.Interfaces.CardHolderListener;
import com.adamnickle.deck.Interfaces.GameUiListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
public class CardDisplayLayout extends FrameLayout implements CardHolderListener
{
private static final long CARD_RECEIVE_VIBRATION = 40L;
private static final int STARTING_LAYOUT_X = 50;
private static final int STARTING_LAYOUT_Y = 50;
public enum Side
{
LEFT, TOP, RIGHT, BOTTOM, NONE
}
private final GestureDetector mDetector;
private final Vibrator mVibrator;
private GameUiListener mGameUiListener;
private boolean mCanVibrate;
protected final HashMap< String, ArrayList< PlayingCardView > > mCardViewsByOwner;
public CardDisplayLayout( Context context )
{
this( context, null );
}
public CardDisplayLayout( Context context, AttributeSet attrs )
{
this( context, attrs, 0 );
}
public CardDisplayLayout( Context context, AttributeSet attrs, int defStyle )
{
super( context, attrs, defStyle );
mDetector = new GestureDetector( getContext(), new CardDisplayGestureListener() );
mCardViewsByOwner = new HashMap< String, ArrayList< PlayingCardView > >();
if( isInEditMode() )
{
mVibrator = null;
}
else
{
mVibrator = (Vibrator) getContext().getSystemService( Context.VIBRATOR_SERVICE );
}
mCanVibrate = true;
}
@Override
protected void onLayout( boolean changed, int left, int top, int right, int bottom )
{
super.onLayout( changed, left, top, right, bottom );
final int childCount = getChildCount();
for( int i = 0; i < childCount; i++ )
{
final View child = getChildAt( i );
if( child instanceof PlayingCardView )
{
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
child.layout( lp.Left, lp.Top, lp.Left + child.getMeasuredWidth(), lp.Top + child.getMeasuredHeight() );
}
}
}
@Override
protected boolean checkLayoutParams( ViewGroup.LayoutParams p )
{
return ( p instanceof LayoutParams );
}
@Override
protected LayoutParams generateDefaultLayoutParams()
{
return new LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT );
}
@Override
protected LayoutParams generateLayoutParams( @NonNull ViewGroup.LayoutParams p )
{
return new LayoutParams( p.width, p.height );
}
public static class LayoutParams extends FrameLayout.LayoutParams
{
public int Left;
public int Top;
public LayoutParams( int width, int height )
{
super( width, height );
}
}
public void onViewDestroy()
{
for( ArrayList< PlayingCardView > views : mCardViewsByOwner.values() )
{
views.clear();
}
mCardViewsByOwner.clear();
this.removeAllViews();
System.gc();
}
public CardHolderListener getCardHolderListener()
{
return this;
}
public void setGameUiListener( GameUiListener gameUiListener )
{
mGameUiListener = gameUiListener;
}
@Override
public void setBackgroundResource( int resourceID )
{
super.setBackgroundResource( resourceID );
final Drawable background = this.getBackground();
if( background instanceof BitmapDrawable )
{
( (BitmapDrawable) background ).setTileModeXY( Shader.TileMode.REPEAT, Shader.TileMode.REPEAT );
}
}
@SuppressWarnings( "SuspiciousNameCombination" )
public void onOrientationChange()
{
final int childCount = getChildCount();
for( int i = 0; i < childCount; i++ )
{
final View view = getChildAt( i );
if( view instanceof PlayingCardView )
{
final float x = view.getX();
view.setX( view.getY() );
view.setY( x );
}
}
}
public boolean canVibrate()
{
return mCanVibrate;
}
public void setCanVibrate( boolean canVibrate)
{
mCanVibrate = canVibrate;
}
public void childViewOffScreen( PlayingCardView playingCardView, Side side )
{
if( mGameUiListener != null )
{
mGameUiListener.onAttemptSendCard( playingCardView.getOwnerID(), playingCardView.getCard(), side );
}
}
public boolean childShouldBounce( PlayingCardView playingCardView, Side wall )
{
if( playingCardView.isSpreading() )
{
return true;
}
else if( mGameUiListener != null )
{
return !mGameUiListener.canSendCard( playingCardView.getOwnerID(), playingCardView.getCard() );
}
else
{
return true;
}
}
@Override
public boolean onInterceptTouchEvent( MotionEvent event )
{
return true;
}
private HashMap< Integer, PlayingCardView > mMovingCards = new HashMap< Integer, PlayingCardView >();
private HashMap< Integer, PointF > mLastPoints = new HashMap< Integer, PointF >();
private MotionEvent mCurrentDownEvent;
@Override
public boolean onTouchEvent( @NonNull MotionEvent event )
{
final int action = event.getActionMasked();
final int pointerIndex = event.getActionIndex();
final int pointerId = event.getPointerId( pointerIndex );
final float x = event.getX( pointerIndex );
final float y = event.getY( pointerIndex );
switch( action )
{
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
{
mCurrentDownEvent = event;
PlayingCardView playingCardView = findFirstCardUnder( x, y );
if( mMovingCards.containsValue( playingCardView ) )
{
return true;
}
mLastPoints.put( pointerId, new PointF( x, y ) );
if( playingCardView != null )
{
CardDisplayLayout.this.onCardDown( event, playingCardView );
mMovingCards.put( pointerId, playingCardView );
}
else
{
CardDisplayLayout.this.onBackgroundDown( event );
}
break;
}
case MotionEvent.ACTION_MOVE:
{
final int pointerCount = event.getPointerCount();
for( int i = 0; i < pointerCount; i++ )
{
final int movePointerID = event.getPointerId( i );
final PointF lastPoint = mLastPoints.get( movePointerID );
if( lastPoint != null )
{
final float moveX = event.getX( i );
final float moveY = event.getY( i );
final float deltaX = lastPoint.x - moveX;
final float deltaY = lastPoint.y - moveY;
final PlayingCardView playingCardView = mMovingCards.get( movePointerID );
if( playingCardView != null )
{
CardDisplayLayout.this.onCardScroll( mCurrentDownEvent, event, deltaX, deltaY, playingCardView );
}
else
{
CardDisplayLayout.this.onBackgroundScroll( mCurrentDownEvent, event, deltaX, deltaY );
}
lastPoint.set( moveX, moveY );
}
}
break;
}
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
{
final PlayingCardView playingCardView = mMovingCards.get( pointerId );
if( playingCardView != null )
{
CardDisplayLayout.this.onCardUp( event, playingCardView );
}
else
{
CardDisplayLayout.this.onBackgroundUp( event );
}
mMovingCards.remove( pointerId );
mLastPoints.remove( pointerId );
break;
}
case MotionEvent.ACTION_CANCEL:
{
mMovingCards.clear();
mLastPoints.clear();
break;
}
}
mDetector.onTouchEvent( event );
return true;
}
public PlayingCardView findFirstCardUnder( float x, float y )
{
final int childCount = getChildCount();
for( int i = childCount - 1; i >= 0; i-- )
{
final View child = getChildAt( i );
if( child instanceof PlayingCardView )
{
if( ( (PlayingCardView) child ).contains( x, y ) )
{
return (PlayingCardView) child;
}
}
}
return null;
}
public class CardDisplayGestureListener extends GestureDetector.SimpleOnGestureListener
{
@Override
public boolean onDown( MotionEvent event )
{
return true;
}
@Override
public boolean onSingleTapConfirmed( MotionEvent event )
{
final int pointerIndex = event.getActionIndex();
final PlayingCardView playingCardView = findFirstCardUnder( event.getX( pointerIndex ), event.getY( pointerIndex ) );
if( playingCardView != null )
{
CardDisplayLayout.this.onCardSingleTap( event, playingCardView );
}
else
{
CardDisplayLayout.this.onBackgroundSingleTap( event );
}
return true;
}
@Override
public boolean onDoubleTap( MotionEvent event )
{
final int pointerIndex = event.getActionIndex();
final PlayingCardView playingCardView = findFirstCardUnder( event.getX( pointerIndex ), event.getY( pointerIndex ) );
if( playingCardView != null )
{
CardDisplayLayout.this.onCardDoubleTap( event, playingCardView );
}
else
{
CardDisplayLayout.this.onBackgroundDoubleTap( event );
}
return true;
}
@Override
public boolean onScroll( MotionEvent event, MotionEvent event2, float distanceX, float distanceY )
{
return true;
}
@Override
public void onLongPress( MotionEvent event )
{
final int pointerIndex = event.getActionIndex();
final PlayingCardView playingCardView = findFirstCardUnder( event.getX( pointerIndex ), event.getY( pointerIndex ) );
if( playingCardView != null )
{
CardDisplayLayout.this.onCardLongPress( event, playingCardView );
}
else
{
CardDisplayLayout.this.onBackgroundLongPress( event );
}
}
@Override
public boolean onFling( MotionEvent event, MotionEvent event2, float velocityX, float velocityY )
{
final int pointerIndex = event.getActionIndex();
final PlayingCardView playingCardView = findFirstCardUnder( event2.getX( pointerIndex ), event2.getY( pointerIndex ) );
if( playingCardView != null )
{
CardDisplayLayout.this.onCardFling( event, event2, velocityX, velocityY, playingCardView );
}
else
{
CardDisplayLayout.this.onBackgroundFling( event, event2, velocityX, velocityY );
}
return true;
}
}
public void onBackgroundDown( MotionEvent event )
{
}
public void onCardDown( MotionEvent event, PlayingCardView playingCardView )
{
playingCardView.bringToFront();
playingCardView.onTouched();
}
public void onBackgroundSingleTap( MotionEvent event )
{
}
public void onCardSingleTap( MotionEvent event, PlayingCardView playingCardView )
{
}
public void onBackgroundDoubleTap( MotionEvent event )
{
}
public void onCardDoubleTap( MotionEvent event, PlayingCardView playingCardView )
{
}
public void onCardScroll( MotionEvent initialDownEvent, MotionEvent event, float deltaX, float deltaY, PlayingCardView playingCardView )
{
playingCardView.setX( playingCardView.getX() - deltaX );
playingCardView.setY( playingCardView.getY() - deltaY );
}
public void onBackgroundScroll( MotionEvent event1, MotionEvent event2, float distanceX, float distanceY )
{
}
public void onBackgroundLongPress( MotionEvent event )
{
}
public void onCardLongPress( MotionEvent event, PlayingCardView playingCardView )
{
}
public void onCardFling( MotionEvent event, MotionEvent event2, float velocityX, float velocityY, PlayingCardView playingCardView )
{
playingCardView.fling( velocityX, velocityY );
}
public void onBackgroundFling( MotionEvent event, MotionEvent event2, float velocityX, float velocityY )
{
}
public void onBackgroundUp( MotionEvent event )
{
}
public void onCardUp( MotionEvent event, PlayingCardView playingCardView )
{
}
public void sortCards( String cardHolderID, CardCollection.SortingType sortingType )
{
final ArrayList<PlayingCardView> playingCardViews = mCardViewsByOwner.get( cardHolderID );
Collections.sort( playingCardViews, new PlayingCardView.PlayingCardViewComparator( sortingType ) );
for( PlayingCardView playingCardView : playingCardViews )
{
playingCardView.bringToFront();
}
}
public synchronized void layoutCards( String cardHolderID )
{
final ArrayList<PlayingCardView> cardViews = mCardViewsByOwner.get( cardHolderID );
if( cardViews == null || cardViews.size() == 0 )
{
return;
}
final int displayWidth = this.getWidth();
final int displayHeight = this.getHeight();
final int cardWidth = cardViews.get( 0 ).getWidth();
final int cardHorizontalOffset = (int) ( cardWidth * 0.3f );
final int cardsPerRow = ( displayWidth - STARTING_LAYOUT_X - ( cardWidth - cardHorizontalOffset ) ) / cardHorizontalOffset;
final int rows = (int) Math.ceil( (float) cardViews.size() / cardsPerRow );
final int verticalCardSpace = displayHeight - STARTING_LAYOUT_Y;
final int cardVerticalOffset = (int) ( (float) verticalCardSpace / rows );
int x = STARTING_LAYOUT_X;
int y = STARTING_LAYOUT_Y;
for( PlayingCardView playingCardView : cardViews )
{
playingCardView.stop();
playingCardView.flip( true, false );
playingCardView.setX( x );
playingCardView.setY( y );
x += cardHorizontalOffset;
if( x + cardWidth > displayWidth )
{
x = 50;
y += cardVerticalOffset;
}
}
}
public void resetCard( String cardHolderID, Card card )
{
List<PlayingCardView> cardViews = mCardViewsByOwner.get( cardHolderID );
if( cardViews != null )
{
for( PlayingCardView playingCardView : mCardViewsByOwner.get( cardHolderID ) )
{
if( playingCardView.getCard().equals( card ) )
{
playingCardView.reset();
break;
}
}
}
}
public void spreadCards()
{
final int childCount = getChildCount();
for( int i = 0; i < childCount; i++ )
{
final View view = getChildAt( i );
if( view instanceof PlayingCardView )
{
( (PlayingCardView) view ).spreadCard();
}
}
}
public PlayingCardView createPlayingCardView( String cardHolderID, Card card )
{
return new PlayingCardView( getContext(), cardHolderID, card );
}
@Override
public void onCardRemoved( final String playerID, final Card card )
{
this.post( new Runnable()
{
@Override
public void run()
{
ArrayList< PlayingCardView > cardViews = mCardViewsByOwner.get( playerID );
if( cardViews != null )
{
PlayingCardView removingCardView = null;
Iterator< PlayingCardView > playingCardViewIterator = cardViews.iterator();
while( playingCardViewIterator.hasNext() )
{
PlayingCardView playingCardView = playingCardViewIterator.next();
if( playingCardView.getCard().equals( card ) )
{
removingCardView = playingCardView;
playingCardViewIterator.remove();
break;
}
}
if( removingCardView != null )
{
CardDisplayLayout.this.removeView( removingCardView );
}
}
}
} );
}
@Override
public void onCardsRemoved( final String playerID, final Card[] cards )
{
this.post( new Runnable()
{
@Override
public void run()
{
Arrays.sort( cards, new Card.CardComparator( CardCollection.SortingType.SORT_BY_CARD_NUMBER ) );
final ArrayList< PlayingCardView > playingCardViews = mCardViewsByOwner.get( playerID );
final Iterator< PlayingCardView > playingCardViewIterator = playingCardViews.iterator();
final ArrayList< PlayingCardView > removedCardViews = new ArrayList< PlayingCardView >();
while( playingCardViewIterator.hasNext() && ( removedCardViews.size() < cards.length ) )
{
final PlayingCardView playingCardView = playingCardViewIterator.next();
if( Arrays.binarySearch( cards, playingCardView.getCard() ) >= 0 )
{
removedCardViews.add( playingCardView );
playingCardViewIterator.remove();
}
}
for( PlayingCardView cardView : removedCardViews )
{
CardDisplayLayout.this.removeView( cardView );
}
}
} );
}
@Override
public void onCardAdded( final String playerID, final Card card )
{
this.post( new Runnable()
{
@Override
public void run()
{
final PlayingCardView playingCardView = createPlayingCardView( playerID, card );
CardDisplayLayout.this.addView( playingCardView );
ArrayList< PlayingCardView > playingCardViews;
if( !mCardViewsByOwner.containsKey( playerID ) )
{
playingCardViews = new ArrayList< PlayingCardView >();
mCardViewsByOwner.put( playerID, playingCardViews );
}
else
{
playingCardViews = mCardViewsByOwner.get( playerID );
}
playingCardViews.add( playingCardView );
if( mCanVibrate )
{
mVibrator.vibrate( CARD_RECEIVE_VIBRATION );
}
}
} );
}
@Override
public void onCardsAdded( final String playerID, final Card[] cards )
{
this.post( new Runnable()
{
@Override
public void run()
{
final ArrayList< PlayingCardView > playingCardViews;
if( !mCardViewsByOwner.containsKey( playerID ) )
{
playingCardViews = new ArrayList< PlayingCardView >();
mCardViewsByOwner.put( playerID, playingCardViews );
}
else
{
playingCardViews = mCardViewsByOwner.get( playerID );
}
System.gc();
for( Card card : cards )
{
final PlayingCardView playingCardView = createPlayingCardView( playerID, card );
CardDisplayLayout.this.addView( playingCardView );
playingCardViews.add( playingCardView );
}
if( mCanVibrate )
{
mVibrator.vibrate( CARD_RECEIVE_VIBRATION );
}
}
} );
}
@Override
public void onCardsCleared( final String cardHolderID )
{
this.post( new Runnable()
{
@Override
public void run()
{
ArrayList< PlayingCardView > playingCardViews = mCardViewsByOwner.remove( cardHolderID );
if( playingCardViews != null )
{
for( PlayingCardView cardView : playingCardViews )
{
CardDisplayLayout.this.removeView( cardView );
}
playingCardViews.clear();
System.gc();
}
}
} );
}
}