diff --git a/app/src/main/java/com/android/launcher3/Launcher.java b/app/src/main/java/com/android/launcher3/Launcher.java index 917c8e007..1801500f1 100644 --- a/app/src/main/java/com/android/launcher3/Launcher.java +++ b/app/src/main/java/com/android/launcher3/Launcher.java @@ -99,13 +99,13 @@ import com.android.launcher3.DropTarget.DragObject; import com.android.launcher3.PagedView.PageSwitchListener; import com.android.launcher3.allapps.AllAppsContainerView; +import com.android.launcher3.allapps.PredictiveAppsProvider; import com.android.launcher3.compat.AppWidgetManagerCompat; import com.android.launcher3.compat.LauncherActivityInfoCompat; import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.model.WidgetsModel; -import com.android.launcher3.testing.LauncherExtension; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.LongArrayMap; import com.android.launcher3.util.Thunk; @@ -431,6 +431,8 @@ protected void onCreate(Bundle savedInstanceState) { .build()); } + predictiveAppsProvider = new PredictiveAppsProvider(this); + if (mLauncherCallbacks != null) { mLauncherCallbacks.preOnCreate(); } @@ -1071,6 +1073,8 @@ protected void onResume() { if (mLauncherCallbacks != null) { mLauncherCallbacks.onResume(); } + + tryAndUpdatePredictedApps(); } @Override @@ -2951,10 +2955,12 @@ private boolean startActivity(View v, Intent intent, Object tag) { } try { success = startActivity(v, intent, tag); + predictiveAppsProvider.updateComponentCount(intent.getComponent()); } catch (ActivityNotFoundException e) { Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show(); Log.e(TAG, "Unable to launch. tag=" + tag + " intent=" + intent, e); } + return success; } @@ -3461,11 +3467,16 @@ void exitSpringLoadedDragMode() { * resumed. */ private void tryAndUpdatePredictedApps() { + List apps; if (mLauncherCallbacks != null) { - List apps = mLauncherCallbacks.getPredictedApps(); - if (apps != null) { - mAppsView.setPredictedApps(apps); - } + apps = mLauncherCallbacks.getPredictedApps(); + } else { + apps = predictiveAppsProvider.getPredictions(); + predictiveAppsProvider.updateTopPredictedApps(); + } + + if (apps != null) { + mAppsView.setPredictedApps(apps); } } @@ -4762,9 +4773,11 @@ public Void doInBackground(Void ... args) { }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null); } } + + private PredictiveAppsProvider predictiveAppsProvider; } interface DebugIntents { static final String DELETE_DATABASE = "com.android.launcher3.action.DELETE_DATABASE"; static final String MIGRATE_DATABASE = "com.android.launcher3.action.MIGRATE_DATABASE"; -} +} \ No newline at end of file diff --git a/app/src/main/java/com/android/launcher3/allapps/AlphabeticalAppsList.java b/app/src/main/java/com/android/launcher3/allapps/AlphabeticalAppsList.java index 47241ce5d..20d56a0df 100644 --- a/app/src/main/java/com/android/launcher3/allapps/AlphabeticalAppsList.java +++ b/app/src/main/java/com/android/launcher3/allapps/AlphabeticalAppsList.java @@ -15,6 +15,7 @@ */ package com.android.launcher3.allapps; +import android.content.ComponentName; import android.content.Context; import android.support.v7.widget.RecyclerView; import android.util.Log; diff --git a/app/src/main/java/com/android/launcher3/allapps/PredictiveAppsProvider.java b/app/src/main/java/com/android/launcher3/allapps/PredictiveAppsProvider.java new file mode 100644 index 000000000..f080d7d1b --- /dev/null +++ b/app/src/main/java/com/android/launcher3/allapps/PredictiveAppsProvider.java @@ -0,0 +1,123 @@ +package com.android.launcher3.allapps; + + +import android.content.ComponentName; +import android.content.Context; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; +import android.util.Log; + +import com.android.launcher3.compat.UserHandleCompat; +import com.android.launcher3.util.ComponentKey; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class PredictiveAppsProvider { + private static final int NUM_PREDICTIVE_APPS_TO_HOLD = 9; // since we can't have more than 9 columns + + private static final String PREDICTIVE_APPS_KEY = "predictive_apps"; + private static final String TOP_PREDICTIVE_APPS_KEY = "top_predictive_apps"; + + private SharedPreferences sharedPreferences; + private Context context; + + public PredictiveAppsProvider(Context context) { + this.context = context; + this.sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); + } + + public void updateComponentCount(ComponentName component) { + String key = buildComponentString(component); + long current = sharedPreferences.getLong(key, 0); + + sharedPreferences.edit().putLong(key, current + 1).commit(); + + // ensure that the set of predictive apps contains this one + Set predictiveApps = + sharedPreferences.getStringSet(PREDICTIVE_APPS_KEY, new HashSet()); + if (!predictiveApps.contains(key)) { + predictiveApps.add(key); + sharedPreferences.edit().putStringSet(PREDICTIVE_APPS_KEY, predictiveApps).commit(); + } + } + + public void updateTopPredictedApps() { + new Thread(new Runnable() { + @Override + public void run() { + List< PredictedApp > allPredictions = new ArrayList<>(); + Set predictiveAppsSet = + sharedPreferences.getStringSet(PREDICTIVE_APPS_KEY, new HashSet()); + + for (String s : predictiveAppsSet) { + allPredictions.add(new PredictedApp(buildComponentFromString(s), + sharedPreferences.getLong(s, 0))); + } + + Collections.sort(allPredictions, new Comparator() { + public int compare(PredictedApp result1, PredictedApp result2) { + return Long.valueOf(result2.count).compareTo(Long.valueOf(result1.count)); + } + }); + + if (allPredictions.size() > NUM_PREDICTIVE_APPS_TO_HOLD) { + allPredictions = allPredictions.subList(0, NUM_PREDICTIVE_APPS_TO_HOLD); + } + + sharedPreferences.edit().putString(TOP_PREDICTIVE_APPS_KEY, buildStringFromAppList(allPredictions)).commit(); + } + }).start(); + } + + public List getPredictions() { + String[] topPredictions = sharedPreferences.getString(TOP_PREDICTIVE_APPS_KEY, "").split(" "); + List keys = new ArrayList<>(); + + for (int i = 0; i < topPredictions.length - 1; i++) { + keys.add(buildComponentKey(topPredictions[i] + " " + topPredictions[i + 1])); + } + + return keys; + } + + private String buildStringFromAppList(List apps) { + String string = ""; + for (PredictedApp app : apps) { + string += buildComponentString(app.component) + " "; + } + + return string.substring(0, string.length() - 1); + } + + private String buildComponentString(ComponentName component) { + return component.getPackageName() + " " + component.getClassName(); + } + + private ComponentName buildComponentFromString(String key) { + String[] arr = key.split(" "); + return new ComponentName(arr[0], arr[1]); + } + + private ComponentKey buildComponentKey(String key) { + return buildComponentKey(buildComponentFromString(key)); + } + + private ComponentKey buildComponentKey(ComponentName component) { + return new ComponentKey(component, UserHandleCompat.myUserHandle()); + } + + private class PredictedApp { + public ComponentName component; + public long count; + + public PredictedApp(ComponentName component, long count) { + this.component = component; + this.count = count; + } + } +}