1/*
2 * Copyright (C) 2016 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.v17.leanback.widget;
18
19import android.animation.PropertyValuesHolder;
20import android.content.Context;
21import android.graphics.Color;
22import android.graphics.drawable.ColorDrawable;
23import android.graphics.drawable.Drawable;
24import android.support.annotation.ColorInt;
25import android.support.v17.leanback.R;
26import android.support.v17.leanback.graphics.CompositeDrawable;
27import android.support.v17.leanback.graphics.FitWidthBitmapDrawable;
28import android.util.TypedValue;
29
30/**
31 * Helper class responsible for wiring in parallax effect in
32 * {@link android.support.v17.leanback.app.DetailsFragment}. The default effect will render
33 * a drawable like the following two parts, cover drawable above DetailsOverviewRow and solid
34 * color below DetailsOverviewRow.
35 * <pre>
36 *        ***************************
37 *        *        Cover Drawable   *
38 *        ***************************
39 *        *    DetailsOverviewRow   *
40 *        *                         *
41 *        ***************************
42 *        *     Bottom Drawable     *
43 *        *      (Solid Color)      *
44 *        *         Related         *
45 *        *         Content         *
46 *        ***************************
47 * </pre>
48 * <ul>
49 * <li>
50 * Call {@link #DetailsParallaxDrawable(Context, DetailsParallax)} to create DetailsParallaxDrawable
51 * using {@link FitWidthBitmapDrawable} for cover drawable.
52 * </li>
53 * </ul>
54 * <li>
55 * In case the solid color is not set, it will use defaultBrandColorDark from LeanbackTheme.
56 * </li>
57 * @hide
58 */
59public class DetailsParallaxDrawable extends CompositeDrawable {
60    private Drawable mBottomDrawable;
61
62    /**
63     * Creates a DetailsParallaxDrawable using a cover drawable.
64     * @param context Context to get resource values.
65     * @param parallax DetailsParallax to add background parallax effect.
66     * @param coverDrawable Cover drawable at top
67     * @param coverDrawableParallaxTarget Define a ParallaxTarget that would be performed on cover
68     *                                    Drawable. e.g. To change "verticalOffset" of cover
69     *                                    Drawable from 0 to 120 pixels above screen, uses:
70     *                                    new ParallaxTarget.PropertyValuesHolderTarget(
71     *                                        coverDrawable,
72     *                                        PropertyValuesHolder.ofInt("verticalOffset", 0, -120))
73     */
74    public DetailsParallaxDrawable(Context context, DetailsParallax parallax,
75                                   Drawable coverDrawable,
76                                   ParallaxTarget coverDrawableParallaxTarget) {
77        init(context, parallax, coverDrawable, new ColorDrawable(), coverDrawableParallaxTarget);
78    }
79
80    /**
81     * Creates a DetailsParallaxDrawable using a cover drawable and bottom drawable.
82     * @param context Context to get resource values.
83     * @param parallax DetailsParallax to add background parallax effect.
84     * @param coverDrawable Cover drawable at top
85     * @param bottomDrawable Bottom drawable, when null it will create a default ColorDrawable.
86     * @param coverDrawableParallaxTarget Define a ParallaxTarget that would be performed on cover
87     *                                    Drawable. e.g. To change "verticalOffset" of cover
88     *                                    Drawable from 0 to 120 pixels above screen, uses:
89     *                                    new ParallaxTarget.PropertyValuesHolderTarget(
90     *                                        coverDrawable,
91     *                                        PropertyValuesHolder.ofInt("verticalOffset", 0, -120))
92     */
93    public DetailsParallaxDrawable(Context context, DetailsParallax parallax,
94                                   Drawable coverDrawable, Drawable bottomDrawable,
95                                   ParallaxTarget coverDrawableParallaxTarget) {
96
97        init(context, parallax, coverDrawable, bottomDrawable, coverDrawableParallaxTarget);
98    }
99
100    /**
101     * Creates DetailsParallaxDrawable using {@link FitWidthBitmapDrawable} for cover drawable.
102     * @param context Context to get resource values.
103     * @param parallax DetailsParallax to add background parallax effect.
104     */
105    public DetailsParallaxDrawable(Context context, DetailsParallax parallax) {
106        int verticalMovementMax = -context.getResources().getDimensionPixelSize(
107                R.dimen.lb_details_cover_drawable_parallax_movement);
108        Drawable coverDrawable = new FitWidthBitmapDrawable();
109        ParallaxTarget coverDrawableParallaxTarget = new ParallaxTarget.PropertyValuesHolderTarget(
110                coverDrawable, PropertyValuesHolder.ofInt("verticalOffset", 0,
111                verticalMovementMax));
112        init(context, parallax, coverDrawable, new ColorDrawable(), coverDrawableParallaxTarget);
113    }
114
115    void init(Context context, DetailsParallax parallax,
116              Drawable coverDrawable, Drawable bottomDrawable,
117              ParallaxTarget coverDrawableParallaxTarget) {
118        if (bottomDrawable instanceof ColorDrawable) {
119            ColorDrawable colorDrawable = ((ColorDrawable) bottomDrawable);
120            if (colorDrawable.getColor() == Color.TRANSPARENT) {
121                colorDrawable.setColor(getDefaultBackgroundColor(context));
122            }
123        }
124        addChildDrawable(coverDrawable);
125        addChildDrawable(mBottomDrawable = bottomDrawable);
126        connect(context, parallax, coverDrawableParallaxTarget);
127    }
128
129    private static int getDefaultBackgroundColor(Context context) {
130        TypedValue outValue = new TypedValue();
131        if (context.getTheme().resolveAttribute(R.attr.defaultBrandColorDark, outValue, true)) {
132            return context.getResources().getColor(outValue.resourceId);
133        }
134        return context.getResources().getColor(R.color.lb_default_brand_color_dark);
135    }
136
137    /**
138     * @return First child which is cover drawable appearing at top.
139     */
140    public Drawable getCoverDrawable() {
141        return getChildAt(0).getDrawable();
142    }
143
144    /**
145     * @return Second child which is ColorDrawable by default.
146     */
147    public Drawable getBottomDrawable() {
148        return mBottomDrawable;
149    }
150
151    /**
152     * Changes the solid background color of the related content section.
153     */
154    public void setSolidColor(@ColorInt int color) {
155        ((ColorDrawable) mBottomDrawable).setColor(color);
156    }
157
158    /**
159     * @return Returns the solid background color of the related content section.
160     */
161    public @ColorInt int getSolidColor() {
162        return ((ColorDrawable) mBottomDrawable).getColor();
163    }
164
165    /**
166     * Connects DetailsParallaxDrawable to DetailsParallax object.
167     * @param parallax The DetailsParallax object to add ParallaxEffects for the drawable.
168     */
169    void connect(Context context, DetailsParallax parallax,
170                        ParallaxTarget coverDrawableParallaxTarget) {
171
172        Parallax.IntProperty frameTop = parallax.getOverviewRowTop();
173        Parallax.IntProperty frameBottom = parallax.getOverviewRowBottom();
174
175        final int fromValue = context.getResources()
176                .getDimensionPixelSize(R.dimen.lb_details_v2_align_pos_for_actions);
177        final int toValue = context.getResources()
178                .getDimensionPixelSize(R.dimen.lb_details_v2_align_pos_for_description);
179        parallax.addEffect(frameTop.atAbsolute(fromValue), frameTop.atAbsolute(toValue))
180                .target(coverDrawableParallaxTarget);
181
182        // Add solid color parallax effect:
183        // When frameBottom moves from bottom of the screen to top of the screen,
184        // change solid ColorDrawable's top from bottom of screen to top of the screen.
185        parallax.addEffect(frameBottom.atMax(), frameBottom.atMin())
186                .target(getChildAt(1), ChildDrawable.TOP_ABSOLUTE);
187        // Also when frameTop moves from bottom of screen to top of the screen,
188        // we are changing bottom of the bitmap from bottom of screen to top of screen.
189        parallax.addEffect(frameTop.atMax(), frameTop.atMin())
190                .target(getChildAt(0), ChildDrawable.BOTTOM_ABSOLUTE);
191    }
192
193}
194