Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a9171adc authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Correct API for new sizes in App Widgets." into sc-dev

parents c4b1e289 3e443e6b
Loading
Loading
Loading
Loading
+6 −5
Original line number Diff line number Diff line
@@ -8372,7 +8372,6 @@ package android.appwidget {
  public class AppWidgetHostView extends android.widget.FrameLayout {
    ctor public AppWidgetHostView(android.content.Context);
    ctor public AppWidgetHostView(android.content.Context, int, int);
    method public void clearCurrentSize();
    method public int getAppWidgetId();
    method public android.appwidget.AppWidgetProviderInfo getAppWidgetInfo();
    method public static android.graphics.Rect getDefaultPaddingForWidget(android.content.Context, android.content.ComponentName, android.graphics.Rect);
@@ -8382,13 +8381,12 @@ package android.appwidget {
    method public void resetColorResources();
    method public void setAppWidget(int, android.appwidget.AppWidgetProviderInfo);
    method public void setColorResources(@NonNull android.util.SparseIntArray);
    method public void setCurrentSize(@NonNull android.graphics.PointF);
    method public void setExecutor(java.util.concurrent.Executor);
    method public void setOnLightBackground(boolean);
    method public void updateAppWidget(android.widget.RemoteViews);
    method public void updateAppWidgetOptions(android.os.Bundle);
    method @Deprecated public void updateAppWidgetSize(android.os.Bundle, int, int, int, int);
    method public void updateAppWidgetSize(@NonNull android.os.Bundle, @NonNull java.util.List<android.graphics.PointF>);
    method public void updateAppWidgetSize(@NonNull android.os.Bundle, @NonNull java.util.List<android.util.SizeF>);
  }
  public class AppWidgetManager {
@@ -45948,11 +45946,14 @@ package android.util {
    method public static android.util.Size parseSize(String) throws java.lang.NumberFormatException;
  }
  public final class SizeF {
  public final class SizeF implements android.os.Parcelable {
    ctor public SizeF(float, float);
    method public int describeContents();
    method public float getHeight();
    method public float getWidth();
    method public static android.util.SizeF parseSizeF(String) throws java.lang.NumberFormatException;
    method public void writeToParcel(@NonNull android.os.Parcel, int);
    field @NonNull public static final android.os.Parcelable.Creator<android.util.SizeF> CREATOR;
  }
  public class SparseArray<E> implements java.lang.Cloneable {
@@ -54844,7 +54845,7 @@ package android.widget {
  public class RemoteViews implements android.view.LayoutInflater.Filter android.os.Parcelable {
    ctor public RemoteViews(String, int);
    ctor public RemoteViews(android.widget.RemoteViews, android.widget.RemoteViews);
    ctor public RemoteViews(@NonNull java.util.Map<android.graphics.PointF,android.widget.RemoteViews>);
    ctor public RemoteViews(@NonNull java.util.Map<android.util.SizeF,android.widget.RemoteViews>);
    ctor public RemoteViews(android.widget.RemoteViews);
    ctor public RemoteViews(android.os.Parcel);
    method public void addView(@IdRes int, android.widget.RemoteViews);
+43 −49
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@ import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Pair;
import android.util.SizeF;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.view.Gravity;
@@ -56,7 +57,6 @@ import android.widget.TextView;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;

/**
@@ -93,7 +93,7 @@ public class AppWidgetHostView extends FrameLayout {
    int mLayoutId = -1;
    private InteractionHandler mInteractionHandler;
    private boolean mOnLightBackground;
    private PointF mCurrentSize = null;
    private SizeF mCurrentSize = null;
    private RemoteViews.ColorResources mColorResources = null;
    // Stores the last remote views last inflated.
    private RemoteViews mLastInflatedRemoteViews = null;
@@ -253,9 +253,33 @@ public class AppWidgetHostView extends FrameLayout {
        }
    }

    private SizeF computeSizeFromLayout(int left, int top, int right, int bottom) {
        Rect padding = getDefaultPadding();
        float density = getResources().getDisplayMetrics().density;
        return new SizeF(
                (right - left - padding.right - padding.left) / density,
                (bottom - top - padding.bottom - padding.top) / density
        );
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        try {
            SizeF oldSize = mCurrentSize;
            SizeF newSize = computeSizeFromLayout(left, top, right, bottom);
            mCurrentSize = newSize;
            if (mLastInflatedRemoteViews != null) {
                RemoteViews toApply = mLastInflatedRemoteViews.getRemoteViewsToApplyIfDifferent(
                        oldSize, newSize);
                if (toApply != null) {
                    applyRemoteViews(toApply, false);
                    measureChildWithMargins(mView,
                            MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY),
                            0 /* widthUsed */,
                            MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY),
                            0 /* heightUsed */);
                }
            }
            super.onLayout(changed, left, top, right, bottom);
        } catch (final RuntimeException e) {
            Log.e(TAG, "Remote provider threw runtime exception, using error view instead.", e);
@@ -309,7 +333,7 @@ public class AppWidgetHostView extends FrameLayout {
     *              again. Typically, this will be size of the widget in landscape and portrait.
     *              On some foldables, this might include the size on the outer and inner screens.
     */
    public void updateAppWidgetSize(@NonNull Bundle newOptions, @NonNull List<PointF> sizes) {
    public void updateAppWidgetSize(@NonNull Bundle newOptions, @NonNull List<SizeF> sizes) {
        AppWidgetManager widgetManager = AppWidgetManager.getInstance(mContext);

        Rect padding = getDefaultPadding();
@@ -318,20 +342,20 @@ public class AppWidgetHostView extends FrameLayout {
        float xPaddingDips = (padding.left + padding.right) / density;
        float yPaddingDips = (padding.top + padding.bottom) / density;

        ArrayList<PointF> paddedSizes = new ArrayList<>(sizes.size());
        ArrayList<SizeF> paddedSizes = new ArrayList<>(sizes.size());
        float minWidth = Float.MAX_VALUE;
        float maxWidth = 0;
        float minHeight = Float.MAX_VALUE;
        float maxHeight = 0;
        for (int i = 0; i < sizes.size(); i++) {
            PointF size = sizes.get(i);
            PointF paddedPoint = new PointF(Math.max(0.f, size.x - xPaddingDips),
                    Math.max(0.f, size.y - yPaddingDips));
            paddedSizes.add(paddedPoint);
            minWidth = Math.min(minWidth, paddedPoint.x);
            maxWidth = Math.max(maxWidth, paddedPoint.x);
            minHeight = Math.min(minHeight, paddedPoint.y);
            maxHeight = Math.max(maxHeight, paddedPoint.y);
            SizeF size = sizes.get(i);
            SizeF paddedSize = new SizeF(Math.max(0.f, size.getWidth() - xPaddingDips),
                    Math.max(0.f, size.getHeight() - yPaddingDips));
            paddedSizes.add(paddedSize);
            minWidth = Math.min(minWidth, paddedSize.getWidth());
            maxWidth = Math.max(maxWidth, paddedSize.getWidth());
            minHeight = Math.min(minHeight, paddedSize.getHeight());
            maxHeight = Math.max(maxHeight, paddedSize.getHeight());
        }
        if (paddedSizes.equals(
                widgetManager.getAppWidgetOptions(mAppWidgetId).<PointF>getParcelableArrayList(
@@ -347,35 +371,6 @@ public class AppWidgetHostView extends FrameLayout {
        updateAppWidgetOptions(options);
    }

    /**
     * Set the current size of the widget. This should be the full area the AppWidgetHostView is
     * given. Padding added by the framework will be accounted for automatically.
     *
     * This size will be used to choose the appropriate layout the next time the {@link RemoteViews}
     * is re-inflated, if it was created with {@link RemoteViews#RemoteViews(Map)} .
     */
    public void setCurrentSize(@NonNull PointF size) {
        Rect padding = getDefaultPadding();
        float density = getResources().getDisplayMetrics().density;
        float xPaddingDips = (padding.left + padding.right) / density;
        float yPaddingDips = (padding.top + padding.bottom) / density;
        PointF newSize = new PointF(size.x - xPaddingDips, size.y - yPaddingDips);
        if (!newSize.equals(mCurrentSize)) {
            mCurrentSize = newSize;
            reapplyLastRemoteViews();
        }
    }

    /**
     * Clear the current size, indicating it is not currently known.
     */
    public void clearCurrentSize() {
        if (mCurrentSize != null) {
            mCurrentSize = null;
            reapplyLastRemoteViews();
        }
    }

    /**
     * @hide
     */
@@ -514,23 +509,22 @@ public class AppWidgetHostView extends FrameLayout {
            mLayoutId = -1;
            mViewMode = VIEW_MODE_DEFAULT;
        } else {
            // Select the remote view we are actually going to apply.
            RemoteViews rvToApply = remoteViews.getRemoteViewsToApply(mContext, mCurrentSize);
            if (mOnLightBackground) {
                remoteViews = remoteViews.getDarkTextViews();
                rvToApply = rvToApply.getDarkTextViews();
            }

            if (mAsyncExecutor != null && useAsyncIfPossible) {
                inflateAsync(remoteViews);
                inflateAsync(rvToApply);
                return;
            }
            // Prepare a local reference to the remote Context so we're ready to
            // inflate any requested LayoutParams.
            mRemoteContext = getRemoteContext();
            int layoutId = remoteViews.getLayoutId();
            int layoutId = rvToApply.getLayoutId();
            // If our stale view has been prepared to match active, and the new
            // layout matches, try recycling it
            if (content == null && layoutId == mLayoutId) {
                try {
                    remoteViews.reapply(mContext, mView, mInteractionHandler, mCurrentSize,
                    rvToApply.reapply(mContext, mView, mInteractionHandler, mCurrentSize,
                            mColorResources);
                    content = mView;
                    recycled = true;
@@ -543,7 +537,7 @@ public class AppWidgetHostView extends FrameLayout {
            // Try normal RemoteView inflation
            if (content == null) {
                try {
                    content = remoteViews.apply(mContext, this, mInteractionHandler,
                    content = rvToApply.apply(mContext, this, mInteractionHandler,
                            mCurrentSize, mColorResources);
                    if (LOGD) Log.d(TAG, "had to inflate new layout");
                } catch (RuntimeException e) {
+1 −1
Original line number Diff line number Diff line
@@ -219,7 +219,7 @@ public class AppWidgetManager {
    public static final String OPTION_APPWIDGET_MAX_HEIGHT = "appWidgetMaxHeight";

    /**
     * A bundle extra ({@code List<PointF>}) that contains the list of possible sizes, in dips, a
     * A bundle extra ({@code List<SizeF>}) that contains the list of possible sizes, in dips, a
     * widget instance can take.
     */
    public static final String OPTION_APPWIDGET_SIZES = "appWidgetSizes";
+45 −2
Original line number Diff line number Diff line
@@ -16,8 +16,12 @@

package android.util;

import static com.android.internal.util.Preconditions.checkNotNull;
import static com.android.internal.util.Preconditions.checkArgumentFinite;
import static com.android.internal.util.Preconditions.checkNotNull;

import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;

/**
 * Immutable class for describing width and height dimensions in some arbitrary
@@ -26,7 +30,7 @@ import static com.android.internal.util.Preconditions.checkArgumentFinite;
 * Width and height are finite values stored as a floating point representation.
 * </p>
 */
public final class SizeF {
public final class SizeF implements Parcelable {
    /**
     * Create a new immutable SizeF instance.
     *
@@ -161,4 +165,43 @@ public final class SizeF {

    private final float mWidth;
    private final float mHeight;

    /**
     * Parcelable interface methods
     */
    @Override
    public int describeContents() {
        return 0;
    }

    /**
     * Write this size to the specified parcel. To restore a size from a parcel, use the
     * {@link #CREATOR}.
     * @param out The parcel to write the point's coordinates into
     */
    @Override
    public void writeToParcel(@NonNull Parcel out, int flags) {
        out.writeFloat(mWidth);
        out.writeFloat(mHeight);
    }

    public static final @NonNull Creator<SizeF> CREATOR = new Creator<SizeF>() {
        /**
         * Return a new size from the data in the specified parcel.
         */
        @Override
        public @NonNull SizeF createFromParcel(@NonNull Parcel in) {
            float width = in.readFloat();
            float height = in.readFloat();
            return new SizeF(width, height);
        }

        /**
         * Return an array of sizes of the specified size.
         */
        @Override
        public @NonNull SizeF[] newArray(int size) {
            return new SizeF[size];
        }
    };
}
+70 −39
Original line number Diff line number Diff line
@@ -51,7 +51,6 @@ import android.content.res.loader.ResourcesProvider;
import android.graphics.Bitmap;
import android.graphics.BlendMode;
import android.graphics.Outline;
import android.graphics.PointF;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
@@ -76,6 +75,7 @@ import android.util.DisplayMetrics;
import android.util.IntArray;
import android.util.Log;
import android.util.Pair;
import android.util.SizeF;
import android.util.SparseIntArray;
import android.util.TypedValue;
import android.util.TypedValue.ComplexDimensionUnit;
@@ -353,7 +353,7 @@ public class RemoteViews implements Parcelable, Filter {
     * Only to be used on children views used in a {@link RemoteViews} with
     * {@link RemoteViews#hasSizedRemoteViews()}.
     */
    private PointF mIdealSize = null;
    private SizeF mIdealSize = null;

    @ApplyFlags
    private int mApplyFlags = 0;
@@ -3042,11 +3042,11 @@ public class RemoteViews implements Parcelable, Filter {
        return mSizedRemoteViews != null;
    }

    private @Nullable PointF getIdealSize() {
    private @Nullable SizeF getIdealSize() {
        return mIdealSize;
    }

    private void setIdealSize(@Nullable PointF size) {
    private void setIdealSize(@Nullable SizeF size) {
        mIdealSize = size;
    }

@@ -3094,13 +3094,18 @@ public class RemoteViews implements Parcelable, Filter {
     * Create a new RemoteViews object that will inflate the layout with the closest size
     * specification.
     *
     * The default remote views in that case is always the smallest one provided.
     * The default remote views in that case is always the one with the smallest area.
     *
     * If the {@link RemoteViews} host provides the size of the view, the layout with the largest
     * area that fits entirely in the provided size will be used (i.e. the width and height of
     * the layout must be less than the size of the view, with a 1dp margin to account for
     * rounding). If no layout fits in the view, the layout with the smallest area will be used.
     *
     * @param remoteViews Mapping of size to layout.
     * @throws IllegalArgumentException if the map is empty, there are more than
     *   MAX_INIT_VIEW_COUNT layouts or the remote views are not all from the same application.
     */
    public RemoteViews(@NonNull Map<PointF, RemoteViews> remoteViews) {
    public RemoteViews(@NonNull Map<SizeF, RemoteViews> remoteViews) {
        if (remoteViews.isEmpty()) {
            throw new IllegalArgumentException("The set of RemoteViews cannot be empty");
        }
@@ -3135,8 +3140,8 @@ public class RemoteViews implements Parcelable, Filter {
        RemoteViews smallestView = null;
        while (remoteViews.hasNext()) {
            RemoteViews view = remoteViews.next();
            PointF size = view.getIdealSize();
            float newViewArea = size.x * size.y;
            SizeF size = view.getIdealSize();
            float newViewArea = size.getWidth() * size.getHeight();
            if (smallestView != null && !view.hasSameAppInfo(smallestView.mApplication)) {
                throw new IllegalArgumentException(
                        "All RemoteViews must share the same package and user");
@@ -3239,7 +3244,7 @@ public class RemoteViews implements Parcelable, Filter {
        if (mode == MODE_NORMAL) {
            mApplication = parcel.readInt() == 0 ? info :
                    ApplicationInfo.CREATOR.createFromParcel(parcel);
            mIdealSize = parcel.readInt() == 0 ? null : PointF.CREATOR.createFromParcel(parcel);
            mIdealSize = parcel.readInt() == 0 ? null : SizeF.CREATOR.createFromParcel(parcel);
            mLayoutId = parcel.readInt();
            mLightBackgroundLayoutId = parcel.readInt();

@@ -4625,9 +4630,9 @@ public class RemoteViews implements Parcelable, Filter {
     *
     * This is particularly useful when we only care about the ordering of the distances.
     */
    private static float squareDistance(PointF p1, PointF p2) {
        float dx = p1.x - p2.x;
        float dy = p1.y - p2.y;
    private static float squareDistance(SizeF p1, SizeF p2) {
        float dx = p1.getWidth() - p2.getWidth();
        float dy = p1.getHeight() - p2.getHeight();
        return dx * dx + dy * dy;
    }

@@ -4637,31 +4642,17 @@ public class RemoteViews implements Parcelable, Filter {
     * A layout fits on a widget if the widget size is known (i.e. not null) and both dimensions
     * are smaller than the ones of the widget, adding some padding to account for rounding errors.
     */
    private static boolean fitsIn(PointF sizeLayout, @Nullable PointF sizeWidget) {
        return sizeWidget != null && (Math.ceil(sizeWidget.x) + 1 > sizeLayout.x)
                && (Math.ceil(sizeWidget.y) + 1 > sizeLayout.y);
    private static boolean fitsIn(SizeF sizeLayout, @Nullable SizeF sizeWidget) {
        return sizeWidget != null && (Math.ceil(sizeWidget.getWidth()) + 1 > sizeLayout.getWidth())
                && (Math.ceil(sizeWidget.getHeight()) + 1 > sizeLayout.getHeight());
    }

    /**
     * Returns the most appropriate {@link RemoteViews} given the context and, if not null, the
     * size of the widget.
     *
     * If {@link RemoteViews#hasSizedRemoteViews()} returns true, the most appropriate view is
     * the one that fits in the widget (according to {@link RemoteViews#fitsIn}) and has the
     * diagonal the most similar to the widget. If no layout fits or the size of the widget is
     * not specified, the one with the smallest area will be chosen.
     */
    private RemoteViews getRemoteViewsToApply(@NonNull Context context,
            @Nullable PointF widgetSize) {
        if (!hasSizedRemoteViews()) {
            // If there isn't multiple remote views, fall back on the previous methods.
            return getRemoteViewsToApply(context);
        }
    private RemoteViews findBestFitLayout(@NonNull SizeF widgetSize) {
        // Find the better remote view
        RemoteViews bestFit = null;
        float bestSqDist = Float.MAX_VALUE;
        for (RemoteViews layout : mSizedRemoteViews) {
            PointF layoutSize = layout.getIdealSize();
            SizeF layoutSize = layout.getIdealSize();
            if (fitsIn(layoutSize, widgetSize)) {
                if (bestFit == null) {
                    bestFit = layout;
@@ -4682,6 +4673,46 @@ public class RemoteViews implements Parcelable, Filter {
        return bestFit;
    }

    /**
     * Returns the most appropriate {@link RemoteViews} given the context and, if not null, the
     * size of the widget.
     *
     * If {@link RemoteViews#hasSizedRemoteViews()} returns true, the most appropriate view is
     * the one that fits in the widget (according to {@link RemoteViews#fitsIn}) and has the
     * diagonal the most similar to the widget. If no layout fits or the size of the widget is
     * not specified, the one with the smallest area will be chosen.
     *
     * @hide
     */
    public RemoteViews getRemoteViewsToApply(@NonNull Context context,
            @Nullable SizeF widgetSize) {
        if (!hasSizedRemoteViews()) {
            // If there isn't multiple remote views, fall back on the previous methods.
            return getRemoteViewsToApply(context);
        }
        return findBestFitLayout(widgetSize);
    }

    /**
     * Checks whether the change of size will lead to using a different {@link RemoteViews}.
     *
     * @hide
     */
    @Nullable
    public RemoteViews getRemoteViewsToApplyIfDifferent(@Nullable SizeF oldSize,
            @NonNull SizeF newSize) {
        if (!hasSizedRemoteViews()) {
            return null;
        }
        RemoteViews oldBestFit = oldSize == null ? findSmallestRemoteView() : findBestFitLayout(
                oldSize);
        RemoteViews newBestFit = findBestFitLayout(newSize);
        if (oldBestFit != newBestFit) {
            return newBestFit;
        }
        return null;
    }


    /**
     * Inflates the view hierarchy represented by this object and applies
@@ -4705,7 +4736,7 @@ public class RemoteViews implements Parcelable, Filter {

    /** @hide */
    public View apply(@NonNull Context context, @NonNull ViewGroup parent,
            @Nullable InteractionHandler handler, @Nullable PointF size) {
            @Nullable InteractionHandler handler, @Nullable SizeF size) {
        RemoteViews rvToApply = getRemoteViewsToApply(context, size);

        View result = inflateView(context, rvToApply, parent);
@@ -4722,7 +4753,7 @@ public class RemoteViews implements Parcelable, Filter {
    /** @hide */
    public View applyWithTheme(@NonNull Context context, @NonNull ViewGroup parent,
            @Nullable InteractionHandler handler, @StyleRes int applyThemeResId,
            @Nullable PointF size) {
            @Nullable SizeF size) {
        RemoteViews rvToApply = getRemoteViewsToApply(context, size);

        View result = inflateView(context, rvToApply, parent, applyThemeResId, null);
@@ -4732,7 +4763,7 @@ public class RemoteViews implements Parcelable, Filter {

    /** @hide */
    public View apply(Context context, ViewGroup parent, InteractionHandler handler,
            @NonNull PointF size, @Nullable ColorResources colorResources) {
            @NonNull SizeF size, @Nullable ColorResources colorResources) {
        RemoteViews rvToApply = getRemoteViewsToApply(context, size);

        View result = inflateView(context, rvToApply, parent, 0, colorResources);
@@ -4828,21 +4859,21 @@ public class RemoteViews implements Parcelable, Filter {
    /** @hide */
    public CancellationSignal applyAsync(Context context, ViewGroup parent,
            Executor executor, OnViewAppliedListener listener, InteractionHandler handler,
            PointF size) {
            SizeF size) {
        return getAsyncApplyTask(context, parent, listener, handler, size, null /* themeColors */)
                .startTaskOnExecutor(executor);
    }

    /** @hide */
    public CancellationSignal applyAsync(Context context, ViewGroup parent, Executor executor,
            OnViewAppliedListener listener, InteractionHandler handler, PointF size,
            OnViewAppliedListener listener, InteractionHandler handler, SizeF size,
            ColorResources colorResources) {
        return getAsyncApplyTask(context, parent, listener, handler, size, colorResources)
                .startTaskOnExecutor(executor);
    }

    private AsyncApplyTask getAsyncApplyTask(Context context, ViewGroup parent,
            OnViewAppliedListener listener, InteractionHandler handler, PointF size,
            OnViewAppliedListener listener, InteractionHandler handler, SizeF size,
            ColorResources colorResources) {
        return new AsyncApplyTask(getRemoteViewsToApply(context, size), parent, context, listener,
                handler, colorResources, null /* result */);
@@ -4968,7 +4999,7 @@ public class RemoteViews implements Parcelable, Filter {
    }

    /** @hide */
    public void reapply(Context context, View v, InteractionHandler handler, PointF size,
    public void reapply(Context context, View v, InteractionHandler handler, SizeF size,
            ColorResources colorResources) {
        RemoteViews rvToApply = getRemoteViewsToApply(context, size);

@@ -5012,7 +5043,7 @@ public class RemoteViews implements Parcelable, Filter {

    /** @hide */
    public CancellationSignal reapplyAsync(Context context, View v, Executor executor,
            OnViewAppliedListener listener, InteractionHandler handler, PointF size,
            OnViewAppliedListener listener, InteractionHandler handler, SizeF size,
            ColorResources colorResources) {
        RemoteViews rvToApply = getRemoteViewsToApply(context, size);