react-native-stallion 2.1.0-alpha.2 → 2.1.0-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/src/main/java/com/stallion/events/StallionEventConstants.java +2 -0
- package/android/src/main/java/com/stallion/networkmanager/StallionDownloadCacheManager.java +48 -0
- package/android/src/main/java/com/stallion/networkmanager/StallionFileDownloader.java +33 -24
- package/android/src/main/java/com/stallion/networkmanager/StallionStageManager.java +12 -4
- package/android/src/main/java/com/stallion/networkmanager/StallionSyncHandler.java +12 -6
- package/android/src/main/java/com/stallion/storage/StallionConfig.java +13 -0
- package/android/src/main/java/com/stallion/storage/StallionConfigConstants.java +1 -0
- package/package.json +1 -1
|
@@ -3,6 +3,7 @@ package com.stallion.events;
|
|
|
3
3
|
public class StallionEventConstants {
|
|
4
4
|
public enum NativeProdEventTypes {
|
|
5
5
|
DOWNLOAD_STARTED_PROD,
|
|
6
|
+
DOWNLOAD_RESUME_PROD,
|
|
6
7
|
DOWNLOAD_ERROR_PROD,
|
|
7
8
|
DOWNLOAD_COMPLETE_PROD,
|
|
8
9
|
SYNC_ERROR_PROD,
|
|
@@ -20,6 +21,7 @@ public class StallionEventConstants {
|
|
|
20
21
|
DOWNLOAD_COMPLETE_STAGE,
|
|
21
22
|
EXCEPTION_STAGE,
|
|
22
23
|
DOWNLOAD_STARTED_STAGE,
|
|
24
|
+
DOWNLOAD_RESUME_STAGE,
|
|
23
25
|
DOWNLOAD_ERROR_STAGE,
|
|
24
26
|
INSTALLED_STAGE,
|
|
25
27
|
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
package com.stallion.networkmanager;
|
|
2
|
+
|
|
3
|
+
import com.stallion.storage.StallionConfig;
|
|
4
|
+
import com.stallion.utils.StallionFileManager;
|
|
5
|
+
|
|
6
|
+
import java.io.File;
|
|
7
|
+
import java.io.FileInputStream;
|
|
8
|
+
import java.io.FileOutputStream;
|
|
9
|
+
import java.util.Objects;
|
|
10
|
+
|
|
11
|
+
public class StallionDownloadCacheManager {
|
|
12
|
+
private static final String metaFilePath = "/download-cache.meta";
|
|
13
|
+
|
|
14
|
+
public static long getDownloadCache(StallionConfig config, String downloadUrl, String downloadPath) {
|
|
15
|
+
String lastDownloadingUrl = config.getLastDownloadingUrl();
|
|
16
|
+
long alreadyDownloaded = readMetaFile(downloadPath);
|
|
17
|
+
if(!Objects.equals(lastDownloadingUrl, downloadUrl) || alreadyDownloaded <= 0) {
|
|
18
|
+
config.setLastDownloadingUrl(downloadUrl);
|
|
19
|
+
StallionFileManager.deleteFileOrFolderSilently(new File(downloadPath));
|
|
20
|
+
return 0;
|
|
21
|
+
} else {
|
|
22
|
+
return alreadyDownloaded;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
public static void saveDownloadCache(String path, long bytes) {
|
|
27
|
+
try (FileOutputStream fos = new FileOutputStream(path + metaFilePath)) {
|
|
28
|
+
fos.write(Long.toString(bytes).getBytes());
|
|
29
|
+
} catch (Exception ignored) {}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
private static long readMetaFile(String path) {
|
|
33
|
+
File meta = new File(path + metaFilePath);
|
|
34
|
+
if (!meta.exists()) return 0;
|
|
35
|
+
try (FileInputStream fis = new FileInputStream(meta)) {
|
|
36
|
+
byte[] data = new byte[(int) meta.length()];
|
|
37
|
+
fis.read(data);
|
|
38
|
+
return Long.parseLong(new String(data));
|
|
39
|
+
} catch (Exception e) {
|
|
40
|
+
return 0;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
public static void deleteDownloadCache(String path) {
|
|
45
|
+
File meta = new File(path + metaFilePath);
|
|
46
|
+
if (meta.exists()) meta.delete();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -8,6 +8,7 @@ import java.io.File;
|
|
|
8
8
|
import java.io.FileInputStream;
|
|
9
9
|
import java.io.FileOutputStream;
|
|
10
10
|
import java.io.IOException;
|
|
11
|
+
import java.io.RandomAccessFile;
|
|
11
12
|
import java.net.HttpURLConnection;
|
|
12
13
|
import java.net.URL;
|
|
13
14
|
import java.nio.ByteBuffer;
|
|
@@ -28,6 +29,7 @@ public class StallionFileDownloader {
|
|
|
28
29
|
public static void downloadBundle(
|
|
29
30
|
String downloadUrl,
|
|
30
31
|
String downloadDirectory,
|
|
32
|
+
long alreadyDownloaded,
|
|
31
33
|
StallionDownloadCallback stallionDownloadCallback
|
|
32
34
|
) {
|
|
33
35
|
executor.execute(() -> {
|
|
@@ -61,7 +63,7 @@ public class StallionFileDownloader {
|
|
|
61
63
|
}
|
|
62
64
|
|
|
63
65
|
// Download file
|
|
64
|
-
downloadFile(downloadUrl, downloadedZip, appToken, sdkToken, stallionDownloadCallback);
|
|
66
|
+
downloadFile(downloadUrl, downloadedZip, appToken, sdkToken, stallionDownloadCallback, alreadyDownloaded, downloadDirectory);
|
|
65
67
|
|
|
66
68
|
// Validate and unzip the downloaded file
|
|
67
69
|
validateAndUnzip(downloadedZip, downloadDirectory, stallionDownloadCallback);
|
|
@@ -75,7 +77,7 @@ public class StallionFileDownloader {
|
|
|
75
77
|
private static long getFileSize(String downloadUrl, String appToken, String apiKey) throws IOException {
|
|
76
78
|
HttpURLConnection connection = null;
|
|
77
79
|
try {
|
|
78
|
-
connection = setupConnection(downloadUrl, appToken, apiKey);
|
|
80
|
+
connection = setupConnection(downloadUrl, appToken, apiKey, 0);
|
|
79
81
|
return connection.getContentLength();
|
|
80
82
|
} finally {
|
|
81
83
|
if (connection != null) {
|
|
@@ -96,11 +98,10 @@ public class StallionFileDownloader {
|
|
|
96
98
|
|
|
97
99
|
private static File prepareForDownload(String downloadDirectory) throws IOException {
|
|
98
100
|
File downloadFolder = new File(downloadDirectory);
|
|
99
|
-
if (downloadFolder.exists()) {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
throw new IOException("Failed to create download directory: " + downloadDirectory);
|
|
101
|
+
if (!downloadFolder.exists()) {
|
|
102
|
+
if (!downloadFolder.mkdirs()) {
|
|
103
|
+
throw new IOException("Failed to create download directory: " + downloadDirectory);
|
|
104
|
+
}
|
|
104
105
|
}
|
|
105
106
|
return new File(downloadFolder, StallionApiConstants.ZIP_FILE_NAME);
|
|
106
107
|
}
|
|
@@ -110,21 +111,21 @@ public class StallionFileDownloader {
|
|
|
110
111
|
File destinationFile,
|
|
111
112
|
String appToken,
|
|
112
113
|
String sdkToken,
|
|
113
|
-
StallionDownloadCallback callback
|
|
114
|
+
StallionDownloadCallback callback,
|
|
115
|
+
long alreadyDownloaded,
|
|
116
|
+
String downloadDirectory
|
|
114
117
|
) throws IOException {
|
|
115
|
-
HttpURLConnection connection =
|
|
118
|
+
HttpURLConnection connection = setupConnection(downloadUrl, appToken, sdkToken, alreadyDownloaded);
|
|
116
119
|
try (
|
|
117
|
-
BufferedInputStream inputStream = new BufferedInputStream(
|
|
118
|
-
|
|
119
|
-
BufferedOutputStream bout = new BufferedOutputStream(fout, StallionApiConstants.DOWNLOAD_BUFFER_SIZE)
|
|
120
|
+
BufferedInputStream inputStream = new BufferedInputStream(connection.getInputStream());
|
|
121
|
+
RandomAccessFile raf = new RandomAccessFile(destinationFile, "rw")
|
|
120
122
|
) {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
+
raf.seek(alreadyDownloaded);
|
|
123
124
|
byte[] buffer = new byte[StallionApiConstants.DOWNLOAD_BUFFER_SIZE];
|
|
124
|
-
long totalBytes = connection.getContentLength();
|
|
125
|
-
long receivedBytes =
|
|
125
|
+
long totalBytes = connection.getContentLength() + alreadyDownloaded;
|
|
126
|
+
long receivedBytes = alreadyDownloaded;
|
|
126
127
|
int bytesRead;
|
|
127
|
-
double lastProgress =
|
|
128
|
+
double lastProgress = (double) receivedBytes / totalBytes;
|
|
128
129
|
|
|
129
130
|
// Ensure totalBytes is valid
|
|
130
131
|
if (totalBytes <= 0) {
|
|
@@ -133,9 +134,11 @@ public class StallionFileDownloader {
|
|
|
133
134
|
}
|
|
134
135
|
|
|
135
136
|
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
|
136
|
-
|
|
137
|
+
raf.write(buffer, 0, bytesRead);
|
|
137
138
|
receivedBytes += bytesRead;
|
|
138
139
|
|
|
140
|
+
StallionDownloadCacheManager.saveDownloadCache(downloadDirectory, receivedBytes);
|
|
141
|
+
|
|
139
142
|
double progress = (double) receivedBytes / totalBytes;
|
|
140
143
|
if (Double.isNaN(progress) || Double.isInfinite(progress)) {
|
|
141
144
|
callback.onReject(StallionApiConstants.DOWNLOAD_ERROR_PREFIX, "Invalid progress calculation");
|
|
@@ -148,8 +151,7 @@ public class StallionFileDownloader {
|
|
|
148
151
|
}
|
|
149
152
|
}
|
|
150
153
|
|
|
151
|
-
|
|
152
|
-
fout.close();
|
|
154
|
+
raf.close();
|
|
153
155
|
inputStream.close();
|
|
154
156
|
|
|
155
157
|
// Check for incomplete download
|
|
@@ -161,18 +163,25 @@ public class StallionFileDownloader {
|
|
|
161
163
|
callback.onReject(StallionApiConstants.DOWNLOAD_ERROR_PREFIX, "IOException occurred: ");
|
|
162
164
|
throw e;
|
|
163
165
|
} finally {
|
|
164
|
-
|
|
165
|
-
connection.disconnect();
|
|
166
|
-
}
|
|
166
|
+
connection.disconnect();
|
|
167
167
|
}
|
|
168
168
|
}
|
|
169
169
|
|
|
170
170
|
|
|
171
|
-
private static HttpURLConnection setupConnection(
|
|
171
|
+
private static HttpURLConnection setupConnection(
|
|
172
|
+
String downloadUrl,
|
|
173
|
+
String appToken,
|
|
174
|
+
String sdkToken,
|
|
175
|
+
long offset
|
|
176
|
+
) throws IOException {
|
|
172
177
|
URL url = new URL(downloadUrl);
|
|
173
178
|
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
|
174
179
|
connection.setRequestMethod("GET");
|
|
175
180
|
|
|
181
|
+
if (offset > 0) {
|
|
182
|
+
connection.setRequestProperty("Range", "bytes=" + offset + "-");
|
|
183
|
+
}
|
|
184
|
+
|
|
176
185
|
if(!appToken.isEmpty()) {
|
|
177
186
|
connection.setRequestProperty(StallionApiConstants.STALLION_APP_TOKEN_KEY, appToken);
|
|
178
187
|
}
|
|
@@ -4,6 +4,7 @@ import com.facebook.react.bridge.Promise;
|
|
|
4
4
|
import com.facebook.react.bridge.ReadableMap;
|
|
5
5
|
import com.stallion.events.StallionEventConstants;
|
|
6
6
|
import com.stallion.events.StallionEventManager;
|
|
7
|
+
import com.stallion.storage.StallionConfig;
|
|
7
8
|
import com.stallion.storage.StallionConfigConstants;
|
|
8
9
|
import com.stallion.storage.StallionMetaConstants;
|
|
9
10
|
import com.stallion.storage.StallionStateManager;
|
|
@@ -21,14 +22,18 @@ public class StallionStageManager {
|
|
|
21
22
|
&& receivedHash != null
|
|
22
23
|
&& !receivedHash.isEmpty()
|
|
23
24
|
) {
|
|
24
|
-
|
|
25
|
+
StallionConfig config = stallionStateManager.getStallionConfig();
|
|
26
|
+
String downloadPath = config.getFilesDirectory()
|
|
25
27
|
+ StallionConfigConstants.STAGE_DIRECTORY
|
|
26
28
|
+ StallionConfigConstants.TEMP_FOLDER_SLOT;
|
|
29
|
+
long alreadyDownloaded = StallionDownloadCacheManager.getDownloadCache(config, receivedDownloadUrl, downloadPath);
|
|
30
|
+
|
|
31
|
+
emitDownloadStartedStage(receivedHash, alreadyDownloaded > 0);
|
|
27
32
|
|
|
28
|
-
emitDownloadStartedStage(receivedHash);
|
|
29
33
|
StallionFileDownloader.downloadBundle(
|
|
30
34
|
receivedDownloadUrl,
|
|
31
35
|
downloadPath,
|
|
36
|
+
alreadyDownloaded,
|
|
32
37
|
new StallionDownloadCallback() {
|
|
33
38
|
@Override
|
|
34
39
|
public void onReject(String prefix, String error) {
|
|
@@ -42,6 +47,7 @@ public class StallionStageManager {
|
|
|
42
47
|
stallionStateManager.stallionMeta.setStageTempHash(receivedHash);
|
|
43
48
|
stallionStateManager.syncStallionMeta();
|
|
44
49
|
emitDownloadSuccessStage(receivedHash);
|
|
50
|
+
StallionDownloadCacheManager.deleteDownloadCache(downloadPath);
|
|
45
51
|
promise.resolve(successPayload);
|
|
46
52
|
}
|
|
47
53
|
|
|
@@ -77,13 +83,15 @@ public class StallionStageManager {
|
|
|
77
83
|
);
|
|
78
84
|
}
|
|
79
85
|
|
|
80
|
-
private static void emitDownloadStartedStage(String releaseHash) {
|
|
86
|
+
private static void emitDownloadStartedStage(String releaseHash, Boolean isResume) {
|
|
81
87
|
JSONObject startedPayload = new JSONObject();
|
|
82
88
|
try {
|
|
83
89
|
startedPayload.put("releaseHash", releaseHash);
|
|
84
90
|
} catch (Exception ignored) { }
|
|
85
91
|
StallionEventManager.getInstance().sendEvent(
|
|
86
|
-
|
|
92
|
+
isResume ?
|
|
93
|
+
StallionEventConstants.NativeStageEventTypes.DOWNLOAD_RESUME_STAGE.toString()
|
|
94
|
+
: StallionEventConstants.NativeStageEventTypes.DOWNLOAD_STARTED_STAGE.toString(),
|
|
87
95
|
startedPayload
|
|
88
96
|
);
|
|
89
97
|
}
|
|
@@ -106,16 +106,21 @@ public class StallionSyncHandler {
|
|
|
106
106
|
}
|
|
107
107
|
try {
|
|
108
108
|
StallionStateManager stateManager = StallionStateManager.getInstance();
|
|
109
|
-
|
|
109
|
+
StallionConfig config = stateManager.getStallionConfig();
|
|
110
|
+
String downloadPath = config.getFilesDirectory()
|
|
110
111
|
+ StallionConfigConstants.PROD_DIRECTORY
|
|
111
112
|
+ StallionConfigConstants.TEMP_FOLDER_SLOT;
|
|
112
|
-
String projectId =
|
|
113
|
+
String projectId = config.getProjectId();
|
|
114
|
+
String downloadUrl = newReleaseUrl + "?projectId=" + projectId;
|
|
113
115
|
|
|
114
|
-
|
|
116
|
+
long alreadyDownloaded = StallionDownloadCacheManager.getDownloadCache(config, downloadUrl, downloadPath);
|
|
117
|
+
|
|
118
|
+
emitDownloadStarted(newReleaseHash, alreadyDownloaded > 0);
|
|
115
119
|
|
|
116
120
|
StallionFileDownloader.downloadBundle(
|
|
117
|
-
|
|
121
|
+
downloadUrl,
|
|
118
122
|
downloadPath,
|
|
123
|
+
alreadyDownloaded,
|
|
119
124
|
new StallionDownloadCallback() {
|
|
120
125
|
@Override
|
|
121
126
|
public void onReject(String prefix, String error) {
|
|
@@ -133,6 +138,7 @@ public class StallionSyncHandler {
|
|
|
133
138
|
StallionSlotManager.stabilizeProd();
|
|
134
139
|
}
|
|
135
140
|
stateManager.syncStallionMeta();
|
|
141
|
+
StallionDownloadCacheManager.deleteDownloadCache(downloadPath);
|
|
136
142
|
emitDownloadSuccess(newReleaseHash);
|
|
137
143
|
}
|
|
138
144
|
|
|
@@ -182,13 +188,13 @@ public class StallionSyncHandler {
|
|
|
182
188
|
);
|
|
183
189
|
}
|
|
184
190
|
|
|
185
|
-
private static void emitDownloadStarted(String releaseHash) {
|
|
191
|
+
private static void emitDownloadStarted(String releaseHash, Boolean isResume) {
|
|
186
192
|
JSONObject successPayload = new JSONObject();
|
|
187
193
|
try {
|
|
188
194
|
successPayload.put("releaseHash", releaseHash);
|
|
189
195
|
} catch (Exception ignored) { }
|
|
190
196
|
StallionEventManager.getInstance().sendEvent(
|
|
191
|
-
NativeProdEventTypes.DOWNLOAD_STARTED_PROD.toString(),
|
|
197
|
+
isResume ? NativeProdEventTypes.DOWNLOAD_RESUME_PROD.toString(): NativeProdEventTypes.DOWNLOAD_STARTED_PROD.toString(),
|
|
192
198
|
successPayload
|
|
193
199
|
);
|
|
194
200
|
}
|
|
@@ -19,6 +19,7 @@ public class StallionConfig {
|
|
|
19
19
|
private final String appVersion;
|
|
20
20
|
private final SharedPreferences sharedPreferences;
|
|
21
21
|
private final String filesDirectory;
|
|
22
|
+
private String lastDownloadingUrl;
|
|
22
23
|
|
|
23
24
|
public StallionConfig(Context context, SharedPreferences sharedPreferences) {
|
|
24
25
|
this.sharedPreferences = sharedPreferences;
|
|
@@ -63,6 +64,18 @@ public class StallionConfig {
|
|
|
63
64
|
|
|
64
65
|
this.appVersion = fetchAppVersion(context);
|
|
65
66
|
this.filesDirectory = context.getFilesDir().getAbsolutePath();
|
|
67
|
+
this.lastDownloadingUrl = sharedPreferences.getString(StallionConfigConstants.LAST_DOWNLOADING_URL_IDENTIFIER, "");
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
public String getLastDownloadingUrl() {
|
|
71
|
+
return this.lastDownloadingUrl;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
public void setLastDownloadingUrl(String newUrl) {
|
|
75
|
+
this.lastDownloadingUrl = newUrl;
|
|
76
|
+
SharedPreferences.Editor editor = sharedPreferences.edit();
|
|
77
|
+
editor.putString(StallionConfigConstants.LAST_DOWNLOADING_URL_IDENTIFIER, newUrl);
|
|
78
|
+
editor.apply();
|
|
66
79
|
}
|
|
67
80
|
|
|
68
81
|
private String fetchAppVersion(Context context) {
|
|
@@ -9,6 +9,7 @@ public class StallionConfigConstants {
|
|
|
9
9
|
public static final String STALLION_APP_TOKEN_IDENTIFIER = "StallionAppToken";
|
|
10
10
|
public static final String UNIQUE_ID_IDENTIFIER = "stallionDeviceId";
|
|
11
11
|
public static final String API_KEY_IDENTIFIER = "x-sdk-access-token";
|
|
12
|
+
public static final String LAST_DOWNLOADING_URL_IDENTIFIER = "StallionLastDownloadingUrl";
|
|
12
13
|
|
|
13
14
|
public static final String PROD_DIRECTORY = "/StallionProd";
|
|
14
15
|
public static final String STAGE_DIRECTORY = "/StallionStage";
|