Chromium Code Reviews| Index: chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappDataStorage.java |
| diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappDataStorage.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappDataStorage.java |
| index fabf9fb1324426f58f60bdc5ddafc3386f95350c..f202757cac331fbbb5a7c43782dbfbfccb79abde 100644 |
| --- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappDataStorage.java |
| +++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappDataStorage.java |
| @@ -5,6 +5,7 @@ |
| package org.chromium.chrome.browser.webapps; |
| import android.content.Context; |
| +import android.content.Intent; |
| import android.content.SharedPreferences; |
| import android.graphics.Bitmap; |
| import android.os.AsyncTask; |
| @@ -12,6 +13,9 @@ import android.os.AsyncTask; |
| import org.chromium.base.ThreadUtils; |
| import org.chromium.base.VisibleForTesting; |
| import org.chromium.chrome.browser.ShortcutHelper; |
| +import org.chromium.chrome.browser.ShortcutSource; |
| +import org.chromium.chrome.browser.util.IntentUtils; |
| +import org.chromium.content_public.common.ScreenOrientationValues; |
| import java.util.Map; |
| @@ -31,16 +35,28 @@ public class WebappDataStorage { |
| static final String SHARED_PREFS_FILE_PREFIX = "webapp_"; |
| static final String KEY_SPLASH_ICON = "splash_icon"; |
| static final String KEY_LAST_USED = "last_used"; |
| + static final String KEY_URL = "url"; |
| static final String KEY_SCOPE = "scope"; |
| + static final String KEY_ICON = "icon"; |
| + static final String KEY_NAME = "name"; |
| + static final String KEY_SHORT_NAME = "short_name"; |
| + static final String KEY_ORIENTATION = "orientation"; |
| + static final String KEY_THEME_COLOR = "theme_color"; |
| + static final String KEY_BACKGROUND_COLOR = "background_color"; |
| + static final String KEY_SOURCE = "source"; |
| + static final String KEY_ACTION = "action"; |
| + static final String KEY_IS_ICON_GENERATED = "is_icon_generated"; |
| - // Unset/invalid constants for last used times and scopes. 0 is used as the null last |
| + // Unset/invalid constants for last used times and URLs. 0 is used as the null last |
| // used time as WebappRegistry assumes that this is always a valid timestamp. |
| static final long LAST_USED_UNSET = 0; |
| static final long LAST_USED_INVALID = -1; |
| - static final String SCOPE_INVALID = ""; |
| + static final String URL_INVALID = ""; |
| + static final String NAME_INVALID = ""; |
| private static Factory sFactory = new Factory(); |
| + private final String mId; |
| private final SharedPreferences mPreferences; |
| /** |
| @@ -48,7 +64,7 @@ public class WebappDataStorage { |
| * @param context The context to open the SharedPreferences. |
| * @param webappId The ID of the web app which is being opened. |
| */ |
| - public static WebappDataStorage open(final Context context, final String webappId) { |
| + static WebappDataStorage open(final Context context, final String webappId) { |
| final WebappDataStorage storage = sFactory.create(context, webappId); |
| new AsyncTask<Void, Void, Void>() { |
| @Override |
| @@ -94,7 +110,7 @@ public class WebappDataStorage { |
| /** |
| * Asynchronously retrieves the scope stored in this WebappDataStorage. The scope is the URL |
| - * over which the webapp data is applied to. |
| + * over which the web app data is applied to. |
| * @param context The context to read the SharedPreferences file. |
| * @param webappId The ID of the web app the used time is being read for. |
| * @param callback Called when the scope has been retrieved. |
| @@ -105,8 +121,7 @@ public class WebappDataStorage { |
| new AsyncTask<Void, Void, String>() { |
| @Override |
| protected final String doInBackground(Void... nothing) { |
| - return new WebappDataStorage(context.getApplicationContext(), webappId) |
| - .getScope(); |
| + return new WebappDataStorage(context.getApplicationContext(), webappId).getScope(); |
| } |
| @Override |
| @@ -117,20 +132,23 @@ public class WebappDataStorage { |
| } |
| /** |
| - * Asynchronously sets the scope stored in this WebappDataStorage. Does nothing if there |
| - * is already a scope stored; since webapps added to homescreen cannot change the scope which |
| - * they launch, it is not intended that a WebappDataStorage will be able to change the scope |
| - * once it is set. |
| + * Asynchronously retrieves the URL stored in this WebappDataStorage. |
| * @param context The context to read the SharedPreferences file. |
| * @param webappId The ID of the web app the used time is being read for. |
| - * @param scope The scope to set for the web app. |
| + * @param callback Called when the URL has been retrieved. |
| */ |
| - public static void setScope(final Context context, final String webappId, final String scope) { |
| - new AsyncTask<Void, Void, Void>() { |
| + @VisibleForTesting |
| + public static void getURL(final Context context, final String webappId, |
| + final FetchCallback<String> callback) { |
| + new AsyncTask<Void, Void, String>() { |
| @Override |
| - protected final Void doInBackground(Void... nothing) { |
| - new WebappDataStorage(context.getApplicationContext(), webappId).setScope(scope); |
| - return null; |
| + protected final String doInBackground(Void... nothing) { |
| + return new WebappDataStorage(context.getApplicationContext(), webappId).getURL(); |
| + } |
| + |
| + @Override |
| + protected final void onPostExecute(String url) { |
| + callback.onDataRetrieved(url); |
| } |
| }.execute(); |
| } |
| @@ -147,18 +165,18 @@ public class WebappDataStorage { |
| } |
| /** |
| - * Deletes the scope and sets last used time to 0 this web app in SharedPreferences. |
| + * Deletes the URL and scope, and sets last used time to 0 in SharedPreferences. |
| * This does not remove the stored splash screen image (if any) for the app. |
| * @param context The context to read the SharedPreferences file. |
| - * @param webappId The ID of the web app being deleted. |
| + * @param webappId The ID of the web app for which history is being cleared. |
| */ |
| static void clearHistory(final Context context, final String webappId) { |
| // The last used time is set to 0 to ensure that a valid value is always present. |
| - // If the webapp is not launched prior to the next cleanup, then its remaining data will be |
| + // If the web app is not launched prior to the next cleanup, then its remaining data will be |
| // removed. Otherwise, the next launch will update the last used time. |
| assert !ThreadUtils.runningOnUiThread(); |
| - openSharedPreferences(context, webappId) |
| - .edit().putLong(KEY_LAST_USED, LAST_USED_UNSET).remove(KEY_SCOPE).apply(); |
| + openSharedPreferences(context, webappId).edit() |
| + .putLong(KEY_LAST_USED, LAST_USED_UNSET).remove(KEY_URL).remove(KEY_SCOPE).apply(); |
| } |
| /** |
| @@ -175,6 +193,7 @@ public class WebappDataStorage { |
| } |
| protected WebappDataStorage(Context context, String webappId) { |
| + mId = webappId; |
| mPreferences = openSharedPreferences(context, webappId); |
| } |
| @@ -200,31 +219,80 @@ public class WebappDataStorage { |
| } |
| /* |
| - * Update the information associated with the web app with the specified data. |
| + * Update the information associated with the web app with the specified data. Must not be |
| + * called on the UI thread. |
| * @param splashScreenImage The image which should be shown on the splash screen of the web app. |
| */ |
| public void updateSplashScreenImage(final Bitmap splashScreenImage) { |
| + assert !ThreadUtils.runningOnUiThread(); |
| + mPreferences.edit().putString(KEY_SPLASH_ICON, |
| + ShortcutHelper.encodeBitmapAsString(splashScreenImage)).apply(); |
| + } |
| + |
| + public void createWebappLaunchIntent(final FetchCallback<Intent> callback) { |
|
gone
2016/04/01 23:44:48
AIUI no value in reading these preferences on a ba
dominickn
2016/04/04 07:26:24
This method has a possibly expensive bitmap unpack
gone
2016/04/04 19:04:02
Arg, missed that call in that blob. sgtm
|
| + new AsyncTask<Void, Void, Intent>() { |
| + @Override |
| + protected final Intent doInBackground(Void... nothing) { |
| + // Assume that all of the data is invalid if the name is not set. Otherwise, assume |
| + // that all of the data is valid. |
| + String name = mPreferences.getString(KEY_NAME, NAME_INVALID); |
| + if (name.equals(NAME_INVALID)) return null; |
| + |
| + return ShortcutHelper.createWebappShortcutIntent(mId, |
| + mPreferences.getString(KEY_ACTION, null), |
| + mPreferences.getString(KEY_URL, null), name, |
| + mPreferences.getString(KEY_SHORT_NAME, null), |
| + ShortcutHelper.decodeBitmapFromString( |
| + mPreferences.getString(KEY_ICON, null)), |
| + mPreferences.getInt(KEY_ORIENTATION, ScreenOrientationValues.DEFAULT), |
| + mPreferences.getLong(KEY_THEME_COLOR, |
| + ShortcutHelper.MANIFEST_COLOR_INVALID_OR_MISSING), |
| + mPreferences.getLong(KEY_BACKGROUND_COLOR, |
| + ShortcutHelper.MANIFEST_COLOR_INVALID_OR_MISSING), |
| + mPreferences.getBoolean(KEY_IS_ICON_GENERATED, false)); |
| + } |
| + |
| + @Override |
| + protected final void onPostExecute(Intent result) { |
| + callback.onDataRetrieved(result); |
| + } |
| + }.execute(); |
| + } |
| + |
| + /** |
| + * Updates the data stored in this object to match that in the supplied intent. Calls the |
| + * provided callback when the update is complete. |
| + * @param intent the web app intent to update this object to match. |
| + * @param callback the callback to run when the update is complete. |
| + */ |
| + public void updateFromShortcutIntent(final Intent shortcutIntent, final Runnable callback) { |
| new AsyncTask<Void, Void, Void>() { |
| @Override |
| protected final Void doInBackground(Void... nothing) { |
| - mPreferences.edit() |
| - .putString(KEY_SPLASH_ICON, |
| - ShortcutHelper.encodeBitmapAsString(splashScreenImage)) |
| - .apply(); |
| + updateFromIntent(shortcutIntent); |
| return null; |
| } |
| + |
| + @Override |
| + protected final void onPostExecute(Void nothing) { |
| + assert callback != null; |
| + callback.run(); |
| + } |
| }.execute(); |
| } |
| /** |
| - * Updates the scope stored in this object. Does nothing if there is already a scope stored. |
| - * @param scope the scope to store. |
| + * Updates the data stored in this object to match that in the supplied intent. |
| + * @param intent the web app intent to update this object to match. |
| */ |
| - void setScope(String scope) { |
| - assert !ThreadUtils.runningOnUiThread(); |
| - if (mPreferences.getString(KEY_SCOPE, SCOPE_INVALID).equals(SCOPE_INVALID)) { |
| - mPreferences.edit().putString(KEY_SCOPE, scope).apply(); |
| - } |
| + public void updateFromShortcutIntent(final Intent shortcutIntent) { |
| + new AsyncTask<Void, Void, Void>() { |
| + @Override |
| + protected final Void doInBackground(Void... nothing) { |
| + updateFromIntent(shortcutIntent); |
| + return null; |
| + } |
| + }.execute(); |
| } |
| /** |
| @@ -232,7 +300,72 @@ public class WebappDataStorage { |
| */ |
| String getScope() { |
| assert !ThreadUtils.runningOnUiThread(); |
| - return mPreferences.getString(KEY_SCOPE, SCOPE_INVALID); |
| + return mPreferences.getString(KEY_SCOPE, URL_INVALID); |
| + } |
| + |
| + /** |
| + * Returns the URL stored in this object, or "" if it is not stored. |
|
gone
2016/04/01 23:44:48
Should be more explicit bot here and in the previo
dominickn
2016/04/04 07:26:24
Done.
|
| + */ |
| + String getURL() { |
| + assert !ThreadUtils.runningOnUiThread(); |
| + return mPreferences.getString(KEY_URL, URL_INVALID); |
| + } |
| + |
| + /** |
| + * Updates this object from the fields in the suppled intent. Does not check if the intent is |
|
gone
2016/04/01 23:44:48
nit: supplied
dominickn
2016/04/04 07:26:24
Done.
|
| + * a valid web app launch intent. |
| + */ |
| + private void updateFromIntent(Intent shortcutIntent) { |
| + assert !ThreadUtils.runningOnUiThread(); |
| + SharedPreferences.Editor editor = mPreferences.edit(); |
| + boolean updated = false; |
| + |
| + // The URL and scope may have been deleted by the user clearing their history. Check whether |
| + // they are present, and update if necessary. |
| + String url = mPreferences.getString(KEY_URL, URL_INVALID); |
| + if (url.equals(URL_INVALID)) { |
| + url = IntentUtils.safeGetStringExtra(shortcutIntent, ShortcutHelper.EXTRA_URL); |
| + editor.putString(KEY_URL, url); |
| + updated = true; |
| + } |
| + |
| + if (mPreferences.getString(KEY_SCOPE, URL_INVALID).equals(URL_INVALID)) { |
| + String scope = IntentUtils.safeGetStringExtra( |
| + shortcutIntent, ShortcutHelper.EXTRA_SCOPE); |
| + if (scope == null) scope = url; |
| + |
| + editor.putString(KEY_SCOPE, scope); |
| + updated = true; |
| + } |
| + |
| + // For all other fields, assume that if the name key is present, then all fields are |
| + // present and do not need to be updated. All fields except for the last used time, scope, |
| + // and URL are either set or cleared together. |
|
gone
2016/04/01 23:44:48
Are we going to have to start worrying about versi
dominickn
2016/04/04 07:26:24
Yeah, versioning is probably a good idea. Done.
|
| + if (mPreferences.getString(KEY_NAME, NAME_INVALID).equals(NAME_INVALID)) { |
| + editor.putString(KEY_ICON, IntentUtils.safeGetStringExtra( |
| + shortcutIntent, ShortcutHelper.EXTRA_ICON)); |
| + editor.putInt(KEY_ORIENTATION, IntentUtils.safeGetIntExtra( |
| + shortcutIntent, ShortcutHelper.EXTRA_ORIENTATION, |
| + ScreenOrientationValues.DEFAULT)); |
| + editor.putLong(KEY_THEME_COLOR, IntentUtils.safeGetLongExtra( |
| + shortcutIntent, ShortcutHelper.EXTRA_THEME_COLOR, |
| + ShortcutHelper.MANIFEST_COLOR_INVALID_OR_MISSING)); |
| + editor.putLong(KEY_BACKGROUND_COLOR, IntentUtils.safeGetLongExtra( |
| + shortcutIntent, ShortcutHelper.EXTRA_BACKGROUND_COLOR, |
| + ShortcutHelper.MANIFEST_COLOR_INVALID_OR_MISSING)); |
| + editor.putInt(KEY_SOURCE, IntentUtils.safeGetIntExtra( |
| + shortcutIntent, ShortcutHelper.EXTRA_SOURCE, |
| + ShortcutSource.UNKNOWN)); |
| + editor.putString(KEY_NAME, IntentUtils.safeGetStringExtra( |
| + shortcutIntent, ShortcutHelper.EXTRA_NAME)); |
| + editor.putString(KEY_SHORT_NAME, IntentUtils.safeGetStringExtra( |
| + shortcutIntent, ShortcutHelper.EXTRA_SHORT_NAME)); |
| + editor.putBoolean(KEY_IS_ICON_GENERATED, IntentUtils.safeGetBooleanExtra( |
| + shortcutIntent, ShortcutHelper.EXTRA_IS_ICON_GENERATED, false)); |
| + editor.putString(KEY_ACTION, shortcutIntent.getAction()); |
| + updated = true; |
| + } |
| + if (updated) editor.apply(); |
| } |
| /** |