Home | History | Annotate | Download | only in media
      1 /*
      2  * Copyright (C) 2009 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.cooliris.media;
     18 
     19 import android.content.Context;
     20 import android.hardware.Sensor;
     21 import android.hardware.SensorEvent;
     22 import android.os.SystemClock;
     23 import android.view.GestureDetector;
     24 import android.view.KeyEvent;
     25 import android.view.MotionEvent;
     26 
     27 import com.cooliris.app.App;
     28 
     29 public final class GridInputProcessor implements GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener,
     30         ScaleGestureDetector.OnScaleGestureListener {
     31     private int mCurrentFocusSlot;
     32     private boolean mCurrentFocusIsPressed;
     33     private int mCurrentSelectedSlot;
     34 
     35     private float mPrevTiltValueLowPass;
     36     private float mPrevShakeValueHighPass;
     37     private float mShakeValue;
     38     private int mTouchPosX;
     39     private int mTouchPosY;
     40     private int mActionCode;
     41     private long mPrevTouchTime;
     42     private float mFirstTouchPosX;
     43     private float mFirstTouchPosY;
     44     private float mPrevTouchPosX;
     45     private float mPrevTouchPosY;
     46     private float mTouchVelX;
     47     private float mTouchVelY;
     48     private boolean mProcessTouch;
     49     private boolean mTouchMoved;
     50     private float mDpadIgnoreTime = 0.0f;
     51     private GridCamera mCamera;
     52     private GridLayer mLayer;
     53     private Context mContext;
     54     private Pool<Vector3f> mPool;
     55     private DisplayItem[] mDisplayItems;
     56     private boolean mPrevHitEdge;
     57     private boolean mTouchFeedbackDelivered;
     58     private GestureDetector mGestureDetector;
     59     private ScaleGestureDetector mScaleGestureDetector;
     60     private boolean mZoomGesture;
     61     private int mCurrentScaleSlot;
     62     private float mScale;
     63 
     64     public GridInputProcessor(Context context, GridCamera camera, GridLayer layer, RenderView view, Pool<Vector3f> pool,
     65             DisplayItem[] displayItems) {
     66         mPool = pool;
     67         mCamera = camera;
     68         mLayer = layer;
     69         mCurrentFocusSlot = Shared.INVALID;
     70         mCurrentSelectedSlot = Shared.INVALID;
     71         mCurrentScaleSlot = Shared.INVALID;
     72         mContext = context;
     73         mDisplayItems = displayItems;
     74         mGestureDetector = new GestureDetector(context, this);
     75         mScaleGestureDetector = new ScaleGestureDetector(context, this);
     76         mGestureDetector.setIsLongpressEnabled(true);
     77         mZoomGesture = false;
     78         mScale = 1.0f;
     79     }
     80 
     81     public int getCurrentFocusSlot() {
     82         return mCurrentFocusSlot;
     83     }
     84 
     85     public int getCurrentSelectedSlot() {
     86         return mCurrentSelectedSlot;
     87     }
     88 
     89     public int getCurrentScaledSlot() {
     90         return mCurrentScaleSlot;
     91     }
     92 
     93     public void setCurrentSelectedSlot(int slot) {
     94         mCurrentSelectedSlot = slot;
     95         GridLayer layer = mLayer;
     96         layer.setState(GridLayer.STATE_FULL_SCREEN);
     97         mCamera.mConvergenceSpeed = 2.0f;
     98         mCamera.mFriction = 0.0f;
     99         DisplayItem displayItem = layer.getDisplayItemForSlotId(slot);
    100         MediaItem item = null;
    101         if (displayItem != null)
    102             item = displayItem.mItemRef;
    103         layer.getHud().fullscreenSelectionChanged(item, mCurrentSelectedSlot + 1, layer.getCompleteRange().end + 1);
    104     }
    105 
    106     public void onSensorChanged(RenderView view, SensorEvent event, int state) {
    107         if (mZoomGesture)
    108             return;
    109         switch (event.sensor.getType()) {
    110         case Sensor.TYPE_ACCELEROMETER:
    111             float[] values = event.values;
    112             float valueToUse = (mCamera.mWidth < mCamera.mHeight) ? values[0] : -values[1];
    113             float tiltValue = 0.8f * mPrevTiltValueLowPass + 0.2f * valueToUse;
    114             if (Math.abs(tiltValue) < 0.5f)
    115                 tiltValue = 0.0f;
    116             if (state == GridLayer.STATE_FULL_SCREEN)
    117                 tiltValue = 0.0f;
    118             if (tiltValue != 0.0f)
    119                 view.requestRender();
    120             mCamera.mEyeOffsetX = -3.0f * tiltValue;
    121             float shakeValue = values[1] * values[1] + values[2] * values[2];
    122             mShakeValue = shakeValue - mPrevShakeValueHighPass;
    123             mPrevShakeValueHighPass = shakeValue;
    124             if (mShakeValue < 16.0f) {
    125                 mShakeValue = 0;
    126             } else {
    127                 mShakeValue = mShakeValue * 4.0f;
    128                 if (mShakeValue > 200) {
    129                     mShakeValue = 200;
    130                 }
    131             }
    132             break;
    133         }
    134     }
    135 
    136     public boolean onTouchEvent(MotionEvent event) {
    137         mTouchPosX = (int) (event.getX());
    138         mTouchPosY = (int) (event.getY());
    139         mActionCode = event.getAction();
    140         long timestamp = SystemClock.elapsedRealtime();
    141         long delta = timestamp - mPrevTouchTime;
    142         mPrevTouchTime = timestamp;
    143         float timeElapsed = (float) delta;
    144         timeElapsed = timeElapsed * 0.001f; // division by 1000 for seconds
    145         switch (mActionCode) {
    146         case MotionEvent.ACTION_UP:
    147             touchEnded(mTouchPosX, mTouchPosY, timeElapsed);
    148             break;
    149         case MotionEvent.ACTION_DOWN:
    150             mPrevTouchTime = timestamp;
    151             touchBegan(mTouchPosX, mTouchPosY);
    152             break;
    153         case MotionEvent.ACTION_MOVE:
    154             touchMoved(mTouchPosX, mTouchPosY, timeElapsed);
    155             break;
    156         }
    157         if (!mZoomGesture)
    158             mGestureDetector.onTouchEvent(event);
    159         mScaleGestureDetector.onTouchEvent(event);
    160         return true;
    161     }
    162 
    163     public boolean onKeyDown(int keyCode, KeyEvent event, int state) {
    164         GridLayer layer = mLayer;
    165         if (keyCode == KeyEvent.KEYCODE_BACK) {
    166             if (layer.getViewIntent())
    167                 return false;
    168             if (layer.getHud().getMode() == HudLayer.MODE_SELECT) {
    169                 layer.deselectAll();
    170                 return true;
    171             }
    172             if (layer.inSlideShowMode()) {
    173                 layer.endSlideshow();
    174                 layer.getHud().setAlpha(1.0f);
    175                 return true;
    176             }
    177             float zoomValue = layer.getZoomValue();
    178             if (zoomValue != 1.0f) {
    179                 layer.setZoomValue(1.0f);
    180                 layer.centerCameraForSlot(mCurrentSelectedSlot, 1.0f);
    181                 return true;
    182             }
    183             layer.goBack();
    184             if (state == GridLayer.STATE_MEDIA_SETS)
    185                 return false;
    186             return true;
    187         }
    188         if (mDpadIgnoreTime < 0.1f)
    189             return true;
    190         mDpadIgnoreTime = 0.0f;
    191         IndexRange bufferedVisibleRange = layer.getBufferedVisibleRange();
    192         int firstBufferedVisibleSlot = bufferedVisibleRange.begin;
    193         int lastBufferedVisibleSlot = bufferedVisibleRange.end;
    194         int anchorSlot = layer.getAnchorSlotIndex(GridLayer.ANCHOR_CENTER);
    195         if (state == GridLayer.STATE_FULL_SCREEN) {
    196             if (keyCode != KeyEvent.KEYCODE_VOLUME_UP && keyCode != KeyEvent.KEYCODE_VOLUME_DOWN
    197                     && keyCode != KeyEvent.KEYCODE_MUTE && keyCode != KeyEvent.KEYCODE_HEADSETHOOK
    198                     && keyCode != KeyEvent.KEYCODE_NOTIFICATION) {
    199                 layer.endSlideshow();
    200             }
    201             boolean needsVibrate = false;
    202             boolean directionalKeyPressed = false;
    203             if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
    204                 needsVibrate = !layer.changeFocusToNextSlot(1.0f);
    205                 directionalKeyPressed = true;
    206             }
    207             if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) {
    208                 needsVibrate = !layer.changeFocusToPreviousSlot(1.0f);
    209                 directionalKeyPressed = true;
    210             }
    211             if (directionalKeyPressed) {
    212                 if (layer.getHud().getMode() == HudLayer.MODE_SELECT) {
    213                     layer.deselectAll();
    214                     layer.enterSelectionMode();
    215                 }
    216             }
    217             if (needsVibrate) {
    218                 vibrateShort();
    219             }
    220             if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER && !mCamera.isAnimating()) {
    221                 if (layer.getZoomValue() == 1.0f)
    222                     layer.zoomInToSelectedItem();
    223                 else
    224                     layer.setZoomValue(1.0f);
    225             }
    226             if (keyCode == KeyEvent.KEYCODE_MENU) {
    227                 if (mLayer.getFeed() != null && mLayer.getFeed().isSingleImageMode()) {
    228                     return true;
    229                 }
    230                 if (layer.getHud().getMode() == HudLayer.MODE_NORMAL)
    231                     layer.enterSelectionMode();
    232                 else
    233                     layer.deselectAll();
    234             }
    235         } else {
    236             mCurrentFocusIsPressed = false;
    237             int numRows = ((GridLayoutInterface) layer.getLayoutInterface()).mNumRows;
    238             if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER && mCurrentFocusSlot != Shared.INVALID) {
    239                 if (layer.getHud().getMode() != HudLayer.MODE_SELECT) {
    240                     boolean centerCamera = layer.tapGesture(mCurrentFocusSlot, false);
    241                     if (centerCamera) {
    242                         int slotId = mCurrentFocusSlot;
    243                         selectSlot(slotId);
    244                     }
    245                     mCurrentFocusSlot = Shared.INVALID;
    246                     return true;
    247                 } else {
    248                     layer.addSlotToSelectedItems(mCurrentFocusSlot, true, true);
    249                 }
    250                 mCurrentFocusIsPressed = true;
    251             } else if (keyCode == KeyEvent.KEYCODE_MENU && mCurrentFocusSlot != Shared.INVALID) {
    252                 if (layer.getHud().getMode() == HudLayer.MODE_NORMAL)
    253                     layer.enterSelectionMode();
    254                 else
    255                     layer.deselectAll();
    256             } else if (mCurrentFocusSlot == Shared.INVALID) {
    257                 mCurrentFocusSlot = anchorSlot;
    258             } else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
    259                 mCurrentFocusSlot += numRows;
    260             } else if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) {
    261                 mCurrentFocusSlot -= numRows;
    262             } else if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
    263                 --mCurrentFocusSlot;
    264             } else if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
    265                 ++mCurrentFocusSlot;
    266             }
    267             if (mCurrentFocusSlot > lastBufferedVisibleSlot) {
    268                 mCurrentFocusSlot = lastBufferedVisibleSlot;
    269             }
    270             if (mCurrentFocusSlot < firstBufferedVisibleSlot)
    271                 mCurrentFocusSlot = firstBufferedVisibleSlot;
    272             if (mCurrentFocusSlot != Shared.INVALID) {
    273                 layer.centerCameraForSlot(mCurrentFocusSlot, 1.0f);
    274             }
    275         }
    276         return false;
    277     }
    278 
    279     private void touchBegan(int posX, int posY) {
    280         mPrevTouchPosX = posX;
    281         mPrevTouchPosY = posY;
    282         mFirstTouchPosX = posX;
    283         mFirstTouchPosY = posY;
    284         mTouchVelX = 0;
    285         mTouchVelY = 0;
    286         mProcessTouch = true;
    287         mZoomGesture = false;
    288         mTouchMoved = false;
    289         mCamera.stopMovementInX();
    290         GridLayer layer = mLayer;
    291         mCurrentFocusSlot = layer.getSlotIndexForScreenPosition(posX, posY);
    292         mCurrentFocusIsPressed = true;
    293         mTouchFeedbackDelivered = false;
    294         HudLayer hud = layer.getHud();
    295         if (hud.getMode() == HudLayer.MODE_SELECT)
    296             hud.closeSelectionMenu();
    297         if (layer.getState() == GridLayer.STATE_FULL_SCREEN && hud.getMode() == HudLayer.MODE_SELECT) {
    298             layer.deselectAll();
    299             hud.setAlpha(1.0f);
    300         }
    301         int slotId = layer.getSlotIndexForScreenPosition(posX, posY);
    302         if (slotId != Shared.INVALID && layer.getState() != GridLayer.STATE_FULL_SCREEN) {
    303             vibrateShort();
    304         }
    305     }
    306 
    307     private void touchMoved(int posX, int posY, float timeElapsedx) {
    308         if (mProcessTouch && !mZoomGesture) {
    309             GridLayer layer = mLayer;
    310             GridCamera camera = mCamera;
    311             float deltaX = -(posX - mPrevTouchPosX); // negation since the wall
    312             // moves in a direction
    313             // opposite to that of
    314             // the touch
    315             float deltaY = -(posY - mPrevTouchPosY);
    316             if (Math.abs(deltaX) >= 10.0f || Math.abs(deltaY) >= 10.0f) {
    317                 mTouchMoved = true;
    318             }
    319             Pool<Vector3f> pool = mPool;
    320             Vector3f firstPosition = pool.create();
    321             Vector3f lastPosition = pool.create();
    322             Vector3f deltaAnchorPosition = pool.create();
    323             Vector3f worldPosDelta = pool.create();
    324             try {
    325                 deltaAnchorPosition.set(layer.getDeltaAnchorPosition());
    326                 LayoutInterface layout = layer.getLayoutInterface();
    327                 GridCameraManager.getSlotPositionForSlotIndex(0, camera, layout, deltaAnchorPosition, firstPosition);
    328                 int lastSlotIndex = 0;
    329                 IndexRange completeRange = layer.getCompleteRange();
    330                 synchronized (completeRange) {
    331                     lastSlotIndex = completeRange.end;
    332                 }
    333                 GridCameraManager.getSlotPositionForSlotIndex(lastSlotIndex, camera, layout, deltaAnchorPosition, lastPosition);
    334 
    335                 camera.convertToRelativeCameraSpace(deltaX, deltaY, 0, worldPosDelta);
    336                 deltaX = worldPosDelta.x;
    337                 deltaY = worldPosDelta.y;
    338                 camera.moveBy(deltaX, (layer.getZoomValue() == 1.0f) ? 0 : deltaY, 0);
    339                 deltaX *= camera.mScale;
    340                 deltaY *= camera.mScale;
    341             } finally {
    342                 pool.delete(firstPosition);
    343                 pool.delete(lastPosition);
    344                 pool.delete(deltaAnchorPosition);
    345                 pool.delete(worldPosDelta);
    346             }
    347             if (layer.getZoomValue() == 1.0f) {
    348                 if (camera
    349                         .computeConstraints(false, (layer.getState() != GridLayer.STATE_FULL_SCREEN), firstPosition, lastPosition)) {
    350                     deltaX = 0.0f;
    351                     // vibrate
    352                     if (!mTouchFeedbackDelivered) {
    353                         mTouchFeedbackDelivered = true;
    354                         vibrateLong();
    355                     }
    356                 }
    357             }
    358             mTouchVelX = deltaX * timeElapsedx;
    359             mTouchVelY = deltaY * timeElapsedx;
    360             float maxVelXx = (mCamera.mWidth * 0.5f);
    361             float maxVelYx = (mCamera.mHeight);
    362             mTouchVelX = FloatUtils.clamp(mTouchVelX, -maxVelXx, maxVelXx);
    363             mTouchVelY = FloatUtils.clamp(mTouchVelY, -maxVelYx, maxVelYx);
    364             mPrevTouchPosX = posX;
    365             mPrevTouchPosY = posY;
    366             // you want the movement to track the finger immediately
    367             if (mTouchMoved == false)
    368                 mCurrentFocusSlot = layer.getSlotIndexForScreenPosition(posX, posY);
    369             else
    370                 mCurrentFocusSlot = Shared.INVALID;
    371             if (!mCamera.isZAnimating()) {
    372                 mCamera.commitMoveInX();
    373                 mCamera.commitMoveInY();
    374             }
    375             int anchorSlotIndex = layer.getAnchorSlotIndex(GridLayer.ANCHOR_LEFT);
    376             DisplayItem[] displayItems = mDisplayItems;
    377             IndexRange bufferedVisibleRange = layer.getBufferedVisibleRange();
    378             int firstBufferedVisibleSlot = bufferedVisibleRange.begin;
    379             int lastBufferedVisibleSlot = bufferedVisibleRange.end;
    380             synchronized (displayItems) {
    381                 if (anchorSlotIndex >= firstBufferedVisibleSlot && anchorSlotIndex <= lastBufferedVisibleSlot) {
    382                     DisplayItem item = displayItems[(anchorSlotIndex - firstBufferedVisibleSlot) * GridLayer.MAX_ITEMS_PER_SLOT];
    383                     if (item != null) {
    384                         layer.getHud().setTimeBarTime(item.mItemRef.mDateTakenInMs);
    385                     }
    386                 }
    387             }
    388         }
    389     }
    390 
    391     private void touchEnded(int posX, int posY, float timeElapsedx) {
    392         if (mProcessTouch == false || mZoomGesture) {
    393             return;
    394         }
    395         int maxPixelsBeforeSwitch = mCamera.mWidth / 8;
    396         mCamera.mConvergenceSpeed = 2.0f;
    397         GridLayer layer = mLayer;
    398         if (layer.getExpandedSlot() == Shared.INVALID && !layer.feedAboutToChange() && !mZoomGesture) {
    399             if (mCurrentSelectedSlot != Shared.INVALID) {
    400                 if (layer.getState() == GridLayer.STATE_FULL_SCREEN) {
    401                     if (!mTouchMoved) {
    402                         // tap gesture for fullscreen
    403                         if (layer.getZoomValue() == 1.0f)
    404                             layer.changeFocusToSlot(mCurrentSelectedSlot, 1.0f);
    405                     } else if (layer.getZoomValue() == 1.0f) {
    406                         // we want to snap to a new slotIndex based on where the
    407                         // current position is
    408                         if (layer.inSlideShowMode()) {
    409                             layer.endSlideshow();
    410                         }
    411                         float deltaX = posX - mFirstTouchPosX;
    412                         float deltaY = posY - mFirstTouchPosY;
    413                         if (deltaY != 0) {
    414                             // it has moved vertically
    415                         }
    416                         layer.changeFocusToSlot(mCurrentSelectedSlot, 1.0f);
    417                         HudLayer hud = layer.getHud();
    418                         if (deltaX > maxPixelsBeforeSwitch && hud.getMode() != HudLayer.MODE_SELECT) {
    419                             layer.changeFocusToPreviousSlot(1.0f);
    420                         } else if (deltaX < -maxPixelsBeforeSwitch && hud.getMode() != HudLayer.MODE_SELECT) {
    421                             layer.changeFocusToNextSlot(1.0f);
    422                         }
    423                     } else {
    424                         // in zoomed state
    425                         // we do nothing for now, but we should clamp to the
    426                         // image bounds
    427                         boolean hitEdge = layer.constrainCameraForSlot(mCurrentSelectedSlot);
    428                         // mPrevHitEdge = false;
    429                         if (hitEdge && mPrevHitEdge) {
    430                             float deltaX = posX - mFirstTouchPosX;
    431                             float deltaY = posY - mFirstTouchPosY;
    432                             maxPixelsBeforeSwitch *= 4;
    433                             if (deltaY != 0) {
    434                                 // it has moved vertically
    435                             }
    436                             mPrevHitEdge = false;
    437                             HudLayer hud = layer.getHud();
    438                             if (deltaX > maxPixelsBeforeSwitch && hud.getMode() != HudLayer.MODE_SELECT) {
    439                                 layer.changeFocusToPreviousSlot(1.0f);
    440                             } else if (deltaX < -maxPixelsBeforeSwitch && hud.getMode() != HudLayer.MODE_SELECT) {
    441                                 layer.changeFocusToNextSlot(1.0f);
    442                             } else {
    443                                 mPrevHitEdge = hitEdge;
    444                             }
    445                         } else {
    446                             mPrevHitEdge = hitEdge;
    447                         }
    448                     }
    449                 }
    450             } else {
    451                 if (!layer.feedAboutToChange() && layer.getZoomValue() == 1.0f && mTouchMoved) {
    452                     constrainCamera(true);
    453                 }
    454             }
    455         }
    456         mCurrentFocusSlot = Shared.INVALID;
    457         mCurrentFocusIsPressed = false;
    458         mPrevTouchPosX = posX;
    459         mPrevTouchPosY = posY;
    460         mProcessTouch = false;
    461     }
    462 
    463     private void constrainCamera(boolean b) {
    464         Pool<Vector3f> pool = mPool;
    465         GridLayer layer = mLayer;
    466         Vector3f firstPosition = pool.create();
    467         Vector3f lastPosition = pool.create();
    468         Vector3f deltaAnchorPosition = pool.create();
    469         try {
    470             deltaAnchorPosition.set(layer.getDeltaAnchorPosition());
    471             GridCamera camera = mCamera;
    472             LayoutInterface layout = layer.getLayoutInterface();
    473             GridCameraManager.getSlotPositionForSlotIndex(0, camera, layout, deltaAnchorPosition, firstPosition);
    474             int lastSlotIndex = 0;
    475             IndexRange completeRange = layer.getCompleteRange();
    476             synchronized (completeRange) {
    477                 lastSlotIndex = completeRange.end;
    478             }
    479             GridCameraManager.getSlotPositionForSlotIndex(lastSlotIndex, camera, layout, deltaAnchorPosition, lastPosition);
    480             camera.computeConstraints(true, (layer.getState() != GridLayer.STATE_FULL_SCREEN), firstPosition, lastPosition);
    481         } finally {
    482             pool.delete(firstPosition);
    483             pool.delete(lastPosition);
    484             pool.delete(deltaAnchorPosition);
    485         }
    486     }
    487 
    488     public void clearSelection() {
    489         mCurrentSelectedSlot = Shared.INVALID;
    490     }
    491 
    492     public void clearFocus() {
    493         mCurrentFocusSlot = Shared.INVALID;
    494     }
    495 
    496     public boolean isFocusItemPressed() {
    497         return mCurrentFocusIsPressed;
    498     }
    499 
    500     public void update(float timeElapsed) {
    501         mDpadIgnoreTime += timeElapsed;
    502         if (mCamera.mFriction != 0.0f) {
    503             constrainCamera(true);
    504         }
    505     }
    506 
    507     public void setCurrentFocusSlot(int slotId) {
    508         mCurrentSelectedSlot = slotId;
    509     }
    510 
    511     public boolean onDown(MotionEvent e) {
    512         // TODO Auto-generated method stub
    513         return true;
    514     }
    515 
    516     public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
    517         if (mCurrentSelectedSlot == Shared.INVALID) {
    518             mCamera.moveYTo(0);
    519             mCamera.moveZTo(0);
    520             mCamera.mConvergenceSpeed = 1.0f;
    521             mCamera.mFriction = 0.0f;
    522             float normalizedVelocity = velocityX * mCamera.mOneByScale;
    523             // mCamera.moveBy(-velocityX * mCamera.mOneByScale * 0.25f, 0, 0);
    524             // constrainCamera(true);
    525             IndexRange visibleRange = mLayer.getVisibleRange();
    526             int numVisibleSlots = visibleRange.end - visibleRange.begin;
    527             if (numVisibleSlots > 0) {
    528                 float fastFlingVelocity = 10.0f;
    529                 int slotsToSkip = (int) (numVisibleSlots * (-normalizedVelocity / fastFlingVelocity));
    530                 int maxSlots = numVisibleSlots;
    531                 if (slotsToSkip > maxSlots)
    532                     slotsToSkip = maxSlots;
    533                 if (slotsToSkip < -maxSlots)
    534                     slotsToSkip = -maxSlots;
    535                 if (Math.abs(slotsToSkip) <= 1) {
    536                     if (velocityX > 0)
    537                         slotsToSkip = -2;
    538                     else if (velocityX < 0)
    539                         slotsToSkip = 2;
    540                 }
    541                 int slotToGetTo = mLayer.getAnchorSlotIndex(GridLayer.ANCHOR_CENTER) + slotsToSkip;
    542                 if (slotToGetTo < 0)
    543                     slotToGetTo = 0;
    544                 int lastSlot = mLayer.getCompleteRange().end;
    545                 if (slotToGetTo > lastSlot)
    546                     slotToGetTo = lastSlot;
    547                 mLayer.centerCameraForSlot(slotToGetTo, 1.0f);
    548             }
    549             constrainCamera(true);
    550             return true;
    551         } else {
    552             return false;
    553         }
    554     }
    555 
    556     public void onLongPress(MotionEvent e) {
    557         if (mZoomGesture)
    558             return;
    559         if (mLayer.getFeed() != null && mLayer.getFeed().isSingleImageMode()) {
    560             HudLayer hud = mLayer.getHud();
    561             hud.getPathBar().setHidden(true);
    562             hud.getMenuBar().setHidden(true);
    563             if (hud.getMode() != HudLayer.MODE_NORMAL)
    564                 hud.setMode(HudLayer.MODE_NORMAL);
    565         }
    566         if (mCurrentFocusSlot != Shared.INVALID) {
    567             vibrateLong();
    568             GridLayer layer = mLayer;
    569             if (layer.getState() == GridLayer.STATE_FULL_SCREEN) {
    570                 layer.deselectAll();
    571             }
    572             HudLayer hud = layer.getHud();
    573             hud.enterSelectionMode();
    574             layer.addSlotToSelectedItems(mCurrentFocusSlot, true, true);
    575         }
    576     }
    577 
    578     public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
    579         // TODO Auto-generated method stub
    580         return false;
    581     }
    582 
    583     public void onShowPress(MotionEvent e) {
    584         // TODO Auto-generated method stub
    585 
    586     }
    587 
    588     public boolean onSingleTapUp(MotionEvent e) {
    589         GridLayer layer = mLayer;
    590         int posX = (int) e.getX();
    591         int posY = (int) e.getY();
    592         if (mCurrentSelectedSlot != Shared.INVALID) {
    593             // Fullscreen mode.
    594             mCamera.mConvergenceSpeed = 2.0f;
    595             mCamera.mFriction = 0.0f;
    596             int slotId = mCurrentSelectedSlot;
    597             if (layer.getZoomValue() == 1.0f) {
    598                 layer.centerCameraForSlot(slotId, 1.0f);
    599             } else {
    600                 layer.constrainCameraForSlot(slotId);
    601             }
    602             DisplayItem displayItem = layer.getDisplayItemForSlotId(slotId);
    603             if (displayItem != null) {
    604                 final MediaItem item = displayItem.mItemRef;
    605                 int heightBy2 = mCamera.mHeight / 2;
    606                 boolean posYInBounds = (Math.abs(posY - heightBy2) < 64);
    607                 if (posX < 32 && posYInBounds) {
    608                     layer.changeFocusToPreviousSlot(1.0f);
    609                 } else if (posX > mCamera.mWidth - 32 && posYInBounds) {
    610                     layer.changeFocusToNextSlot(1.0f);
    611                 } else if (item.getMediaType() == MediaItem.MEDIA_TYPE_VIDEO) {
    612                     Utils.playVideo(mContext, item);
    613                 } else {
    614                     // We stop any slideshow.
    615                     HudLayer hud = layer.getHud();
    616                     if (layer.inSlideShowMode()) {
    617                         layer.endSlideshow();
    618                     } else {
    619                         hud.setAlpha(1.0f - hud.getAlpha());
    620                     }
    621                     if (hud.getMode() == HudLayer.MODE_SELECT) {
    622                         hud.setAlpha(1.0f);
    623                     }
    624                 }
    625             }
    626         } else {
    627             int slotId = layer.getSlotIndexForScreenPosition(posX, posY);
    628             if (slotId != Shared.INVALID) {
    629                 HudLayer hud = layer.getHud();
    630                 if (hud.getMode() == HudLayer.MODE_SELECT) {
    631                     layer.addSlotToSelectedItems(slotId, true, true);
    632                 } else {
    633                     boolean centerCamera = (mCurrentSelectedSlot == Shared.INVALID) ? layer.tapGesture(slotId, false) : true;
    634                     if (centerCamera) {
    635                         // We check if this item is a video or not.
    636                         selectSlot(slotId);
    637                     }
    638                 }
    639             } else {
    640                 int state = layer.getState();
    641                 if (state != GridLayer.STATE_FULL_SCREEN && state != GridLayer.STATE_GRID_VIEW
    642                         && layer.getHud().getMode() != HudLayer.MODE_SELECT) {
    643                     slotId = layer.getMetadataSlotIndexForScreenPosition(posX, posY);
    644                     if (slotId != Shared.INVALID) {
    645                         layer.tapGesture(slotId, true);
    646                     }
    647                 }
    648             }
    649         }
    650         return true;
    651     }
    652 
    653     private void selectSlot(int slotId) {
    654         GridLayer layer = mLayer;
    655         if (layer.getState() == GridLayer.STATE_GRID_VIEW) {
    656             DisplayItem displayItem = layer.getDisplayItemForSlotId(slotId);
    657             if (displayItem != null) {
    658                 final MediaItem item = displayItem.mItemRef;
    659                 if (layer.getPickIntent()) {
    660                     // we need to return this item
    661                     App.get(mContext).getHandler().post(new Runnable() {
    662                         public void run() {
    663                             CropImage.launchCropperOrFinish(mContext, item);
    664                         }
    665                     });
    666                     return;
    667                 }
    668                 if (item.getMediaType() == MediaItem.MEDIA_TYPE_VIDEO) {
    669                     Utils.playVideo(mContext, item);
    670                 } else {
    671                     mCurrentSelectedSlot = slotId;
    672                     layer.endSlideshow();
    673                     layer.setState(GridLayer.STATE_FULL_SCREEN);
    674                     mCamera.mConvergenceSpeed = 2.0f;
    675                     mCamera.mFriction = 0.0f;
    676                     layer.getHud().fullscreenSelectionChanged(item, mCurrentSelectedSlot + 1, layer.getCompleteRange().end + 1);
    677                 }
    678             }
    679         }
    680         constrainCamera(true);
    681     }
    682 
    683     public boolean onDoubleTap(MotionEvent e) {
    684         final GridLayer layer = mLayer;
    685         if (layer.getState() == GridLayer.STATE_FULL_SCREEN && !mCamera.isZAnimating()) {
    686             float posX = e.getX();
    687             float posY = e.getY();
    688             final Vector3f retVal = new Vector3f();
    689             posX -= (mCamera.mWidth / 2);
    690             posY -= (mCamera.mHeight / 2);
    691             mCamera.convertToRelativeCameraSpace(posX, posY, 0, retVal);
    692             if (layer.getZoomValue() == 1.0f) {
    693                 layer.setZoomValue(3f);
    694                 mCamera.update(0.001f);
    695                 mCamera.moveBy(retVal.x, retVal.y, 0);
    696                 layer.constrainCameraForSlot(mCurrentSelectedSlot);
    697             } else {
    698                 layer.setZoomValue(1.0f);
    699             }
    700             mCamera.mConvergenceSpeed = 2.0f;
    701             mCamera.mFriction = 0.0f;
    702         } else {
    703             return onSingleTapConfirmed(e);
    704         }
    705         return true;
    706     }
    707 
    708     public boolean onDoubleTapEvent(MotionEvent e) {
    709         return false;
    710     }
    711 
    712     public boolean onSingleTapConfirmed(MotionEvent e) {
    713         return false;
    714     }
    715 
    716     public boolean touchPressed() {
    717         return mProcessTouch;
    718     }
    719 
    720     private void vibrateShort() {
    721         // As per request by Google, this line disables vibration.
    722         // mView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
    723     }
    724 
    725     private void vibrateLong() {
    726         // mView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
    727     }
    728 
    729     public boolean onScale(ScaleGestureDetector detector) {
    730         final GridLayer layer = mLayer;
    731         float scale = detector.getScaleFactor();
    732         if (Float.isInfinite(scale) || Float.isNaN(scale))
    733             return true;
    734         mScale = scale * mScale;
    735         boolean performTranslation = Math.abs(scale - 1.0f) < 0.001f ? false : true;
    736         if (layer.getState() == GridLayer.STATE_FULL_SCREEN) {
    737             float currentScale = layer.getZoomValue();
    738             if (currentScale <= 1.0f)
    739                 performTranslation = false;
    740             final Vector3f retVal = new Vector3f();
    741             if (performTranslation) {
    742                 float posX = detector.getFocusX();
    743                 float posY = detector.getFocusY();
    744                 posX -= (mCamera.mWidth / 2);
    745                 posY -= (mCamera.mHeight / 2);
    746                 mCamera.convertToRelativeCameraSpace(posX, posY, 0, retVal);
    747             }
    748             if (currentScale < 0.7f && scale < 1.0f) {
    749                 scale = 1.0f;
    750             }
    751             if (currentScale > 8.0f && scale > 1.0f) {
    752                 scale = 1.0f;
    753             }
    754             layer.setZoomValue(currentScale * scale);
    755             if (performTranslation) {
    756                 mCamera.update(0.001f);
    757                 mCamera.moveBy(retVal.x, retVal.y, 0);
    758                 layer.constrainCameraForSlot(mCurrentSelectedSlot);
    759             }
    760         }
    761         if (mLayer.getState() == GridLayer.STATE_GRID_VIEW) {
    762             mCurrentScaleSlot = Shared.INVALID;
    763             mCurrentFocusSlot = Shared.INVALID;
    764         }
    765         return true;
    766     }
    767 
    768     public boolean onScaleBegin(ScaleGestureDetector detector) {
    769         mZoomGesture = true;
    770         mScale = 1.0f;
    771         mLayer.getHud().hideZoomButtons(true);
    772         int posX = (int) detector.getFocusX();
    773         int posY = (int) detector.getFocusY();
    774         int slotId = mLayer.getSlotIndexForScreenPosition(posX, posY);
    775         if (slotId == Shared.INVALID) {
    776             slotId = mLayer.getAnchorSlotIndex(GridLayer.ANCHOR_CENTER);
    777         }
    778         if (slotId != Shared.INVALID) {
    779             mCurrentScaleSlot = slotId;
    780             mCurrentFocusSlot = slotId;
    781         }
    782         if (mLayer.getState() == GridLayer.STATE_GRID_VIEW) {
    783             mCurrentScaleSlot = Shared.INVALID;
    784             mCurrentFocusSlot = Shared.INVALID;
    785         }
    786         constrainCamera(true);
    787         return true;
    788     }
    789 
    790     public void onScaleEnd(ScaleGestureDetector detector, boolean cancel) {
    791         if (!cancel) {
    792             final GridLayer layer = mLayer;
    793             if (layer.getState() == GridLayer.STATE_FULL_SCREEN) {
    794                 float currentScale = layer.getZoomValue();
    795                 if (currentScale < 1.0f) {
    796                     currentScale = 1.0f;
    797                 } else if (currentScale > 6.0f) {
    798                     currentScale = 6.0f;
    799                 }
    800                 if (currentScale != layer.getZoomValue()) {
    801                     layer.setZoomValue(currentScale);
    802                 }
    803                 layer.constrainCameraForSlot(mCurrentSelectedSlot);
    804                 mLayer.getHud().hideZoomButtons(false);
    805                 if (mScale < 0.7f && false) {
    806                     layer.goBack();
    807                 }
    808             } else {
    809                 if (mScale > 3.0f && false) {
    810                     HudLayer hud = layer.getHud();
    811                     if (hud.getMode() == HudLayer.MODE_SELECT) {
    812                         layer.addSlotToSelectedItems(mCurrentScaleSlot, true, true);
    813                     } else {
    814                         boolean centerCamera = (mCurrentSelectedSlot == Shared.INVALID) ? layer
    815                                 .tapGesture(mCurrentScaleSlot, false) : true;
    816                         if (centerCamera) {
    817                             // We check if this item is a video or not.
    818                             selectSlot(mCurrentScaleSlot);
    819                         }
    820                     }
    821                 } else {
    822                     if (layer.getState() == GridLayer.STATE_GRID_VIEW) {
    823                         if (mScale < 0.7f && false) {
    824                             layer.goBack();
    825                         }
    826                     }
    827                 }
    828             }
    829         } else {
    830             // The gesture was cancelled.
    831             final GridLayer layer = mLayer;
    832             if (layer.getState() == GridLayer.STATE_FULL_SCREEN) {
    833                 layer.setZoomValue(1.0f);
    834                 mLayer.getHud().hideZoomButtons(false);
    835             }
    836         }
    837         resetScale();
    838     }
    839 
    840     public float getScale() {
    841         return mScale;
    842     }
    843 
    844     public void resetScale() {
    845         mScale = 1.0f;
    846         mCurrentScaleSlot = Shared.INVALID;
    847     }
    848 
    849     public ScaleGestureDetector getScaleGestureDetector() {
    850         return mScaleGestureDetector;
    851     }
    852 }
    853