NotificationContentView.java revision 1bf47b91179a3ebb43dbf3fa6c21a365f595882d
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.ColorFilter; 21import android.graphics.ColorMatrix; 22import android.graphics.ColorMatrixColorFilter; 23import android.graphics.Paint; 24import android.graphics.PorterDuff; 25import android.graphics.PorterDuffXfermode; 26import android.graphics.Rect; 27import android.graphics.drawable.Drawable; 28import android.util.AttributeSet; 29import android.view.View; 30import android.view.animation.Interpolator; 31import android.view.animation.LinearInterpolator; 32import android.widget.FrameLayout; 33import android.widget.ImageView; 34 35import com.android.systemui.R; 36 37/** 38 * A frame layout containing the actual payload of the notification, including the contracted and 39 * expanded layout. This class is responsible for clipping the content and and switching between the 40 * expanded and contracted view depending on its clipped size. 41 */ 42public class NotificationContentView extends FrameLayout { 43 44 private static final long ANIMATION_DURATION_LENGTH = 170; 45 private static final Paint INVERT_PAINT = createInvertPaint(); 46 private static final ColorFilter NO_COLOR_FILTER = new ColorFilter(); 47 48 private final Rect mClipBounds = new Rect(); 49 50 private View mContractedChild; 51 private View mExpandedChild; 52 53 private int mSmallHeight; 54 private int mClipTopAmount; 55 private int mActualHeight; 56 57 private final Interpolator mLinearInterpolator = new LinearInterpolator(); 58 59 private boolean mContractedVisible = true; 60 private boolean mDark; 61 62 private final Paint mFadePaint = new Paint(); 63 64 public NotificationContentView(Context context, AttributeSet attrs) { 65 super(context, attrs); 66 mFadePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD)); 67 reset(); 68 } 69 70 @Override 71 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 72 super.onLayout(changed, left, top, right, bottom); 73 updateClipping(); 74 } 75 76 public void reset() { 77 if (mContractedChild != null) { 78 mContractedChild.animate().cancel(); 79 } 80 if (mExpandedChild != null) { 81 mExpandedChild.animate().cancel(); 82 } 83 removeAllViews(); 84 mContractedChild = null; 85 mExpandedChild = null; 86 mSmallHeight = getResources().getDimensionPixelSize(R.dimen.notification_min_height); 87 mActualHeight = mSmallHeight; 88 mContractedVisible = true; 89 } 90 91 public void setContractedChild(View child) { 92 if (mContractedChild != null) { 93 mContractedChild.animate().cancel(); 94 removeView(mContractedChild); 95 } 96 sanitizeContractedLayoutParams(child); 97 addView(child); 98 mContractedChild = child; 99 selectLayout(false /* animate */, true /* force */); 100 } 101 102 public void setExpandedChild(View child) { 103 if (mExpandedChild != null) { 104 mExpandedChild.animate().cancel(); 105 removeView(mExpandedChild); 106 } 107 addView(child); 108 mExpandedChild = child; 109 selectLayout(false /* animate */, true /* force */); 110 } 111 112 public void setActualHeight(int actualHeight) { 113 mActualHeight = actualHeight; 114 selectLayout(true /* animate */, false /* force */); 115 updateClipping(); 116 } 117 118 public int getMaxHeight() { 119 120 // The maximum height is just the laid out height. 121 return getHeight(); 122 } 123 124 public int getMinHeight() { 125 return mSmallHeight; 126 } 127 128 public void setClipTopAmount(int clipTopAmount) { 129 mClipTopAmount = clipTopAmount; 130 updateClipping(); 131 } 132 133 private void updateClipping() { 134 mClipBounds.set(0, mClipTopAmount, getWidth(), mActualHeight); 135 setClipBounds(mClipBounds); 136 } 137 138 private void sanitizeContractedLayoutParams(View contractedChild) { 139 LayoutParams lp = (LayoutParams) contractedChild.getLayoutParams(); 140 lp.height = mSmallHeight; 141 contractedChild.setLayoutParams(lp); 142 } 143 144 private void selectLayout(boolean animate, boolean force) { 145 if (mContractedChild == null) { 146 return; 147 } 148 boolean showContractedChild = showContractedChild(); 149 if (showContractedChild != mContractedVisible || force) { 150 if (animate && mExpandedChild != null) { 151 runSwitchAnimation(showContractedChild); 152 } else if (mExpandedChild != null) { 153 mContractedChild.setVisibility(showContractedChild ? View.VISIBLE : View.INVISIBLE); 154 mContractedChild.setAlpha(showContractedChild ? 1f : 0f); 155 mExpandedChild.setVisibility(showContractedChild ? View.INVISIBLE : View.VISIBLE); 156 mExpandedChild.setAlpha(showContractedChild ? 0f : 1f); 157 } 158 } 159 mContractedVisible = showContractedChild; 160 } 161 162 private void runSwitchAnimation(final boolean showContractedChild) { 163 mContractedChild.setVisibility(View.VISIBLE); 164 mExpandedChild.setVisibility(View.VISIBLE); 165 mContractedChild.setLayerType(LAYER_TYPE_HARDWARE, mFadePaint); 166 mExpandedChild.setLayerType(LAYER_TYPE_HARDWARE, mFadePaint); 167 setLayerType(LAYER_TYPE_HARDWARE, null); 168 mContractedChild.animate() 169 .alpha(showContractedChild ? 1f : 0f) 170 .setDuration(ANIMATION_DURATION_LENGTH) 171 .setInterpolator(mLinearInterpolator); 172 mExpandedChild.animate() 173 .alpha(showContractedChild ? 0f : 1f) 174 .setDuration(ANIMATION_DURATION_LENGTH) 175 .setInterpolator(mLinearInterpolator) 176 .withEndAction(new Runnable() { 177 @Override 178 public void run() { 179 mContractedChild.setLayerType(LAYER_TYPE_NONE, null); 180 mExpandedChild.setLayerType(LAYER_TYPE_NONE, null); 181 setLayerType(LAYER_TYPE_NONE, null); 182 mContractedChild.setVisibility(showContractedChild 183 ? View.VISIBLE 184 : View.INVISIBLE); 185 mExpandedChild.setVisibility(showContractedChild 186 ? View.INVISIBLE 187 : View.VISIBLE); 188 } 189 }); 190 } 191 192 private boolean showContractedChild() { 193 return mActualHeight <= mSmallHeight || mExpandedChild == null; 194 } 195 196 public void notifyContentUpdated() { 197 selectLayout(false /* animate */, true /* force */); 198 } 199 200 public boolean isContentExpandable() { 201 return mExpandedChild != null; 202 } 203 204 public void setDark(boolean dark, boolean fade) { 205 if (mDark == dark) return; 206 mDark = dark; 207 setImageViewDark(dark, fade, com.android.internal.R.id.right_icon); 208 setImageViewDark(dark, fade, com.android.internal.R.id.icon); 209 } 210 211 private void setImageViewDark(boolean dark, boolean fade, int imageViewId) { 212 // TODO: implement fade 213 final ImageView v = (ImageView) mContractedChild.findViewById(imageViewId); 214 if (v == null) return; 215 final Drawable d = v.getBackground(); 216 if (dark) { 217 v.setLayerType(LAYER_TYPE_HARDWARE, INVERT_PAINT); 218 if (d != null) { 219 v.setTag(R.id.doze_saved_filter_tag, d.getColorFilter() != null ? d.getColorFilter() 220 : NO_COLOR_FILTER); 221 d.setColorFilter(getResources().getColor(R.color.doze_small_icon_background_color), 222 PorterDuff.Mode.SRC_ATOP); 223 v.setImageAlpha(getResources().getInteger(R.integer.doze_small_icon_alpha)); 224 } 225 } else { 226 v.setLayerType(LAYER_TYPE_NONE, null); 227 if (d != null) { 228 final ColorFilter filter = (ColorFilter) v.getTag(R.id.doze_saved_filter_tag); 229 if (filter != null) { 230 d.setColorFilter(filter == NO_COLOR_FILTER ? null : filter); 231 v.setTag(R.id.doze_saved_filter_tag, null); 232 } 233 v.setImageAlpha(0xff); 234 } 235 } 236 } 237 238 private static Paint createInvertPaint() { 239 final Paint p = new Paint(); 240 final float[] invert = { 241 -1f, 0f, 0f, 1f, 1f, 242 0f, -1f, 0f, 1f, 1f, 243 0f, 0f, -1f, 1f, 1f, 244 0f, 0f, 0f, 1f, 0f 245 }; 246 p.setColorFilter(new ColorMatrixColorFilter(new ColorMatrix(invert))); 247 return p; 248 } 249} 250