NotificationContentView.java revision a7fe63190cc3f95af1830886edd80acef0c334e3
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        removeAllViews();
70        mContractedChild = null;
71        mExpandedChild = null;
72        mSmallHeight = getResources().getDimensionPixelSize(R.dimen.notification_min_height);
73        mActualHeight = mSmallHeight;
74        mContractedVisible = true;
75    }
76
77    public void setContractedChild(View child) {
78        if (mContractedChild != null) {
79            removeView(mContractedChild);
80        }
81        sanitizeContractedLayoutParams(child);
82        addView(child);
83        mContractedChild = child;
84        selectLayout(false /* animate */, true /* force */);
85    }
86
87    public void setExpandedChild(View child) {
88        if (mExpandedChild != null) {
89            removeView(mExpandedChild);
90        }
91        addView(child);
92        mExpandedChild = child;
93        selectLayout(false /* animate */, true /* force */);
94    }
95
96    public void setActualHeight(int actualHeight) {
97        mActualHeight = actualHeight;
98        selectLayout(true /* animate */, false /* force */);
99        updateClipping();
100    }
101
102    public int getMaxHeight() {
103
104        // The maximum height is just the laid out height.
105        return getHeight();
106    }
107
108    public int getMinHeight() {
109        return mSmallHeight;
110    }
111
112    public void setClipTopAmount(int clipTopAmount) {
113        mClipTopAmount = clipTopAmount;
114        updateClipping();
115    }
116
117    private void updateClipping() {
118        mClipBounds.set(0, mClipTopAmount, getWidth(), mActualHeight);
119        setClipBounds(mClipBounds);
120    }
121
122    private void sanitizeContractedLayoutParams(View contractedChild) {
123        LayoutParams lp = (LayoutParams) contractedChild.getLayoutParams();
124        lp.height = mSmallHeight;
125        contractedChild.setLayoutParams(lp);
126    }
127
128    private void selectLayout(boolean animate, boolean force) {
129        if (mContractedChild == null) {
130            return;
131        }
132        boolean showContractedChild = showContractedChild();
133        if (showContractedChild != mContractedVisible || force) {
134            if (animate && mExpandedChild != null) {
135                runSwitchAnimation(showContractedChild);
136            } else if (mExpandedChild != null) {
137                mContractedChild.setVisibility(showContractedChild ? View.VISIBLE : View.INVISIBLE);
138                mContractedChild.setAlpha(showContractedChild ? 1f : 0f);
139                mExpandedChild.setVisibility(showContractedChild ? View.INVISIBLE : View.VISIBLE);
140                mExpandedChild.setAlpha(showContractedChild ? 0f : 1f);
141            }
142        }
143        mContractedVisible = showContractedChild;
144    }
145
146    private void runSwitchAnimation(final boolean showContractedChild) {
147        mContractedChild.setVisibility(View.VISIBLE);
148        mExpandedChild.setVisibility(View.VISIBLE);
149        mContractedChild.setLayerType(LAYER_TYPE_HARDWARE, mFadePaint);
150        mExpandedChild.setLayerType(LAYER_TYPE_HARDWARE, mFadePaint);
151        setLayerType(LAYER_TYPE_HARDWARE, null);
152        mContractedChild.animate()
153                .alpha(showContractedChild ? 1f : 0f)
154                .setDuration(ANIMATION_DURATION_LENGTH)
155                .setInterpolator(mLinearInterpolator);
156        mExpandedChild.animate()
157                .alpha(showContractedChild ? 0f : 1f)
158                .setDuration(ANIMATION_DURATION_LENGTH)
159                .setInterpolator(mLinearInterpolator)
160                .withEndAction(new Runnable() {
161                    @Override
162                    public void run() {
163                        mContractedChild.setLayerType(LAYER_TYPE_NONE, null);
164                        mExpandedChild.setLayerType(LAYER_TYPE_NONE, null);
165                        setLayerType(LAYER_TYPE_NONE, null);
166                        mContractedChild.setVisibility(showContractedChild
167                                ? View.VISIBLE
168                                : View.INVISIBLE);
169                        mExpandedChild.setVisibility(showContractedChild
170                                ? View.INVISIBLE
171                                : View.VISIBLE);
172                    }
173                });
174    }
175
176    private boolean showContractedChild() {
177        return mActualHeight <= mSmallHeight || mExpandedChild == null;
178    }
179
180    public void notifyContentUpdated() {
181        selectLayout(false /* animate */, true /* force */);
182    }
183
184    public boolean isContentExpandable() {
185        return mExpandedChild != null;
186    }
187}
188