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

Commit 908caf83 authored by Joshua Trask's avatar Joshua Trask Committed by Android (Google) Code Review
Browse files

Merge "Create ResolverComparatorModel interface."

parents db64a419 8b5d279d
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