package org.ebookdroid.ui.viewer.viewers; import org.ebookdroid.core.models.DocumentModel; import org.ebookdroid.ui.viewer.IActivityController; import org.ebookdroid.ui.viewer.IView; import org.ebookdroid.ui.viewer.IViewController; import android.graphics.Rect; import android.view.View; import java.lang.reflect.Field; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import org.emdev.utils.MathUtils; import org.emdev.utils.concurrent.Flag; final class ScrollEventThread extends Thread { private static boolean mergeEvents = false; private static Field SCROLL_X; private static Field SCROLL_Y; static { final Field[] fields = View.class.getDeclaredFields(); for (final Field f : fields) { if ("mScrollX".equals(f.getName())) { SCROLL_X = f; SCROLL_X.setAccessible(true); } else if ("mScrollY".equals(f.getName())) { SCROLL_Y = f; SCROLL_Y.setAccessible(true); } } } private final IActivityController base; private final IView view; private final Flag stop = new Flag(); private final BlockingQueue<OnScrollEvent> queue = new LinkedBlockingQueue<OnScrollEvent>(); private final ConcurrentLinkedQueue<OnScrollEvent> pool = new ConcurrentLinkedQueue<OnScrollEvent>(); ScrollEventThread(final IActivityController base, IView view) { super("ScrollEventThread"); this.base = base; this.view = view; } @Override public void run() { while (!stop.get()) { try { final OnScrollEvent event = queue.poll(1, TimeUnit.SECONDS); if (event == null) { continue; } if (mergeEvents) { for (OnScrollEvent event1 = queue.poll(); event1 != null; event1 = queue.poll()) { event.reuse(event1.m_curX, event1.m_curY, event.m_oldX, event.m_oldY); pool.add(event1); } } process(event); } catch (final InterruptedException e) { Thread.interrupted(); } catch (final Throwable th) { th.printStackTrace(); } } // System.out.println("ScrollEventThread.run(): finished"); } void finish() { stop.set(); } void scrollTo(final int x, final int y) { final IViewController dc = base.getDocumentController(); final DocumentModel dm = base.getDocumentModel(); final View w = view.getView(); if (dc != null && dm != null) { final Rect l = dc.getScrollLimits(); final int xx = MathUtils.adjust(x, l.left, l.right); final int yy = MathUtils.adjust(y, l.top, l.bottom); if (SCROLL_X == null || SCROLL_Y == null) { try { view._scrollTo(xx, yy); } catch (Throwable th) { System.err.println("(1) " + th.getMessage()); } } else { try { int mScrollX = SCROLL_X.getInt(w); int mScrollY = SCROLL_Y.getInt(w); if (mScrollX != xx || mScrollY != yy) { int oldX = mScrollX; int oldY = mScrollY; SCROLL_X.setInt(w, xx); SCROLL_Y.setInt(w, yy); view.onScrollChanged(mScrollX, mScrollY, oldX, oldY); } } catch (Throwable th) { System.err.println("(2) " + th.getMessage()); try { view._scrollTo(xx, yy); } catch (Throwable thh) { System.err.println("(3) " + thh.getMessage()); } } } } } void onScrollChanged(final int curX, final int curY, final int oldX, final int oldY) { OnScrollEvent event = pool.poll(); if (event != null) { event.reuse(curX, curY, oldX, oldY); } else { event = new OnScrollEvent(curX, curY, oldX, oldY); } queue.offer(event); } private void process(final OnScrollEvent event) { // final long t1 = System.currentTimeMillis(); try { final int dX = event.m_curX - event.m_oldX; final int dY = event.m_curY - event.m_oldY; base.getDocumentController().onScrollChanged(dX, dY); } catch (final Throwable th) { th.printStackTrace(); } finally { pool.add(event); // final long t2 = System.currentTimeMillis(); // System.out.println("ScrollEventThread.onScrollChanged(): " + (t2 - t1) + " ms, " + pool.size()); } } final static class OnScrollEvent { int m_oldX; int m_curY; int m_curX; int m_oldY; OnScrollEvent(final int curX, final int curY, final int oldX, final int oldY) { reuse(curX, curY, oldX, oldY); } void reuse(final int curX, final int curY, final int oldX, final int oldY) { m_oldX = oldX; m_curY = curY; m_curX = curX; m_oldY = oldY; } } }