1 package org.robolectric.shadows; 2 3 import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1; 4 import static android.os.Build.VERSION_CODES.P; 5 6 import android.hardware.display.DisplayManagerGlobal; 7 import android.hardware.display.IDisplayManager; 8 import android.hardware.display.IDisplayManagerCallback; 9 import android.hardware.display.WifiDisplayStatus; 10 import android.os.RemoteException; 11 import android.view.DisplayInfo; 12 import java.util.ArrayList; 13 import java.util.List; 14 import java.util.TreeMap; 15 import org.robolectric.annotation.Implementation; 16 import org.robolectric.annotation.Implements; 17 import org.robolectric.annotation.Resetter; 18 import org.robolectric.shadow.api.Shadow; 19 import org.robolectric.util.ReflectionHelpers; 20 import org.robolectric.util.ReflectionHelpers.ClassParameter; 21 22 @Implements(value = DisplayManagerGlobal.class, isInAndroidSdk = false, minSdk = JELLY_BEAN_MR1) 23 public class ShadowDisplayManagerGlobal { 24 private static DisplayManagerGlobal instance; 25 26 private MyDisplayManager mDm; 27 28 @Resetter reset()29 public static void reset() { 30 instance = null; 31 } 32 33 @Implementation getInstance()34 synchronized public static DisplayManagerGlobal getInstance() { 35 if (instance == null) { 36 MyDisplayManager myIDisplayManager = new MyDisplayManager(); 37 IDisplayManager proxy = ReflectionHelpers.createDelegatingProxy(IDisplayManager.class, myIDisplayManager); 38 instance = ReflectionHelpers.callConstructor(DisplayManagerGlobal.class, 39 ClassParameter.from(IDisplayManager.class, proxy)); 40 ShadowDisplayManagerGlobal shadow = Shadow.extract(instance); 41 shadow.mDm = myIDisplayManager; 42 } 43 return instance; 44 } 45 46 @Implementation getWifiDisplayStatus()47 protected WifiDisplayStatus getWifiDisplayStatus() { 48 return new WifiDisplayStatus(); 49 } 50 addDisplay(DisplayInfo displayInfo)51 int addDisplay(DisplayInfo displayInfo) { 52 fixNominalDimens(displayInfo); 53 54 return mDm.addDisplay(displayInfo); 55 } 56 fixNominalDimens(DisplayInfo displayInfo)57 private void fixNominalDimens(DisplayInfo displayInfo) { 58 int min = Math.min(displayInfo.appWidth, displayInfo.appHeight); 59 int max = Math.max(displayInfo.appWidth, displayInfo.appHeight); 60 displayInfo.smallestNominalAppHeight = displayInfo.smallestNominalAppWidth = min; 61 displayInfo.largestNominalAppHeight = displayInfo.largestNominalAppWidth = max; 62 } 63 changeDisplay(int displayId, DisplayInfo displayInfo)64 void changeDisplay(int displayId, DisplayInfo displayInfo) { 65 mDm.changeDisplay(displayId, displayInfo); 66 } 67 removeDisplay(int displayId)68 void removeDisplay(int displayId) { 69 mDm.removeDisplay(displayId); 70 } 71 72 private static class MyDisplayManager { 73 private final TreeMap<Integer, DisplayInfo> displayInfos = new TreeMap<>(); 74 private int nextDisplayId = 0; 75 private final List<IDisplayManagerCallback> callbacks = new ArrayList<>(); 76 77 // @Override getDisplayInfo(int i)78 public DisplayInfo getDisplayInfo(int i) throws RemoteException { 79 DisplayInfo displayInfo = displayInfos.get(i); 80 return displayInfo == null ? null : new DisplayInfo(displayInfo); 81 } 82 83 // @Override // todo: use @Implements/@Implementation for signature checking getDisplayIds()84 public int[] getDisplayIds() throws RemoteException { 85 int[] ids = new int[displayInfos.size()]; 86 int i = 0; 87 for (Integer displayId : displayInfos.keySet()) { 88 ids[i++] = displayId; 89 } 90 return ids; 91 } 92 93 // @Override registerCallback(IDisplayManagerCallback iDisplayManagerCallback)94 public void registerCallback(IDisplayManagerCallback iDisplayManagerCallback) throws RemoteException { 95 this.callbacks.add(iDisplayManagerCallback); 96 } 97 addDisplay(DisplayInfo displayInfo)98 synchronized private int addDisplay(DisplayInfo displayInfo) { 99 int nextId = nextDisplayId++; 100 displayInfos.put(nextId, displayInfo); 101 notifyListeners(nextId, DisplayManagerGlobal.EVENT_DISPLAY_ADDED); 102 return nextId; 103 } 104 changeDisplay(int displayId, DisplayInfo displayInfo)105 synchronized private void changeDisplay(int displayId, DisplayInfo displayInfo) { 106 if (!displayInfos.containsKey(displayId)) { 107 throw new IllegalStateException("no display " + displayId); 108 } 109 110 displayInfos.put(displayId, displayInfo); 111 notifyListeners(displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED); 112 } 113 removeDisplay(int displayId)114 synchronized private void removeDisplay(int displayId) { 115 if (!displayInfos.containsKey(displayId)) { 116 throw new IllegalStateException("no display " + displayId); 117 } 118 119 displayInfos.remove(displayId); 120 notifyListeners(displayId, DisplayManagerGlobal.EVENT_DISPLAY_REMOVED); 121 } 122 notifyListeners(int nextId, int event)123 private void notifyListeners(int nextId, int event) { 124 for (IDisplayManagerCallback callback : callbacks) { 125 try { 126 callback.onDisplayEvent(nextId, event); 127 } catch (RemoteException e) { 128 throw new RuntimeException(e); 129 } 130 } 131 } 132 } 133 } 134