blob: e35cec3d327d536177a6a78f992195aa4f26cdd3 [file] [log] [blame]
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
var wallpaperPickerWindow = null;
var surpriseWallpaper = null;
/**
* Returns the highResolutionSuffix.
* @param {function} callback A callback function that takes one value:
* |highResolutionSuffix|: the suffix to append to the wallpaper urls.
*/
function getHighResolutionSuffix(callback) {
chrome.wallpaperPrivate.getStrings(strings => {
callback(strings['highResolutionSuffix']);
});
}
function SurpriseWallpaper() {}
/**
* Gets SurpriseWallpaper instance. In case it hasn't been initialized, a new
* instance is created.
* @return {SurpriseWallpaper} A SurpriseWallpaper instance.
*/
SurpriseWallpaper.getInstance = function() {
if (!surpriseWallpaper)
surpriseWallpaper = new SurpriseWallpaper();
return surpriseWallpaper;
};
/**
* Retries changing the wallpaper 1 hour later. This is called when fetching the
* wallpaper from server fails.
* @private
*/
SurpriseWallpaper.prototype.retryLater_ = function() {
chrome.alarms.create('RetryAlarm', {delayInMinutes: 60});
};
/**
* Sets a new random wallpaper if one has not already been set today.
* @private
*/
SurpriseWallpaper.prototype.updateRandomWallpaper_ = function() {
var self = this;
var onSuccess = function(items) {
var dateString = new Date().toDateString();
// At most one random wallpaper per day.
if (items[Constants.AccessLastSurpriseWallpaperChangedDate] != dateString) {
self.setRandomWallpaper_(dateString);
}
};
WallpaperUtil.enabledSyncThemesCallback(function(syncEnabled) {
if (syncEnabled) {
Constants.WallpaperSyncStorage.get(
Constants.AccessLastSurpriseWallpaperChangedDate, onSuccess);
} else {
Constants.WallpaperLocalStorage.get(
Constants.AccessLastSurpriseWallpaperChangedDate, onSuccess);
}
});
};
/**
* Sets wallpaper to be a random one. The wallpaper url is retrieved either from
* the stored manifest file, or by fetching the wallpaper info from the server.
* @param {string} dateString String representation of current local date.
* @private
*/
SurpriseWallpaper.prototype.setRandomWallpaper_ = function(dateString) {
var onSuccess = function(url, layout) {
WallpaperUtil.saveWallpaperInfo(
url, layout, Constants.WallpaperSourceEnum.Daily, '');
WallpaperUtil.saveToLocalStorage(
Constants.AccessLastSurpriseWallpaperChangedDate, dateString,
function() {
WallpaperUtil.saveToSyncStorage(
Constants.AccessLastSurpriseWallpaperChangedDate, dateString);
});
};
getHighResolutionSuffix(highResolutionSuffix => {
this.setRandomWallpaperFromServer_(onSuccess, highResolutionSuffix);
});
};
/**
* Sets wallpaper to be a random one retrieved from the backend service. If the
* wallpaper download fails, retry one hour later.
* @param {function} onSuccess The success callback.
* @param {string} suffix The url suffix for high resolution wallpaper.
* @private
*/
SurpriseWallpaper.prototype.setRandomWallpaperFromServer_ = function(
onSuccess, suffix) {
var onDailyRefreshInfoReturned = dailyRefreshInfo => {
var setRandomWallpaperFromServerImpl = dailyRefreshInfo => {
chrome.wallpaperPrivate.getSurpriseMeImage(
dailyRefreshInfo.collectionId, dailyRefreshInfo.resumeToken,
(imageInfo, nextResumeToken) => {
if (chrome.runtime.lastError) {
this.retryLater_();
return;
}
dailyRefreshInfo.resumeToken = nextResumeToken;
WallpaperUtil.saveDailyRefreshInfo(dailyRefreshInfo);
var wallpaperUrl = imageInfo['imageUrl'] + suffix;
var layout = Constants.WallpaperThumbnailDefaultLayout;
WallpaperUtil.setOnlineWallpaperWithoutPreview(
wallpaperUrl, layout,
onSuccess.bind(null, wallpaperUrl, layout),
this.retryLater_.bind(this));
});
};
if (dailyRefreshInfo) {
if (dailyRefreshInfo.enabled) {
setRandomWallpaperFromServerImpl(dailyRefreshInfo);
} else {
console.error(
'Daily refresh is disabled when the alarm goes off. ' +
'This should never happen!');
}
return;
}
// Migration: we reach here if the old picker set an alarm and by the time
// the alarm goes off, the new picker is already in use. We should ensure
// the user transitions to the daily refresh feature.
chrome.wallpaperPrivate.getCollectionsInfo(collectionsInfo => {
if (chrome.runtime.lastError) {
this.retryLater_();
return;
}
if (collectionsInfo.length == 0) {
// Although the fetch succeeds, it's theoretically possible that the
// collection list is empty, in this case do nothing.
return;
}
dailyRefreshInfo = {
enabled: true,
// Use the first collection (an arbitrary choice).
collectionId: collectionsInfo[0]['collectionId'],
resumeToken: null
};
setRandomWallpaperFromServerImpl(dailyRefreshInfo);
});
};
WallpaperUtil.getDailyRefreshInfo(onDailyRefreshInfoReturned.bind(null));
};
/**
* Disables the wallpaper surprise me feature. Clear all alarms and states.
*/
SurpriseWallpaper.prototype.disable = function() {
chrome.alarms.clearAll();
// Makes last changed date invalid.
WallpaperUtil.saveToLocalStorage(
Constants.AccessLastSurpriseWallpaperChangedDate, '', function() {
WallpaperUtil.saveToSyncStorage(
Constants.AccessLastSurpriseWallpaperChangedDate, '');
});
};
/**
* Changes current wallpaper and sets up an alarm to schedule next change around
* midnight.
*/
SurpriseWallpaper.prototype.next = function() {
var nextUpdate = this.nextUpdateTime(new Date());
chrome.alarms.create({when: nextUpdate});
this.updateRandomWallpaper_();
};
/**
* Calculates when the next wallpaper change should be triggered.
* @param {Date} now Current time.
* @return {number} The time when next wallpaper change should happen.
*/
SurpriseWallpaper.prototype.nextUpdateTime = function(now) {
var nextUpdate = new Date(now.setDate(now.getDate() + 1)).toDateString();
return new Date(nextUpdate).getTime();
};
chrome.app.runtime.onLaunched.addListener(function() {
if (wallpaperPickerWindow && !wallpaperPickerWindow.contentWindow.closed) {
wallpaperPickerWindow.focus();
chrome.wallpaperPrivate.minimizeInactiveWindows();
return;
}
var options = {
frame: 'none',
innerBounds: {width: 768, height: 512, minWidth: 768, minHeight: 512},
resizable: true,
alphaEnabled: true
};
chrome.app.window.create('main.html', options, function(window) {
wallpaperPickerWindow = window;
chrome.wallpaperPrivate.minimizeInactiveWindows();
window.onClosed.addListener(function() {
wallpaperPickerWindow = null;
const isDuringPreview =
window.contentWindow.document.body.classList.contains('preview-mode');
const isWallpaperSet =
window.contentWindow.document.body.classList.contains(
'wallpaper-set-successfully');
// Cancel preview if the app exits before user confirming the
// wallpaper (e.g. when the app is closed in overview mode).
if (isDuringPreview && !isWallpaperSet)
chrome.wallpaperPrivate.cancelPreviewWallpaper(() => {});
// Do not restore the minimized windows if the app exits because of
// confirming preview: prefer to continue showing the new wallpaper to
// user.
const isExitingAfterPreviewConfirm = isDuringPreview && isWallpaperSet;
if (!isExitingAfterPreviewConfirm)
chrome.wallpaperPrivate.restoreMinimizedWindows();
});
// By design, the wallpaper picker should never be shown on top of
// another window.
wallpaperPickerWindow.contentWindow.addEventListener('focus', function() {
chrome.wallpaperPrivate.minimizeInactiveWindows();
});
WallpaperUtil.testSendMessage('wallpaper-window-created');
});
});
chrome.syncFileSystem.onFileStatusChanged.addListener(function(detail) {
WallpaperUtil.enabledSyncThemesCallback(function(syncEnabled) {
if (!syncEnabled)
return;
if (detail.status != 'synced' || detail.direction != 'remote_to_local')
return;
if (detail.action == 'added') {
// TODO(xdai): Get rid of this setCustomWallpaperFromSyncFS logic.
// WallpaperInfo might have been saved in the sync filesystem before the
// corresonding custom wallpaper and thumbnail are saved, thus the
// onChanged() might not set the custom wallpaper correctly. So we need
// setCustomWallpaperFromSyncFS() to be called here again to make sure
// custom wallpaper is set.
Constants.WallpaperLocalStorage.get(
Constants.AccessLocalWallpaperInfoKey, function(items) {
var localData = items[Constants.AccessLocalWallpaperInfoKey];
if (localData && localData.url == detail.fileEntry.name &&
localData.source == Constants.WallpaperSourceEnum.Custom) {
WallpaperUtil.setCustomWallpaperFromSyncFS(
localData.url, localData.layout);
}
});
// We only need to store the custom wallpaper if it was set by the
// built-in wallpaper picker.
if (!detail.fileEntry.name.startsWith(
Constants.ThirdPartyWallpaperPrefix)) {
WallpaperUtil.storeWallpaperFromSyncFSToLocalFS(detail.fileEntry);
}
} else if (detail.action == 'deleted') {
var fileName = detail.fileEntry.name.replace(
Constants.CustomWallpaperThumbnailSuffix, '');
WallpaperUtil.deleteWallpaperFromLocalFS(fileName);
}
});
});
chrome.storage.onChanged.addListener(function(changes, namespace) {
WallpaperUtil.enabledSyncThemesCallback(function(syncEnabled) {
var updateDailyRefreshStates = key => {
if (!changes[key])
return;
// If the user did not change Daily Refresh in this sync update,
// changes[key].oldValue will be empty
var oldDailyRefreshInfo =
changes[key].oldValue ? JSON.parse(changes[key].oldValue) : '';
var newDailyRefreshInfo = JSON.parse(changes[key].newValue);
// The resume token is expected to change after a new daily refresh
// wallpaper is set. Ignore it if it's the only change.
if (oldDailyRefreshInfo.enabled === newDailyRefreshInfo.enabled &&
oldDailyRefreshInfo.collectionId ===
newDailyRefreshInfo.collectionId) {
return;
}
// Although the old and new values may both have enabled == true, they can
// have different collection ids, so the old alarm should always be
// cleared.
chrome.alarms.clearAll();
if (newDailyRefreshInfo.enabled)
SurpriseWallpaper.getInstance().next();
};
updateDailyRefreshStates(
syncEnabled ? Constants.AccessSyncDailyRefreshInfoKey :
Constants.AccessLocalDailyRefreshInfoKey);
if (syncEnabled) {
// If sync theme is enabled, use values from chrome.storage.sync to sync
// wallpaper changes.
if (changes[Constants.AccessSyncSurpriseMeEnabledKey]) {
if (changes[Constants.AccessSyncSurpriseMeEnabledKey].newValue) {
SurpriseWallpaper.getInstance().next();
} else {
SurpriseWallpaper.getInstance().disable();
}
}
// If the built-in Wallpaper Picker App is open, update the check mark
// and the corresponding message in time.
var updateCheckMarkAndAppNameIfAppliable = function(appName) {
if (!wallpaperPickerWindow)
return;
var wpDocument = wallpaperPickerWindow.contentWindow.document;
var messageContent = wpDocument.querySelector('#message-content');
chrome.wallpaperPrivate.getStrings(strings => {
if (appName) {
wpDocument.querySelector('#message-container').display = 'block';
var message =
strings.currentWallpaperSetByMessage.replace(/\$1/g, appName);
messageContent.textContent = message;
wpDocument.querySelector('#checkbox').classList.remove('checked');
wpDocument.querySelector('#categories-list').disabled = false;
wpDocument.querySelector('#wallpaper-grid').disabled = false;
} else {
Constants.WallpaperSyncStorage.get(
Constants.AccessSyncSurpriseMeEnabledKey, function(item) {
// TODO(crbug.com/810169): Try to combine this part with
// |WallpaperManager.onSurpriseMeStateChanged_|. The logic is
// duplicate.
var enable = item[Constants.AccessSyncSurpriseMeEnabledKey];
if (enable) {
wpDocument.querySelector('#checkbox')
.classList.add('checked');
} else {
wpDocument.querySelector('#checkbox')
.classList.remove('checked');
if (wpDocument.querySelector('.check'))
wpDocument.querySelector('.check').style.visibility =
'visible';
}
});
}
});
};
if (changes[Constants.AccessLocalWallpaperInfoKey]) {
// If the old wallpaper is a third party wallpaper we should remove it
// from the local & sync file system to free space.
var oldInfo = changes[Constants.AccessLocalWallpaperInfoKey].oldValue;
if (oldInfo &&
oldInfo.url.indexOf(Constants.ThirdPartyWallpaperPrefix) != -1) {
WallpaperUtil.deleteWallpaperFromLocalFS(oldInfo.url);
WallpaperUtil.deleteWallpaperFromSyncFS(oldInfo.url);
}
var newInfo = changes[Constants.AccessLocalWallpaperInfoKey].newValue;
if (newInfo && newInfo.hasOwnProperty('appName'))
updateCheckMarkAndAppNameIfAppliable(newInfo.appName);
}
if (changes[Constants.AccessSyncWallpaperInfoKey]) {
var syncInfo = changes[Constants.AccessSyncWallpaperInfoKey].newValue;
Constants.WallpaperSyncStorage.get(
Constants.AccessSyncSurpriseMeEnabledKey, function(enabledItems) {
var syncSurpriseMeEnabled =
enabledItems[Constants.AccessSyncSurpriseMeEnabledKey];
Constants.WallpaperSyncStorage.get(
Constants.AccessLastSurpriseWallpaperChangedDate,
function(items) {
var syncLastSurpriseMeChangedDate =
items[Constants.AccessLastSurpriseWallpaperChangedDate];
var today = new Date().toDateString();
// If SurpriseMe is enabled and surprise wallpaper hasn't
// been changed today, we should not sync the change,
// instead onAlarm() will be triggered to update a surprise
// me wallpaper.
if (!syncSurpriseMeEnabled ||
(syncSurpriseMeEnabled &&
syncLastSurpriseMeChangedDate == today)) {
Constants.WallpaperLocalStorage.get(
Constants.AccessLocalWallpaperInfoKey,
function(infoItems) {
var localInfo =
infoItems[Constants
.AccessLocalWallpaperInfoKey];
// Normally, the wallpaper info saved in local
// storage and sync storage are the same. If the
// synced value changed by sync service, they may
// different. In that case, change wallpaper to the
// one saved in sync storage and update the local
// value.
if (!localInfo || localInfo.url != syncInfo.url ||
localInfo.layout != syncInfo.layout ||
localInfo.source != syncInfo.source) {
if (syncInfo.source ==
Constants.WallpaperSourceEnum.Online) {
// TODO(bshe): Consider schedule an alarm to set
// online wallpaper later when failed. Note that
// we need to cancel the retry if user set
// another wallpaper before retry alarm invoked.
WallpaperUtil.setOnlineWallpaperWithoutPreview(
syncInfo.url, syncInfo.layout,
function() {}, function() {});
} else if (
syncInfo.source ==
Constants.WallpaperSourceEnum.Custom) {
WallpaperUtil.setCustomWallpaperFromSyncFS(
syncInfo.url, syncInfo.layout);
} else if (
syncInfo.source ==
Constants.WallpaperSourceEnum.Default) {
chrome.wallpaperPrivate.resetWallpaper();
}
// If the old wallpaper is a third party wallpaper
// we should remove it from the local & sync file
// system to free space.
if (localInfo &&
localInfo.url.indexOf(
Constants.ThirdPartyWallpaperPrefix) !=
-1) {
WallpaperUtil.deleteWallpaperFromLocalFS(
localInfo.url);
WallpaperUtil.deleteWallpaperFromSyncFS(
localInfo.url);
}
if (syncInfo &&
syncInfo.hasOwnProperty('appName'))
updateCheckMarkAndAppNameIfAppliable(
syncInfo.appName);
WallpaperUtil.saveToLocalStorage(
Constants.AccessLocalWallpaperInfoKey,
syncInfo);
}
});
}
});
});
}
} else {
// If sync theme is disabled, use values from chrome.storage.local to
// track wallpaper changes.
if (changes[Constants.AccessLocalSurpriseMeEnabledKey]) {
if (changes[Constants.AccessLocalSurpriseMeEnabledKey].newValue) {
SurpriseWallpaper.getInstance().next();
} else {
SurpriseWallpaper.getInstance().disable();
}
}
}
});
});
chrome.alarms.onAlarm.addListener(function() {
SurpriseWallpaper.getInstance().next();
});
chrome.wallpaperPrivate.onWallpaperChangedBy3rdParty.addListener(function(
wallpaper, thumbnail, layout, appName) {
WallpaperUtil.saveToLocalStorage(
Constants.AccessLocalSurpriseMeEnabledKey, false, function() {
WallpaperUtil.saveToSyncStorage(
Constants.AccessSyncSurpriseMeEnabledKey, false);
});
// Make third party wallpaper syncable through different devices.
var fileName = Constants.ThirdPartyWallpaperPrefix + new Date().getTime();
var thumbnailFileName = fileName + Constants.CustomWallpaperThumbnailSuffix;
WallpaperUtil.storeWallpaperToSyncFS(fileName, wallpaper);
WallpaperUtil.storeWallpaperToSyncFS(thumbnailFileName, thumbnail);
WallpaperUtil.saveWallpaperInfo(
fileName, layout, Constants.WallpaperSourceEnum.ThirdParty, appName);
WallpaperUtil.saveDailyRefreshInfo(
{enabled: false, collectionId: null, resumeToken: null});
if (wallpaperPickerWindow) {
var event = new CustomEvent(
Constants.WallpaperChangedBy3rdParty,
{detail: {wallpaperFileName: fileName}});
wallpaperPickerWindow.contentWindow.dispatchEvent(event);
}
});
chrome.wallpaperPrivate.onClosePreviewWallpaper.addListener(function() {
if (wallpaperPickerWindow) {
var event = new CustomEvent(Constants.ClosePreviewWallpaper);
wallpaperPickerWindow.contentWindow.dispatchEvent(event);
}
});