NotificationContentView.java revision 0d9f35d1d86b1022d3ebd5fc988750bf062828c0
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;
18
19import android.content.Context;
20import android.graphics.Paint;
21import android.graphics.PorterDuff;
22import android.graphics.PorterDuffXfermode;
23import android.graphics.Rect;
24import android.util.AttributeSet;
25import android.view.View;
26import android.view.animation.Interpolator;
27import android.view.animation.LinearInterpolator;
28import android.widget.FrameLayout;
29
30import com.android.systemui.R;
31
32/**
33 * A frame layout containing the actual payload of the notification, including the contracted and
34 * expanded layout. This class is responsible for clipping the content and and switching between the
35 * expanded and contracted view depending on its clipped size.
36 */
37public class NotificationContentView extends FrameLayout {
38
39    private static final long ANIMATION_DURATION_LENGTH = 170;
40
41    private final Rect mClipBounds = new Rect();
42
43    private View mContractedChild;
44    private View mExpandedChild;
45
46    private int mSmallHeight;
47    private int mClipTopAmount;
48    private int mActualHeight;
49
50    private final Interpolator mLinearInterpolator = new LinearInterpolator();
51
52    private boolean mContractedVisible = true;
53
54    private final Paint mFadePaint = new Paint();
55
56    public NotificationContentView(Context context, AttributeSet attrs) {
57        super(context, attrs);
58        mFadePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD));
59        reset();
60    }
61
62    @Override
63    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
64        super.onLayout(changed, left, top, right, bottom);
65        updateClipping();
66    }
67
68    public void reset() {
69        if (mContractedChild != null) {
70            mContractedChild.animate().cancel();
71        }
72        if (mExpandedChild != null) {
73            mExpandedChild.animate().cancel();
74        }
75        removeAllViews();
76        mContractedChild = null;
77        mExpandedChild = null;
78        mSmallHeight = getResources().getDimensionPixelSize(R.dimen.notification_min_height);
79        mActualHeight = mSmallHeight;
80        mContractedVisible = true;
81    }
82
83    public void setContractedChild(View child) {
84        if (mContractedChild != null) {
85            mContractedChild.animate().cancel();
86            removeView(mContractedChild);
87        }
88        sanitizeContractedLayoutParams(child);
89        addView(child);
90        mContractedChild = child;
91        selectLayout(false /* animate */, true /* force */);
92    }
93
94    public void setExpandedChild(View child) {
95        if (mExpandedChild != null) {
96            mExpandedChild.animate().cancel();
97            removeView(mExpandedChild);
98        }
99        addView(child);
100        mExpandedChild = child;
101        selectLayout(false /* animate */, true /* force */);
102    }
103
104    public void setActualHeight(int actualHeight) {
105        mActualHeight = actualHeight;
106        selectLayout(true /* animate */, false /* force */);
107        updateClipping();
108    }
109
110    public int getMaxHeight() {
111
112        // The maximum height is just the laid out height.
113        return getHeight();
114    }
115
116    public int getMinHeight() {
117        return mSmallHeight;
118    }
119
120    public void setClipTopAmount(int clipTopAmount) {
121        mClipTopAmount = clipTopAmount;
122        updateClipping();
123    }
124
125    private void updateClipping() {
126        mClipBounds.set(0, mClipTopAmount, getWidth(), mActualHeight);
127        setClipBounds(mClipBounds);
128    }
129
130    private void sanitizeContractedLayoutParams(View contractedChild) {
131        LayoutParams lp = (LayoutParams) contractedChild.getLayoutParams();
132        lp.height = mSmallHeight;
133        contractedChild.setLayoutParams(lp);
134    }
135
136    private void selectLayout(boolean animate, boolean force) {
137        if (mContractedChild == null) {
138            return;
139        }
140        boolean showContractedChild = showContractedChild();
141        if (showContractedChild != mContractedVisible || force) {
142            if (animate && mExpandedChild != null) {
143                runSwitchAnimation(showContractedChild);
144            } else if (mExpandedChild != null) {
145                mContractedChild.setVisibility(showContractedChild ? View.VISIBLE : View.INVISIBLE);
146                mContractedChild.setAlpha(showContractedChild ? 1f : 0f);
147                mExpandedChild.setVisibility(showContractedChild ? View.INVISIBLE : View.VISIBLE);
148                mExpandedChild.setAlpha(showContractedChild ? 0f : 1f);
149            }
150        }
151        mContractedVisible = showContractedChild;
152    }
153
154    private void runSwitchAnimation(final boolean showContractedChild) {
155        mContractedChild.setVisibility(View.VISIBLE);
156        mExpandedChild.setVisibility(View.VISIBLE);
157        mContractedChild.setLayerType(LAYER_TYPE_HARDWARE, mFadePaint);
158        mExpandedChild.setLayerType(LAYER_TYPE_HARDWARE, mFadePaint);
159        setLayerType(LAYER_TYPE_HARDWARE, null);
160        mContractedChild.animate()
161                .alpha(showContractedChild ? 1f : 0f)
162                .setDuration(ANIMATION_DURATION_LENGTH)
163                .setInterpolator(mLinearInterpolator);
164        mExpandedChild.animate()
165                .alpha(showContractedChild ? 0f : 1f)
166                .setDuration(ANIMATION_DURATION_LENGTH)
167                .setInterpolator(mLinearInterpolator)
168                .withEndAction(new Runnable() {
169                    @Override
170                    public void run() {
171                        mContractedChild.setLayerType(LAYER_TYPE_NONE, null);
172                        mExpandedChild.setLayerType(LAYER_TYPE_NONE, null);
173                        setLayerType(LAYER_TYPE_NONE, null);
174                        mContractedChild.setVisibility(showContractedChild
175                                ? View.VISIBLE
176                                : View.INVISIBLE);
177                        mExpandedChild.setVisibility(showContractedChild
178                                ? View.INVISIBLE
179                                : View.VISIBLE);
180                    }
181                });
182    }
183
184    private boolean showContractedChild() {
185        return mActualHeight <= mSmallHeight || mExpandedChild == null;
186    }
187
188    public void notifyContentUpdated() {
189        selectLayout(false /* animate */, true /* force */);
190    }
191
192    public boolean isContentExpandable() {
193        return mExpandedChild != null;
194    }
195}
196