am b535c5e2: Merge "Start using some better sorting for intent resolution" into mnc-dev
* commit 'b535c5e28a7d689020461e9f9e8bed165eacc522': Start using some better sorting for intent resolution
This commit is contained in:
@@ -18,8 +18,6 @@ package com.android.internal.app;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.ActivityThread;
|
||||
import android.app.usage.UsageStats;
|
||||
import android.app.usage.UsageStatsManager;
|
||||
import android.os.AsyncTask;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
@@ -64,14 +62,11 @@ import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import com.android.internal.widget.ResolverDrawerLayout;
|
||||
|
||||
import java.text.Collator;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
|
||||
@@ -100,10 +95,7 @@ public class ResolverActivity extends Activity {
|
||||
private boolean mResolvingHome = false;
|
||||
private int mProfileSwitchMessageId = -1;
|
||||
private final ArrayList<Intent> mIntents = new ArrayList<>();
|
||||
|
||||
private UsageStatsManager mUsm;
|
||||
private Map<String, UsageStats> mStats;
|
||||
private static final long USAGE_STATS_PERIOD = 1000 * 60 * 60 * 24 * 14;
|
||||
private ResolverComparator mResolverComparator;
|
||||
|
||||
private boolean mRegistered;
|
||||
private final PackageMonitor mPackageMonitor = new PackageMonitor() {
|
||||
@@ -222,10 +214,6 @@ public class ResolverActivity extends Activity {
|
||||
}
|
||||
|
||||
mPm = getPackageManager();
|
||||
mUsm = (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE);
|
||||
|
||||
final long sinceTime = System.currentTimeMillis() - USAGE_STATS_PERIOD;
|
||||
mStats = mUsm.queryAndAggregateUsageStats(sinceTime, System.currentTimeMillis());
|
||||
|
||||
mPackageMonitor.register(this, getMainLooper(), false);
|
||||
mRegistered = true;
|
||||
@@ -236,6 +224,10 @@ public class ResolverActivity extends Activity {
|
||||
// Add our initial intent as the first item, regardless of what else has already been added.
|
||||
mIntents.add(0, new Intent(intent));
|
||||
|
||||
final String referrerPackage = getReferrerPackageName();
|
||||
|
||||
mResolverComparator = new ResolverComparator(this, getTargetIntent(), referrerPackage);
|
||||
|
||||
configureContentView(mIntents, initialIntents, rList, alwaysUseOption);
|
||||
|
||||
// Prevent the Resolver window from becoming the top fullscreen window and thus from taking
|
||||
@@ -265,7 +257,6 @@ public class ResolverActivity extends Activity {
|
||||
// Try to initialize the title icon if we have a view for it and a title to match
|
||||
final ImageView titleIcon = (ImageView) findViewById(R.id.title_icon);
|
||||
if (titleIcon != null) {
|
||||
final String referrerPackage = getReferrerPackageName();
|
||||
ApplicationInfo ai = null;
|
||||
try {
|
||||
if (!TextUtils.isEmpty(referrerPackage)) {
|
||||
@@ -1175,8 +1166,8 @@ public class ResolverActivity extends Activity {
|
||||
}
|
||||
}
|
||||
if (N > 1) {
|
||||
Collections.sort(currentResolveList,
|
||||
new ResolverComparator(ResolverActivity.this, getTargetIntent()));
|
||||
mResolverComparator.compute(currentResolveList);
|
||||
Collections.sort(currentResolveList, mResolverComparator);
|
||||
}
|
||||
// First put the initial items at the top.
|
||||
if (mInitialIntents != null) {
|
||||
@@ -1651,63 +1642,4 @@ public class ResolverActivity extends Activity {
|
||||
&& match <= IntentFilter.MATCH_CATEGORY_PATH;
|
||||
}
|
||||
|
||||
class ResolverComparator implements Comparator<ResolvedComponentInfo> {
|
||||
private final Collator mCollator;
|
||||
private final boolean mHttp;
|
||||
|
||||
public ResolverComparator(Context context, Intent intent) {
|
||||
mCollator = Collator.getInstance(context.getResources().getConfiguration().locale);
|
||||
String scheme = intent.getScheme();
|
||||
mHttp = "http".equals(scheme) || "https".equals(scheme);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(ResolvedComponentInfo lhsp, ResolvedComponentInfo rhsp) {
|
||||
final ResolveInfo lhs = lhsp.getResolveInfoAt(0);
|
||||
final ResolveInfo rhs = rhsp.getResolveInfoAt(0);
|
||||
|
||||
// We want to put the one targeted to another user at the end of the dialog.
|
||||
if (lhs.targetUserId != UserHandle.USER_CURRENT) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (mHttp) {
|
||||
// Special case: we want filters that match URI paths/schemes to be
|
||||
// ordered before others. This is for the case when opening URIs,
|
||||
// to make native apps go above browsers.
|
||||
final boolean lhsSpecific = isSpecificUriMatch(lhs.match);
|
||||
final boolean rhsSpecific = isSpecificUriMatch(rhs.match);
|
||||
if (lhsSpecific != rhsSpecific) {
|
||||
return lhsSpecific ? -1 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (mStats != null) {
|
||||
final long timeDiff =
|
||||
getPackageTimeSpent(rhs.activityInfo.packageName) -
|
||||
getPackageTimeSpent(lhs.activityInfo.packageName);
|
||||
|
||||
if (timeDiff != 0) {
|
||||
return timeDiff > 0 ? 1 : -1;
|
||||
}
|
||||
}
|
||||
|
||||
CharSequence sa = lhs.loadLabel(mPm);
|
||||
if (sa == null) sa = lhs.activityInfo.name;
|
||||
CharSequence sb = rhs.loadLabel(mPm);
|
||||
if (sb == null) sb = rhs.activityInfo.name;
|
||||
|
||||
return mCollator.compare(sa.toString(), sb.toString());
|
||||
}
|
||||
|
||||
private long getPackageTimeSpent(String packageName) {
|
||||
if (mStats != null) {
|
||||
final UsageStats stats = mStats.get(packageName);
|
||||
if (stats != null) {
|
||||
return stats.getTotalTimeInForeground();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
215
core/java/com/android/internal/app/ResolverComparator.java
Normal file
215
core/java/com/android/internal/app/ResolverComparator.java
Normal file
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.app.usage.UsageStats;
|
||||
import android.app.usage.UsageStatsManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.ComponentInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.os.UserHandle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
|
||||
|
||||
import java.text.Collator;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Ranks and compares packages based on usage stats.
|
||||
*/
|
||||
class ResolverComparator implements Comparator<ResolvedComponentInfo> {
|
||||
private static final String TAG = "ResolverComparator";
|
||||
|
||||
private static final boolean DEBUG = true;
|
||||
|
||||
// Two weeks
|
||||
private static final long USAGE_STATS_PERIOD = 1000 * 60 * 60 * 24 * 14;
|
||||
|
||||
private static final long RECENCY_TIME_PERIOD = 1000 * 60 * 60 * 12;
|
||||
|
||||
private static final float RECENCY_MULTIPLIER = 3.f;
|
||||
|
||||
private final Collator mCollator;
|
||||
private final boolean mHttp;
|
||||
private final PackageManager mPm;
|
||||
private final UsageStatsManager mUsm;
|
||||
private final Map<String, UsageStats> mStats;
|
||||
private final long mCurrentTime;
|
||||
private final long mSinceTime;
|
||||
private final LinkedHashMap<ComponentName, ScoredTarget> mScoredTargets = new LinkedHashMap<>();
|
||||
private final String mReferrerPackage;
|
||||
|
||||
public ResolverComparator(Context context, Intent intent, String referrerPackage) {
|
||||
mCollator = Collator.getInstance(context.getResources().getConfiguration().locale);
|
||||
String scheme = intent.getScheme();
|
||||
mHttp = "http".equals(scheme) || "https".equals(scheme);
|
||||
mReferrerPackage = referrerPackage;
|
||||
|
||||
mPm = context.getPackageManager();
|
||||
mUsm = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
|
||||
|
||||
mCurrentTime = System.currentTimeMillis();
|
||||
mSinceTime = mCurrentTime - USAGE_STATS_PERIOD;
|
||||
mStats = mUsm.queryAndAggregateUsageStats(mSinceTime, mCurrentTime);
|
||||
}
|
||||
|
||||
public void compute(List<ResolvedComponentInfo> targets) {
|
||||
mScoredTargets.clear();
|
||||
|
||||
final long recentSinceTime = mCurrentTime - RECENCY_TIME_PERIOD;
|
||||
|
||||
long mostRecentlyUsedTime = recentSinceTime + 1;
|
||||
long mostTimeSpent = 1;
|
||||
int mostLaunched = 1;
|
||||
|
||||
for (ResolvedComponentInfo target : targets) {
|
||||
final ScoredTarget scoredTarget
|
||||
= new ScoredTarget(target.getResolveInfoAt(0).activityInfo);
|
||||
mScoredTargets.put(target.name, scoredTarget);
|
||||
final UsageStats pkStats = mStats.get(target.name.getPackageName());
|
||||
if (pkStats != null) {
|
||||
// Only count recency for apps that weren't the caller
|
||||
// since the caller is always the most recent.
|
||||
// Persistent processes muck this up, so omit them too.
|
||||
if (!target.name.getPackageName().equals(mReferrerPackage)
|
||||
&& !isPersistentProcess(target)) {
|
||||
final long lastTimeUsed = pkStats.getLastTimeUsed();
|
||||
scoredTarget.lastTimeUsed = lastTimeUsed;
|
||||
if (lastTimeUsed > mostRecentlyUsedTime) {
|
||||
mostRecentlyUsedTime = lastTimeUsed;
|
||||
}
|
||||
}
|
||||
final long timeSpent = pkStats.getTotalTimeInForeground();
|
||||
scoredTarget.timeSpent = timeSpent;
|
||||
if (timeSpent > mostTimeSpent) {
|
||||
mostTimeSpent = timeSpent;
|
||||
}
|
||||
final int launched = pkStats.mLaunchCount;
|
||||
scoredTarget.launchCount = launched;
|
||||
if (launched > mostLaunched) {
|
||||
mostLaunched = launched;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "compute - mostRecentlyUsedTime: " + mostRecentlyUsedTime
|
||||
+ " mostTimeSpent: " + mostTimeSpent
|
||||
+ " recentSinceTime: " + recentSinceTime
|
||||
+ " mostLaunched: " + mostLaunched);
|
||||
}
|
||||
|
||||
for (ScoredTarget target : mScoredTargets.values()) {
|
||||
final float recency = (float) Math.max(target.lastTimeUsed - recentSinceTime, 0)
|
||||
/ (mostRecentlyUsedTime - recentSinceTime);
|
||||
final float recencyScore = recency * recency * RECENCY_MULTIPLIER;
|
||||
final float usageTimeScore = (float) target.timeSpent / mostTimeSpent;
|
||||
final float launchCountScore = (float) target.launchCount / mostLaunched;
|
||||
|
||||
target.score = recencyScore + usageTimeScore + launchCountScore;
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Scores: recencyScore: " + recencyScore
|
||||
+ " usageTimeScore: " + usageTimeScore
|
||||
+ " launchCountScore: " + launchCountScore
|
||||
+ " - " + target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static boolean isPersistentProcess(ResolvedComponentInfo rci) {
|
||||
if (rci != null && rci.getCount() > 0) {
|
||||
return (rci.getResolveInfoAt(0).activityInfo.applicationInfo.flags &
|
||||
ApplicationInfo.FLAG_PERSISTENT) != 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(ResolvedComponentInfo lhsp, ResolvedComponentInfo rhsp) {
|
||||
final ResolveInfo lhs = lhsp.getResolveInfoAt(0);
|
||||
final ResolveInfo rhs = rhsp.getResolveInfoAt(0);
|
||||
|
||||
// We want to put the one targeted to another user at the end of the dialog.
|
||||
if (lhs.targetUserId != UserHandle.USER_CURRENT) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (mHttp) {
|
||||
// Special case: we want filters that match URI paths/schemes to be
|
||||
// ordered before others. This is for the case when opening URIs,
|
||||
// to make native apps go above browsers.
|
||||
final boolean lhsSpecific = ResolverActivity.isSpecificUriMatch(lhs.match);
|
||||
final boolean rhsSpecific = ResolverActivity.isSpecificUriMatch(rhs.match);
|
||||
if (lhsSpecific != rhsSpecific) {
|
||||
return lhsSpecific ? -1 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (mStats != null) {
|
||||
final ScoredTarget lhsTarget = mScoredTargets.get(new ComponentName(
|
||||
lhs.activityInfo.packageName, lhs.activityInfo.name));
|
||||
final ScoredTarget rhsTarget = mScoredTargets.get(new ComponentName(
|
||||
rhs.activityInfo.packageName, rhs.activityInfo.name));
|
||||
final float diff = rhsTarget.score - lhsTarget.score;
|
||||
|
||||
if (diff != 0) {
|
||||
return diff > 0 ? 1 : -1;
|
||||
}
|
||||
}
|
||||
|
||||
CharSequence sa = lhs.loadLabel(mPm);
|
||||
if (sa == null) sa = lhs.activityInfo.name;
|
||||
CharSequence sb = rhs.loadLabel(mPm);
|
||||
if (sb == null) sb = rhs.activityInfo.name;
|
||||
|
||||
return mCollator.compare(sa.toString().trim(), sb.toString().trim());
|
||||
}
|
||||
|
||||
static class ScoredTarget {
|
||||
public final ComponentInfo componentInfo;
|
||||
public float score;
|
||||
public long lastTimeUsed;
|
||||
public long timeSpent;
|
||||
public long launchCount;
|
||||
|
||||
public ScoredTarget(ComponentInfo ci) {
|
||||
componentInfo = ci;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ScoredTarget{" + componentInfo
|
||||
+ " score: " + score
|
||||
+ " lastTimeUsed: " + lastTimeUsed
|
||||
+ " timeSpent: " + timeSpent
|
||||
+ " launchCount: " + launchCount
|
||||
+ "}";
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user