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

Commit ae8fb7fa authored by Roshan Pius's avatar Roshan Pius Committed by Android (Google) Code Review
Browse files

Merge "Introduce `NoOpResolverComparator`." into main

parents fc6ff5a9 9f560c6e
Loading
Loading
Loading
Loading
+88 −0
Original line number Diff line number Diff line
/*
 * Copyright 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.internal.app;

import android.content.Context;
import android.content.Intent;
import android.content.pm.ResolveInfo;
import android.os.Message;
import android.os.UserHandle;

import androidx.annotation.Nullable;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
import com.android.internal.app.chooser.TargetInfo;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;


/**
 * A basic {@link AbstractResolverComparator} implementation that sorts items into the same order as
 * they appeared in the list provided to {@link #doCompute(List)}. "Unknown" items that didn't
 * appear in the original list are ordered arbitrarily at the end.
 */
public class NoOpResolverComparator extends AbstractResolverComparator {
    @Nullable
    private List<ResolveInfo> mOriginalTargetOrder = null;

    public NoOpResolverComparator(
            Context launchedFromContext,
            Intent intent,
            List<UserHandle> targetUserSpaceList) {
        super(launchedFromContext, intent, targetUserSpaceList);
    }

    @Override
    public void doCompute(List<ResolvedComponentInfo> targets) {
        mOriginalTargetOrder = new ArrayList<>();
        for (ResolvedComponentInfo target : targets) {
            mOriginalTargetOrder.add(target.getResolveInfoAt(0));
        }
        afterCompute();
    }

    @Override
    public int compare(ResolveInfo lhs, ResolveInfo rhs) {
        Comparator<ResolveInfo> c = Comparator.comparingDouble(r -> getScore((ResolveInfo) r));
        c = c.reversed();
        return c.compare(lhs, rhs);
    }

    @Override
    public float getScore(TargetInfo targetInfo) {
        return getScore(targetInfo.getResolveInfo());
    }

    @Override
    public void handleResultMessage(Message message) {}

    @VisibleForTesting
    public float getScore(ResolveInfo resolveInfo) {
        if (!mOriginalTargetOrder.contains(resolveInfo)) {
            return 0;
        }

        // Assign a score from 1 (for the first item in the original list) down
        // to 1/(n+1) for the last item (which is still greater than 0, the
        // score we assign to any unknown items).
        float rank = mOriginalTargetOrder.indexOf(resolveInfo);
        return 1.0f - (rank / (1 + mOriginalTargetOrder.size()));
    }
}
+22 −8
Original line number Diff line number Diff line
@@ -170,6 +170,10 @@ public class ResolverActivity extends Activity implements
    // Expected to be true if this object is ResolverActivity or is ResolverWrapperActivity.
    private final boolean mIsIntentPicker;

    // Whether this activity was instantiated with a specialized constructor that predefines a list
    // of resolutions to be displayed for the target intent (as in, e.g., the NFC use case).
    private boolean mHasSubclassSpecifiedResolutions;

    // Whether or not this activity supports choosing a default handler for the intent.
    @VisibleForTesting
    protected boolean mSupportsAlwaysUseOption;
@@ -421,6 +425,8 @@ public class ResolverActivity extends Activity implements
        setTheme(appliedThemeResId());
        super.onCreate(savedInstanceState);

        mHasSubclassSpecifiedResolutions = (rList != null);

        mQuietModeManager = createQuietModeManager();

        // Determine whether we should show that intent is forwarded
@@ -1698,17 +1704,25 @@ public class ResolverActivity extends Activity implements
                isAudioCaptureDevice, initialIntentsUserSpace);
    }

    @VisibleForTesting
    protected ResolverListController createListController(UserHandle userHandle) {
        UserHandle queryIntentsUser = getQueryIntentsUser(userHandle);
        ResolverRankerServiceResolverComparator resolverComparator =
                new ResolverRankerServiceResolverComparator(
    private AbstractResolverComparator makeResolverComparator(UserHandle userHandle) {
        if (mHasSubclassSpecifiedResolutions) {
            return new NoOpResolverComparator(
                    this, getTargetIntent(), getResolverRankerServiceUserHandleList(userHandle));
        } else {
            return new ResolverRankerServiceResolverComparator(
                   this,
                   getTargetIntent(),
                   getReferrerPackageName(),
                   null,
                   null,
                   getResolverRankerServiceUserHandleList(userHandle));
        }
    }

    @VisibleForTesting
    protected ResolverListController createListController(UserHandle userHandle) {
        UserHandle queryIntentsUser = getQueryIntentsUser(userHandle);
        AbstractResolverComparator resolverComparator = makeResolverComparator(userHandle);
        return new ResolverListController(
                this,
                mPm,
+108 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.internal.app;

import static com.google.common.truth.Truth.assertThat;

import android.content.Intent;
import android.os.UserHandle;

import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;

import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/** Unit tests for the behavior of {@link NoOpResolverComparator}. */
@RunWith(AndroidJUnit4.class)
public class NoOpResolverComparatorTest {

    private static final UserHandle PERSONAL_USER_HANDLE = InstrumentationRegistry
            .getInstrumentation().getTargetContext().getUser();

    public final ResolvedComponentInfo resolution1 =
            ResolverDataProvider.createResolvedComponentInfo(1, PERSONAL_USER_HANDLE);
    public final ResolvedComponentInfo resolution2 =
            ResolverDataProvider.createResolvedComponentInfo(2, PERSONAL_USER_HANDLE);
    public final ResolvedComponentInfo resolution3 =
            ResolverDataProvider.createResolvedComponentInfo(3, PERSONAL_USER_HANDLE);
    public final ResolvedComponentInfo resolution4 =
            ResolverDataProvider.createResolvedComponentInfo(4, PERSONAL_USER_HANDLE);

    private NoOpResolverComparator mComparator;

    @Before
    public void setUp() {
        mComparator = new NoOpResolverComparator(
                InstrumentationRegistry.getInstrumentation().getTargetContext(),
                new Intent(),
                List.of(PERSONAL_USER_HANDLE));
    }

    @Test
    public void testKnownItemsSortInOriginalOrder() {
        List<ResolvedComponentInfo> originalOrder = List.of(resolution1, resolution2, resolution3);
        mComparator.doCompute(originalOrder);

        List<ResolvedComponentInfo> queryOrder = new ArrayList<>(
                List.of(resolution2, resolution3, resolution1));

        Collections.sort(queryOrder, mComparator);
        assertThat(queryOrder).isEqualTo(originalOrder);
    }

    @Test
    public void testUnknownItemsSortAfterKnownItems() {
        List<ResolvedComponentInfo> originalOrder = List.of(resolution1, resolution2);
        mComparator.doCompute(originalOrder);

        // Query includes the unknown `resolution4`.
        List<ResolvedComponentInfo> queryOrder = new ArrayList<>(
                List.of(resolution2, resolution4, resolution1));
        Collections.sort(queryOrder, mComparator);

        assertThat(queryOrder).isEqualTo(List.of(resolution1, resolution2, resolution4));
    }

    @Test
    public void testKnownItemsGetNonZeroScoresInOrder() {
        List<ResolvedComponentInfo> originalOrder = List.of(resolution1, resolution2);
        mComparator.doCompute(originalOrder);

        float score1 = mComparator.getScore(resolution1.getResolveInfoAt(0));
        float score2 = mComparator.getScore(resolution2.getResolveInfoAt(0));

        assertThat(score1).isEqualTo(1.0f);
        assertThat(score2).isLessThan(score1);
        assertThat(score2).isGreaterThan(0.0f);
    }

    @Test
    public void testUnknownItemsGetZeroScore() {
        List<ResolvedComponentInfo> originalOrder = List.of(resolution1, resolution2);
        mComparator.doCompute(originalOrder);

        assertThat(mComparator.getScore(resolution3.getResolveInfoAt(0))).isEqualTo(0.0f);
    }
}