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

Commit 5e6edca7 authored by James O'Leary's avatar James O'Leary Committed by Automerger Merge Worker
Browse files

Merge "Create ResolverComparatorModel interface." into tm-dev am: ca46e52c

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/17913997



Change-Id: I86021827078bcf547a6b56cbdcb6f297e24ba693
Ignore-AOSP-First: this is an automerge
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents c55f2b59 ca46e52c
Loading
Loading
Loading
Loading
+0 −6
Original line number Diff line number Diff line
@@ -228,12 +228,6 @@ public abstract class AbstractResolverComparator implements Comparator<ResolvedC
     */
    abstract float getScore(ComponentName name);

    /**
     * Returns the list of top K component names which have highest
     * {@link #getScore(ComponentName)}
     */
    abstract List<ComponentName> getTopComponentNames(int topK);

    /** Handles result message sent to mHandler. */
    abstract void handleResultMessage(Message message);

+101 −50
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.internal.app;

import static android.app.prediction.AppTargetEvent.ACTION_LAUNCH;

import android.annotation.Nullable;
import android.app.prediction.AppPredictor;
import android.app.prediction.AppTarget;
import android.app.prediction.AppTargetEvent;
@@ -33,12 +34,11 @@ import android.util.Log;
import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

/**
 * Uses an {@link AppPredictor} to sort Resolver targets. If the AppPredictionService appears to be
@@ -58,7 +58,9 @@ class AppPredictionServiceResolverComparator extends AbstractResolverComparator
    private final String mReferrerPackage;
    // If this is non-null (and this is not destroyed), it means APS is disabled and we should fall
    // back to using the ResolverRankerService.
    // TODO: responsibility for this fallback behavior can live outside of the AppPrediction client.
    private ResolverRankerServiceResolverComparator mResolverRankerService;
    private AppPredictionServiceComparatorModel mComparatorModel;

    AppPredictionServiceResolverComparator(
            Context context,
@@ -74,25 +76,12 @@ class AppPredictionServiceResolverComparator extends AbstractResolverComparator
        mUser = user;
        mReferrerPackage = referrerPackage;
        setChooserActivityLogger(chooserActivityLogger);
        mComparatorModel = buildUpdatedModel();
    }

    @Override
    int compare(ResolveInfo lhs, ResolveInfo rhs) {
        if (mResolverRankerService != null) {
            return mResolverRankerService.compare(lhs, rhs);
        }
        Integer lhsRank = mTargetRanks.get(new ComponentName(lhs.activityInfo.packageName,
                lhs.activityInfo.name));
        Integer rhsRank = mTargetRanks.get(new ComponentName(rhs.activityInfo.packageName,
                rhs.activityInfo.name));
        if (lhsRank == null && rhsRank == null) {
            return 0;
        } else if (lhsRank == null) {
            return -1;
        } else if (rhsRank == null) {
            return 1;
        }
        return lhsRank - rhsRank;
        return mComparatorModel.getComparator().compare(lhs, rhs);
    }

    @Override
@@ -121,6 +110,7 @@ class AppPredictionServiceResolverComparator extends AbstractResolverComparator
                                        mContext, mIntent, mReferrerPackage,
                                        () -> mHandler.sendEmptyMessage(RANKER_SERVICE_RESULT),
                                        getChooserActivityLogger());
                        mComparatorModel = buildUpdatedModel();
                        mResolverRankerService.compute(targets);
                    } else {
                        Log.i(TAG, "AppPredictionService response received");
@@ -163,6 +153,7 @@ class AppPredictionServiceResolverComparator extends AbstractResolverComparator
            mTargetRanks.put(componentName, i);
            Log.i(TAG, "handleSortedAppTargets, sortedAppTargets #" + i + ": " + componentName);
        }
        mComparatorModel = buildUpdatedModel();
    }

    private boolean checkAppTargetRankValid(List<AppTarget> sortedAppTargets) {
@@ -176,6 +167,85 @@ class AppPredictionServiceResolverComparator extends AbstractResolverComparator

    @Override
    float getScore(ComponentName name) {
        return mComparatorModel.getScore(name);
    }

    @Override
    void updateModel(ComponentName componentName) {
        mComparatorModel.notifyOnTargetSelected(componentName);
    }

    @Override
    void destroy() {
        if (mResolverRankerService != null) {
            mResolverRankerService.destroy();
            mResolverRankerService = null;
            mComparatorModel = buildUpdatedModel();
        }
    }

    /**
     * Re-construct an {@code AppPredictionServiceComparatorModel} to replace the current model
     * instance (if any) using the up-to-date {@code AppPredictionServiceResolverComparator} ivar
     * values.
     *
     * TODO: each time we replace the model instance, we're either updating the model to use
     * adjusted data (which is appropriate), or we're providing a (late) value for one of our ivars
     * that wasn't available the last time the model was updated. For those latter cases, we should
     * just avoid creating the model altogether until we have all the prerequisites we'll need. Then
     * we can probably simplify the logic in {@code AppPredictionServiceComparatorModel} since we
     * won't need to handle edge cases when the model data isn't fully prepared.
     * (In some cases, these kinds of "updates" might interleave -- e.g., we might have finished
     * initializing the first time and now want to adjust some data, but still need to wait for
     * changes to propagate to the other ivars before rebuilding the model.)
     */
    private AppPredictionServiceComparatorModel buildUpdatedModel() {
        return new AppPredictionServiceComparatorModel(
                mAppPredictor, mResolverRankerService, mUser, mTargetRanks);
    }

    // TODO: Finish separating behaviors of AbstractResolverComparator, then (probably) make this a
    // standalone class once clients are written in terms of ResolverComparatorModel.
    static class AppPredictionServiceComparatorModel implements ResolverComparatorModel {
        private final AppPredictor mAppPredictor;
        private final ResolverRankerServiceResolverComparator mResolverRankerService;
        private final UserHandle mUser;
        private final Map<ComponentName, Integer> mTargetRanks;  // Treat as immutable.

        AppPredictionServiceComparatorModel(
                AppPredictor appPredictor,
                @Nullable ResolverRankerServiceResolverComparator resolverRankerService,
                UserHandle user,
                Map<ComponentName, Integer> targetRanks) {
            mAppPredictor = appPredictor;
            mResolverRankerService = resolverRankerService;
            mUser = user;
            mTargetRanks = targetRanks;
        }

        @Override
        public Comparator<ResolveInfo> getComparator() {
            return (lhs, rhs) -> {
                if (mResolverRankerService != null) {
                    return mResolverRankerService.compare(lhs, rhs);
                }
                Integer lhsRank = mTargetRanks.get(new ComponentName(lhs.activityInfo.packageName,
                        lhs.activityInfo.name));
                Integer rhsRank = mTargetRanks.get(new ComponentName(rhs.activityInfo.packageName,
                        rhs.activityInfo.name));
                if (lhsRank == null && rhsRank == null) {
                    return 0;
                } else if (lhsRank == null) {
                    return -1;
                } else if (rhsRank == null) {
                    return 1;
                }
                return lhsRank - rhsRank;
            };
        }

        @Override
        public float getScore(ComponentName name) {
            if (mResolverRankerService != null) {
                return mResolverRankerService.getScore(name);
            }
@@ -189,19 +259,7 @@ class AppPredictionServiceResolverComparator extends AbstractResolverComparator
        }

        @Override
    List<ComponentName> getTopComponentNames(int topK) {
        if (mResolverRankerService != null) {
            return mResolverRankerService.getTopComponentNames(topK);
        }
        return mTargetRanks.entrySet().stream()
                .sorted(Entry.comparingByValue())
                .limit(topK)
                .map(Entry::getKey)
                .collect(Collectors.toList());
    }

    @Override
    void updateModel(ComponentName componentName) {
        public void notifyOnTargetSelected(ComponentName componentName) {
            if (mResolverRankerService != null) {
                mResolverRankerService.updateModel(componentName);
                return;
@@ -214,12 +272,5 @@ class AppPredictionServiceResolverComparator extends AbstractResolverComparator
                            .setClassName(componentName.getClassName()).build(),
                        ACTION_LAUNCH).build());
        }

    @Override
    void destroy() {
        if (mResolverRankerService != null) {
            mResolverRankerService.destroy();
            mResolverRankerService = null;
        }
    }
}
+57 −0
Original line number Diff line number Diff line
/*
 * Copyright 2022 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.ComponentName;
import android.content.pm.ResolveInfo;

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

/**
 * A ranking model for resolver targets, providing ordering and (optionally) numerical scoring.
 *
 * As required by the {@link Comparator} contract, objects returned by {@code getComparator()} must
 * apply a total ordering on its inputs consistent across all calls to {@code Comparator#compare()}.
 * Other query methods and ranking feedback should refer to that same ordering, so implementors are
 * generally advised to "lock in" an immutable snapshot of their model data when this object is
 * initialized (preferring to replace the entire {@code ResolverComparatorModel} instance if the
 * backing data needs to be updated in the future).
 */
interface ResolverComparatorModel {
    /**
     * Get a {@code Comparator} that can be used to sort {@code ResolveInfo} targets according to
     * the model ranking.
     */
    Comparator<ResolveInfo> getComparator();

    /**
     * Get the numerical score, if any, that the model assigns to the component with the specified
     * {@code name}. Scores range from zero to one, with one representing the highest possible
     * likelihood that the user will select that component as the target. Implementations that don't
     * assign numerical scores are <em>recommended</em> to return a value of 0 for all components.
     */
    float getScore(ComponentName name);

    /**
     * Notify the model that the user selected a target. (Models may log this information, use it as
     * a feedback signal for their ranking, etc.) Because the data in this
     * {@code ResolverComparatorModel} instance is immutable, clients will need to get an up-to-date
     * instance in order to see any changes in the ranking that might result from this feedback.
     */
    void notifyOnTargetSelected(ComponentName componentName);
}
+0 −8
Original line number Diff line number Diff line
@@ -155,14 +155,6 @@ public class ResolverListAdapter extends BaseAdapter {
        return mResolverListController.getScore(componentName);
    }

    /**
     * Returns the list of top K component names which have highest
     * {@link #getScore(DisplayResolveInfo)}
     */
    public List<ComponentName> getTopComponentNames(int topK) {
        return mResolverListController.getTopComponentNames(topK);
    }

    public void updateModel(ComponentName componentName) {
        mResolverListController.updateModel(componentName);
    }
+0 −8
Original line number Diff line number Diff line
@@ -393,14 +393,6 @@ public class ResolverListController {
        return mResolverComparator.getScore(componentName);
    }

    /**
     * Returns the list of top K component names which have highest
     * {@link #getScore(DisplayResolveInfo)}
     */
    public List<ComponentName> getTopComponentNames(int topK) {
        return mResolverComparator.getTopComponentNames(topK);
    }

    public void updateModel(ComponentName componentName) {
        mResolverComparator.updateModel(componentName);
    }
Loading