[Magnifier-46] Add builder for magnifier objects

The CL adds a builder class that enables creating Magnifier objects with
custom properties. The defaults of the builder remain the predefined
values of the magnifier in P.

Bug: 72211470
Test: manual testing
Test: atest CtsWidgetTestCases:android.widget.cts.MagnifierTest
Change-Id: I066082fb17cfb8c483c49b7011abfa9dca9de77a
This commit is contained in:
Mihai Popa
2018-07-18 14:52:26 +01:00
parent 7433a07046
commit 469aba83bb
8 changed files with 240 additions and 40 deletions

View File

@@ -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);

View File

@@ -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];
}
@@ -129,21 +145,6 @@ public final class Magnifier {
sPixelCopyHandlerThread.start();
}
/**
* 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.
*
@@ -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.
/**

View File

@@ -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
@@ -271,6 +271,36 @@ public class Preconditions {
return value;
}
/**
* 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.
*

View File

@@ -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>

View File

@@ -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 -->

View File

@@ -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 />

View File

@@ -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" />

View File

@@ -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>