1 package org.robolectric.shadows; 2 3 import static android.os.Build.VERSION_CODES.M; 4 import static android.os.Build.VERSION_CODES.N; 5 import static android.os.Build.VERSION_CODES.Q; 6 import static android.os.Build.VERSION_CODES.R; 7 import static org.robolectric.RuntimeEnvironment.getApiLevel; 8 import static org.robolectric.util.reflector.Reflector.reflector; 9 10 import android.content.Context; 11 import android.graphics.drawable.Drawable; 12 import android.os.Handler; 13 import android.view.FrameMetrics; 14 import android.view.Window; 15 import android.view.Window.OnFrameMetricsAvailableListener; 16 import java.util.HashSet; 17 import java.util.Set; 18 import org.robolectric.annotation.HiddenApi; 19 import org.robolectric.annotation.Implementation; 20 import org.robolectric.annotation.Implements; 21 import org.robolectric.annotation.RealObject; 22 import org.robolectric.util.ReflectionHelpers; 23 import org.robolectric.util.ReflectionHelpers.ClassParameter; 24 import org.robolectric.util.reflector.Direct; 25 import org.robolectric.util.reflector.ForType; 26 27 @SuppressWarnings({"UnusedDeclaration"}) 28 @Implements(Window.class) 29 public class ShadowWindow { 30 @RealObject private Window realWindow; 31 32 protected CharSequence title = ""; 33 protected Drawable backgroundDrawable; 34 private int flags; 35 private int privateFlags; 36 private int softInputMode; 37 private final Set<OnFrameMetricsAvailableListener> onFrameMetricsAvailableListeners = 38 new HashSet<>(); 39 create(Context context)40 public static Window create(Context context) throws ClassNotFoundException { 41 String className = getApiLevel() >= M 42 ? "com.android.internal.policy.PhoneWindow" 43 : "com.android.internal.policy.impl.PhoneWindow"; 44 Class<? extends Window> phoneWindowClass = 45 (Class<? extends Window>) Window.class.getClassLoader().loadClass(className); 46 return ReflectionHelpers.callConstructor(phoneWindowClass, ClassParameter.from(Context.class, context)); 47 } 48 49 @Implementation setFlags(int flags, int mask)50 protected void setFlags(int flags, int mask) { 51 this.flags = (this.flags & ~mask) | (flags & mask); 52 reflector(WindowReflector.class, realWindow).setFlags(flags, mask); 53 } 54 55 @Implementation(minSdk = Q) 56 @HiddenApi addSystemFlags(int flags)57 protected void addSystemFlags(int flags) { 58 this.privateFlags |= flags; 59 reflector(WindowReflector.class, realWindow).addSystemFlags(flags); 60 } 61 62 @Implementation(maxSdk = R) 63 @HiddenApi addPrivateFlags(int flags)64 protected void addPrivateFlags(int flags) { 65 this.privateFlags |= flags; 66 reflector(WindowReflector.class, realWindow).addPrivateFlags(flags); 67 } 68 69 @Implementation setSoftInputMode(int softInputMode)70 protected void setSoftInputMode(int softInputMode) { 71 this.softInputMode = softInputMode; 72 reflector(WindowReflector.class, realWindow).setSoftInputMode(softInputMode); 73 } 74 getFlag(int flag)75 public boolean getFlag(int flag) { 76 return (flags & flag) == flag; 77 } 78 79 /** 80 * Return the value from a private flag (a.k.a system flag). 81 * 82 * <p>Private flags can be set via either {@link #addPrivateFlags} (SDK 19-30) or {@link 83 * #addSystemFlags} (SDK 29+) methods. 84 */ getPrivateFlag(int flag)85 public boolean getPrivateFlag(int flag) { 86 return (privateFlags & flag) == flag; 87 } 88 getTitle()89 public CharSequence getTitle() { 90 return title; 91 } 92 getSoftInputMode()93 public int getSoftInputMode() { 94 return softInputMode; 95 } 96 getBackgroundDrawable()97 public Drawable getBackgroundDrawable() { 98 return backgroundDrawable; 99 } 100 101 @Implementation(minSdk = N) addOnFrameMetricsAvailableListener( Window.OnFrameMetricsAvailableListener listener, Handler handler)102 protected void addOnFrameMetricsAvailableListener( 103 Window.OnFrameMetricsAvailableListener listener, Handler handler) { 104 onFrameMetricsAvailableListeners.add(listener); 105 } 106 107 @Implementation(minSdk = N) removeOnFrameMetricsAvailableListener( Window.OnFrameMetricsAvailableListener listener)108 protected void removeOnFrameMetricsAvailableListener( 109 Window.OnFrameMetricsAvailableListener listener) { 110 if (!onFrameMetricsAvailableListeners.remove(listener)) { 111 // Matches current behavior of android. 112 throw new IllegalArgumentException( 113 "attempt to remove OnFrameMetricsAvailableListener that was never added"); 114 } 115 } 116 117 /** 118 * Calls {@link Window.OnFrameMetrisAvailableListener#onFrameMetricsAvailable()} on each current 119 * listener with 0 as the dropCountSinceLastInvocation. 120 */ reportOnFrameMetricsAvailable(FrameMetrics frameMetrics)121 public void reportOnFrameMetricsAvailable(FrameMetrics frameMetrics) { 122 reportOnFrameMetricsAvailable(frameMetrics, /* dropCountSinceLastInvocation= */ 0); 123 } 124 125 /** 126 * Calls {@link Window.OnFrameMetrisAvailableListener#onFrameMetricsAvailable()} on each current 127 * listener. 128 * 129 * @param frameMetrics the {@link FrameMetrics} instance passed to the listeners. 130 * @param dropCountSinceLastInvocation the dropCountSinceLastInvocation passed to the listeners. 131 */ reportOnFrameMetricsAvailable( FrameMetrics frameMetrics, int dropCountSinceLastInvocation)132 public void reportOnFrameMetricsAvailable( 133 FrameMetrics frameMetrics, int dropCountSinceLastInvocation) { 134 for (OnFrameMetricsAvailableListener listener : onFrameMetricsAvailableListeners) { 135 listener.onFrameMetricsAvailable(realWindow, frameMetrics, dropCountSinceLastInvocation); 136 } 137 } 138 139 @ForType(Window.class) 140 interface WindowReflector { 141 142 @Direct setFlags(int flags, int mask)143 void setFlags(int flags, int mask); 144 145 @Direct addSystemFlags(int flags)146 void addSystemFlags(int flags); 147 148 @Direct addPrivateFlags(int flags)149 void addPrivateFlags(int flags); 150 151 @Direct setSoftInputMode(int softInputMode)152 void setSoftInputMode(int softInputMode); 153 } 154 } 155