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

Commit 1748418e authored by Jernej Virag's avatar Jernej Virag Committed by Android (Google) Code Review
Browse files

Merge "Allow setting of LayoutInflater.Factory in RemoteViews" into udc-qpr-dev

parents ce5df3f1 4e2fb11d
Loading
Loading
Loading
Loading
+33 −0
Original line number Diff line number Diff line
@@ -413,6 +413,13 @@ public class RemoteViews implements Parcelable, Filter {
    /** Class cookies of the Parcel this instance was read from. */
    private Map<Class, Object> mClassCookies;

    /**
     * {@link LayoutInflater.Factory2} which will be passed into a {@link LayoutInflater} instance
     * used by this class.
     */
    @Nullable
    private LayoutInflater.Factory2 mLayoutInflaterFactory2;

    private static final InteractionHandler DEFAULT_INTERACTION_HANDLER =
            (view, pendingIntent, response) ->
                    startPendingIntent(view, pendingIntent, response.getLaunchOptions(view));
@@ -431,6 +438,29 @@ public class RemoteViews implements Parcelable, Filter {
        mActions.add(new SetRemoteInputsAction(viewId, remoteInputs));
    }

    /**
     * Sets {@link LayoutInflater.Factory2} to be passed into {@link LayoutInflater} used
     * by this class instance. It has to be set before the views are inflated to have any effect.
     *
     * The factory callbacks will be called on the background thread so the implementation needs
     * to be thread safe.
     *
     * @hide
     */
    public void setLayoutInflaterFactory(@Nullable LayoutInflater.Factory2 factory) {
        mLayoutInflaterFactory2 = factory;
    }

    /**
     * Returns currently set {@link LayoutInflater.Factory2}.
     *
     * @hide
     */
    @Nullable
    public LayoutInflater.Factory2 getLayoutInflaterFactory() {
        return mLayoutInflaterFactory2;
    }

    /**
     * Reduces all images and ensures that they are all below the given sizes.
     *
@@ -5659,6 +5689,9 @@ public class RemoteViews implements Parcelable, Filter {
        // we don't add a filter to the static version returned by getSystemService.
        inflater = inflater.cloneInContext(inflationContext);
        inflater.setFilter(shouldUseStaticFilter() ? INFLATER_FILTER : this);
        if (mLayoutInflaterFactory2 != null) {
            inflater.setFactory2(mLayoutInflaterFactory2);
        }
        View v = inflater.inflate(rv.getLayoutId(), parent, false);
        if (mViewId != View.NO_ID) {
            v.setId(mViewId);
+72 −0
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.spy;
@@ -39,11 +40,15 @@ import android.os.AsyncTask;
import android.os.Binder;
import android.os.Looper;
import android.os.Parcel;
import android.util.AttributeSet;
import android.util.SizeF;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -827,4 +832,71 @@ public class RemoteViewsTest {
        verify(visitor, times(1)).accept(eq(icon3S.getUri()));
        verify(visitor, times(1)).accept(eq(icon4S.getUri()));
    }

    @Test
    public void layoutInflaterFactory_nothingSet_returnsNull() {
        final RemoteViews rv = new RemoteViews(mPackage, R.layout.remote_views_test);
        assertNull(rv.getLayoutInflaterFactory());
    }

    @Test
    public void layoutInflaterFactory_replacesImageView_viewReplaced() {
        final RemoteViews rv = new RemoteViews(mPackage, R.layout.remote_views_test);
        final View replacement = new FrameLayout(mContext);
        replacement.setId(1337);

        LayoutInflater.Factory2 factory = createLayoutInflaterFactory("ImageView", replacement);
        rv.setLayoutInflaterFactory(factory);

        // Now inflate the views.
        View inflated = rv.apply(mContext, mContainer);

        assertEquals(factory, rv.getLayoutInflaterFactory());
        View replacedFrameLayout = inflated.findViewById(1337);
        assertNotNull(replacedFrameLayout);
        assertEquals(replacement, replacedFrameLayout);
        // ImageView should be fully replaced.
        assertNull(inflated.findViewById(R.id.image));
    }

    @Test
    public void layoutInflaterFactory_replacesImageView_settersStillFunctional() {
        final RemoteViews rv = new RemoteViews(mPackage, R.layout.remote_views_test);
        final TextView replacement = new TextView(mContext);
        replacement.setId(R.id.text);
        final String testText = "testText";
        rv.setLayoutInflaterFactory(createLayoutInflaterFactory("TextView", replacement));
        rv.setTextViewText(R.id.text, testText);


        // Now inflate the views.
        View inflated = rv.apply(mContext, mContainer);

        TextView replacedTextView = inflated.findViewById(R.id.text);
        assertSame(replacement, replacedTextView);
        assertEquals(testText, replacedTextView.getText());
    }

    private static LayoutInflater.Factory2 createLayoutInflaterFactory(String viewTypeToReplace,
            View replacementView) {
        return new LayoutInflater.Factory2() {
            @Nullable
            @Override
            public View onCreateView(@Nullable View parent, @NonNull String name,
                                     @NonNull Context context, @NonNull AttributeSet attrs) {
                if (viewTypeToReplace.equals(name)) {
                    return replacementView;
                }

                return null;
            }

            @Nullable
            @Override
            public View onCreateView(@NonNull String name, @NonNull Context context,
                                     @NonNull AttributeSet attrs) {
                return null;
            }
        };
    }
}
+3 −1
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ import android.os.Bundle;
import android.os.IBinder;
import android.os.Parcel;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.RemoteViews;

@@ -95,7 +96,8 @@ public class NotificationVisitUrisTest extends UiServiceTestCase {
    // Types that we can't really produce. No methods receiving these parameters will be invoked.
    private static final ImmutableSet<Class<?>> UNUSABLE_TYPES =
            ImmutableSet.of(Consumer.class, IBinder.class, MediaSession.Token.class, Parcel.class,
                    PrintWriter.class, Resources.Theme.class, View.class);
                    PrintWriter.class, Resources.Theme.class, View.class,
                    LayoutInflater.Factory2.class);

    // Maximum number of times we allow generating the same class recursively.
    // E.g. new RemoteViews.addView(new RemoteViews()) but stop there.