From 8169daed2f7a8731d478b884b1f455c747b88478 Mon Sep 17 00:00:00 2001 From: Mitsuru Oshima <> Date: Tue, 28 Apr 2009 18:12:09 -0700 Subject: [PATCH] AI 147976: Compatibility mode support. Part 2. * Introduced ApplicationScale (may not be good name. CompatibilityScale? CanvasScale? Pls let me know if you have better idea) * Changes to RootView / SurfaceView - Makes the app believe it's running in the supported density/resolution. - Makes the window manager believe it's running at the right density/resolution. * Added methods to Rect/Event for scaling up/down. Known issues: * certain kind of images (such as nine patch for buttons) seesm to be loaded not by app, thus does not take the scale into account, which, in turn, is causing layout issue. * ZoomButton in MapView is rendered in wrong place * Transparent region on Surface is not correct * Specifying different densities in one process is not working. BUG=1770627 Automated import of CL 147976 --- core/java/android/app/ActivityThread.java | 76 +++++++++- core/java/android/app/ApplicationContext.java | 17 ++- core/java/android/content/Context.java | 10 ++ core/java/android/content/ContextWrapper.java | 8 ++ core/java/android/content/res/Resources.java | 42 +++++- core/java/android/view/MotionEvent.java | 27 +++- core/java/android/view/SurfaceView.java | 33 ++++- core/java/android/view/ViewRoot.java | 130 +++++++++++++----- core/java/android/view/WindowManager.java | 2 +- graphics/java/android/graphics/Rect.java | 13 ++ .../android/test/mock/MockContext.java | 8 ++ .../layoutlib/bridge/BridgeContext.java | 8 ++ 12 files changed, 324 insertions(+), 50 deletions(-) diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index d8161936a44d1..b7cd080f463ae 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -161,7 +161,7 @@ public final class ActivityThread { return metrics; } - Resources getTopLevelResources(String appDir) { + Resources getTopLevelResources(String appDir, float applicationScale) { synchronized (mPackages) { //Log.w(TAG, "getTopLevelResources: " + appDir); WeakReference wr = mActiveResources.get(appDir); @@ -181,7 +181,27 @@ public final class ActivityThread { return null; } DisplayMetrics metrics = getDisplayMetricsLocked(false); - r = new Resources(assets, metrics, getConfiguration()); + // density used to load resources + // scaledDensity is calculated in Resources constructor + // + boolean usePreloaded = true; + + // TODO: use explicit flag to indicate the compatibility mode. + if (applicationScale != 1.0f) { + usePreloaded = false; + DisplayMetrics newMetrics = new DisplayMetrics(); + newMetrics.setTo(metrics); + float invertedScale = 1.0f / applicationScale; + newMetrics.density *= invertedScale; + newMetrics.xdpi *= invertedScale; + newMetrics.ydpi *= invertedScale; + newMetrics.widthPixels *= invertedScale; + newMetrics.heightPixels *= invertedScale; + metrics = newMetrics; + } + //Log.i(TAG, "Resource:" + appDir + ", density " + newMetrics.density + ", orig density:" + + // metrics.density); + r = new Resources(assets, metrics, getConfiguration(), usePreloaded); //Log.i(TAG, "Created app resources " + r + ": " + r.getConfiguration()); // XXX need to remove entries when weak references go away mActiveResources.put(appDir, new WeakReference(r)); @@ -209,6 +229,8 @@ public final class ActivityThread { private Resources mResources; private ClassLoader mClassLoader; private Application mApplication; + private float mApplicationScale; + private final HashMap> mReceivers = new HashMap>(); private final HashMap> mUnregisteredReceivers @@ -241,8 +263,8 @@ public final class ActivityThread { mSystemContext = ApplicationContext.createSystemContext(mainThread); mSystemContext.getResources().updateConfiguration( - mainThread.getConfiguration(), - mainThread.getDisplayMetricsLocked(false)); + mainThread.getConfiguration(), + mainThread.getDisplayMetricsLocked(false)); //Log.i(TAG, "Created system resources " // + mSystemContext.getResources() + ": " // + mSystemContext.getResources().getConfiguration()); @@ -250,6 +272,8 @@ public final class ActivityThread { mClassLoader = mSystemContext.getClassLoader(); mResources = mSystemContext.getResources(); } + + mApplicationScale = -1.0f; } public PackageInfo(ActivityThread activityThread, String name, @@ -268,6 +292,7 @@ public final class ActivityThread { mIncludeCode = true; mClassLoader = systemContext.getClassLoader(); mResources = systemContext.getResources(); + mApplicationScale = systemContext.getApplicationScale(); } public String getPackageName() { @@ -278,6 +303,47 @@ public final class ActivityThread { return mSecurityViolation; } + public float getApplicationScale() { + if (mApplicationScale > 0.0f) { + return mApplicationScale; + } + DisplayMetrics metrics = mActivityThread.getDisplayMetricsLocked(false); + // Find out the density scale (relative to 160) of the supported density that + // is closest to the system's density. + try { + ApplicationInfo ai = getPackageManager().getApplicationInfo( + mPackageName, PackageManager.GET_SUPPORTS_DENSITIES); + + float appScale = -1.0f; + if (ai.supportsDensities != null) { + // TODO: precompute this in DisplayMetrics + float systemDensityDpi = metrics.density * DisplayMetrics.DEFAULT_DENSITY; + int minDiff = Integer.MAX_VALUE; + for (int density : ai.supportsDensities) { + int tmpDiff = (int) Math.abs(systemDensityDpi - density); + if (tmpDiff == 0) { + appScale = 1.0f; + break; + } + // prefer higher density (appScale>1.0), unless that's only option. + if (tmpDiff < minDiff && appScale < 1.0f) { + appScale = systemDensityDpi / density; + minDiff = tmpDiff; + } + } + } + if (appScale < 0.0f) { + mApplicationScale = metrics.density; + } else { + mApplicationScale = appScale; + } + } catch (RemoteException e) { + throw new AssertionError(e); + } + if (localLOGV) Log.v(TAG, "appScale=" + mApplicationScale + ", pkg=" + mPackageName); + return mApplicationScale; + } + /** * Gets the array of shared libraries that are listed as * used by the given package. @@ -435,7 +501,7 @@ public final class ActivityThread { public Resources getResources(ActivityThread mainThread) { if (mResources == null) { - mResources = mainThread.getTopLevelResources(mResDir); + mResources = mainThread.getTopLevelResources(mResDir, getApplicationScale()); } return mResources; } diff --git a/core/java/android/app/ApplicationContext.java b/core/java/android/app/ApplicationContext.java index 55fce4932e6de..a1f5a58313a06 100644 --- a/core/java/android/app/ApplicationContext.java +++ b/core/java/android/app/ApplicationContext.java @@ -550,6 +550,19 @@ class ApplicationContext extends Context { } } + /** + * @hide + */ + @Override + public float getApplicationScale() { + if (mPackageInfo != null) { + return mPackageInfo.getApplicationScale(); + } else { + // same as system density + return 1.0f; + } + } + @Override public void setWallpaper(Bitmap bitmap) throws IOException { try { @@ -2008,9 +2021,11 @@ class ApplicationContext extends Context { if (app.packageName.equals("system")) { return mContext.mMainThread.getSystemContext().getResources(); } + ActivityThread.PackageInfo pi = mContext.mMainThread.getPackageInfoNoCheck(app); Resources r = mContext.mMainThread.getTopLevelResources( app.uid == Process.myUid() ? app.sourceDir - : app.publicSourceDir); + : app.publicSourceDir, + pi.getApplicationScale()); if (r != null) { return r; } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 600dfa40abc4a..a3014498114a0 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -526,6 +526,16 @@ public abstract class Context { */ public abstract int getWallpaperDesiredMinimumHeight(); + /** + * Returns the scale in which the application will be drawn on the + * screen. This is usually 1.0f if the application supports the device's + * resolution/density. This will be 1.5f, for example, if the application + * that supports only 160 density runs on 240 density screen. + * + * @hide + */ + public abstract float getApplicationScale(); + /** * Change the current system wallpaper to a bitmap. The given bitmap is * converted to a PNG and stored as the wallpaper. On success, the intent diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java index 36e1c340d1d98..25b2caeb72790 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -419,4 +419,12 @@ public class ContextWrapper extends Context { throws PackageManager.NameNotFoundException { return mBase.createPackageContext(packageName, flags); } + + /** + * @hide + */ + @Override + public float getApplicationScale() { + return mBase.getApplicationScale(); + } } diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index e020462f8bd79..665e40ca362fd 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -24,6 +24,7 @@ import org.xmlpull.v1.XmlPullParserException; import android.graphics.Movie; import android.graphics.drawable.Drawable; +import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.ColorDrawable; import android.os.Bundle; import android.os.SystemProperties; @@ -56,12 +57,14 @@ public class Resources { // Information about preloaded resources. Note that they are not // protected by a lock, because while preloading in zygote we are all // single-threaded, and after that these are immutable. - private static final SparseArray mPreloadedDrawables + private static final SparseArray sPreloadedDrawables = new SparseArray(); private static final SparseArray mPreloadedColorStateLists = new SparseArray(); private static boolean mPreloaded; + private final SparseArray mPreloadedDrawables; + /*package*/ final TypedValue mTmpValue = new TypedValue(); // These are protected by the mTmpValue lock. @@ -82,6 +85,22 @@ public class Resources { /*package*/ final DisplayMetrics mMetrics = new DisplayMetrics(); PluralRules mPluralRule; + private static final SparseArray EMPTY_ARRAY = new SparseArray() { + @Override + public void put(int k, Object o) { + throw new UnsupportedOperationException(); + } + @Override + public void append(int k, Object o) { + throw new UnsupportedOperationException(); + } + }; + + @SuppressWarnings("unchecked") + private static SparseArray emptySparseArray() { + return (SparseArray) EMPTY_ARRAY; + } + /** * This exception is thrown by the resource APIs when a requested resource * can not be found. @@ -107,11 +126,27 @@ public class Resources { */ public Resources(AssetManager assets, DisplayMetrics metrics, Configuration config) { + this(assets, metrics, config, true); + } + + /** + * Create a resource with an additional flag for preloaded + * drawable cache. Used by {@link ActivityThread}. + * + * @hide + */ + public Resources(AssetManager assets, DisplayMetrics metrics, + Configuration config, boolean usePreloadedCache) { mAssets = assets; mConfiguration.setToDefaults(); mMetrics.setToDefaults(); updateConfiguration(config, metrics); assets.ensureStringBlocks(); + if (usePreloadedCache) { + mPreloadedDrawables = sPreloadedDrawables; + } else { + mPreloadedDrawables = emptySparseArray(); + } } /** @@ -1218,6 +1253,7 @@ public class Resources { mMetrics.setTo(metrics); } mMetrics.scaledDensity = mMetrics.density * mConfiguration.fontScale; + String locale = null; if (mConfiguration.locale != null) { locale = mConfiguration.locale.getLanguage(); @@ -1653,7 +1689,7 @@ public class Resources { cs = dr.getConstantState(); if (cs != null) { if (mPreloading) { - mPreloadedDrawables.put(key, cs); + sPreloadedDrawables.put(key, cs); } else { synchronized (mTmpValue) { //Log.i(TAG, "Saving cached drawable @ #" + @@ -1883,6 +1919,6 @@ public class Resources { mMetrics.setToDefaults(); updateConfiguration(null, null); mAssets.ensureStringBlocks(); + mPreloadedDrawables = sPreloadedDrawables; } } - diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java index 882a07903741f..24026601145e3 100644 --- a/core/java/android/view/MotionEvent.java +++ b/core/java/android/view/MotionEvent.java @@ -102,7 +102,7 @@ public final class MotionEvent implements Parcelable { private float mYPrecision; private int mDeviceId; private int mEdgeFlags; - + private MotionEvent mNext; private RuntimeException mRecycledLocation; private boolean mRecycled; @@ -210,7 +210,29 @@ public final class MotionEvent implements Parcelable { return ev; } - + + /** + * Scales down the cood of this event by the given scale. + * + * @hide + */ + public void scale(float scale) { + if (scale != 1.0f) { + mX *= scale; + mY *= scale; + mRawX *= scale; + mRawY *= scale; + mSize *= scale; + mXPrecision *= scale; + mYPrecision *= scale; + float[] history = mHistory; + int length = history.length; + for (int i = 0; i < length; i++) { + history[i] *= scale; + } + } + } + /** * Create a new MotionEvent, copying from an existing one. */ @@ -682,4 +704,3 @@ public final class MotionEvent implements Parcelable { } } - diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index e928998b6d00a..61dca4c1a37bc 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -135,20 +135,28 @@ public class SurfaceView extends View { int mFormat = -1; int mType = -1; final Rect mSurfaceFrame = new Rect(); + private final float mAppScale; + private final float mAppScaleInverted; public SurfaceView(Context context) { super(context); setWillNotDraw(true); + mAppScale = context.getApplicationScale(); + mAppScaleInverted = 1.0f / mAppScale; } public SurfaceView(Context context, AttributeSet attrs) { super(context, attrs); setWillNotDraw(true); + mAppScale = context.getApplicationScale(); + mAppScaleInverted = 1.0f / mAppScale; } public SurfaceView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); setWillNotDraw(true); + mAppScale = context.getApplicationScale(); + mAppScaleInverted = 1.0f / mAppScale; } /** @@ -297,8 +305,8 @@ public class SurfaceView extends View { mLayout.x = mLeft; mLayout.y = mTop; - mLayout.width = getWidth(); - mLayout.height = getHeight(); + mLayout.width = (int) (getWidth() * mAppScale); + mLayout.height = (int) (getHeight() * mAppScale); mLayout.format = mRequestedFormat; mLayout.flags |=WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS | WindowManager.LayoutParams.FLAG_SCALED @@ -325,9 +333,14 @@ public class SurfaceView extends View { mSurfaceLock.lock(); mDrawingStopped = !visible; final int relayoutResult = mSession.relayout( - mWindow, mLayout, mWidth, mHeight, + mWindow, mLayout, (int) (mWidth * mAppScale), (int) (mHeight * mAppScale), visible ? VISIBLE : GONE, false, mWinFrame, mContentInsets, mVisibleInsets, mSurface); + + mContentInsets.scale(mAppScaleInverted); + mVisibleInsets.scale(mAppScaleInverted); + mWinFrame.scale(mAppScaleInverted); + if (localLOGV) Log.i(TAG, "New surface: " + mSurface + ", vis=" + visible + ", frame=" + mWinFrame); mSurfaceFrame.left = 0; @@ -395,15 +408,25 @@ public class SurfaceView extends View { } private static class MyWindow extends IWindow.Stub { - private WeakReference mSurfaceView; + private final WeakReference mSurfaceView; + private final float mAppScale; + private final float mAppScaleInverted; public MyWindow(SurfaceView surfaceView) { mSurfaceView = new WeakReference(surfaceView); + mAppScale = surfaceView.getContext().getApplicationScale(); + mAppScaleInverted = 1.0f / mAppScale; } public void resized(int w, int h, Rect coveredInsets, Rect visibleInsets, boolean reportDraw) { SurfaceView surfaceView = mSurfaceView.get(); + float scale = mAppScaleInverted; + w *= scale; + h *= scale; + coveredInsets.scale(scale); + visibleInsets.scale(scale); + if (surfaceView != null) { if (localLOGV) Log.v( "SurfaceView", surfaceView + " got resized: w=" + @@ -566,6 +589,7 @@ public class SurfaceView extends View { Canvas c = null; if (!mDrawingStopped && mWindow != null) { Rect frame = dirty != null ? dirty : mSurfaceFrame; + frame.scale(mAppScale); try { c = mSurface.lockCanvas(frame); } catch (Exception e) { @@ -611,4 +635,3 @@ public class SurfaceView extends View { } }; } - diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java index 18ee9aeb8f68e..0b03626e6b678 100644 --- a/core/java/android/view/ViewRoot.java +++ b/core/java/android/view/ViewRoot.java @@ -128,6 +128,9 @@ 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 final View.AttachInfo mAttachInfo; @@ -384,10 +387,12 @@ public final class ViewRoot extends Handler implements ViewParent, View panelParentView) { synchronized (this) { if (mView == null) { + mView = view; + mAppScale = mView.getContext().getApplicationScale(); + mAppScaleInverted = 1.0f / mAppScale; mWindowAttributes.copyFrom(attrs); mSoftInputMode = attrs.softInputMode; mWindowAttributesChanged = true; - mView = view; mAttachInfo.mRootView = view; if (panelParentView != null) { mAttachInfo.mPanelParentWindowToken @@ -400,7 +405,7 @@ public final class ViewRoot extends Handler implements ViewParent, // manager, to make sure we do the relayout before receiving // any other events from the system. requestLayout(); - + try { res = sWindowSession.add(mWindow, attrs, getHostVisibility(), mAttachInfo.mContentInsets); @@ -411,6 +416,7 @@ public final class ViewRoot extends Handler implements ViewParent, unscheduleTraversals(); throw new RuntimeException("Adding window failed", e); } + mAttachInfo.mContentInsets.scale(mAppScaleInverted); mPendingContentInsets.set(mAttachInfo.mContentInsets); mPendingVisibleInsets.set(0, 0, 0, 0); if (Config.LOGV) Log.v("ViewRoot", "Added window " + mWindow); @@ -472,6 +478,8 @@ 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; requestLayout(); @@ -521,9 +529,14 @@ public final class ViewRoot extends Handler implements ViewParent, public void invalidateChild(View child, Rect dirty) { checkThread(); if (LOCAL_LOGV) Log.v(TAG, "Invalidate child: " + dirty); - if (mCurScrollY != 0) { + if (mCurScrollY != 0 || mAppScale != 1.0f) { mTempRect.set(dirty); - mTempRect.offset(0, -mCurScrollY); + if (mCurScrollY != 0) { + mTempRect.offset(0, -mCurScrollY); + } + if (mAppScale != 1.0f) { + mTempRect.scale(mAppScale); + } dirty = mTempRect; } mDirty.union(dirty); @@ -613,8 +626,8 @@ public final class ViewRoot extends Handler implements ViewParent, mLayoutRequested = true; Display d = new Display(0); - desiredWindowWidth = d.getWidth(); - desiredWindowHeight = d.getHeight(); + desiredWindowWidth = (int) (d.getWidth() * mAppScaleInverted); + desiredWindowHeight = (int) (d.getHeight() * mAppScaleInverted); // For the very first time, tell the view hierarchy that it // is attached to the window. Note that at this point the surface @@ -683,8 +696,8 @@ public final class ViewRoot extends Handler implements ViewParent, windowResizesToFitContent = true; Display d = new Display(0); - desiredWindowWidth = d.getWidth(); - desiredWindowHeight = d.getHeight(); + desiredWindowWidth = (int) (d.getWidth() * mAppScaleInverted); + desiredWindowHeight = (int) (d.getHeight() * mAppScaleInverted); } } @@ -792,10 +805,12 @@ public final class ViewRoot extends Handler implements ViewParent, params.flags |= WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; } } - relayoutResult = sWindowSession.relayout( - mWindow, params, host.mMeasuredWidth, host.mMeasuredHeight, - viewVisibility, insetsPending, frame, - mPendingContentInsets, mPendingVisibleInsets, mSurface); + if (DEBUG_LAYOUT) { + Log.i(TAG, "host=w:" + host.mMeasuredWidth + ", h:" + + host.mMeasuredHeight + ", params=" + params); + } + relayoutResult = relayoutWindow(params, viewVisibility, insetsPending); + if (params != null) { params.flags = fl; } @@ -862,7 +877,7 @@ public final class ViewRoot extends Handler implements ViewParent, mHeight = frame.height(); if (initialized) { - mGlCanvas.setViewport(mWidth, mHeight); + mGlCanvas.setViewport((int) (mWidth * mAppScale), (int) (mHeight * mAppScale)); } boolean focusChangedDueToTouchMode = ensureTouchModeLocally( @@ -944,6 +959,11 @@ public final class ViewRoot extends Handler implements ViewParent, mTmpLocation[1] + host.mBottom - host.mTop); host.gatherTransparentRegion(mTransparentRegion); + + // TODO: scale the region, like: + // Region uses native methods. We probabl should have ScalableRegion class. + + // Region does not have equals method ? if (!mTransparentRegion.equals(mPreviousTransparentRegion)) { mPreviousTransparentRegion.set(mTransparentRegion); // reconfigure window manager @@ -974,6 +994,9 @@ public final class ViewRoot extends Handler implements ViewParent, givenContent.left = givenContent.top = givenContent.right = givenContent.bottom = givenVisible.left = givenVisible.top = givenVisible.right = givenVisible.bottom = 0; + insets.contentInsets.scale(mAppScale); + insets.visibleInsets.scale(mAppScale); + attachInfo.mTreeObserver.dispatchOnComputeInternalInsets(insets); if (insetsPending || !mLastGivenInsets.equals(insets)) { mLastGivenInsets.set(insets); @@ -1113,7 +1136,7 @@ public final class ViewRoot extends Handler implements ViewParent, int yoff; final boolean scrolling = mScroller != null - && mScroller.computeScrollOffset(); + && mScroller.computeScrollOffset(); if (scrolling) { yoff = mScroller.getCurrY(); } else { @@ -1135,10 +1158,19 @@ public final class ViewRoot extends Handler implements ViewParent, mGL.glEnable(GL_SCISSOR_TEST); mAttachInfo.mDrawingTime = SystemClock.uptimeMillis(); - canvas.translate(0, -yoff); mView.mPrivateFlags |= View.DRAWN; - mView.draw(canvas); - canvas.translate(0, yoff); + + float scale = mAppScale; + int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG); + try { + canvas.translate(0, -yoff); + if (scale != 1.0f) { + canvas.scale(scale, scale); + } + mView.draw(canvas); + } finally { + canvas.restoreToCount(saveCount); + } mEgl.eglSwapBuffers(mEglDisplay, mEglSurface); checkEglErrors(); @@ -1160,7 +1192,7 @@ public final class ViewRoot extends Handler implements ViewParent, } if (fullRedrawNeeded) - dirty.union(0, 0, mWidth, mHeight); + dirty.union(0, 0, (int) (mWidth * mAppScale), (int) (mHeight * mAppScale)); if (DEBUG_ORIENTATION || DEBUG_DRAW) { Log.v("ViewRoot", "Draw " + mView + "/" @@ -1212,10 +1244,24 @@ public final class ViewRoot extends Handler implements ViewParent, dirty.setEmpty(); mIsAnimating = false; mAttachInfo.mDrawingTime = SystemClock.uptimeMillis(); - canvas.translate(0, -yoff); - mView.mPrivateFlags |= View.DRAWN; - mView.draw(canvas); - canvas.translate(0, yoff); + mView.mPrivateFlags |= View.DRAWN; + + float scale = mAppScale; + Context cxt = mView.getContext(); + if (DEBUG_DRAW) { + Log.i(TAG, "Drawing: package:" + cxt.getPackageName() + ", appScale=" + mAppScale); + } + int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG); + try { + canvas.translate(0, -yoff); + if (scale != 1.0f) { + // re-scale this + canvas.scale(scale, scale); + } + mView.draw(canvas); + } finally { + canvas.restoreToCount(saveCount); + } if (SHOW_FPS) { int now = (int)SystemClock.elapsedRealtime(); @@ -1508,6 +1554,9 @@ public final class ViewRoot extends Handler implements ViewParent, } else { didFinish = event.getAction() == MotionEvent.ACTION_OUTSIDE; } + if (event != null) { + event.scale(mAppScaleInverted); + } try { boolean handled; @@ -1628,7 +1677,8 @@ public final class ViewRoot extends Handler implements ViewParent, if (mGlWanted && !mUseGL) { initializeGL(); if (mGlCanvas != null) { - mGlCanvas.setViewport(mWidth, mHeight); + mGlCanvas.setViewport((int) (mWidth * mAppScale), + (int) (mHeight * mAppScale)); } } } @@ -1828,6 +1878,9 @@ public final class ViewRoot extends Handler implements ViewParent, } else { didFinish = false; } + if (event != null) { + event.scale(mAppScaleInverted); + } if (DEBUG_TRACKBALL) Log.v(TAG, "Motion event:" + event); @@ -2254,6 +2307,20 @@ public final class ViewRoot extends Handler implements ViewParent, return mAudioManager; } + private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility, + boolean insetsPending) throws RemoteException { + int relayoutResult = sWindowSession.relayout( + mWindow, params, + (int) (mView.mMeasuredWidth * mAppScale), + (int) (mView.mMeasuredHeight * mAppScale), + viewVisibility, insetsPending, mWinFrame, + mPendingContentInsets, mPendingVisibleInsets, mSurface); + mPendingContentInsets.scale(mAppScaleInverted); + mPendingVisibleInsets.scale(mAppScaleInverted); + mWinFrame.scale(mAppScaleInverted); + return relayoutResult; + } + /** * {@inheritDoc} */ @@ -2322,12 +2389,8 @@ public final class ViewRoot extends Handler implements ViewParent, // to the window manager to make sure it has the correct // animation info. try { - if ((sWindowSession.relayout( - mWindow, mWindowAttributes, - mView.mMeasuredWidth, mView.mMeasuredHeight, - viewVisibility, false, mWinFrame, mPendingContentInsets, - mPendingVisibleInsets, mSurface) - &WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0) { + if ((relayoutWindow(mWindowAttributes, viewVisibility, false) + & WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0) { sWindowSession.finishDrawing(mWindow); } } catch (RemoteException e) { @@ -2361,8 +2424,11 @@ public final class ViewRoot extends Handler implements ViewParent, + " visibleInsets=" + visibleInsets.toShortString() + " reportDraw=" + reportDraw); Message msg = obtainMessage(reportDraw ? RESIZED_REPORT :RESIZED); - msg.arg1 = w; - msg.arg2 = h; + + coveredInsets.scale(mAppScaleInverted); + visibleInsets.scale(mAppScaleInverted); + msg.arg1 = (int) (w * mAppScaleInverted); + msg.arg2 = (int) (h * mAppScaleInverted); msg.obj = new Rect[] { new Rect(coveredInsets), new Rect(visibleInsets) }; sendMessage(msg); } @@ -2493,7 +2559,7 @@ public final class ViewRoot extends Handler implements ViewParent, sWindowSession.finishKey(mWindow); } catch (RemoteException e) { } - } else if (mIsPointer) { + } else if (mIsPointer) { boolean didFinish; MotionEvent event = mMotionEvent; if (event == null) { diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 34e65ad3cc0ca..f6a171dc74e08 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -954,7 +954,7 @@ public interface WindowManager extends ViewManager { return sb.toString(); } - void scaleUp(float scale) { + void scale(float scale) { if (scale != 1.0f) { x *= scale; y *= scale; diff --git a/graphics/java/android/graphics/Rect.java b/graphics/java/android/graphics/Rect.java index 20053449c87ba..dda37e5bcadb5 100644 --- a/graphics/java/android/graphics/Rect.java +++ b/graphics/java/android/graphics/Rect.java @@ -517,4 +517,17 @@ public final class Rect implements Parcelable { right = in.readInt(); bottom = in.readInt(); } + + /** + * Scales up the rect by the given scale. + * @hide + */ + public void scale(float scale) { + if (scale != 1.0f) { + left *= scale; + top *= scale; + right *= scale; + bottom*= scale; + } + } } diff --git a/test-runner/android/test/mock/MockContext.java b/test-runner/android/test/mock/MockContext.java index e733dd1910b8a..bd39a14857e05 100644 --- a/test-runner/android/test/mock/MockContext.java +++ b/test-runner/android/test/mock/MockContext.java @@ -386,4 +386,12 @@ public class MockContext extends Context { throws PackageManager.NameNotFoundException { throw new UnsupportedOperationException(); } + + /** + * @hide + */ + @Override + public float getApplicationScale() { + throw new UnsupportedOperationException(); + } } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java index baa3d53106efe..f434e14e5968f 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java @@ -1145,4 +1145,12 @@ public final class BridgeContext extends Context { public Context getApplicationContext() { throw new UnsupportedOperationException(); } + + /** + * @hide + */ + @Override + public float getApplicationScale() { + throw new UnsupportedOperationException(); + } }