2
2
3
3
import android .content .Context ;
4
4
5
+ import com .facebook .react .bridge .ReadableArray ;
5
6
import com .facebook .react .bridge .ReadableMap ;
6
7
import com .facebook .react .bridge .WritableMap ;
7
8
import com .facebook .react .bridge .WritableNativeMap ;
8
9
10
+ import org .json .JSONException ;
11
+ import org .json .JSONObject ;
12
+
9
13
import java .io .BufferedInputStream ;
10
14
import java .io .BufferedOutputStream ;
11
15
import java .io .File ;
14
18
import java .net .HttpURLConnection ;
15
19
import java .net .MalformedURLException ;
16
20
import java .net .URL ;
21
+ import java .nio .ByteBuffer ;
17
22
18
23
public class CodePushPackage {
19
24
20
25
public final String CODE_PUSH_FOLDER_PREFIX = "CodePush" ;
21
- public final String STATUS_FILE = "codepush.json" ;
22
- public final String UPDATE_BUNDLE_FILE_NAME = "app.jsbundle" ;
23
26
public final String CURRENT_PACKAGE_KEY = "currentPackage" ;
24
- public final String PREVIOUS_PACKAGE_KEY = "previousPackage" ;
27
+ public final String DIFF_MANIFEST_FILE_NAME = "hotcodepush.json" ;
28
+ public final int DOWNLOAD_BUFFER_SIZE = 1024 * 256 ;
29
+ public final String DOWNLOAD_FILE_NAME = "download.zip" ;
30
+ public final String DOWNLOAD_URL_KEY = "downloadUrl" ;
25
31
public final String PACKAGE_FILE_NAME = "app.json" ;
26
32
public final String PACKAGE_HASH_KEY = "packageHash" ;
27
- public final String DOWNLOAD_URL_KEY = "downloadUrl" ;
28
- public final int DOWNLOAD_BUFFER_SIZE = 1024 * 256 ;
33
+ public final String PREVIOUS_PACKAGE_KEY = "previousPackage" ;
34
+ public final String RELATIVE_BUNDLE_PATH_KEY = "bundlePath" ;
35
+ public final String STATUS_FILE = "codepush.json" ;
36
+ public final String UNZIPPED_FOLDER_NAME = "unzipped" ;
37
+ public final String UPDATE_BUNDLE_FILE_NAME = "app.jsbundle" ;
29
38
30
39
private String documentsDirectory ;
31
40
32
41
public CodePushPackage (String documentsDirectory ) {
33
42
this .documentsDirectory = documentsDirectory ;
34
43
}
35
44
45
+ public String getDownloadFilePath () {
46
+ return CodePushUtils .appendPathComponent (getCodePushPath (), DOWNLOAD_FILE_NAME );
47
+ }
48
+
49
+ public String getUnzippedFolderPath () {
50
+ return CodePushUtils .appendPathComponent (getCodePushPath (), UNZIPPED_FOLDER_NAME );
51
+ }
52
+
36
53
public String getDocumentsDirectory () {
37
54
return documentsDirectory ;
38
55
}
@@ -87,7 +104,13 @@ public String getCurrentPackageBundlePath() {
87
104
return null ;
88
105
}
89
106
90
- return CodePushUtils .appendPathComponent (packageFolder , UPDATE_BUNDLE_FILE_NAME );
107
+ WritableMap currentPackage = getCurrentPackage ();
108
+ String relativeBundlePath = CodePushUtils .tryGetString (currentPackage , RELATIVE_BUNDLE_PATH_KEY );
109
+ if (relativeBundlePath == null ) {
110
+ return CodePushUtils .appendPathComponent (packageFolder , UPDATE_BUNDLE_FILE_NAME );
111
+ } else {
112
+ return CodePushUtils .appendPathComponent (packageFolder , relativeBundlePath );
113
+ }
91
114
}
92
115
93
116
public String getPackageFolderPath (String packageHash ) {
@@ -132,14 +155,16 @@ public WritableMap getPackage(String packageHash) {
132
155
public void downloadPackage (Context applicationContext , ReadableMap updatePackage ,
133
156
DownloadProgressCallback progressCallback ) throws IOException {
134
157
135
- String packageFolderPath = getPackageFolderPath (CodePushUtils .tryGetString (updatePackage , PACKAGE_HASH_KEY ));
158
+ String newPackageFolderPath = getPackageFolderPath (CodePushUtils .tryGetString (updatePackage , PACKAGE_HASH_KEY ));
136
159
String downloadUrlString = CodePushUtils .tryGetString (updatePackage , DOWNLOAD_URL_KEY );
137
160
138
161
URL downloadUrl = null ;
139
162
HttpURLConnection connection = null ;
140
163
BufferedInputStream bin = null ;
141
164
FileOutputStream fos = null ;
142
165
BufferedOutputStream bout = null ;
166
+ File downloadFile = null ;
167
+ boolean isZip = false ;
143
168
144
169
try {
145
170
downloadUrl = new URL (downloadUrlString );
@@ -149,23 +174,34 @@ public void downloadPackage(Context applicationContext, ReadableMap updatePackag
149
174
long receivedBytes = 0 ;
150
175
151
176
bin = new BufferedInputStream (connection .getInputStream ());
152
- File downloadFolder = new File (packageFolderPath );
177
+ File downloadFolder = new File (getCodePushPath () );
153
178
downloadFolder .mkdirs ();
154
- File downloadFile = new File (downloadFolder , UPDATE_BUNDLE_FILE_NAME );
179
+ downloadFile = new File (downloadFolder , DOWNLOAD_FILE_NAME );
155
180
fos = new FileOutputStream (downloadFile );
156
181
bout = new BufferedOutputStream (fos , DOWNLOAD_BUFFER_SIZE );
157
182
byte [] data = new byte [DOWNLOAD_BUFFER_SIZE ];
183
+ byte [] header = new byte [4 ];
184
+
158
185
int numBytesRead = 0 ;
159
186
while ((numBytesRead = bin .read (data , 0 , DOWNLOAD_BUFFER_SIZE )) >= 0 ) {
187
+ if (receivedBytes < 4 ) {
188
+ for (int i = 0 ; i < numBytesRead ; i ++) {
189
+ int headerOffset = (int )(receivedBytes ) + i ;
190
+ if (headerOffset >= 4 ) {
191
+ break ;
192
+ }
193
+
194
+ header [headerOffset ] = data [i ];
195
+ }
196
+ }
197
+
160
198
receivedBytes += numBytesRead ;
161
199
bout .write (data , 0 , numBytesRead );
162
200
progressCallback .call (new DownloadProgress (totalBytes , receivedBytes ));
163
201
}
164
202
165
203
assert totalBytes == receivedBytes ;
166
-
167
- String bundlePath = CodePushUtils .appendPathComponent (packageFolderPath , PACKAGE_FILE_NAME );
168
- CodePushUtils .writeReadableMapToFile (updatePackage , bundlePath );
204
+ isZip = ByteBuffer .wrap (header ).getInt () == 0x504b0304 ;
169
205
} catch (MalformedURLException e ) {
170
206
throw new CodePushMalformedDataException (downloadUrlString , e );
171
207
} finally {
@@ -178,6 +214,78 @@ public void downloadPackage(Context applicationContext, ReadableMap updatePackag
178
214
throw new CodePushUnknownException ("Error closing IO resources." , e );
179
215
}
180
216
}
217
+
218
+ if (isZip ) {
219
+ System .err .println ("THIS IS A ZIP!" );
220
+ String unzippedFolderPath = getUnzippedFolderPath ();
221
+ CodePushUtils .unzipFile (downloadFile , unzippedFolderPath );
222
+ CodePushUtils .deleteFileSilently (downloadFile );
223
+ String diffManifestFilePath = CodePushUtils .appendPathComponent (unzippedFolderPath ,
224
+ DIFF_MANIFEST_FILE_NAME );
225
+ File diffManifestFile = new File (unzippedFolderPath , DIFF_MANIFEST_FILE_NAME );
226
+ if (diffManifestFile .exists ()) {
227
+ String currentPackageFolderPath = getCurrentPackageFolderPath ();
228
+ CodePushUtils .mergeEntriesInFolder (currentPackageFolderPath , newPackageFolderPath );
229
+ WritableMap diffManifest = CodePushUtils .getWritableMapFromFile (diffManifestFilePath );
230
+ ReadableArray deletedFiles = diffManifest .getArray ("deletedFiles" );
231
+ for (int i = 0 ; i < deletedFiles .size (); i ++) {
232
+ String fileNameToDelete = deletedFiles .getString (i );
233
+ File fileToDelete = new File (newPackageFolderPath , fileNameToDelete );
234
+ CodePushUtils .deleteFileSilently (fileToDelete );
235
+ }
236
+ }
237
+
238
+ CodePushUtils .mergeEntriesInFolder (unzippedFolderPath , newPackageFolderPath );
239
+ CodePushUtils .deleteFileAtPathSilently (unzippedFolderPath );
240
+ String relativeBundlePath = findMainBundleInFolder (newPackageFolderPath );
241
+
242
+ if (relativeBundlePath == null ) {
243
+ throw new CodePushInvalidPackageException ();
244
+ } else {
245
+ JSONObject updatePackageJSON = CodePushUtils .convertReadableToJsonObject (updatePackage );
246
+ try {
247
+ updatePackageJSON .put (RELATIVE_BUNDLE_PATH_KEY , relativeBundlePath );
248
+ } catch (JSONException e ) {
249
+ throw new CodePushUnknownException ("Unable to set key " +
250
+ RELATIVE_BUNDLE_PATH_KEY + " to value " + relativeBundlePath +
251
+ " in update package." , e );
252
+ }
253
+ updatePackage = CodePushUtils .convertJsonObjectToWriteable (updatePackageJSON );
254
+ }
255
+ } else {
256
+ System .err .println ("THIS IS NOT A ZIP!" );
257
+ // File is not a zip.
258
+ File updateBundleFile = new File (newPackageFolderPath , UPDATE_BUNDLE_FILE_NAME );
259
+ downloadFile .renameTo (updateBundleFile );
260
+ }
261
+
262
+ String bundlePath = CodePushUtils .appendPathComponent (newPackageFolderPath , PACKAGE_FILE_NAME );
263
+ CodePushUtils .writeReadableMapToFile (updatePackage , bundlePath );
264
+ }
265
+
266
+ public String findMainBundleInFolder (String folderPath ) {
267
+ File folder = new File (folderPath );
268
+ File [] folderFiles = folder .listFiles ();
269
+ for (File file : folderFiles ) {
270
+ String fullFilePath = CodePushUtils .appendPathComponent (folderPath , file .getName ());
271
+ if (file .isDirectory ()) {
272
+ String mainBundlePathInSubFolder = findMainBundleInFolder (fullFilePath );
273
+ if (mainBundlePathInSubFolder != null ) {
274
+ return CodePushUtils .appendPathComponent (file .getName (), mainBundlePathInSubFolder );
275
+ }
276
+ } else {
277
+ String fileName = file .getName ();
278
+ int dotIndex = fileName .lastIndexOf ("." );
279
+ if (dotIndex >= 0 ) {
280
+ String fileExtension = fileName .substring (dotIndex + 1 );
281
+ if (fileExtension .equals ("bundle" ) || fileExtension .equals ("js" ) || fileExtension .equals ("jsbundle" )) {
282
+ return fileName ;
283
+ }
284
+ }
285
+ }
286
+ }
287
+
288
+ return null ;
181
289
}
182
290
183
291
public void installPackage (ReadableMap updatePackage ) throws IOException {
0 commit comments