diff options
-rw-r--r-- | core/java/android/widget/Editor.java | 23 | ||||
-rw-r--r-- | core/java/android/widget/TextView.java | 30 | ||||
-rw-r--r-- | core/java/com/android/internal/widget/Magnifier.java | 125 | ||||
-rw-r--r-- | core/res/res/layout/magnifier.xml | 19 | ||||
-rw-r--r-- | core/res/res/values/dimens.xml | 2 | ||||
-rw-r--r-- | core/res/res/values/symbols.xml | 1 |
6 files changed, 64 insertions, 136 deletions
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index af0959239b6f..384f4f8393cf 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -476,17 +476,6 @@ public class Editor { stopTextActionModeWithPreservingSelection(); } - void invalidateMagnifier() { - final DisplayMetrics dm = mTextView.getResources().getDisplayMetrics(); - invalidateMagnifier(0, 0, dm.widthPixels, dm.heightPixels); - } - - void invalidateMagnifier(final float l, final float t, final float r, final float b) { - if (mMagnifier != null) { - mTextView.post(() -> mMagnifier.invalidate(new RectF(l, t, r, b))); - } - } - private void discardTextDisplayLists() { if (mTextRenderNodes != null) { for (int i = 0; i < mTextRenderNodes.length; i++) { @@ -4550,17 +4539,15 @@ public class Editor { final Layout layout = mTextView.getLayout(); final int lineNumber = layout.getLineForOffset(offset); // Horizontally snap to character offset. - final float xPosInView = getHorizontal(mTextView.getLayout(), offset); + final float xPosInView = getHorizontal(mTextView.getLayout(), offset) + + mTextView.getTotalPaddingLeft() - mTextView.getScrollX(); // Vertically snap to middle of current line. final float yPosInView = (mTextView.getLayout().getLineTop(lineNumber) - + mTextView.getLayout().getLineBottom(lineNumber)) / 2.0f; - final int[] coordinatesOnScreen = new int[2]; - mTextView.getLocationOnScreen(coordinatesOnScreen); - final float centerXOnScreen = mTextView.convertViewToScreenCoord(xPosInView, true); - final float centerYOnScreen = mTextView.convertViewToScreenCoord(yPosInView, false); + + mTextView.getLayout().getLineBottom(lineNumber)) / 2.0f + + mTextView.getTotalPaddingTop() - mTextView.getScrollY(); suspendBlink(); - mMagnifier.show(centerXOnScreen, centerYOnScreen, MAGNIFIER_ZOOM); + mMagnifier.show(xPosInView, yPosInView, MAGNIFIER_ZOOM); } protected final void dismissMagnifier() { diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index ce805526e4ad..d9bc51fffd6a 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -9219,36 +9219,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } - @Override - public void invalidate() { - super.invalidate(); - - if (mEditor != null) { - mEditor.invalidateMagnifier(); - } - } - - @Override - public void invalidate(int l, int t, int r, int b) { - super.invalidate(l, t, r, b); - - if (mEditor != null) { - mEditor.invalidateMagnifier( - convertViewToScreenCoord(l, true /* isHorizontal */), - convertViewToScreenCoord(t, false /* isHorizontal */), - convertViewToScreenCoord(r, true /* isHorizontal */), - convertViewToScreenCoord(b, false /* isHorizontal */)); - } - } - - float convertViewToScreenCoord(float viewCoord, boolean isHorizontal) { - final int[] coordinatesOnScreen = new int[2]; - getLocationOnScreen(coordinatesOnScreen); - return isHorizontal - ? viewCoord + getTotalPaddingLeft() - getScrollX() + coordinatesOnScreen[0] - : viewCoord + getTotalPaddingTop() - getScrollY() + coordinatesOnScreen[1]; - } - /** * @return whether or not the cursor is visible (assuming this TextView is editable) * diff --git a/core/java/com/android/internal/widget/Magnifier.java b/core/java/com/android/internal/widget/Magnifier.java index 9bc0778d9be6..6d54d7b8f1e2 100644 --- a/core/java/com/android/internal/widget/Magnifier.java +++ b/core/java/com/android/internal/widget/Magnifier.java @@ -22,9 +22,7 @@ import android.annotation.UiThread; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Point; -import android.graphics.PointF; import android.graphics.Rect; -import android.graphics.RectF; import android.os.Handler; import android.util.Log; import android.view.Gravity; @@ -38,13 +36,15 @@ import android.widget.PopupWindow; import com.android.internal.R; import com.android.internal.util.Preconditions; +import java.util.Timer; +import java.util.TimerTask; + /** * Android magnifier widget. Can be used by any view which is attached to window. */ public final class Magnifier { private static final String LOG_TAG = "magnifier"; - // Use this to specify that a previous configuration value does not exist. - private static final int INEXISTENT_PREVIOUS_CONFIG_VALUE = -1; + private static final int MAGNIFIER_REFRESH_RATE_MS = 33; // ~30fps // The view for which this magnifier is attached. private final View mView; // The window containing the magnifier. @@ -62,15 +62,10 @@ public final class Magnifier { // The callback of the pixel copy request will be invoked on this Handler when // the copy is finished. private final Handler mPixelCopyHandler = Handler.getMain(); - - private RectF mTmpRectF; - - // Variables holding previous states, used for detecting redundant calls and invalidation. - private Point mPrevStartCoordsOnScreen = new Point( - INEXISTENT_PREVIOUS_CONFIG_VALUE, INEXISTENT_PREVIOUS_CONFIG_VALUE); - private PointF mPrevCenterCoordsOnScreen = new PointF( - INEXISTENT_PREVIOUS_CONFIG_VALUE, INEXISTENT_PREVIOUS_CONFIG_VALUE); - private float mPrevScale = INEXISTENT_PREVIOUS_CONFIG_VALUE; + // Current magnification scale. + private float mScale; + // Timer used to schedule the copy task. + private Timer mTimer; /** * Initializes a magnifier. @@ -82,6 +77,7 @@ public final class Magnifier { mView = Preconditions.checkNotNull(view); final Context context = mView.getContext(); final View content = LayoutInflater.from(context).inflate(R.layout.magnifier, null); + content.findViewById(R.id.magnifier_inner).setClipToOutline(true); mWindowWidth = context.getResources().getDimensionPixelSize(R.dimen.magnifier_width); mWindowHeight = context.getResources().getDimensionPixelSize(R.dimen.magnifier_height); final float elevation = context.getResources().getDimension(R.dimen.magnifier_elevation); @@ -101,15 +97,15 @@ public final class Magnifier { /** * Shows the magnifier on the screen. * - * @param centerXOnScreen horizontal coordinate of the center point of the magnifier source. The - * lower end is clamped to 0 - * @param centerYOnScreen vertical coordinate of the center point of the magnifier source. The - * lower end is clamped to 0 + * @param xPosInView horizontal coordinate of the center point of the magnifier source relative + * to the view. The lower end is clamped to 0 + * @param yPosInView vertical coordinate of the center point of the magnifier source + * relative to the view. The lower end is clamped to 0 * @param scale the scale at which the magnifier zooms on the source content. The * lower end is clamped to 1 and the higher end to 4 */ - public void show(@FloatRange(from=0) float centerXOnScreen, - @FloatRange(from=0) float centerYOnScreen, + public void show(@FloatRange(from=0) float xPosInView, + @FloatRange(from=0) float yPosInView, @FloatRange(from=1, to=4) float scale) { if (scale > 4) { scale = 4; @@ -119,27 +115,29 @@ public final class Magnifier { scale = 1; } - if (centerXOnScreen < 0) { - centerXOnScreen = 0; + if (xPosInView < 0) { + xPosInView = 0; } - if (centerYOnScreen < 0) { - centerYOnScreen = 0; + if (yPosInView < 0) { + yPosInView = 0; } - showInternal(centerXOnScreen, centerYOnScreen, scale, false); - } - - private void showInternal(@FloatRange(from=0) float centerXOnScreen, - @FloatRange(from=0) float centerYOnScreen, - @FloatRange(from=1, to=4) float scale, - boolean forceShow) { - if (mPrevScale != scale) { + if (mScale != scale) { resizeBitmap(scale); - mPrevScale = scale; } - configureCoordinates(centerXOnScreen, centerYOnScreen); - maybePerformPixelCopy(scale, forceShow); + mScale = scale; + configureCoordinates(xPosInView, yPosInView); + + if (mTimer == null) { + mTimer = new Timer(); + mTimer.schedule(new TimerTask() { + @Override + public void run() { + performPixelCopy(); + } + }, 0 /* delay */, MAGNIFIER_REFRESH_RATE_MS); + } if (mWindow.isShowing()) { mWindow.update(mWindowCoords.x, mWindowCoords.y, mWindow.getWidth(), @@ -148,9 +146,6 @@ public final class Magnifier { mWindow.showAtLocation(mView.getRootView(), Gravity.NO_GRAVITY, mWindowCoords.x, mWindowCoords.y); } - - mPrevCenterCoordsOnScreen.x = centerXOnScreen; - mPrevCenterCoordsOnScreen.y = centerYOnScreen; } /** @@ -159,36 +154,10 @@ public final class Magnifier { public void dismiss() { mWindow.dismiss(); - mPrevStartCoordsOnScreen.x = INEXISTENT_PREVIOUS_CONFIG_VALUE; - mPrevStartCoordsOnScreen.y = INEXISTENT_PREVIOUS_CONFIG_VALUE; - mPrevCenterCoordsOnScreen.x = INEXISTENT_PREVIOUS_CONFIG_VALUE; - mPrevCenterCoordsOnScreen.y = INEXISTENT_PREVIOUS_CONFIG_VALUE; - mPrevScale = INEXISTENT_PREVIOUS_CONFIG_VALUE; - } - - /** - * Forces the magnifier to update content by taking and showing a new snapshot using the - * previous coordinates. It does this only if the magnifier is showing and the dirty rectangle - * intersects the rectangle which holds the content to be magnified. - * - * @param dirtyRectOnScreen the rectangle representing the screen bounds of the dirty region - */ - public void invalidate(RectF dirtyRectOnScreen) { - if (mWindow.isShowing() && mPrevCenterCoordsOnScreen.x != INEXISTENT_PREVIOUS_CONFIG_VALUE - && mPrevCenterCoordsOnScreen.y != INEXISTENT_PREVIOUS_CONFIG_VALUE - && mPrevScale != INEXISTENT_PREVIOUS_CONFIG_VALUE) { - // Update the current showing RectF. - mTmpRectF = new RectF(mPrevStartCoordsOnScreen.x, - mPrevStartCoordsOnScreen.y, - mPrevStartCoordsOnScreen.x + mBitmap.getWidth(), - mPrevStartCoordsOnScreen.y + mBitmap.getHeight()); - - // Update only if we are currently showing content that has been declared as invalid. - if (RectF.intersects(dirtyRectOnScreen, mTmpRectF)) { - // Update the contents shown in the magnifier. - showInternal(mPrevCenterCoordsOnScreen.x, mPrevCenterCoordsOnScreen.y, mPrevScale, - true /* forceShow */); - } + if (mTimer != null) { + mTimer.cancel(); + mTimer.purge(); + mTimer = null; } } @@ -213,7 +182,12 @@ public final class Magnifier { getImageView().setImageBitmap(mBitmap); } - private void configureCoordinates(float posXOnScreen, float posYOnScreen) { + private void configureCoordinates(float xPosInView, float yPosInView) { + final int[] coordinatesOnScreen = new int[2]; + mView.getLocationOnScreen(coordinatesOnScreen); + final float posXOnScreen = xPosInView + coordinatesOnScreen[0]; + final float posYOnScreen = yPosInView + coordinatesOnScreen[1]; + mCenterZoomCoords.x = (int) posXOnScreen; mCenterZoomCoords.y = (int) posYOnScreen; @@ -223,7 +197,7 @@ public final class Magnifier { mWindowCoords.y = mCenterZoomCoords.y - mWindowHeight / 2 - verticalMagnifierOffset; } - private void maybePerformPixelCopy(final float scale, final boolean forceShow) { + private void performPixelCopy() { final int startY = mCenterZoomCoords.y - mBitmap.getHeight() / 2; int rawStartX = mCenterZoomCoords.x - mBitmap.getWidth() / 2; @@ -234,13 +208,6 @@ public final class Magnifier { rawStartX = mView.getWidth() - mBitmap.getWidth(); } - if (!forceShow && rawStartX == mPrevStartCoordsOnScreen.x - && startY == mPrevStartCoordsOnScreen.y - && scale == mPrevScale) { - // Skip, we are already showing the desired content. - return; - } - final int startX = rawStartX; final ViewRootImpl viewRootImpl = mView.getViewRootImpl(); @@ -251,11 +218,7 @@ public final class Magnifier { new Rect(startX, startY, startX + mBitmap.getWidth(), startY + mBitmap.getHeight()), mBitmap, - result -> { - getImageView().invalidate(); - mPrevStartCoordsOnScreen.x = startX; - mPrevStartCoordsOnScreen.y = startY; - }, + result -> getImageView().invalidate(), mPixelCopyHandler); } else { Log.d(LOG_TAG, "Could not perform PixelCopy request"); diff --git a/core/res/res/layout/magnifier.xml b/core/res/res/layout/magnifier.xml index 181e5e54bb00..d6cd8b4e2737 100644 --- a/core/res/res/layout/magnifier.xml +++ b/core/res/res/layout/magnifier.xml @@ -18,10 +18,17 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:background="?android:attr/floatingToolbarPopupBackgroundDrawable"> - <ImageView - android:id="@+id/magnifier_image" - android:layout_width="match_parent" - android:layout_height="match_parent" /> + android:layout_height="wrap_content"> + <LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/magnifier_inner" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:background="?android:attr/floatingToolbarPopupBackgroundDrawable" + android:elevation="@android:dimen/magnifier_elevation"> + <ImageView + android:id="@+id/magnifier_image" + android:layout_width="match_parent" + android:layout_height="match_parent" /> + </LinearLayout> </LinearLayout> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 14069e779939..08e2233ebba7 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -521,7 +521,7 @@ <dimen name="content_rect_bottom_clip_allowance">20dp</dimen> <!-- Magnifier dimensions --> - <dimen name="magnifier_width">200dp</dimen> + <dimen name="magnifier_width">164dp</dimen> <dimen name="magnifier_height">48dp</dimen> <dimen name="magnifier_elevation">2dp</dimen> <dimen name="magnifier_offset">42dp</dimen> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 46a6e6efca45..1627a0eca6dd 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2479,6 +2479,7 @@ <!-- Magnifier --> <java-symbol type="id" name="magnifier_image" /> + <java-symbol type="id" name="magnifier_inner" /> <java-symbol type="layout" name="magnifier" /> <java-symbol type="dimen" name="magnifier_width" /> <java-symbol type="dimen" name="magnifier_height" /> |