WindowAlignment.java revision 08a42433300b4617bad27bda1214e12ca7854adc
1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 * in compliance with the License. You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software distributed under the License
10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 * or implied. See the License for the specific language governing permissions and limitations under
12 * the License.
13 */
14
15package android.support.v17.leanback.widget;
16
17import static android.support.v17.leanback.widget.BaseGridView.WINDOW_ALIGN_LOW_EDGE;
18import static android.support.v17.leanback.widget.BaseGridView.WINDOW_ALIGN_HIGH_EDGE;
19import static android.support.v17.leanback.widget.BaseGridView.WINDOW_ALIGN_BOTH_EDGE;
20import static android.support.v17.leanback.widget.BaseGridView.WINDOW_ALIGN_OFFSET_PERCENT_DISABLED;
21
22import static android.support.v7.widget.RecyclerView.HORIZONTAL;
23
24/**
25 * Maintains Window Alignment information of two axis.
26 */
27class WindowAlignment {
28
29    /**
30     * Maintains alignment information in one direction.
31     */
32    public static class Axis {
33        /**
34         * mScrollCenter is used to calculate dynamic transformation based on how far a view
35         * is from the mScrollCenter. For example, the views with center close to mScrollCenter
36         * will be scaled up.
37         */
38        private float mScrollCenter;
39        /**
40         * Right or bottom edge of last child.
41         */
42        private int mMaxEdge;
43        /**
44         * Left or top edge of first child, typically should be zero.
45         */
46        private int mMinEdge;
47        /**
48         * Max Scroll value
49         */
50        private int mMaxScroll;
51        /**
52         * Min Scroll value
53         */
54        private int mMinScroll;
55
56        private int mWindowAlignment = WINDOW_ALIGN_BOTH_EDGE;
57
58        private int mWindowAlignmentOffset = 0;
59
60        private float mWindowAlignmentOffsetPercent = 50f;
61
62        private int mSize;
63
64        private int mPaddingLow;
65
66        private int mPaddingHigh;
67
68        private String mName; // for debugging
69
70        public Axis(String name) {
71            reset();
72            mName = name;
73        }
74
75        final public int getWindowAlignment() {
76            return mWindowAlignment;
77        }
78
79        final public void setWindowAlignment(int windowAlignment) {
80            mWindowAlignment = windowAlignment;
81        }
82
83        final public int getWindowAlignmentOffset() {
84            return mWindowAlignmentOffset;
85        }
86
87        final public void setWindowAlignmentOffset(int offset) {
88            mWindowAlignmentOffset = offset;
89        }
90
91        final public void setWindowAlignmentOffsetPercent(float percent) {
92            if ((percent < 0 || percent > 100)
93                    && percent != WINDOW_ALIGN_OFFSET_PERCENT_DISABLED) {
94                throw new IllegalArgumentException();
95            }
96            mWindowAlignmentOffsetPercent = percent;
97        }
98
99        final public float getWindowAlignmentOffsetPercent() {
100            return mWindowAlignmentOffsetPercent;
101        }
102
103        final public int getScrollCenter() {
104            return (int) mScrollCenter;
105        }
106
107        /** set minEdge,  Integer.MIN_VALUE means unknown*/
108        final public void setMinEdge(int minEdge) {
109            mMinEdge = minEdge;
110        }
111
112        final public int getMinEdge() {
113            return mMinEdge;
114        }
115
116        /** set minScroll,  Integer.MIN_VALUE means unknown*/
117        final public void setMinScroll(int minScroll) {
118            mMinScroll = minScroll;
119        }
120
121        final public int getMinScroll() {
122            return mMinScroll;
123        }
124
125        final public void invalidateScrollMin() {
126            mMinEdge = Integer.MIN_VALUE;
127            mMinScroll = Integer.MIN_VALUE;
128        }
129
130        /** update max edge,  Integer.MAX_VALUE means unknown*/
131        final public void setMaxEdge(int maxEdge) {
132            mMaxEdge = maxEdge;
133        }
134
135        final public int getMaxEdge() {
136            return mMaxEdge;
137        }
138
139        /** update max scroll,  Integer.MAX_VALUE means unknown*/
140        final public void setMaxScroll(int maxScroll) {
141            mMaxScroll = maxScroll;
142        }
143
144        final public int getMaxScroll() {
145            return mMaxScroll;
146        }
147
148        final public void invalidateScrollMax() {
149            mMaxEdge = Integer.MAX_VALUE;
150            mMaxScroll = Integer.MAX_VALUE;
151        }
152
153        final public float updateScrollCenter(float scrollTarget) {
154            mScrollCenter = scrollTarget;
155            return scrollTarget;
156        }
157
158        private void reset() {
159            mScrollCenter = Integer.MIN_VALUE;
160            mMinEdge = Integer.MIN_VALUE;
161            mMaxEdge = Integer.MAX_VALUE;
162        }
163
164        final public boolean isMinUnknown() {
165            return mMinEdge == Integer.MIN_VALUE;
166        }
167
168        final public boolean isMaxUnknown() {
169            return mMaxEdge == Integer.MAX_VALUE;
170        }
171
172        final public void setSize(int size) {
173            mSize = size;
174        }
175
176        final public int getSize() {
177            return mSize;
178        }
179
180        final public void setPadding(int paddingLow, int paddingHigh) {
181            mPaddingLow = paddingLow;
182            mPaddingHigh = paddingHigh;
183        }
184
185        final public int getPaddingLow() {
186            return mPaddingLow;
187        }
188
189        final public int getPaddingHigh() {
190            return mPaddingHigh;
191        }
192
193        final public int getClientSize() {
194            return mSize - mPaddingLow - mPaddingHigh;
195        }
196
197        final public int getSystemScrollPos(boolean isFirst, boolean isLast) {
198            return getSystemScrollPos((int) mScrollCenter, isFirst, isLast);
199        }
200
201        final public int getSystemScrollPos(int scrollCenter, boolean isFirst, boolean isLast) {
202            int middlePosition;
203            if (mWindowAlignmentOffset >= 0) {
204                middlePosition = mWindowAlignmentOffset - mPaddingLow;
205            } else {
206                middlePosition = mSize + mWindowAlignmentOffset - mPaddingLow;
207            }
208            if (mWindowAlignmentOffsetPercent != WINDOW_ALIGN_OFFSET_PERCENT_DISABLED) {
209                middlePosition += (int) (mSize * mWindowAlignmentOffsetPercent / 100);
210            }
211            int clientSize = getClientSize();
212            int afterMiddlePosition = clientSize - middlePosition;
213            boolean isMinUnknown = isMinUnknown();
214            boolean isMaxUnknown = isMaxUnknown();
215            if (!isMinUnknown && !isMaxUnknown &&
216                    (mWindowAlignment & WINDOW_ALIGN_BOTH_EDGE) == WINDOW_ALIGN_BOTH_EDGE) {
217                if (mMaxEdge - mMinEdge <= clientSize) {
218                    // total children size is less than view port and we want to align
219                    // both edge:  align first child to left edge of view port
220                    return mMinEdge - mPaddingLow;
221                }
222            }
223            if (!isMinUnknown) {
224                if ((mWindowAlignment & WINDOW_ALIGN_LOW_EDGE) != 0 &&
225                        (isFirst || scrollCenter - mMinEdge <= middlePosition)) {
226                    // scroll center is within half of view port size: align the left edge
227                    // of first child to the left edge of view port
228                    return mMinEdge - mPaddingLow;
229                }
230            }
231            if (!isMaxUnknown) {
232                if ((mWindowAlignment & WINDOW_ALIGN_HIGH_EDGE) != 0 &&
233                        (isLast || mMaxEdge - scrollCenter <= afterMiddlePosition)) {
234                    // scroll center is very close to the right edge of view port : align the
235                    // right edge of last children (plus expanded size) to view port's right
236                    return mMaxEdge -mPaddingLow - (clientSize);
237                }
238            }
239            // else put scroll center in middle of view port
240            return scrollCenter - middlePosition - mPaddingLow;
241        }
242
243        @Override
244        public String toString() {
245            return "center: " + mScrollCenter + " min:" + mMinEdge +
246                    " max:" + mMaxEdge;
247        }
248
249    }
250
251    private int mOrientation = HORIZONTAL;
252
253    final public Axis vertical = new Axis("vertical");
254
255    final public Axis horizontal = new Axis("horizontal");
256
257    private Axis mMainAxis = horizontal;
258
259    private Axis mSecondAxis = vertical;
260
261    final public Axis mainAxis() {
262        return mMainAxis;
263    }
264
265    final public Axis secondAxis() {
266        return mSecondAxis;
267    }
268
269    final public void setOrientation(int orientation) {
270        mOrientation = orientation;
271        if (mOrientation == HORIZONTAL) {
272            mMainAxis = horizontal;
273            mSecondAxis = vertical;
274        } else {
275            mMainAxis = vertical;
276            mSecondAxis = horizontal;
277        }
278    }
279
280    final public int getOrientation() {
281        return mOrientation;
282    }
283
284    final public void reset() {
285        mainAxis().reset();
286    }
287
288    @Override
289    public String toString() {
290        return new StringBuffer().append("horizontal=")
291                .append(horizontal.toString())
292                .append("vertical=")
293                .append(vertical.toString())
294                .toString();
295    }
296
297}
298