-
-
Notifications
You must be signed in to change notification settings - Fork 197
/
Copy pathandroid-device-livesync-service.ts
144 lines (122 loc) · 6.2 KB
/
android-device-livesync-service.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
import { DeviceAndroidDebugBridge } from "../../common/mobile/android/device-android-debug-bridge";
import { AndroidDeviceHashService } from "../../common/mobile/android/android-device-hash-service";
import { DeviceLiveSyncServiceBase } from "./device-livesync-service-base";
import * as helpers from "../../common/helpers";
import { LiveSyncPaths } from "../../constants";
import { cache } from "../../common/decorators";
import * as path from "path";
import * as net from "net";
export class AndroidDeviceLiveSyncService extends DeviceLiveSyncServiceBase implements IAndroidNativeScriptDeviceLiveSyncService, INativeScriptDeviceLiveSyncService {
private static BACKEND_PORT = 18182;
private device: Mobile.IAndroidDevice;
constructor(_device: Mobile.IDevice,
private $mobileHelper: Mobile.IMobileHelper,
private $devicePathProvider: IDevicePathProvider,
private $injector: IInjector,
protected $platformsData: IPlatformsData) {
super($platformsData);
this.device = <Mobile.IAndroidDevice>(_device);
}
public async refreshApplication(projectData: IProjectData, liveSyncInfo: ILiveSyncResultInfo): Promise<void> {
const deviceAppData = liveSyncInfo.deviceAppData;
const localToDevicePaths = liveSyncInfo.modifiedFilesData;
const deviceProjectRootDirname = await this.$devicePathProvider.getDeviceProjectRootPath(liveSyncInfo.deviceAppData.device, {
appIdentifier: liveSyncInfo.deviceAppData.appIdentifier,
getDirname: true
});
await this.device.adb.executeShellCommand(
["chmod",
"777",
path.dirname(deviceProjectRootDirname),
deviceProjectRootDirname,
`${deviceProjectRootDirname}/sync`]
);
await this.reloadResources(deviceAppData, localToDevicePaths);
const canExecuteFastSync = !liveSyncInfo.isFullSync && !_.some(localToDevicePaths,
(localToDevicePath: Mobile.ILocalToDevicePathData) => !this.canExecuteFastSync(localToDevicePath.getLocalPath(), projectData, this.device.deviceInfo.platform));
if (!canExecuteFastSync) {
return this.restartApplication(deviceAppData);
}
}
private async cleanLivesyncDirectories(deviceAppData: Mobile.IDeviceAppData): Promise<void> {
const deviceRootPath = await this.$devicePathProvider.getDeviceProjectRootPath(deviceAppData.device, {
appIdentifier: deviceAppData.appIdentifier,
getDirname: true
});
await this.device.adb.executeShellCommand(["rm", "-rf", await this.$mobileHelper.buildDevicePath(deviceRootPath, LiveSyncPaths.FULLSYNC_DIR_NAME),
this.$mobileHelper.buildDevicePath(deviceRootPath, LiveSyncPaths.SYNC_DIR_NAME),
await this.$mobileHelper.buildDevicePath(deviceRootPath, LiveSyncPaths.REMOVEDSYNC_DIR_NAME)]);
}
private async restartApplication(deviceAppData: Mobile.IDeviceAppData): Promise<void> {
let devicePathRoot = `/data/data/${deviceAppData.appIdentifier}/files`;
let devicePath = this.$mobileHelper.buildDevicePath(devicePathRoot, "code_cache", "secondary_dexes", "proxyThumb");
await this.device.adb.executeShellCommand(["rm", "-rf", devicePath]);
await this.device.applicationManager.restartApplication(deviceAppData.appIdentifier);
}
public async beforeLiveSyncAction(deviceAppData: Mobile.IDeviceAppData): Promise<void> {
const deviceRootPath = await this.$devicePathProvider.getDeviceProjectRootPath(deviceAppData.device, {
appIdentifier: deviceAppData.appIdentifier,
getDirname: true
});
const deviceRootDir = path.dirname(deviceRootPath);
const deviceRootBasename = path.basename(deviceRootPath);
const listResult = await this.device.adb.executeShellCommand(["ls", "-l", deviceRootDir]);
const regex = new RegExp(`^-.*${deviceRootBasename}$`, "m");
const matchingFile = (listResult || "").match(regex);
// Check if there is already a file with deviceRootBasename. If so, delete it as it breaks LiveSyncing.
if (matchingFile && matchingFile[0] && _.startsWith(matchingFile[0], '-')) {
await this.device.adb.executeShellCommand(["rm", "-f", deviceRootPath]);
}
await this.cleanLivesyncDirectories(deviceAppData);
}
private async reloadResources(deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]): Promise<void> {
await this.device.adb.executeCommand(["forward", `tcp:${AndroidDeviceLiveSyncService.BACKEND_PORT.toString()}`, `localabstract:${deviceAppData.appIdentifier}-livesync`]);
if (await this.sendPageReloadMessage()) {
await this.cleanLivesyncDirectories(deviceAppData);
} else {
await this.restartApplication(deviceAppData); //in case runtime socket error/close
}
}
public async removeFiles(deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]): Promise<void> {
const deviceRootPath = await this.$devicePathProvider.getDeviceProjectRootPath(deviceAppData.device, {
appIdentifier: deviceAppData.appIdentifier,
getDirname: true
});
for (const localToDevicePathData of localToDevicePaths) {
const relativeUnixPath = _.trimStart(helpers.fromWindowsRelativePathToUnix(localToDevicePathData.getRelativeToProjectBasePath()), "/");
const deviceFilePath = this.$mobileHelper.buildDevicePath(deviceRootPath, LiveSyncPaths.REMOVEDSYNC_DIR_NAME, relativeUnixPath);
await this.device.adb.executeShellCommand(["mkdir", "-p", path.dirname(deviceFilePath), " && ", "touch", deviceFilePath]);
}
await this.getDeviceHashService(deviceAppData.appIdentifier).removeHashes(localToDevicePaths);
}
@cache()
public getDeviceHashService(appIdentifier: string): Mobile.IAndroidDeviceHashService {
let adb = this.$injector.resolve(DeviceAndroidDebugBridge, { identifier: this.device.deviceInfo.identifier });
return this.$injector.resolve(AndroidDeviceHashService, { adb, appIdentifier });
}
private async sendPageReloadMessage(): Promise<boolean> {
return new Promise<boolean>((resolve, reject) => {
let isResolved = false;
let socket = new net.Socket();
socket.connect(AndroidDeviceLiveSyncService.BACKEND_PORT, '127.0.0.1', () => {
socket.write(new Buffer([0, 0, 0, 1, 1]));
});
socket.on("data", (data: any) => {
socket.destroy();
resolve(true);
});
socket.on("error", () => {
if (!isResolved) {
isResolved = true;
resolve(false);
}
});
socket.on("close", () => {
if (!isResolved) {
isResolved = true;
resolve(false);
}
});
});
}
}