Loading core/java/com/android/internal/app/ResolverActivity.java +7 −75 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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() { Loading Loading @@ -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; Loading @@ -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 Loading Loading @@ -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)) { Loading Loading @@ -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) { Loading Loading @@ -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; } } } core/java/com/android/internal/app/ResolverComparator.java 0 → 100644 +215 −0 Original line number Diff line number Diff line /* * 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 + "}"; } } } Loading
core/java/com/android/internal/app/ResolverActivity.java +7 −75 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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() { Loading Loading @@ -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; Loading @@ -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 Loading Loading @@ -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)) { Loading Loading @@ -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) { Loading Loading @@ -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; } } }
core/java/com/android/internal/app/ResolverComparator.java 0 → 100644 +215 −0 Original line number Diff line number Diff line /* * 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 + "}"; } } }