Chromium Code Reviews| Index: chrome/android/java/src/org/chromium/chrome/browser/webapps/ActivityAssigner.java | 
| diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/ActivityAssigner.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/ActivityAssigner.java | 
| index c01923984a7ed61d439d20aa2a7c4520ad5416ea..9ae5dcb543ad7b802e05dcf7b6af4c7d2efb84d9 100644 | 
| --- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/ActivityAssigner.java | 
| +++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/ActivityAssigner.java | 
| @@ -12,6 +12,7 @@ import android.util.Log; | 
| import org.chromium.base.ThreadUtils; | 
| import org.chromium.base.VisibleForTesting; | 
| import org.chromium.base.metrics.RecordHistogram; | 
| +import org.chromium.webapk.lib.common.WebApkConstants; | 
| import java.util.ArrayList; | 
| import java.util.HashSet; | 
| @@ -20,18 +21,21 @@ import java.util.Set; | 
| import java.util.concurrent.TimeUnit; | 
| /** | 
| - * Manages a rotating LRU buffer of WebappActivities to assign webapps to. | 
| + * Manages two rotating LRU buffers, one for WebappActivities, and the other one for | 
| + * WebApkActivities to assign webapps to. | 
| * | 
| 
 
gone
2016/05/25 21:50:14
Don't need to / everything, especially since you l
 
 | 
| - * In order to accommodate a limited number of WebappActivities with a potentially unlimited number | 
| - * of webapps, we have to rotate the available WebappActivities between the webapps we start up. | 
| + * In order to accommodate a limited number of WebappActivitiess/WebApkActivities with a | 
| + * potentially unlimited number of webapps, we have to rotate the available | 
| + * WebappActivities/WebApkActivities between the webapps we start up. | 
| * Activities are reused in order of when they were last used, with the least recently used | 
| * ones culled first. | 
| * | 
| * It is impossible to know whether Tasks have been removed from the Recent Task list without the | 
| * GET_TASKS permission. As a result, the list of Activities inside the Recent Task list will | 
| * be highly unlikely to match the list maintained in memory. Instead, we store the mapping as it | 
| - * was the last time we changed it, which allows us to launch webapps in the WebappActivity they | 
| - * were most recently associated with in cases where a user restarts a webapp from the Recent Tasks. | 
| + * was the last time we changed it, which allows us to launch webapps in the | 
| + * WebappActivity/WebApkActivity they were most recently associated with in cases where a user | 
| + * restarts a webapp from the Recent Tasks. | 
| * Note that in situations where the user manually clears the app data, we will again have an | 
| * incorrect mapping. | 
| * | 
| @@ -63,24 +67,34 @@ public class ActivityAssigner { | 
| // Don't ever change the package. Left for backwards compatibility. | 
| @VisibleForTesting | 
| - static final String PREF_PACKAGE = "com.google.android.apps.chrome.webapps"; | 
| - static final String PREF_NUM_SAVED_ENTRIES = "ActivityAssigner.numSavedEntries"; | 
| - static final String PREF_ACTIVITY_INDEX = "ActivityAssigner.activityIndex"; | 
| - static final String PREF_WEBAPP_ID = "ActivityAssigner.webappId"; | 
| + static final String PREF_PACKAGE[] = {"com.google.android.apps.chrome.webapps", | 
| 
 
gone
2016/05/25 21:50:13
Don't bother with adding another package -- just u
 
 | 
| + "com.google.android.apps.chrome.webapps.webapk"}; | 
| + | 
| + static final String PREF_NUM_SAVED_ENTRIES[] = {"ActivityAssigner.numSavedEntries", | 
| + "ActivityAssigner.numSavedEntries.webapk"}; | 
| + static final String PREF_ACTIVITY_INDEX[] = {"ActivityAssigner.activityIndex", | 
| + "ActivityAssigner.activityIndex.webapk"}; | 
| + static final String PREF_WEBAPP_ID[] = {"ActivityAssigner.webappId", | 
| + "ActivityAssigner.webappId.webapk"}; | 
| static final int INVALID_ACTIVITY_INDEX = -1; | 
| + static final int WEBAPP_ACTIVITY_INDEX = 0; | 
| + static final int WEBAPK_ACTIVITY_INDEX = 1; | 
| + static final int ACTIVITY_TYPES = 2; | 
| 
 
gone
2016/05/25 21:50:14
nit: ACTIVITY_TYPE_COUNT
 
 | 
| private static ActivityAssigner sInstance; | 
| private final Context mContext; | 
| - private final List<ActivityEntry> mActivityList; | 
| + private final List<ActivityEntry>[] mActivityList; | 
| /** | 
| * Pre-load shared prefs to avoid being blocked on the | 
| * disk access async task in the future. | 
| */ | 
| public static void warmUpSharedPrefs(Context context) { | 
| - context.getSharedPreferences(PREF_PACKAGE, Context.MODE_PRIVATE); | 
| + for (int i = 0; i < ACTIVITY_TYPES; ++i) { | 
| + context.getSharedPreferences(PREF_PACKAGE[i], Context.MODE_PRIVATE); | 
| + } | 
| } | 
| static class ActivityEntry { | 
| @@ -106,15 +120,18 @@ public class ActivityAssigner { | 
| private ActivityAssigner(Context context) { | 
| mContext = context.getApplicationContext(); | 
| - mActivityList = new ArrayList<ActivityEntry>(); | 
| - | 
| - restoreActivityList(); | 
| + mActivityList = new ArrayList[ACTIVITY_TYPES]; | 
| + for (int i = 0; i < mActivityList.length; i++) { | 
| + mActivityList[i] = new ArrayList<ActivityEntry>(); | 
| + restoreActivityList(i); | 
| + } | 
| } | 
| /** | 
| - * Assigns the webapp with the given ID to one of the available WebappActivities. | 
| + * Assigns the webapp with the given ID to one of the available | 
| + * WebappActivities/WebApkActivities. | 
| 
 
gone
2016/05/25 21:50:13
Assign the app with the given ID to one of the ava
 
 | 
| * If we know that the webapp was previously launched in one of the Activities, re-use it. | 
| 
 
gone
2016/05/25 21:50:13
webapp -> app
least recently used ID and use that
 
 | 
| - * Otherwise, take the least recently used WebappActivity ID and use that. | 
| + * Otherwise, take the least recently used WebappActivity/WebApkActivity ID and use that. | 
| * @param webappId ID of the webapp. | 
| * @return Index of the Activity to use for the webapp. | 
| */ | 
| @@ -124,9 +141,11 @@ public class ActivityAssigner { | 
| // Allocate the one in the front of the list. | 
| if (activityIndex == INVALID_ACTIVITY_INDEX) { | 
| - activityIndex = mActivityList.get(0).mActivityIndex; | 
| + // Get index of the LRU buffer depending on the {@link webappId}. | 
| + int index = getIndex(webappId); | 
| + activityIndex = mActivityList[index].get(0).mActivityIndex; | 
| ActivityEntry newEntry = new ActivityEntry(activityIndex, webappId); | 
| - mActivityList.set(0, newEntry); | 
| + mActivityList[index].set(0, newEntry); | 
| } | 
| markActivityUsed(activityIndex, webappId); | 
| @@ -134,6 +153,15 @@ public class ActivityAssigner { | 
| } | 
| /** | 
| + * Returns {@link WEBAPP_ACTIVITY_INDEX} for WebappActivity, {@link WEBAPK_ACTIVITY_INDEX} for | 
| + * WebApkActivity whose webappId starts with "webapk:". | 
| + */ | 
| + int getIndex(String webappId) { | 
| + return webappId.startsWith(WebApkConstants.WEBAPK_ID_PREFIX) ? WEBAPK_ACTIVITY_INDEX | 
| + : WEBAPP_ACTIVITY_INDEX; | 
| + } | 
| + | 
| + /** | 
| * Checks if the webapp with the given ID has been assigned to an Activity already. | 
| * @param webappId ID of the webapp being displayed. | 
| * @return Index of the Activity for the webapp if assigned, INVALID_ACTIVITY_INDEX otherwise. | 
| @@ -144,23 +172,25 @@ public class ActivityAssigner { | 
| } | 
| // Go backwards in the queue to catch more recent instances of any duplicated webapps. | 
| - for (int i = mActivityList.size() - 1; i >= 0; i--) { | 
| - if (webappId.equals(mActivityList.get(i).mWebappId)) { | 
| - return mActivityList.get(i).mActivityIndex; | 
| + int index = getIndex(webappId); | 
| + for (int i = mActivityList[index].size() - 1; i >= 0; i--) { | 
| + if (webappId.equals(mActivityList[index].get(i).mWebappId)) { | 
| + return mActivityList[index].get(i).mActivityIndex; | 
| } | 
| } | 
| return INVALID_ACTIVITY_INDEX; | 
| } | 
| /** | 
| - * Moves a WebappActivity to the back of the queue, indicating that the Webapp is still in use | 
| - * and shouldn't be killed. | 
| - * @param activityIndex Index of the WebappActivity. | 
| - * @param webappId ID of the webapp being shown in the WebappActivity. | 
| + * Moves a WebappActivity/WebApkActivity to the back of the queue, indicating that the Webapp | 
| + * is still in use and shouldn't be killed. | 
| + * @param activityIndex Index of the WebappActivity/WebApkActivity. | 
| + * @param webappId ID of the webapp being shown in the WebappActivity/WebApkActivity. | 
| */ | 
| void markActivityUsed(int activityIndex, String webappId) { | 
| // Find the entry corresponding to the Activity. | 
| - int elementIndex = findActivityElement(activityIndex); | 
| + int index = getIndex(webappId); | 
| + int elementIndex = findActivityElement(activityIndex, index); | 
| if (elementIndex == -1) { | 
| Log.e(TAG, "Failed to find WebappActivity entry: " + activityIndex + ", " + webappId); | 
| @@ -169,19 +199,20 @@ public class ActivityAssigner { | 
| // We have to reassign the webapp ID in case WebappActivities get repurposed. | 
| ActivityEntry updatedEntry = new ActivityEntry(activityIndex, webappId); | 
| - mActivityList.remove(elementIndex); | 
| - mActivityList.add(updatedEntry); | 
| - storeActivityList(); | 
| + mActivityList[index].remove(elementIndex); | 
| + mActivityList[index].add(updatedEntry); | 
| + storeActivityList(index); | 
| } | 
| /** | 
| * Finds the index of the ActivityElement corresponding to the given activityIndex. | 
| * @param activityIndex Index of the activity to find. | 
| + * @param typeIndex Index of WebappActivities or WebApkActivities in {@link mActivityIndex}. | 
| * @return The index of the ActivityElement in the activity list, or -1 if it couldn't be found. | 
| */ | 
| - private int findActivityElement(int activityIndex) { | 
| - for (int elementIndex = 0; elementIndex < mActivityList.size(); elementIndex++) { | 
| - if (mActivityList.get(elementIndex).mActivityIndex == activityIndex) { | 
| + private int findActivityElement(int activityIndex, int typeIndex) { | 
| + for (int elementIndex = 0; elementIndex < mActivityList[typeIndex].size(); elementIndex++) { | 
| + if (mActivityList[typeIndex].get(elementIndex).mActivityIndex == activityIndex) { | 
| return elementIndex; | 
| } | 
| } | 
| @@ -192,18 +223,19 @@ public class ActivityAssigner { | 
| * Returns the current mapping between Activities and webapps. | 
| */ | 
| @VisibleForTesting | 
| - List<ActivityEntry> getEntries() { | 
| + List<ActivityEntry>[] getEntries() { | 
| return mActivityList; | 
| } | 
| /** | 
| - * Restores/creates the mapping between webapps and WebappActivities. | 
| + * Restores/creates the mapping between webapps and WebappActivities/WebApkActivities depending | 
| + * on the {@link index}. | 
| * The logic is slightly complicated to future-proof against situations where the number of | 
| - * WebappActivities is changed. | 
| + * WebappActivities/WebApkActivity is changed. | 
| */ | 
| - private void restoreActivityList() { | 
| + private void restoreActivityList(int index) { | 
| boolean isMapDirty = false; | 
| - mActivityList.clear(); | 
| + mActivityList[index].clear(); | 
| // Create a Set of indices corresponding to every possible Activity. | 
| // As ActivityEntries are read, they are and removed from this list to indicate that the | 
| @@ -215,10 +247,11 @@ public class ActivityAssigner { | 
| // Restore any entries that were previously saved. If it seems that the preferences have | 
| // been corrupted somehow, just discard the whole map. | 
| - SharedPreferences prefs = mContext.getSharedPreferences(PREF_PACKAGE, Context.MODE_PRIVATE); | 
| + SharedPreferences prefs = mContext.getSharedPreferences(PREF_PACKAGE[index], | 
| + Context.MODE_PRIVATE); | 
| try { | 
| long time = SystemClock.elapsedRealtime(); | 
| - final int numSavedEntries = prefs.getInt(PREF_NUM_SAVED_ENTRIES, 0); | 
| + final int numSavedEntries = prefs.getInt(PREF_NUM_SAVED_ENTRIES[index], 0); | 
| try { | 
| RecordHistogram.recordTimesHistogram("Android.StrictMode.WebappSharedPrefs", | 
| SystemClock.elapsedRealtime() - time, TimeUnit.MILLISECONDS); | 
| @@ -227,15 +260,15 @@ public class ActivityAssigner { | 
| } | 
| if (numSavedEntries <= NUM_WEBAPP_ACTIVITIES) { | 
| for (int i = 0; i < numSavedEntries; ++i) { | 
| - String currentActivityIndexPref = PREF_ACTIVITY_INDEX + i; | 
| - String currentWebappIdPref = PREF_WEBAPP_ID + i; | 
| + String currentActivityIndexPref = PREF_ACTIVITY_INDEX[index] + i; | 
| + String currentWebappIdPref = PREF_WEBAPP_ID[index] + i; | 
| int activityIndex = prefs.getInt(currentActivityIndexPref, i); | 
| String webappId = prefs.getString(currentWebappIdPref, null); | 
| ActivityEntry entry = new ActivityEntry(activityIndex, webappId); | 
| if (availableWebapps.remove(entry.mActivityIndex)) { | 
| - mActivityList.add(entry); | 
| + mActivityList[index].add(entry); | 
| } else { | 
| // If the same activity was assigned to two different entries, or if the | 
| // number of activities changed, discard it and mark that it needs to be | 
| @@ -246,38 +279,40 @@ public class ActivityAssigner { | 
| } | 
| } catch (ClassCastException exception) { | 
| // Something went wrong reading the preferences. Nuke everything. | 
| - mActivityList.clear(); | 
| + mActivityList[index].clear(); | 
| availableWebapps.clear(); | 
| for (int i = 0; i < NUM_WEBAPP_ACTIVITIES; ++i) { | 
| availableWebapps.add(i); | 
| } | 
| } | 
| - // Add entries for any missing WebappActivities. | 
| + // Add entries for any missing WebappActivities/WebApkActivities. | 
| for (Integer availableIndex : availableWebapps) { | 
| ActivityEntry entry = new ActivityEntry(availableIndex, null); | 
| - mActivityList.add(entry); | 
| + mActivityList[index].add(entry); | 
| isMapDirty = true; | 
| } | 
| if (isMapDirty) { | 
| - storeActivityList(); | 
| + storeActivityList(index); | 
| } | 
| } | 
| /** | 
| - * Saves the mapping between webapps and WebappActivities. | 
| + * Saves the mapping between webapps and WebappActivities/WebApkActivities depending on the | 
| + * {@link index}. | 
| */ | 
| - private void storeActivityList() { | 
| - SharedPreferences prefs = mContext.getSharedPreferences(PREF_PACKAGE, Context.MODE_PRIVATE); | 
| + private void storeActivityList(int index) { | 
| + SharedPreferences prefs = mContext.getSharedPreferences(PREF_PACKAGE[index], | 
| + Context.MODE_PRIVATE); | 
| SharedPreferences.Editor editor = prefs.edit(); | 
| editor.clear(); | 
| - editor.putInt(PREF_NUM_SAVED_ENTRIES, mActivityList.size()); | 
| - for (int i = 0; i < mActivityList.size(); ++i) { | 
| - String currentActivityIndexPref = PREF_ACTIVITY_INDEX + i; | 
| - String currentWebappIdPref = PREF_WEBAPP_ID + i; | 
| - editor.putInt(currentActivityIndexPref, mActivityList.get(i).mActivityIndex); | 
| - editor.putString(currentWebappIdPref, mActivityList.get(i).mWebappId); | 
| + editor.putInt(PREF_NUM_SAVED_ENTRIES[index], mActivityList[index].size()); | 
| + for (int i = 0; i < mActivityList[index].size(); ++i) { | 
| + String currentActivityIndexPref = PREF_ACTIVITY_INDEX[index] + i; | 
| + String currentWebappIdPref = PREF_WEBAPP_ID[index] + i; | 
| + editor.putInt(currentActivityIndexPref, mActivityList[index].get(i).mActivityIndex); | 
| + editor.putString(currentWebappIdPref, mActivityList[index].get(i).mWebappId); | 
| } | 
| editor.apply(); | 
| } |