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

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

Correct recycling to take setViewId into account.

Views shouldn't be recycled if the view id is changed as the identity
of the view is then altered.

Second pass: this has been further tested by adding/removing AppWidgets
on a test phone.

Bug: 181985606
Test: atest CtsWidgetTestCases:RemoteViewsTest
Test: atest CtsWidgetTestCases:RemoteViewsRecyclingTest
Test: atest CtsInputMethodTestCases:android.view.inputmethod.cts.InputConnectionBlockingMethodTest
Change-Id: I8924c02f7f0223458f556a07a3dfdc96b4ce612e
parent b4a4b46b
Loading
Loading
Loading
Loading
+1 −3
Original line number Diff line number Diff line
@@ -520,9 +520,7 @@ public class AppWidgetHostView extends FrameLayout {
                return;
            }
            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) {
            if (rvToApply.canRecycleView(mView)) {
                try {
                    rvToApply.reapply(mContext, mView, mInteractionHandler, mCurrentSize,
                            mColorResources);
+24 −4
Original line number Diff line number Diff line
@@ -2196,7 +2196,7 @@ public class RemoteViews implements Parcelable, Filter {
                int recycledViewIndex = findViewIndexToRecycle(target, rvToApply);
                if (recycledViewIndex >= 0) {
                    View child = target.getChildAt(recycledViewIndex);
                    if (getViewLayoutId(child) == rvToApply.getLayoutId()) {
                    if (rvToApply.canRecycleView(child)) {
                        if (nextChild < recycledViewIndex) {
                            target.removeViews(nextChild, recycledViewIndex - nextChild);
                        }
@@ -2254,7 +2254,7 @@ public class RemoteViews implements Parcelable, Filter {
                    // application are placed before.
                    ViewTree recycled = target.mChildren.get(recycledViewIndex);
                    // We can only recycle the view if the layout id is the same.
                    if (getViewLayoutId(recycled.mRoot) == rvToApply.getLayoutId()) {
                    if (rvToApply.canRecycleView(recycled.mRoot)) {
                        if (recycledViewIndex > nextChild) {
                            target.removeChildren(nextChild, recycledViewIndex - nextChild);
                        }
@@ -3726,7 +3726,8 @@ public class RemoteViews implements Parcelable, Filter {
     *
     * The {@code stableId} will be used to identify a potential view to recycled when the remote
     * view is inflated. Views can be re-used if inserted in the same order, potentially with
     * some views appearing / disappearing.
     * some views appearing / disappearing. To be recycled the view must not change the layout
     * used to inflate it or its view id (see {@link RemoteViews#setViewId}).
     *
     * Note: if a view is re-used, all the actions will be re-applied on it. However, its properties
     * are not reset, so what was applied in previous round will have an effect. As a view may be
@@ -5116,6 +5117,7 @@ public class RemoteViews implements Parcelable, Filter {
        View v = inflater.inflate(rv.getLayoutId(), parent, false);
        if (mViewId != View.NO_ID) {
            v.setId(mViewId);
            v.setTagInternal(R.id.remote_views_override_id, mViewId);
        }
        v.setTagInternal(R.id.widget_frame, rv.getLayoutId());
        return v;
@@ -5335,6 +5337,24 @@ public class RemoteViews implements Parcelable, Filter {
        reapply(context, v, handler, size, colorResources, true);
    }

    /** @hide */
    public boolean canRecycleView(@Nullable View v) {
        if (v == null) {
            return false;
        }
        Integer previousLayoutId = (Integer) v.getTag(R.id.widget_frame);
        if (previousLayoutId == null) {
            return false;
        }
        Integer overrideIdTag = (Integer) v.getTag(R.id.remote_views_override_id);
        int overrideId = overrideIdTag == null ? View.NO_ID : overrideIdTag;
        // If mViewId is View.NO_ID, we only recycle if overrideId is also View.NO_ID.
        // Otherwise, it might be that, on a previous iteration, the view's ID was set to
        // something else, and it should now be reset to the ID defined in the XML layout file,
        // whatever it is.
        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,
@@ -5347,7 +5367,7 @@ public class RemoteViews implements Parcelable, Filter {
        // (orientation or size), we throw an exception, since the layouts may be completely
        // unrelated.
        if (hasMultipleLayouts()) {
            if ((Integer) v.getTag(R.id.widget_frame) != rvToApply.getLayoutId()) {
            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.");
            }
+4 −1
Original line number Diff line number Diff line
@@ -253,4 +253,7 @@

  <!-- View tag associating a view with its stable id for potential recycling. -->
  <item type="id" name="remote_views_stable_id" />

  <!-- View tag associating a view with its overridden id, to ensure valid recycling only. -->
  <item type="id" name="remote_views_override_id" />
</resources>
+3 −0
Original line number Diff line number Diff line
@@ -4321,8 +4321,11 @@
  <java-symbol type="bool" name="config_cecRcProfileSourceMediaContextSensitiveMenuNotHandled_allowed" />
  <java-symbol type="bool" name="config_cecRcProfileSourceMediaContextSensitiveMenuNotHandled_default" />

  <!-- Ids for RemoteViews -->
  <java-symbol type="id" name="remote_views_next_child" />
  <java-symbol type="id" name="remote_views_stable_id" />
  <java-symbol type="id" name="remote_views_override_id" />

  <!-- View and control prompt -->
  <java-symbol type="drawable" name="ic_accessibility_24dp" />
  <java-symbol type="string" name="view_and_control_notification_title" />