1/*
2 * Copyright (C) 2015 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 */
16package android.databinding.adapters;
17
18import android.annotation.TargetApi;
19import android.databinding.BindingAdapter;
20import android.databinding.BindingMethod;
21import android.databinding.BindingMethods;
22import android.graphics.drawable.Drawable;
23import android.os.Build;
24import android.os.Build.VERSION;
25import android.os.Build.VERSION_CODES;
26import android.view.View;
27import android.view.View.OnAttachStateChangeListener;
28import com.android.databinding.library.baseAdapters.R;
29
30@BindingMethods({
31        @BindingMethod(type = View.class, attribute = "android:backgroundTint", method = "setBackgroundTintList"),
32        @BindingMethod(type = View.class, attribute = "android:fadeScrollbars", method = "setScrollbarFadingEnabled"),
33        @BindingMethod(type = View.class, attribute = "android:getOutline", method = "setOutlineProvider"),
34        @BindingMethod(type = View.class, attribute = "android:nextFocusForward", method = "setNextFocusForwardId"),
35        @BindingMethod(type = View.class, attribute = "android:nextFocusLeft", method = "setNextFocusLeftId"),
36        @BindingMethod(type = View.class, attribute = "android:nextFocusRight", method = "setNextFocusRightId"),
37        @BindingMethod(type = View.class, attribute = "android:nextFocusUp", method = "setNextFocusUpId"),
38        @BindingMethod(type = View.class, attribute = "android:nextFocusDown", method = "setNextFocusDownId"),
39        @BindingMethod(type = View.class, attribute = "android:requiresFadingEdge", method = "setVerticalFadingEdgeEnabled"),
40        @BindingMethod(type = View.class, attribute = "android:scrollbarDefaultDelayBeforeFade", method = "setScrollBarDefaultDelayBeforeFade"),
41        @BindingMethod(type = View.class, attribute = "android:scrollbarFadeDuration", method = "setScrollBarFadeDuration"),
42        @BindingMethod(type = View.class, attribute = "android:scrollbarSize", method = "setScrollBarSize"),
43        @BindingMethod(type = View.class, attribute = "android:scrollbarStyle", method = "setScrollBarStyle"),
44        @BindingMethod(type = View.class, attribute = "android:transformPivotX", method = "setPivotX"),
45        @BindingMethod(type = View.class, attribute = "android:transformPivotY", method = "setPivotY"),
46        @BindingMethod(type = View.class, attribute = "android:onDrag", method = "setOnDragListener"),
47        @BindingMethod(type = View.class, attribute = "android:onClick", method = "setOnClickListener"),
48        @BindingMethod(type = View.class, attribute = "android:onApplyWindowInsets", method = "setOnApplyWindowInsetsListener"),
49        @BindingMethod(type = View.class, attribute = "android:onCreateContextMenu", method = "setOnCreateContextMenuListener"),
50        @BindingMethod(type = View.class, attribute = "android:onFocusChange", method = "setOnFocusChangeListener"),
51        @BindingMethod(type = View.class, attribute = "android:onGenericMotion", method = "setOnGenericMotionListener"),
52        @BindingMethod(type = View.class, attribute = "android:onHover", method = "setOnHoverListener"),
53        @BindingMethod(type = View.class, attribute = "android:onKey", method = "setOnKeyListener"),
54        @BindingMethod(type = View.class, attribute = "android:onLongClick", method = "setOnLongClickListener"),
55        @BindingMethod(type = View.class, attribute = "android:onSystemUiVisibilityChange", method = "setOnSystemUiVisibilityChangeListener"),
56        @BindingMethod(type = View.class, attribute = "android:onTouch", method = "setOnTouchListener"),
57})
58public class ViewBindingAdapter {
59    public static int FADING_EDGE_NONE = 0;
60    public static int FADING_EDGE_HORIZONTAL = 1;
61    public static int FADING_EDGE_VERTICAL = 2;
62
63    @BindingAdapter({"android:padding"})
64    public static void setPadding(View view, float paddingFloat) {
65        final int padding = pixelsToDimensionPixelSize(paddingFloat);
66        view.setPadding(padding, padding, padding, padding);
67    }
68
69    @BindingAdapter({"android:paddingBottom"})
70    public static void setPaddingBottom(View view, float paddingFloat) {
71        final int padding = pixelsToDimensionPixelSize(paddingFloat);
72        view.setPadding(view.getPaddingLeft(), view.getPaddingTop(), view.getPaddingRight(),
73                padding);
74    }
75
76    @BindingAdapter({"android:paddingEnd"})
77    public static void setPaddingEnd(View view, float paddingFloat) {
78        final int padding = pixelsToDimensionPixelSize(paddingFloat);
79        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
80            view.setPaddingRelative(view.getPaddingStart(), view.getPaddingTop(), padding,
81                    view.getPaddingBottom());
82        } else {
83            view.setPadding(view.getPaddingLeft(), view.getPaddingTop(), padding,
84                    view.getPaddingBottom());
85        }
86    }
87
88    @BindingAdapter({"android:paddingLeft"})
89    public static void setPaddingLeft(View view, float paddingFloat) {
90        final int padding = pixelsToDimensionPixelSize(paddingFloat);
91        view.setPadding(padding, view.getPaddingTop(), view.getPaddingRight(),
92                view.getPaddingBottom());
93    }
94
95    @BindingAdapter({"android:paddingRight"})
96    public static void setPaddingRight(View view, float paddingFloat) {
97        final int padding = pixelsToDimensionPixelSize(paddingFloat);
98        view.setPadding(view.getPaddingLeft(), view.getPaddingTop(), padding,
99                view.getPaddingBottom());
100    }
101
102    @BindingAdapter({"android:paddingStart"})
103    public static void setPaddingStart(View view, float paddingFloat) {
104        final int padding = pixelsToDimensionPixelSize(paddingFloat);
105        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
106            view.setPaddingRelative(padding, view.getPaddingTop(), view.getPaddingEnd(),
107                    view.getPaddingBottom());
108        } else {
109            view.setPadding(padding, view.getPaddingTop(), view.getPaddingRight(),
110                    view.getPaddingBottom());
111        }
112    }
113
114    @BindingAdapter({"android:paddingTop"})
115    public static void setPaddingTop(View view, float paddingFloat) {
116        final int padding = pixelsToDimensionPixelSize(paddingFloat);
117        view.setPadding(view.getPaddingLeft(), padding, view.getPaddingRight(),
118                view.getPaddingBottom());
119    }
120
121    @BindingAdapter({"android:requiresFadingEdge"})
122    public static void setRequiresFadingEdge(View view, int value) {
123        final boolean vertical = (value & FADING_EDGE_VERTICAL) != 0;
124        final boolean horizontal = (value & FADING_EDGE_HORIZONTAL) != 0;
125        view.setVerticalFadingEdgeEnabled(vertical);
126        view.setHorizontalFadingEdgeEnabled(horizontal);
127    }
128
129    @BindingAdapter({"android:onClickListener", "android:clickable"})
130    public static void setClickListener(View view, View.OnClickListener clickListener,
131            boolean clickable) {
132        view.setOnClickListener(clickListener);
133        view.setClickable(clickable);
134    }
135
136    @BindingAdapter({"android:onClick", "android:clickable"})
137    public static void setOnClick(View view, View.OnClickListener clickListener,
138            boolean clickable) {
139        view.setOnClickListener(clickListener);
140        view.setClickable(clickable);
141    }
142
143    @BindingAdapter({"android:onLongClickListener", "android:longClickable"})
144    public static void setOnLongClickListener(View view, View.OnLongClickListener clickListener,
145            boolean clickable) {
146        view.setOnLongClickListener(clickListener);
147        view.setLongClickable(clickable);
148    }
149
150    @BindingAdapter({"android:onLongClick", "android:longClickable"})
151    public static void setOnLongClick(View view, View.OnLongClickListener clickListener,
152            boolean clickable) {
153        view.setOnLongClickListener(clickListener);
154        view.setLongClickable(clickable);
155    }
156
157    @BindingAdapter(value = {"android:onViewDetachedFromWindow", "android:onViewAttachedToWindow"},
158            requireAll = false)
159    public static void setOnAttachStateChangeListener(View view,
160            final OnViewDetachedFromWindow detach, final OnViewAttachedToWindow attach) {
161        if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB_MR1) {
162            final OnAttachStateChangeListener newListener;
163            if (detach == null && attach == null) {
164                newListener = null;
165            } else {
166                newListener = new OnAttachStateChangeListener() {
167                    @Override
168                    public void onViewAttachedToWindow(View v) {
169                        if (attach != null) {
170                            attach.onViewAttachedToWindow(v);
171                        }
172                    }
173
174                    @Override
175                    public void onViewDetachedFromWindow(View v) {
176                        if (detach != null) {
177                            detach.onViewDetachedFromWindow(v);
178                        }
179                    }
180                };
181            }
182            final OnAttachStateChangeListener oldListener = ListenerUtil.trackListener(view,
183                    newListener, R.id.onAttachStateChangeListener);
184            if (oldListener != null) {
185                view.removeOnAttachStateChangeListener(oldListener);
186            }
187            if (newListener != null) {
188                view.addOnAttachStateChangeListener(newListener);
189            }
190        }
191    }
192
193    @BindingAdapter("android:onLayoutChange")
194    public static void setOnLayoutChangeListener(View view, View.OnLayoutChangeListener oldValue,
195            View.OnLayoutChangeListener newValue) {
196        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
197            if (oldValue != null) {
198                view.removeOnLayoutChangeListener(oldValue);
199            }
200            if (newValue != null) {
201                view.addOnLayoutChangeListener(newValue);
202            }
203        }
204    }
205
206    @SuppressWarnings("deprecation")
207    @BindingAdapter("android:background")
208    public static void setBackground(View view, Drawable drawable) {
209        if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {
210            view.setBackground(drawable);
211        } else {
212            view.setBackgroundDrawable(drawable);
213        }
214    }
215
216    // Follows the same conversion mechanism as in TypedValue.complexToDimensionPixelSize as used
217    // when setting padding. It rounds off the float value unless the value is < 1.
218    // When a value is between 0 and 1, it is set to 1. A value less than 0 is set to -1.
219    private static int pixelsToDimensionPixelSize(float pixels) {
220        final int result = (int) (pixels + 0.5f);
221        if (result != 0) {
222            return result;
223        } else if (pixels == 0) {
224            return 0;
225        } else if (pixels > 0) {
226            return 1;
227        } else {
228            return -1;
229        }
230    }
231
232    @TargetApi(VERSION_CODES.HONEYCOMB_MR1)
233    public interface OnViewDetachedFromWindow {
234        void onViewDetachedFromWindow(View v);
235    }
236
237    @TargetApi(VERSION_CODES.HONEYCOMB_MR1)
238    public interface OnViewAttachedToWindow {
239        void onViewAttachedToWindow(View v);
240    }
241}
242