Roll android_tools support library to 25.1.0
[android_tools.git] / sdk / sources / android-23 / com / android / internal / app / ResolverComparator.java
blob31556e29bc3237e740c72bca47879afa79b1cba0
1 /*
2 * Copyright (C) 2015 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 package com.android.internal.app;
20 import android.app.usage.UsageStats;
21 import android.app.usage.UsageStatsManager;
22 import android.content.ComponentName;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.IntentFilter;
26 import android.content.pm.ApplicationInfo;
27 import android.content.pm.ComponentInfo;
28 import android.content.pm.PackageManager;
29 import android.content.pm.ResolveInfo;
30 import android.os.UserHandle;
31 import android.text.TextUtils;
32 import android.util.Log;
33 import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
35 import java.text.Collator;
36 import java.util.ArrayList;
37 import java.util.Comparator;
38 import java.util.LinkedHashMap;
39 import java.util.List;
40 import java.util.Map;
42 /**
43 * Ranks and compares packages based on usage stats.
45 class ResolverComparator implements Comparator<ResolvedComponentInfo> {
46 private static final String TAG = "ResolverComparator";
48 private static final boolean DEBUG = false;
50 // Two weeks
51 private static final long USAGE_STATS_PERIOD = 1000 * 60 * 60 * 24 * 14;
53 private static final long RECENCY_TIME_PERIOD = 1000 * 60 * 60 * 12;
55 private static final float RECENCY_MULTIPLIER = 3.f;
57 private final Collator mCollator;
58 private final boolean mHttp;
59 private final PackageManager mPm;
60 private final UsageStatsManager mUsm;
61 private final Map<String, UsageStats> mStats;
62 private final long mCurrentTime;
63 private final long mSinceTime;
64 private final LinkedHashMap<ComponentName, ScoredTarget> mScoredTargets = new LinkedHashMap<>();
65 private final String mReferrerPackage;
67 public ResolverComparator(Context context, Intent intent, String referrerPackage) {
68 mCollator = Collator.getInstance(context.getResources().getConfiguration().locale);
69 String scheme = intent.getScheme();
70 mHttp = "http".equals(scheme) || "https".equals(scheme);
71 mReferrerPackage = referrerPackage;
73 mPm = context.getPackageManager();
74 mUsm = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
76 mCurrentTime = System.currentTimeMillis();
77 mSinceTime = mCurrentTime - USAGE_STATS_PERIOD;
78 mStats = mUsm.queryAndAggregateUsageStats(mSinceTime, mCurrentTime);
81 public void compute(List<ResolvedComponentInfo> targets) {
82 mScoredTargets.clear();
84 final long recentSinceTime = mCurrentTime - RECENCY_TIME_PERIOD;
86 long mostRecentlyUsedTime = recentSinceTime + 1;
87 long mostTimeSpent = 1;
88 int mostLaunched = 1;
90 for (ResolvedComponentInfo target : targets) {
91 final ScoredTarget scoredTarget
92 = new ScoredTarget(target.getResolveInfoAt(0).activityInfo);
93 mScoredTargets.put(target.name, scoredTarget);
94 final UsageStats pkStats = mStats.get(target.name.getPackageName());
95 if (pkStats != null) {
96 // Only count recency for apps that weren't the caller
97 // since the caller is always the most recent.
98 // Persistent processes muck this up, so omit them too.
99 if (!target.name.getPackageName().equals(mReferrerPackage)
100 && !isPersistentProcess(target)) {
101 final long lastTimeUsed = pkStats.getLastTimeUsed();
102 scoredTarget.lastTimeUsed = lastTimeUsed;
103 if (lastTimeUsed > mostRecentlyUsedTime) {
104 mostRecentlyUsedTime = lastTimeUsed;
107 final long timeSpent = pkStats.getTotalTimeInForeground();
108 scoredTarget.timeSpent = timeSpent;
109 if (timeSpent > mostTimeSpent) {
110 mostTimeSpent = timeSpent;
112 final int launched = pkStats.mLaunchCount;
113 scoredTarget.launchCount = launched;
114 if (launched > mostLaunched) {
115 mostLaunched = launched;
121 if (DEBUG) {
122 Log.d(TAG, "compute - mostRecentlyUsedTime: " + mostRecentlyUsedTime
123 + " mostTimeSpent: " + mostTimeSpent
124 + " recentSinceTime: " + recentSinceTime
125 + " mostLaunched: " + mostLaunched);
128 for (ScoredTarget target : mScoredTargets.values()) {
129 final float recency = (float) Math.max(target.lastTimeUsed - recentSinceTime, 0)
130 / (mostRecentlyUsedTime - recentSinceTime);
131 final float recencyScore = recency * recency * RECENCY_MULTIPLIER;
132 final float usageTimeScore = (float) target.timeSpent / mostTimeSpent;
133 final float launchCountScore = (float) target.launchCount / mostLaunched;
135 target.score = recencyScore + usageTimeScore + launchCountScore;
136 if (DEBUG) {
137 Log.d(TAG, "Scores: recencyScore: " + recencyScore
138 + " usageTimeScore: " + usageTimeScore
139 + " launchCountScore: " + launchCountScore
140 + " - " + target);
145 static boolean isPersistentProcess(ResolvedComponentInfo rci) {
146 if (rci != null && rci.getCount() > 0) {
147 return (rci.getResolveInfoAt(0).activityInfo.applicationInfo.flags &
148 ApplicationInfo.FLAG_PERSISTENT) != 0;
150 return false;
153 @Override
154 public int compare(ResolvedComponentInfo lhsp, ResolvedComponentInfo rhsp) {
155 final ResolveInfo lhs = lhsp.getResolveInfoAt(0);
156 final ResolveInfo rhs = rhsp.getResolveInfoAt(0);
158 // We want to put the one targeted to another user at the end of the dialog.
159 if (lhs.targetUserId != UserHandle.USER_CURRENT) {
160 return 1;
163 if (mHttp) {
164 // Special case: we want filters that match URI paths/schemes to be
165 // ordered before others. This is for the case when opening URIs,
166 // to make native apps go above browsers.
167 final boolean lhsSpecific = ResolverActivity.isSpecificUriMatch(lhs.match);
168 final boolean rhsSpecific = ResolverActivity.isSpecificUriMatch(rhs.match);
169 if (lhsSpecific != rhsSpecific) {
170 return lhsSpecific ? -1 : 1;
174 if (mStats != null) {
175 final ScoredTarget lhsTarget = mScoredTargets.get(new ComponentName(
176 lhs.activityInfo.packageName, lhs.activityInfo.name));
177 final ScoredTarget rhsTarget = mScoredTargets.get(new ComponentName(
178 rhs.activityInfo.packageName, rhs.activityInfo.name));
179 final float diff = rhsTarget.score - lhsTarget.score;
181 if (diff != 0) {
182 return diff > 0 ? 1 : -1;
186 CharSequence sa = lhs.loadLabel(mPm);
187 if (sa == null) sa = lhs.activityInfo.name;
188 CharSequence sb = rhs.loadLabel(mPm);
189 if (sb == null) sb = rhs.activityInfo.name;
191 return mCollator.compare(sa.toString().trim(), sb.toString().trim());
194 public float getScore(ComponentName name) {
195 final ScoredTarget target = mScoredTargets.get(name);
196 if (target != null) {
197 return target.score;
199 return 0;
202 static class ScoredTarget {
203 public final ComponentInfo componentInfo;
204 public float score;
205 public long lastTimeUsed;
206 public long timeSpent;
207 public long launchCount;
209 public ScoredTarget(ComponentInfo ci) {
210 componentInfo = ci;
213 @Override
214 public String toString() {
215 return "ScoredTarget{" + componentInfo
216 + " score: " + score
217 + " lastTimeUsed: " + lastTimeUsed
218 + " timeSpent: " + timeSpent
219 + " launchCount: " + launchCount
220 + "}";