1/*
2 * Copyright (C) 2014 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 com.android.systemui.statusbar.notification;
18
19import android.animation.ValueAnimator;
20import android.content.Context;
21import android.graphics.Color;
22import android.service.notification.StatusBarNotification;
23import android.view.View;
24import android.widget.ImageView;
25import android.widget.ProgressBar;
26import android.widget.TextView;
27
28import com.android.systemui.statusbar.CrossFadeHelper;
29import com.android.systemui.statusbar.ExpandableNotificationRow;
30import com.android.systemui.statusbar.TransformableView;
31import com.android.systemui.statusbar.ViewTransformationHelper;
32
33/**
34 * Wraps a notification view inflated from a template.
35 */
36public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapper {
37
38    private static final int mDarkProgressTint = 0xffffffff;
39
40    protected ImageView mPicture;
41    private ProgressBar mProgressBar;
42    private TextView mTitle;
43    private TextView mText;
44    private View mActionsContainer;
45
46    private int mContentHeight;
47    private int mMinHeightHint;
48
49    protected NotificationTemplateViewWrapper(Context ctx, View view, ExpandableNotificationRow row) {
50        super(ctx, view, row);
51        mTransformationHelper.setCustomTransformation(
52                new ViewTransformationHelper.CustomTransformation() {
53                    @Override
54                    public boolean transformTo(TransformState ownState,
55                            TransformableView notification, final float transformationAmount) {
56                        if (!(notification instanceof HybridNotificationView)) {
57                            return false;
58                        }
59                        TransformState otherState = notification.getCurrentState(
60                                TRANSFORMING_VIEW_TITLE);
61                        final View text = ownState.getTransformedView();
62                        CrossFadeHelper.fadeOut(text, transformationAmount);
63                        if (otherState != null) {
64                            ownState.transformViewVerticalTo(otherState, this,
65                                    transformationAmount);
66                            otherState.recycle();
67                        }
68                        return true;
69                    }
70
71                    @Override
72                    public boolean customTransformTarget(TransformState ownState,
73                            TransformState otherState) {
74                        float endY = getTransformationY(ownState, otherState);
75                        ownState.setTransformationEndY(endY);
76                        return true;
77                    }
78
79                    @Override
80                    public boolean transformFrom(TransformState ownState,
81                            TransformableView notification, float transformationAmount) {
82                        if (!(notification instanceof HybridNotificationView)) {
83                            return false;
84                        }
85                        TransformState otherState = notification.getCurrentState(
86                                TRANSFORMING_VIEW_TITLE);
87                        final View text = ownState.getTransformedView();
88                        CrossFadeHelper.fadeIn(text, transformationAmount);
89                        if (otherState != null) {
90                            ownState.transformViewVerticalFrom(otherState, this,
91                                    transformationAmount);
92                            otherState.recycle();
93                        }
94                        return true;
95                    }
96
97                    @Override
98                    public boolean initTransformation(TransformState ownState,
99                            TransformState otherState) {
100                        float startY = getTransformationY(ownState, otherState);
101                        ownState.setTransformationStartY(startY);
102                        return true;
103                    }
104
105                    private float getTransformationY(TransformState ownState,
106                            TransformState otherState) {
107                        int[] otherStablePosition = otherState.getLaidOutLocationOnScreen();
108                        int[] ownStablePosition = ownState.getLaidOutLocationOnScreen();
109                        return (otherStablePosition[1]
110                                + otherState.getTransformedView().getHeight()
111                                - ownStablePosition[1]) * 0.33f;
112                    }
113
114                }, TRANSFORMING_VIEW_TEXT);
115    }
116
117    private void resolveTemplateViews(StatusBarNotification notification) {
118        mPicture = (ImageView) mView.findViewById(com.android.internal.R.id.right_icon);
119        mPicture.setTag(ImageTransformState.ICON_TAG,
120                notification.getNotification().getLargeIcon());
121        mTitle = (TextView) mView.findViewById(com.android.internal.R.id.title);
122        mText = (TextView) mView.findViewById(com.android.internal.R.id.text);
123        final View progress = mView.findViewById(com.android.internal.R.id.progress);
124        if (progress instanceof ProgressBar) {
125            mProgressBar = (ProgressBar) progress;
126        } else {
127            // It's still a viewstub
128            mProgressBar = null;
129        }
130        mActionsContainer = mView.findViewById(com.android.internal.R.id.actions_container);
131    }
132
133    @Override
134    public void notifyContentUpdated(StatusBarNotification notification) {
135        // Reinspect the notification. Before the super call, because the super call also updates
136        // the transformation types and we need to have our values set by then.
137        resolveTemplateViews(notification);
138        super.notifyContentUpdated(notification);
139    }
140
141    @Override
142    protected void updateInvertHelper() {
143        super.updateInvertHelper();
144        View mainColumn = mView.findViewById(com.android.internal.R.id.notification_main_column);
145        if (mainColumn != null) {
146            mInvertHelper.addTarget(mainColumn);
147        }
148    }
149
150    @Override
151    protected void updateTransformedTypes() {
152        // This also clears the existing types
153        super.updateTransformedTypes();
154        if (mTitle != null) {
155            mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TITLE, mTitle);
156        }
157        if (mText != null) {
158            mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TEXT, mText);
159        }
160        if (mPicture != null) {
161            mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_IMAGE, mPicture);
162        }
163        if (mProgressBar != null) {
164            mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_PROGRESS, mProgressBar);
165        }
166    }
167
168    @Override
169    public void setDark(boolean dark, boolean fade, long delay) {
170        if (dark == mDark && mDarkInitialized) {
171            return;
172        }
173        super.setDark(dark, fade, delay);
174        setPictureGrayscale(dark, fade, delay);
175        setProgressBarDark(dark, fade, delay);
176    }
177
178    private void setProgressBarDark(boolean dark, boolean fade, long delay) {
179        if (mProgressBar != null) {
180            if (fade) {
181                fadeProgressDark(mProgressBar, dark, delay);
182            } else {
183                updateProgressDark(mProgressBar, dark);
184            }
185        }
186    }
187
188    private void fadeProgressDark(final ProgressBar target, final boolean dark, long delay) {
189        startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
190            @Override
191            public void onAnimationUpdate(ValueAnimator animation) {
192                float t = (float) animation.getAnimatedValue();
193                updateProgressDark(target, t);
194            }
195        }, dark, delay, null /* listener */);
196    }
197
198    private void updateProgressDark(ProgressBar target, float intensity) {
199        int color = interpolateColor(mColor, mDarkProgressTint, intensity);
200        target.getIndeterminateDrawable().mutate().setTint(color);
201        target.getProgressDrawable().mutate().setTint(color);
202    }
203
204    private void updateProgressDark(ProgressBar target, boolean dark) {
205        updateProgressDark(target, dark ? 1f : 0f);
206    }
207
208    protected void setPictureGrayscale(boolean grayscale, boolean fade, long delay) {
209        if (mPicture != null) {
210            if (fade) {
211                fadeGrayscale(mPicture, grayscale, delay);
212            } else {
213                updateGrayscale(mPicture, grayscale);
214            }
215        }
216    }
217
218    private static int interpolateColor(int source, int target, float t) {
219        int aSource = Color.alpha(source);
220        int rSource = Color.red(source);
221        int gSource = Color.green(source);
222        int bSource = Color.blue(source);
223        int aTarget = Color.alpha(target);
224        int rTarget = Color.red(target);
225        int gTarget = Color.green(target);
226        int bTarget = Color.blue(target);
227        return Color.argb(
228                (int) (aSource * (1f - t) + aTarget * t),
229                (int) (rSource * (1f - t) + rTarget * t),
230                (int) (gSource * (1f - t) + gTarget * t),
231                (int) (bSource * (1f - t) + bTarget * t));
232    }
233
234    @Override
235    public void setContentHeight(int contentHeight, int minHeightHint) {
236        super.setContentHeight(contentHeight, minHeightHint);
237
238        mContentHeight = contentHeight;
239        mMinHeightHint = minHeightHint;
240        updateActionOffset();
241    }
242
243    private void updateActionOffset() {
244        if (mActionsContainer != null) {
245            // We should never push the actions higher than they are in the headsup view.
246            int constrainedContentHeight = Math.max(mContentHeight, mMinHeightHint);
247            mActionsContainer.setTranslationY(constrainedContentHeight - mView.getHeight());
248        }
249    }
250}
251