aboutsummaryrefslogtreecommitdiff
path: root/shadows/framework/src/main/java/org/robolectric/shadows/ShadowWindow.java
blob: 78feb1bb417cff21571b7dabda67a0844af9609e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package org.robolectric.shadows;

import static android.os.Build.VERSION_CODES.M;
import static android.os.Build.VERSION_CODES.N;
import static android.os.Build.VERSION_CODES.Q;
import static android.os.Build.VERSION_CODES.R;
import static org.robolectric.RuntimeEnvironment.getApiLevel;
import static org.robolectric.util.reflector.Reflector.reflector;

import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.view.FrameMetrics;
import android.view.Window;
import android.view.Window.OnFrameMetricsAvailableListener;
import java.util.HashSet;
import java.util.Set;
import org.robolectric.annotation.HiddenApi;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.RealObject;
import org.robolectric.util.ReflectionHelpers;
import org.robolectric.util.ReflectionHelpers.ClassParameter;
import org.robolectric.util.reflector.Direct;
import org.robolectric.util.reflector.ForType;

@SuppressWarnings({"UnusedDeclaration"})
@Implements(Window.class)
public class ShadowWindow {
  @RealObject private Window realWindow;

  protected CharSequence title = "";
  protected Drawable backgroundDrawable;
  private int flags;
  private int privateFlags;
  private int softInputMode;
  private final Set<OnFrameMetricsAvailableListener> onFrameMetricsAvailableListeners =
      new HashSet<>();

  public static Window create(Context context) throws ClassNotFoundException {
    String className = getApiLevel() >= M
        ? "com.android.internal.policy.PhoneWindow"
        : "com.android.internal.policy.impl.PhoneWindow";
    Class<? extends Window> phoneWindowClass =
        (Class<? extends Window>) Window.class.getClassLoader().loadClass(className);
    return ReflectionHelpers.callConstructor(phoneWindowClass, ClassParameter.from(Context.class, context));
  }

  @Implementation
  protected void setFlags(int flags, int mask) {
    this.flags = (this.flags & ~mask) | (flags & mask);
    reflector(WindowReflector.class, realWindow).setFlags(flags, mask);
  }

  @Implementation(minSdk = Q)
  @HiddenApi
  protected void addSystemFlags(int flags) {
    this.privateFlags |= flags;
    reflector(WindowReflector.class, realWindow).addSystemFlags(flags);
  }

  @Implementation(maxSdk = R)
  @HiddenApi
  protected void addPrivateFlags(int flags) {
    this.privateFlags |= flags;
    reflector(WindowReflector.class, realWindow).addPrivateFlags(flags);
  }

  @Implementation
  protected void setSoftInputMode(int softInputMode) {
    this.softInputMode = softInputMode;
    reflector(WindowReflector.class, realWindow).setSoftInputMode(softInputMode);
  }

  public boolean getFlag(int flag) {
    return (flags & flag) == flag;
  }

  /**
   * Return the value from a private flag (a.k.a system flag).
   *
   * <p>Private flags can be set via either {@link #addPrivateFlags} (SDK 19-30) or {@link
   * #addSystemFlags} (SDK 29+) methods.
   */
  public boolean getPrivateFlag(int flag) {
    return (privateFlags & flag) == flag;
  }

  public CharSequence getTitle() {
    return title;
  }

  public int getSoftInputMode() {
    return softInputMode;
  }

  public Drawable getBackgroundDrawable() {
    return backgroundDrawable;
  }

  @Implementation(minSdk = N)
  protected void addOnFrameMetricsAvailableListener(
      Window.OnFrameMetricsAvailableListener listener, Handler handler) {
    onFrameMetricsAvailableListeners.add(listener);
  }

  @Implementation(minSdk = N)
  protected void removeOnFrameMetricsAvailableListener(
      Window.OnFrameMetricsAvailableListener listener) {
    if (!onFrameMetricsAvailableListeners.remove(listener)) {
      // Matches current behavior of android.
      throw new IllegalArgumentException(
          "attempt to remove OnFrameMetricsAvailableListener that was never added");
    }
  }

  /**
   * Calls {@link Window.OnFrameMetrisAvailableListener#onFrameMetricsAvailable()} on each current
   * listener with 0 as the dropCountSinceLastInvocation.
   */
  public void reportOnFrameMetricsAvailable(FrameMetrics frameMetrics) {
    reportOnFrameMetricsAvailable(frameMetrics, /* dropCountSinceLastInvocation= */ 0);
  }

  /**
   * Calls {@link Window.OnFrameMetrisAvailableListener#onFrameMetricsAvailable()} on each current
   * listener.
   *
   * @param frameMetrics the {@link FrameMetrics} instance passed to the listeners.
   * @param dropCountSinceLastInvocation the dropCountSinceLastInvocation passed to the listeners.
   */
  public void reportOnFrameMetricsAvailable(
      FrameMetrics frameMetrics, int dropCountSinceLastInvocation) {
    for (OnFrameMetricsAvailableListener listener : onFrameMetricsAvailableListeners) {
      listener.onFrameMetricsAvailable(realWindow, frameMetrics, dropCountSinceLastInvocation);
    }
  }

  @ForType(Window.class)
  interface WindowReflector {

    @Direct
    void setFlags(int flags, int mask);

    @Direct
    void addSystemFlags(int flags);

    @Direct
    void addPrivateFlags(int flags);

    @Direct
    void setSoftInputMode(int softInputMode);
  }
}