/* * Copyright 2008-2013, ETH Zürich, Samuel Welten, Michael Kuhn, Tobias Langner, * Sandro Affentranger, Lukas Bossard, Michael Grob, Rahul Jain, * Dominic Langenegger, Sonia Mayor Alonso, Roger Odermatt, Tobias Schlueter, * Yannick Stucki, Sebastian Wendland, Samuel Zehnder, Samuel Zihlmann, * Samuel Zweifel * * This file is part of Jukefox. * * Jukefox is free software: you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or any later version. Jukefox is * distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * Jukefox. If not, see <http://www.gnu.org/licenses/>. */ package ch.ethz.dcg.pancho3.controller.eventhandlers; import java.util.ArrayList; import java.util.List; import java.util.Timer; import java.util.TimerTask; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import android.widget.Toast; import ch.ethz.dcg.jukefox.commons.Constants; import ch.ethz.dcg.jukefox.commons.DataUnavailableException; import ch.ethz.dcg.jukefox.commons.utils.AndroidUtils; import ch.ethz.dcg.jukefox.commons.utils.Log; import ch.ethz.dcg.jukefox.commons.utils.Pair; import ch.ethz.dcg.jukefox.commons.utils.RandomProvider; import ch.ethz.dcg.jukefox.controller.Controller; import ch.ethz.dcg.jukefox.model.collection.BaseAlbum; import ch.ethz.dcg.jukefox.model.collection.BaseArtist; import ch.ethz.dcg.jukefox.model.collection.BaseSong; import ch.ethz.dcg.jukefox.model.collection.CompleteAlbum; import ch.ethz.dcg.jukefox.model.collection.MapAlbum; import ch.ethz.dcg.jukefox.model.collection.PlaylistSong; import ch.ethz.dcg.jukefox.model.collection.PlaylistSong.SongSource; import ch.ethz.dcg.jukefox.model.player.PlayModeType; import ch.ethz.dcg.pancho3.R; import ch.ethz.dcg.pancho3.model.JukefoxApplication; import ch.ethz.dcg.pancho3.view.commons.JukefoxActivity; import ch.ethz.dcg.pancho3.view.tabs.MapActivity; import ch.ethz.dcg.pancho3.view.tabs.JukefoxTabActivity.Tab; import ch.ethz.dcg.pancho3.view.tabs.opengl.GlAlbumCover; import ch.ethz.dcg.pancho3.view.tabs.opengl.MapRenderer; import ch.ethz.dcg.pancho3.view.tabs.opengl.RegionPlaylistCreator; import ch.ethz.dcg.pancho3.view.tabs.opengl.RegionPlaylistCreator.OnRegionCreatedListener; public class MapEventListener extends MainTabButtonEventListener implements OnRegionCreatedListener { private static final float CLICK_DIST_THRESH = 20; private static final String TAG = MapEventListener.class.getSimpleName(); private float lastTouchPosX; private float lastTouchPosY; private float touchDownPosX; private float touchDownPosY; private boolean canBeClick; private long touchDownTime; private Timer regionSelectHoldTimeTimer; private float lastZoomBarTouchY; private float lastZoomBarMoveY; private long lastZoomClickTime; private long lastToastTime; private boolean isKineticScrolling; protected final MapRenderer mapRenderer; private RegionPlaylistCreator regionPlaylist; private boolean isCreatingRegionPlaylist; private MapMultiTouchEventHandler multiTouchHandler; private MapActivity mapActivity; public MapEventListener(Controller controller, JukefoxActivity activity, MapRenderer mapRenderer, boolean allowMultiTouchPanning) { super(controller, activity, Tab.MAP); this.mapRenderer = mapRenderer; if (AndroidUtils.isMultiTouchOs()) { multiTouchHandler = new MapMultiTouchEventHandler(mapRenderer, this, allowMultiTouchPanning); } } public void setMapActivity(MapActivity mapActivity) { this.mapActivity = mapActivity; } @Override public boolean onKey(int keyCode, KeyEvent event) { if (event.getAction() == KeyEvent.ACTION_DOWN) { switch (keyCode) { case KeyEvent.KEYCODE_DPAD_CENTER: startCreatingRegionPlaylist(); return true; } } return super.onKey(keyCode, event); } public void startCreatingRegionPlaylist() { isCreatingRegionPlaylist = true; } public void stopCreatingRegionPlaylist() { isCreatingRegionPlaylist = false; } public boolean onGlTouch(View v, MotionEvent event) { if (multiTouchHandler != null) { if (multiTouchHandler.handleEvent(event)) { return true; } } return handleSingleTouchEvent(event); } private boolean handleSingleTouchEvent(MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { return handleTouchDownEvent(event); } else if (event.getAction() == MotionEvent.ACTION_MOVE) { return handleTouchMoveEvent(event); } else if (event.getAction() == MotionEvent.ACTION_UP) { return handleTouchUpEvent(event); } return true; } private boolean handleTouchUpEvent(MotionEvent event) { if (isCreatingRegionPlaylist) { if (regionPlaylist != null) { regionPlaylist.createPlaylist(); mapRenderer.stopDrawingRegionPlaylist(); } isCreatingRegionPlaylist = false; } else if (canBeClick) { if (event.getEventTime() - touchDownTime < 300) { // normal click doSelection(lastTouchPosX, lastTouchPosY); mapRenderer.getCamera().stopMotion(); } } cancelRegionSelectHoldTimeTimer(); canBeClick = false; mapRenderer.getCamera().setGrasped(false); return true; } private void doSelection(float touchPosX, float touchPosY) { float camPosZ = mapRenderer.getCamera().getPosZ(); float camPosX = mapRenderer.getCamera().getPosX(); float camPosY = mapRenderer.getCamera().getPosY(); float widthFactor = mapRenderer.getViewRatio() / mapRenderer.getCamera().getFrontClippingPlane(); float heightFactor = 1f / mapRenderer.getCamera().getFrontClippingPlane(); float screenMinX = camPosX - camPosY * widthFactor; float screenMaxX = camPosX + camPosY * widthFactor; float screenMinZ = camPosZ - camPosY * heightFactor; float screenMaxZ = camPosZ + camPosY * heightFactor; float mapTouchX = screenMinX + (screenMaxX - screenMinX) / mapRenderer.getViewWidth() * touchPosX; float mapTouchY = screenMinZ + (screenMaxZ - screenMinZ) / mapRenderer.getViewHeight() * touchPosY; List<GlAlbumCover> visibleAlbums = mapRenderer.getAlbums(); for (GlAlbumCover glAlbum : visibleAlbums) { MapAlbum mapAlbum = glAlbum.getMapAlbum(); if (mapAlbum.getGridCoords()[1] < screenMinZ) { continue; } if (mapAlbum.getGridCoords()[1] > screenMaxZ) { break; } if (mapTouchX > mapAlbum.getGridCoords()[0] - 2 * GlAlbumCover.COVER_SIZE && mapTouchX < mapAlbum.getGridCoords()[0] + 2 * GlAlbumCover.COVER_SIZE && mapTouchY > mapAlbum.getGridCoords()[1] - 2 * GlAlbumCover.COVER_SIZE && mapTouchY < mapAlbum.getGridCoords()[1] + 2 * GlAlbumCover.COVER_SIZE) { Log.v("Selected", mapAlbum.getName()); controller.doHapticFeedback(); showAlbumDetailInfo(activity, mapAlbum); } } } protected void showAlbumDetailInfo(JukefoxActivity activity, MapAlbum mapAlbum) { controller.showAlbumDetailInfo(activity, mapAlbum); } private boolean handleTouchMoveEvent(MotionEvent event) { float diffX = event.getX() - lastTouchPosX; float diffY = event.getY() - lastTouchPosY; if (isCreatingRegionPlaylist) { if (Math.abs(event.getX() - lastTouchPosX) > 10 || Math.abs(event.getY() - lastTouchPosY) > 10) { if (regionPlaylist != null) { regionPlaylist.addPoint(lastTouchPosX, lastTouchPosY); } } else { return true; } } else { Pair<Float, Float> newCamPosDiff = GET_MOVEMENT_FACTOR(mapRenderer, diffX, diffY); mapRenderer.getCamera().setCameraPosition(mapRenderer.getCamera().getPosX() + newCamPosDiff.first, mapRenderer.getCamera().getPosY(), mapRenderer.getCamera().getPosZ() + newCamPosDiff.second, isKineticScrolling); } lastTouchPosX = event.getX(); lastTouchPosY = event.getY(); if (AndroidUtils.distance(lastTouchPosX, lastTouchPosY, touchDownPosX, touchDownPosY) > CLICK_DIST_THRESH) { canBeClick = false; cancelRegionSelectHoldTimeTimer(); } // if (mapActivity != null && canBeClick // && (event.getEventTime() - touchDownTime) > 1500) { // long // // click // canBeClick = false; // mapActivity.showRegionSelectDialog(); // } return true; } public static Pair<Float, Float> GET_MOVEMENT_FACTOR(MapRenderer mapRenderer, float diffX, float diffY) { float xFactor = diffX / mapRenderer.getViewWidth() * mapRenderer.getCamera().getPosY(); float yFactor = diffY / mapRenderer.getViewHeight() * mapRenderer.getCamera().getPosY(); float newCamPosXDiff = -2f * mapRenderer.getViewRatio() / mapRenderer.getCamera().getFrontClippingPlane() * xFactor; float newCamPosZDiff = -2f / mapRenderer.getCamera().getFrontClippingPlane() * yFactor; return new Pair<Float, Float>(newCamPosXDiff, newCamPosZDiff); } private boolean handleTouchDownEvent(MotionEvent event) { initTouchDownPositions(event); startRegionSelectHoldTimeTimer(); canBeClick = true; if (isCreatingRegionPlaylist) { regionPlaylist = new RegionPlaylistCreator(mapRenderer, this, lastTouchPosX, lastTouchPosY); mapRenderer.startDrawingRegionPlaylist(regionPlaylist); } return true; } public void multiTouchFinished(MotionEvent event) { initTouchDownPositions(event); } private void initTouchDownPositions(MotionEvent event) { if (isKineticScrolling) { mapRenderer.getCamera().setGrasped(true); } lastTouchPosX = event.getX(); lastTouchPosY = event.getY(); touchDownPosX = lastTouchPosX; touchDownPosY = lastTouchPosY; touchDownTime = event.getDownTime(); } public boolean onZoomBarTouch(View v, MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { lastZoomBarTouchY = event.getY(); lastZoomBarMoveY = event.getY(); } if (event.getAction() == MotionEvent.ACTION_MOVE) { float absDiffY = Math.abs(event.getY() - lastZoomBarMoveY); if (absDiffY > 20) { if (event.getY() > lastZoomBarMoveY) { lastZoomBarMoveY += 20; } if (event.getY() < lastZoomBarMoveY) { lastZoomBarMoveY -= 20; } controller.doHapticFeedback(); } float diffY = event.getY() - lastZoomBarTouchY; lastZoomBarTouchY = event.getY(); mapRenderer.getCamera().setCameraPosition(mapRenderer.getCamera().getPosX(), mapRenderer.getCamera().getPosY() + diffY / 5, mapRenderer.getCamera().getPosZ(), false); } return true; } public boolean onZoomInTouch(View v, MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { controller.doHapticFeedback(); long currentTime = System.currentTimeMillis(); if (currentTime - lastZoomClickTime < 500 && currentTime - lastToastTime > 20 * 1000) { Toast.makeText(controller.getApplicationContext(), R.string.use_zoom_bar, Toast.LENGTH_LONG).show(); lastToastTime = currentTime; } lastZoomClickTime = currentTime; mapRenderer.getCamera().setCameraPosition(mapRenderer.getCamera().getPosX(), mapRenderer.getCamera().getPosY() - 6f, mapRenderer.getCamera().getPosZ(), false); } return true; } public boolean onZoomOutTouch(View v, MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { controller.doHapticFeedback(); long currentTime = System.currentTimeMillis(); if (currentTime - lastZoomClickTime < 500 && currentTime - lastToastTime > 20 * 1000) { Toast.makeText(controller.getApplicationContext(), R.string.use_zoom_bar, Toast.LENGTH_LONG).show(); lastToastTime = currentTime; } lastZoomClickTime = currentTime; mapRenderer.getCamera().setCameraPosition(mapRenderer.getCamera().getPosX(), mapRenderer.getCamera().getPosY() + 6f, mapRenderer.getCamera().getPosZ(), false); } return true; } public void setKineticMovement(boolean kineticMovement) { isKineticScrolling = kineticMovement; } public void onPause() { controller.getSettingsEditor().setLastPositionInPcaMapX(mapRenderer.getCamera().getPosX()); controller.getSettingsEditor().setLastPositionInPcaMapY(mapRenderer.getCamera().getPosZ()); Log.v(TAG, "Saving cam pos x: " + mapRenderer.getCamera().getPosX() + " y: " + mapRenderer.getCamera().getPosZ()); } // @Override // public void onRegionCreated(List<MapAlbum> albumsInRegion) { // // boolean at_least_one_album = false; // // List<PlaylistSong<BaseArtist, BaseAlbum>> songs = new // ArrayList<PlaylistSong<BaseArtist, BaseAlbum>>(); // for (MapAlbum mapAlbum : albumsInRegion) { // CompleteAlbum album; // try { // album = activity.getData().getCompleteAlbumBlocking(mapAlbum); // } catch (DataUnavailableException e) { // Log.w(TAG, e); // continue; // Ignore this album // } // // if (!at_least_one_album) { // controller.getPlaylistController().clearPlaylist(); // } // // for (BaseSong<BaseArtist, BaseAlbum> song : album.getSongs()) { // controller.getPlaylistController().appendSongAtEnd( // new PlaylistSong<BaseArtist, BaseAlbum>(song, // SongSource.MANUALLY_SELECTED)); // } // // // if (!at_least_one_album) { // at_least_one_album = true; // // // Set play-mode to shuffle and play // controller.getPlaylistController().setPlayMode( // PlayModeType.SHUFFLE_PLAYLIST); // try { // controller.getPlayManager().play(); // controller.getPlaylistController().next(true); // TODO // // verify // // 'true' // } catch (Exception e) { // Log.w(TAG, e); // } // } // } // } @Override public void onRegionCreated(List<MapAlbum> albumsInRegion) { long startTime = System.currentTimeMillis(); controller.showProgressDialog(activity, activity.getString(R.string.loading)); try { List<PlaylistSong<BaseArtist, BaseAlbum>> songs = new ArrayList<PlaylistSong<BaseArtist, BaseAlbum>>(); for (MapAlbum mapAlbum : albumsInRegion) { CompleteAlbum album; try { album = activity.getCollectionModel().getAlbumProvider().getCompleteAlbum(mapAlbum); } catch (DataUnavailableException e) { Log.w(TAG, e); continue; // Ignore this album } for (BaseSong<BaseArtist, BaseAlbum> song : album.getSongs()) { songs.add(new PlaylistSong<BaseArtist, BaseAlbum>(song, SongSource.MANUALLY_SELECTED)); } } if (songs.size() == 0) { return; } long intermediateTime = System.currentTimeMillis(); Log.v(TAG, "number of songs in region playlist: " + songs.size()); controller.getPlayerController().clearPlaylist(); controller.getPlayerController().appendSongsAtEnd(songs); long endTime = System.currentTimeMillis(); Log.v(TAG, "timing: total: " + (endTime - startTime) + ", reading: " + (intermediateTime - startTime)); // Set play-mode to shuffle and play controller.getPlayerController().setPlayMode(PlayModeType.SHUFFLE_PLAYLIST, 0, Constants.SAME_SONG_AVOIDANCE_NUM); try { controller.getPlayerController().playSongAtPosition(RandomProvider.getRandom().nextInt(songs.size())); } catch (Exception e) { Log.w(TAG, e); } } finally { controller.removeProgressDialog(); } } private synchronized void startRegionSelectHoldTimeTimer() { if (mapActivity == null) { return; } cancelRegionSelectHoldTimeTimer(); regionSelectHoldTimeTimer = new Timer(); regionSelectHoldTimeTimer.schedule(new TimerTask() { @Override public void run() { canBeClick = false; regionSelectHoldTimeTimer = null; JukefoxApplication.getHandler().post(new Runnable() { @Override public void run() { mapActivity.showRegionSelectDialog(); } }); } }, 1500); } public void cancelRegionSelectHoldTimeTimer() { if (regionSelectHoldTimeTimer != null) { regionSelectHoldTimeTimer.cancel(); regionSelectHoldTimeTimer = null; } } }