Commit ccb7e26a authored by Kuo Jen Wei's avatar Kuo Jen Wei Committed by Commit Bot

Separate shutter/stop buttons for different modes.

BUG=None
TEST=Video recording and photo taking function normally with/without
timer tick. Timer tick can be canceled from stop shutter button.

Change-Id: I62feaec286513b3eca7933a85f7f070c63cf74d6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1486791Reviewed-by: default avatarSheng-hao Tsao <shenghao@google.com>
Commit-Queue: Kuo Jen Wei <inker@chromium.org>
Auto-Submit: Kuo Jen Wei <inker@chromium.org>
Cr-Commit-Position: refs/heads/master@{#638468}
parent 397a94c2
......@@ -240,42 +240,54 @@ body.tablet-landscape .actions-group button {
margin: 8px 0;
}
#shutter {
background-image: url(../images/camera_shutter_photo_start.svg);
button.shutter {
display: none;
height: 72px;
width: 72px;
z-index: 1; /* On top of transforming switch-mode buttons. */
}
#shutter:hover {
body.record-mode:not(.taking) #start-recordvideo,
body.record-mode.taking #stop-recordvideo,
body:not(.taking):not(.record-mode) #start-takephoto,
body:not(.timer):not(.record-mode) #start-takephoto,
body.taking.timer:not(.record-mode) #stop-takephoto {
display: inline-block;
}
#start-takephoto {
background-image: url(../images/camera_shutter_photo_start.svg);
}
#start-takephoto:hover {
background-image: url(../images/camera_shutter_photo_start_hover.svg);
}
#shutter:active {
#start-takephoto:active {
background-image: url(../images/camera_shutter_photo_start_active.svg);
}
body.taking.timer #shutter {
#stop-takephoto {
background-image: url(../images/camera_shutter_photo_stop.svg);
}
body.taking.timer #shutter:hover {
#stop-takephoto:hover {
background-image: url(../images/camera_shutter_photo_stop_hover.svg);
}
body.record-mode #shutter {
#start-recordvideo {
background-image: url(../images/camera_shutter_recording_start.svg);
}
body.record-mode #shutter:hover {
#start-recordvideo:hover {
background-image: url(../images/camera_shutter_recording_start_hover.svg);
}
body.record-mode.taking #shutter {
#stop-recordvideo {
background-image: url(../images/camera_shutter_recording_stop.svg);
}
body.record-mode.taking #shutter:hover {
#stop-recordvideo:hover {
background-image: url(../images/camera_shutter_recording_stop_hover.svg);
}
......
......@@ -30,7 +30,7 @@ cca.sound.play = function(selector) {
setTimeout(resolve, parseInt(element.dataset.timeout || 0), 10);
cancel = () => {
clearTimeout(timeout);
reject();
reject(new Error('cancel'));
};
element.currentTime = 0;
element.play();
......
......@@ -78,13 +78,6 @@ cca.views.Camera = function(model) {
*/
this.recordTime_ = new cca.views.camera.RecordTime();
/**
* Button for taking photos and recording videos.
* @type {HTMLButtonElement}
* @private
*/
this.shutterButton_ = document.querySelector('#shutter');
/**
* @type {string}
* @private
......@@ -119,7 +112,7 @@ cca.views.Camera = function(model) {
/**
* Promise for the current take of photo or recording.
* @type {Promise<Blob>}
* @type {?Promise}
* @private
*/
this.take_ = null;
......@@ -127,8 +120,11 @@ cca.views.Camera = function(model) {
// End of properties, seal the object.
Object.seal(this);
this.shutterButton_.addEventListener('click',
() => this.onShutterButtonClicked_());
document.querySelectorAll('#start-takephoto, #start-recordvideo')
.forEach((btn) => btn.addEventListener('click', () => this.beginTake_()));
document.querySelectorAll('#stop-takephoto, #stop-recordvideo')
.forEach((btn) => btn.addEventListener('click', () => this.endTake_()));
// Monitor the states to stop camera when locked/minimized.
chrome.idle.onStateChanged.addListener((newState) => {
......@@ -160,52 +156,85 @@ cca.views.Camera.prototype = {
* @override
*/
cca.views.Camera.prototype.focus = function() {
this.shutterButton_.focus();
// Avoid focusing invisible shutters.
document.querySelectorAll('.shutter')
.forEach((btn) => btn.offsetParent && btn.focus());
};
/**
* Handles clicking on the shutter button.
* @param {Event} event Mouse event
* Begins to take photo or recording with the current options, e.g. timer.
* @private
*/
cca.views.Camera.prototype.onShutterButtonClicked_ = function(event) {
if (!cca.state.get('streaming')) {
return;
}
if (cca.state.get('taking')) {
// End the prior ongoing take if any; a new take shouldn't be started
// until the prior one is ended.
this.endTake_();
cca.views.Camera.prototype.beginTake_ = function() {
if (!cca.state.get('streaming') || cca.state.get('taking')) {
return;
}
try {
if (this.recordMode) {
this.prepareMediaRecorder_();
} else {
this.prepareImageCapture_();
}
this.beginTake_();
} catch (e) {
console.error(e);
cca.toast.show(this.recordMode ?
'error_msg_record_start_failed' : 'error_msg_take_photo_failed');
}
};
/**
* Updates the shutter button's label for taking/record-mode state changes.
* @private
*/
cca.views.Camera.prototype.updateShutterLabel_ = function() {
var label;
if (this.recordMode) {
label = cca.state.get('taking') ?
'record_video_stop_button' : 'record_video_start_button';
} else {
label = (cca.state.get('taking') && cca.state.get('timer')) ?
'take_photo_cancel_button' : 'take_photo_button';
}
this.shutterButton_.setAttribute('aria-label', chrome.i18n.getMessage(label));
cca.state.set('taking', true);
this.focus(); // Refocus the visible shutter button for ChromeVox.
this.take_ =
cca.views.camera.timertick.start()
.then(() => {
// Play a sound before starting to record and delay the take to
// avoid the sound being recorded if necessary.
this.deferred_capture_ =
this.recordMode ? cca.sound.play('#sound-rec-start') : null;
return this.deferred_capture_ &&
this.deferred_capture_.finally(
() => this.deferred_capture_ = null);
})
.then(() => {
if (this.recordMode) {
try {
this.prepareMediaRecorder_();
} catch (e) {
cca.toast.show('error_msg_record_start_failed');
throw e;
}
// Take of recording will be ended by another shutter click.
return this.createRecordingBlob_().catch((e) => {
cca.toast.show('error_msg_empty_recording');
throw e;
});
} else {
try {
this.prepareImageCapture_();
} catch (e) {
cca.toast.show('error_msg_take_photo_failed');
throw e;
}
return this.createPhotoBlob_().catch((e) => {
cca.toast.show('error_msg_take_photo_failed');
throw e;
});
}
})
.then((blob) => {
if (blob) {
// Play a sound and save the result after a successful take.
cca.metrics.log(
cca.metrics.Type.CAPTURE, this.facingMode_, blob.mins);
var recordMode = this.recordMode;
cca.sound.play(recordMode ? '#sound-rec-end' : '#sound-shutter');
return this.model_.savePicture(blob, recordMode)
.catch((e) => {
cca.toast.show('error_msg_save_file_failed');
throw e;
});
}
})
.catch((e) => {
if (e && e.message == 'cancel') {
return;
}
console.error(e);
})
.finally(() => {
this.take_ = null;
cca.state.set('taking', false);
this.focus(); // Refocus the visible shutter button for ChromeVox.
});
};
/**
......@@ -226,38 +255,6 @@ cca.views.Camera.prototype.handlingKey = function(key) {
return false;
};
/**
* Begins to take photo or recording with the current options, e.g. timer.
* @private
*/
cca.views.Camera.prototype.beginTake_ = function() {
cca.state.set('taking', true);
this.updateShutterLabel_();
cca.views.camera.timertick.start().then(() => {
// Play a sound before starting to record and delay the take to avoid the
// sound being recorded if necessary.
this.deferred_capture_ =
this.recordMode ? cca.sound.play('#sound-rec-start') : null;
return this.deferred_capture_ &&
this.deferred_capture_.finally(() => this.deferred_capture_ = null);
}).then(() => {
if (this.recordMode) {
// Take of recording will be ended by another shutter click.
this.take_ = this.createRecordingBlob_().catch((error) => {
cca.toast.show('error_msg_empty_recording');
throw error;
});
} else {
this.take_ = this.createPhotoBlob_().catch((error) => {
cca.toast.show('error_msg_take_photo_failed');
throw error;
});
this.endTake_();
}
}).catch(() => {});
};
/**
* Ends the current take (or clears scheduled further takes if any.)
* @return {!Promise} Promise for the operation.
......@@ -272,24 +269,7 @@ cca.views.Camera.prototype.endTake_ = function() {
this.mediaRecorder_.stop();
}
return Promise.resolve(this.take_).then((blob) => {
if (blob && !blob.handled) {
// Play a sound and save the result after a successful take.
cca.metrics.log(cca.metrics.Type.CAPTURE, this.facingMode_, blob.mins);
blob.handled = true;
var recordMode = this.recordMode;
cca.sound.play(recordMode ? '#sound-rec-end' : '#sound-shutter');
return this.model_.savePicture(blob, recordMode).catch((error) => {
cca.toast.show('error_msg_save_file_failed');
throw error;
});
}
}).catch(console.error).finally(() => {
// Re-enable UI controls after finishing the take.
this.take_ = null;
cca.state.set('taking', false);
this.updateShutterLabel_();
});
return Promise.resolve(this.take_);
};
/**
......@@ -438,8 +418,6 @@ cca.views.Camera.prototype.constraintsCandidates_ = function() {
* @private
*/
cca.views.Camera.prototype.stop_ = function() {
// Update shutter label as record-mode might be toggled before reaching here.
this.updateShutterLabel_();
// Wait for ongoing 'start' and 'take' done before restarting camera.
return Promise.all([
this.started_,
......
......@@ -48,7 +48,7 @@ cca.views.camera.timertick.start = function() {
tickTimeout = null;
}
cca.util.animateCancel(tickMsg);
reject();
reject(new Error('cancel'));
};
var tickCounter = cca.state.get('_10sec') ? 10 : 3;
......
......@@ -52,8 +52,14 @@
<div class="actions-group buttons circle">
<button id="switch-takephoto" tabindex="0"
i18n-label="switch_take_photo_button"></button>
<button id="shutter" tabindex="0"
<button id="start-recordvideo" class="shutter" tabindex="0"
i18n-label="record_video_start_button"></button>
<button id="stop-recordvideo" class="shutter" tabindex="0"
i18n-label="record_video_stop_button"></button>
<button id="start-takephoto" class="shutter" tabindex="0"
i18n-label="take_photo_button"></button>
<button id="stop-takephoto" class="shutter" tabindex="0"
i18n-label="take_photo_cancel_button"></button>
<button id="switch-recordvideo" tabindex="0"
i18n-label="switch_record_video_button"></button>
</div>
......
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