Initial commit 🎉

fbshipit-source-id: b6fc29740c6875d2e78953b8a7123890a67930f2
Co-authored-by: Sebastian McKenzie <sebmck@fb.com>
Co-authored-by: John Knox <jknox@fb.com>
Co-authored-by: Emil Sjölander <emilsj@fb.com>
Co-authored-by: Pritesh Nandgaonkar <prit91@fb.com>
This commit is contained in:
Daniel Büchele
2018-04-13 08:38:06 -07:00
committed by Daniel Buchele
commit fbbf8cf16b
659 changed files with 87130 additions and 0 deletions

View File

@@ -0,0 +1,97 @@
/**
* Copyright 2018-present Facebook.
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
* @format
*/
import AndroidDevice from '../devices/AndroidDevice';
import type {Store} from '../reducers/index.js';
import type BaseDevice from '../devices/BaseDevice';
const adb = require('adbkit-fb');
function createDecive(client, device): Promise<AndroidDevice> {
return new Promise((resolve, reject) => {
const type =
device.type !== 'device' || device.id.startsWith('emulator')
? 'emulator'
: 'physical';
client.getProperties(device.id).then(props => {
const androidDevice = new AndroidDevice(
device.id,
type,
props['ro.product.model'],
client,
);
androidDevice.reverse();
resolve(androidDevice);
});
});
}
export default (store: Store) => {
const client = adb.createClient();
client
.trackDevices()
.then(tracker => {
tracker.on('error', err => {
if (err.message === 'Connection closed') {
// adb server has shutdown, remove all android devices
const {devices} = store.getState();
const deviceIDsToRemove: Array<string> = devices
.filter((device: BaseDevice) => device instanceof AndroidDevice)
.map((device: BaseDevice) => device.serial);
store.dispatch({
type: 'UNREGISTER_DEVICES',
payload: new Set(deviceIDsToRemove),
});
console.error(
'adb server shutdown. Run `adb start-server` and restart Sonar.',
);
} else {
throw err;
}
});
tracker.on('add', async device => {
const androidDevice = await createDecive(client, device);
if (device.type !== 'offline') {
store.dispatch({
type: 'REGISTER_DEVICE',
payload: androidDevice,
});
}
});
tracker.on('change', async device => {
if (device.type === 'offline') {
store.dispatch({
type: 'UNREGISTER_DEVICES',
payload: new Set([device.id]),
});
} else {
const androidDevice = await createDecive(client, device);
store.dispatch({
type: 'REGISTER_DEVICE',
payload: androidDevice,
});
}
});
tracker.on('remove', device => {
store.dispatch({
type: 'UNREGISTER_DEVICES',
payload: new Set([device.id]),
});
});
})
.catch(err => {
if (err.code === 'ECONNREFUSED') {
// adb server isn't running
} else {
throw err;
}
});
};

View File

@@ -0,0 +1,25 @@
/**
* Copyright 2018-present Facebook.
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
* @format
*/
import {remote} from 'electron';
import type {Store} from '../reducers/index.js';
export default (store: Store) => {
const currentWindow = remote.getCurrentWindow();
currentWindow.on('focus', () =>
store.dispatch({
type: 'windowIsFocused',
payload: true,
}),
);
currentWindow.on('blur', () =>
store.dispatch({
type: 'windowIsFocused',
payload: false,
}),
);
};

View File

@@ -0,0 +1,99 @@
/**
* Copyright 2018-present Facebook.
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
* @format
*/
import type {ChildProcess} from 'child_process';
import type {Store} from '../reducers/index.js';
import child_process from 'child_process';
import IOSDevice from '../devices/IOSDevice';
type iOSSimulatorDevice = {|
state: 'Booted' | 'Shutdown' | 'Shutting Down',
availability: string,
name: string,
udid: string,
|};
type IOSDeviceMap = {[id: string]: Array<iOSSimulatorDevice>};
// start port forwarding server for real device connections
const portForwarder: ChildProcess = child_process.exec(
'PortForwardingMacApp.app/Contents/MacOS/PortForwardingMacApp -portForward=8088 -multiplexChannelPort=8078',
);
window.addEventListener('beforeunload', () => {
portForwarder.kill();
});
function querySimulatorDevices(): Promise<IOSDeviceMap> {
return new Promise((resolve, reject) => {
child_process.execFile(
'xcrun',
['simctl', 'list', 'devices', '--json'],
{encoding: 'utf8'},
(err, stdout) => {
if (err) {
console.error('Failed to load iOS devices', err);
return resolve({});
}
try {
const {devices} = JSON.parse(stdout.toString());
resolve(devices);
} catch (err) {
console.error('Failed to parse iOS device list', err);
resolve({});
}
},
);
});
}
export default (store: Store) => {
// monitoring iOS devices only available on MacOS.
if (process.platform !== 'darwin') {
return;
}
setInterval(() => {
const {devices} = store.getState();
querySimulatorDevices().then((simulatorDevices: IOSDeviceMap) => {
const simulators: Array<iOSSimulatorDevice> = Object.values(
simulatorDevices,
// $FlowFixMe
).reduce((acc, cv) => acc.concat(cv), []);
const currentDeviceIDs: Set<string> = new Set(
devices
.filter(device => device instanceof IOSDevice)
.map(device => device.serial),
);
const deviceIDsToRemove = new Set();
simulators.forEach((simulator: iOSSimulatorDevice) => {
const isRunning =
simulator.state === 'Booted' &&
simulator.availability === '(available)';
if (isRunning && !currentDeviceIDs.has(simulator.udid)) {
// create device
store.dispatch({
type: 'REGISTER_DEVICE',
payload: new IOSDevice(simulator.udid, 'emulator', simulator.name),
});
} else if (!isRunning && currentDeviceIDs.has(simulator.udid)) {
deviceIDsToRemove.add(simulator.udid);
// delete device
}
});
if (deviceIDsToRemove.size > 0) {
store.dispatch({
type: 'UNREGISTER_DEVICES',
payload: deviceIDsToRemove,
});
}
});
}, 3000);
};

14
src/dispatcher/index.js Normal file
View File

@@ -0,0 +1,14 @@
/**
* Copyright 2018-present Facebook.
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
* @format
*/
import androidDevice from './androidDevice';
import iOSDevice from './iOSDevice';
import application from './application';
import type {Store} from '../reducers/index.js';
export default (store: Store) =>
[application, androidDevice, iOSDevice].forEach(fn => fn(store));