cz.metaverse.android.bilingualreader.helper.BookPanelOnTouchListener.java Source code

Java tutorial

Introduction

Here is the source code for cz.metaverse.android.bilingualreader.helper.BookPanelOnTouchListener.java

Source

/*
Bilingual Reader for Android
    
Copyright (c) 2015 Tom Orsava
    
This program 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.
    
This program 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 this
program (see the LICENSE.html file). If not, see http://www.gnu.org/licenses/.
    
Contact: gpl at orsava.cz
 */

package cz.metaverse.android.bilingualreader.helper;

import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.support.v4.view.GestureDetectorCompat;
import android.support.v4.view.MotionEventCompat;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.Window;
import cz.metaverse.android.bilingualreader.ReaderActivity;
import cz.metaverse.android.bilingualreader.dialog.PanelSizeDialog;
import cz.metaverse.android.bilingualreader.enums.BookPanelState;
import cz.metaverse.android.bilingualreader.manager.Governor;
import cz.metaverse.android.bilingualreader.manager.PanelHolder;
import cz.metaverse.android.bilingualreader.panel.BookPanel;
import cz.metaverse.android.bilingualreader.selectionwebview.SelectionWebView;

/**
 *
 *                                                       /===-_---~~~~~~~~~------____
 *                                                      |===-~___                _,-'
 *                       -==\\                         `//~\\   ~~~~`---.___.-~~
 *                   ______-==|                         | |  \\           _-~`
 *             __--~~~  ,-/-==\\                        | |   `\        ,'
 *          _-~       /'    |  \\                      / /      \      /
 *        .'        /       |   \\                   /' /        \   /'
 *       /  ____  /         |    \`\.__/-~~ ~ \ _ _/'  /          \/'
 *      /-'~    ~~~~~---__  |     ~-/~         ( )   /'        _--~`
 *                        \_|      /        _)   ;  ),   __--~~
 *                          '~~--_/      _-~/-  / \   '-~ \
 *                         {\__--_/}    / \\_>- )<__\      \
 *                         /'   (_/  _-~  | |__>--<__|      |
 *                        |0  0 _/) )-~     | |__>--<__|     |
 *                        / /~ ,_/       / /__>---<__/      |
 *                       o o _//        /-~_>---<__-~      /
 *                       (^(~          /~_>---<__-      _-~
 *                      ,/|           /__>--<__/     _-~
 *                   ,//('(          |__>--<__|     /                  .----_
 *                  ( ( '))          |__>--<__|    |                 /' _---_~\
 *               `-)) )) (           |__>--<__|    |               /'  /     ~\`\
 *              ,/,'//( (             \__>--<__\    \            /'  //        ||
 *            ,( ( ((, ))              ~-__>--<_~-_  ~--____---~' _/'/        /'
 *          `~/  )` ) ,/|                 ~-_~>--<_/-__       __-~ _/
 *        ._-~//( )/ )) `                    ~~-'_/_/ /~~~~~~~__--~
 *         ;'( ')/ ,)(                              ~~~~~~~~~~
 *        ' ') '( (/
 *          '   '  `
 *
 *                                HERE BE DRAGONS
 *
 *
 *
 * Be warned, stranger, editing this class is perilous.
 * Editing one functionality can and will have unforeseen adverse effects in other areas.
 * Test your changes profusely.
 *
 *
 *
 * This class serves as a OnTouchListener for our BookPanel.
 *
 * It comprises of a complex logic that allows to discern several single and multitouch gestures:
 *
 *   - Single finger swipe up/down
 *      - standard scrolling (implicit, not implemented here) with ScrollSync scrolling
 *        of the other panel, if ScrollSync is activated.
 *
 *   - Single finger swipe left/right - switch book chapter
 *
 *   - Two (or more) finger swipe up or down
 *      - standard scrolling with forced disabled ScrollSync, i.e. other panel won't never scroll,
 *        after the end of this scroll, the SelectionWebView is given notice to resume ScrollSync
 *        if appropriate and compute new offset/sync point. See SelectionWebView class.
 *
 *   - Double tap and swipe towards the side of the device (left/right in landscape, up/down in portrait)
 *      - Reappear the other panel if it is hidden, or hide this panel.
 *
 *   - Double tap and swipe towards or away from the center of the device
 *               (up/down in landscape, left/right in portrait)
 *      - Change the relative size of the two panels on the fly.
 *
 *
 */
public class BookPanelOnTouchListener
        implements OnTouchListener, GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener {

    // If the computed panel weight is closer to 0.5 than this constant,
    // snap the panel weight to 0.5 (i.e. 50/50 panel size).
    private static final double PANEL_RESIZE_SNAP_WEIGHT_AROUND_HALF = 0.035;

    /*
     * Threshold constants for supported gestures.
     * All values are in device independent pixels (dp).
     *
     * For illustration, here are some numbers for typical screen *widths*:
     *  - 320dp: a typical phone screen (240x320 ldpi, 320x480 mdpi, 480x800 hdpi, etc).
     *  - 480dp: a phablet (480x800 mdpi).
     *  - 600dp: a 7? tablet (600x1024 mdpi).
     *  - 720dp: a 10? tablet (720x1280 mdpi, 800x1280 mdpi, etc).
     */
    // Change Panel Size in Real Time threshold - both for up/down (portrait) and left/right (landscape mode)
    private static final int THRESHOLD_SWIPE_UP_OR_DOWN_CHANGE_PANEL_SIZE = 20; // dp
    private static final int THRESHOLD_SWIPE_LEFT_RIGHT_CHANGE_PANEL_SIZE = 20; // dp

    // Switch chapter threshold - swipe left/right
    private static final int THRESHOLD_SWIPE_LEFT_RIGHT_SWITCH_CHAPTER = 120; // dp

    // Hide/reappear panel threshold - both for left/right (portrait) and up/down (landscape mode)
    private static final int THRESHOLD_SWIPE_LEFT_RIGHT_HIDE_PANEL = 120; // dp
    private static final int THRESHOLD_SWIPE_UP_OR_DOWN_HIDE_PANEL = 120; // dp

    /* Threshold fiels - calculated pixel values from the dp constants above. */
    private final int threshold_swipe_left_right_switch_chapter_px;
    private final int threshold_swipe_left_right_hide_panel_px;
    private final int threshold_swipe_up_or_down_hide_panel_px;
    private final int threshold_swipe_up_or_down_change_panel_size_px;
    private final int threshold_swipe_left_right_change_panel_size_px;

    /* Logging */
    protected static final String LOG = "alfons";

    /* Variables for interconnectivity with the outside world. */
    private ReaderActivity activity;
    private Governor governor;
    private PanelHolder panelHolder;
    private BookPanel bookPanel;
    private SelectionWebView webView;
    private int panelPosition;

    /*
     * Fields for touch/gesture events.
     */
    protected GestureDetectorCompat gestureDetector;
    protected float touchOriginX;
    protected float touchOriginY;

    // For scrolling gesture events.
    protected boolean scrollIsOrWasMultitouch;
    protected float scrollOriginX;
    protected float scrollOriginY;

    // For DoubleTapEvent gestures.
    private boolean isDoubleTapSwipe;
    private float doubleTapOriginX;
    private float doubleTapOriginY;
    private boolean doubleTapSwipeEscapedBounds;
    private float newPanelsWeight;
    private int doubleTapSwipe_contentStartsAtHeight;
    private int doubleTapSwipe_viewHeight;
    private int doubleTapSwipe_orientation;

    // For fullscreen.
    private boolean justClickedOnUrlLink = false;
    private long justClickedOnUrlLinkTimestamp;

    public BookPanelOnTouchListener(ReaderActivity readerActivity, Governor governor, PanelHolder panelHolder,
            BookPanel bookPanel, SelectionWebView webView, int position) {

        // Interconnectivity.
        this.activity = readerActivity;
        this.governor = governor;
        this.panelHolder = panelHolder;
        this.bookPanel = bookPanel;
        this.webView = webView;
        this.panelPosition = position;

        // Instantiate the gesture detector.
        gestureDetector = new GestureDetectorCompat(activity, this);
        gestureDetector.setOnDoubleTapListener(this);

        /* Compute the threshold limits in pixels */
        Resources r = activity.getResources();

        // Change Panel Size in Real Time threshold - both for up/down (portrait) and left/right (landscape mode)
        threshold_swipe_up_or_down_change_panel_size_px = Func.dpToPix(r,
                THRESHOLD_SWIPE_UP_OR_DOWN_CHANGE_PANEL_SIZE);
        threshold_swipe_left_right_change_panel_size_px = Func.dpToPix(r,
                THRESHOLD_SWIPE_LEFT_RIGHT_CHANGE_PANEL_SIZE);

        // Switch chapter threshold - swipe left/right
        threshold_swipe_left_right_switch_chapter_px = Func.dpToPix(r, THRESHOLD_SWIPE_LEFT_RIGHT_SWITCH_CHAPTER);

        // Hide/reappear panel threshold - both for left/right (portrait) and up/down (landscape mode)
        threshold_swipe_left_right_hide_panel_px = Func.dpToPix(r, THRESHOLD_SWIPE_LEFT_RIGHT_HIDE_PANEL);
        threshold_swipe_up_or_down_hide_panel_px = Func.dpToPix(r, THRESHOLD_SWIPE_UP_OR_DOWN_HIDE_PANEL);
    }

    /**
     * Update the position of the panel.
     */
    public void updatePanelPosition(int position) {
        panelPosition = position;
    }

    /**
     * Called when our custom WebViewClient that listens to our WebView detects that the user just
     * clicked on a URL link.
     * In that case we will ignore the next onSingleTapConfirmed, and won't switch to/from fullscreen.
     */
    public void setJustClickedOnUrlLink() {
        justClickedOnUrlLink = true;
        justClickedOnUrlLinkTimestamp = System.currentTimeMillis();
    }

    // ============================================================================================
    //      onTouch
    // ============================================================================================

    /**
     * The entry method for any touch-related event.
     * @param view The WebView where the swipe took place
     * @param event The MotionEvent of the swipe
     */
    @Override
    public boolean onTouch(View view, MotionEvent event) {
        // Provide data to the GestureDetector.
        gestureDetector.onTouchEvent(event);

        /*
         * Handle Double Tap Swipe in real time.
         *  This handling isn't in onDoubleTapEvent() because that method has a limit how many times
         *  it gets called after which it isn't called at all until the user lifts the finger in which
         *  case it gets called one last time. That is not workable for on-the-fly resizing of panels.
         */
        if (isDoubleTapSwipe) {
            float absDiffX = Math.abs(doubleTapOriginX - event.getX());
            float absDiffY = Math.abs(doubleTapOriginY - event.getY());

            // If the swipe was over predefined threshold value high and it was higher than wider,
            // or if the swipe was over predefined threshold value wide and it was wider than higher.
            if (doubleTapSwipeEscapedBounds ||
            // Bounds for PORTRAIT orientation.
                    (absDiffY > threshold_swipe_up_or_down_change_panel_size_px && absDiffY > absDiffX
                            && doubleTapSwipe_orientation == Configuration.ORIENTATION_PORTRAIT)
                    ||
                    // Bounds for LANDSCAPE orientation.
                    (absDiffX > threshold_swipe_left_right_change_panel_size_px && absDiffX > absDiffY
                            && doubleTapSwipe_orientation != Configuration.ORIENTATION_PORTRAIT)) {

                if (!doubleTapSwipeEscapedBounds) {
                    // This is the first time doubleTapSwipe escaped it's bounds
                    // - we're officially in the set-panels-size mode.
                    doubleTapSwipeEscapedBounds = true;

                    // Find out and save the relevant dimensions of our view/display
                    Window window = activity.getWindow();

                    if (doubleTapSwipe_orientation == Configuration.ORIENTATION_PORTRAIT) {
                        doubleTapSwipe_contentStartsAtHeight = window.findViewById(Window.ID_ANDROID_CONTENT)
                                .getTop();
                        doubleTapSwipe_viewHeight = window.getDecorView().getHeight();
                    } else {
                        doubleTapSwipe_contentStartsAtHeight = window.findViewById(Window.ID_ANDROID_CONTENT)
                                .getLeft();
                        doubleTapSwipe_viewHeight = window.getDecorView().getWidth();
                    }
                }

                // Compute the panels weight
                float useCoordinate = (doubleTapSwipe_orientation == Configuration.ORIENTATION_PORTRAIT)
                        ? event.getRawY()
                        : event.getRawX();

                newPanelsWeight = (useCoordinate - doubleTapSwipe_contentStartsAtHeight)
                        / (doubleTapSwipe_viewHeight - doubleTapSwipe_contentStartsAtHeight);

                // If the weight is close to 0.5, let it stick to it.
                if (Math.abs(0.5 - newPanelsWeight) < PANEL_RESIZE_SNAP_WEIGHT_AROUND_HALF) {
                    newPanelsWeight = 0.5f;
                }

                // Assure that the weight is between the allowed minimum and maximum.
                newPanelsWeight = Func.minMaxRange(Func.PANEL_WEIGHT_MIN, newPanelsWeight, Func.PANEL_WEIGHT_MAX);

                // Change relative panel size on the fly.
                governor.changePanelsWeight(newPanelsWeight);

                //Log.v(LOG, "[" + panelPosition + "] doubleTapSwipe " + newPanelsWeight);
                //+ " = (" + event.getRawY() + " - " + contentViewTop + ") / " + (height - contentViewTop));
            }
        }

        /*
         * Handle ACTION_UP event for scrolling, because onScroll (almost?) never gets
         * called with the last MotionEvent.ACTION_UP.
         * Don't forget to mirror any changes made here in the onScroll() method as well, to be safe.
         */
        if (MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_UP) {
            Log.d(LOG, "[" + panelPosition + "] OnTouchListener --> onTouch ACTION_UP - scrollY: "
                    + webView.getScrollY());

            // Only if it's not DoubleTapSwipe, that is handled separately.
            if (!isDoubleTapSwipe) {
                // Evaluate if the scroll that has just ended constitutes some gesture.
                handleScrollEnd(event);
            }
        }

        // Modify the event so that it is passed on in a state where the X coordinate hasn't changed at all.
        // This prevents the "over scroll glow effect" on pages that have some (hidden) horizontal scroll.
        //  -- Disabled because it causes the wrong scroll position to be loaded after opening a page
        //     sometimes (but not every time), for some unknown reason.
        //event.setLocation(touchOriginX, event.getY());

        view.performClick(); // Android system mandates we pass the baton to the onClick listener now.

        return view.onTouchEvent(event);
    }

    // ============================================================================================
    //      Gesture Detector hooks
    // ============================================================================================

    /**
     * First finger was put on the screen.
     */
    @Override
    public boolean onDown(MotionEvent event) {
        Log.d(LOG, "[" + panelPosition + "] onDown - scrollY: " + webView.getScrollY()); //: " + event.toString());

        /*
         * Reset all variables from handling of the last touch gesture
         * and re-initiate them for this new touch gesture handling.
         *
         * Warning: When doing double-tap, after the second tap, all variables get re-initiated here for
         *  the second time. There's no good way around this, but it also doesn't cause any issues so far.
         */
        // Reset general touch fields
        touchOriginX = event.getX();
        touchOriginY = event.getY();

        // Reset fields dealing with scroll.
        scrollIsOrWasMultitouch = false;
        scrollOriginX = event.getX();
        scrollOriginY = event.getY();

        // Reset fields dealing with double tap swipe.
        isDoubleTapSwipe = false;
        doubleTapSwipeEscapedBounds = false;
        webView.setDoNotScrollThisWebView(false); // (Re)activate WebView scrolling.
        doubleTapSwipe_contentStartsAtHeight = 0; // Will be recomputed upon need, and with current values
        doubleTapSwipe_viewHeight = 0; // instead of saved ones that need to be updated upon change.
        doubleTapSwipe_orientation = activity.getResources().getConfiguration().orientation;

        // If ScrollSync is active:
        if (governor.isScrollSync()) {
            // The user laid finger in this WebView and may start scrolling it.
            //  Therefore send resumeScrollSync to both WebViews, the one that was active in User Scrolling
            //   will compute new offset and send it to the other one.
            //  Afterwards activate User Scrolling in this WebView and deactivate it in the sister WebView.
            webView.resumeScrollSync();
            webView.setUserIsScrolling(true);

            BookPanel otherBookPanel = panelHolder.getSisterBookPanel();
            if (otherBookPanel != null) {
                otherBookPanel.getWebView().resumeScrollSync();
                otherBookPanel.getWebView().setUserIsScrolling(false);

                // onDown() gesture stops the fling in this WebView,
                //  therefore we do the same for the sister WebView.
                otherBookPanel.getWebView().flingScroll(0, 0);
            }
        }

        return true;
    }

    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
        Log.d(LOG, "[" + panelPosition + "] onFling"); //: " + event1.toString()+event2.toString());

        // Evaluation of the end of the scroll is handled directly in onTouch for consistent results.

        return true;
    }

    /**
     * Gets called when the user scrolls, but not when he double taps and swipes/scrolls.
     */
    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
        //Log.d(LOG, "[" + panelPosition + "] onScroll"); //: " + e2.toString());

        /* Change from single to a Multi-touch event */
        if (e2.getPointerCount() > 1 && !scrollIsOrWasMultitouch) {
            Log.d(LOG, "[" + panelPosition + "] onScroll: Change from single to a Multitouch event");

            // Evaluate if the scroll section that has just ended constitutes some gesture.
            handleScrollEnd(e2);

            if (governor.isScrollSync() && webView.isUserScrolling()) {
                // Pause ScrollSync, user is setting a scrolling offset.
                webView.pauseScrollSync();
            }

            // Start a new scroll section.
            scrollIsOrWasMultitouch = true;
            scrollOriginX = e2.getX();
            scrollOriginY = e2.getY();
        }
        /* Change from multi to a Single-touch event */
        else if (e2.getPointerCount() <= 1 && scrollIsOrWasMultitouch) {
            // Do nothing.
            Log.d(LOG, "[" + panelPosition
                    + "] onScroll: Change from multi to a Singletouch event detected - doing nothing.");
        }

        // Evaluation of the end of the scroll is handled directly in onTouch for consistent results,
        //  because the last onScroll call with MotionEvent.ACTION_UP (almost?) never happens.
        //  Also, this solution unifies handling with scrolls that end up in onFling.

        return true;
    }

    /**
     * Handles the ending of a scroll gesture.
     * @param e2  The last MotionEvent of the scroll or of the scroll section.
     */
    private void handleScrollEnd(MotionEvent e2) {
        float diffX = scrollOriginX - e2.getX();
        float diffY = scrollOriginY - e2.getY();
        float absDiffX = Math.abs(diffX);
        float absDiffY = Math.abs(diffY);

        // If this scroll has never been multi-touch or doubleTapSwipe:
        if (!scrollIsOrWasMultitouch && !isDoubleTapSwipe) {
            // If the WebView is displaying a book (no sense in switching chapters of metadata or Toc):
            if (bookPanel.enumState == BookPanelState.books) {

                // Single touch scroll on a book - evaluate if the user wants to change chapters.
                if (diffX > threshold_swipe_left_right_switch_chapter_px && absDiffX > absDiffY) {
                    // Next chapter - If the swipe was to the right, over the predefined threshold,
                    //  and more broad than high.
                    panelHolder.changeChapter(true);
                } else if (diffX < -threshold_swipe_left_right_switch_chapter_px && absDiffX > absDiffY) {
                    // Previous chapter - If the swipe was to the left, over the predefined threshold,
                    //  and more broad than high.
                    panelHolder.changeChapter(false);
                }
            }
        }

        /* Disabled because it was getting confused with two-finger scrolling gesture,
         * and because this functionality is already covered by doubleTapSwipe.
        // Multi-touch scroll sideways:
        if (scrollIsMultitouch) {
           if (absDiffX > quarterWidth && absDiffX > absDiffY) {
        // Hide or reappear panel -
        //  If the swipe was over 1/4 of the screen wide, and more broad than high.
        Toast.makeText(ReaderActivity.debugContext, "Hiding panel - by multitouch scroll.", Toast.LENGTH_SHORT).show();
           }
        }*/
    }

    @Override
    public boolean onDoubleTap(MotionEvent event) {
        Log.d(LOG, "[" + panelPosition + "] onDoubleTap"); //: " + event.toString());

        doubleTapOriginX = event.getX();
        doubleTapOriginY = event.getY();
        return true;
    }

    /**
     * Called when the user double taps and then swipes his finger away on the second tap.
     *
     * Warning: Gets called before the second onDown() call, which resets values used here.
     * But the subsequent call of this method can repair the damage.
     */
    @Override
    public boolean onDoubleTapEvent(MotionEvent event) {
        //Log.v(LOG, "[" + panelPosition + "] onDoubleTapEvent"); //: " + event.toString());

        // This cannot be moved to onDoubleTap, because another onDown comes after it that wipes it out.
        if (!isDoubleTapSwipe) {
            isDoubleTapSwipe = true;
            webView.setDoNotScrollThisWebView(true);
        }

        /*
         * Detecting Double tap + swipe up/down was moved directly to onTouch(),
         *  because this method has a limit how many times it gets called after which it isn't called
         *  at all until the user lifts the finger in which case it gets called one last time.
         *  That is not workable for on-the-fly resizing of panels.
         */

        // If the finger has finally risen.
        if (MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_UP) {
            //Log.d(DEBUG_TAG, "onDoubleTapEvent ACTION_UP"); //: " + event.toString());

            // If doubleTapSwipe escaped its vertical bounds and up/down sliding begun changing
            // the relative size of panels on the fly. The user has settled on a relative size he wants,
            // and our job now is to save this preference into PanelSizeDialog for further use in the dialog.
            if (doubleTapSwipeEscapedBounds) {
                // Save the newly set panels weight into preferences so PanelSizeDialog can acess it.
                SharedPreferences preferences = activity.getPreferences(Context.MODE_PRIVATE);
                PanelSizeDialog.saveSeekBarValue(preferences, newPanelsWeight);
            } else {
                // DoubleTapSwipe never escaped its bounds and therefore
                // we will evaluate if it constituted a swipe to the side.
                float absDiffX = Math.abs(doubleTapOriginX - event.getX());
                float absDiffY = Math.abs(doubleTapOriginY - event.getY());

                // PORTRAIT: If the swipe was over the threshold wide and it was wider than higher.
                if ((absDiffX > threshold_swipe_left_right_hide_panel_px && absDiffX > absDiffY
                        && doubleTapSwipe_orientation == Configuration.ORIENTATION_PORTRAIT) ||
                // LANDSCAPE: If the swipe was over the threshold high and it was higher than wider.
                        (absDiffY > threshold_swipe_up_or_down_hide_panel_px && absDiffY > absDiffX
                                && doubleTapSwipe_orientation != Configuration.ORIENTATION_PORTRAIT)) {

                    // Hide or reappear panel.
                    panelHolder.hideOrReappearPanel();
                }
            }
        }

        return true;
    }

    /**
     * onSingleTapConfirmed - Activate/deactivate immersive fullscreen mode.
     */
    @Override
    public boolean onSingleTapConfirmed(MotionEvent event) {
        Log.d(LOG, "[" + panelPosition + "] onSingleTapConfirmed"); //: " + event.toString());

        // If the user hasn't just clicked on a URL link, or if he has clicked on it, but more than 250ms ago:
        if (!justClickedOnUrlLink || System.currentTimeMillis() - justClickedOnUrlLinkTimestamp > 250) {

            // If we are displaying a book page, but aren't in selection mode:
            if (bookPanel.enumState == BookPanelState.books && !webView.inSelectionActionMode()) {
                activity.switchFullscreen();
            }
        }

        justClickedOnUrlLink = false;
        return true;
    }

    @Override
    public boolean onSingleTapUp(MotionEvent event) {
        Log.d(LOG, "[" + panelPosition + "] onSingleTapUp"); //: " + event.toString());
        return true;
    }

    @Override
    public void onLongPress(MotionEvent event) {
        Log.d(LOG, "[" + panelPosition + "] onLongPress"); //: " + event.toString());
    }

    @Override
    public void onShowPress(MotionEvent event) {
        Log.d(LOG, "[" + panelPosition + "] onShowPress"); //: " + event.toString());
    }

}