1 /* 2 * Copyright (C) 2011 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 17 package com.android.systemui.recent; 18 19 import android.animation.LayoutTransition; 20 import android.content.Context; 21 import android.content.res.TypedArray; 22 import android.graphics.Canvas; 23 import android.graphics.LinearGradient; 24 import android.graphics.Matrix; 25 import android.graphics.Paint; 26 import android.graphics.Shader; 27 import android.graphics.drawable.Drawable; 28 import android.util.AttributeSet; 29 import android.view.View; 30 import android.view.ViewConfiguration; 31 import android.view.ViewGroup; 32 import android.widget.LinearLayout; 33 34 import com.android.systemui.R; 35 36 public class RecentsScrollViewPerformanceHelper { 37 public static final boolean OPTIMIZE_SW_RENDERED_RECENTS = true; 38 public static final boolean USE_DARK_FADE_IN_HW_ACCELERATED_MODE = true; 39 private View mScrollView; 40 private LinearLayout mLinearLayout; 41 private RecentsCallback mCallback; 42 43 private boolean mShowBackground = false; 44 private int mFadingEdgeLength; 45 private Drawable.ConstantState mBackgroundDrawable; 46 private Context mContext; 47 private boolean mIsVertical; 48 private boolean mFirstTime = true; 49 private boolean mSoftwareRendered = false; 50 private boolean mAttachedToWindow = false; 51 create(Context context, AttributeSet attrs, View scrollView, boolean isVertical)52 public static RecentsScrollViewPerformanceHelper create(Context context, 53 AttributeSet attrs, View scrollView, boolean isVertical) { 54 boolean isTablet = context.getResources(). 55 getBoolean(R.bool.config_recents_interface_for_tablets); 56 if (!isTablet && (OPTIMIZE_SW_RENDERED_RECENTS || USE_DARK_FADE_IN_HW_ACCELERATED_MODE)) { 57 return new RecentsScrollViewPerformanceHelper(context, attrs, scrollView, isVertical); 58 } else { 59 return null; 60 } 61 } 62 RecentsScrollViewPerformanceHelper(Context context, AttributeSet attrs, View scrollView, boolean isVertical)63 public RecentsScrollViewPerformanceHelper(Context context, 64 AttributeSet attrs, View scrollView, boolean isVertical) { 65 mScrollView = scrollView; 66 mContext = context; 67 TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.View); 68 mFadingEdgeLength = a.getDimensionPixelSize(android.R.styleable.View_fadingEdgeLength, 69 ViewConfiguration.get(context).getScaledFadingEdgeLength()); 70 mIsVertical = isVertical; 71 } 72 onAttachedToWindowCallback( RecentsCallback callback, LinearLayout layout, boolean hardwareAccelerated)73 public void onAttachedToWindowCallback( 74 RecentsCallback callback, LinearLayout layout, boolean hardwareAccelerated) { 75 mSoftwareRendered = !hardwareAccelerated; 76 if ((mSoftwareRendered && OPTIMIZE_SW_RENDERED_RECENTS) 77 || USE_DARK_FADE_IN_HW_ACCELERATED_MODE) { 78 mScrollView.setVerticalFadingEdgeEnabled(false); 79 mScrollView.setHorizontalFadingEdgeEnabled(false); 80 } 81 if (mSoftwareRendered && OPTIMIZE_SW_RENDERED_RECENTS) { 82 mCallback = callback; 83 mLinearLayout = layout; 84 mAttachedToWindow = true; 85 mBackgroundDrawable = mContext.getResources() 86 .getDrawable(R.drawable.status_bar_recents_background_solid).getConstantState(); 87 updateShowBackground(); 88 } 89 90 } 91 addViewCallback(View newLinearLayoutChild)92 public void addViewCallback(View newLinearLayoutChild) { 93 if (mSoftwareRendered && OPTIMIZE_SW_RENDERED_RECENTS) { 94 final View view = newLinearLayoutChild; 95 if (mShowBackground) { 96 view.setBackgroundDrawable(mBackgroundDrawable.newDrawable()); 97 view.setDrawingCacheEnabled(true); 98 view.buildDrawingCache(); 99 } else { 100 view.setBackgroundDrawable(null); 101 view.setDrawingCacheEnabled(false); 102 view.destroyDrawingCache(); 103 } 104 } 105 } 106 onLayoutCallback()107 public void onLayoutCallback() { 108 if (mSoftwareRendered && OPTIMIZE_SW_RENDERED_RECENTS) { 109 mScrollView.post(new Runnable() { 110 public void run() { 111 updateShowBackground(); 112 } 113 }); 114 } 115 } 116 drawCallback(Canvas canvas, int left, int right, int top, int bottom, int scrollX, int scrollY, float topFadingEdgeStrength, float bottomFadingEdgeStrength, float leftFadingEdgeStrength, float rightFadingEdgeStrength)117 public void drawCallback(Canvas canvas, 118 int left, int right, int top, int bottom, int scrollX, int scrollY, 119 float topFadingEdgeStrength, float bottomFadingEdgeStrength, 120 float leftFadingEdgeStrength, float rightFadingEdgeStrength) { 121 if (mSoftwareRendered && OPTIMIZE_SW_RENDERED_RECENTS) { 122 if (mIsVertical) { 123 if (scrollY < 0) { 124 Drawable d = mBackgroundDrawable.newDrawable().getCurrent(); 125 d.setBounds(0, scrollY, mScrollView.getWidth(), 0); 126 d.draw(canvas); 127 } else { 128 final int childHeight = mLinearLayout.getHeight(); 129 if (scrollY + mScrollView.getHeight() > childHeight) { 130 Drawable d = mBackgroundDrawable.newDrawable().getCurrent(); 131 d.setBounds(0, childHeight, mScrollView.getWidth(), 132 scrollY + mScrollView.getHeight()); 133 d.draw(canvas); 134 } 135 } 136 } else { 137 if (scrollX < 0) { 138 Drawable d = mBackgroundDrawable.newDrawable().getCurrent(); 139 d.setBounds(scrollX, 0, 0, mScrollView.getHeight()); 140 d.draw(canvas); 141 } else { 142 final int childWidth = mLinearLayout.getWidth(); 143 if (scrollX + mScrollView.getWidth() > childWidth) { 144 Drawable d = mBackgroundDrawable.newDrawable().getCurrent(); 145 d.setBounds(childWidth, 0, 146 scrollX + mScrollView.getWidth(), mScrollView.getHeight()); 147 d.draw(canvas); 148 } 149 } 150 } 151 } 152 153 if ((mSoftwareRendered && OPTIMIZE_SW_RENDERED_RECENTS) 154 || USE_DARK_FADE_IN_HW_ACCELERATED_MODE) { 155 Paint p = new Paint(); 156 Matrix matrix = new Matrix(); 157 // use use a height of 1, and then wack the matrix each time we 158 // actually use it. 159 Shader fade = new LinearGradient(0, 0, 0, 1, 0xCC000000, 0, Shader.TileMode.CLAMP); 160 // PULL OUT THIS CONSTANT 161 162 p.setShader(fade); 163 164 // draw the fade effect 165 boolean drawTop = false; 166 boolean drawBottom = false; 167 boolean drawLeft = false; 168 boolean drawRight = false; 169 170 float topFadeStrength = 0.0f; 171 float bottomFadeStrength = 0.0f; 172 float leftFadeStrength = 0.0f; 173 float rightFadeStrength = 0.0f; 174 175 final float fadeHeight = mFadingEdgeLength; 176 int length = (int) fadeHeight; 177 178 // clip the fade length if top and bottom fades overlap 179 // overlapping fades produce odd-looking artifacts 180 if (mIsVertical && (top + length > bottom - length)) { 181 length = (bottom - top) / 2; 182 } 183 184 // also clip horizontal fades if necessary 185 if (!mIsVertical && (left + length > right - length)) { 186 length = (right - left) / 2; 187 } 188 189 if (mIsVertical) { 190 topFadeStrength = Math.max(0.0f, Math.min(1.0f, topFadingEdgeStrength)); 191 drawTop = topFadeStrength * fadeHeight > 1.0f; 192 bottomFadeStrength = Math.max(0.0f, Math.min(1.0f, bottomFadingEdgeStrength)); 193 drawBottom = bottomFadeStrength * fadeHeight > 1.0f; 194 } 195 196 if (!mIsVertical) { 197 leftFadeStrength = Math.max(0.0f, Math.min(1.0f, leftFadingEdgeStrength)); 198 drawLeft = leftFadeStrength * fadeHeight > 1.0f; 199 rightFadeStrength = Math.max(0.0f, Math.min(1.0f, rightFadingEdgeStrength)); 200 drawRight = rightFadeStrength * fadeHeight > 1.0f; 201 } 202 203 if (drawTop) { 204 matrix.setScale(1, fadeHeight * topFadeStrength); 205 matrix.postTranslate(left, top); 206 fade.setLocalMatrix(matrix); 207 canvas.drawRect(left, top, right, top + length, p); 208 } 209 210 if (drawBottom) { 211 matrix.setScale(1, fadeHeight * bottomFadeStrength); 212 matrix.postRotate(180); 213 matrix.postTranslate(left, bottom); 214 fade.setLocalMatrix(matrix); 215 canvas.drawRect(left, bottom - length, right, bottom, p); 216 } 217 218 if (drawLeft) { 219 matrix.setScale(1, fadeHeight * leftFadeStrength); 220 matrix.postRotate(-90); 221 matrix.postTranslate(left, top); 222 fade.setLocalMatrix(matrix); 223 canvas.drawRect(left, top, left + length, bottom, p); 224 } 225 226 if (drawRight) { 227 matrix.setScale(1, fadeHeight * rightFadeStrength); 228 matrix.postRotate(90); 229 matrix.postTranslate(right, top); 230 fade.setLocalMatrix(matrix); 231 canvas.drawRect(right - length, top, right, bottom, p); 232 } 233 } 234 } 235 getVerticalFadingEdgeLengthCallback()236 public int getVerticalFadingEdgeLengthCallback() { 237 return mFadingEdgeLength; 238 } 239 getHorizontalFadingEdgeLengthCallback()240 public int getHorizontalFadingEdgeLengthCallback() { 241 return mFadingEdgeLength; 242 } 243 setLayoutTransitionCallback(LayoutTransition transition)244 public void setLayoutTransitionCallback(LayoutTransition transition) { 245 if (mSoftwareRendered && OPTIMIZE_SW_RENDERED_RECENTS) { 246 if (transition != null) { 247 transition.addTransitionListener(new LayoutTransition.TransitionListener() { 248 @Override 249 public void startTransition(LayoutTransition transition, 250 ViewGroup container, View view, int transitionType) { 251 updateShowBackground(); 252 } 253 254 @Override 255 public void endTransition(LayoutTransition transition, 256 ViewGroup container, View view, int transitionType) { 257 updateShowBackground(); 258 } 259 }); 260 } 261 } 262 } 263 264 // Turn on/off drawing the background in our ancestor, and turn on/off drawing 265 // in the items in LinearLayout contained by this scrollview. 266 // Moving the background drawing to our children, and turning on a drawing cache 267 // for each of them, gives us a ~20fps gain when Recents is rendered in software updateShowBackground()268 public void updateShowBackground() { 269 if (!mAttachedToWindow) { 270 // We haven't been initialized yet-- we'll get called again when we are 271 return; 272 } 273 if (mSoftwareRendered && OPTIMIZE_SW_RENDERED_RECENTS) { 274 LayoutTransition transition = mLinearLayout.getLayoutTransition(); 275 int linearLayoutSize = 276 mIsVertical ? mLinearLayout.getHeight() : mLinearLayout.getWidth(); 277 int scrollViewSize = 278 mIsVertical ? mScrollView.getHeight() : mScrollView.getWidth(); 279 boolean show = !mScrollView.isHardwareAccelerated() && 280 (linearLayoutSize > scrollViewSize) && 281 !(transition != null && transition.isRunning()) && 282 mCallback.isRecentsVisible(); 283 284 if (!mFirstTime && show == mShowBackground) return; 285 mShowBackground = show; 286 mFirstTime = false; 287 288 mCallback.handleShowBackground(!show); 289 for (int i = 0; i < mLinearLayout.getChildCount(); i++) { 290 View v = mLinearLayout.getChildAt(i); 291 if (show) { 292 v.setBackgroundDrawable(mBackgroundDrawable.newDrawable()); 293 v.setDrawingCacheEnabled(true); 294 v.buildDrawingCache(); 295 } else { 296 v.setDrawingCacheEnabled(false); 297 v.destroyDrawingCache(); 298 v.setBackgroundDrawable(null); 299 } 300 } 301 } 302 } 303 304 } 305