• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 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.layoutlib.bridge;
18 
19 import com.android.ide.common.rendering.api.ILayoutLog;
20 import com.android.ide.common.rendering.api.RenderParams;
21 import com.android.ide.common.rendering.api.RenderSession;
22 import com.android.ide.common.rendering.api.ResourceReference;
23 import com.android.ide.common.rendering.api.ResourceValue;
24 import com.android.ide.common.rendering.api.Result;
25 import com.android.ide.common.rendering.api.ViewInfo;
26 import com.android.internal.lang.System_Delegate;
27 import com.android.internal.util.ArrayUtils_Delegate;
28 import com.android.layoutlib.bridge.impl.RenderSessionImpl;
29 
30 import android.annotation.NonNull;
31 import android.annotation.Nullable;
32 import android.os.Handler_Delegate;
33 import android.os.SystemClock_Delegate;
34 import android.view.MotionEvent;
35 
36 import java.awt.image.BufferedImage;
37 import java.util.Collections;
38 import java.util.List;
39 import java.util.Map;
40 
41 import static com.android.layoutlib.bridge.impl.RenderAction.getCurrentContext;
42 
43 /**
44  * An implementation of {@link RenderSession}.
45  *
46  * This is a pretty basic class that does almost nothing. All of the work is done in
47  * {@link RenderSessionImpl}.
48  *
49  */
50 public class BridgeRenderSession extends RenderSession {
51 
52     @Nullable
53     private final RenderSessionImpl mSession;
54     @NonNull
55     private Result mLastResult;
56 
57     private static final Runnable NOOP_RUNNABLE = () -> { };
58 
59     @Override
getResult()60     public Result getResult() {
61         return mLastResult;
62     }
63 
64     @Override
getImage()65     public BufferedImage getImage() {
66         return mSession != null ? mSession.getImage() :
67                 new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
68     }
69 
70     @Override
getRootViews()71     public List<ViewInfo> getRootViews() {
72         return mSession != null ? mSession.getViewInfos() : Collections.emptyList();
73     }
74 
75     @Override
getSystemRootViews()76     public List<ViewInfo> getSystemRootViews() {
77         return mSession != null ? mSession.getSystemViewInfos() : Collections.emptyList();
78     }
79 
80     @Override
getDefaultNamespacedProperties()81     public Map<Object, Map<ResourceReference, ResourceValue>> getDefaultNamespacedProperties() {
82         return mSession != null ? mSession.getDefaultNamespacedProperties() :
83                 Collections.emptyMap();
84     }
85 
86     @Override
getDefaultNamespacedStyles()87     public Map<Object, ResourceReference> getDefaultNamespacedStyles() {
88         return mSession != null ? mSession.getDefaultNamespacedStyles() : Collections.emptyMap();
89     }
90 
91     @Override
measure(long timeout)92     public Result measure(long timeout) {
93         if (mSession != null) {
94             try {
95                 Bridge.prepareThread();
96                 mLastResult = mSession.acquire(timeout);
97                 if (mLastResult.isSuccess()) {
98                     mSession.invalidateRenderingSize();
99                     mLastResult = mSession.measure();
100                 }
101             } finally {
102                 mSession.release();
103                 Bridge.cleanupThread();
104             }
105         }
106 
107         return mLastResult;
108     }
109 
110     @Override
render(long timeout, boolean forceMeasure)111     public Result render(long timeout, boolean forceMeasure) {
112         if (mSession != null) {
113             try {
114                 Bridge.prepareThread();
115                 mLastResult = mSession.acquire(timeout);
116                 if (mLastResult.isSuccess()) {
117                     if (forceMeasure) {
118                         mSession.invalidateRenderingSize();
119                     }
120                     mLastResult = mSession.render(false /*freshRender*/);
121                 }
122             } finally {
123                 mSession.release();
124                 Bridge.cleanupThread();
125             }
126         }
127 
128         return mLastResult;
129     }
130 
131     @Override
setSystemTimeNanos(long nanos)132     public void setSystemTimeNanos(long nanos) {
133         execute(() -> System_Delegate.setNanosTime(nanos));
134     }
135 
136     @Override
setSystemBootTimeNanos(long nanos)137     public void setSystemBootTimeNanos(long nanos) {
138         execute(() -> System_Delegate.setBootTimeNanos(nanos));
139     }
140 
141     @Override
setElapsedFrameTimeNanos(long nanos)142     public void setElapsedFrameTimeNanos(long nanos) {
143         if (mSession != null) {
144             mSession.setElapsedFrameTimeNanos(nanos);
145         }
146     }
147 
148     @Override
executeCallbacks(long nanos)149     public boolean executeCallbacks(long nanos) {
150         // Currently, Compose relies on Choreographer frame callback and Handler#postAtFrontOfQueue.
151         // Calls to Handler are handled by Handler_Delegate and can be executed by Handler_Delegate#
152         // executeCallbacks. Choreographer frame callback is handled by Choreographer#doFrame.
153         if (mSession == null) {
154             return false;
155         }
156         try {
157             Bridge.prepareThread();
158             mLastResult = mSession.acquire(RenderParams.DEFAULT_TIMEOUT);
159             boolean hasMoreCallbacks = Handler_Delegate.executeCallbacks();
160             long currentTimeMs = SystemClock_Delegate.uptimeMillis();
161             getCurrentContext()
162                     .getSessionInteractiveData()
163                     .getChoreographerCallbacks()
164                     .execute(currentTimeMs, Bridge.getLog());
165             return hasMoreCallbacks;
166         } catch (Throwable t) {
167             Bridge.getLog().error(ILayoutLog.TAG_BROKEN, "Failed executing Choreographer#doFrame "
168                     , t, null, null);
169             return false;
170         } finally {
171             mSession.release();
172             Bridge.cleanupThread();
173         }
174     }
175 
toMotionEventType(TouchEventType eventType)176     private static int toMotionEventType(TouchEventType eventType) {
177         switch (eventType) {
178             case PRESS:
179                 return MotionEvent.ACTION_DOWN;
180             case RELEASE:
181                 return MotionEvent.ACTION_UP;
182             case DRAG:
183                 return MotionEvent.ACTION_MOVE;
184         }
185         throw new IllegalStateException("Unexpected touch event type: " + eventType);
186     }
187 
188     @Override
triggerTouchEvent(TouchEventType type, int x, int y)189     public void triggerTouchEvent(TouchEventType type, int x, int y) {
190         execute(() -> {
191             int motionEventType = toMotionEventType(type);
192             mSession.dispatchTouchEvent(motionEventType, System_Delegate.nanoTime(), x, y);
193         });
194     }
195 
triggerKeyEvent(java.awt.event.KeyEvent event)196     public void triggerKeyEvent(java.awt.event.KeyEvent event) {
197         execute(() -> mSession.dispatchKeyEvent(event, System_Delegate.nanoTime()));
198     }
199 
200     @Override
execute(Runnable r)201     public void execute(Runnable r) {
202         if (mSession != null) {
203             try {
204                 Bridge.prepareThread();
205                 mLastResult = mSession.acquire(RenderParams.DEFAULT_TIMEOUT);
206                 r.run();
207             } finally {
208                 mSession.release();
209                 Bridge.cleanupThread();
210             }
211         }
212     }
213 
214     @Override
dispose()215     public void dispose() {
216         execute(mSession::dispose);
217         ArrayUtils_Delegate.clearCache();
218     }
219 
BridgeRenderSession(@ullable RenderSessionImpl scene, @NonNull Result lastResult)220     /*package*/ BridgeRenderSession(@Nullable RenderSessionImpl scene, @NonNull Result lastResult) {
221         mSession = scene;
222         if (scene != null) {
223             mSession.setScene(this);
224         }
225         mLastResult = lastResult;
226     }
227 
228     @Override
getValidationData()229     public Object getValidationData() {
230         if (mSession != null) {
231             return mSession.getValidatorHierarchy();
232         }
233         return null;
234     }
235 }
236