Commit fd68bf21 authored by ckitagawa's avatar ckitagawa Committed by Commit Bot

[WebAPKs] chrome://webapks update buttons

Add support for a manual WebAPK update flow. This is a *dev*
feature intended to help test changes to WebAPKs/manifests
without waiting for a long time period.

The buttons set a flag that schedules an update when
next launching the WebAPK. Once scheduled, the update will
occur sometime after the WebAPK is closed. This avoids the
need for refactoring or modifying the update path and
instead just requires plumbing a status message to the UI.

Bug: 918898
Change-Id: I7eb3203f30de95f4918ae64489be9c72e24ce8f9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1674164
Commit-Queue: Calder Kitagawa <ckitagawa@chromium.org>
Reviewed-by: default avatarDominick Ng <dominickn@chromium.org>
Reviewed-by: default avatarcalamity <calamity@chromium.org>
Reviewed-by: default avatarPeter Kotwicz <pkotwicz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#676451}
parent 6577551a
......@@ -773,6 +773,7 @@ public class ShortcutHelper {
List<String> names = new ArrayList<>();
List<String> shortNames = new ArrayList<>();
List<String> packageNames = new ArrayList<>();
List<String> ids = new ArrayList<>();
List<Integer> shellApkVersions = new ArrayList<>();
List<Integer> versionCodes = new ArrayList<>();
List<String> uris = new ArrayList<>();
......@@ -785,6 +786,7 @@ public class ShortcutHelper {
List<Long> backgroundColors = new ArrayList<>();
List<Long> lastUpdateCheckTimesMs = new ArrayList<>();
List<Boolean> relaxUpdates = new ArrayList<>();
List<String> updateStatuses = new ArrayList<>();
Context context = ContextUtils.getApplicationContext();
PackageManager packageManager = context.getPackageManager();
......@@ -799,6 +801,7 @@ public class ShortcutHelper {
names.add(webApkInfo.name());
shortNames.add(webApkInfo.shortName());
packageNames.add(webApkInfo.webApkPackageName());
ids.add(webApkInfo.id());
shellApkVersions.add(webApkInfo.shellApkVersion());
versionCodes.add(packageInfo.versionCode);
uris.add(webApkInfo.uri().toString());
......@@ -821,12 +824,13 @@ public class ShortcutHelper {
}
lastUpdateCheckTimesMs.add(lastUpdateCheckTimeMsForStorage);
relaxUpdates.add(relaxUpdatesForStorage);
updateStatuses.add(storage.getUpdateStatus());
}
}
}
nativeOnWebApksRetrieved(callbackPointer, names.toArray(new String[0]),
shortNames.toArray(new String[0]), packageNames.toArray(new String[0]),
CollectionUtil.integerListToIntArray(shellApkVersions),
ids.toArray(new String[0]), CollectionUtil.integerListToIntArray(shellApkVersions),
CollectionUtil.integerListToIntArray(versionCodes), uris.toArray(new String[0]),
scopes.toArray(new String[0]), manifestUrls.toArray(new String[0]),
manifestStartUrls.toArray(new String[0]),
......@@ -835,13 +839,23 @@ public class ShortcutHelper {
CollectionUtil.longListToLongArray(themeColors),
CollectionUtil.longListToLongArray(backgroundColors),
CollectionUtil.longListToLongArray(lastUpdateCheckTimesMs),
CollectionUtil.booleanListToBooleanArray(relaxUpdates));
CollectionUtil.booleanListToBooleanArray(relaxUpdates),
updateStatuses.toArray(new String[0]));
}
@CalledByNative
public static void setForceWebApkUpdate(String id) {
WebappDataStorage storage = WebappRegistry.getInstance().getWebappDataStorage(id);
if (storage != null) {
storage.setShouldForceUpdate(true);
}
}
private static native void nativeOnWebappDataStored(long callbackPointer);
private static native void nativeOnWebApksRetrieved(long callbackPointer, String[] names,
String[] shortNames, String[] packageName, int[] shellApkVersions, int[] versionCodes,
String[] uris, String[] scopes, String[] manifestUrls, String[] manifestStartUrls,
int[] displayModes, int[] orientations, long[] themeColors, long[] backgroundColors,
long[] lastUpdateCheckTimesMs, boolean[] relaxUpdates);
String[] shortNames, String[] packageNames, String[] ids, int[] shellApkVersions,
int[] versionCodes, String[] uris, String[] scopes, String[] manifestUrls,
String[] manifestStartUrls, int[] displayModes, int[] orientations, long[] themeColors,
long[] backgroundColors, long[] lastUpdateCheckTimesMs, boolean[] relaxUpdates,
String[] updateStatuses);
}
......@@ -110,6 +110,9 @@ public class WebApkUpdateManager implements WebApkUpdateDataFetcher.Observer {
@WebApkUpdateReason
int updateReason = needsUpdate(mInfo, fetchedInfo, primaryIconUrl, badgeIconUrl);
boolean needsUpgrade = (updateReason != WebApkUpdateReason.NONE);
if (mStorage.shouldForceUpdate() && needsUpgrade) {
updateReason = WebApkUpdateReason.MANUALLY_TRIGGERED;
}
Log.i(TAG, "Got Manifest: " + gotManifest);
Log.i(TAG, "WebAPK upgrade needed: " + needsUpgrade);
......@@ -130,7 +133,7 @@ public class WebApkUpdateManager implements WebApkUpdateDataFetcher.Observer {
}
if (!needsUpgrade) {
if (!mStorage.didPreviousUpdateSucceed()) {
if (!mStorage.didPreviousUpdateSucceed() || mStorage.shouldForceUpdate()) {
onFinishedUpdate(mStorage, WebApkInstallResult.SUCCESS, false /* relaxUpdates */);
}
return;
......@@ -178,18 +181,30 @@ public class WebApkUpdateManager implements WebApkUpdateDataFetcher.Observer {
/** Schedules update for when WebAPK is not running. */
private void scheduleUpdate() {
WebApkUma.recordUpdateRequestQueued(1);
TaskInfo updateTask;
if (mStorage.shouldForceUpdate()) {
// Start an update task ASAP for forced updates.
updateTask = TaskInfo.createOneOffTask(TaskIds.WEBAPK_UPDATE_JOB_ID,
WebApkUpdateTask.class, 0 /* windowEndTimeMs */)
.setUpdateCurrent(true)
.setIsPersisted(true)
.build();
mStorage.setUpdateScheduled(true);
mStorage.setShouldForceUpdate(false);
} else {
// The task deadline should be before {@link WebappDataStorage#RETRY_UPDATE_DURATION}
updateTask =
TaskInfo.createOneOffTask(TaskIds.WEBAPK_UPDATE_JOB_ID, WebApkUpdateTask.class,
DateUtils.HOUR_IN_MILLIS, DateUtils.HOUR_IN_MILLIS * 23)
.setRequiredNetworkType(TaskInfo.NetworkType.UNMETERED)
.setUpdateCurrent(true)
.setIsPersisted(true)
.setRequiresCharging(true)
.build();
}
// The task deadline should be before {@link WebappDataStorage#RETRY_UPDATE_DURATION}
TaskInfo taskInfo =
TaskInfo.createOneOffTask(TaskIds.WEBAPK_UPDATE_JOB_ID, WebApkUpdateTask.class,
DateUtils.HOUR_IN_MILLIS, DateUtils.HOUR_IN_MILLIS * 23)
.setRequiredNetworkType(TaskInfo.NetworkType.UNMETERED)
.setUpdateCurrent(true)
.setIsPersisted(true)
.setRequiresCharging(true)
.build();
BackgroundTaskSchedulerFactory.getScheduler().schedule(
ContextUtils.getApplicationContext(), taskInfo);
ContextUtils.getApplicationContext(), updateTask);
}
/** Sends update request to the WebAPK Server. Should be called when WebAPK is not running. */
......@@ -284,6 +299,8 @@ public class WebApkUpdateManager implements WebApkUpdateDataFetcher.Observer {
*/
private static void onFinishedUpdate(
WebappDataStorage storage, @WebApkInstallResult int result, boolean relaxUpdates) {
storage.setShouldForceUpdate(false);
storage.setUpdateScheduled(false);
recordUpdate(storage, result, relaxUpdates);
storage.deletePendingUpdateRequestFile();
}
......
......@@ -4,17 +4,15 @@
package org.chromium.chrome.browser.webapps;
import android.app.Activity;
import android.content.Context;
import android.text.TextUtils;
import org.chromium.base.ApplicationStatus;
import org.chromium.base.StrictModeContext;
import org.chromium.chrome.browser.background_task_scheduler.NativeBackgroundTask;
import org.chromium.components.background_task_scheduler.BackgroundTask.TaskFinishedCallback;
import org.chromium.components.background_task_scheduler.TaskIds;
import org.chromium.components.background_task_scheduler.TaskParameters;
import java.lang.ref.WeakReference;
import java.util.List;
/**
......@@ -41,7 +39,9 @@ public class WebApkUpdateTask extends NativeBackgroundTask {
List<String> ids = WebappRegistry.getInstance().findWebApksWithPendingUpdate();
for (String id : ids) {
WebappDataStorage storage = WebappRegistry.getInstance().getWebappDataStorage(id);
if (!isWebApkActivityRunning(storage.getWebApkPackageName())) {
WeakReference<WebappActivity> activity =
WebappActivity.findRunningWebappActivityWithId(storage.getId());
if (activity == null || activity.get() == null) {
mStorageToUpdate = storage;
mMoreToUpdate = ids.size() > 1;
return StartBeforeNativeResult.LOAD_NATIVE;
......@@ -79,19 +79,4 @@ public class WebApkUpdateTask extends NativeBackgroundTask {
@Override
public void reschedule(Context context) {}
/** Returns whether a WebApkActivity with {@link webApkPackageName} is running. */
private static boolean isWebApkActivityRunning(String webApkPackageName) {
for (Activity activity : ApplicationStatus.getRunningActivities()) {
if (!(activity instanceof WebApkActivity)) {
continue;
}
WebApkActivity webApkActivity = (WebApkActivity) activity;
if (webApkActivity != null
&& TextUtils.equals(webApkPackageName, webApkActivity.getWebApkPackageName())) {
return true;
}
}
return false;
}
}
......@@ -127,6 +127,21 @@ public class WebappActivity extends SingleTabActivity {
return null;
}
/** Returns the WebappActivity with the given {@link webappId}. */
public static WeakReference<WebappActivity> findRunningWebappActivityWithId(String webappId) {
for (Activity activity : ApplicationStatus.getRunningActivities()) {
if (!(activity instanceof WebappActivity)) {
continue;
}
WebappActivity webappActivity = (WebappActivity) activity;
if (webappActivity != null
&& TextUtils.equals(webappId, webappActivity.getWebappInfo().id())) {
return new WeakReference<>(webappActivity);
}
}
return null;
}
/**
* Construct all the variables that shouldn't change. We do it here both to clarify when the
* objects are created and to ensure that they exist throughout the parallelized initialization
......
......@@ -77,6 +77,12 @@ public class WebappDataStorage {
// The path where serialized update data is written before uploading to the WebAPK server.
static final String KEY_PENDING_UPDATE_FILE_PATH = "pending_update_file_path";
// Whether to force an update.
static final String KEY_SHOULD_FORCE_UPDATE = "should_force_update";
// Whether an update has been scheduled.
static final String KEY_UPDATE_SCHEDULED = "update_scheduled";
// Number of milliseconds between checks for whether the WebAPK's Web Manifest has changed.
public static final long UPDATE_INTERVAL = DateUtils.DAY_IN_MILLIS;
......@@ -514,6 +520,33 @@ public class WebappDataStorage {
return mPreferences.getBoolean(KEY_RELAX_UPDATES, false);
}
/** Sets whether an update has been scheduled. */
public void setUpdateScheduled(boolean isUpdateScheduled) {
mPreferences.edit().putBoolean(KEY_UPDATE_SCHEDULED, isUpdateScheduled).apply();
}
/** Gets whether an update has been scheduled. */
public boolean isUpdateScheduled() {
return mPreferences.getBoolean(KEY_UPDATE_SCHEDULED, false);
}
/** Sets whether an update should be forced. */
public void setShouldForceUpdate(boolean forceUpdate) {
mPreferences.edit().putBoolean(KEY_SHOULD_FORCE_UPDATE, forceUpdate).apply();
}
/** Whether to force an update. */
public boolean shouldForceUpdate() {
return mPreferences.getBoolean(KEY_SHOULD_FORCE_UPDATE, false);
}
/** Returns the update status. */
public String getUpdateStatus() {
if (isUpdateScheduled()) return "Scheduled";
if (shouldForceUpdate()) return "Pending";
return didPreviousUpdateSucceed() ? "Succeeded" : "Failed";
}
/**
* Returns file where WebAPK update data should be stored and stores the file name in
* SharedPreferences.
......@@ -556,6 +589,7 @@ public class WebappDataStorage {
/** Returns whether we should check for update. */
boolean shouldCheckForUpdate() {
if (shouldForceUpdate()) return true;
long checkUpdatesInterval =
shouldRelaxUpdates() ? RELAXED_UPDATE_INTERVAL : UPDATE_INTERVAL;
long now = sClock.currentTimeMillis();
......
......@@ -1094,4 +1094,68 @@ public class WebApkUpdateManagerUnitTest {
onGotManifestData(updateManager, defaultManifestData());
assertTrue(updateManager.updateRequested());
}
/**
* Tests that a forced update is requested and performed immediately if there is a material
* change to the manifest.
*/
@Test
public void testForcedUpdateSuccess() throws Exception {
WebappDataStorage storage = getStorage(WEBAPK_PACKAGE_NAME);
storage.setShouldForceUpdate(true);
TestWebApkUpdateManager updateManager = new TestWebApkUpdateManager(storage);
updateIfNeeded(updateManager);
assertTrue(updateManager.updateCheckStarted());
onGotDifferentData(updateManager);
assertTrue(updateManager.updateRequested());
tryCompletingUpdate(updateManager, storage, WebApkInstallResult.SUCCESS);
assertEquals(false, storage.shouldForceUpdate());
}
/**
* Tests that a forced update is requested, but not performed if there is no material change to
* the manifest.
*/
@Test
public void testForcedUpdateNotNeeded() throws Exception {
WebappDataStorage storage = getStorage(WEBAPK_PACKAGE_NAME);
storage.setShouldForceUpdate(true);
TestWebApkUpdateManager updateManager = new TestWebApkUpdateManager(storage);
updateIfNeeded(updateManager);
assertTrue(updateManager.updateCheckStarted());
onGotManifestData(updateManager, defaultManifestData());
assertFalse(updateManager.updateRequested());
assertEquals(false, storage.shouldForceUpdate());
}
/**
* Tests that a forced update handles failure gracefully.
*/
@Test
public void testForcedUpdateFailure() throws Exception {
WebappDataStorage storage = getStorage(WEBAPK_PACKAGE_NAME);
storage.setShouldForceUpdate(true);
TestWebApkUpdateManager updateManager = new TestWebApkUpdateManager(storage);
updateIfNeeded(updateManager);
assertTrue(updateManager.updateCheckStarted());
onGotDifferentData(updateManager);
assertTrue(updateManager.updateRequested());
tryCompletingUpdate(updateManager, storage, WebApkInstallResult.FAILURE);
assertEquals(false, storage.shouldForceUpdate());
}
/**
* Tests that a forced update handles failing to retrieve the manifest.
*/
@Test
public void testForcedUpdateManifestNotRetrieved() throws Exception {
WebappDataStorage storage = getStorage(WEBAPK_PACKAGE_NAME);
storage.setShouldForceUpdate(true);
TestWebApkUpdateManager updateManager = new TestWebApkUpdateManager(storage);
updateIfNeeded(updateManager);
assertTrue(updateManager.updateCheckStarted());
onGotManifestData(updateManager, null);
assertFalse(updateManager.updateRequested());
assertEquals(false, storage.shouldForceUpdate());
}
}
......@@ -358,6 +358,7 @@ void JNI_ShortcutHelper_OnWebApksRetrieved(
const JavaParamRef<jobjectArray>& jnames,
const JavaParamRef<jobjectArray>& jshort_names,
const JavaParamRef<jobjectArray>& jpackage_names,
const JavaParamRef<jobjectArray>& jids,
const JavaParamRef<jintArray>& jshell_apk_versions,
const JavaParamRef<jintArray>& jversion_codes,
const JavaParamRef<jobjectArray>& juris,
......@@ -369,7 +370,8 @@ void JNI_ShortcutHelper_OnWebApksRetrieved(
const JavaParamRef<jlongArray>& jtheme_colors,
const JavaParamRef<jlongArray>& jbackground_colors,
const JavaParamRef<jlongArray>& jlast_update_check_times_ms,
const JavaParamRef<jbooleanArray>& jrelax_updates) {
const JavaParamRef<jbooleanArray>& jrelax_updates,
const JavaParamRef<jobjectArray>& jupdateStatuses) {
DCHECK(jcallback_pointer);
std::vector<std::string> names;
base::android::AppendJavaStringArrayToStringVector(env, jnames, &names);
......@@ -379,6 +381,8 @@ void JNI_ShortcutHelper_OnWebApksRetrieved(
std::vector<std::string> package_names;
base::android::AppendJavaStringArrayToStringVector(env, jpackage_names,
&package_names);
std::vector<std::string> ids;
base::android::AppendJavaStringArrayToStringVector(env, jids, &ids);
std::vector<int> shell_apk_versions;
base::android::JavaIntArrayToIntVector(env, jshell_apk_versions,
&shell_apk_versions);
......@@ -409,9 +413,13 @@ void JNI_ShortcutHelper_OnWebApksRetrieved(
std::vector<bool> relax_updates;
base::android::JavaBooleanArrayToBoolVector(env, jrelax_updates,
&relax_updates);
std::vector<std::string> update_statuses;
base::android::AppendJavaStringArrayToStringVector(env, jupdateStatuses,
&update_statuses);
DCHECK(short_names.size() == names.size());
DCHECK(short_names.size() == package_names.size());
DCHECK(short_names.size() == ids.size());
DCHECK(short_names.size() == shell_apk_versions.size());
DCHECK(short_names.size() == version_codes.size());
DCHECK(short_names.size() == uris.size());
......@@ -424,21 +432,22 @@ void JNI_ShortcutHelper_OnWebApksRetrieved(
DCHECK(short_names.size() == background_colors.size());
DCHECK(short_names.size() == last_update_check_times_ms.size());
DCHECK(short_names.size() == relax_updates.size());
DCHECK(short_names.size() == update_statuses.size());
std::vector<WebApkInfo> webapk_list;
webapk_list.reserve(short_names.size());
for (size_t i = 0; i < short_names.size(); ++i) {
webapk_list.push_back(WebApkInfo(
std::move(names[i]), std::move(short_names[i]),
std::move(package_names[i]), shell_apk_versions[i], version_codes[i],
std::move(uris[i]), std::move(scopes[i]), std::move(manifest_urls[i]),
std::move(manifest_start_urls[i]),
std::move(package_names[i]), std::move(ids[i]), shell_apk_versions[i],
version_codes[i], std::move(uris[i]), std::move(scopes[i]),
std::move(manifest_urls[i]), std::move(manifest_start_urls[i]),
static_cast<blink::WebDisplayMode>(display_modes[i]),
static_cast<blink::WebScreenOrientationLockType>(orientations[i]),
JavaColorToOptionalSkColor(theme_colors[i]),
JavaColorToOptionalSkColor(background_colors[i]),
base::Time::FromJavaTime(last_update_check_times_ms[i]),
relax_updates[i]));
relax_updates[i], std::move(update_statuses[i])));
}
ShortcutHelper::WebApkInfoCallback* webapk_list_callback =
......@@ -446,3 +455,9 @@ void JNI_ShortcutHelper_OnWebApksRetrieved(
webapk_list_callback->Run(webapk_list);
delete webapk_list_callback;
}
void ShortcutHelper::SetForceWebApkUpdate(const std::string& id) {
JNIEnv* env = base::android::AttachCurrentThread();
Java_ShortcutHelper_setForceWebApkUpdate(
env, base::android::ConvertUTF8ToJavaString(env, id));
}
......@@ -104,6 +104,10 @@ class ShortcutHelper {
// the info to the |callback|.
static void RetrieveWebApks(const WebApkInfoCallback& callback);
// Sets a flag to force an update for the WebAPK corresponding to |id| on next
// launch.
static void SetForceWebApkUpdate(const std::string& id);
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(ShortcutHelper);
};
......
......@@ -46,6 +46,7 @@ message WebApk {
ORIENTATION_DIFFERS = 11;
DISPLAY_MODE_DIFFERS = 12;
WEB_SHARE_TARGET_DIFFERS = 13;
MANUALLY_TRIGGERED = 14;
}
// Package name of the WebAPK.
......
......@@ -9,6 +9,7 @@
WebApkInfo::WebApkInfo(std::string name,
std::string short_name,
std::string package_name,
std::string id,
int shell_apk_version,
int version_code,
std::string uri,
......@@ -20,10 +21,12 @@ WebApkInfo::WebApkInfo(std::string name,
base::Optional<SkColor> theme_color,
base::Optional<SkColor> background_color,
base::Time last_update_check_time,
bool relax_updates)
bool relax_updates,
std::string update_status)
: name(std::move(name)),
short_name(std::move(short_name)),
package_name(std::move(package_name)),
id(std::move(id)),
shell_apk_version(shell_apk_version),
version_code(version_code),
uri(std::move(uri)),
......@@ -35,7 +38,8 @@ WebApkInfo::WebApkInfo(std::string name,
theme_color(theme_color),
background_color(background_color),
last_update_check_time(last_update_check_time),
relax_updates(relax_updates) {}
relax_updates(relax_updates),
update_status(std::move(update_status)) {}
WebApkInfo::~WebApkInfo() {}
......
......@@ -23,6 +23,7 @@ struct WebApkInfo {
WebApkInfo(std::string name,
std::string short_name,
std::string package_name,
std::string id,
int shell_apk_version,
int version_code,
std::string uri,
......@@ -34,7 +35,8 @@ struct WebApkInfo {
base::Optional<SkColor> theme_color,
base::Optional<SkColor> background_color,
base::Time last_update_check_time,
bool relax_updates);
bool relax_updates,
std::string update_status);
~WebApkInfo();
WebApkInfo& operator=(WebApkInfo&& other) noexcept;
......@@ -49,6 +51,9 @@ struct WebApkInfo {
// Package name of the WebAPK.
std::string package_name;
// Internal ID of the WebAPK.
std::string id;
// Shell APK version of the WebAPK.
int shell_apk_version;
......@@ -66,6 +71,9 @@ struct WebApkInfo {
base::Time last_update_check_time;
bool relax_updates;
// Update Status of the WebAPK.
std::string update_status;
private:
DISALLOW_COPY_AND_ASSIGN(WebApkInfo);
};
......
......@@ -140,6 +140,8 @@ webapk::WebApk_UpdateReason ConvertUpdateReasonToProtoEnum(
return webapk::WebApk::DISPLAY_MODE_DIFFERS;
case WebApkUpdateReason::WEB_SHARE_TARGET_DIFFERS:
return webapk::WebApk::WEB_SHARE_TARGET_DIFFERS;
case WebApkUpdateReason::MANUALLY_TRIGGERED:
return webapk::WebApk::MANUALLY_TRIGGERED;
}
}
......
......@@ -23,6 +23,7 @@ enum class WebApkUpdateReason {
ORIENTATION_DIFFERS,
DISPLAY_MODE_DIFFERS,
WEB_SHARE_TARGET_DIFFERS,
MANUALLY_TRIGGERED,
};
#endif // CHROME_BROWSER_ANDROID_WEBAPK_WEBAPK_TYPES_H_
......@@ -7,6 +7,7 @@
* name: string,
* shortName: string,
* packageName: string,
* id: string,
* shellApkVersion: number,
* versionCode: number,
* uri: string,
......@@ -19,10 +20,19 @@
* backgroundColor: string,
* lastUpdateCheckTimeMs: number,
* relaxUpdates: boolean,
* updateStatus: string,
* }}
*/
let WebApkInfo;
/**
* @typedef {{
* id: string,
* status: string,
* }}
*/
let UpdateStatus;
/**
* Creates and returns an element (with |text| as content) assigning it the
* |className| class.
......@@ -40,8 +50,8 @@ function createElementWithTextAndClass(text, type, className) {
/**
* Callback from the backend with the information of a WebAPK to display.
* This will be called once for each WebAPK available on the device and each
* one will be appended at the end of the other.
* This will be called once. All WebAPKs available on the device will be
* returned.
*
* @param {!Array<WebApkInfo>} webApkList List of objects with information about
* WebAPKs installed.
......@@ -66,6 +76,19 @@ function addWebApkField(webApkList, label, value) {
webApkList.appendChild(divElement);
}
/**
* @param {HTMLElement} webApkList List of elements which contain WebAPK
* attributes.
* @param {string} text For the button.
* @param {function()} callback Invoked on click.
*/
function addWebApkButton(webApkList, text, callback) {
const divElement =
createElementWithTextAndClass(text, 'button', 'update-button');
divElement.onclick = callback;
webApkList.appendChild(divElement);
}
/**
* Adds a new entry to the page with the information of a WebAPK.
*
......@@ -100,6 +123,16 @@ function addWebApk(webApkInfo) {
addWebApkField(
webApkList, 'Check for Updates Less Frequently: ',
webApkInfo.relaxUpdates.toString());
addWebApkField(webApkList, 'Update Status: ', webApkInfo.updateStatus);
addWebApkButton(webApkList, 'Update ' + webApkInfo.name, () => {
alert(
'The WebAPK will check for an update the next time it launches. ' +
'If an update is available, the "Update Status" on this page ' +
'will switch to "Scheduled". The update will be installed once ' +
'the WebAPK is closed (this may take a few minutes).');
chrome.send('requestWebApkUpdate', [webApkInfo.id]);
});
}
document.addEventListener('DOMContentLoaded', function() {
......
......@@ -4,7 +4,9 @@
#include "chrome/browser/ui/webui/webapks_handler.h"
#include <memory>
#include <string>
#include <vector>
#include "base/bind.h"
#include "base/callback_forward.h"
......@@ -25,6 +27,10 @@ void WebApksHandler::RegisterMessages() {
"requestWebApksInfo",
base::BindRepeating(&WebApksHandler::HandleRequestWebApksInfo,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"requestWebApkUpdate",
base::BindRepeating(&WebApksHandler::HandleRequestWebApkUpdate,
base::Unretained(this)));
}
void WebApksHandler::HandleRequestWebApksInfo(const base::ListValue* args) {
......@@ -33,13 +39,21 @@ void WebApksHandler::HandleRequestWebApksInfo(const base::ListValue* args) {
&WebApksHandler::OnWebApkInfoRetrieved, weak_ptr_factory_.GetWeakPtr()));
}
void WebApksHandler::HandleRequestWebApkUpdate(const base::ListValue* args) {
AllowJavascript();
for (const auto& val : args->GetList()) {
if (val.is_string())
ShortcutHelper::SetForceWebApkUpdate(val.GetString());
}
}
void WebApksHandler::OnWebApkInfoRetrieved(
const std::vector<WebApkInfo>& webapks_list) {
if (!IsJavascriptAllowed())
return;
base::ListValue list;
for (const auto& webapk_info : webapks_list) {
std::unique_ptr<base::DictionaryValue> result(new base::DictionaryValue());
auto result = std::make_unique<base::DictionaryValue>();
result->SetString("name", webapk_info.name);
result->SetString("shortName", webapk_info.short_name);
result->SetString("packageName", webapk_info.package_name);
......@@ -61,6 +75,7 @@ void WebApksHandler::OnWebApkInfoRetrieved(
result->SetDouble("lastUpdateCheckTimeMs",
webapk_info.last_update_check_time.ToJsTime());
result->SetBoolean("relaxUpdates", webapk_info.relax_updates);
result->SetString("updateStatus", webapk_info.update_status);
list.Append(std::move(result));
}
......
......@@ -28,7 +28,12 @@ class WebApksHandler : public content::WebUIMessageHandler {
// Handler for the "requestWebApksInfo" message. This requests
// information for the installed WebAPKs and returns it to JS using
// OnWebApkInfoReceived().
virtual void HandleRequestWebApksInfo(const base::ListValue* args);
void HandleRequestWebApksInfo(const base::ListValue* args);
// Handler for the "requestWebApkUpdate" message. This sets the
// update flag for a set of WebAPKs. |args| should contain the
// webapp IDs of the WebAPKs to update.
void HandleRequestWebApkUpdate(const base::ListValue* args);
private:
// Sends information for the installed WebAPKs to JS.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment