Skip to content

Livesync service refactoring #1941

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 29, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions lib/bootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ $injector.require("androidProjectService", "./services/android-project-service")
$injector.require("iOSProjectService", "./services/ios-project-service");

$injector.require("cocoapodsService", "./services/cocoapods-service");
$injector.require("liveSyncServiceBase", "./services/livesync/livesync-service-base");

$injector.require("projectTemplatesService", "./services/project-templates-service");
$injector.require("projectNameService", "./services/project-name-service");
Expand Down Expand Up @@ -105,8 +104,10 @@ $injector.requireCommand("platform|clean", "./commands/platform-clean");

$injector.requireCommand("livesync", "./commands/livesync");
$injector.require("usbLiveSyncService", "./services/livesync/livesync-service"); // The name is used in https://github.com/NativeScript/nativescript-dev-typescript
$injector.require("iosLiveSyncServiceLocator", "./services/livesync/ios-livesync-service");
$injector.require("androidLiveSyncServiceLocator", "./services/livesync/android-livesync-service");
$injector.require("iosPlatformLiveSyncServiceLocator", "./services/livesync/ios-platform-livesync-service");
$injector.require("iosLiveSyncServiceLocator", "./services/livesync/ios-device-livesync-service");
$injector.require("androidPlatformLiveSyncServiceLocator", "./services/livesync/android-platform-livesync-service");
$injector.require("androidLiveSyncServiceLocator", "./services/livesync/android-device-livesync-service");

$injector.require("sysInfo", "./sys-info");

Expand Down
38 changes: 17 additions & 21 deletions lib/commands/debug.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,26 @@
private $childProcess: IChildProcess,
private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants,
private $config: IConfiguration,
private $usbLiveSyncService: ILiveSyncService,
protected $options: IOptions) { }

execute(args: string[]): IFuture<void> {
if (!this.$options.rebuild && !this.$options.start) {
this.$config.debugLivesync = true;
let usbLiveSyncService: ILiveSyncService = this.$injector.resolve("usbLiveSyncService");
let liveSyncServiceBase: any = this.$injector.resolve("liveSyncServiceBase");
let liveSyncProvider: ILiveSyncProvider = this.$injector.resolve("liveSyncProvider");
let applicationReloadAction = (deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]): IFuture<void> => {
return (() => {
let projectData: IProjectData = this.$injector.resolve("projectData");

liveSyncServiceBase.on("sync", (device: Mobile.IDevice, data: ILiveSyncData) => {
let platformLiveSyncService: IPlatformLiveSyncService = this.$injector.resolve(liveSyncProvider.platformSpecificLiveSyncServices[data.platform.toLowerCase()], { _device: device });
let projectData: IProjectData = this.$injector.resolve("projectData");
let appId = device.isEmulator ? projectData.projectName : data.appIdentifier;
if (data.platform === this.$devicePlatformsConstants.iOS) {
platformLiveSyncService.debugService.debugStop().wait();
}
device.applicationManager.stopApplication(appId).wait();
platformLiveSyncService.debugService.debug().wait();
});
this.debugService.debugStop().wait();

liveSyncServiceBase.on("syncAfterInstall", (device: Mobile.IDevice, data: ILiveSyncData) => {
let platformLiveSyncService: IPlatformLiveSyncService = this.$injector.resolve(liveSyncProvider.platformSpecificLiveSyncServices[data.platform.toLowerCase()], { _device: device });
platformLiveSyncService.debugService.debug().wait();
});
let applicationId = deviceAppData.device.isEmulator ? projectData.projectName : deviceAppData.appIdentifier;
deviceAppData.device.applicationManager.stopApplication(applicationId).wait();

return usbLiveSyncService.liveSync(this.$devicesService.platform);
this.debugService.debug().wait();
}).future<void>()();
};

return this.$usbLiveSyncService.liveSync(this.$devicesService.platform, applicationReloadAction);
}
return this.debugService.debug();
}
Expand Down Expand Up @@ -66,8 +60,9 @@ export class DebugIOSCommand extends DebugPlatformCommand {
$childProcess: IChildProcess,
$devicePlatformsConstants: Mobile.IDevicePlatformsConstants,
$config: IConfiguration,
$usbLiveSyncService: ILiveSyncService,
$options: IOptions) {
super($iOSDebugService, $devicesService, $injector, $logger, $childProcess, $devicePlatformsConstants, $config, $options);
super($iOSDebugService, $devicesService, $injector, $logger, $childProcess, $devicePlatformsConstants, $config, $usbLiveSyncService, $options);
}
}
$injector.registerCommand("debug|ios", DebugIOSCommand);
Expand All @@ -78,10 +73,11 @@ export class DebugAndroidCommand extends DebugPlatformCommand {
$injector: IInjector,
$logger: ILogger,
$childProcess: IChildProcess,
$devicePlatformsConstants: Mobile.IDevicePlatformsConstants,
$devicePlatformsConstants: Mobile.IDevicePlatformsConstants,
$config: IConfiguration,
$usbLiveSyncService: ILiveSyncService,
$options: IOptions) {
super($androidDebugService, $devicesService, $injector, $logger, $childProcess, $devicePlatformsConstants, $config, $options);
super($androidDebugService, $devicesService, $injector, $logger, $childProcess, $devicePlatformsConstants, $config, $usbLiveSyncService, $options);
}
}
$injector.registerCommand("debug|android", DebugAndroidCommand);
8 changes: 7 additions & 1 deletion lib/declarations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,16 @@ interface IOpener {
}

interface ILiveSyncService {
liveSync(platform: string): IFuture<void>;
liveSync(platform: string, applicationReloadAction?: (deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]) => IFuture<void>): IFuture<void>;
forceExecuteFullSync: boolean;
}

interface IPlatformLiveSyncService {
fullSync(postAction?: (deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]) => IFuture<void>): IFuture<void>;
partialSync(event: string, filePath: string, dispatcher: IFutureDispatcher, afterFileSyncAction: (deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]) => IFuture<void>): void;
refreshApplication(deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]): IFuture<void>;
}

interface IOptions extends ICommonOptions {
all: boolean;
baseConfig: string;
Expand Down
2 changes: 1 addition & 1 deletion lib/definitions/debug.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
interface IDebugService {
debug(shouldBreak?: boolean): IFuture<void>;
debugStart(): IFuture<void>;
debugStop?(): IFuture<void>
debugStop(): IFuture<void>
platform: string;
}
41 changes: 32 additions & 9 deletions lib/providers/livesync-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,53 @@ import * as temp from "temp";

export class LiveSyncProvider implements ILiveSyncProvider {
constructor(private $androidLiveSyncServiceLocator: {factory: Function},
private $androidPlatformLiveSyncServiceLocator: {factory: Function},
private $iosLiveSyncServiceLocator: {factory: Function},
private $iosPlatformLiveSyncServiceLocator: {factory: Function},
private $platformService: IPlatformService,
private $platformsData: IPlatformsData,
private $logger: ILogger,
private $childProcess: IChildProcess) { }
private $childProcess: IChildProcess,
private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants) { }

private static FAST_SYNC_FILE_EXTENSIONS = [".css", ".xml" ,".html"];

private deviceSpecificLiveSyncServicesCache: IDictionary<any> = {};
public get deviceSpecificLiveSyncServices(): IDictionary<any> {
return {
android: (_device: Mobile.IDevice, $injector: IInjector) => {
if(!this.deviceSpecificLiveSyncServicesCache[_device.deviceInfo.identifier]) {
this.deviceSpecificLiveSyncServicesCache[_device.deviceInfo.identifier] = $injector.resolve(this.$androidLiveSyncServiceLocator.factory, {_device: _device});
}

return this.deviceSpecificLiveSyncServicesCache[_device.deviceInfo.identifier];
},
ios: (_device: Mobile.IDevice, $injector: IInjector) => {
if(!this.deviceSpecificLiveSyncServicesCache[_device.deviceInfo.identifier]) {
this.deviceSpecificLiveSyncServicesCache[_device.deviceInfo.identifier] = $injector.resolve(this.$iosLiveSyncServiceLocator.factory, {_device: _device});
}

return this.deviceSpecificLiveSyncServicesCache[_device.deviceInfo.identifier];
}
};
}

private platformSpecificLiveSyncServicesCache: IDictionary<any> = {};
public get platformSpecificLiveSyncServices(): IDictionary<any> {
return {
android: (_device: Mobile.IDevice, $injector: IInjector): IPlatformLiveSyncService => {
if(!this.platformSpecificLiveSyncServicesCache[_device.deviceInfo.identifier]) {
this.platformSpecificLiveSyncServicesCache[_device.deviceInfo.identifier] = $injector.resolve(this.$androidLiveSyncServiceLocator.factory, {_device: _device});
android: (_liveSyncData: ILiveSyncData, $injector: IInjector) => {
if(!this.platformSpecificLiveSyncServicesCache[this.$devicePlatformsConstants.Android]) {
this.platformSpecificLiveSyncServicesCache[this.$devicePlatformsConstants.Android] = $injector.resolve(this.$androidPlatformLiveSyncServiceLocator.factory, { _liveSyncData: _liveSyncData });
}

return this.platformSpecificLiveSyncServicesCache[_device.deviceInfo.identifier];
return this.platformSpecificLiveSyncServicesCache[this.$devicePlatformsConstants.Android];
},
ios: (_device: Mobile.IDevice, $injector: IInjector) => {
if(!this.platformSpecificLiveSyncServicesCache[_device.deviceInfo.identifier]) {
this.platformSpecificLiveSyncServicesCache[_device.deviceInfo.identifier] = $injector.resolve(this.$iosLiveSyncServiceLocator.factory, {_device: _device});
ios: (_liveSyncData: ILiveSyncData, $injector: IInjector) => {
if(!this.platformSpecificLiveSyncServicesCache[this.$devicePlatformsConstants.iOS]) {
this.platformSpecificLiveSyncServicesCache[this.$devicePlatformsConstants.iOS] = $injector.resolve(this.$iosPlatformLiveSyncServiceLocator.factory, { _liveSyncData: _liveSyncData });
}

return this.platformSpecificLiveSyncServicesCache[_device.deviceInfo.identifier];
return this.platformSpecificLiveSyncServicesCache[this.$devicePlatformsConstants.iOS];
}
};
}
Expand Down
4 changes: 4 additions & 0 deletions lib/services/android-debug-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,10 @@ class AndroidDebugService implements IDebugService {
}).future<void>()();
}

public debugStop(): IFuture<void> {
return Future.fromResult();
}

private debugStartCore(): IFuture<void> {
return (() => {
let packageName = this.$projectData.projectId;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import {DeviceAndroidDebugBridge} from "../../common/mobile/android/device-android-debug-bridge";
import {AndroidDeviceHashService} from "../../common/mobile/android/android-device-hash-service";
import {PlatformLiveSyncServiceBase} from "./platform-livesync-service-base";
import {DeviceLiveSyncServiceBase} from "./device-livesync-service-base";
import Future = require("fibers/future");
import * as helpers from "../../common/helpers";
import * as path from "path";
import * as net from "net";

class AndroidLiveSyncService extends PlatformLiveSyncServiceBase<Mobile.IAndroidDevice> implements IPlatformLiveSyncService {
class AndroidLiveSyncService extends DeviceLiveSyncServiceBase<Mobile.IAndroidDevice> implements IDeviceLiveSyncService {
private static BACKEND_PORT = 18182;

constructor(_device: Mobile.IDevice,
Expand Down
59 changes: 59 additions & 0 deletions lib/services/livesync/android-platform-livesync-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import {PlatformLiveSyncServiceBase} from "./platform-livesync-service-base";

class AndroidPlatformLiveSyncService extends PlatformLiveSyncServiceBase {
constructor(_liveSyncData: ILiveSyncData,
protected $devicesService: Mobile.IDevicesService,
protected $mobileHelper: Mobile.IMobileHelper,
protected $logger: ILogger,
protected $options: ICommonOptions,
protected $deviceAppDataFactory: Mobile.IDeviceAppDataFactory,
protected $fs: IFileSystem,
protected $injector: IInjector,
protected $projectFilesManager: IProjectFilesManager,
protected $projectFilesProvider: IProjectFilesProvider,
protected $liveSyncProvider: ILiveSyncProvider) {
super(_liveSyncData, $devicesService, $mobileHelper, $logger, $options, $deviceAppDataFactory, $fs, $injector, $projectFilesManager, $projectFilesProvider, $liveSyncProvider);
}

public fullSync(postAction?: (deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]) => IFuture<void>): IFuture<void> {
return (() => {
let appIdentifier = this.liveSyncData.appIdentifier;
let platform = this.liveSyncData.platform;
let projectFilesPath = this.liveSyncData.projectFilesPath;
let canExecute = this.getCanExecuteAction(platform, appIdentifier);
let action = (device: Mobile.IDevice): IFuture<void> => {
return (() => {
let deviceLiveSyncService = this.resolveDeviceSpecificLiveSyncService(platform, device);
let deviceAppData = this.$deviceAppDataFactory.create(appIdentifier, this.$mobileHelper.normalizePlatformName(platform), device);

deviceLiveSyncService.beforeLiveSyncAction(deviceAppData).wait();;

let installed = this.tryInstallApplication(device, deviceAppData).wait();
let localToDevicePaths = this.$projectFilesManager.createLocalToDevicePaths(deviceAppData, projectFilesPath, null, this.liveSyncData.excludedProjectDirsAndFiles);
let afterSyncAction: () => IFuture<void>;

if (installed) {
deviceLiveSyncService.afterInstallApplicationAction(deviceAppData, localToDevicePaths).wait();
afterSyncAction = () => device.applicationManager.tryStartApplication(deviceAppData.appIdentifier);
} else {
this.transferFiles(deviceAppData, localToDevicePaths, this.liveSyncData.projectFilesPath, true).wait();
afterSyncAction = () => this.refreshApplication(deviceAppData, localToDevicePaths);
}

if (postAction) {
return postAction(deviceAppData, localToDevicePaths);
}

return afterSyncAction();
}).future<void>()();
};
this.$devicesService.execute(action, canExecute).wait();
}).future<void>()();
}

protected getCanExecuteActionCore(platform: string, appIdentifier: string): (dev: Mobile.IDevice) => boolean {
return (device: Mobile.IDevice) => true;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if the device's platform is not Android, this will return true as well and the Android action will be executed. Is this expected?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, ignore me, I've noticed it's in PlatformLiveSyncServiceBase

}
}

$injector.register("androidPlatformLiveSyncServiceLocator", {factory: AndroidPlatformLiveSyncService});
21 changes: 21 additions & 0 deletions lib/services/livesync/device-livesync-service-base.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
export abstract class DeviceLiveSyncServiceBase<T extends Mobile.IDevice> {
protected get device(): T {
return <T>(this._device);
}

constructor(private _device: Mobile.IDevice,
private $liveSyncProvider: ILiveSyncProvider) { }

public refreshApplication(deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[], forceExecuteFullSync: boolean): IFuture<void> {
let canExecuteFastSync = !forceExecuteFullSync && !_.some(localToDevicePaths, (localToDevicePath:any) => !this.$liveSyncProvider.canExecuteFastSync(localToDevicePath.getLocalPath(), deviceAppData.platform));

if (canExecuteFastSync) {
return this.reloadPage(deviceAppData);
}

return this.restartApplication(deviceAppData);
}

protected abstract restartApplication(deviceAppData: Mobile.IDeviceAppData): IFuture<void>;
protected abstract reloadPage(deviceAppData: Mobile.IDeviceAppData): IFuture<void>;
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import {PlatformLiveSyncServiceBase} from "./platform-livesync-service-base";
import {DeviceLiveSyncServiceBase} from "./device-livesync-service-base";
import * as helpers from "../../common/helpers";
import * as net from "net";

let currentPageReloadId = 0;

class IOSLiveSyncService extends PlatformLiveSyncServiceBase<Mobile.IiOSDevice> implements IPlatformLiveSyncService {
class IOSLiveSyncService extends DeviceLiveSyncServiceBase<Mobile.IiOSDevice> implements IDeviceLiveSyncService {
private static BACKEND_PORT = 18181;
private socket: net.Socket;

Expand Down
71 changes: 71 additions & 0 deletions lib/services/livesync/ios-platform-livesync-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import {PlatformLiveSyncServiceBase} from "./platform-livesync-service-base";

class IOSPlatformLiveSyncService extends PlatformLiveSyncServiceBase {
constructor(_liveSyncData: ILiveSyncData,
protected $devicesService: Mobile.IDevicesService,
protected $mobileHelper: Mobile.IMobileHelper,
protected $logger: ILogger,
protected $options: ICommonOptions,
protected $deviceAppDataFactory: Mobile.IDeviceAppDataFactory,
protected $fs: IFileSystem,
protected $injector: IInjector,
protected $projectFilesManager: IProjectFilesManager,
protected $projectFilesProvider: IProjectFilesProvider,
protected $liveSyncProvider: ILiveSyncProvider) {
super(_liveSyncData, $devicesService, $mobileHelper, $logger, $options, $deviceAppDataFactory, $fs, $injector, $projectFilesManager, $projectFilesProvider, $liveSyncProvider);
}

public fullSync(postAction?: (deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]) => IFuture<void>): IFuture<void> {
return (() => {
let appIdentifier = this.liveSyncData.appIdentifier;
let platform = this.liveSyncData.platform;
let projectFilesPath = this.liveSyncData.projectFilesPath;
let canExecute = this.getCanExecuteAction(platform, appIdentifier);

let action = (device: Mobile.IDevice): IFuture<void> => {
return (() => {
let deviceAppData = this.$deviceAppDataFactory.create(appIdentifier, this.$mobileHelper.normalizePlatformName(platform), device);
let installed = this.tryInstallApplication(device, deviceAppData).wait();
let localToDevicePaths = this.$projectFilesManager.createLocalToDevicePaths(deviceAppData, projectFilesPath, null, this.liveSyncData.excludedProjectDirsAndFiles);
let afterSyncAction: () => IFuture<void>;

if(installed) {
afterSyncAction = () => device.applicationManager.tryStartApplication(deviceAppData.appIdentifier);
} else {
this.transferFiles(deviceAppData, localToDevicePaths, this.liveSyncData.projectFilesPath, true).wait();
afterSyncAction = () => this.refreshApplication(deviceAppData, localToDevicePaths);
}

if (postAction) {
return postAction(deviceAppData, localToDevicePaths);
}

return afterSyncAction();
}).future<void>()();
};
this.$devicesService.execute(action, canExecute).wait();
}).future<void>()();
}

protected getCanExecuteActionCore(platform: string, appIdentifier: string): (dev: Mobile.IDevice) => boolean {
if (this.$options.emulator) {
return (device: Mobile.IDevice): boolean => this.$devicesService.isiOSSimulator(device);
} else {
let devices = this.$devicesService.getDevicesForPlatform(platform);
let simulator = _.find(devices, d => this.$devicesService.isiOSSimulator(d));
if (simulator) {
let iOSDevices = _.filter(devices, d => d.deviceInfo.identifier !== simulator.deviceInfo.identifier);
if (iOSDevices && iOSDevices.length) {
let isApplicationInstalledOnSimulator = simulator.applicationManager.isApplicationInstalled(appIdentifier).wait();
let isApplicationInstalledOnAllDevices = _.intersection.apply(null, iOSDevices.map(device => device.applicationManager.isApplicationInstalled(appIdentifier).wait()));
// In case the application is not installed on both device and simulator, syncs only on device.
if (!isApplicationInstalledOnSimulator && !isApplicationInstalledOnAllDevices) {
return (device: Mobile.IDevice): boolean => this.$devicesService.isiOSDevice(device);
}
}
}
}
}
}

$injector.register("iosPlatformLiveSyncServiceLocator", {factory: IOSPlatformLiveSyncService});
Loading