1 package org.robolectric.shadows; 2 3 import static android.os.Build.VERSION_CODES.R; 4 import static android.os.Build.VERSION_CODES.S_V2; 5 import static android.view.View.SYSTEM_UI_FLAG_VISIBLE; 6 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING; 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.Insets; 12 import android.graphics.Rect; 13 import android.util.SparseIntArray; 14 import android.view.Display; 15 import android.view.DisplayCutout; 16 import android.view.InsetsState; 17 import android.view.View; 18 import android.view.ViewGroup; 19 import android.view.WindowInsets; 20 import android.view.WindowManager; 21 import android.view.WindowManagerImpl; 22 import com.google.common.collect.ArrayListMultimap; 23 import com.google.common.collect.ImmutableList; 24 import com.google.common.collect.Multimap; 25 import java.util.List; 26 import org.robolectric.annotation.Implementation; 27 import org.robolectric.annotation.Implements; 28 import org.robolectric.annotation.RealObject; 29 import org.robolectric.annotation.Resetter; 30 import org.robolectric.shadows.ShadowViewRootImpl.ViewRootImplReflector; 31 import org.robolectric.util.ReflectionHelpers; 32 import org.robolectric.util.ReflectionHelpers.ClassParameter; 33 import org.robolectric.util.reflector.Accessor; 34 import org.robolectric.util.reflector.Direct; 35 import org.robolectric.util.reflector.ForType; 36 37 @Implements(value = WindowManagerImpl.class, isInAndroidSdk = false) 38 public class ShadowWindowManagerImpl extends ShadowWindowManager { 39 40 @RealObject WindowManagerImpl realObject; 41 private static final Multimap<Integer, View> views = ArrayListMultimap.create(); 42 43 // removed from WindowManagerImpl in S 44 public static final int NEW_INSETS_MODE_FULL = 2; 45 46 @Implementation addView(View view, android.view.ViewGroup.LayoutParams layoutParams)47 public void addView(View view, android.view.ViewGroup.LayoutParams layoutParams) { 48 views.put(realObject.getDefaultDisplay().getDisplayId(), view); 49 // views.add(view); 50 reflector(ReflectorWindowManagerImpl.class, realObject).addView(view, layoutParams); 51 } 52 53 @Implementation removeView(View view)54 public void removeView(View view) { 55 views.remove(realObject.getDefaultDisplay().getDisplayId(), view); 56 reflector(ReflectorWindowManagerImpl.class, realObject).removeView(view); 57 } 58 59 @Implementation removeViewImmediate(View view)60 protected void removeViewImmediate(View view) { 61 views.remove(realObject.getDefaultDisplay().getDisplayId(), view); 62 reflector(ReflectorWindowManagerImpl.class, realObject).removeViewImmediate(view); 63 } 64 getViews()65 public List<View> getViews() { 66 return ImmutableList.copyOf(views.get(realObject.getDefaultDisplay().getDisplayId())); 67 } 68 69 /** Re implement to avoid server call */ 70 @Implementation(minSdk = R, maxSdk = S_V2) getWindowInsetsFromServer(WindowManager.LayoutParams attrs, Rect bounds)71 protected WindowInsets getWindowInsetsFromServer(WindowManager.LayoutParams attrs, Rect bounds) { 72 Context context = reflector(ReflectorWindowManagerImpl.class, realObject).getContext(); 73 final Rect systemWindowInsets = new Rect(); 74 final Rect stableInsets = new Rect(); 75 final DisplayCutout.ParcelableWrapper displayCutout = new DisplayCutout.ParcelableWrapper(); 76 final InsetsState insetsState = new InsetsState(); 77 final boolean alwaysConsumeSystemBars = true; 78 79 final boolean isScreenRound = context.getResources().getConfiguration().isScreenRound(); 80 if (getApiLevel() <= R 81 && reflector(ViewRootImplReflector.class).getNewInsetsMode() == NEW_INSETS_MODE_FULL) { 82 return ReflectionHelpers.callInstanceMethod( 83 insetsState, 84 "calculateInsets", 85 ClassParameter.from(Rect.class, bounds), 86 ClassParameter.from(InsetsState.class, null), 87 ClassParameter.from(Boolean.TYPE, isScreenRound), 88 ClassParameter.from(Boolean.TYPE, alwaysConsumeSystemBars), 89 ClassParameter.from(DisplayCutout.class, displayCutout.get()), 90 ClassParameter.from(int.class, SOFT_INPUT_ADJUST_NOTHING), 91 ClassParameter.from(int.class, SYSTEM_UI_FLAG_VISIBLE), 92 ClassParameter.from(SparseIntArray.class, null)); 93 } else { 94 return new WindowInsets.Builder() 95 .setAlwaysConsumeSystemBars(alwaysConsumeSystemBars) 96 .setRound(isScreenRound) 97 .setSystemWindowInsets(Insets.of(systemWindowInsets)) 98 .setStableInsets(Insets.of(stableInsets)) 99 .setDisplayCutout(displayCutout.get()) 100 .build(); 101 } 102 } 103 104 @ForType(WindowManagerImpl.class) 105 interface ReflectorWindowManagerImpl { 106 107 @Direct addView(View view, ViewGroup.LayoutParams layoutParams)108 void addView(View view, ViewGroup.LayoutParams layoutParams); 109 110 @Direct removeView(View view)111 void removeView(View view); 112 113 @Direct removeViewImmediate(View view)114 void removeViewImmediate(View view); 115 116 @Direct getDefaultDisplay()117 Display getDefaultDisplay(); 118 119 @Accessor("mContext") getContext()120 Context getContext(); 121 } 122 123 @Resetter reset()124 public static void reset() { 125 views.clear(); 126 } 127 } 128