Merge "Block and flag unsupported media art, flag non vector buttons." into pi-car-dev
diff --git a/car-apps-common/res/values-port/values.xml b/car-apps-common/res/values-port/values.xml
index 7476cb9..5ce54da 100644
--- a/car-apps-common/res/values-port/values.xml
+++ b/car-apps-common/res/values-port/values.xml
@@ -17,7 +17,4 @@
<resources>
<bool name="car_tab_flexible_layout">true</bool>
-
- <!-- Scale factor for the background image -->
- <dimen name="background_image_scale" format="float">1.5</dimen>
</resources>
\ No newline at end of file
diff --git a/car-apps-common/res/values/dimens.xml b/car-apps-common/res/values/dimens.xml
index 1b616bf..b2993e3 100644
--- a/car-apps-common/res/values/dimens.xml
+++ b/car-apps-common/res/values/dimens.xml
@@ -70,11 +70,10 @@
<item name="scroller_deceleration_time_divisor" format="float" type="dimen">0.45</item>
<item name="scroller_interpolator_factor" format="float" type="dimen">1.8</item>
- <!-- Scale factor for the background image -->
- <dimen name="background_image_scale" format="float">1</dimen>
- <!-- Blurring radius used to blur background images -->
- <item name="background_image_blur_radius" format="float" type="dimen">25</item>
- <!-- Scale to apply to images before blurring them to create the playback background -->
- <item name="background_image_blur_scale" format="float" type="dimen">.5</item>
+ <!-- Target size for the background bitmap to blur (in pixels) -->
+ <integer name="background_bitmap_target_size_px">64</integer>
+ <!-- Value used to blur background images, the blur radius is P * (W + H)/2.
+ The computed blur radius is capped at 25 pixels. -->
+ <item name="background_bitmap_blur_percent" format="float" type="dimen">0.05</item>
</resources>
diff --git a/car-apps-common/src/com/android/car/apps/common/BackgroundImageView.java b/car-apps-common/src/com/android/car/apps/common/BackgroundImageView.java
index 7cac306..935034c 100644
--- a/car-apps-common/src/com/android/car/apps/common/BackgroundImageView.java
+++ b/car-apps-common/src/com/android/car/apps/common/BackgroundImageView.java
@@ -32,13 +32,9 @@
private CrossfadeImageView mImageView;
/** Configuration (controlled from resources) */
- private float mBackgroundBlurRadius;
- private float mBackgroundBlurScale;
+ private int mBitmapTargetSize;
+ private float mBitmapBlurPercent;
- private float mBackgroundScale = 0;
- private int mBackgroundImageSize = 0;
-
- private Bitmap mBitmap;
private View mDarkeningScrim;
public BackgroundImageView(Context context) {
@@ -57,20 +53,8 @@
mImageView = findViewById(R.id.background_image_image);
mDarkeningScrim = findViewById(R.id.background_image_darkening_scrim);
- mBackgroundScale = getResources().getFloat(R.dimen.background_image_scale);
-
- mBackgroundBlurRadius = getResources().getFloat(R.dimen.background_image_blur_radius);
- mBackgroundBlurScale = getResources().getFloat(R.dimen.background_image_blur_scale);
-
- addOnLayoutChangeListener(
- (view, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
- int newBackgroundImageSize = Math.round(getHeight() * mBackgroundScale);
-
- if (newBackgroundImageSize != mBackgroundImageSize) {
- mBackgroundImageSize = newBackgroundImageSize;
- setBackgroundImage(mBitmap, false);
- }
- });
+ mBitmapTargetSize = getResources().getInteger(R.integer.background_bitmap_target_size_px);
+ mBitmapBlurPercent = getResources().getFloat(R.dimen.background_bitmap_blur_percent);
}
/**
@@ -79,61 +63,29 @@
* @param showAnimation Whether or not to cross fade to the new image
*/
public void setBackgroundImage(@Nullable Bitmap bitmap, boolean showAnimation) {
- // Save the bitmap so that we can reset it when we resize
- mBitmap = bitmap;
-
if (bitmap == null) {
mImageView.setImageBitmap(null, false);
return;
}
- if (mBackgroundImageSize == 0) {
- // We're not set up yet, wait for the OnLayoutChangeListener to set the correct size
- return;
- }
-
- // STOPSHIP(b130576879) Rework this to not be so wasteful
- // We need to scale it to a reasonable size, because if the image was small
- // our blur radius would be way to large, comparably.
- bitmap = Bitmap.createScaledBitmap(bitmap,
- mBackgroundImageSize,
- mBackgroundImageSize,
- false);
-
- if (bitmap != null) {
- bitmap = ImageUtils.blur(getContext(), bitmap, mBackgroundBlurScale,
- mBackgroundBlurRadius);
- }
-
- if (bitmap == null) {
- showAnimation = false;
- }
-
+ bitmap = ImageUtils.blur(getContext(), bitmap, mBitmapTargetSize, mBitmapBlurPercent);
mImageView.setImageBitmap(bitmap, showAnimation);
invalidate();
requestLayout();
}
- /**
- * Sets the image to display to a bitmap
- * @param bitmap The image to show. It will be scaled to the correct size and blurred.
- */
- public void setBackgroundImage(@Nullable Bitmap bitmap) {
- setBackgroundImage(bitmap, true);
- }
-
/** Sets the background to a color */
public void setBackgroundColor(int color) {
mImageView.setBackgroundColor(color);
}
/**
- * Gets the desired size for an image to pass to {@link #setBackgroundImage(Bitmap, boolean)}
- * If the image doesn't match this size, it will be scaled to it.
+ * Gets the desired size for an image to pass to {@link #setBackgroundImage}. That size is
+ * defined by R.integer.background_bitmap_target_size_px.
*/
public int getDesiredBackgroundSize() {
- return mBackgroundImageSize;
+ return mBitmapTargetSize;
}
/** Dims/undims the background image by 30% */
diff --git a/car-apps-common/src/com/android/car/apps/common/ImageUtils.java b/car-apps-common/src/com/android/car/apps/common/ImageUtils.java
index deaedfa..0fae806 100644
--- a/car-apps-common/src/com/android/car/apps/common/ImageUtils.java
+++ b/car-apps-common/src/com/android/car/apps/common/ImageUtils.java
@@ -28,31 +28,54 @@
* Utility methods to manipulate images.
*/
public class ImageUtils {
+
+ private static final float MIN_BLUR = 0.1f;
+ private static final float MAX_BLUR = 25f;
+
/**
* Blurs the given image by scaling it down by the given factor and applying the given
* blurring radius.
*/
@NonNull
- public static Bitmap blur(Context context, @NonNull Bitmap image, float scale, float radius) {
- int width = Math.round(image.getWidth() * scale);
- int height = Math.round(image.getHeight() * scale);
+ public static Bitmap blur(Context context, @NonNull Bitmap image, int bitmapTargetSize,
+ float bitmapBlurPercent) {
+ image = maybeResize(image, bitmapTargetSize);
+ float blurRadius = bitmapBlurPercent * getBitmapDimension(image);
+
+ if (blurRadius <= MIN_BLUR) return image;
+ if (blurRadius > MAX_BLUR) blurRadius = MAX_BLUR;
if (image.getConfig() != Bitmap.Config.ARGB_8888) {
image = image.copy(Bitmap.Config.ARGB_8888, true);
}
- Bitmap inputBitmap = Bitmap.createScaledBitmap(image, width, height, false);
- Bitmap outputBitmap = Bitmap.createBitmap(inputBitmap);
+ Bitmap outputBitmap = Bitmap.createBitmap(image);
RenderScript rs = RenderScript.create(context);
ScriptIntrinsicBlur theIntrinsic = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
- Allocation tmpIn = Allocation.createFromBitmap(rs, inputBitmap);
+ Allocation tmpIn = Allocation.createFromBitmap(rs, image);
Allocation tmpOut = Allocation.createFromBitmap(rs, outputBitmap);
- theIntrinsic.setRadius(radius);
+ theIntrinsic.setRadius(blurRadius);
theIntrinsic.setInput(tmpIn);
theIntrinsic.forEach(tmpOut);
tmpOut.copyTo(outputBitmap);
return outputBitmap;
}
+
+ private static Bitmap maybeResize(@NonNull Bitmap image, int bitmapTargetSize) {
+ int imageDim = getBitmapDimension(image);
+ if (imageDim > bitmapTargetSize) {
+ float scale = bitmapTargetSize / (float) imageDim;
+ int width = Math.round(scale * image.getWidth());
+ int height = Math.round(scale * image.getHeight());
+ return Bitmap.createScaledBitmap(image, width, height, false);
+ } else {
+ return image;
+ }
+ }
+
+ private static int getBitmapDimension(@NonNull Bitmap image) {
+ return (image.getWidth() + image.getHeight()) / 2;
+ }
}
diff --git a/car-apps-common/src/com/android/car/apps/common/util/ScrollBarUI.java b/car-apps-common/src/com/android/car/apps/common/util/ScrollBarUI.java
index b4a5fa1..7098a82 100644
--- a/car-apps-common/src/com/android/car/apps/common/util/ScrollBarUI.java
+++ b/car-apps-common/src/com/android/car/apps/common/util/ScrollBarUI.java
@@ -47,4 +47,9 @@
* affect the size of the scrollbar view.
*/
public abstract void requestLayout();
+
+ /**
+ * Sets the padding of the scrollbar, relative to the padding of the RecyclerView.
+ */
+ public abstract void setPadding(int padddingStart, int paddingEnd);
}
diff --git a/car-apps-common/src/com/android/car/apps/common/widget/CarScrollBar.java b/car-apps-common/src/com/android/car/apps/common/widget/CarScrollBar.java
index 6dfb62f..2de23a3 100644
--- a/car-apps-common/src/com/android/car/apps/common/widget/CarScrollBar.java
+++ b/car-apps-common/src/com/android/car/apps/common/widget/CarScrollBar.java
@@ -62,6 +62,9 @@
private int mSeparatingMargin;
private int mScrollBarThumbWidth;
+ private int mPaddingStart;
+ private int mPaddingEnd;
+
/** The amount of space that the scroll thumb is allowed to roam over. */
private int mScrollThumbTrackHeight;
@@ -138,10 +141,9 @@
OrientationHelper orientationHelper =
getOrientationHelper(getRecyclerView().getLayoutManager());
- int height = orientationHelper.getTotalSpace();
// This value will keep track of the top of the current view being laid out.
- int layoutTop = orientationHelper.getStartAfterPadding();
+ int layoutTop = orientationHelper.getStartAfterPadding() + mPaddingStart;
// Lay out the up button at the top of the view.
layoutViewCenteredFromTop(mUpButton, layoutTop, width);
@@ -152,7 +154,7 @@
layoutViewCenteredFromTop(mScrollThumb, layoutTop, width);
// Lay out the bottom button at the bottom of the view.
- int downBottom = height + orientationHelper.getStartAfterPadding();
+ int downBottom = orientationHelper.getEndAfterPadding() - mPaddingEnd;
layoutViewCenteredFromBottom(mDownButton, downBottom, width);
mHandler.post(this::calculateScrollThumbTrackHeight);
@@ -165,6 +167,13 @@
mScrollView.requestLayout();
}
+ @Override
+ public void setPadding(int paddingStart, int paddingEnd) {
+ mPaddingStart = paddingStart;
+ mPaddingEnd = paddingEnd;
+ requestLayout();
+ }
+
/**
* Sets the listener that will be notified when the up and down buttons have been pressed.
*
diff --git a/car-apps-common/src/com/android/car/apps/common/widget/PagedRecyclerView.java b/car-apps-common/src/com/android/car/apps/common/widget/PagedRecyclerView.java
index 70826a6..a8ebdba 100644
--- a/car-apps-common/src/com/android/car/apps/common/widget/PagedRecyclerView.java
+++ b/car-apps-common/src/com/android/car/apps/common/widget/PagedRecyclerView.java
@@ -23,11 +23,13 @@
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.util.Log;
+import android.view.View;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
@@ -49,7 +51,7 @@
private Context mContext;
- private final CarUxRestrictionsUtil mCarUxRestrictionsUtil;
+ private CarUxRestrictionsUtil mCarUxRestrictionsUtil;
private final CarUxRestrictionsUtil.OnUxRestrictionsChangedListener mListener;
private boolean mScrollBarEnabled;
@@ -57,6 +59,8 @@
private @ScrollBarPosition int mScrollBarPosition;
private boolean mScrollBarAboveRecyclerView;
private String mScrollBarClass;
+ private int mScrollBarPaddingStart;
+ private int mScrollBarPaddingEnd;
@Gutter
private int mGutter;
@@ -191,7 +195,11 @@
public PagedRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
- mCarUxRestrictionsUtil = CarUxRestrictionsUtil.getInstance(context);
+ try {
+ mCarUxRestrictionsUtil = CarUxRestrictionsUtil.getInstance(context);
+ } catch (NullPointerException e) {
+ // Do nothing, mCarUxRestrictionsUtil will be null
+ }
mListener = this::updateCarUxRestrictions;
init(context, attrs, defStyle);
@@ -236,7 +244,7 @@
R.styleable.PagedRecyclerView_scrollBarContainerWidth, carMargin);
mScrollBarPosition = a.getInt(R.styleable.PagedRecyclerView_scrollBarPosition,
- ScrollBarPosition.START);
+ ScrollBarPosition.START);
mScrollBarAboveRecyclerView = a.getBoolean(
R.styleable.PagedRecyclerView_scrollBarAboveRecyclerView, /* defValue= */true);
@@ -263,13 +271,17 @@
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- mCarUxRestrictionsUtil.register(mListener);
+ if (mCarUxRestrictionsUtil != null) {
+ mCarUxRestrictionsUtil.register(mListener);
+ }
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
- mCarUxRestrictionsUtil.unregister(mListener);
+ if (mCarUxRestrictionsUtil != null) {
+ mCarUxRestrictionsUtil.unregister(mListener);
+ }
}
private void updateCarUxRestrictions(CarUxRestrictions carUxRestrictions) {
@@ -395,6 +407,43 @@
}
@Override
+ public ViewHolder findViewHolderForLayoutPosition(int position) {
+ if (mScrollBarEnabled) {
+ return mNestedRecyclerView.findViewHolderForLayoutPosition(position);
+ } else {
+ return super.findViewHolderForLayoutPosition(position);
+ }
+ }
+
+ @Override
+ public ViewHolder findContainingViewHolder(View view) {
+ if (mScrollBarEnabled) {
+ return mNestedRecyclerView.findContainingViewHolder(view);
+ } else {
+ return super.findContainingViewHolder(view);
+ }
+ }
+
+ @Override
+ @Nullable
+ public View findChildViewUnder(float x, float y) {
+ if (mScrollBarEnabled) {
+ return mNestedRecyclerView.findChildViewUnder(x, y);
+ } else {
+ return super.findChildViewUnder(x, y);
+ }
+ }
+
+ /**
+ * Calls {@link #layout(int, int, int, int)} for both this RecyclerView and the nested one.
+ */
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ public void layoutBothForTesting(int l, int t, int r, int b) {
+ super.layout(l, t, r, b);
+ mNestedRecyclerView.layout(l, t, r, b);
+ }
+
+ @Override
public int getPaddingStart() {
return mScrollBarEnabled ? mNestedRecyclerView.getPaddingStart() : super.getPaddingStart();
}
@@ -457,10 +506,27 @@
mScrollBarUI.initialize(mContext, mNestedRecyclerView, mScrollBarContainerWidth,
mScrollBarPosition, mScrollBarAboveRecyclerView);
+ mScrollBarUI.setPadding(mScrollBarPaddingStart, mScrollBarPaddingEnd);
+
if (DEBUG) Log.d(TAG, "started " + mScrollBarUI.getClass().getSimpleName());
}
/**
+ * Sets the scrollbar's padding start (top) and end (bottom).
+ * This padding is applied in addition to the padding of the inner RecyclerView.
+ */
+ public void setScrollBarPadding(int paddingStart, int paddingEnd) {
+ if (mScrollBarEnabled) {
+ mScrollBarPaddingStart = paddingStart;
+ mScrollBarPaddingEnd = paddingEnd;
+
+ if (mScrollBarUI != null) {
+ mScrollBarUI.setPadding(paddingStart, paddingEnd);
+ }
+ }
+ }
+
+ /**
* Set the nested view's layout to the specified value.
*
* <p>The gutter is the space to the start/end of the list view items and will be equal in size
diff --git a/car-media-common/res/layout/fragment_app_selection.xml b/car-media-common/res/layout/fragment_app_selection.xml
index dc5087c..05f1dcd 100644
--- a/car-media-common/res/layout/fragment_app_selection.xml
+++ b/car-media-common/res/layout/fragment_app_selection.xml
@@ -49,7 +49,7 @@
android:background="@drawable/appbar_view_icon_background"
android:gravity="center" />
- <androidx.recyclerview.widget.RecyclerView
+ <com.android.car.apps.common.widget.PagedRecyclerView
android:id="@+id/apps_grid"
android:layout_width="match_parent"
android:layout_height="match_parent"