1/*
2 * Copyright (C) 2008 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
17package com.android.launcher3;
18
19import android.os.Handler;
20import android.os.Looper;
21import android.os.Message;
22import android.os.MessageQueue;
23
24import com.android.launcher3.util.Thunk;
25
26import java.util.LinkedList;
27
28/**
29 * Queue of things to run on a looper thread.  Items posted with {@link #post} will not
30 * be actually enqued on the handler until after the last one has run, to keep from
31 * starving the thread.
32 *
33 * This class is fifo.
34 */
35public class DeferredHandler {
36    @Thunk LinkedList<Runnable> mQueue = new LinkedList<>();
37    private MessageQueue mMessageQueue = Looper.myQueue();
38    private Impl mHandler = new Impl();
39
40    @Thunk class Impl extends Handler implements MessageQueue.IdleHandler {
41        public void handleMessage(Message msg) {
42            Runnable r;
43            synchronized (mQueue) {
44                if (mQueue.size() == 0) {
45                    return;
46                }
47                r = mQueue.removeFirst();
48            }
49            r.run();
50            synchronized (mQueue) {
51                scheduleNextLocked();
52            }
53        }
54
55        public boolean queueIdle() {
56            handleMessage(null);
57            return false;
58        }
59    }
60
61    private class IdleRunnable implements Runnable {
62        Runnable mRunnable;
63
64        IdleRunnable(Runnable r) {
65            mRunnable = r;
66        }
67
68        public void run() {
69            mRunnable.run();
70        }
71    }
72
73    public DeferredHandler() {
74    }
75
76    /** Schedule runnable to run after everything that's on the queue right now. */
77    public void post(Runnable runnable) {
78        synchronized (mQueue) {
79            mQueue.add(runnable);
80            if (mQueue.size() == 1) {
81                scheduleNextLocked();
82            }
83        }
84    }
85
86    /** Schedule runnable to run when the queue goes idle. */
87    public void postIdle(final Runnable runnable) {
88        post(new IdleRunnable(runnable));
89    }
90
91    public void cancelAll() {
92        synchronized (mQueue) {
93            mQueue.clear();
94        }
95    }
96
97    /** Runs all queued Runnables from the calling thread. */
98    public void flush() {
99        LinkedList<Runnable> queue = new LinkedList<>();
100        synchronized (mQueue) {
101            queue.addAll(mQueue);
102            mQueue.clear();
103        }
104        for (Runnable r : queue) {
105            r.run();
106        }
107    }
108
109    void scheduleNextLocked() {
110        if (mQueue.size() > 0) {
111            Runnable peek = mQueue.getFirst();
112            if (peek instanceof IdleRunnable) {
113                mMessageQueue.addIdleHandler(mHandler);
114            } else {
115                mHandler.sendEmptyMessage(1);
116            }
117        }
118    }
119}
120
121