diff options
Diffstat (limited to 'core/java/android/view')
-rw-r--r-- | core/java/android/view/SurfaceView.java | 34 | ||||
-rw-r--r-- | core/java/android/view/View.java | 164 | ||||
-rw-r--r-- | core/java/android/view/ViewDebug.java | 156 | ||||
-rw-r--r-- | core/java/android/view/ViewGroup.java | 89 | ||||
-rw-r--r-- | core/java/android/view/ViewRoot.java | 105 | ||||
-rw-r--r-- | core/java/android/view/WindowManager.java | 26 |
6 files changed, 505 insertions, 69 deletions
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 71da9cf..49e4e4c 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -257,6 +257,23 @@ public class SurfaceView extends View { } @Override + public boolean dispatchTouchEvent(MotionEvent event) { + // SurfaceView uses pre-scaled size unless fixed size is requested. This hook + // scales the event back to the pre-scaled coordinates for such surface. + if (mRequestedWidth < 0 && mAppScale != 1.0f) { + MotionEvent scaledBack = MotionEvent.obtain(event); + scaledBack.scale(mAppScale); + try { + return super.dispatchTouchEvent(scaledBack); + } finally { + scaledBack.recycle(); + } + } else { + return super.dispatchTouchEvent(event); + } + } + + @Override protected void dispatchDraw(Canvas canvas) { // if SKIP_DRAW is cleared, draw() has already punched a hole if ((mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) { @@ -278,7 +295,13 @@ public class SurfaceView extends View { if (myWidth <= 0) myWidth = getWidth(); int myHeight = mRequestedHeight; if (myHeight <= 0) myHeight = getHeight(); - + + // Use original size for surface unless fixed size is requested. + if (mRequestedWidth <= 0) { + myWidth *= mAppScale; + myHeight *= mAppScale; + } + getLocationInWindow(mLocation); final boolean creating = mWindow == null; final boolean formatChanged = mFormat != mRequestedFormat; @@ -304,8 +327,9 @@ public class SurfaceView extends View { mFormat = mRequestedFormat; mType = mRequestedType; - mLayout.x = mLeft; - mLayout.y = mTop; + // Scaling window's layout here beause mLayout is not used elsewhere. + mLayout.x = (int) (mLeft * mAppScale); + mLayout.y = (int) (mTop * mAppScale); mLayout.width = (int) (getWidth() * mAppScale); mLayout.height = (int) (getHeight() * mAppScale); mLayout.format = mRequestedFormat; @@ -334,7 +358,7 @@ public class SurfaceView extends View { mSurfaceLock.lock(); mDrawingStopped = !visible; final int relayoutResult = mSession.relayout( - mWindow, mLayout, (int) (mWidth * mAppScale), (int) (mHeight * mAppScale), + mWindow, mLayout, mWidth, mHeight, visible ? VISIBLE : GONE, false, mWinFrame, mContentInsets, mVisibleInsets, mSurface); @@ -358,7 +382,7 @@ public class SurfaceView extends View { synchronized (mCallbacks) { callbacks = new SurfaceHolder.Callback[mCallbacks.size()]; mCallbacks.toArray(callbacks); - } + } if (visibleChanged) { mIsCreating = true; diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index d042f28..16b70ed 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -49,6 +49,7 @@ import android.util.Poolable; import android.util.Pool; import android.util.Pools; import android.util.PoolableManager; +import android.util.Config; import android.view.ContextMenu.ContextMenuInfo; import android.view.animation.Animation; import android.view.inputmethod.InputConnection; @@ -1405,6 +1406,28 @@ public class View implements Drawable.Callback, KeyEvent.Callback { static final int SCROLL_CONTAINER_ADDED = 0x00100000; /** + * View flag indicating whether this view was invalidated (fully or partially.) + * + * @hide + */ + static final int DIRTY = 0x00200000; + + /** + * View flag indicating whether this view was invalidated by an opaque + * invalidate request. + * + * @hide + */ + static final int DIRTY_OPAQUE = 0x00400000; + + /** + * Mask for {@link #DIRTY} and {@link #DIRTY_OPAQUE}. + * + * @hide + */ + static final int DIRTY_MASK = 0x00600000; + + /** * The parent this view is attached to. * {@hide} * @@ -1420,7 +1443,18 @@ public class View implements Drawable.Callback, KeyEvent.Callback { /** * {@hide} */ - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(flagMapping = { + @ViewDebug.FlagToString(mask = FORCE_LAYOUT, equals = FORCE_LAYOUT, + name = "FORCE_LAYOUT"), + @ViewDebug.FlagToString(mask = LAYOUT_REQUIRED, equals = LAYOUT_REQUIRED, + name = "LAYOUT_REQUIRED"), + @ViewDebug.FlagToString(mask = DRAWING_CACHE_VALID, equals = DRAWING_CACHE_VALID, + name = "DRAWING_CACHE_INVALID", outputIf = false), + @ViewDebug.FlagToString(mask = DRAWN, equals = DRAWN, name = "DRAWN", outputIf = true), + @ViewDebug.FlagToString(mask = DRAWN, equals = DRAWN, name = "NOT_DRAWN", outputIf = false), + @ViewDebug.FlagToString(mask = DIRTY_MASK, equals = DIRTY_OPAQUE, name = "DIRTY_OPAQUE"), + @ViewDebug.FlagToString(mask = DIRTY_MASK, equals = DIRTY, name = "DIRTY") + }) int mPrivateFlags; /** @@ -4522,6 +4556,24 @@ public class View implements Drawable.Callback, KeyEvent.Callback { } /** + * Indicates whether this View is opaque. An opaque View guarantees that it will + * draw all the pixels overlapping its bounds using a fully opaque color. + * + * Subclasses of View should override this method whenever possible to indicate + * whether an instance is opaque. Opaque Views are treated in a special way by + * the View hierarchy, possibly allowing it to perform optimizations during + * invalidate/draw passes. + * + * @return True if this View is guaranteed to be fully opaque, false otherwise. + * + * @hide Pending API council approval + */ + @ViewDebug.ExportedProperty + public boolean isOpaque() { + return mBGDrawable != null && mBGDrawable.getOpacity() == PixelFormat.OPAQUE; + } + + /** * @return A handler associated with the thread running the View. This * handler can be used to pump events in the UI events queue. */ @@ -5601,7 +5653,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { if (ViewDebug.TRACE_HIERARCHY) { ViewDebug.trace(this, ViewDebug.HierarchyTraceType.BUILD_CACHE); } - if (ViewRoot.PROFILE_DRAWING) { + if (Config.DEBUG && ViewDebug.profileDrawing) { EventLog.writeEvent(60002, hashCode()); } @@ -5694,6 +5746,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { if (ViewDebug.TRACE_HIERARCHY) { ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW); } + mPrivateFlags &= ~DIRTY_MASK; dispatchDraw(canvas); } else { draw(canvas); @@ -5740,7 +5793,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { canvas = new Canvas(bitmap); } - if ((backgroundColor&0xff000000) != 0) { + if ((backgroundColor & 0xff000000) != 0) { bitmap.eraseColor(backgroundColor); } @@ -5748,6 +5801,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback { final int restoreCount = canvas.save(); canvas.translate(-mScrollX, -mScrollY); + // Temporarily remove the dirty mask + int flags = mPrivateFlags; + mPrivateFlags &= ~DIRTY_MASK; + // Fast path for layouts with no backgrounds if ((mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) { dispatchDraw(canvas); @@ -5755,6 +5812,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback { draw(canvas); } + mPrivateFlags = flags; + canvas.restoreToCount(restoreCount); if (attachInfo != null) { @@ -5875,7 +5934,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback { ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW); } - mPrivateFlags |= DRAWN; + final int privateFlags = mPrivateFlags; + final boolean dirtyOpaque = (privateFlags & DIRTY_MASK) == DIRTY_OPAQUE && + (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState); + mPrivateFlags = (privateFlags & ~DIRTY_MASK) | DRAWN; /* * Draw traversal performs several drawing steps which must be executed @@ -5892,22 +5954,24 @@ public class View implements Drawable.Callback, KeyEvent.Callback { // Step 1, draw the background, if needed int saveCount; - final Drawable background = mBGDrawable; - if (background != null) { - final int scrollX = mScrollX; - final int scrollY = mScrollY; + if (!dirtyOpaque) { + final Drawable background = mBGDrawable; + if (background != null) { + final int scrollX = mScrollX; + final int scrollY = mScrollY; - if (mBackgroundSizeChanged) { - background.setBounds(0, 0, mRight - mLeft, mBottom - mTop); - mBackgroundSizeChanged = false; - } + if (mBackgroundSizeChanged) { + background.setBounds(0, 0, mRight - mLeft, mBottom - mTop); + mBackgroundSizeChanged = false; + } - if ((scrollX | scrollY) == 0) { - background.draw(canvas); - } else { - canvas.translate(scrollX, scrollY); - background.draw(canvas); - canvas.translate(-scrollX, -scrollY); + if ((scrollX | scrollY) == 0) { + background.draw(canvas); + } else { + canvas.translate(scrollX, scrollY); + background.draw(canvas); + canvas.translate(-scrollX, -scrollY); + } } } @@ -5917,7 +5981,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0; if (!verticalEdges && !horizontalEdges) { // Step 3, draw the content - onDraw(canvas); + if (!dirtyOpaque) onDraw(canvas); // Step 4, draw the children dispatchDraw(canvas); @@ -6020,7 +6084,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { } // Step 3, draw the content - onDraw(canvas); + if (!dirtyOpaque) onDraw(canvas); // Step 4, draw the children dispatchDraw(canvas); @@ -7123,6 +7187,60 @@ public class View implements Drawable.Callback, KeyEvent.Callback { } /** + * @param consistency The type of consistency. See ViewDebug for more information. + * + * @hide + */ + protected boolean dispatchConsistencyCheck(int consistency) { + return onConsistencyCheck(consistency); + } + + /** + * Method that subclasses should implement to check their consistency. The type of + * consistency check is indicated by the bit field passed as a parameter. + * + * @param consistency The type of consistency. See ViewDebug for more information. + * + * @throws IllegalStateException if the view is in an inconsistent state. + * + * @hide + */ + protected boolean onConsistencyCheck(int consistency) { + boolean result = true; + + final boolean checkLayout = (consistency & ViewDebug.CONSISTENCY_LAYOUT) != 0; + final boolean checkDrawing = (consistency & ViewDebug.CONSISTENCY_DRAWING) != 0; + + if (checkLayout) { + if (getParent() == null) { + result = false; + android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG, + "View " + this + " does not have a parent."); + } + + if (mAttachInfo == null) { + result = false; + android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG, + "View " + this + " is not attached to a window."); + } + } + + if (checkDrawing) { + // Do not check the DIRTY/DRAWN flags because views can call invalidate() + // from their draw() method + + if ((mPrivateFlags & DRAWN) != DRAWN && + (mPrivateFlags & DRAWING_CACHE_VALID) == DRAWING_CACHE_VALID) { + result = false; + android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG, + "View " + this + " was invalidated but its drawing cache is valid."); + } + } + + return result; + } + + /** * Prints information about this view in the log output, with the tag * {@link #VIEW_LOG_TAG}. * @@ -8049,7 +8167,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback { * window. */ static class AttachInfo { - interface Callbacks { void playSoundEffect(int effectId); boolean performHapticFeedback(int effectId, boolean always); @@ -8179,6 +8296,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback { long mDrawingTime; /** + * Indicates whether or not ignoring the DIRTY_MASK flags. + */ + boolean mIgnoreDirtyState; + + /** * Indicates whether the view's window is currently in touch mode. */ boolean mInTouchMode; diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java index 367c9a2..aaaadef 100644 --- a/core/java/android/view/ViewDebug.java +++ b/core/java/android/view/ViewDebug.java @@ -54,6 +54,27 @@ import java.lang.reflect.AccessibleObject; */ public class ViewDebug { /** + * Log tag used to log errors related to the consistency of the view hierarchy. + * + * @hide + */ + public static final String CONSISTENCY_LOG_TAG = "ViewConsistency"; + + /** + * Flag indicating the consistency check should check layout-related properties. + * + * @hide + */ + public static final int CONSISTENCY_LAYOUT = 0x1; + + /** + * Flag indicating the consistency check should check drawing-related properties. + * + * @hide + */ + public static final int CONSISTENCY_DRAWING = 0x2; + + /** * Enables or disables view hierarchy tracing. Any invoker of * {@link #trace(View, android.view.ViewDebug.HierarchyTraceType)} should first * check that this value is set to true as not to affect performance. @@ -80,6 +101,49 @@ public class ViewDebug { static final String SYSTEM_PROPERTY_CAPTURE_EVENT = "debug.captureevent"; /** + * Profiles drawing times in the events log. + * + * @hide + */ + @Debug.DebugProperty + public static boolean profileDrawing = false; + + /** + * Profiles layout times in the events log. + * + * @hide + */ + @Debug.DebugProperty + public static boolean profileLayout = false; + + /** + * Profiles real fps (times between draws) and displays the result. + * + * @hide + */ + @Debug.DebugProperty + public static boolean showFps = false; + + /** + * <p>Enables or disables views consistency check. Even when this property is enabled, + * view consistency checks happen only if {@link android.util.Config#DEBUG} is set + * to true. The value of this property can be configured externally in one of the + * following files:</p> + * <ul> + * <li>/system/debug.prop</li> + * <li>/debug.prop</li> + * <li>/data/debug.prop</li> + * </ul> + * @hide + */ + @Debug.DebugProperty + public static boolean consistencyCheckEnabled = false; + + static { + Debug.setFieldsOn(ViewDebug.class, true); + } + + /** * This annotation can be used to mark fields and methods to be dumped by * the view server. Only non-void methods with no arguments can be annotated * by this annotation. @@ -123,7 +187,7 @@ public class ViewDebug { * of an array: * * <pre> - * @ViewDebug.ExportedProperty(mapping = { + * @ViewDebug.ExportedProperty(indexMapping = { * @ViewDebug.IntToString(from = 0, to = "INVALID"), * @ViewDebug.IntToString(from = 1, to = "FIRST"), * @ViewDebug.IntToString(from = 2, to = "SECOND") @@ -139,6 +203,25 @@ public class ViewDebug { IntToString[] indexMapping() default { }; /** + * A flags mapping can be defined to map flags encoded in an integer to + * specific strings. A mapping can be used to see human readable values + * for the flags of an integer: + * + * <pre> + * @ViewDebug.ExportedProperty(flagMapping = { + * @ViewDebug.FlagToString(mask = ENABLED_MASK, equals = ENABLED, name = "ENABLED"), + * @ViewDebug.FlagToString(mask = ENABLED_MASK, equals = DISABLED, name = "DISABLED"), + * }) + * private int mFlags; + * <pre> + * + * A specified String is output when the following is true: + * + * @return An array of int to String mappings + */ + FlagToString[] flagMapping() default { }; + + /** * When deep export is turned on, this property is not dumped. Instead, the * properties contained in this property are dumped. Each child property * is prefixed with the name of this property. @@ -182,7 +265,45 @@ public class ViewDebug { */ String to(); } - + + /** + * Defines a mapping from an flag to a String. Such a mapping can be used + * in a @ExportedProperty to provide more meaningful values to the end user. + * + * @see android.view.ViewDebug.ExportedProperty + */ + @Target({ ElementType.TYPE }) + @Retention(RetentionPolicy.RUNTIME) + public @interface FlagToString { + /** + * The mask to apply to the original value. + * + * @return An arbitrary int value. + */ + int mask(); + + /** + * The value to compare to the result of: + * <code>original value & {@link #mask()}</code>. + * + * @return An arbitrary value. + */ + int equals(); + + /** + * The String to use in place of the original int value. + * + * @return An arbitrary non-null String. + */ + String name(); + + /** + * Indicates whether to output the flag when the test is true, + * or false. Defaults to true. + */ + boolean outputIf() default true; + } + /** * This annotation can be used to mark fields and methods to be dumped when * the view is captured. Methods with this annotation must have no arguments @@ -975,6 +1096,13 @@ public class ViewDebug { final int id = (Integer) methodValue; methodValue = resolveId(context, id); } else { + final FlagToString[] flagsMapping = property.flagMapping(); + if (flagsMapping.length > 0) { + final int intValue = (Integer) methodValue; + final String valuePrefix = prefix + method.getName() + '_'; + exportUnrolledFlags(out, flagsMapping, intValue, valuePrefix); + } + final IntToString[] mapping = property.mapping(); if (mapping.length > 0) { final int intValue = (Integer) methodValue; @@ -1036,6 +1164,13 @@ public class ViewDebug { final int id = field.getInt(view); fieldValue = resolveId(context, id); } else { + final FlagToString[] flagsMapping = property.flagMapping(); + if (flagsMapping.length > 0) { + final int intValue = field.getInt(view); + final String valuePrefix = prefix + field.getName() + '_'; + exportUnrolledFlags(out, flagsMapping, intValue, valuePrefix); + } + final IntToString[] mapping = property.mapping(); if (mapping.length > 0) { final int intValue = field.getInt(view); @@ -1093,6 +1228,23 @@ public class ViewDebug { out.write(' '); } + private static void exportUnrolledFlags(BufferedWriter out, FlagToString[] mapping, + int intValue, String prefix) throws IOException { + + final int count = mapping.length; + for (int j = 0; j < count; j++) { + final FlagToString flagMapping = mapping[j]; + final boolean ifTrue = flagMapping.outputIf(); + final int maskResult = intValue & flagMapping.mask(); + final boolean test = maskResult == flagMapping.equals(); + if ((test && ifTrue) || (!test && !ifTrue)) { + final String name = flagMapping.name(); + final String value = "0x" + Integer.toHexString(maskResult); + writeEntry(out, prefix, name, "", value); + } + } + } + private static void exportUnrolledArray(Context context, BufferedWriter out, ExportedProperty property, int[] array, String prefix, String suffix) throws IOException { diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index e686d1c..db5177f 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -32,6 +32,7 @@ import android.util.AttributeSet; import android.util.EventLog; import android.util.Log; import android.util.SparseArray; +import android.util.Config; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.view.animation.LayoutAnimationController; @@ -1402,7 +1403,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } } - // Clear the flag as early as possible to allow draw() implementations + // Sets the flag as early as possible to allow draw() implementations // to call invalidate() successfully when doing animations child.mPrivateFlags |= DRAWN; @@ -1481,6 +1482,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager if (ViewDebug.TRACE_HIERARCHY) { ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW); } + child.mPrivateFlags &= ~DIRTY_MASK; child.dispatchDraw(canvas); } else { child.draw(canvas); @@ -1494,7 +1496,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager cachePaint.setAlpha(255); mGroupFlags &= ~FLAG_ALPHA_LOWER_THAN_ONE; } - if (ViewRoot.PROFILE_DRAWING) { + if (Config.DEBUG && ViewDebug.profileDrawing) { EventLog.writeEvent(60003, hashCode()); } canvas.drawBitmap(cache, 0.0f, 0.0f, cachePaint); @@ -1796,7 +1798,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager boolean preventRequestLayout) { child.mParent = null; addViewInner(child, index, params, preventRequestLayout); - child.mPrivateFlags |= DRAWN; + child.mPrivateFlags = (child.mPrivateFlags & ~DIRTY_MASK) | DRAWN; return true; } @@ -2210,7 +2212,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager addInArray(child, index); child.mParent = this; - child.mPrivateFlags |= DRAWN; + child.mPrivateFlags = (child.mPrivateFlags & ~DIRTY_MASK) | DRAWN; if (child.hasFocus()) { requestChildFocus(child, child.findFocus()); @@ -2320,15 +2322,33 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager // ourselves and the parent to make sure the invalidate request goes // through final boolean drawAnimation = (child.mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION; - + + // Check whether the child that requests the invalidate is fully opaque + final boolean isOpaque = child.isOpaque(); + // Mark the child as dirty, using the appropriate flag + // Make sure we do not set both flags at the same time + final int opaqueFlag = isOpaque ? DIRTY_OPAQUE : DIRTY; + do { + View view = null; + if (parent instanceof View) { + view = (View) parent; + } + if (drawAnimation) { - if (parent instanceof View) { - ((View) parent).mPrivateFlags |= DRAW_ANIMATION; + if (view != null) { + view.mPrivateFlags |= DRAW_ANIMATION; } else if (parent instanceof ViewRoot) { ((ViewRoot) parent).mIsAnimating = true; } } + + // If the parent is dirty opaque or not dirty, mark it dirty with the opaque + // flag coming from the child that initiated the invalidate + if (view != null && (view.mPrivateFlags & DIRTY_MASK) != DIRTY) { + view.mPrivateFlags = (view.mPrivateFlags & ~DIRTY_MASK) | opaqueFlag; + } + parent = parent.invalidateChildInParent(location, dirty); } while (parent != null); } @@ -2732,6 +2752,61 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } /** + * @hide + */ + @Override + protected boolean dispatchConsistencyCheck(int consistency) { + boolean result = super.dispatchConsistencyCheck(consistency); + + final int count = mChildrenCount; + final View[] children = mChildren; + for (int i = 0; i < count; i++) { + if (!children[i].dispatchConsistencyCheck(consistency)) result = false; + } + + return result; + } + + /** + * @hide + */ + @Override + protected boolean onConsistencyCheck(int consistency) { + boolean result = super.onConsistencyCheck(consistency); + + final boolean checkLayout = (consistency & ViewDebug.CONSISTENCY_LAYOUT) != 0; + final boolean checkDrawing = (consistency & ViewDebug.CONSISTENCY_DRAWING) != 0; + + if (checkLayout) { + final int count = mChildrenCount; + final View[] children = mChildren; + for (int i = 0; i < count; i++) { + if (children[i].getParent() != this) { + result = false; + android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG, + "View " + children[i] + " has no parent/a parent that is not " + this); + } + } + } + + if (checkDrawing) { + // If this group is dirty, check that the parent is dirty as well + if ((mPrivateFlags & DIRTY_MASK) != 0) { + final ViewParent parent = getParent(); + if (parent != null && !(parent instanceof ViewRoot)) { + if ((((View) parent).mPrivateFlags & DIRTY_MASK) == 0) { + result = false; + android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG, + "ViewGroup " + this + " is dirty but its parent is not: " + this); + } + } + } + } + + return result; + } + + /** * {@inheritDoc} */ @Override diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java index 0b03626..dbfb194 100644 --- a/core/java/android/view/ViewRoot.java +++ b/core/java/android/view/ViewRoot.java @@ -75,13 +75,6 @@ public final class ViewRoot extends Handler implements ViewParent, private static final boolean DEBUG_IMF = false || LOCAL_LOGV; private static final boolean WATCH_POINTER = false; - static final boolean PROFILE_DRAWING = false; - private static final boolean PROFILE_LAYOUT = false; - // profiles real fps (times between draws) and displays the result - private static final boolean SHOW_FPS = false; - // used by SHOW_FPS - private static int sDrawTime; - /** * Maximum time we allow the user to roll the trackball enough to generate * a key event, before resetting the counters. @@ -97,6 +90,8 @@ public final class ViewRoot extends Handler implements ViewParent, static final ThreadLocal<RunQueue> sRunQueues = new ThreadLocal<RunQueue>(); + private static int sDrawTime; + long mLastTrackballTime = 0; final TrackballAxis mTrackballAxisX = new TrackballAxis(); final TrackballAxis mTrackballAxisY = new TrackballAxis(); @@ -128,9 +123,10 @@ public final class ViewRoot extends Handler implements ViewParent, int mHeight; Rect mDirty; // will be a graphics.Region soon boolean mIsAnimating; - // TODO: change these to scaler class. - float mAppScale; - float mAppScaleInverted; // = 1.0f / mAppScale + // TODO: change these to scalar class. + private float mAppScale; + private float mAppScaleInverted; // = 1.0f / mAppScale + private int[] mWindowLayoutParamsBackup = null; final View.AttachInfo mAttachInfo; @@ -389,6 +385,9 @@ public final class ViewRoot extends Handler implements ViewParent, if (mView == null) { mView = view; mAppScale = mView.getContext().getApplicationScale(); + if (mAppScale != 1.0f) { + mWindowLayoutParamsBackup = new int[4]; + } mAppScaleInverted = 1.0f / mAppScale; mWindowAttributes.copyFrom(attrs); mSoftInputMode = attrs.softInputMode; @@ -478,7 +477,6 @@ public final class ViewRoot extends Handler implements ViewParent, synchronized (this) { int oldSoftInputMode = mWindowAttributes.softInputMode; mWindowAttributes.copyFrom(attrs); - mWindowAttributes.scale(mAppScale); if (newView) { mSoftInputMode = attrs.softInputMode; @@ -795,7 +793,7 @@ public final class ViewRoot extends Handler implements ViewParent, final Rect frame = mWinFrame; boolean initialized = false; boolean contentInsetsChanged = false; - boolean visibleInsetsChanged = false; + boolean visibleInsetsChanged; try { boolean hadSurface = mSurface.isValid(); int fl = 0; @@ -936,14 +934,22 @@ public final class ViewRoot extends Handler implements ViewParent, if (DEBUG_ORIENTATION || DEBUG_LAYOUT) Log.v( "ViewRoot", "Laying out " + host + " to (" + host.mMeasuredWidth + ", " + host.mMeasuredHeight + ")"); - long startTime; - if (PROFILE_LAYOUT) { + long startTime = 0L; + if (Config.DEBUG && ViewDebug.profileLayout) { startTime = SystemClock.elapsedRealtime(); } host.layout(0, 0, host.mMeasuredWidth, host.mMeasuredHeight); - if (PROFILE_LAYOUT) { + if (Config.DEBUG && ViewDebug.consistencyCheckEnabled) { + if (!host.dispatchConsistencyCheck(ViewDebug.CONSISTENCY_LAYOUT)) { + throw new IllegalStateException("The view hierarchy is an inconsistent state," + + "please refer to the logs with the tag " + + ViewDebug.CONSISTENCY_LOG_TAG + " for more infomation."); + } + } + + if (Config.DEBUG && ViewDebug.profileLayout) { EventLog.writeEvent(60001, SystemClock.elapsedRealtime() - startTime); } @@ -1135,8 +1141,7 @@ public final class ViewRoot extends Handler implements ViewParent, } int yoff; - final boolean scrolling = mScroller != null - && mScroller.computeScrollOffset(); + final boolean scrolling = mScroller != null && mScroller.computeScrollOffset(); if (scrolling) { yoff = mScroller.getCurrY(); } else { @@ -1151,13 +1156,14 @@ public final class ViewRoot extends Handler implements ViewParent, if (mUseGL) { if (!dirty.isEmpty()) { Canvas canvas = mGlCanvas; - if (mGL!=null && canvas != null) { + if (mGL != null && canvas != null) { mGL.glDisable(GL_SCISSOR_TEST); mGL.glClearColor(0, 0, 0, 0); mGL.glClear(GL_COLOR_BUFFER_BIT); mGL.glEnable(GL_SCISSOR_TEST); mAttachInfo.mDrawingTime = SystemClock.uptimeMillis(); + mAttachInfo.mIgnoreDirtyState = true; mView.mPrivateFlags |= View.DRAWN; float scale = mAppScale; @@ -1168,14 +1174,19 @@ public final class ViewRoot extends Handler implements ViewParent, canvas.scale(scale, scale); } mView.draw(canvas); + if (Config.DEBUG && ViewDebug.consistencyCheckEnabled) { + mView.dispatchConsistencyCheck(ViewDebug.CONSISTENCY_DRAWING); + } } finally { canvas.restoreToCount(saveCount); } + mAttachInfo.mIgnoreDirtyState = false; + mEgl.eglSwapBuffers(mEglDisplay, mEglSurface); checkEglErrors(); - if (SHOW_FPS) { + if (Config.DEBUG && ViewDebug.showFps) { int now = (int)SystemClock.elapsedRealtime(); if (sDrawTime != 0) { nativeShowFPS(canvas, now - sDrawTime); @@ -1191,8 +1202,10 @@ public final class ViewRoot extends Handler implements ViewParent, return; } - if (fullRedrawNeeded) + if (fullRedrawNeeded) { + mAttachInfo.mIgnoreDirtyState = true; dirty.union(0, 0, (int) (mWidth * mAppScale), (int) (mHeight * mAppScale)); + } if (DEBUG_ORIENTATION || DEBUG_DRAW) { Log.v("ViewRoot", "Draw " + mView + "/" @@ -1204,7 +1217,18 @@ public final class ViewRoot extends Handler implements ViewParent, Canvas canvas; try { + int left = dirty.left; + int top = dirty.top; + int right = dirty.right; + int bottom = dirty.bottom; + canvas = surface.lockCanvas(dirty); + + if (left != dirty.left || top != dirty.top || right != dirty.right || + bottom != dirty.bottom) { + mAttachInfo.mIgnoreDirtyState = true; + } + // TODO: Do this in native canvas.setDensityScale(mDensity); } catch (Surface.OutOfResourcesException e) { @@ -1216,7 +1240,7 @@ public final class ViewRoot extends Handler implements ViewParent, try { if (!dirty.isEmpty() || mIsAnimating) { - long startTime; + long startTime = 0L; if (DEBUG_ORIENTATION || DEBUG_DRAW) { Log.v("ViewRoot", "Surface " + surface + " drawing to bitmap w=" @@ -1224,7 +1248,7 @@ public final class ViewRoot extends Handler implements ViewParent, //canvas.drawARGB(255, 255, 0, 0); } - if (PROFILE_DRAWING) { + if (Config.DEBUG && ViewDebug.profileDrawing) { startTime = SystemClock.elapsedRealtime(); } @@ -1232,12 +1256,11 @@ public final class ViewRoot extends Handler implements ViewParent, // need to clear it before drawing so that the child will // properly re-composite its drawing on a transparent // background. This automatically respects the clip/dirty region - if (!canvas.isOpaque()) { - canvas.drawColor(0x00000000, PorterDuff.Mode.CLEAR); - } else if (yoff != 0) { - // If we are applying an offset, we need to clear the area - // where the offset doesn't appear to avoid having garbage - // left in the blank areas. + // or + // If we are applying an offset, we need to clear the area + // where the offset doesn't appear to avoid having garbage + // left in the blank areas. + if (!canvas.isOpaque() || yoff != 0) { canvas.drawColor(0, PorterDuff.Mode.CLEAR); } @@ -1247,9 +1270,10 @@ public final class ViewRoot extends Handler implements ViewParent, mView.mPrivateFlags |= View.DRAWN; float scale = mAppScale; - Context cxt = mView.getContext(); if (DEBUG_DRAW) { - Log.i(TAG, "Drawing: package:" + cxt.getPackageName() + ", appScale=" + mAppScale); + Context cxt = mView.getContext(); + Log.i(TAG, "Drawing: package:" + cxt.getPackageName() + + ", appScale=" + mAppScale); } int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG); try { @@ -1260,10 +1284,15 @@ public final class ViewRoot extends Handler implements ViewParent, } mView.draw(canvas); } finally { + mAttachInfo.mIgnoreDirtyState = false; canvas.restoreToCount(saveCount); } - if (SHOW_FPS) { + if (Config.DEBUG && ViewDebug.consistencyCheckEnabled) { + mView.dispatchConsistencyCheck(ViewDebug.CONSISTENCY_DRAWING); + } + + if (Config.DEBUG && ViewDebug.showFps) { int now = (int)SystemClock.elapsedRealtime(); if (sDrawTime != 0) { nativeShowFPS(canvas, now - sDrawTime); @@ -1271,7 +1300,7 @@ public final class ViewRoot extends Handler implements ViewParent, sDrawTime = now; } - if (PROFILE_DRAWING) { + if (Config.DEBUG && ViewDebug.profileDrawing) { EventLog.writeEvent(60000, SystemClock.elapsedRealtime() - startTime); } } @@ -2309,12 +2338,22 @@ public final class ViewRoot extends Handler implements ViewParent, private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility, boolean insetsPending) throws RemoteException { + + boolean restore = false; + if (params != null && mAppScale != 1.0f) { + restore = true; + params.scale(mAppScale, mWindowLayoutParamsBackup); + } int relayoutResult = sWindowSession.relayout( mWindow, params, (int) (mView.mMeasuredWidth * mAppScale), (int) (mView.mMeasuredHeight * mAppScale), viewVisibility, insetsPending, mWinFrame, mPendingContentInsets, mPendingVisibleInsets, mSurface); + if (restore) { + params.restore(mWindowLayoutParamsBackup); + } + mPendingContentInsets.scale(mAppScaleInverted); mPendingVisibleInsets.scale(mAppScaleInverted); mWinFrame.scale(mAppScaleInverted); @@ -2416,7 +2455,7 @@ public final class ViewRoot extends Handler implements ViewParent, msg.arg2 = handled ? 1 : 0; sendMessage(msg); } - + public void dispatchResized(int w, int h, Rect coveredInsets, Rect visibleInsets, boolean reportDraw) { if (DEBUG_LAYOUT) Log.v(TAG, "Resizing " + this + ": w=" + w diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index f6a171d..c69c281 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -18,6 +18,7 @@ package android.view; import android.content.pm.ActivityInfo; import android.graphics.PixelFormat; +import android.graphics.Rect; import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; @@ -954,19 +955,42 @@ public interface WindowManager extends ViewManager { return sb.toString(); } - void scale(float scale) { + /** + * Scale the layout params' coordinates and size. + * Returns the original info as a backup so that the caller can + * restore the layout params; + */ + void scale(float scale, int[] backup) { if (scale != 1.0f) { + backup[0] = x; + backup[1] = y; x *= scale; y *= scale; if (width > 0) { + backup[2] = width; width *= scale; } if (height > 0) { + backup[3] = height; height *= scale; } } } + /** + * Restore the layout params' coordinates and size. + */ + void restore(int[] backup) { + x = backup[0]; + y = backup[1]; + if (width > 0) { + width = backup[2]; + } + if (height > 0) { + height = backup[3]; + } + } + private CharSequence mTitle = ""; } } |