Loading core/java/com/android/internal/app/NoOpResolverComparator.java 0 → 100644 +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())); } } core/java/com/android/internal/app/ResolverActivity.java +22 −8 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 Loading Loading @@ -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, Loading core/tests/coretests/src/com/android/internal/app/NoOpResolverComparatorTest.java 0 → 100644 +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); } } Loading
core/java/com/android/internal/app/NoOpResolverComparator.java 0 → 100644 +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())); } }
core/java/com/android/internal/app/ResolverActivity.java +22 −8 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 Loading Loading @@ -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, Loading
core/tests/coretests/src/com/android/internal/app/NoOpResolverComparatorTest.java 0 → 100644 +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); } }