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

Commit ff55ff2c authored by Pierre Barbier de Reuille's avatar Pierre Barbier de Reuille
Browse files

Check if the view can be recycled if viewId is set

Previously, the recycling was only checked for multiple layout, as it
was assumed the AppWidgetHostView would make the basic check. However,
adapters views (e.g. ListView, ...) do not make any recycling check,
instead relying on caching using the Layout id. The caching cannot be
changed as it is common to other parts of Android.

Note, however, that some apps are already using this lack of test to
create the view themselves and use reapply to apply the RemoteViews. So
we limit the test to uses involving changing the viewId.

This CL ensures the view can be recycled when reapplied, always.

Bug: 181985606
Test: atest android.widget.cts.RemoteViewsRecyclingTest
Change-Id: Ib4f908c66a666faa4e6c27e2be38246eb7748f61
parent d9422307
Loading
Loading
Loading
Loading
+27 −17
Original line number Diff line number Diff line
@@ -5735,24 +5735,44 @@ public class RemoteViews implements Parcelable, Filter {
        return previousLayoutId == getLayoutId() && mViewId == overrideId;
    }

    // Note: topLevel should be true only for calls on the topLevel RemoteViews, internal calls
    // should set it to false.
    private void reapply(Context context, View v, InteractionHandler handler, SizeF size,
            ColorResources colorResources, boolean topLevel) {

    /**
     * Returns the RemoteViews that should be used in the reapply operation.
     *
     * If the current RemoteViews has multiple layout, this will select the correct one.
     *
     * @throws RuntimeException If the current RemoteViews should not be reapplied onto the provided
     * View.
     */
    private RemoteViews getRemoteViewsToReapply(Context context, View v, @Nullable SizeF size) {
        RemoteViews rvToApply = getRemoteViewsToApply(context, size);

        // In the case that a view has this RemoteViews applied in one orientation or size, is
        // persisted across change, and has the RemoteViews re-applied in a different situation
        // (orientation or size), we throw an exception, since the layouts may be completely
        // unrelated.
        if (hasMultipleLayouts()) {
        // If the ViewID has been changed on the view, or is changed by the RemoteViews, we also
        // may throw an exception, as the RemoteViews will probably not apply properly.
        // However, we need to let potentially unrelated RemoteViews apply, as this lack of testing
        // is already used in production code in some apps.
        if (hasMultipleLayouts()
                || rvToApply.mViewId != View.NO_ID
                || v.getTag(R.id.remote_views_override_id) != null) {
            if (!rvToApply.canRecycleView(v)) {
                throw new RuntimeException("Attempting to re-apply RemoteViews to a view that" +
                        " that does not share the same root layout id.");
            }
        }

        return rvToApply;
    }

    // Note: topLevel should be true only for calls on the topLevel RemoteViews, internal calls
    // should set it to false.
    private void reapply(Context context, View v, InteractionHandler handler, SizeF size,
            ColorResources colorResources, boolean topLevel) {

        RemoteViews rvToApply = getRemoteViewsToReapply(context, v, size);

        rvToApply.performApply(v, (ViewGroup) v.getParent(), handler, colorResources);

        // If the parent of the view is has is a root, resolve the recycling.
@@ -5789,17 +5809,7 @@ public class RemoteViews implements Parcelable, Filter {
    public CancellationSignal reapplyAsync(Context context, View v, Executor executor,
            OnViewAppliedListener listener, InteractionHandler handler, SizeF size,
            ColorResources colorResources) {
        RemoteViews rvToApply = getRemoteViewsToApply(context, size);

        // In the case that a view has this RemoteViews applied in one orientation, is persisted
        // across orientation change, and has the RemoteViews re-applied in the new orientation,
        // we throw an exception, since the layouts may be completely unrelated.
        if (hasMultipleLayouts()) {
            if (!rvToApply.canRecycleView(v)) {
                throw new RuntimeException("Attempting to re-apply RemoteViews to a view that" +
                        " that does not share the same root layout id.");
            }
        }
        RemoteViews rvToApply = getRemoteViewsToReapply(context, v, size);

        return new AsyncApplyTask(rvToApply, (ViewGroup) v.getParent(),
                context, listener, handler, colorResources, v, true /* topLevel */)