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