Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(17)

Side by Side Diff: chrome/android/java_staging/src/org/chromium/chrome/browser/webapps/ActivityAssigner.java

Issue 1141283003: Upstream oodles of Chrome for Android code into Chromium. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: final patch? Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 package org.chromium.chrome.browser.webapps;
6
7 import android.content.Context;
8 import android.content.SharedPreferences;
9 import android.util.Log;
10
11 import org.chromium.base.ThreadUtils;
12 import org.chromium.base.VisibleForTesting;
13
14 import java.util.ArrayList;
15 import java.util.HashSet;
16 import java.util.List;
17 import java.util.Set;
18
19 /**
20 * Manages a rotating LRU buffer of WebappActivities to assign webapps to.
21 *
22 * In order to accommodate a limited number of WebappActivities with a potential ly unlimited number
23 * of webapps, we have to rotate the available WebappActivities between the weba pps we start up.
24 * Activities are reused in order of when they were last used, with the least re cently used
25 * ones culled first.
26 *
27 * It is impossible to know whether Tasks have been removed from the Recent Task list without the
28 * GET_TASKS permission. As a result, the list of Activities inside the Recent Task list will
29 * be highly unlikely to match the list maintained in memory. Instead, we store the mapping as it
30 * was the last time we changed it, which allows us to launch webapps in the Web appActivity they
31 * were most recently associated with in cases where a user restarts a webapp fr om the Recent Tasks.
32 * Note that in situations where the user manually clears the app data, we will again have an
33 * incorrect mapping.
34 *
35 * Unless otherwise noted, all methods MUST be called on the UI thread to avoid threading issues.
36 *
37 * EXAMPLE:
38 * - 3 Activities are available for assignment (0, 1, 2).
39 * - 4 webapps exist (X, Y, Z, W).
40 *
41 * ACTION EFFECT ACTIVI TY LIST
42 * 0) Clean slate (0 -) (1 -) (2 -)
43 * 1) Start X Assigned to Activity 0 and pushed back. (1 -) (2 -) (0 X)
44 * 2) Start Y Assigned to Activity 1 and pushed back. (2 -) (0 X) (1 Y)
45 * 3) Start Z Assigned to Activity 2 and pushed back. (0 X) (1 Y) (2 Z)
46 * 4) Restart Y Re-assigned to Activity 1 and pushed back. (0 X) (2 Z) (1 Y)
47 * 4) Start W Assigned to Activity 0 and pushed back. X evicted. (2 Z) (1 Y) (0 W)
48 * 5) Restart X Assigned to Activity 2 and pushed back. Z evicted. (1 Y) (0 W) (2 X)
49 */
50 public class ActivityAssigner {
51 private static final String TAG = "ActivityAssigner";
52
53 // Don't ever change this. 10 is enough for everyone.
54 static final int NUM_WEBAPP_ACTIVITIES = 10;
55
56 // A sanity check limit to ensure that we aren't reading an unreasonable num ber of preferences.
57 // This number is different from above because the number of WebappActivitie s available may
58 // change.
59 static final int MAX_WEBAPP_ACTIVITIES_EVER = 100;
60
61 // Don't ever change the package. Left for backwards compatibility.
62 @VisibleForTesting
63 static final String PREF_PACKAGE = "com.google.android.apps.chrome.webapps";
64 static final String PREF_NUM_SAVED_ENTRIES = "ActivityAssigner.numSavedEntri es";
65 static final String PREF_ACTIVITY_INDEX = "ActivityAssigner.activityIndex";
66 static final String PREF_WEBAPP_ID = "ActivityAssigner.webappId";
67
68 static final int INVALID_ACTIVITY_INDEX = -1;
69
70 private static ActivityAssigner sInstance;
71
72 private final Context mContext;
73 private final List<ActivityEntry> mActivityList;
74
75 static class ActivityEntry {
76 final int mActivityIndex;
77 final String mWebappId;
78
79 ActivityEntry(int activity, String webapp) {
80 mActivityIndex = activity;
81 mWebappId = webapp;
82 }
83 }
84
85 /**
86 * Returns the singleton instance, creating it if necessary.
87 */
88 public static ActivityAssigner instance(Context context) {
89 ThreadUtils.assertOnUiThread();
90 if (sInstance == null) {
91 sInstance = new ActivityAssigner(context);
92 }
93 return sInstance;
94 }
95
96 private ActivityAssigner(Context context) {
97 mContext = context.getApplicationContext();
98 mActivityList = new ArrayList<ActivityEntry>();
99
100 restoreActivityList();
101 }
102
103 /**
104 * Assigns the webapp with the given ID to one of the available WebappActivi ties.
105 * If we know that the webapp was previously launched in one of the Activiti es, re-use it.
106 * Otherwise, take the least recently used WebappActivity ID and use that.
107 * @param webappId ID of the webapp.
108 * @return Index of the Activity to use for the webapp.
109 */
110 int assign(String webappId) {
111 // Reuse a running Activity with the same ID, if it exists.
112 int activityIndex = checkIfAssigned(webappId);
113
114 // Allocate the one in the front of the list.
115 if (activityIndex == INVALID_ACTIVITY_INDEX) {
116 activityIndex = mActivityList.get(0).mActivityIndex;
117 ActivityEntry newEntry = new ActivityEntry(activityIndex, webappId);
118 mActivityList.set(0, newEntry);
119 }
120
121 markActivityUsed(activityIndex, webappId);
122 return activityIndex;
123 }
124
125 /**
126 * Checks if the webapp with the given ID has been assigned to an Activity a lready.
127 * @param webappId ID of the webapp being displayed.
128 * @return Index of the Activity for the webapp if assigned, INVALID_ACTIVIT Y_INDEX otherwise.
129 */
130 int checkIfAssigned(String webappId) {
131 if (webappId == null) {
132 return INVALID_ACTIVITY_INDEX;
133 }
134
135 // Go backwards in the queue to catch more recent instances of any dupli cated webapps.
136 for (int i = mActivityList.size() - 1; i >= 0; i--) {
137 if (webappId.equals(mActivityList.get(i).mWebappId)) {
138 return mActivityList.get(i).mActivityIndex;
139 }
140 }
141 return INVALID_ACTIVITY_INDEX;
142 }
143
144 /**
145 * Moves a WebappActivity to the back of the queue, indicating that the Weba pp is still in use
146 * and shouldn't be killed.
147 * @param activityIndex Index of the WebappActivity.
148 * @param webappId ID of the webapp being shown in the WebappActivity.
149 */
150 void markActivityUsed(int activityIndex, String webappId) {
151 // Find the entry corresponding to the Activity.
152 int elementIndex = findActivityElement(activityIndex);
153
154 if (elementIndex == -1) {
155 Log.e(TAG, "Failed to find WebappActivity entry: " + activityIndex + ", " + webappId);
156 return;
157 }
158
159 // We have to reassign the webapp ID in case WebappActivities get repurp osed.
160 ActivityEntry updatedEntry = new ActivityEntry(activityIndex, webappId);
161 mActivityList.remove(elementIndex);
162 mActivityList.add(updatedEntry);
163 storeActivityList();
164 }
165
166 /**
167 * Finds the index of the ActivityElement corresponding to the given activit yIndex.
168 * @param activityIndex Index of the activity to find.
169 * @return The index of the ActivityElement in the activity list, or -1 if i t couldn't be found.
170 */
171 private int findActivityElement(int activityIndex) {
172 for (int elementIndex = 0; elementIndex < mActivityList.size(); elementI ndex++) {
173 if (mActivityList.get(elementIndex).mActivityIndex == activityIndex) {
174 return elementIndex;
175 }
176 }
177 return -1;
178 }
179
180 /**
181 * Returns the current mapping between Activities and webapps.
182 */
183 @VisibleForTesting
184 List<ActivityEntry> getEntries() {
185 return mActivityList;
186 }
187
188 /**
189 * Restores/creates the mapping between webapps and WebappActivities.
190 * The logic is slightly complicated to future-proof against situations wher e the number of
191 * WebappActivities is changed.
192 */
193 private void restoreActivityList() {
194 boolean isMapDirty = false;
195 mActivityList.clear();
196
197 // Create a Set of indices corresponding to every possible Activity.
198 // As ActivityEntries are read, they are and removed from this list to i ndicate that the
199 // Activity has already been assigned.
200 Set<Integer> availableWebapps = new HashSet<Integer>();
201 for (int i = 0; i < NUM_WEBAPP_ACTIVITIES; ++i) {
202 availableWebapps.add(i);
203 }
204
205 // Restore any entries that were previously saved. If it seems that the preferences have
206 // been corrupted somehow, just discard the whole map.
207 SharedPreferences prefs = mContext.getSharedPreferences(PREF_PACKAGE, Co ntext.MODE_PRIVATE);
208 try {
209 final int numSavedEntries = prefs.getInt(PREF_NUM_SAVED_ENTRIES, 0);
210 if (numSavedEntries <= NUM_WEBAPP_ACTIVITIES) {
211 for (int i = 0; i < numSavedEntries; ++i) {
212 String currentActivityIndexPref = PREF_ACTIVITY_INDEX + i;
213 String currentWebappIdPref = PREF_WEBAPP_ID + i;
214
215 int activityIndex = prefs.getInt(currentActivityIndexPref, i );
216 String webappId = prefs.getString(currentWebappIdPref, null) ;
217 ActivityEntry entry = new ActivityEntry(activityIndex, webap pId);
218
219 if (availableWebapps.remove(entry.mActivityIndex)) {
220 mActivityList.add(entry);
221 } else {
222 // If the same activity was assigned to two different en tries, or if the
223 // number of activities changed, discard it and mark tha t it needs to be
224 // rewritten.
225 isMapDirty = true;
226 }
227 }
228 }
229 } catch (ClassCastException exception) {
230 // Something went wrong reading the preferences. Nuke everything.
231 mActivityList.clear();
232 availableWebapps.clear();
233 for (int i = 0; i < NUM_WEBAPP_ACTIVITIES; ++i) {
234 availableWebapps.add(i);
235 }
236 }
237
238 // Add entries for any missing WebappActivities.
239 for (Integer availableIndex : availableWebapps) {
240 ActivityEntry entry = new ActivityEntry(availableIndex, null);
241 mActivityList.add(entry);
242 isMapDirty = true;
243 }
244
245 if (isMapDirty) {
246 storeActivityList();
247 }
248 }
249
250 /**
251 * Saves the mapping between webapps and WebappActivities.
252 */
253 private void storeActivityList() {
254 SharedPreferences prefs = mContext.getSharedPreferences(PREF_PACKAGE, Co ntext.MODE_PRIVATE);
255 SharedPreferences.Editor editor = prefs.edit();
256 editor.clear();
257 editor.putInt(PREF_NUM_SAVED_ENTRIES, mActivityList.size());
258 for (int i = 0; i < mActivityList.size(); ++i) {
259 String currentActivityIndexPref = PREF_ACTIVITY_INDEX + i;
260 String currentWebappIdPref = PREF_WEBAPP_ID + i;
261 editor.putInt(currentActivityIndexPref, mActivityList.get(i).mActivi tyIndex);
262 editor.putString(currentWebappIdPref, mActivityList.get(i).mWebappId );
263 }
264 editor.apply();
265 }
266 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698