Index: chrome/browser/resources/chromeos/login/oobe_screen_user_image.js |
diff --git a/chrome/browser/resources/chromeos/login/oobe_screen_user_image.js b/chrome/browser/resources/chromeos/login/oobe_screen_user_image.js |
index 59eaa02509f69d7cdb7571420c42bc519afec37d..34ac81458fdda59ba04e4c8442e91ae07d53aea8 100644 |
--- a/chrome/browser/resources/chromeos/login/oobe_screen_user_image.js |
+++ b/chrome/browser/resources/chromeos/login/oobe_screen_user_image.js |
@@ -7,20 +7,20 @@ |
*/ |
cr.define('oobe', function() { |
- |
var UserImagesGrid = options.UserImagesGrid; |
var ButtonImages = UserImagesGrid.ButtonImages; |
/** |
* Array of button URLs used on this page. |
* @type {Array.<string>} |
+ * @const |
*/ |
- const ButtonImageUrls = [ |
+ var ButtonImageUrls = [ |
ButtonImages.TAKE_PHOTO |
]; |
/** |
- * Creates a new oobe screen div. |
+ * Creates a new OOBE screen div. |
* @constructor |
* @extends {HTMLDivElement} |
*/ |
@@ -31,11 +31,15 @@ cr.define('oobe', function() { |
*/ |
UserImageScreen.register = function() { |
var screen = $('user-image'); |
+ var isWebRTC = document.documentElement.getAttribute('camera') == 'webrtc'; |
+ console.log('WebRTC: ' + isWebRTC); |
Nikita (slow)
2012/06/07 15:26:55
Remove console output.
Ivan Korotkov
2012/06/09 14:19:01
Done.
|
+ UserImageScreen.prototype = isWebRTC ? UserImageScreenWebRTCProto : |
+ UserImageScreenOldProto; |
UserImageScreen.decorate(screen); |
Oobe.getInstance().registerScreen(screen); |
}; |
- UserImageScreen.prototype = { |
+ var UserImageScreenOldProto = { |
__proto__: HTMLDivElement.prototype, |
/** |
@@ -102,7 +106,7 @@ cr.define('oobe', function() { |
okButton.id = 'ok-button'; |
okButton.textContent = localStrings.getString('okButtonText'); |
okButton.addEventListener('click', this.acceptImage_.bind(this)); |
- return [ okButton ]; |
+ return [okButton]; |
}, |
/** |
@@ -322,7 +326,419 @@ cr.define('oobe', function() { |
/** |
* Updates localized content of the screen that is not updated via template. |
- * @public |
+ */ |
+ updateLocalizedContent: function() { |
+ this.updateProfileImageCaption_(); |
+ }, |
+ |
+ /** |
+ * Updates profile image caption. |
+ * @private |
+ */ |
+ updateProfileImageCaption_: function() { |
+ this.profileImageCaption = localStrings.getString( |
+ this.profileImageLoading_ ? 'profilePhotoLoading' : 'profilePhoto'); |
+ } |
+ }; |
+ |
+ var UserImageScreenWebRTCProto = { |
+ __proto__: HTMLDivElement.prototype, |
+ |
+ /** |
+ * Currently selected user image index (take photo button is with zero |
+ * index). |
+ * @type {number} |
+ */ |
+ selectedUserImage_: -1, |
+ |
+ /** @inheritDoc */ |
+ decorate: function(element) { |
+ var imageGrid = $('user-image-grid'); |
+ UserImagesGrid.decorate(imageGrid); |
+ |
+ imageGrid.addEventListener('change', |
+ this.handleSelection_.bind(this)); |
+ imageGrid.addEventListener('activate', |
+ this.handleImageActivated_.bind(this)); |
+ imageGrid.addEventListener('dblclick', |
+ this.handleImageDblClick_.bind(this)); |
+ |
+ // Profile image data (if present). |
+ this.profileImage_ = imageGrid.addItem( |
+ ButtonImages.PROFILE_PICTURE, |
+ undefined, undefined, undefined, |
+ function(el) { // Custom decorator for Profile image element. |
+ var spinner = el.ownerDocument.createElement('div'); |
+ spinner.className = 'spinner'; |
+ var spinnerBg = el.ownerDocument.createElement('div'); |
+ spinnerBg.className = 'spinner-bg'; |
+ spinnerBg.appendChild(spinner); |
+ el.appendChild(spinnerBg); |
+ el.id = 'profile-image'; |
+ }); |
+ this.profileImage_.type = 'profile'; |
+ this.selectionType = 'default'; |
+ |
+ var video = $('user-image-stream'); |
+ video.addEventListener('canplay', this.handleVideoStarted_.bind(this)); |
+ video.addEventListener('timeupdate', this.handleVideoUpdate_.bind(this)); |
+ $('take-photo').addEventListener('click', |
+ this.handleTakePhoto_.bind(this)); |
+ $('discard-photo').addEventListener('click', |
+ this.handleDiscardPhoto_.bind(this)); |
+ this.cameraImage = null; |
+ this.checkVideo_(); |
Nikita (slow)
2012/06/07 15:26:55
Not sure if this the right place for this check.
Ivan Korotkov
2012/06/09 14:19:01
Right, fixed.
|
+ }, |
+ |
+ /** |
+ * Header text of the screen. |
+ * @type {string} |
+ */ |
+ get header() { |
+ return localStrings.getString('userImageScreenTitle'); |
+ }, |
+ |
+ /** |
+ * Buttons in oobe wizard's button strip. |
+ * @type {array} Array of Buttons. |
+ */ |
+ get buttons() { |
+ var okButton = this.ownerDocument.createElement('button'); |
+ okButton.id = 'ok-button'; |
+ okButton.textContent = localStrings.getString('okButtonText'); |
+ okButton.addEventListener('click', this.acceptImage_.bind(this)); |
+ return [okButton]; |
+ }, |
+ |
+ /** |
+ * The caption to use for the Profile image preview. |
+ * @type {string} |
+ */ |
+ get profileImageCaption() { |
+ return this.profileImageCaption_; |
+ }, |
+ set profileImageCaption(value) { |
+ this.profileImageCaption_ = value; |
+ this.updateCaption_(); |
+ }, |
+ |
+ /** |
+ * True if the Profile image is being loaded. |
+ * @type {boolean} |
+ */ |
+ get profileImageLoading() { |
+ return this.profileImageLoading_; |
+ }, |
+ set profileImageLoading(value) { |
+ this.profileImageLoading_ = value; |
+ $('user-image-screen-main').classList[ |
+ value ? 'add' : 'remove']('profile-image-loading'); |
+ this.updateProfileImageCaption_(); |
+ }, |
+ |
+ /** |
+ * True when camera is in live mode (i.e. no still photo selected). |
+ * @type {boolean} |
+ */ |
+ get cameraLive() { |
+ return this.cameraLive_; |
+ }, |
+ set cameraLive(value) { |
+ this.cameraLive_ = value; |
+ $('user-image-preview').classList[value ? 'add' : 'remove']('live'); |
+ }, |
+ |
+ /** |
+ * Type of the selected image (one of 'default', 'profile', 'camera'). |
+ * @type {string} |
+ */ |
+ get selectionType() { |
+ return this.selectionType_; |
+ }, |
+ set selectionType(value) { |
+ this.selectionType_ = value; |
+ var previewClassList = $('user-image-preview').classList; |
+ previewClassList[value == 'default' ? 'add' : 'remove']('default-image'); |
+ previewClassList[value == 'profile' ? 'add' : 'remove']('profile-image'); |
+ previewClassList[value == 'camera' ? 'add' : 'remove']('camera'); |
+ this.updateCaption_(); |
+ }, |
+ |
+ /** |
+ * Handles image activation (by pressing Enter). |
+ * @private |
+ */ |
+ handleImageActivated_: function() { |
+ switch ($('user-image-grid').selectedItemUrl) { |
+ case ButtonImages.TAKE_PHOTO: |
+ this.handleTakePhoto_(); |
+ break; |
+ default: |
+ this.acceptImage_(); |
+ break; |
+ } |
+ }, |
+ |
+ /** |
+ * Handles photo capture from the live camera stream. |
+ * @private |
+ */ |
+ handleTakePhoto_: function() { |
+ var self = this; |
+ var photoURL = this.captureFrame_($('user-image-stream'), |
+ {width: 320, height: 320}); |
Nikita (slow)
2012/06/07 15:26:55
nit: Extract constants.
Ivan Korotkov
2012/06/09 14:19:01
Done.
|
+ chrome.send('photoTaken', [photoURL]); |
+ // Wait until image is loaded before displaying it. |
+ var previewImg = new Image(); |
+ previewImg.addEventListener('load', function(e) { |
+ self.cameraImage = this.src; |
+ }); |
+ previewImg.src = photoURL; |
+ }, |
+ |
+ /** |
+ * Discard current photo and return to the live camera stream. |
+ * @private |
+ */ |
+ handleDiscardPhoto_: function() { |
+ this.cameraImage = null; |
+ }, |
+ |
+ /** |
+ * Capture a single still frame from a <video> element. |
+ * @param {HTMLVideoElement} video Video element to capture from. |
+ * @param {{width: number, height: number}} destSize Capture size. |
+ * @return {string} Captured frame as a data URL. |
+ * @private |
+ */ |
+ captureFrame_: function(video, destSize) { |
+ var canvas = document.createElement('canvas'); |
+ canvas.width = destSize.width; |
+ canvas.height = destSize.height; |
+ var ctx = canvas.getContext('2d'); |
+ var W = video.videoWidth, H = video.videoHeight; |
Nikita (slow)
2012/06/07 15:26:55
Split into 2 lines.
Nikita (slow)
2012/06/07 15:26:55
rename to width/height in lowercase
Ivan Korotkov
2012/06/09 14:19:01
Done.
Ivan Korotkov
2012/06/09 14:19:01
Done.
|
+ if (W < destSize.width || H < destSize.height) |
+ console.error('Video capture size too small: ' + W + 'x' + H + '!'); |
+ var src = {}; |
+ if (W / destSize.width > H / destSize.height) { |
+ // Full height, crop left/right. |
+ src.height = H; |
+ src.width = H * destSize.width / destSize.height; |
+ } else { |
+ // Full width, crop top/bottom. |
+ src.width = W; |
+ src.height = W * destSize.height / destSize.width; |
+ } |
+ src.x = (W - src.width) / 2; |
+ src.y = (H - src.height) / 2; |
+ ctx.drawImage(video, src.x, src.y, src.width, src.height, |
+ 0, 0, destSize.width, destSize.height); |
+ return canvas.toDataURL('image/png'); |
+ }, |
+ |
+ /** |
+ * Handles selection change. |
+ * @private |
+ */ |
+ handleSelection_: function() { |
+ var selectedItem = $('user-image-grid').selectedItem; |
+ if (selectedItem === null) |
+ return; |
+ |
+ // Update preview image URL. |
+ var url = selectedItem.url; |
+ $('user-image-preview-img').src = url; |
+ |
+ // Update current selection type. |
+ this.selectionType = selectedItem.type; |
+ |
+ // Show grey silhouette with the same border as stock images. |
+ if (/^chrome:\/\/theme\//.test(url)) |
+ $('user-image-preview').classList.add('default-image'); |
+ |
+ if (ButtonImageUrls.indexOf(url) == -1) { |
+ // Non-button image is selected. |
+ $('ok-button').disabled = false; |
+ chrome.send('selectImage', [url]); |
+ } else { |
+ $('ok-button').disabled = true; |
+ } |
+ }, |
+ |
+ /** |
+ * Handles double click on the image grid. |
+ * @param {Event} e Double click Event. |
+ */ |
+ handleImageDblClick_: function(e) { |
+ // If an image is double-clicked and not the grid itself, handle this |
+ // as 'OK' button button press. |
+ if (e.target.id != 'user-image-grid') |
+ this.acceptImage_(); |
+ }, |
+ |
+ /** |
+ * Event handler that is invoked just before the screen is shown. |
+ * @param {object} data Screen init payload. |
+ */ |
+ onBeforeShow: function(data) { |
+ Oobe.getInstance().headerHidden = true; |
+ $('user-image-grid').updateAndFocus(); |
+ chrome.send('onUserImageScreenShown'); |
+ //this.checkVideo_(); |
Nikita (slow)
2012/06/07 15:26:55
Remove or add a second part of the initialization
Ivan Korotkov
2012/06/09 14:19:01
Done.
|
+ }, |
+ |
+ /** |
+ * Accepts currently selected image, if possible. |
+ * @private |
+ */ |
+ acceptImage_: function() { |
+ var okButton = $('ok-button'); |
+ if (!okButton.disabled) { |
+ // This ensures that #ok-button won't be re-enabled again. |
+ $('user-image-grid').disabled = true; |
+ okButton.disabled = true; |
+ chrome.send('onUserImageAccepted'); |
+ } |
+ }, |
+ |
+ /** |
+ * @param {boolean} present Whether a camera is present or not. |
+ */ |
+ get cameraPresent() { |
+ return this.cameraPresent_; |
+ }, |
+ set cameraPresent(value) { |
+ this.cameraPresent_ = value; |
+ if (this.cameraLive) |
+ this.cameraImage = null; |
+ }, |
+ |
+ /** |
+ * Start camera presence check. |
+ * @private |
+ */ |
+ checkVideo_: function() { |
Nikita (slow)
2012/06/07 15:26:55
Rename to checkCameraPresence?
|
+ $('user-image-preview').classList.remove('online'); |
+ navigator.webkitGetUserMedia({video: true}, |
+ this.handleVideoAvailable_.bind(this), |
+ this.handleVideoFailed_.bind(this)); |
+ }, |
+ |
+ /** |
+ * Handles successful camera check. |
+ * @param {MediaStream} stream Stream object as returned by getUserMedia. |
+ * @private |
+ */ |
+ handleVideoAvailable_: function(stream) { |
+ $('user-image-stream').src = window.webkitURL.createObjectURL(stream); |
+ this.cameraPresent = true; |
+ }, |
+ |
+ /** |
+ * Handles camera check failure. |
+ * @param {NavigatorUserMediaError} err Error object. |
+ * @private |
+ */ |
+ handleVideoFailed_: function(err) { |
+ this.cameraPresent = false; |
+ }, |
+ |
+ /** |
+ * Handles successful camera capture start. |
+ * @private |
+ */ |
+ handleVideoStarted_: function() { |
+ $('user-image-preview').classList.add('online'); |
+ }, |
+ |
+ /** |
+ * Handles camera stream update. Called regularly (at rate no greater then |
+ * 4/sec) while camera stream is live. |
+ * @private |
+ */ |
+ handleVideoUpdate_: function() { |
+ if (!this.lastFrameTime_) { |
+ this.lastFrameTime_ = new Date().getTime(); |
+ return; |
+ } |
+ var newFrameTime = new Date().getTime(); |
+ this.lastFrameTime_ = newFrameTime; |
+ }, |
+ |
+ /** |
+ * Current image captured from camera as data URL. Setting to null will |
+ * return to the live camera stream. |
+ * @type {string=} |
+ */ |
+ get cameraImage() { |
+ return this.cameraImage_; |
+ }, |
+ set cameraImage(imageUrl) { |
+ this.cameraLive = !imageUrl; |
+ var imageGrid = $('user-image-grid'); |
+ if (this.cameraPresent && !imageUrl) { |
+ imageUrl = ButtonImages.TAKE_PHOTO; |
+ } |
+ if (imageUrl) { |
+ this.cameraImage_ = this.cameraImage_ ? |
+ imageGrid.updateItem(this.cameraImage_, imageUrl) : |
+ imageGrid.addItem(imageUrl, undefined, undefined, 0); |
+ this.cameraImage_.type = 'camera'; |
+ } else { |
+ imageGrid.removeItem(this.cameraImage_); |
+ this.cameraImage_ = null; |
+ } |
+ imageGrid.selectedItem = this.cameraImage_; |
+ imageGrid.focus(); |
+ }, |
+ |
+ /** |
+ * Updates user profile image. |
+ * @param {?string} imageUrl Image encoded as data URL. If null, user has |
+ * the default profile image, which we don't want to show. |
+ * @private |
+ */ |
+ setProfileImage_: function(imageUrl) { |
+ this.profileImageLoading = false; |
+ if (imageUrl !== null) { |
+ this.profileImage_ = |
+ $('user-image-grid').updateItem(this.profileImage_, imageUrl); |
+ } |
+ }, |
+ |
+ /** |
+ * Appends received images to the list. |
+ * @param {Array.<string>} images An array of URLs to user images. |
+ * @private |
+ */ |
+ setUserImages_: function(images) { |
+ var imageGrid = $('user-image-grid'); |
+ for (var i = 0, url; url = images[i]; i++) |
+ imageGrid.addItem(url).type = 'default'; |
+ }, |
+ |
+ /** |
+ * Selects user image with the given URL. |
+ * @param {string} url URL of the image to select. |
+ * @private |
+ */ |
+ setSelectedImage_: function(url) { |
+ var imageGrid = $('user-image-grid'); |
+ imageGrid.selectedItemUrl = url; |
+ imageGrid.focus(); |
+ }, |
+ |
+ /** |
+ * Updates the image preview caption. |
+ * @private |
+ */ |
+ updateCaption_: function() { |
+ $('user-image-preview-caption').textContent = |
+ (this.selectionType == 'profile') ? this.profileImageCaption : ''; |
+ }, |
+ |
+ /** |
+ * Updates localized content of the screen that is not updated via template. |
*/ |
updateLocalizedContent: function() { |
this.updateProfileImageCaption_(); |