1/*
2 * Copyright (C) 2015 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 */
14package android.support.v17.leanback.widget;
15
16import android.support.v7.widget.RecyclerView;
17import android.view.View;
18
19/**
20 * Optional facet provided by {@link RecyclerView.Adapter} or {@link RecyclerView.ViewHolder} for
21 * use in {@link HorizontalGridView} and {@link VerticalGridView}. Apps using {@link Presenter} may
22 * set facet using {@link Presenter#setFacet(Class, Object)} or
23 * {@link Presenter.ViewHolder#setFacet(Class, Object)}. Facet on ViewHolder has a higher priority
24 * than Presenter or Adapter.
25 * <p>
26 * ItemAlignmentFacet contains single or multiple {@link ItemAlignmentDef}s. First
27 * {@link ItemAlignmentDef} describes the default alignment position for ViewHolder, it also
28 * overrides the default item alignment settings on {@link VerticalGridView} and
29 * {@link HorizontalGridView} (see {@link BaseGridView#setItemAlignmentOffset(int)} etc). One
30 * ItemAlignmentFacet can have multiple {@link ItemAlignmentDef}s, e.g. having two aligned positions
31 * when child1 gets focus or child2 gets focus. Grid view will visit focused view and its
32 * ancestors till the root of ViewHolder to match {@link ItemAlignmentDef}s'
33 * {@link ItemAlignmentDef#getItemAlignmentFocusViewId()}. Once a match found, the
34 * {@link ItemAlignmentDef} is used to calculate alignment position.
35 */
36public final class ItemAlignmentFacet {
37
38    /**
39     * Value indicates that percent is not used. Equivalent to 0.
40     */
41    public final static float ITEM_ALIGN_OFFSET_PERCENT_DISABLED = -1;
42
43    /**
44     * Definition of an alignment position under a view.
45     */
46    public static class ItemAlignmentDef {
47        int mViewId = View.NO_ID;
48        int mFocusViewId = View.NO_ID;
49        int mOffset = 0;
50        float mOffsetPercent = 50f;
51        boolean mOffsetWithPadding = false;
52        private boolean mAlignToBaseline;
53
54        /**
55         * Sets number of pixels to the end of low edge. Supports right to left layout direction.
56         * @param offset In left to right or vertical case, it's the offset added to left/top edge.
57         *               In right to left case, it's the offset subtracted from right edge.
58         */
59        public final void setItemAlignmentOffset(int offset) {
60            mOffset = offset;
61        }
62
63        /**
64         * Returns number of pixels to the end of low edge. Supports right to left layout direction.
65         * In left to right or vertical case, it's the offset added to left/top edge. In right to
66         * left case, it's the offset subtracted from right edge.
67         * @return Number of pixels to the end of low edge.
68         */
69        public final int getItemAlignmentOffset() {
70            return mOffset;
71        }
72
73        /**
74         * Sets whether applies padding to item alignment when
75         * {@link #getItemAlignmentOffsetPercent()} is 0 or 100.
76         * <p>When true:
77         * Applies start/top padding if {@link #getItemAlignmentOffsetPercent()} is 0.
78         * Applies end/bottom padding if {@link #getItemAlignmentOffsetPercent()} is 100.
79         * Does not apply padding if {@link #getItemAlignmentOffsetPercent()} is neither 0 nor 100.
80         * </p>
81         * <p>When false: does not apply padding</p>
82         */
83        public final void setItemAlignmentOffsetWithPadding(boolean withPadding) {
84            mOffsetWithPadding = withPadding;
85        }
86
87        /**
88         * Returns true if applies padding to item alignment when
89         * {@link #getItemAlignmentOffsetPercent()} is 0 or 100; returns false otherwise.
90         * <p>When true:
91         * Applies start/top padding when {@link #getItemAlignmentOffsetPercent()} is 0.
92         * Applies end/bottom padding when {@link #getItemAlignmentOffsetPercent()} is 100.
93         * Does not apply padding if {@link #getItemAlignmentOffsetPercent()} is neither 0 nor 100.
94         * </p>
95         * <p>When false: does not apply padding</p>
96         */
97        public final boolean isItemAlignmentOffsetWithPadding() {
98            return mOffsetWithPadding;
99        }
100
101        /**
102         * Sets the offset percent for item alignment in addition to offset.  E.g., 40
103         * means 40% of width/height from the low edge. In the right to left case, it's the 40%
104         * width from right edge. Use {@link #ITEM_ALIGN_OFFSET_PERCENT_DISABLED} to disable.
105         */
106        public final void setItemAlignmentOffsetPercent(float percent) {
107            if ((percent < 0 || percent > 100)
108                    && percent != ITEM_ALIGN_OFFSET_PERCENT_DISABLED) {
109                throw new IllegalArgumentException();
110            }
111            mOffsetPercent = percent;
112        }
113
114        /**
115         * Gets the offset percent for item alignment in addition to offset. E.g., 40
116         * means 40% of the width from the low edge. In the right to left case, it's the 40% from
117         * right edge. Use {@link #ITEM_ALIGN_OFFSET_PERCENT_DISABLED} to disable.
118         */
119        public final float getItemAlignmentOffsetPercent() {
120            return mOffsetPercent;
121        }
122
123        /**
124         * Sets Id of which child view to be aligned.  View.NO_ID refers to root view and should
125         * be only used in first one.  Different view ids of {@link ItemAlignmentFacet
126         * #getAlignmentDefs()} define multiple alignment steps within one itemView, e.g. there are
127         * two child views R.id.child1 and R.id.child2. App may allocated two
128         * {@link ItemAlignmentDef}s, one with view id R.id.child1, the other with view id
129         * R.id.child2. Note this id may or may not be same as the child view that takes focus.
130         *
131         * @param viewId The id of child view that will be aligned to.
132         * @see #setItemAlignmentFocusViewId(int)
133         */
134        public final void setItemAlignmentViewId(int viewId) {
135            mViewId = viewId;
136        }
137
138        /**
139         * Returns Id of which child view to be aligned.  View.NO_ID refers to root view and should
140         * be only used in first one.  Different view ids of {@link ItemAlignmentFacet
141         * #getAlignmentDefs()} define multiple alignment steps within one itemView, e.g. there are
142         * two child views R.id.child1 and R.id.child2. App may allocated two
143         * {@link ItemAlignmentDef}s, one with view id R.id.child1, the other with view id
144         * R.id.child2. Note this id may or may not be same as the child view that takes focus.
145         *
146         * @see #setItemAlignmentFocusViewId(int)
147         */
148        public final int getItemAlignmentViewId() {
149            return mViewId;
150        }
151
152        /**
153         * Sets Id of which child view take focus for alignment.  When not set, it will use
154         * use same id of {@link #getItemAlignmentViewId()}.
155         * @param viewId The id of child view that will be focused to.
156         */
157        public final void setItemAlignmentFocusViewId(int viewId) {
158            mFocusViewId = viewId;
159        }
160
161        /**
162         * Returns Id of which child view take focus for alignment.  When not set, it will use
163         * use same id of {@link #getItemAlignmentViewId()}
164         */
165        public final int getItemAlignmentFocusViewId() {
166            return mFocusViewId != View.NO_ID ? mFocusViewId : mViewId;
167        }
168
169        /**
170         * When true, align to {@link View#getBaseline()} for the view of with id equals
171         * {@link #getItemAlignmentViewId()}; false otherwise.
172         * @param alignToBaseline Boolean indicating whether to align to view baseline.
173         */
174        public final void setAlignedToTextViewBaseline(boolean alignToBaseline) {
175            this.mAlignToBaseline = alignToBaseline;
176        }
177
178        /**
179         * Returns true when View should be aligned to {@link View#getBaseline()}
180         */
181        public boolean isAlignedToTextViewBaseLine() {
182            return mAlignToBaseline;
183        }
184    }
185
186    private ItemAlignmentDef[] mAlignmentDefs = new ItemAlignmentDef[]{new ItemAlignmentDef()};
187
188    public boolean isMultiAlignment() {
189        return mAlignmentDefs.length > 1;
190    }
191
192    /**
193     * Sets definitions of alignment positions.
194     */
195    public void setAlignmentDefs(ItemAlignmentDef[] defs) {
196        if (defs == null || defs.length < 1) {
197            throw new IllegalArgumentException();
198        }
199        mAlignmentDefs = defs;
200    }
201
202    /**
203     * Returns read only definitions of alignment positions.
204     */
205    public ItemAlignmentDef[] getAlignmentDefs() {
206        return mAlignmentDefs;
207    }
208
209}
210