• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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