package org.ebookdroid.common.touch; import org.sufficientlysecure.viewer.R; import org.ebookdroid.common.touch.TouchManager.Region; import org.ebookdroid.common.touch.TouchManager.TouchProfile; import org.ebookdroid.ui.viewer.IActivityController; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.PointF; import android.graphics.RectF; import android.util.FloatMath; import android.view.GestureDetector.SimpleOnGestureListener; import android.view.MotionEvent; import android.view.View; import java.util.ListIterator; import org.emdev.common.log.LogContext; import org.emdev.common.log.LogManager; import org.emdev.ui.actions.ActionController; import org.emdev.utils.MathUtils; public class TouchManagerView extends View { private static final LogContext LCTX = LogManager.root().lctx("TouchManagerView"); private static final float GRID_X = 10; private static final float GRID_Y = 10; private static final int[] COLORS = { Color.BLUE, Color.GRAY, Color.RED, Color.YELLOW }; private final IActivityController base; private final Paint bgPaint; private final Paint gridPaint; private final ActionController<TouchManagerView> actions; private final DefaultGestureDetector detector; private final Paint rPaint = new Paint(); public TouchManagerView(final IActivityController base) { super(base.getContext()); this.base = base; this.actions = new ActionController<TouchManagerView>(base, this); this.detector = new DefaultGestureDetector(getContext(), new GestureListener()); super.setVisibility(View.GONE); setFocusable(true); setFocusableInTouchMode(true); bgPaint = new Paint(); bgPaint.setColor(Color.BLACK); bgPaint.setStyle(Paint.Style.FILL); bgPaint.setAlpha(0x40); gridPaint = new Paint(); gridPaint.setColor(Color.GREEN); } private TouchProfile getProfile() { return TouchManager.topProfile(); } @Override protected void onDraw(final Canvas canvas) { super.onDraw(canvas); TouchProfile profile = getProfile(); if (profile == null) { return; } final int width = getWidth(); final int height = getHeight(); canvas.drawRect(0, 0, width, height, bgPaint); final float xStep = width / GRID_X; final float yStep = height / GRID_Y; int cIndex = 0; ListIterator<Region> regions = profile.regions(false); while (regions.hasPrevious()) { final Region region = regions.previous(); final RectF r = region.getActualRect(width, height); rPaint.setColor(COLORS[cIndex]); rPaint.setAlpha(0x80); canvas.drawRect(r, rPaint); cIndex = (cIndex + 1) % COLORS.length; } drawGrid(canvas, xStep, yStep); cIndex = 0; regions = profile.regions(false); while (regions.hasPrevious()) { final Region region = regions.previous(); final RectF r = region.getActualRect(width, height); rPaint.setColor(COLORS[cIndex]); drawBounds(canvas, r, rPaint); cIndex = (cIndex + 1) % COLORS.length; } if (current != null) { rPaint.setColor(Color.WHITE); rPaint.setAlpha(0x80); final RectF r = current.getActualRect(width, height); canvas.drawRect(r, rPaint); rPaint.setColor(Color.WHITE); drawBounds(canvas, r, rPaint); } } protected void drawGrid(final Canvas canvas, final float xStep, final float yStep) { for (float x = xStep; x < getWidth(); x += xStep) { canvas.drawLine(x, 0, x, getHeight(), gridPaint); } for (float y = yStep; y < getHeight(); y += yStep) { canvas.drawLine(0, y, getWidth(), y, gridPaint); } } protected void drawBounds(final Canvas canvas, final RectF r, final Paint p) { canvas.drawLine(r.left, r.top, r.right - 1, r.top, p); canvas.drawLine(r.left, r.bottom - 1, r.right - 1, r.bottom - 1, p); canvas.drawLine(r.left, r.top, r.left, r.bottom - 1, p); canvas.drawLine(r.right - 1, r.top, r.right - 1, r.bottom - 1, p); } private PointF startPoint; private PointF endPoint; private Region current; protected void processRegion() { TouchProfile profile = getProfile(); if (profile != null) { if (startPoint != null && endPoint != null) { current = getOrCreareRegion(startPoint, endPoint); } if (LCTX.isDebugEnabled()) { LCTX.d("processRegion(): " + current); } if (current != null) { final TouchConfigDialog dlg = new TouchConfigDialog(base, this, profile, current); dlg.show(); } } startPoint = null; endPoint = null; current = null; } protected Region getOrCreareRegion(final PointF startPoint, final PointF endPoint) { TouchProfile profile = getProfile(); final Region selected = getRegion(startPoint, endPoint); for (final Region r : profile.regions) { if (r.getRect().equals(selected.getRect())) { return r; } } profile.addRegion(selected); return selected; } @Override public final boolean onTouchEvent(final MotionEvent ev) { try { Thread.sleep(16); } catch (final InterruptedException e) { Thread.interrupted(); } if (LCTX.isDebugEnabled()) { LCTX.d("onTouchEvent(): " + ev); } boolean res = detector.onTouchEvent(ev); int action = ev.getAction(); if (!res && (action == MotionEvent.ACTION_UP)) { if (startPoint != null) { endPoint = new PointF(ev.getX(), ev.getY()); current = getRegion(startPoint, endPoint); processRegion(); } invalidate(); return true; } return res; } protected Region getRegion(final PointF startPoint, final PointF endPoint) { final float width = getWidth(); final float height = getHeight(); final float xStep = width / GRID_X; final float yStep = height / GRID_Y; final float cellWidth = 100 / GRID_X; final float cellHeight = 100 / GRID_X; float left = MathUtils.fmin(startPoint.x, endPoint.x); float right = MathUtils.fmax(startPoint.x, endPoint.x); float top = MathUtils.fmin(startPoint.y, endPoint.y); float bottom = MathUtils.fmax(startPoint.y, endPoint.y); left = cellWidth * (float) Math.floor(left / xStep); right = cellWidth * (float) Math.floor(right / xStep) + cellWidth; top = cellHeight * (float) Math.floor(top / yStep); bottom = cellHeight * (float) Math.floor(bottom / yStep) + cellHeight; return new Region(MathUtils.rect(left, top, right, bottom)); } protected class GestureListener extends SimpleOnGestureListener { @Override public boolean onDoubleTap(final MotionEvent e) { actions.getOrCreateAction(R.id.actions_toggleTouchManagerView).run(); return true; } @Override public boolean onDown(final MotionEvent e) { startPoint = new PointF(e.getX(), e.getY()); endPoint = startPoint; current = getRegion(startPoint, endPoint); if (LCTX.isDebugEnabled()) { LCTX.d("onDown(): " + current); } return true; } @Override public boolean onSingleTapUp(MotionEvent e) { if (LCTX.isDebugEnabled()) { LCTX.d("onSingleTapUp(): " + current); } endPoint = null; return true; } @Override public boolean onScroll(final MotionEvent e1, final MotionEvent e2, final float distanceX, final float distanceY) { final float x = e2.getX(), y = e2.getY(); endPoint = new PointF(x, y); current = getRegion(startPoint, endPoint); if (LCTX.isDebugEnabled()) { LCTX.d("onScroll(): " + current); } invalidate(); return true; } @Override public boolean onSingleTapConfirmed(final MotionEvent e) { TouchProfile profile = getProfile(); if (profile != null) { current = profile.getRegion(e.getX(), e.getY(), getWidth(), getHeight()); if (LCTX.isDebugEnabled()) { LCTX.d("onSingleTapConfirmed(): " + current); } processRegion(); } return true; } @Override public void onLongPress(final MotionEvent e) { } } }