WindowAlignment.java revision 8e3566285de4ac771d6188f62fe947e23d371a3d
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
24import android.view.View;
25
26/**
27 * Maintains Window Alignment information of two axis.
28 */
29class WindowAlignment {
30
31    /**
32     * Maintains alignment information in one direction.
33     */
34    public static class Axis {
35        /**
36         * mScrollCenter is used to calculate dynamic transformation based on how far a view
37         * is from the mScrollCenter. For example, the views with center close to mScrollCenter
38         * will be scaled up.
39         */
40        private float mScrollCenter;
41        /**
42         * Right or bottom edge of last child.
43         */
44        private int mMaxEdge;
45        /**
46         * Left or top edge of first child, typically should be zero.
47         */
48        private int mMinEdge;
49        /**
50         * Max Scroll value
51         */
52        private int mMaxScroll;
53        /**
54         * Min Scroll value
55         */
56        private int mMinScroll;
57
58        private int mWindowAlignment = WINDOW_ALIGN_BOTH_EDGE;
59
60        private int mWindowAlignmentOffset = 0;
61
62        private float mWindowAlignmentOffsetPercent = 50f;
63
64        private int mSize;
65
66        private int mPaddingLow;
67
68        private int mPaddingHigh;
69
70        private boolean mReversedFlow = false;
71
72        private String mName; // for debugging
73
74        public Axis(String name) {
75            reset();
76            mName = name;
77        }
78
79        final public int getWindowAlignment() {
80            return mWindowAlignment;
81        }
82
83        final public void setWindowAlignment(int windowAlignment) {
84            mWindowAlignment = windowAlignment;
85        }
86
87        final public int getWindowAlignmentOffset() {
88            return mWindowAlignmentOffset;
89        }
90
91        final public void setWindowAlignmentOffset(int offset) {
92            mWindowAlignmentOffset = offset;
93        }
94
95        final public void setWindowAlignmentOffsetPercent(float percent) {
96            if ((percent < 0 || percent > 100)
97                    && percent != WINDOW_ALIGN_OFFSET_PERCENT_DISABLED) {
98                throw new IllegalArgumentException();
99            }
100            mWindowAlignmentOffsetPercent = percent;
101        }
102
103        final public float getWindowAlignmentOffsetPercent() {
104            return mWindowAlignmentOffsetPercent;
105        }
106
107        final public int getScrollCenter() {
108            return (int) mScrollCenter;
109        }
110
111        /** set minEdge,  Integer.MIN_VALUE means unknown*/
112        final public void setMinEdge(int minEdge) {
113            mMinEdge = minEdge;
114        }
115
116        final public int getMinEdge() {
117            return mMinEdge;
118        }
119
120        /** set minScroll,  Integer.MIN_VALUE means unknown*/
121        final public void setMinScroll(int minScroll) {
122            mMinScroll = minScroll;
123        }
124
125        final public int getMinScroll() {
126            return mMinScroll;
127        }
128
129        final public void setLayoutDirection(int layoutDirection) {
130            mReversedFlow = layoutDirection == View.LAYOUT_DIRECTION_RTL;
131        }
132
133        final public void invalidateScrollMin() {
134            mMinEdge = Integer.MIN_VALUE;
135            mMinScroll = Integer.MIN_VALUE;
136        }
137
138        /** update max edge,  Integer.MAX_VALUE means unknown*/
139        final public void setMaxEdge(int maxEdge) {
140            mMaxEdge = maxEdge;
141        }
142
143        final public int getMaxEdge() {
144            return mMaxEdge;
145        }
146
147        /** update max scroll,  Integer.MAX_VALUE means unknown*/
148        final public void setMaxScroll(int maxScroll) {
149            mMaxScroll = maxScroll;
150        }
151
152        final public int getMaxScroll() {
153            return mMaxScroll;
154        }
155
156        final public void invalidateScrollMax() {
157            mMaxEdge = Integer.MAX_VALUE;
158            mMaxScroll = Integer.MAX_VALUE;
159        }
160
161        final public float updateScrollCenter(float scrollTarget) {
162            mScrollCenter = scrollTarget;
163            return scrollTarget;
164        }
165
166        private void reset() {
167            mScrollCenter = Integer.MIN_VALUE;
168            mMinEdge = Integer.MIN_VALUE;
169            mMaxEdge = Integer.MAX_VALUE;
170        }
171
172        final public boolean isMinUnknown() {
173            return mMinEdge == Integer.MIN_VALUE;
174        }
175
176        final public boolean isMaxUnknown() {
177            return mMaxEdge == Integer.MAX_VALUE;
178        }
179
180        final public void setSize(int size) {
181            mSize = size;
182        }
183
184        final public int getSize() {
185            return mSize;
186        }
187
188        final public void setPadding(int paddingLow, int paddingHigh) {
189            mPaddingLow = paddingLow;
190            mPaddingHigh = paddingHigh;
191        }
192
193        final public int getPaddingLow() {
194            return mPaddingLow;
195        }
196
197        final public int getPaddingHigh() {
198            return mPaddingHigh;
199        }
200
201        final public int getClientSize() {
202            return mSize - mPaddingLow - mPaddingHigh;
203        }
204
205        final public int getSystemScrollPos(boolean isFirst, boolean isLast) {
206            return getSystemScrollPos((int) mScrollCenter, isFirst, isLast);
207        }
208
209        final public int getSystemScrollPos(int scrollCenter, boolean isFirst, boolean isLast) {
210            int middlePosition;
211            if (mWindowAlignmentOffset >= 0) {
212                middlePosition = mWindowAlignmentOffset - mPaddingLow;
213            } else {
214                middlePosition = mSize + mWindowAlignmentOffset - mPaddingLow;
215            }
216            if (mWindowAlignmentOffsetPercent != WINDOW_ALIGN_OFFSET_PERCENT_DISABLED) {
217                middlePosition += (int) (mSize * mWindowAlignmentOffsetPercent / 100);
218            }
219            int clientSize = getClientSize();
220            int afterMiddlePosition = clientSize - middlePosition;
221            boolean isMinUnknown = isMinUnknown();
222            boolean isMaxUnknown = isMaxUnknown();
223            boolean isAtMin = (!mReversedFlow) ? isFirst : isLast;
224            boolean isAtMax = (!mReversedFlow) ? isLast : isFirst;
225            if (!isMinUnknown && !isMaxUnknown &&
226                    (mWindowAlignment & WINDOW_ALIGN_BOTH_EDGE) == WINDOW_ALIGN_BOTH_EDGE) {
227                if (mMaxEdge - mMinEdge <= clientSize) {
228                    // total children size is less than view port and we want to align
229                    // both edge:  align first child to start edge of view port
230                    int result = (!mReversedFlow) ?
231                        mMinEdge - mPaddingLow :
232                        mMaxEdge - mPaddingLow - clientSize;
233                    return result;
234                }
235            }
236            if (!isMinUnknown) {
237                if ((mWindowAlignment & WINDOW_ALIGN_LOW_EDGE) != 0 &&
238                        (isAtMin || scrollCenter - mMinEdge <= middlePosition)) {
239                    // scroll center is within half of view port size: align the start edge
240                    // of first child to the start edge of view port
241                    int result = mMinEdge - mPaddingLow;
242                    return result;
243                }
244            }
245            if (!isMaxUnknown) {
246                if ((mWindowAlignment & WINDOW_ALIGN_HIGH_EDGE) != 0 &&
247                        (isAtMax || mMaxEdge - scrollCenter <= afterMiddlePosition)) {
248                    // scroll center is very close to the end edge of view port : align the
249                    // end edge of last children (plus expanded size) to view port's end
250                    int result = mMaxEdge - mPaddingLow - clientSize;
251                    return result;
252                }
253            }
254            // else put scroll center in middle of view port
255            int result = scrollCenter - middlePosition - mPaddingLow;
256            return result;
257        }
258
259        @Override
260        public String toString() {
261            return "center: " + mScrollCenter + " min:" + mMinEdge +
262                    " max:" + mMaxEdge;
263        }
264
265    }
266
267    private int mOrientation = HORIZONTAL;
268
269    final public Axis vertical = new Axis("vertical");
270
271    final public Axis horizontal = new Axis("horizontal");
272
273    private Axis mMainAxis = horizontal;
274
275    private Axis mSecondAxis = vertical;
276
277    final public Axis mainAxis() {
278        return mMainAxis;
279    }
280
281    final public Axis secondAxis() {
282        return mSecondAxis;
283    }
284
285    final public void setOrientation(int orientation) {
286        mOrientation = orientation;
287        if (mOrientation == HORIZONTAL) {
288            mMainAxis = horizontal;
289            mSecondAxis = vertical;
290        } else {
291            mMainAxis = vertical;
292            mSecondAxis = horizontal;
293        }
294    }
295
296    final public int getOrientation() {
297        return mOrientation;
298    }
299
300    final public void setLayoutDirection(int layoutDirection) {
301        horizontal.setLayoutDirection(layoutDirection);
302    }
303
304    final public void reset() {
305        mainAxis().reset();
306    }
307
308    @Override
309    public String toString() {
310        return new StringBuffer().append("horizontal=")
311                .append(horizontal.toString())
312                .append("; vertical=")
313                .append(vertical.toString())
314                .toString();
315    }
316
317}
318