Merge "Fill out RemoteViews methods for editing LayoutParams."

This commit is contained in:
Jeff DeCew
2021-01-08 14:53:47 +00:00
committed by Android (Google) Code Review
4 changed files with 519 additions and 66 deletions

View File

@@ -83,6 +83,7 @@ import android.util.ArraySet;
import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;
import android.util.TypedValue;
import android.util.proto.ProtoOutputStream;
import android.view.ContextThemeWrapper;
import android.view.Gravity;
@@ -4920,7 +4921,8 @@ public class Notification implements Parcelable
setTextViewColorPrimary(contentView, R.id.title, p);
contentView.setViewLayoutWidth(R.id.title, showProgress
? ViewGroup.LayoutParams.WRAP_CONTENT
: ViewGroup.LayoutParams.MATCH_PARENT);
: ViewGroup.LayoutParams.MATCH_PARENT,
TypedValue.COMPLEX_UNIT_PX);
}
if (p.text != null && p.text.length() != 0) {
int textId = showProgress ? com.android.internal.R.id.text_line_1
@@ -5365,8 +5367,9 @@ public class Notification implements Parcelable
final boolean snoozeEnabled = mContext.getContentResolver() != null
&& (Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.SHOW_NOTIFICATION_SNOOZE, 0) == 1);
big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target,
snoozeEnabled ? 0 : R.dimen.notification_content_margin);
int bottomMarginDimen = snoozeEnabled ? 0 : R.dimen.notification_content_margin;
big.setViewLayoutMarginDimen(R.id.notification_action_list_margin_target,
RemoteViews.MARGIN_BOTTOM, bottomMarginDimen);
}
private static List<Notification.Action> filterOutContextualActions(
@@ -5398,7 +5401,8 @@ public class Notification implements Parcelable
if (N > 0) {
big.setViewVisibility(R.id.actions_container, View.VISIBLE);
big.setViewVisibility(R.id.actions, View.VISIBLE);
big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target, 0);
big.setViewLayoutMarginDimen(R.id.notification_action_list_margin_target,
RemoteViews.MARGIN_BOTTOM, 0);
if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS;
for (int i=0; i<N; i++) {
Action action = nonContextualActions.get(i);
@@ -7797,8 +7801,8 @@ public class Notification implements Parcelable
// also update the end margin if there is an image
// NOTE: This template doesn't support moving this icon to the left, so we don't
// need to fully apply the MarginSet
contentView.setViewLayoutMarginEnd(R.id.notification_messaging,
bindResult.mHeadingExtraMarginSet.getValue());
contentView.setViewLayoutMargin(R.id.notification_messaging, RemoteViews.MARGIN_END,
bindResult.mHeadingExtraMarginSet.getValue(), TypedValue.COMPLEX_UNIT_PX);
}
contentView.setInt(R.id.status_bar_latest_event_content, "setLayoutColor",
mBuilder.isColorized(p)
@@ -8622,7 +8626,8 @@ public class Notification implements Parcelable
if (mBuilder.mN.hasLargeIcon()) {
endMargin = R.dimen.notification_media_image_margin_end;
}
view.setViewLayoutMarginEndDimen(R.id.notification_main_column, endMargin);
view.setViewLayoutMarginDimen(R.id.notification_main_column,
RemoteViews.MARGIN_END, endMargin);
return view;
}
@@ -8659,8 +8664,8 @@ public class Notification implements Parcelable
private void handleImage(RemoteViews contentView) {
if (mBuilder.mN.hasLargeIcon()) {
contentView.setViewLayoutMarginEndDimen(R.id.line1, 0);
contentView.setViewLayoutMarginEndDimen(R.id.text, 0);
contentView.setViewLayoutMarginDimen(R.id.line1, RemoteViews.MARGIN_END, 0);
contentView.setViewLayoutMarginDimen(R.id.text, RemoteViews.MARGIN_END, 0);
}
}
@@ -11089,7 +11094,8 @@ public class Notification implements Parcelable
if (viewId == R.id.notification_header) {
views.setInt(R.id.notification_header, "setTopLineExtraMarginEnd", marginEnd);
} else {
views.setViewLayoutMarginEnd(viewId, marginEnd);
views.setViewLayoutMargin(viewId, RemoteViews.MARGIN_END,
marginEnd, TypedValue.COMPLEX_UNIT_PX);
}
if (mRightIconVisible) {
views.setIntTag(viewId, R.id.tag_margin_end_when_icon_visible,

View File

@@ -17,8 +17,14 @@
package android.util;
import android.annotation.AnyRes;
import android.annotation.FloatRange;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.content.pm.ActivityInfo.Config;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Container for a dynamically typed data value. Primarily used with
* {@link android.content.res.Resources} for holding resource values.
@@ -95,6 +101,18 @@ public class TypedValue {
* defined below. */
public static final int COMPLEX_UNIT_MASK = 0xf;
/** @hide **/
@IntDef(prefix = "COMPLEX_UNIT_", value = {
COMPLEX_UNIT_PX,
COMPLEX_UNIT_DIP,
COMPLEX_UNIT_SP,
COMPLEX_UNIT_PT,
COMPLEX_UNIT_IN,
COMPLEX_UNIT_MM,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ComplexDimensionUnit {}
/** {@link #TYPE_DIMENSION} complex unit: Value is raw pixels. */
public static final int COMPLEX_UNIT_PX = 0;
/** {@link #TYPE_DIMENSION} complex unit: Value is Device Independent
@@ -381,7 +399,7 @@ public class TypedValue {
* @return The complex floating point value multiplied by the appropriate
* metrics depending on its unit.
*/
public static float applyDimension(int unit, float value,
public static float applyDimension(@ComplexDimensionUnit int unit, float value,
DisplayMetrics metrics)
{
switch (unit) {
@@ -416,6 +434,130 @@ public class TypedValue {
return complexToDimension(data, metrics);
}
/**
* Construct a complex data integer. This validates the radix and the magnitude of the
* mantissa, and sets the {@link TypedValue#COMPLEX_MANTISSA_MASK} and
* {@link TypedValue#COMPLEX_RADIX_MASK} components as provided. The units are not set.
**
* @param mantissa an integer representing the mantissa.
* @param radix a radix option, e.g. {@link TypedValue#COMPLEX_RADIX_23p0}.
* @return A complex data integer representing the value.
* @hide
*/
private static int createComplex(@IntRange(from = -0x800000, to = 0x7FFFFF) int mantissa,
int radix) {
if (mantissa < -0x800000 || mantissa >= 0x800000) {
throw new IllegalArgumentException("Magnitude of mantissa is too large: " + mantissa);
}
if (radix < TypedValue.COMPLEX_RADIX_23p0 || radix > TypedValue.COMPLEX_RADIX_0p23) {
throw new IllegalArgumentException("Invalid radix: " + radix);
}
return ((mantissa & TypedValue.COMPLEX_MANTISSA_MASK) << TypedValue.COMPLEX_MANTISSA_SHIFT)
| (radix << TypedValue.COMPLEX_RADIX_SHIFT);
}
/**
* Convert a base value to a complex data integer. This sets the {@link
* TypedValue#COMPLEX_MANTISSA_MASK} and {@link TypedValue#COMPLEX_RADIX_MASK} fields of the
* data to create a floating point representation of the given value. The units are not set.
*
* <p>This is the inverse of {@link TypedValue#complexToFloat(int)}.
*
* @param value An integer value.
* @return A complex data integer representing the value.
* @hide
*/
public static int intToComplex(int value) {
if (value < -0x800000 || value >= 0x800000) {
throw new IllegalArgumentException("Magnitude of the value is too large: " + value);
}
return createComplex(value, TypedValue.COMPLEX_RADIX_23p0);
}
/**
* Convert a base value to a complex data integer. This sets the {@link
* TypedValue#COMPLEX_MANTISSA_MASK} and {@link TypedValue#COMPLEX_RADIX_MASK} fields of the
* data to create a floating point representation of the given value. The units are not set.
*
* <p>This is the inverse of {@link TypedValue#complexToFloat(int)}.
*
* @param value A floating point value.
* @return A complex data integer representing the value.
* @hide
*/
public static int floatToComplex(@FloatRange(from = -0x800000, to = 0x7FFFFF) float value) {
// validate that the magnitude fits in this representation
if (value < (float) -0x800000 - .5f || value >= (float) 0x800000 - .5f) {
throw new IllegalArgumentException("Magnitude of the value is too large: " + value);
}
try {
// If there's no fraction, use integer representation, as that's clearer
if (value == (float) (int) value) {
return createComplex((int) value, TypedValue.COMPLEX_RADIX_23p0);
}
float absValue = Math.abs(value);
// If the magnitude is 0, we don't need any magnitude digits
if (absValue < 1f) {
return createComplex(Math.round(value * (1 << 23)), TypedValue.COMPLEX_RADIX_0p23);
}
// If the magnitude is less than 2^8, use 8 magnitude digits
if (absValue < (float) (1 << 8)) {
return createComplex(Math.round(value * (1 << 15)), TypedValue.COMPLEX_RADIX_8p15);
}
// If the magnitude is less than 2^16, use 16 magnitude digits
if (absValue < (float) (1 << 16)) {
return createComplex(Math.round(value * (1 << 7)), TypedValue.COMPLEX_RADIX_16p7);
}
// The magnitude requires all 23 digits
return createComplex(Math.round(value), TypedValue.COMPLEX_RADIX_23p0);
} catch (IllegalArgumentException ex) {
// Wrap exception so as to include the value argument in the message.
throw new IllegalArgumentException("Unable to convert value to complex: " + value, ex);
}
}
/**
* <p>Creates a complex data integer that stores a dimension value and units.
*
* <p>The resulting value can be passed to e.g.
* {@link TypedValue#complexToDimensionPixelOffset(int, DisplayMetrics)} to calculate the pixel
* value for the dimension.
*
* @param value the value of the dimension
* @param units the units of the dimension, e.g. {@link TypedValue#COMPLEX_UNIT_DIP}
* @return A complex data integer representing the value and units of the dimension.
* @hide
*/
public static int createComplexDimension(
@IntRange(from = -0x800000, to = 0x7FFFFF) int value,
@ComplexDimensionUnit int units) {
if (units < TypedValue.COMPLEX_UNIT_PX || units > TypedValue.COMPLEX_UNIT_MM) {
throw new IllegalArgumentException("Must be a valid COMPLEX_UNIT_*: " + units);
}
return intToComplex(value) | units;
}
/**
* <p>Creates a complex data integer that stores a dimension value and units.
*
* <p>The resulting value can be passed to e.g.
* {@link TypedValue#complexToDimensionPixelOffset(int, DisplayMetrics)} to calculate the pixel
* value for the dimension.
*
* @param value the value of the dimension
* @param units the units of the dimension, e.g. {@link TypedValue#COMPLEX_UNIT_DIP}
* @return A complex data integer representing the value and units of the dimension.
* @hide
*/
public static int createComplexDimension(
@FloatRange(from = -0x800000, to = 0x7FFFFF) float value,
@ComplexDimensionUnit int units) {
if (units < TypedValue.COMPLEX_UNIT_PX || units > TypedValue.COMPLEX_UNIT_MM) {
throw new IllegalArgumentException("Must be a valid COMPLEX_UNIT_*: " + units);
}
return floatToComplex(value) | units;
}
/**
* Converts a complex data value holding a fraction to its final floating
* point value. The given <var>data</var> must be structured as a

View File

@@ -18,6 +18,7 @@ package android.widget;
import android.annotation.ColorInt;
import android.annotation.DimenRes;
import android.annotation.IdRes;
import android.annotation.IntDef;
import android.annotation.LayoutRes;
import android.annotation.NonNull;
@@ -63,12 +64,15 @@ import android.util.ArrayMap;
import android.util.IntArray;
import android.util.Log;
import android.util.Pair;
import android.util.TypedValue;
import android.util.TypedValue.ComplexDimensionUnit;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.LayoutInflater.Filter;
import android.view.RemotableViewMethod;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.MarginLayoutParams;
import android.view.ViewStub;
import android.widget.AdapterView.OnItemClickListener;
@@ -172,6 +176,48 @@ public class RemoteViews implements Parcelable, Filter {
private static final int SET_RIPPLE_DRAWABLE_COLOR_TAG = 21;
private static final int SET_INT_TAG_TAG = 22;
/** @hide **/
@IntDef(prefix = "MARGIN_", value = {
MARGIN_LEFT,
MARGIN_TOP,
MARGIN_RIGHT,
MARGIN_BOTTOM,
MARGIN_START,
MARGIN_END
})
@Retention(RetentionPolicy.SOURCE)
public @interface MarginType {}
/**
* The value will apply to the marginLeft.
* @hide
*/
public static final int MARGIN_LEFT = 0;
/**
* The value will apply to the marginTop.
* @hide
*/
public static final int MARGIN_TOP = 1;
/**
* The value will apply to the marginRight.
* @hide
*/
public static final int MARGIN_RIGHT = 2;
/**
* The value will apply to the marginBottom.
* @hide
*/
public static final int MARGIN_BOTTOM = 3;
/**
* The value will apply to the marginStart.
* @hide
*/
public static final int MARGIN_START = 4;
/**
* The value will apply to the marginEnd.
* @hide
*/
public static final int MARGIN_END = 5;
/** @hide **/
@IntDef(flag = true, value = {
FLAG_REAPPLY_DISALLOWED,
@@ -1730,8 +1776,16 @@ public class RemoteViews implements Parcelable, Filter {
final ViewGroup targetVg = (ViewGroup) target.mRoot;
// Clear all children when nested views omitted
target.mChildren = null;
if (mViewIdToKeep == REMOVE_ALL_VIEWS_ID) {
// Clear all children when there's no excepted view
target.mChildren = null;
} else {
// Remove just the children which don't match the excepted view
target.mChildren.removeIf(childTree -> childTree.mRoot.getId() != mViewIdToKeep);
if (target.mChildren.isEmpty()) {
target.mChildren = null;
}
}
return new RuntimeAction() {
@Override
public void apply(View root, ViewGroup rootParent, OnClickHandler handler)
@@ -1922,13 +1976,13 @@ public class RemoteViews implements Parcelable, Filter {
* Helper action to set text size on a TextView in any supported units.
*/
private class TextViewSizeAction extends Action {
public TextViewSizeAction(int viewId, int units, float size) {
TextViewSizeAction(@IdRes int viewId, @ComplexDimensionUnit int units, float size) {
this.viewId = viewId;
this.units = units;
this.size = size;
}
public TextViewSizeAction(Parcel parcel) {
TextViewSizeAction(Parcel parcel) {
viewId = parcel.readInt();
units = parcel.readInt();
size = parcel.readFloat();
@@ -2004,36 +2058,56 @@ public class RemoteViews implements Parcelable, Filter {
*/
private static class LayoutParamAction extends Action {
/** Set marginEnd */
public static final int LAYOUT_MARGIN_END_DIMEN = 1;
/** Set width */
public static final int LAYOUT_WIDTH = 2;
public static final int LAYOUT_MARGIN_BOTTOM_DIMEN = 3;
public static final int LAYOUT_MARGIN_END = 4;
static final int LAYOUT_MARGIN_LEFT = MARGIN_LEFT;
static final int LAYOUT_MARGIN_TOP = MARGIN_TOP;
static final int LAYOUT_MARGIN_RIGHT = MARGIN_RIGHT;
static final int LAYOUT_MARGIN_BOTTOM = MARGIN_BOTTOM;
static final int LAYOUT_MARGIN_START = MARGIN_START;
static final int LAYOUT_MARGIN_END = MARGIN_END;
static final int LAYOUT_WIDTH = 8;
static final int LAYOUT_HEIGHT = 9;
final int mProperty;
final boolean mIsDimen;
final int mValue;
/**
* @param viewId ID of the view alter
* @param property which layout parameter to alter
* @param value new value of the layout parameter
* @param units the units of the given value
*/
public LayoutParamAction(int viewId, int property, int value) {
LayoutParamAction(@IdRes int viewId, int property, float value,
@ComplexDimensionUnit int units) {
this.viewId = viewId;
this.mProperty = property;
this.mValue = value;
this.mIsDimen = false;
this.mValue = TypedValue.createComplexDimension(value, units);
}
/**
* @param viewId ID of the view alter
* @param property which layout parameter to alter
* @param dimen new dimension with the value of the layout parameter
*/
LayoutParamAction(@IdRes int viewId, int property, @DimenRes int dimen) {
this.viewId = viewId;
this.mProperty = property;
this.mIsDimen = true;
this.mValue = dimen;
}
public LayoutParamAction(Parcel parcel) {
viewId = parcel.readInt();
mProperty = parcel.readInt();
mIsDimen = parcel.readBoolean();
mValue = parcel.readInt();
}
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(viewId);
dest.writeInt(mProperty);
dest.writeBoolean(mIsDimen);
dest.writeInt(mValue);
}
@@ -2047,26 +2121,49 @@ public class RemoteViews implements Parcelable, Filter {
if (layoutParams == null) {
return;
}
int value = mValue;
switch (mProperty) {
case LAYOUT_MARGIN_END_DIMEN:
value = resolveDimenPixelOffset(target, mValue);
// fall-through
case LAYOUT_MARGIN_END:
if (layoutParams instanceof ViewGroup.MarginLayoutParams) {
((ViewGroup.MarginLayoutParams) layoutParams).setMarginEnd(value);
case LAYOUT_MARGIN_LEFT:
if (layoutParams instanceof MarginLayoutParams) {
((MarginLayoutParams) layoutParams).leftMargin = getPixelOffset(target);
target.setLayoutParams(layoutParams);
}
break;
case LAYOUT_MARGIN_BOTTOM_DIMEN:
if (layoutParams instanceof ViewGroup.MarginLayoutParams) {
int resolved = resolveDimenPixelOffset(target, mValue);
((ViewGroup.MarginLayoutParams) layoutParams).bottomMargin = resolved;
case LAYOUT_MARGIN_TOP:
if (layoutParams instanceof MarginLayoutParams) {
((MarginLayoutParams) layoutParams).topMargin = getPixelOffset(target);
target.setLayoutParams(layoutParams);
}
break;
case LAYOUT_MARGIN_RIGHT:
if (layoutParams instanceof MarginLayoutParams) {
((MarginLayoutParams) layoutParams).rightMargin = getPixelOffset(target);
target.setLayoutParams(layoutParams);
}
break;
case LAYOUT_MARGIN_BOTTOM:
if (layoutParams instanceof MarginLayoutParams) {
((MarginLayoutParams) layoutParams).bottomMargin = getPixelOffset(target);
target.setLayoutParams(layoutParams);
}
break;
case LAYOUT_MARGIN_START:
if (layoutParams instanceof MarginLayoutParams) {
((MarginLayoutParams) layoutParams).setMarginStart(getPixelOffset(target));
target.setLayoutParams(layoutParams);
}
break;
case LAYOUT_MARGIN_END:
if (layoutParams instanceof MarginLayoutParams) {
((MarginLayoutParams) layoutParams).setMarginEnd(getPixelOffset(target));
target.setLayoutParams(layoutParams);
}
break;
case LAYOUT_WIDTH:
layoutParams.width = mValue;
layoutParams.width = getPixelSize(target);
target.setLayoutParams(layoutParams);
break;
case LAYOUT_HEIGHT:
layoutParams.height = getPixelSize(target);
target.setLayoutParams(layoutParams);
break;
default:
@@ -2074,11 +2171,26 @@ public class RemoteViews implements Parcelable, Filter {
}
}
private static int resolveDimenPixelOffset(View target, int value) {
if (value == 0) {
return 0;
private int getPixelOffset(View target) {
if (mIsDimen) {
if (mValue == 0) {
return 0;
}
return target.getResources().getDimensionPixelOffset(mValue);
}
return target.getContext().getResources().getDimensionPixelOffset(value);
return TypedValue.complexToDimensionPixelOffset(mValue,
target.getResources().getDisplayMetrics());
}
private int getPixelSize(View target) {
if (mIsDimen) {
if (mValue == 0) {
return 0;
}
return target.getResources().getDimensionPixelSize(mValue);
}
return TypedValue.complexToDimensionPixelSize(mValue,
target.getResources().getDisplayMetrics());
}
@Override
@@ -2512,6 +2624,7 @@ public class RemoteViews implements Parcelable, Filter {
* @param nestedView {@link RemoteViews} that describes the child.
*/
public void addView(int viewId, RemoteViews nestedView) {
// Clear all children when nested views omitted
addAction(nestedView == null
? new ViewGroupActionRemove(viewId)
: new ViewGroupActionAdd(viewId, nestedView));
@@ -3044,57 +3157,94 @@ public class RemoteViews implements Parcelable, Filter {
}
/**
* @hide
* Equivalent to calling {@link android.view.ViewGroup.MarginLayoutParams#setMarginEnd(int)}.
* Equivalent to calling {@link MarginLayoutParams#setMarginEnd}.
* Only works if the {@link View#getLayoutParams()} supports margins.
* Hidden for now since we don't want to support this for all different layout margins yet.
*
* @param viewId The id of the view to change
* @param endMarginDimen a dimen resource to read the margin from or 0 to clear the margin.
* @param type The margin being set e.g. {@link #MARGIN_END}
* @param dimen a dimension resource to apply to the margin, or 0 to clear the margin.
* @hide
*/
public void setViewLayoutMarginEndDimen(int viewId, @DimenRes int endMarginDimen) {
addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_MARGIN_END_DIMEN,
endMarginDimen));
public void setViewLayoutMarginDimen(@IdRes int viewId, @MarginType int type,
@DimenRes int dimen) {
addAction(new LayoutParamAction(viewId, type, dimen));
}
/**
* Equivalent to calling {@link android.view.ViewGroup.MarginLayoutParams#setMarginEnd(int)}.
* Equivalent to calling {@link MarginLayoutParams#setMarginEnd}.
* Only works if the {@link View#getLayoutParams()} supports margins.
* Hidden for now since we don't want to support this for all different layout margins yet.
*
* <p>NOTE: It is recommended to use {@link TypedValue#COMPLEX_UNIT_PX} only for 0.
* Setting margins in pixels will behave poorly when the RemoteViews object is used on a
* display with a different density.
*
* @param viewId The id of the view to change
* @param endMargin a value in pixels for the end margin.
* @param type The margin being set e.g. {@link #MARGIN_END}
* @param value a value for the margin the given units.
* @param units The unit type of the value e.g. {@link TypedValue#COMPLEX_UNIT_DIP}
* @hide
*/
public void setViewLayoutMarginEnd(int viewId, @DimenRes int endMargin) {
addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_MARGIN_END,
endMargin));
public void setViewLayoutMargin(@IdRes int viewId, @MarginType int type, float value,
@ComplexDimensionUnit int units) {
addAction(new LayoutParamAction(viewId, type, value, units));
}
/**
* Equivalent to setting {@link android.view.ViewGroup.MarginLayoutParams#bottomMargin}.
* Equivalent to setting {@link android.view.ViewGroup.LayoutParams#width} except that you may
* provide the value in any dimension units.
*
* @param bottomMarginDimen a dimen resource to read the margin from or 0 to clear the margin.
* <p>NOTE: It is recommended to use {@link TypedValue#COMPLEX_UNIT_PX} only for 0,
* {@link ViewGroup.LayoutParams#WRAP_CONTENT}, or {@link ViewGroup.LayoutParams#MATCH_PARENT}.
* Setting actual sizes in pixels will behave poorly when the RemoteViews object is used on a
* display with a different density.
*
* @param width Width of the view in the given units
* @param units The unit type of the value e.g. {@link TypedValue#COMPLEX_UNIT_DIP}
* @hide
*/
public void setViewLayoutMarginBottomDimen(int viewId, @DimenRes int bottomMarginDimen) {
addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_MARGIN_BOTTOM_DIMEN,
bottomMarginDimen));
public void setViewLayoutWidth(@IdRes int viewId, float width,
@ComplexDimensionUnit int units) {
addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_WIDTH, width, units));
}
/**
* Equivalent to setting {@link android.view.ViewGroup.LayoutParams#width}.
* Equivalent to setting {@link android.view.ViewGroup.LayoutParams#width} with
* the result of {@link Resources#getDimensionPixelSize(int)}.
*
* @param layoutWidth one of 0, MATCH_PARENT or WRAP_CONTENT. Other sizes are not allowed
* because they behave poorly when the density changes.
* @param widthDimen the dimension resource for the view's width
* @hide
*/
public void setViewLayoutWidth(int viewId, int layoutWidth) {
if (layoutWidth != 0 && layoutWidth != ViewGroup.LayoutParams.MATCH_PARENT
&& layoutWidth != ViewGroup.LayoutParams.WRAP_CONTENT) {
throw new IllegalArgumentException("Only supports 0, WRAP_CONTENT and MATCH_PARENT");
}
mActions.add(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_WIDTH, layoutWidth));
public void setViewLayoutWidthDimen(@IdRes int viewId, @DimenRes int widthDimen) {
addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_WIDTH, widthDimen));
}
/**
* Equivalent to setting {@link android.view.ViewGroup.LayoutParams#height} except that you may
* provide the value in any dimension units.
*
* <p>NOTE: It is recommended to use {@link TypedValue#COMPLEX_UNIT_PX} only for 0,
* {@link ViewGroup.LayoutParams#WRAP_CONTENT}, or {@link ViewGroup.LayoutParams#MATCH_PARENT}.
* Setting actual sizes in pixels will behave poorly when the RemoteViews object is used on a
* display with a different density.
*
* @param height height of the view in the given units
* @param units The unit type of the value e.g. {@link TypedValue#COMPLEX_UNIT_DIP}
* @hide
*/
public void setViewLayoutHeight(@IdRes int viewId, float height,
@ComplexDimensionUnit int units) {
addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_HEIGHT, height, units));
}
/**
* Equivalent to setting {@link android.view.ViewGroup.LayoutParams#height} with
* the result of {@link Resources#getDimensionPixelSize(int)}.
*
* @param heightDimen a dimen resource to read the height from.
* @hide
*/
public void setViewLayoutHeightDimen(@IdRes int viewId, @DimenRes int heightDimen) {
addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_HEIGHT, heightDimen));
}
/**

View File

@@ -0,0 +1,155 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.util
import androidx.test.filters.LargeTest
import androidx.test.filters.SmallTest
import androidx.test.runner.AndroidJUnit4
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.mock
import kotlin.math.abs
import kotlin.math.min
import kotlin.math.roundToInt
@RunWith(AndroidJUnit4::class)
class TypedValueTest {
@LargeTest
@Test
fun testFloatToComplex() {
fun assertRoundTripEquals(value: Float, expectedRadix: Int? = null) {
val complex = TypedValue.floatToComplex(value)
// Ensure values are accurate within .5% of the original value and within .5
val delta = min(abs(value) / 512f, .5f)
assertEquals(value, TypedValue.complexToFloat(complex), delta)
// If expectedRadix is provided, validate it
if (expectedRadix != null) {
val actualRadix = ((complex shr TypedValue.COMPLEX_RADIX_SHIFT)
and TypedValue.COMPLEX_RADIX_MASK)
assertEquals("Incorrect radix for $value:", expectedRadix, actualRadix)
}
}
assertRoundTripEquals(0f, TypedValue.COMPLEX_RADIX_23p0)
assertRoundTripEquals(0.5f, TypedValue.COMPLEX_RADIX_0p23)
assertRoundTripEquals(0.05f, TypedValue.COMPLEX_RADIX_0p23)
assertRoundTripEquals(0.005f, TypedValue.COMPLEX_RADIX_0p23)
assertRoundTripEquals(0.0005f, TypedValue.COMPLEX_RADIX_0p23)
assertRoundTripEquals(0.00005f, TypedValue.COMPLEX_RADIX_0p23)
assertRoundTripEquals(1.5f, TypedValue.COMPLEX_RADIX_8p15)
assertRoundTripEquals(10.5f, TypedValue.COMPLEX_RADIX_8p15)
assertRoundTripEquals(100.5f, TypedValue.COMPLEX_RADIX_8p15)
assertRoundTripEquals(255.5f, TypedValue.COMPLEX_RADIX_8p15) // 2^8 - .5
assertRoundTripEquals(256.5f, TypedValue.COMPLEX_RADIX_16p7) // 2^8 + .5
assertRoundTripEquals(1000.5f, TypedValue.COMPLEX_RADIX_16p7)
assertRoundTripEquals(10000.5f, TypedValue.COMPLEX_RADIX_16p7)
assertRoundTripEquals(65535.5f, TypedValue.COMPLEX_RADIX_16p7) // 2^16 - .5
assertRoundTripEquals(65536.5f, TypedValue.COMPLEX_RADIX_23p0) // 2^16 + .5
assertRoundTripEquals(100000.5f, TypedValue.COMPLEX_RADIX_23p0)
assertRoundTripEquals(1000000.5f, TypedValue.COMPLEX_RADIX_23p0)
assertRoundTripEquals(8388607.2f, TypedValue.COMPLEX_RADIX_23p0) // 2^23 -.8
assertRoundTripEquals(-0.5f, TypedValue.COMPLEX_RADIX_0p23)
assertRoundTripEquals(-0.05f, TypedValue.COMPLEX_RADIX_0p23)
assertRoundTripEquals(-0.005f, TypedValue.COMPLEX_RADIX_0p23)
assertRoundTripEquals(-0.0005f, TypedValue.COMPLEX_RADIX_0p23)
assertRoundTripEquals(-0.00005f, TypedValue.COMPLEX_RADIX_0p23)
assertRoundTripEquals(-1.5f, TypedValue.COMPLEX_RADIX_8p15)
assertRoundTripEquals(-10.5f, TypedValue.COMPLEX_RADIX_8p15)
assertRoundTripEquals(-100.5f, TypedValue.COMPLEX_RADIX_8p15)
assertRoundTripEquals(-255.5f, TypedValue.COMPLEX_RADIX_8p15) // -2^8 + .5
// NOTE: -256.5f fits in COMPLEX_RADIX_8p15 but is stored with COMPLEX_RADIX_16p7 for
// simplicity of the algorithm. However, it's better not to enforce that with a test.
assertRoundTripEquals(-257.5f, TypedValue.COMPLEX_RADIX_16p7) // -2^8 - 1.5
assertRoundTripEquals(-1000.5f, TypedValue.COMPLEX_RADIX_16p7)
assertRoundTripEquals(-10000.5f, TypedValue.COMPLEX_RADIX_16p7)
assertRoundTripEquals(-65535.5f, TypedValue.COMPLEX_RADIX_16p7) // -2^16 + .5
// NOTE: -65536.5f fits in COMPLEX_RADIX_16p7 but is stored with COMPLEX_RADIX_23p0 for
// simplicity of the algorithm. However, it's better not to enforce that with a test.
assertRoundTripEquals(-65537.5f, TypedValue.COMPLEX_RADIX_23p0) // -2^16 - 1.5
assertRoundTripEquals(-100000.5f, TypedValue.COMPLEX_RADIX_23p0)
assertRoundTripEquals(-1000000.5f, TypedValue.COMPLEX_RADIX_23p0)
assertRoundTripEquals(-8388607.5f, TypedValue.COMPLEX_RADIX_23p0) // 2^23 -.5
// Test for every integer value in the range...
for (i: Int in -(1 shl 23) until (1 shl 23)) {
// ... that true integers are stored as the precise integer
assertRoundTripEquals(i.toFloat(), TypedValue.COMPLEX_RADIX_23p0)
// ... that values round up when just below an integer
assertRoundTripEquals(i - .1f)
// ... that values round down when just above an integer
assertRoundTripEquals(i + .1f)
}
}
@SmallTest
@Test(expected = IllegalArgumentException::class)
fun testFloatToComplex_failsIfValueTooLarge() {
TypedValue.floatToComplex(8388607.5f) // 2^23 - .5
}
@SmallTest
@Test(expected = IllegalArgumentException::class)
fun testFloatToComplex_failsIfValueTooSmall() {
TypedValue.floatToComplex(8388608.5f) // -2^23 - .5
}
@LargeTest
@Test
fun testIntToComplex() {
// Validates every single valid value
for (value: Int in -(1 shl 23) until (1 shl 23)) {
assertEquals(value.toFloat(), TypedValue.complexToFloat(TypedValue.intToComplex(value)))
}
}
@SmallTest
@Test(expected = IllegalArgumentException::class)
fun testIntToComplex_failsIfValueTooLarge() {
TypedValue.intToComplex(0x800000)
}
@SmallTest
@Test(expected = IllegalArgumentException::class)
fun testIntToComplex_failsIfValueTooSmall() {
TypedValue.intToComplex(-0x800001)
}
@SmallTest
@Test
fun testCreateComplexDimension_appliesUnits() {
val metrics: DisplayMetrics = mock(DisplayMetrics::class.java)
metrics.density = 3.25f
val height = 52 * metrics.density
val widthFloat = height * 16 / 9
val widthDimen = TypedValue.createComplexDimension(
widthFloat / metrics.density,
TypedValue.COMPLEX_UNIT_DIP
)
val widthPx = TypedValue.complexToDimensionPixelSize(widthDimen, metrics)
assertEquals(widthFloat.roundToInt(), widthPx)
}
}