From 06e70a155535ace8085c403cb30856a8317c6486 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20B=C3=BCchele?= Date: Wed, 1 Aug 2018 11:11:48 -0700 Subject: [PATCH] Screen capture unique file names and location Summary: Screen captures now have unique names, so they don't get overwritten. By default they are saved to the desktop, but the path can be overwritten by setting `screenCapturePath` in `~/.sonar/config.js` Reviewed By: jknoxville Differential Revision: D9120822 fbshipit-source-id: ab6880eac475da3839f08c6e644c16bdc8693647 --- src/chrome/ScreenCaptureButtons.js | 77 +++++++++++++++++------------- src/ui/components/Button.js | 2 +- 2 files changed, 46 insertions(+), 33 deletions(-) diff --git a/src/chrome/ScreenCaptureButtons.js b/src/chrome/ScreenCaptureButtons.js index 4f23f60c1..ce9cf7eed 100644 --- a/src/chrome/ScreenCaptureButtons.js +++ b/src/chrome/ScreenCaptureButtons.js @@ -12,17 +12,18 @@ import IOSDevice from '../devices/IOSDevice'; import os from 'os'; import fs from 'fs'; import adb from 'adbkit-fb'; -import path from 'path'; import {exec} from 'child_process'; +import {remote} from 'electron'; +import path from 'path'; -const SCREENSHOT_FILE_NAME = 'screen.png'; -const VIDEO_FILE_NAME = 'video.mp4'; -const SCREENSHOT_PATH = path.join( - os.homedir(), - '/.sonar/', - SCREENSHOT_FILE_NAME, -); -const VIDEO_PATH = path.join(os.homedir(), '.sonar', VIDEO_FILE_NAME); +let CAPTURE_LOCATION = remote.app.getPath('desktop'); +try { + CAPTURE_LOCATION = + JSON.parse(window.process.env.CONFIG).screenCapturePath.replace( + /^~/, + os.homedir(), + ) || CAPTURE_LOCATION; +} catch (e) {} import type BaseDevice from '../devices/BaseDevice'; @@ -39,9 +40,9 @@ type State = {| capturingScreenshot: boolean, |}; -function openFile(path: string): Promise<*> { +function openFile(path: string): Promise { return new Promise((resolve, reject) => { - exec(`${getOpenCommand()} ${path}`, (error, stdout, stderr) => { + exec(`${getOpenCommand()} "${path}"`, (error, stdout, stderr) => { if (error) { reject(error); } else { @@ -63,18 +64,24 @@ function getOpenCommand(): string { } } +function getFileName(extension: 'png' | 'mp4'): string { + return `Screen Capture ${new Date().toISOString()}.${extension}`; +} + function writePngStreamToFile(stream: PullTransfer): Promise { return new Promise((resolve, reject) => { + const pngPath = path.join(CAPTURE_LOCATION, getFileName('png')); stream.on('end', () => { - resolve(SCREENSHOT_PATH); + resolve(pngPath); }); stream.on('error', reject); - stream.pipe(fs.createWriteStream(SCREENSHOT_PATH)); + stream.pipe(fs.createWriteStream(pngPath)); }); } class ScreenCaptureButtons extends Component { iOSRecorder: ?any; + videoPath: ?string; state = { pullingData: false, @@ -117,7 +124,7 @@ class ScreenCaptureButtons extends Component { } }; - captureScreenshot = () => { + captureScreenshot = (): ?Promise => { const {selectedDevice} = this.props; if (selectedDevice instanceof AndroidDevice) { @@ -127,29 +134,33 @@ class ScreenCaptureButtons extends Component { .then(openFile) .catch(console.error); } else if (selectedDevice instanceof IOSDevice) { - exec( - `xcrun simctl io booted screenshot ${SCREENSHOT_PATH}`, - (err, data) => { - if (err) { - console.error(err); - } else { - openFile(SCREENSHOT_PATH); - } - }, - ); + const screenshotPath = path.join(CAPTURE_LOCATION, getFileName('png')); + return new Promise((resolve, reject) => { + exec( + `xcrun simctl io booted screenshot "${screenshotPath}"`, + async (err, data) => { + if (err) { + reject(err); + } else { + resolve(await openFile(screenshotPath)); + } + }, + ); + }); } }; startRecording = () => { const {selectedDevice} = this.props; - + const videoPath = path.join(CAPTURE_LOCATION, getFileName('mp4')); + this.videoPath = videoPath; if (selectedDevice instanceof AndroidDevice) { this.setState({ recording: true, }); this.executeShell( selectedDevice, - `screenrecord --bugreport /sdcard/${VIDEO_FILE_NAME}`, + `screenrecord --bugreport /sdcard/video.mp4`, ) .then(output => { if (output) { @@ -166,14 +177,14 @@ class ScreenCaptureButtons extends Component { (): Promise => { return this.pullFromDevice( selectedDevice, - `/sdcard/${VIDEO_FILE_NAME}`, - VIDEO_PATH, + `/sdcard/video.mp4`, + videoPath, ); }, ) .then(openFile) .then(() => { - this.executeShell(selectedDevice, `rm /sdcard/${VIDEO_FILE_NAME}`); + this.executeShell(selectedDevice, `rm /sdcard/video.mp4`); }) .then(() => { this.setState({ @@ -192,7 +203,7 @@ class ScreenCaptureButtons extends Component { recording: true, }); this.iOSRecorder = exec( - `xcrun simctl io booted recordVideo ${VIDEO_PATH}`, + `xcrun simctl io booted recordVideo "${videoPath}"`, ); } }; @@ -214,16 +225,18 @@ class ScreenCaptureButtons extends Component { }; stopRecording = () => { + const {videoPath} = this; const {selectedDevice} = this.props; + this.videoPath = null; if (selectedDevice instanceof AndroidDevice) { this.executeShell(selectedDevice, `pgrep 'screenrecord' -L 2`); - } else if (this.iOSRecorder) { + } else if (this.iOSRecorder && videoPath) { this.iOSRecorder.kill(); this.setState({ recording: false, }); - openFile(VIDEO_PATH); + openFile(videoPath); } }; diff --git a/src/ui/components/Button.js b/src/ui/components/Button.js index e64740728..dc6515907 100644 --- a/src/ui/components/Button.js +++ b/src/ui/components/Button.js @@ -163,7 +163,7 @@ type Props = { /** * onClick handler. */ - onClick?: (event: SyntheticMouseEvent<>) => void, + onClick?: (event: SyntheticMouseEvent<>) => any, /** * Whether this button is disabled. */