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