1 /**
2  * Copyright (C) 2015 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 
17 package com.android.launcher3.util;
18 
19 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
20 
21 import android.os.Process;
22 import android.view.View;
23 import android.view.View.OnAttachStateChangeListener;
24 import android.view.ViewTreeObserver.OnDrawListener;
25 
26 import com.android.launcher3.Launcher;
27 
28 import java.util.ArrayList;
29 import java.util.concurrent.Executor;
30 
31 /**
32  * An executor which runs all the tasks after the first onDraw is called on the target view.
33  */
34 public class ViewOnDrawExecutor implements Executor, OnDrawListener, Runnable,
35         OnAttachStateChangeListener {
36 
37     private final ArrayList<Runnable> mTasks = new ArrayList<>();
38 
39     private Launcher mLauncher;
40     private View mAttachedView;
41     private boolean mCompleted;
42 
43     private boolean mLoadAnimationCompleted;
44     private boolean mFirstDrawCompleted;
45 
attachTo(Launcher launcher)46     public void attachTo(Launcher launcher) {
47         attachTo(launcher, launcher.getWorkspace(), true /* waitForLoadAnimation */);
48     }
49 
attachTo(Launcher launcher, View attachedView, boolean waitForLoadAnimation)50     public void attachTo(Launcher launcher, View attachedView, boolean waitForLoadAnimation) {
51         mLauncher = launcher;
52         mAttachedView = attachedView;
53         mAttachedView.addOnAttachStateChangeListener(this);
54         if (!waitForLoadAnimation) {
55             mLoadAnimationCompleted = true;
56         }
57 
58         if (mAttachedView.isAttachedToWindow()) {
59             attachObserver();
60         }
61     }
62 
attachObserver()63     private void attachObserver() {
64         if (!mCompleted) {
65             mAttachedView.getViewTreeObserver().addOnDrawListener(this);
66         }
67     }
68 
69     @Override
execute(Runnable command)70     public void execute(Runnable command) {
71         mTasks.add(command);
72         MODEL_EXECUTOR.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
73     }
74 
75     @Override
onViewAttachedToWindow(View v)76     public void onViewAttachedToWindow(View v) {
77         attachObserver();
78     }
79 
80     @Override
onViewDetachedFromWindow(View v)81     public void onViewDetachedFromWindow(View v) {}
82 
83     @Override
onDraw()84     public void onDraw() {
85         mFirstDrawCompleted = true;
86         mAttachedView.post(this);
87     }
88 
onLoadAnimationCompleted()89     public void onLoadAnimationCompleted() {
90         mLoadAnimationCompleted = true;
91         if (mAttachedView != null) {
92             mAttachedView.post(this);
93         }
94     }
95 
96     @Override
run()97     public void run() {
98         // Post the pending tasks after both onDraw and onLoadAnimationCompleted have been called.
99         if (mLoadAnimationCompleted && mFirstDrawCompleted && !mCompleted) {
100             runAllTasks();
101         }
102     }
103 
markCompleted()104     public void markCompleted() {
105         mTasks.clear();
106         mCompleted = true;
107         if (mAttachedView != null) {
108             mAttachedView.getViewTreeObserver().removeOnDrawListener(this);
109             mAttachedView.removeOnAttachStateChangeListener(this);
110         }
111         if (mLauncher != null) {
112             mLauncher.clearPendingExecutor(this);
113         }
114         MODEL_EXECUTOR.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
115     }
116 
isCompleted()117     protected boolean isCompleted() {
118         return mCompleted;
119     }
120 
runAllTasks()121     protected void runAllTasks() {
122         for (final Runnable r : mTasks) {
123             r.run();
124         }
125         markCompleted();
126     }
127 }
128