package com.turtleplayer.controller;
import android.app.Activity;
import android.graphics.Color;
import android.graphics.Point;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import com.turtleplayer.model.Track;
import com.turtleplayer.persistance.framework.filter.*;
import com.turtleplayer.persistance.turtle.db.structure.Tables;
import com.turtleplayer.persistance.turtle.filter.DirFilter;
import com.turtleplayer.persistance.turtle.filter.TurtleFilterVisitor;
import com.turtleplayer.player.ObservableOutput;
import com.turtleplayer.playlist.Playlist;
/**
* TURTLE PLAYER
* <p/>
* Licensed under MIT & GPL
* <p/>
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
* OR OTHER DEALINGS IN THE SOFTWARE.
* <p/>
* More Information @ www.turtle-player.co.uk
*
* @author Simon Honegger (Hoene84)
*/
public abstract class TouchHandler extends Playlist.PlaylistFilterChangeObserver implements View.OnTouchListener, ObservableOutput.PlayerObserver
{
private enum Mode
{
MENU,
TRACK
}
private enum BowMenuEntry
{
LEFT(){
@Override
public void setVisible(Activity activity,
boolean visible)
{
int visibility = visible ? View.VISIBLE : View.GONE;
activity.findViewById(com.turtleplayerv2.R.id.track_instant_filter_left).setVisibility(visibility);
activity.findViewById(com.turtleplayerv2.R.id.bowmenu_left_icon).setVisibility(visibility);
activity.findViewById(com.turtleplayerv2.R.id.bowmenu_left).setVisibility(visibility);
}
@Override
public View getView(Activity activity)
{
return activity.findViewById(com.turtleplayerv2.R.id.bowmenu_left);
}
@Override
public void adapt(Track track, Activity activity)
{
((TextView)activity.findViewById(com.turtleplayerv2.R.id.track_instant_filter_left)).setText(track.getAlbumName());
}
@Override
public Filter<? super Tables.Tracks> getFilter(Track track)
{
return track == null ? null : new FieldFilter<Tables.Tracks, Track, String>(Tables.AlbumsReadable.ALBUM, Operator.EQ, Tables.AlbumsReadable.ALBUM.get(track));
}
@Override
public void adapt(final Filter<? super Tables.Tracks> filter, final Track track, final boolean activated, final Activity activity)
{
filter.accept(new FilterVisitor<Tables.Tracks, Boolean>()
{
public <T, Z> Boolean visit(FieldFilter<? super Tables.Tracks, Z, T> fieldFilter)
{
if(Tables.AlbumsReadable.ALBUM.equals(fieldFilter.getField()))
{
setSettedFilter(activated ? filter : null);
ImageView bow = (ImageView) activity.findViewById(com.turtleplayerv2.R.id.bowmenu_left);
bow.setImageResource(activated ? com.turtleplayerv2.R.drawable.menubow_left_290_active : com.turtleplayerv2.R.drawable.menubow_left_290);
if(activated)
{
adapt(track, activity);
}
setVisible(activity, activated);
return true;
}
return false;
}
public Boolean visit(FilterSet<? super Tables.Tracks> filterSet)
{
boolean adapted = false;
for(Filter<? super Tables.Tracks> filter : filterSet.getFilters())
{
if(filter.accept(this)){
adapted = true;
}
}
return adapted;
}
public Boolean visit(NotFilter<? super Tables.Tracks> notFilter)
{
if(notFilter.getFilter().accept(this)){
((TextView)activity.findViewById(com.turtleplayerv2.R.id.track_instant_filter_left)).setTextColor(Color.RED);
return activated;
}
return false;
}
});
}
},
RIGHT{
@Override
public void setVisible(Activity activity,
boolean visible)
{
int visibility = visible ? View.VISIBLE : View.GONE;
activity.findViewById(com.turtleplayerv2.R.id.track_instant_filter_right).setVisibility(visibility);
activity.findViewById(com.turtleplayerv2.R.id.bowmenu_right_icon).setVisibility(visibility);
activity.findViewById(com.turtleplayerv2.R.id.bowmenu_right).setVisibility(visibility);
}
@Override
public View getView(Activity activity)
{
return activity.findViewById(com.turtleplayerv2.R.id.bowmenu_right);
}
@Override
public void adapt(Track track, Activity activity)
{
((TextView)activity.findViewById(com.turtleplayerv2.R.id.track_instant_filter_right)).setText(track.getArtistName());
}
@Override
public Filter<? super Tables.Tracks> getFilter(Track track)
{
return track == null ? null : new FieldFilter<Tables.Tracks, Track, String>(Tables.ArtistsReadable.ARTIST, Operator.EQ, Tables.ArtistsReadable.ARTIST.get(track));
}
@Override
public void adapt(final Filter<? super Tables.Tracks> filter, final Track track, final boolean activated, final Activity activity)
{
filter.accept(new FilterVisitor<Tables.Tracks, Boolean>()
{
public <T, Z> Boolean visit(FieldFilter<? super Tables.Tracks, Z, T> fieldFilter)
{
if(Tables.ArtistsReadable.ARTIST.equals(fieldFilter.getField()))
{
setSettedFilter(activated ? filter : null);
ImageView bow = (ImageView) activity.findViewById(com.turtleplayerv2.R.id.bowmenu_right);
bow.setImageResource(activated ? com.turtleplayerv2.R.drawable.menubow_right_290_active : com.turtleplayerv2.R.drawable.menubow_right_290);
if(activated)
{
adapt(track, activity);
}
setVisible(activity, activated);
return true;
}
return false;
}
public Boolean visit(FilterSet<? super Tables.Tracks> filterSet)
{
boolean adapted = false;
for(Filter<? super Tables.Tracks> filter : filterSet.getFilters())
{
if(filter.accept(this)){
adapted = true;
}
}
return adapted;
}
public Boolean visit(NotFilter<? super Tables.Tracks> notFilter)
{
if(notFilter.getFilter().accept(this)){
((TextView)activity.findViewById(com.turtleplayerv2.R.id.track_instant_filter_right)).setTextColor(Color.RED);
return activated;
}
return false;
}
});
}
},
TOPLINE{
@Override
public void setVisible(Activity activity,
boolean visible)
{
int visibility = visible ? View.VISIBLE : View.GONE;
activity.findViewById(com.turtleplayerv2.R.id.linearLayoutDir).setVisibility(visibility);
}
@Override
public View getView(Activity activity)
{
return activity.findViewById(com.turtleplayerv2.R.id.linearLayoutDir);
}
@Override
public void adapt(Track track, Activity activity)
{
((TextView)activity.findViewById(com.turtleplayerv2.R.id.track_instant_filter_topline)).setText(track.getPath());
}
@Override
public Filter<? super Tables.Tracks> getFilter(Track track)
{
return track == null ? null : new DirFilter(Operator.LIKE, Tables.FsObjects.PATH.get(track) + "%");
}
@Override
public void adapt(final Filter<? super Tables.Tracks> filter, final Track track, final boolean activated, final Activity activity)
{
filter.accept(new TurtleFilterVisitor<Tables.Tracks, Boolean>()
{
public <T, Z> Boolean visit(FieldFilter<? super Tables.Tracks, Z, T> fieldFilter)
{
return false;
}
public Boolean visit(FilterSet<? super Tables.Tracks> filterSet)
{
boolean adapted = false;
for(Filter<? super Tables.Tracks> filter : filterSet.getFilters())
{
if(filter.accept(this)){
adapted = true;
}
}
return adapted;
}
public Boolean visit(NotFilter<? super Tables.Tracks> notFilter)
{
if(notFilter.getFilter().accept(this)){
((TextView)activity.findViewById(com.turtleplayerv2.R.id.track_instant_filter_topline)).setTextColor(Color.RED);
return activated;
}
return false;
}
public Boolean visit(DirFilter dirFilter)
{
activity.findViewById(com.turtleplayerv2.R.id.dir_filter_border).setBackgroundColor(activated ? Color.argb(177, 21, 164, 0) : Color.argb(177, 152, 152, 152));
((TextView)activity.findViewById(com.turtleplayerv2.R.id.track_instant_filter_topline)).setText(dirFilter.getValue().replaceAll("%", "*"));
setSettedFilter(activated ? filter : null);
if(!activated)
{
adapt(track, activity);
}
setVisible(activity, activated);
return true;
}
});
}
},
TOP{
@Override
public void setVisible(Activity activity,
boolean visible)
{
int visibility = visible ? View.VISIBLE : View.GONE;
activity.findViewById(com.turtleplayerv2.R.id.track_instant_filter_top).setVisibility(visibility);
activity.findViewById(com.turtleplayerv2.R.id.bowmenu_top_icon).setVisibility(visibility);
activity.findViewById(com.turtleplayerv2.R.id.bowmenu_top).setVisibility(visibility);
}
@Override
public View getView(Activity activity)
{
return activity.findViewById(com.turtleplayerv2.R.id.bowmenu_top);
}
@Override
public void adapt(Track track, Activity activity)
{
((TextView)activity.findViewById(com.turtleplayerv2.R.id.track_instant_filter_top)).setText(track.getGenreName());
}
@Override
public Filter<? super Tables.Tracks> getFilter(Track track)
{
return track == null ? null : new FieldFilter<Tables.Tracks, Track, String>(Tables.GenresReadable.GENRE, Operator.EQ, Tables.GenresReadable.GENRE.get(track));
}
@Override
public void adapt(final Filter<? super Tables.Tracks> filter, final Track track, final boolean activated, final Activity activity)
{
filter.accept(new FilterVisitor<Tables.Tracks, Boolean>()
{
public <T, Z> Boolean visit(FieldFilter<? super Tables.Tracks, Z, T> fieldFilter)
{
if(Tables.GenresReadable.GENRE.equals(fieldFilter.getField()))
{
setSettedFilter(activated ? filter : null);
ImageView bow = (ImageView) activity.findViewById(com.turtleplayerv2.R.id.bowmenu_top);
bow.setImageResource(activated ? com.turtleplayerv2.R.drawable.menubow_top_290_active : com.turtleplayerv2.R.drawable.menubow_top_290);
if(activated)
{
adapt(track, activity);
}
setVisible(activity, activated);
return true;
}
return false;
}
public Boolean visit(FilterSet<? super Tables.Tracks> filterSet)
{
boolean adapted = false;
for(Filter<? super Tables.Tracks> filter : filterSet.getFilters())
{
if(filter.accept(this)){
adapted = true;
}
}
return adapted;
}
public Boolean visit(NotFilter<? super Tables.Tracks> notFilter)
{
if(notFilter.getFilter().accept(this)){
((TextView)activity.findViewById(com.turtleplayerv2.R.id.track_instant_filter_top)).setTextColor(Color.RED);
return activated;
}
return false;
}
});
}
};
private Filter<? super Tables.Tracks> settedFilter = null;
public boolean isActive()
{
return settedFilter != null;
}
public void setSettedFilter(Filter<? super Tables.Tracks> settedFilter)
{
this.settedFilter = settedFilter;
}
public Filter<? super Tables.Tracks> getSettedFilter()
{
return settedFilter;
}
public abstract void setVisible(Activity activity,
boolean visible);
public abstract View getView(Activity activity);
public abstract void adapt(Track track, Activity activity);
public abstract Filter<? super Tables.Tracks> getFilter(Track track);
public abstract void adapt(Filter<? super Tables.Tracks> filter, Track track, boolean activated, Activity activity);
}
//Filter
private final ImageView pointer;
private final View[] scrollingViews;
private final Point[] initalScrollingOfScrollingViews;
private final Activity activity;
private final GestureDetector gestureDetector;
private final int[] pointerLocationOnScreen = new int[2];
private Mode currMode = Mode.TRACK;
private Track currTrack = null;
protected TouchHandler(final Activity activity, View... scrollingViews)
{
this.activity = activity;
pointerShower = new Runnable()
{
public void run()
{
pointer.setVisibility(View.VISIBLE);
for(BowMenuEntry bowMenuEntry : BowMenuEntry.values()){
bowMenuEntry.setVisible(activity, true);
}
currMode = Mode.MENU;
}
};
pointer = (ImageView) activity.findViewById(com.turtleplayerv2.R.id.pointer);
initalScrollingOfScrollingViews = new Point[scrollingViews.length];
this.scrollingViews = scrollingViews;
gestureDetector = new GestureDetector(activity, gestureListener);
gestureDetector.setIsLongpressEnabled(false);
}
GestureDetector.OnGestureListener gestureListener = new GestureDetector.SimpleOnGestureListener(){
@Override
public boolean onScroll(MotionEvent e1,
MotionEvent e2,
float distanceX,
float distanceY)
{
switch (currMode)
{
case MENU:
break;
case TRACK:
scrollScrollingViewsBy((int)(distanceX*1.5), 0);
pointer.removeCallbacks(pointerShower);
break;
}
pointer.scrollBy((int)distanceX, (int)distanceY);
return true;
}
@Override
public boolean onFling(MotionEvent e1,
MotionEvent e2,
float velocityX,
float velocityY)
{
switch (currMode)
{
case MENU:
break;
case TRACK:
if(velocityX < 0)
{
if(e1.getX() > e2.getX())
{
nextGestureRecognized();
return true;
}
}
else
{
if(e1.getX() < e2.getX())
{
previousGestureRecognized();
return true;
}
}
break;
}
return false;
}
};
final Runnable pointerShower;
public boolean onTouch(View v,
MotionEvent event)
{
boolean wasConsumed = gestureDetector.onTouchEvent(event);
boolean consumed = false;
if(MotionEvent.ACTION_DOWN == event.getAction())
{
pointer.getLocationOnScreen(pointerLocationOnScreen);
pointer.scrollTo(
-((int)event.getX() - pointerLocationOnScreen[0] - pointer.getWidth()/2),
-((int)event.getY() - pointerLocationOnScreen[0] - pointer.getHeight()/2)
);
pointer.postDelayed(pointerShower, 500);
consumed = true;
}
else{
if (MotionEvent.ACTION_UP == event.getAction())
{
if(!wasConsumed)
{
for(BowMenuEntry bowMenuEntry : BowMenuEntry.values()){
if(isPointInsideOf(bowMenuEntry.getView(activity), event.getRawX(), event.getRawY()) &&
(bowMenuEntry.isActive() && scrollingViews[0].getScrollX() == 0 || Mode.MENU.equals(currMode))){
if(bowMenuEntry.isActive())
{
filterSelected(bowMenuEntry.getSettedFilter(), true);
}
else
{
filterSelected(bowMenuEntry.getFilter(currTrack), false);
}
}
}
if (Mode.TRACK.equals(currMode))
{
if(scrollingViews[0].getScrollX() > scrollingViews[0].getWidth()*2/3){
nextGestureRecognized();
consumed = true;
}
else if(-scrollingViews[0].getScrollX() > scrollingViews[0].getWidth()*2/3)
{
previousGestureRecognized();
consumed = true;
}
}
}
resetScrollingViews();
pointer.scrollTo(0,0);
pointer.setVisibility(View.GONE);
for(BowMenuEntry bowMenuEntry : BowMenuEntry.values()){
if(!bowMenuEntry.isActive())
{
bowMenuEntry.setVisible(activity, false);
}
}
pointer.removeCallbacks(pointerShower);
currMode = Mode.TRACK;
}
}
return consumed;
}
private boolean isPointInsideOf(View view, float x, float y){
int location[] = new int[2];
view.getLocationOnScreen(location);
int viewX = location[0];
int viewY = location[1];
//point is inside view bounds
if(( x > viewX && x < (viewX + view.getWidth())) &&
( y > viewY && y < (viewY + view.getHeight()))){
return true;
} else {
return false;
}
}
private void resetScrollingViews(){
saveInitalPositions();
for(int i = 0; i < scrollingViews.length; i++){
scrollingViews[i].scrollTo(initalScrollingOfScrollingViews[i].x, initalScrollingOfScrollingViews[i].y);
}
}
private void scrollScrollingViewsBy(int x, int y){
saveInitalPositions();
for(View scrollingView : scrollingViews){
scrollingView.scrollBy(x, y);
}
}
private void saveInitalPositions(){
if(initalScrollingOfScrollingViews[0] == null)
{
for(int i = 0; i < scrollingViews.length; i++)
{
initalScrollingOfScrollingViews[i] = new Point(scrollingViews[i].getScrollX(), scrollingViews[i].getScrollY());
}
}
}
public void trackChanged(Track track, int lengthInMillis)
{
for(BowMenuEntry bowMenuEntry : BowMenuEntry.values()){
if(!bowMenuEntry.isActive())
{
bowMenuEntry.adapt(track, activity);
}
}
this.currTrack = track;
}
public void started()
{
//do nothing
}
public void stopped()
{
//do nothing
}
public void filterAdded(Filter<? super Tables.Tracks> filter)
{
filterChanged(filter, true);
}
public void filterRemoved(Filter<? super Tables.Tracks> filter)
{
filterChanged(filter, false);
}
private void filterChanged(final Filter<? super Tables.Tracks> filter, final boolean activated){
for(final BowMenuEntry entry : BowMenuEntry.values())
{
entry.adapt(filter, currTrack, activated, activity);
}
}
protected abstract void nextGestureRecognized();
protected abstract void previousGestureRecognized();
protected abstract void filterSelected(Filter<? super Tables.Tracks> filter, boolean wasActive);
}