1/* 2 * Copyright (C) 2013 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 17package android.support.v4.widget; 18 19import android.support.v4.view.GravityCompat; 20import android.support.v4.view.ViewCompat; 21import android.view.Gravity; 22import android.view.View; 23import android.view.WindowManager; 24import android.widget.PopupWindow; 25 26/** 27 * Helper for accessing features in PopupWindow introduced after API level 4 28 * in a backwards compatible fashion. 29 */ 30public final class PopupWindowCompat { 31 /** 32 * Interface for the full API. 33 */ 34 interface PopupWindowImpl { 35 void showAsDropDown(PopupWindow popup, View anchor, int xoff, int yoff, int gravity); 36 void setOverlapAnchor(PopupWindow popupWindow, boolean overlapAnchor); 37 boolean getOverlapAnchor(PopupWindow popupWindow); 38 void setWindowLayoutType(PopupWindow popupWindow, int layoutType); 39 int getWindowLayoutType(PopupWindow popupWindow); 40 } 41 42 /** 43 * Interface implementation that doesn't use anything above v4 APIs. 44 */ 45 static class BasePopupWindowImpl implements PopupWindowImpl { 46 @Override 47 public void showAsDropDown(PopupWindow popup, View anchor, int xoff, int yoff, 48 int gravity) { 49 final int hgrav = GravityCompat.getAbsoluteGravity(gravity, 50 ViewCompat.getLayoutDirection(anchor)) & Gravity.HORIZONTAL_GRAVITY_MASK; 51 if (hgrav == Gravity.RIGHT) { 52 // Flip the location to align the right sides of the popup and 53 // anchor instead of left. 54 xoff -= (popup.getWidth() - anchor.getWidth()); 55 } 56 popup.showAsDropDown(anchor, xoff, yoff); 57 } 58 59 @Override 60 public void setOverlapAnchor(PopupWindow popupWindow, boolean overlapAnchor) { 61 // noop 62 } 63 64 @Override 65 public boolean getOverlapAnchor(PopupWindow popupWindow) { 66 return false; 67 } 68 69 @Override 70 public void setWindowLayoutType(PopupWindow popupWindow, int layoutType) { 71 // no-op 72 } 73 74 @Override 75 public int getWindowLayoutType(PopupWindow popupWindow) { 76 return 0; 77 } 78 } 79 80 /** 81 * Interface implementation that doesn't use anything above v4 APIs. 82 */ 83 static class GingerbreadPopupWindowImpl extends BasePopupWindowImpl { 84 @Override 85 public void setWindowLayoutType(PopupWindow popupWindow, int layoutType) { 86 PopupWindowCompatGingerbread.setWindowLayoutType(popupWindow, layoutType); 87 } 88 89 @Override 90 public int getWindowLayoutType(PopupWindow popupWindow) { 91 return PopupWindowCompatGingerbread.getWindowLayoutType(popupWindow); 92 } 93 } 94 95 /** 96 * Interface implementation for devices with at least KitKat APIs. 97 */ 98 static class KitKatPopupWindowImpl extends GingerbreadPopupWindowImpl { 99 @Override 100 public void showAsDropDown(PopupWindow popup, View anchor, int xoff, int yoff, 101 int gravity) { 102 PopupWindowCompatKitKat.showAsDropDown(popup, anchor, xoff, yoff, gravity); 103 } 104 } 105 106 static class Api21PopupWindowImpl extends KitKatPopupWindowImpl { 107 @Override 108 public void setOverlapAnchor(PopupWindow popupWindow, boolean overlapAnchor) { 109 PopupWindowCompatApi21.setOverlapAnchor(popupWindow, overlapAnchor); 110 } 111 112 @Override 113 public boolean getOverlapAnchor(PopupWindow popupWindow) { 114 return PopupWindowCompatApi21.getOverlapAnchor(popupWindow); 115 } 116 } 117 118 static class Api23PopupWindowImpl extends Api21PopupWindowImpl { 119 @Override 120 public void setOverlapAnchor(PopupWindow popupWindow, boolean overlapAnchor) { 121 PopupWindowCompatApi23.setOverlapAnchor(popupWindow, overlapAnchor); 122 } 123 124 @Override 125 public boolean getOverlapAnchor(PopupWindow popupWindow) { 126 return PopupWindowCompatApi23.getOverlapAnchor(popupWindow); 127 } 128 129 @Override 130 public void setWindowLayoutType(PopupWindow popupWindow, int layoutType) { 131 PopupWindowCompatApi23.setWindowLayoutType(popupWindow, layoutType); 132 } 133 134 @Override 135 public int getWindowLayoutType(PopupWindow popupWindow) { 136 return PopupWindowCompatApi23.getWindowLayoutType(popupWindow); 137 } 138 } 139 140 /** 141 * Select the correct implementation to use for the current platform. 142 */ 143 static final PopupWindowImpl IMPL; 144 static { 145 final int version = android.os.Build.VERSION.SDK_INT; 146 if (version >= 23) { 147 IMPL = new Api23PopupWindowImpl(); 148 } else if (version >= 21) { 149 IMPL = new Api21PopupWindowImpl(); 150 } else if (version >= 19) { 151 IMPL = new KitKatPopupWindowImpl(); 152 } else if (version >= 9) { 153 IMPL = new GingerbreadPopupWindowImpl(); 154 } else { 155 IMPL = new BasePopupWindowImpl(); 156 } 157 } 158 159 private PopupWindowCompat() { 160 // This class is not publicly instantiable. 161 } 162 163 /** 164 * <p>Display the content view in a popup window anchored to the bottom-left 165 * corner of the anchor view offset by the specified x and y coordinates. 166 * If there is not enough room on screen to show 167 * the popup in its entirety, this method tries to find a parent scroll 168 * view to scroll. If no parent scroll view can be scrolled, the bottom-left 169 * corner of the popup is pinned at the top left corner of the anchor view.</p> 170 * <p>If the view later scrolls to move <code>anchor</code> to a different 171 * location, the popup will be moved correspondingly.</p> 172 * 173 * @param popup the PopupWindow to show 174 * @param anchor the view on which to pin the popup window 175 * @param xoff A horizontal offset from the anchor in pixels 176 * @param yoff A vertical offset from the anchor in pixels 177 * @param gravity Alignment of the popup relative to the anchor 178 */ 179 public static void showAsDropDown(PopupWindow popup, View anchor, int xoff, int yoff, 180 int gravity) { 181 IMPL.showAsDropDown(popup, anchor, xoff, yoff, gravity); 182 } 183 184 /** 185 * Sets whether the popup window should overlap its anchor view when 186 * displayed as a drop-down. 187 * 188 * @param overlapAnchor Whether the popup should overlap its anchor. 189 */ 190 public static void setOverlapAnchor(PopupWindow popupWindow, boolean overlapAnchor) { 191 IMPL.setOverlapAnchor(popupWindow, overlapAnchor); 192 } 193 194 /** 195 * Returns whether the popup window should overlap its anchor view when 196 * displayed as a drop-down. 197 * 198 * @return Whether the popup should overlap its anchor. 199 */ 200 public static boolean getOverlapAnchor(PopupWindow popupWindow) { 201 return IMPL.getOverlapAnchor(popupWindow); 202 } 203 204 /** 205 * Set the layout type for this window. This value will be passed through to 206 * {@link WindowManager.LayoutParams#type} therefore the value should match any value 207 * {@link WindowManager.LayoutParams#type} accepts. 208 * 209 * @param layoutType Layout type for this window. 210 * 211 * @see WindowManager.LayoutParams#type 212 */ 213 public static void setWindowLayoutType(PopupWindow popupWindow, int layoutType) { 214 IMPL.setWindowLayoutType(popupWindow, layoutType); 215 } 216 217 /** 218 * Returns the layout type for this window. 219 * 220 * @see #setWindowLayoutType(PopupWindow popupWindow, int) 221 */ 222 public static int getWindowLayoutType(PopupWindow popupWindow) { 223 return IMPL.getWindowLayoutType(popupWindow); 224 } 225} 226