diff options
| -rw-r--r-- | api/current.txt | 10 | ||||
| -rw-r--r-- | core/java/android/widget/Magnifier.java | 192 | ||||
| -rw-r--r-- | core/java/com/android/internal/util/Preconditions.java | 40 | ||||
| -rw-r--r-- | core/res/res/values/attrs.xml | 11 | ||||
| -rw-r--r-- | core/res/res/values/dimens.xml | 5 | ||||
| -rw-r--r-- | core/res/res/values/styles.xml | 9 | ||||
| -rw-r--r-- | core/res/res/values/symbols.xml | 12 | ||||
| -rw-r--r-- | core/res/res/values/themes.xml | 1 |
8 files changed, 240 insertions, 40 deletions
diff --git a/api/current.txt b/api/current.txt index a578e3e1fa3d..e3a5c1c79b84 100644 --- a/api/current.txt +++ b/api/current.txt @@ -52974,6 +52974,16 @@ package android.widget { method public void update(); } + public static class Magnifier.Builder { + ctor public Magnifier.Builder(android.view.View); + method public android.widget.Magnifier build(); + method public android.widget.Magnifier.Builder setCornerRadius(float); + method public android.widget.Magnifier.Builder setDefaultSourceToMagnifierOffset(int, int); + method public android.widget.Magnifier.Builder setElevation(float); + method public android.widget.Magnifier.Builder setSize(int, int); + method public android.widget.Magnifier.Builder setZoom(float); + } + public class MediaController extends android.widget.FrameLayout { ctor public MediaController(android.content.Context, android.util.AttributeSet); ctor public MediaController(android.content.Context, boolean); diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java index 5734171379a9..84af7d296a54 100644 --- a/core/java/android/widget/Magnifier.java +++ b/core/java/android/widget/Magnifier.java @@ -17,8 +17,10 @@ package android.widget; import android.annotation.FloatRange; +import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.Px; import android.annotation.TestApi; import android.annotation.UiThread; import android.content.Context; @@ -74,7 +76,7 @@ public final class Magnifier { private final int mWindowWidth; // The height of the window containing the magnifier. private final int mWindowHeight; - // The zoom applied to the view region copied to the magnifier window. + // The zoom applied to the view region copied to the magnifier view. private final float mZoom; // The width of the content that will be copied to the magnifier. private final int mSourceWidth; @@ -84,6 +86,10 @@ public final class Magnifier { private final float mWindowElevation; // The corner radius of the window containing the magnifier. private final float mWindowCornerRadius; + // The horizontal offset between the source and window coords when #show(float, float) is used. + private final int mDefaultHorizontalSourceToMagnifierOffset; + // The vertical offset between the source and window coords when #show(float, float) is used. + private final int mDefaultVerticalSourceToMagnifierOffset; // The parent surface for the magnifier surface. private SurfaceInfo mParentSurface; // The surface where the content will be copied from. @@ -110,17 +116,27 @@ public final class Magnifier { * Initializes a magnifier. * * @param view the view for which this magnifier is attached + * + * @see Builder */ public Magnifier(@NonNull View view) { - mView = Preconditions.checkNotNull(view); - final Context context = mView.getContext(); - mWindowWidth = context.getResources().getDimensionPixelSize(R.dimen.magnifier_width); - mWindowHeight = context.getResources().getDimensionPixelSize(R.dimen.magnifier_height); - mWindowElevation = context.getResources().getDimension(R.dimen.magnifier_elevation); - mWindowCornerRadius = getDeviceDefaultDialogCornerRadius(); - mZoom = context.getResources().getFloat(R.dimen.magnifier_zoom_scale); + this(new Builder(view)); + } + + private Magnifier(@NonNull Builder params) { + // Copy params from builder. + mView = params.mView; + mWindowWidth = params.mWidth; + mWindowHeight = params.mHeight; + mZoom = params.mZoom; mSourceWidth = Math.round(mWindowWidth / mZoom); mSourceHeight = Math.round(mWindowHeight / mZoom); + mWindowElevation = params.mElevation; + mWindowCornerRadius = params.mCornerRadius; + mDefaultHorizontalSourceToMagnifierOffset = + params.mHorizontalDefaultSourceToMagnifierOffset; + mDefaultVerticalSourceToMagnifierOffset = + params.mVerticalDefaultSourceToMagnifierOffset; // The view's surface coordinates will not be updated until the magnifier is first shown. mViewCoordinatesInSurface = new int[2]; } @@ -130,21 +146,6 @@ public final class Magnifier { } /** - * Returns the device default theme dialog corner radius attribute. - * We retrieve this from the device default theme to avoid - * using the values set in the custom application themes. - */ - private float getDeviceDefaultDialogCornerRadius() { - final Context deviceDefaultContext = - new ContextThemeWrapper(mView.getContext(), R.style.Theme_DeviceDefault); - final TypedArray ta = deviceDefaultContext.obtainStyledAttributes( - new int[]{android.R.attr.dialogCornerRadius}); - final float dialogCornerRadius = ta.getDimension(0, 0); - ta.recycle(); - return dialogCornerRadius; - } - - /** * Shows the magnifier on the screen. * * @param sourceCenterX horizontal coordinate of the center point of the source rectangle that @@ -156,9 +157,9 @@ public final class Magnifier { */ public void show(@FloatRange(from = 0) float sourceCenterX, @FloatRange(from = 0) float sourceCenterY) { - final int verticalOffset = mView.getContext().getResources() - .getDimensionPixelSize(R.dimen.magnifier_offset); - show(sourceCenterX, sourceCenterY, sourceCenterX, sourceCenterY - verticalOffset); + show(sourceCenterX, sourceCenterY, + sourceCenterX + mDefaultHorizontalSourceToMagnifierOffset, + sourceCenterY + mDefaultVerticalSourceToMagnifierOffset); } /** @@ -253,23 +254,24 @@ public final class Magnifier { } /** - * @return The width of the magnifier window, in pixels. + * @return the width of the magnifier window, in pixels */ public int getWidth() { return mWindowWidth; } /** - * @return The height of the magnifier window, in pixels. + * @return the height of the magnifier window, in pixels */ public int getHeight() { return mWindowHeight; } /** - * @return The zoom applied to the magnified view region copied to the magnifier window. + * Returns the zoom to be applied to the magnified view region copied to the magnifier. * If the zoom is x and the magnifier window size is (width, height), the original size - * of the content copied in the magnifier will be (width / x, height / x). + * of the content being magnified will be (width / x, height / x). + * @return the zoom applied to the content */ public float getZoom() { return mZoom; @@ -278,7 +280,7 @@ public final class Magnifier { /** * @hide * - * @return The top left coordinates of the magnifier, relative to the parent window. + * @return the top left coordinates of the magnifier, relative to the parent window */ @Nullable public Point getWindowCoords() { @@ -750,6 +752,134 @@ public final class Magnifier { } } + /** + * Builder class for {@link Magnifier} objects. + */ + public static class Builder { + private @NonNull View mView; + private @Px @IntRange(from = 0) int mWidth; + private @Px @IntRange(from = 0) int mHeight; + private float mZoom; + private @FloatRange(from = 0f) float mElevation; + private @FloatRange(from = 0f) float mCornerRadius; + private int mHorizontalDefaultSourceToMagnifierOffset; + private int mVerticalDefaultSourceToMagnifierOffset; + + /** + * Construct a new builder for {@link Magnifier} objects. + * @param view the view this magnifier is attached to + */ + public Builder(@NonNull View view) { + mView = Preconditions.checkNotNull(view); + applyDefaults(); + } + + private void applyDefaults() { + final Context context = mView.getContext(); + final TypedArray a = context.obtainStyledAttributes(null, R.styleable.Magnifier, + R.attr.magnifierStyle, 0); + mWidth = a.getDimensionPixelSize(R.styleable.Magnifier_magnifierWidth, 0); + mHeight = a.getDimensionPixelSize(R.styleable.Magnifier_magnifierHeight, 0); + mElevation = a.getDimension(R.styleable.Magnifier_magnifierElevation, 0); + mCornerRadius = getDeviceDefaultDialogCornerRadius(); + mZoom = a.getFloat(R.styleable.Magnifier_magnifierZoom, 0); + mHorizontalDefaultSourceToMagnifierOffset = + a.getDimensionPixelSize(R.styleable.Magnifier_magnifierHorizontalOffset, 0); + mVerticalDefaultSourceToMagnifierOffset = + a.getDimensionPixelSize(R.styleable.Magnifier_magnifierVerticalOffset, 0); + a.recycle(); + } + + /** + * Returns the device default theme dialog corner radius attribute. + * We retrieve this from the device default theme to avoid + * using the values set in the custom application themes. + */ + private float getDeviceDefaultDialogCornerRadius() { + final Context deviceDefaultContext = + new ContextThemeWrapper(mView.getContext(), R.style.Theme_DeviceDefault); + final TypedArray ta = deviceDefaultContext.obtainStyledAttributes( + new int[]{android.R.attr.dialogCornerRadius}); + final float dialogCornerRadius = ta.getDimension(0, 0); + ta.recycle(); + return dialogCornerRadius; + } + + /** + * Sets the size of the magnifier window, in pixels. Defaults to (100dp, 48dp). + * Note that the size of the content being magnified and copied to the magnifier + * will be computed as (window width / zoom, window height / zoom). + * @param width the window width to be set + * @param height the window height to be set + */ + public Builder setSize(@Px @IntRange(from = 0) int width, + @Px @IntRange(from = 0) int height) { + Preconditions.checkArgumentPositive(width, "Width should be positive"); + Preconditions.checkArgumentPositive(height, "Height should be positive"); + mWidth = width; + mHeight = height; + return this; + } + + /** + * Sets the zoom to be applied to the chosen content before being copied to the magnifier. + * A content of size (content_width, content_height) will be magnified to + * (content_width * zoom, content_height * zoom), which will coincide with the size + * of the magnifier. A zoom of 1 will translate to no magnification (the content will + * be just copied to the magnifier with no scaling). The zoom defaults to 1.25. + * @param zoom the zoom to be set + */ + public Builder setZoom(@FloatRange(from = 0f) float zoom) { + Preconditions.checkArgumentPositive(zoom, "Zoom should be positive"); + mZoom = zoom; + return this; + } + + /** + * Sets the elevation of the magnifier window, in pixels. Defaults to 4dp. + * @param elevation the elevation to be set + */ + public Builder setElevation(@Px @FloatRange(from = 0) float elevation) { + Preconditions.checkArgumentNonNegative(elevation, "Elevation should be non-negative"); + mElevation = elevation; + return this; + } + + /** + * Sets the corner radius of the magnifier window, in pixels. + * Defaults to the corner radius defined in the device default theme. + * @param cornerRadius the corner radius to be set + */ + public Builder setCornerRadius(@Px @FloatRange(from = 0) float cornerRadius) { + Preconditions.checkArgumentNonNegative(cornerRadius, + "Corner radius should be non-negative"); + mCornerRadius = cornerRadius; + return this; + } + + /** + * Sets an offset, in pixels, that should be added to the content source center to obtain + * the position of the magnifier window, when the {@link #show(float, float)} + * method is called. The offset is ignored when {@link #show(float, float, float, float)} + * is used. The offset can be negative, and it defaults to (0dp, -42dp). + * @param horizontalOffset the horizontal component of the offset + * @param verticalOffset the vertical component of the offset + */ + public Builder setDefaultSourceToMagnifierOffset(@Px int horizontalOffset, + @Px int verticalOffset) { + mHorizontalDefaultSourceToMagnifierOffset = horizontalOffset; + mVerticalDefaultSourceToMagnifierOffset = verticalOffset; + return this; + } + + /** + * Builds a {@link Magnifier} instance based on the configuration of this {@link Builder}. + */ + public @NonNull Magnifier build() { + return new Magnifier(this); + } + } + // The rest of the file consists of test APIs. /** diff --git a/core/java/com/android/internal/util/Preconditions.java b/core/java/com/android/internal/util/Preconditions.java index 91c76afdf5b2..2c6a0e06f6e7 100644 --- a/core/java/com/android/internal/util/Preconditions.java +++ b/core/java/com/android/internal/util/Preconditions.java @@ -192,7 +192,7 @@ public class Preconditions { } /** - * Ensures that that the argument numeric value is non-negative. + * Ensures that that the argument numeric value is non-negative (greater than or equal to 0). * * @param value a numeric int value * @param errorMessage the exception message to use if the check fails @@ -209,7 +209,7 @@ public class Preconditions { } /** - * Ensures that that the argument numeric value is non-negative. + * Ensures that that the argument numeric value is non-negative (greater than or equal to 0). * * @param value a numeric int value * @@ -225,7 +225,7 @@ public class Preconditions { } /** - * Ensures that that the argument numeric value is non-negative. + * Ensures that that the argument numeric value is non-negative (greater than or equal to 0). * * @param value a numeric long value * @return the validated numeric value @@ -240,7 +240,7 @@ public class Preconditions { } /** - * Ensures that that the argument numeric value is non-negative. + * Ensures that that the argument numeric value is non-negative (greater than or equal to 0). * * @param value a numeric long value * @param errorMessage the exception message to use if the check fails @@ -256,7 +256,7 @@ public class Preconditions { } /** - * Ensures that that the argument numeric value is positive. + * Ensures that that the argument numeric value is positive (greater than 0). * * @param value a numeric int value * @param errorMessage the exception message to use if the check fails @@ -272,6 +272,36 @@ public class Preconditions { } /** + * Ensures that the argument floating point value is non-negative (greater than or equal to 0). + * @param value a floating point value + * @param errorMessage the exteption message to use if the check fails + * @return the validated numeric value + * @throws IllegalArgumentException if {@code value} was negative + */ + public static float checkArgumentNonNegative(final float value, final String errorMessage) { + if (value < 0) { + throw new IllegalArgumentException(errorMessage); + } + + return value; + } + + /** + * Ensures that the argument floating point value is positive (greater than 0). + * @param value a floating point value + * @param errorMessage the exteption message to use if the check fails + * @return the validated numeric value + * @throws IllegalArgumentException if {@code value} was not positive + */ + public static float checkArgumentPositive(final float value, final String errorMessage) { + if (value <= 0) { + throw new IllegalArgumentException(errorMessage); + } + + return value; + } + + /** * Ensures that the argument floating point value is a finite number. * * <p>A finite number is defined to be both representable (that is, not NaN) and diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 2f710bf4dd29..64a9e6dc6a85 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -759,6 +759,8 @@ <attr name="contextPopupMenuStyle" format="reference" /> <!-- Default StackView style. --> <attr name="stackViewStyle" format="reference" /> + <!-- Magnifier style. --> + <attr name="magnifierStyle" format="reference" /> <!-- Default style for the FragmentBreadCrumbs widget. This widget is deprecated starting in API level 21 ({@link android.os.Build.VERSION_CODES#.L}). --> @@ -8921,4 +8923,13 @@ </declare-styleable> <attr name="lockPatternStyle" format="reference" /> + + <declare-styleable name="Magnifier"> + <attr name="magnifierWidth" format="dimension" /> + <attr name="magnifierHeight" format="dimension" /> + <attr name="magnifierZoom" format="float" /> + <attr name="magnifierElevation" format="dimension" /> + <attr name="magnifierVerticalOffset" format="dimension" /> + <attr name="magnifierHorizontalOffset" format="dimension" /> + </declare-styleable> </resources> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 471170bbe93b..391b1da41518 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -555,8 +555,9 @@ <dimen name="magnifier_width">100dp</dimen> <dimen name="magnifier_height">48dp</dimen> <dimen name="magnifier_elevation">4dp</dimen> - <dimen name="magnifier_offset">42dp</dimen> - <item type="dimen" format="float" name="magnifier_zoom_scale">1.25</item> + <dimen name="magnifier_vertical_offset">-42dp</dimen> + <dimen name="magnifier_horizontal_offset">0dp</dimen> + <item type="dimen" format="float" name="magnifier_zoom">1.25</item> <dimen name="chooser_grid_padding">0dp</dimen> <!-- Spacing around the background change frome service to non-service --> diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index e1db71f701b6..fafcf935b7d6 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -799,6 +799,15 @@ please see styles_device_defaults.xml. <item name="textOff">@string/capital_off</item> </style> + <style name="Widget.Magnifier"> + <item name="magnifierWidth">@dimen/magnifier_width</item> + <item name="magnifierHeight">@dimen/magnifier_height</item> + <item name="magnifierZoom">@dimen/magnifier_zoom</item> + <item name="magnifierElevation">@dimen/magnifier_elevation</item> + <item name="magnifierVerticalOffset">@dimen/magnifier_vertical_offset</item> + <item name="magnifierHorizontalOffset">@dimen/magnifier_horizontal_offset</item> + </style> + <!-- Text Appearances --> <eat-comment /> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 140bb7d8704a..c4d498b09138 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2625,8 +2625,16 @@ <java-symbol type="dimen" name="magnifier_width" /> <java-symbol type="dimen" name="magnifier_height" /> <java-symbol type="dimen" name="magnifier_elevation" /> - <java-symbol type="dimen" name="magnifier_zoom_scale" /> - <java-symbol type="dimen" name="magnifier_offset" /> + <java-symbol type="dimen" name="magnifier_zoom" /> + <java-symbol type="dimen" name="magnifier_vertical_offset" /> + <java-symbol type="dimen" name="magnifier_horizontal_offset" /> + <java-symbol type="attr" name="magnifierWidth" /> + <java-symbol type="attr" name="magnifierHeight" /> + <java-symbol type="attr" name="magnifierElevation" /> + <java-symbol type="attr" name="magnifierZoom" /> + <java-symbol type="attr" name="magnifierVerticalOffset" /> + <java-symbol type="attr" name="magnifierHorizontalOffset" /> + <java-symbol type="attr" name="magnifierStyle" /> <java-symbol type="string" name="date_picker_prev_month_button" /> <java-symbol type="string" name="date_picker_next_month_button" /> diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml index 483381660655..4a2f06a3bc10 100644 --- a/core/res/res/values/themes.xml +++ b/core/res/res/values/themes.xml @@ -309,6 +309,7 @@ please see themes_device_defaults.xml. <item name="activityChooserViewStyle">@style/Widget.ActivityChooserView</item> <item name="fragmentBreadCrumbsStyle">@style/Widget.FragmentBreadCrumbs</item> <item name="contextPopupMenuStyle">?attr/popupMenuStyle</item> + <item name="magnifierStyle">@style/Widget.Magnifier</item> <!-- Preference styles --> <item name="preferenceScreenStyle">@style/Preference.PreferenceScreen</item> |