react-native-update 10.39.0-beta.2 → 10.39.0-beta.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.
|
@@ -3,9 +3,12 @@ package cn.reactnative.modules.update;
|
|
|
3
3
|
import android.content.Context;
|
|
4
4
|
import android.content.pm.ApplicationInfo;
|
|
5
5
|
import android.content.pm.PackageManager;
|
|
6
|
+
import android.content.res.Resources;
|
|
6
7
|
import android.os.AsyncTask;
|
|
7
8
|
import android.os.Build;
|
|
9
|
+
import android.util.DisplayMetrics;
|
|
8
10
|
import android.util.Log;
|
|
11
|
+
import android.util.TypedValue;
|
|
9
12
|
import com.facebook.react.bridge.Arguments;
|
|
10
13
|
import com.facebook.react.bridge.WritableMap;
|
|
11
14
|
|
|
@@ -29,7 +32,6 @@ import java.util.Enumeration;
|
|
|
29
32
|
import java.util.Iterator;
|
|
30
33
|
import java.util.zip.ZipEntry;
|
|
31
34
|
import java.util.HashMap;
|
|
32
|
-
import java.util.regex.Matcher;
|
|
33
35
|
import java.util.regex.Pattern;
|
|
34
36
|
|
|
35
37
|
import okio.BufferedSink;
|
|
@@ -197,8 +199,7 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, long[], Void> {
|
|
|
197
199
|
JSONObject manifest,
|
|
198
200
|
ArrayList<String> copyFroms,
|
|
199
201
|
ArrayList<String> copyTos,
|
|
200
|
-
ArrayList<String> deletes
|
|
201
|
-
HashMap<String, String> copiesMap
|
|
202
|
+
ArrayList<String> deletes
|
|
202
203
|
) throws JSONException {
|
|
203
204
|
JSONObject copies = manifest.optJSONObject("copies");
|
|
204
205
|
if (copies != null) {
|
|
@@ -211,9 +212,6 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, long[], Void> {
|
|
|
211
212
|
}
|
|
212
213
|
copyFroms.add(from);
|
|
213
214
|
copyTos.add(to);
|
|
214
|
-
if (copiesMap != null) {
|
|
215
|
-
copiesMap.put(to, from);
|
|
216
|
-
}
|
|
217
215
|
}
|
|
218
216
|
}
|
|
219
217
|
|
|
@@ -228,13 +226,20 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, long[], Void> {
|
|
|
228
226
|
|
|
229
227
|
private void copyBundledAssetToFile(String assetName, File destination) throws IOException {
|
|
230
228
|
InputStream in = context.getAssets().open(assetName);
|
|
229
|
+
copyInputStreamToFile(in, destination);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
private void copyInputStreamToFile(InputStream in, File destination) throws IOException {
|
|
231
233
|
FileOutputStream fout = new FileOutputStream(destination);
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
234
|
+
try {
|
|
235
|
+
int count;
|
|
236
|
+
while ((count = in.read(buffer)) != -1) {
|
|
237
|
+
fout.write(buffer, 0, count);
|
|
238
|
+
}
|
|
239
|
+
} finally {
|
|
240
|
+
fout.close();
|
|
241
|
+
in.close();
|
|
235
242
|
}
|
|
236
|
-
fout.close();
|
|
237
|
-
in.close();
|
|
238
243
|
}
|
|
239
244
|
|
|
240
245
|
private HashMap<String, ArrayList<File>> buildCopyList(
|
|
@@ -303,53 +308,127 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, long[], Void> {
|
|
|
303
308
|
return VERSION_QUALIFIER_PATTERN.matcher(result).replaceAll("");
|
|
304
309
|
}
|
|
305
310
|
|
|
306
|
-
private
|
|
307
|
-
|
|
308
|
-
|
|
311
|
+
private static class ResolvedResourceSource {
|
|
312
|
+
final int resourceId;
|
|
313
|
+
final String assetPath;
|
|
314
|
+
|
|
315
|
+
ResolvedResourceSource(int resourceId, String assetPath) {
|
|
316
|
+
this.resourceId = resourceId;
|
|
317
|
+
this.assetPath = assetPath;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
private String extractResourceType(String directoryName) {
|
|
322
|
+
int qualifierIndex = directoryName.indexOf('-');
|
|
323
|
+
if (qualifierIndex == -1) {
|
|
324
|
+
return directoryName;
|
|
325
|
+
}
|
|
326
|
+
return directoryName.substring(0, qualifierIndex);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
private String extractResourceName(String fileName) {
|
|
330
|
+
if (fileName.endsWith(".9.png")) {
|
|
331
|
+
return fileName.substring(0, fileName.length() - ".9.png".length());
|
|
332
|
+
}
|
|
333
|
+
int extensionIndex = fileName.lastIndexOf('.');
|
|
334
|
+
if (extensionIndex == -1) {
|
|
335
|
+
return fileName;
|
|
336
|
+
}
|
|
337
|
+
return fileName.substring(0, extensionIndex);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
private Integer parseDensityQualifier(String directoryName) {
|
|
341
|
+
String[] qualifiers = directoryName.split("-");
|
|
342
|
+
for (String qualifier : qualifiers) {
|
|
343
|
+
if ("ldpi".equals(qualifier)) {
|
|
344
|
+
return DisplayMetrics.DENSITY_LOW;
|
|
345
|
+
}
|
|
346
|
+
if ("mdpi".equals(qualifier)) {
|
|
347
|
+
return DisplayMetrics.DENSITY_MEDIUM;
|
|
348
|
+
}
|
|
349
|
+
if ("hdpi".equals(qualifier)) {
|
|
350
|
+
return DisplayMetrics.DENSITY_HIGH;
|
|
351
|
+
}
|
|
352
|
+
if ("xhdpi".equals(qualifier)) {
|
|
353
|
+
return DisplayMetrics.DENSITY_XHIGH;
|
|
354
|
+
}
|
|
355
|
+
if ("xxhdpi".equals(qualifier)) {
|
|
356
|
+
return DisplayMetrics.DENSITY_XXHIGH;
|
|
357
|
+
}
|
|
358
|
+
if ("xxxhdpi".equals(qualifier)) {
|
|
359
|
+
return DisplayMetrics.DENSITY_XXXHIGH;
|
|
360
|
+
}
|
|
361
|
+
if ("tvdpi".equals(qualifier)) {
|
|
362
|
+
return DisplayMetrics.DENSITY_TV;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
return null;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
private ResolvedResourceSource resolveBundledResource(String resourcePath) {
|
|
369
|
+
String normalizedPath = normalizeResPath(resourcePath);
|
|
370
|
+
if (normalizedPath.startsWith("res/")) {
|
|
371
|
+
normalizedPath = normalizedPath.substring("res/".length());
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
int slash = normalizedPath.indexOf('/');
|
|
375
|
+
if (slash == -1 || slash == normalizedPath.length() - 1) {
|
|
309
376
|
return null;
|
|
310
377
|
}
|
|
311
378
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
379
|
+
String directoryName = normalizedPath.substring(0, slash);
|
|
380
|
+
String fileName = normalizedPath.substring(slash + 1);
|
|
381
|
+
String resourceType = extractResourceType(directoryName);
|
|
382
|
+
String resourceName = extractResourceName(fileName);
|
|
383
|
+
if (resourceType == null || resourceType.isEmpty() || resourceName.isEmpty()) {
|
|
315
384
|
return null;
|
|
316
385
|
}
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
return fallbackFromPath;
|
|
336
|
-
}
|
|
337
|
-
// 尝试版本限定符无关匹配(APK ↔ AAB 兼容)
|
|
338
|
-
String normalizedFallback = normalizeResPath(fallbackFromPath);
|
|
339
|
-
String actualEntry = normalizedEntryMap.get(normalizedFallback);
|
|
340
|
-
if (actualEntry != null) {
|
|
341
|
-
if (UpdateContext.DEBUG) {
|
|
342
|
-
Log.d("react-native-update", "Found normalized fallback for " + originalToPath + ": " + fallbackToPath + " -> " + actualEntry);
|
|
343
|
-
}
|
|
344
|
-
return actualEntry;
|
|
345
|
-
}
|
|
386
|
+
|
|
387
|
+
Resources resources = context.getResources();
|
|
388
|
+
int resourceId = resources.getIdentifier(resourceName, resourceType, context.getPackageName());
|
|
389
|
+
if (resourceId == 0) {
|
|
390
|
+
return null;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
TypedValue typedValue = new TypedValue();
|
|
394
|
+
try {
|
|
395
|
+
Integer density = parseDensityQualifier(directoryName);
|
|
396
|
+
if (density != null) {
|
|
397
|
+
resources.getValueForDensity(resourceId, density, typedValue, true);
|
|
398
|
+
} else {
|
|
399
|
+
resources.getValue(resourceId, typedValue, true);
|
|
400
|
+
}
|
|
401
|
+
} catch (Resources.NotFoundException e) {
|
|
402
|
+
if (UpdateContext.DEBUG) {
|
|
403
|
+
Log.d("react-native-update", "Failed to resolve resource value for " + resourcePath + ": " + e.getMessage());
|
|
346
404
|
}
|
|
405
|
+
return null;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
if (typedValue.string == null) {
|
|
409
|
+
return null;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
String assetPath = typedValue.string.toString();
|
|
413
|
+
if (assetPath.startsWith("/")) {
|
|
414
|
+
assetPath = assetPath.substring(1);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
if (UpdateContext.DEBUG) {
|
|
418
|
+
Log.d("react-native-update", "Resolved resource path " + resourcePath + " -> " + assetPath);
|
|
419
|
+
}
|
|
420
|
+
return new ResolvedResourceSource(resourceId, assetPath);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
private InputStream openResolvedResourceStream(ResolvedResourceSource source) throws IOException {
|
|
424
|
+
try {
|
|
425
|
+
return context.getResources().openRawResource(source.resourceId);
|
|
426
|
+
} catch (Resources.NotFoundException e) {
|
|
427
|
+
throw new IOException("Unable to open resolved resource: " + source.assetPath, e);
|
|
347
428
|
}
|
|
348
|
-
|
|
349
|
-
return null;
|
|
350
429
|
}
|
|
351
430
|
|
|
352
|
-
private void copyFromResource(HashMap<String, ArrayList<File> > resToCopy
|
|
431
|
+
private void copyFromResource(HashMap<String, ArrayList<File> > resToCopy) throws IOException {
|
|
353
432
|
if (UpdateContext.DEBUG) {
|
|
354
433
|
Log.d("react-native-update", "copyFromResource called, resToCopy size: " + resToCopy.size());
|
|
355
434
|
}
|
|
@@ -421,6 +500,7 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, long[], Void> {
|
|
|
421
500
|
|
|
422
501
|
ZipEntry ze = availableEntries.get(fromPath);
|
|
423
502
|
String actualSourcePath = fromPath;
|
|
503
|
+
ResolvedResourceSource resolvedResource = null;
|
|
424
504
|
|
|
425
505
|
// 如果精确匹配找不到,尝试版本限定符无关匹配(APK ↔ AAB 兼容)
|
|
426
506
|
// 例如 __diff.json 中的 "res/drawable-xxhdpi-v4/img.png" 匹配设备上的 "res/drawable-xxhdpi/img.png"
|
|
@@ -435,45 +515,17 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, long[], Void> {
|
|
|
435
515
|
}
|
|
436
516
|
}
|
|
437
517
|
}
|
|
438
|
-
|
|
439
|
-
//
|
|
518
|
+
|
|
519
|
+
// release APK 可能会将资源 entry 名压缩为 res/9w.png 之类的短路径;
|
|
520
|
+
// 这时通过 Resources 解析逻辑资源名,再直接读取资源内容。
|
|
440
521
|
if (ze == null) {
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
// 找到对应的 to 路径(从 copiesMap 的反向查找)
|
|
445
|
-
String toPath = null;
|
|
446
|
-
for (String to : copiesMap.keySet()) {
|
|
447
|
-
if (copiesMap.get(to).equals(fromPath)) {
|
|
448
|
-
toPath = to;
|
|
449
|
-
break;
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
if (toPath != null) {
|
|
454
|
-
if (UpdateContext.DEBUG) {
|
|
455
|
-
Log.d("react-native-update", "Found toPath: " + toPath + " for fromPath: " + fromPath);
|
|
456
|
-
}
|
|
457
|
-
String fallbackFromPath = findDrawableFallback(toPath, copiesMap, availableEntries, normalizedEntryMap);
|
|
458
|
-
if (fallbackFromPath != null) {
|
|
459
|
-
ze = availableEntries.get(fallbackFromPath);
|
|
460
|
-
actualSourcePath = fallbackFromPath;
|
|
461
|
-
if (UpdateContext.DEBUG) {
|
|
462
|
-
Log.w("react-native-update", "Using fallback: " + fallbackFromPath + " for " + fromPath);
|
|
463
|
-
}
|
|
464
|
-
} else {
|
|
465
|
-
if (UpdateContext.DEBUG) {
|
|
466
|
-
Log.w("react-native-update", "No fallback found for: " + fromPath + " (toPath: " + toPath + ")");
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
} else {
|
|
470
|
-
if (UpdateContext.DEBUG) {
|
|
471
|
-
Log.w("react-native-update", "No toPath found for fromPath: " + fromPath);
|
|
472
|
-
}
|
|
522
|
+
resolvedResource = resolveBundledResource(fromPath);
|
|
523
|
+
if (resolvedResource != null) {
|
|
524
|
+
actualSourcePath = resolvedResource.assetPath;
|
|
473
525
|
}
|
|
474
526
|
}
|
|
475
527
|
|
|
476
|
-
if (ze != null) {
|
|
528
|
+
if (ze != null || resolvedResource != null) {
|
|
477
529
|
File lastTarget = null;
|
|
478
530
|
for (File target: targets) {
|
|
479
531
|
if (UpdateContext.DEBUG) {
|
|
@@ -489,12 +541,17 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, long[], Void> {
|
|
|
489
541
|
if (lastTarget != null) {
|
|
490
542
|
copyFile(lastTarget, target);
|
|
491
543
|
} else {
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
sourceZipFile
|
|
544
|
+
if (ze != null) {
|
|
545
|
+
// 从保存的映射中获取包含该条目的 ZipFile
|
|
546
|
+
SafeZipFile sourceZipFile = entryToZipFileMap.get(actualSourcePath);
|
|
547
|
+
if (sourceZipFile == null) {
|
|
548
|
+
sourceZipFile = zipFile; // 回退到基础 APK
|
|
549
|
+
}
|
|
550
|
+
sourceZipFile.unzipToFile(ze, target);
|
|
551
|
+
} else {
|
|
552
|
+
InputStream in = openResolvedResourceStream(resolvedResource);
|
|
553
|
+
copyInputStreamToFile(in, target);
|
|
496
554
|
}
|
|
497
|
-
sourceZipFile.unzipToFile(ze, target);
|
|
498
555
|
lastTarget = target;
|
|
499
556
|
}
|
|
500
557
|
} catch (IOException e) {
|
|
@@ -526,7 +583,6 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, long[], Void> {
|
|
|
526
583
|
|
|
527
584
|
removeDirectory(param.unzipDirectory);
|
|
528
585
|
param.unzipDirectory.mkdirs();
|
|
529
|
-
HashMap<String, String> copiesMap = new HashMap<String, String>(); // to -> from 映射
|
|
530
586
|
ArrayList<String> entryNames = new ArrayList<String>();
|
|
531
587
|
ArrayList<String> copyFroms = new ArrayList<String>();
|
|
532
588
|
ArrayList<String> copyTos = new ArrayList<String>();
|
|
@@ -544,7 +600,7 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, long[], Void> {
|
|
|
544
600
|
byte[] bytes = readBytes(zipFile.getInputStream(ze));
|
|
545
601
|
String json = new String(bytes, "UTF-8");
|
|
546
602
|
JSONObject obj = (JSONObject)new JSONTokener(json).nextValue();
|
|
547
|
-
appendManifestEntries(obj, copyFroms, copyTos, deletes
|
|
603
|
+
appendManifestEntries(obj, copyFroms, copyTos, deletes);
|
|
548
604
|
continue;
|
|
549
605
|
}
|
|
550
606
|
zipFile.unzipToPath(ze, param.unzipDirectory);
|
|
@@ -587,13 +643,13 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, long[], Void> {
|
|
|
587
643
|
}
|
|
588
644
|
|
|
589
645
|
if (UpdateContext.DEBUG) {
|
|
590
|
-
Log.d("react-native-update", "copyList size: " + copyList.size()
|
|
646
|
+
Log.d("react-native-update", "copyList size: " + copyList.size());
|
|
591
647
|
for (String from : copyList.keySet()) {
|
|
592
648
|
Log.d("react-native-update", "copyList entry: " + from + " -> " + copyList.get(from).size() + " targets");
|
|
593
649
|
}
|
|
594
650
|
}
|
|
595
651
|
|
|
596
|
-
copyFromResource(copyList
|
|
652
|
+
copyFromResource(copyList);
|
|
597
653
|
|
|
598
654
|
if (UpdateContext.DEBUG) {
|
|
599
655
|
Log.d("react-native-update", "Unzip finished");
|
|
@@ -625,7 +681,7 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, long[], Void> {
|
|
|
625
681
|
byte[] bytes = readBytes(zipFile.getInputStream(ze));
|
|
626
682
|
String json = new String(bytes, "UTF-8");
|
|
627
683
|
JSONObject obj = (JSONObject)new JSONTokener(json).nextValue();
|
|
628
|
-
appendManifestEntries(obj, copyFroms, copyTos, deletes
|
|
684
|
+
appendManifestEntries(obj, copyFroms, copyTos, deletes);
|
|
629
685
|
continue;
|
|
630
686
|
}
|
|
631
687
|
zipFile.unzipToPath(ze, param.unzipDirectory);
|
package/harmony/pushy.har
CHANGED
|
Binary file
|