1bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook/* 2bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * Copyright (C) 2010 The Android Open Source Project 3bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * 4bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * Licensed under the Apache License, Version 2.0 (the "License"); 5bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * you may not use this file except in compliance with the License. 6bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * You may obtain a copy of the License at 7bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * 8bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * http://www.apache.org/licenses/LICENSE-2.0 9bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * 10bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * Unless required by applicable law or agreed to in writing, software 11bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * distributed under the License is distributed on an "AS IS" BASIS, 12bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * See the License for the specific language governing permissions and 14bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * limitations under the License. 15bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook */ 16bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 17bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookpackage com.android.email; 18bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 19bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport com.android.emailcommon.Logging; 20bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 21bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.os.Handler; 22bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.util.Log; 23bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 24bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport java.util.Timer; 25bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport java.util.TimerTask; 26bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 27bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook/** 28bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * This class used to "throttle" a flow of events. 29bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * 30bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * When {@link #onEvent()} is called, it calls the callback in a certain timeout later. 31bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * Initially {@link #mMinTimeout} is used as the timeout, but if it gets multiple {@link #onEvent} 32bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * calls in a certain amount of time, it extends the timeout, until it reaches {@link #mMaxTimeout}. 33bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * 34bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * This class is primarily used to throttle content changed events. 35bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook */ 36bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookpublic class Throttle { 37bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook public static final boolean DEBUG = false; // Don't submit with true 38bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 39bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook public static final int DEFAULT_MIN_TIMEOUT = 150; 40bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook public static final int DEFAULT_MAX_TIMEOUT = 2500; 41bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook /* package */ static final int TIMEOUT_EXTEND_INTERVAL = 500; 42bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 43bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook private static Timer TIMER = new Timer(); 44bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 45bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook private final Clock mClock; 46bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook private final Timer mTimer; 47bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 48bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook /** Name of the instance. Only for logging. */ 49bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook private final String mName; 50bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 51bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook /** Handler for UI thread. */ 52bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook private final Handler mHandler; 53bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 54bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook /** Callback to be called */ 55bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook private final Runnable mCallback; 56bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 57bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook /** Minimum (default) timeout, in milliseconds. */ 58bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook private final int mMinTimeout; 59bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 60bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook /** Max timeout, in milliseconds. */ 61bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook private final int mMaxTimeout; 62bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 63bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook /** Current timeout, in milliseconds. */ 64bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook private int mTimeout; 65bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 66bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook /** When {@link #onEvent()} was last called. */ 67bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook private long mLastEventTime; 68bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 69bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook private MyTimerTask mRunningTimerTask; 70bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 71bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook /** Constructor with default timeout */ 72bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook public Throttle(String name, Runnable callback, Handler handler) { 73bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook this(name, callback, handler, DEFAULT_MIN_TIMEOUT, DEFAULT_MAX_TIMEOUT); 74bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 75bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 76bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook /** Constructor that takes custom timeout */ 77bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook public Throttle(String name, Runnable callback, Handler handler,int minTimeout, 78bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook int maxTimeout) { 79bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook this(name, callback, handler, minTimeout, maxTimeout, Clock.INSTANCE, TIMER); 80bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 81bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 82bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook /** Constructor for tests */ 83bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook /* package */ Throttle(String name, Runnable callback, Handler handler,int minTimeout, 84bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook int maxTimeout, Clock clock, Timer timer) { 85bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (maxTimeout < minTimeout) { 86bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook throw new IllegalArgumentException(); 87bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 88bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook mName = name; 89bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook mCallback = callback; 90bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook mClock = clock; 91bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook mTimer = timer; 92bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook mHandler = handler; 93bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook mMinTimeout = minTimeout; 94bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook mMaxTimeout = maxTimeout; 95bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook mTimeout = mMinTimeout; 96bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 97bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 98bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook private void debugLog(String message) { 99bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook Log.d(Logging.LOG_TAG, "Throttle: [" + mName + "] " + message); 100bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 101bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 102bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook private boolean isCallbackScheduled() { 103bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook return mRunningTimerTask != null; 104bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 105bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 106bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook public void cancelScheduledCallback() { 107bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (mRunningTimerTask != null) { 108bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (DEBUG) debugLog("Canceling scheduled callback"); 109bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook mRunningTimerTask.cancel(); 110bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook mRunningTimerTask = null; 111bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 112bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 113bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 114bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook /* package */ void updateTimeout() { 115bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook final long now = mClock.getTime(); 116bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if ((now - mLastEventTime) <= TIMEOUT_EXTEND_INTERVAL) { 117bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook mTimeout *= 2; 118bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (mTimeout >= mMaxTimeout) { 119bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook mTimeout = mMaxTimeout; 120bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 121bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (DEBUG) debugLog("Timeout extended " + mTimeout); 122bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } else { 123bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook mTimeout = mMinTimeout; 124bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (DEBUG) debugLog("Timeout reset to " + mTimeout); 125bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 126bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 127bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook mLastEventTime = now; 128bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 129bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 130bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook public void onEvent() { 131bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (DEBUG) debugLog("onEvent"); 132bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 133bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook updateTimeout(); 134bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 135bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (isCallbackScheduled()) { 136bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (DEBUG) debugLog(" callback already scheduled"); 137bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } else { 138bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (DEBUG) debugLog(" scheduling callback"); 139bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook mRunningTimerTask = new MyTimerTask(); 140bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook mTimer.schedule(mRunningTimerTask, mTimeout); 141bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 142bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 143bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 144bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook /** 145bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * Timer task called on timeout, 146bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook */ 147bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook private class MyTimerTask extends TimerTask { 148bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook private boolean mCanceled; 149bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 150bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook @Override 151bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook public void run() { 152bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook mHandler.post(new HandlerRunnable()); 153bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 154bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 155bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook @Override 156bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook public boolean cancel() { 157bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook mCanceled = true; 158bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook return super.cancel(); 159bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 160bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 161bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook private class HandlerRunnable implements Runnable { 162bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook @Override 163bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook public void run() { 164bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook mRunningTimerTask = null; 165bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (!mCanceled) { // This check has to be done on the UI thread. 166bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (DEBUG) debugLog("Kicking callback"); 167bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook mCallback.run(); 168bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 169bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 170bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 171bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 172bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 173bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook /* package */ int getTimeoutForTest() { 174bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook return mTimeout; 175bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 176bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 177bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook /* package */ long getLastEventTimeForTest() { 178bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook return mLastEventTime; 179bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 180bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook} 181