react-native-update 10.38.5 → 10.39.0-beta.1
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/cn/reactnative/modules/update/DownloadTask.java +46 -22
- package/harmony/pushy/src/main/ets/DownloadTask.ts +41 -32
- package/harmony/pushy/src/main/ets/PushyPackage.ets +16 -0
- package/harmony/pushy/src/main/ets/PushyPackageCompat.ts +16 -0
- package/harmony/pushy/src/main/ets/PushyTurboModule.ts +6 -4
- package/harmony/pushy/src/main/ets/UpdateModuleImpl.ts +1 -1
- package/harmony/pushy.har +0 -0
- package/package.json +1 -1
- package/react-native-update.podspec +1 -2
- package/src/utils.ts +0 -6
- package/harmony/pushy/src/main/ets/PushyPackage.ts +0 -22
|
@@ -29,6 +29,8 @@ import java.util.Enumeration;
|
|
|
29
29
|
import java.util.Iterator;
|
|
30
30
|
import java.util.zip.ZipEntry;
|
|
31
31
|
import java.util.HashMap;
|
|
32
|
+
import java.util.regex.Matcher;
|
|
33
|
+
import java.util.regex.Pattern;
|
|
32
34
|
|
|
33
35
|
import okio.BufferedSink;
|
|
34
36
|
import okio.BufferedSource;
|
|
@@ -285,7 +287,16 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, long[], Void> {
|
|
|
285
287
|
}
|
|
286
288
|
}
|
|
287
289
|
|
|
288
|
-
|
|
290
|
+
// Pattern to strip -vN version qualifiers from resource directory paths
|
|
291
|
+
// e.g., "res/drawable-xxhdpi-v4/img.png" → "res/drawable-xxhdpi/img.png"
|
|
292
|
+
private static final Pattern VERSION_QUALIFIER_PATTERN =
|
|
293
|
+
Pattern.compile("-v\\d+(?=/)");
|
|
294
|
+
|
|
295
|
+
private String normalizeResPath(String path) {
|
|
296
|
+
return VERSION_QUALIFIER_PATTERN.matcher(path).replaceAll("");
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
private String findDrawableFallback(String originalToPath, HashMap<String, String> copiesMap, HashMap<String, ZipEntry> availableEntries, HashMap<String, String> normalizedEntryMap) {
|
|
289
300
|
// 检查是否是 drawable 路径
|
|
290
301
|
if (!originalToPath.contains("drawable")) {
|
|
291
302
|
return null;
|
|
@@ -309,13 +320,22 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, long[], Void> {
|
|
|
309
320
|
// 检查这个 key 是否在 copies 映射中
|
|
310
321
|
if (copiesMap.containsKey(fallbackToPath)) {
|
|
311
322
|
String fallbackFromPath = copiesMap.get(fallbackToPath);
|
|
312
|
-
// 检查对应的 value 路径是否在 APK
|
|
323
|
+
// 检查对应的 value 路径是否在 APK 中存在(精确匹配)
|
|
313
324
|
if (availableEntries.containsKey(fallbackFromPath)) {
|
|
314
325
|
if (UpdateContext.DEBUG) {
|
|
315
326
|
Log.d("react-native-update", "Found fallback for " + originalToPath + ": " + fallbackToPath + " -> " + fallbackFromPath);
|
|
316
327
|
}
|
|
317
328
|
return fallbackFromPath;
|
|
318
329
|
}
|
|
330
|
+
// 尝试版本限定符无关匹配(APK ↔ AAB 兼容)
|
|
331
|
+
String normalizedFallback = normalizeResPath(fallbackFromPath);
|
|
332
|
+
String actualEntry = normalizedEntryMap.get(normalizedFallback);
|
|
333
|
+
if (actualEntry != null) {
|
|
334
|
+
if (UpdateContext.DEBUG) {
|
|
335
|
+
Log.d("react-native-update", "Found normalized fallback for " + originalToPath + ": " + fallbackToPath + " -> " + actualEntry);
|
|
336
|
+
}
|
|
337
|
+
return actualEntry;
|
|
338
|
+
}
|
|
319
339
|
}
|
|
320
340
|
}
|
|
321
341
|
|
|
@@ -369,6 +389,14 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, long[], Void> {
|
|
|
369
389
|
}
|
|
370
390
|
}
|
|
371
391
|
|
|
392
|
+
// 构建规范化路径映射,用于 APK ↔ AAB 版本限定符无关匹配
|
|
393
|
+
// 例如 "res/drawable-xxhdpi-v4/img.png" → "res/drawable-xxhdpi/img.png"
|
|
394
|
+
HashMap<String, String> normalizedEntryMap = new HashMap<>();
|
|
395
|
+
for (String entryName : availableEntries.keySet()) {
|
|
396
|
+
String normalized = normalizeResPath(entryName);
|
|
397
|
+
normalizedEntryMap.putIfAbsent(normalized, entryName);
|
|
398
|
+
}
|
|
399
|
+
|
|
372
400
|
// 使用基础 APK 的 ZipFile 作为主要操作对象
|
|
373
401
|
SafeZipFile zipFile = zipFileMap.get(context.getPackageResourcePath());
|
|
374
402
|
|
|
@@ -387,7 +415,21 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, long[], Void> {
|
|
|
387
415
|
ZipEntry ze = availableEntries.get(fromPath);
|
|
388
416
|
String actualSourcePath = fromPath;
|
|
389
417
|
|
|
390
|
-
//
|
|
418
|
+
// 如果精确匹配找不到,尝试版本限定符无关匹配(APK ↔ AAB 兼容)
|
|
419
|
+
// 例如 __diff.json 中的 "res/drawable-xxhdpi-v4/img.png" 匹配设备上的 "res/drawable-xxhdpi/img.png"
|
|
420
|
+
if (ze == null) {
|
|
421
|
+
String normalizedFrom = normalizeResPath(fromPath);
|
|
422
|
+
String actualEntry = normalizedEntryMap.get(normalizedFrom);
|
|
423
|
+
if (actualEntry != null) {
|
|
424
|
+
ze = availableEntries.get(actualEntry);
|
|
425
|
+
actualSourcePath = actualEntry;
|
|
426
|
+
if (UpdateContext.DEBUG) {
|
|
427
|
+
Log.d("react-native-update", "Normalized match: " + fromPath + " -> " + actualEntry);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// 如果仍然找不到,尝试 drawable 密度降级 fallback
|
|
391
433
|
if (ze == null) {
|
|
392
434
|
if (UpdateContext.DEBUG) {
|
|
393
435
|
Log.d("react-native-update", "File not found in APK: " + fromPath + ", trying fallback");
|
|
@@ -405,28 +447,10 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, long[], Void> {
|
|
|
405
447
|
if (UpdateContext.DEBUG) {
|
|
406
448
|
Log.d("react-native-update", "Found toPath: " + toPath + " for fromPath: " + fromPath);
|
|
407
449
|
}
|
|
408
|
-
String fallbackFromPath = findDrawableFallback(toPath, copiesMap, availableEntries);
|
|
450
|
+
String fallbackFromPath = findDrawableFallback(toPath, copiesMap, availableEntries, normalizedEntryMap);
|
|
409
451
|
if (fallbackFromPath != null) {
|
|
410
452
|
ze = availableEntries.get(fallbackFromPath);
|
|
411
453
|
actualSourcePath = fallbackFromPath;
|
|
412
|
-
// 确保 fallback 路径也在 entryToZipFileMap 中
|
|
413
|
-
if (!entryToZipFileMap.containsKey(fallbackFromPath)) {
|
|
414
|
-
// 查找包含该 fallback 路径的 ZipFile
|
|
415
|
-
for (String apkPath : apkPaths) {
|
|
416
|
-
SafeZipFile testZipFile = zipFileMap.get(apkPath);
|
|
417
|
-
if (testZipFile != null) {
|
|
418
|
-
try {
|
|
419
|
-
ZipEntry testEntry = testZipFile.getEntry(fallbackFromPath);
|
|
420
|
-
if (testEntry != null) {
|
|
421
|
-
entryToZipFileMap.put(fallbackFromPath, testZipFile);
|
|
422
|
-
break;
|
|
423
|
-
}
|
|
424
|
-
} catch (Exception e) {
|
|
425
|
-
// 继续查找
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
454
|
if (UpdateContext.DEBUG) {
|
|
431
455
|
Log.w("react-native-update", "Using fallback: " + fallbackFromPath + " for " + fromPath);
|
|
432
456
|
}
|
|
@@ -68,7 +68,13 @@ export class DownloadTask {
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
if (!fileIo.accessSync(path)) {
|
|
71
|
-
|
|
71
|
+
try {
|
|
72
|
+
await fileIo.mkdir(path);
|
|
73
|
+
} catch (error) {
|
|
74
|
+
if (!fileIo.accessSync(path)) {
|
|
75
|
+
throw error;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
72
78
|
}
|
|
73
79
|
}
|
|
74
80
|
|
|
@@ -99,22 +105,14 @@ export class DownloadTask {
|
|
|
99
105
|
}
|
|
100
106
|
|
|
101
107
|
private async listEntryNames(directory: string): Promise<string[]> {
|
|
102
|
-
const entryNames: string[] = [];
|
|
103
108
|
const files = await fileIo.listFile(directory);
|
|
109
|
+
const validFiles = files.filter(file => file !== '.' && file !== '..');
|
|
104
110
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const filePath = `${directory}/${file}`;
|
|
111
|
-
const stat = await fileIo.stat(filePath);
|
|
112
|
-
if (!stat.isDirectory()) {
|
|
113
|
-
entryNames.push(file);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
111
|
+
const stats = await Promise.all(
|
|
112
|
+
validFiles.map(file => fileIo.stat(`${directory}/${file}`)),
|
|
113
|
+
);
|
|
116
114
|
|
|
117
|
-
return
|
|
115
|
+
return validFiles.filter((_, index) => !stats[index].isDirectory());
|
|
118
116
|
}
|
|
119
117
|
|
|
120
118
|
private async writeFileContent(
|
|
@@ -432,11 +430,10 @@ export class DownloadTask {
|
|
|
432
430
|
await this.recreateDirectory(params.unzipDirectory);
|
|
433
431
|
|
|
434
432
|
await zlib.decompressFile(params.targetFile, params.unzipDirectory);
|
|
435
|
-
const entryNames = await
|
|
436
|
-
|
|
437
|
-
params.unzipDirectory,
|
|
438
|
-
|
|
439
|
-
);
|
|
433
|
+
const [entryNames, manifestArrays] = await Promise.all([
|
|
434
|
+
this.listEntryNames(params.unzipDirectory),
|
|
435
|
+
this.readManifestArrays(params.unzipDirectory, true),
|
|
436
|
+
]);
|
|
440
437
|
|
|
441
438
|
NativePatchCore.buildArchivePatchPlan(
|
|
442
439
|
ARCHIVE_PATCH_TYPE_FROM_PACKAGE,
|
|
@@ -475,11 +472,10 @@ export class DownloadTask {
|
|
|
475
472
|
await this.recreateDirectory(params.unzipDirectory);
|
|
476
473
|
|
|
477
474
|
await zlib.decompressFile(params.targetFile, params.unzipDirectory);
|
|
478
|
-
const entryNames = await
|
|
479
|
-
|
|
480
|
-
params.unzipDirectory,
|
|
481
|
-
|
|
482
|
-
);
|
|
475
|
+
const [entryNames, manifestArrays] = await Promise.all([
|
|
476
|
+
this.listEntryNames(params.unzipDirectory),
|
|
477
|
+
this.readManifestArrays(params.unzipDirectory, false),
|
|
478
|
+
]);
|
|
483
479
|
|
|
484
480
|
const plan = NativePatchCore.buildArchivePatchPlan(
|
|
485
481
|
ARCHIVE_PATCH_TYPE_FROM_PPK,
|
|
@@ -524,23 +520,36 @@ export class DownloadTask {
|
|
|
524
520
|
.replace('resources/base/media/', '')
|
|
525
521
|
.split('.')[0];
|
|
526
522
|
const mediaBuffer = await resourceManager.getMediaByName(mediaName);
|
|
527
|
-
const
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
523
|
+
const parentDirs = [
|
|
524
|
+
...new Set(
|
|
525
|
+
targets.map(t => t.substring(0, t.lastIndexOf('/'))).filter(Boolean),
|
|
526
|
+
),
|
|
527
|
+
];
|
|
528
|
+
for (const dir of parentDirs) {
|
|
529
|
+
await this.ensureDirectory(dir);
|
|
531
530
|
}
|
|
531
|
+
await Promise.all(
|
|
532
|
+
targets.map(target => this.writeFileContent(target, mediaBuffer.buffer)),
|
|
533
|
+
);
|
|
532
534
|
continue;
|
|
533
535
|
}
|
|
534
536
|
const fromContent = await resourceManager.getRawFd(currentFrom);
|
|
535
537
|
const [firstTarget, ...restTargets] = targets;
|
|
536
|
-
|
|
538
|
+
const parentDirs = [
|
|
539
|
+
...new Set(
|
|
540
|
+
targets.map(t => t.substring(0, t.lastIndexOf('/'))).filter(Boolean),
|
|
541
|
+
),
|
|
542
|
+
];
|
|
543
|
+
for (const dir of parentDirs) {
|
|
544
|
+
await this.ensureDirectory(dir);
|
|
545
|
+
}
|
|
537
546
|
if (fileIo.accessSync(firstTarget)) {
|
|
538
547
|
await fileIo.unlink(firstTarget);
|
|
539
548
|
}
|
|
540
549
|
saveFileToSandbox(fromContent, firstTarget);
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
550
|
+
await Promise.all(
|
|
551
|
+
restTargets.map(target => this.copySandboxFile(firstTarget, target)),
|
|
552
|
+
);
|
|
544
553
|
}
|
|
545
554
|
} catch (error) {
|
|
546
555
|
error.message =
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import {
|
|
2
|
+
RNOHPackage,
|
|
3
|
+
UITurboModule,
|
|
4
|
+
UITurboModuleContext,
|
|
5
|
+
} from '@rnoh/react-native-openharmony';
|
|
6
|
+
import { PushyTurboModule } from './PushyTurboModule';
|
|
7
|
+
|
|
8
|
+
export class PushyPackage extends RNOHPackage {
|
|
9
|
+
override getUITurboModuleFactoryByNameMap(): Map<
|
|
10
|
+
string,
|
|
11
|
+
(ctx: UITurboModuleContext) => UITurboModule | null
|
|
12
|
+
> {
|
|
13
|
+
return new Map<string, (ctx: UITurboModuleContext) => UITurboModule>()
|
|
14
|
+
.set(PushyTurboModule.NAME, (ctx) => new PushyTurboModule(ctx));
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import {
|
|
2
|
+
RNPackage,
|
|
3
|
+
UITurboModule,
|
|
4
|
+
UITurboModuleContext,
|
|
5
|
+
} from '@rnoh/react-native-openharmony/ts';
|
|
6
|
+
import { PushyTurboModule } from './PushyTurboModule';
|
|
7
|
+
|
|
8
|
+
export class PushyPackage extends RNPackage {
|
|
9
|
+
override getUITurboModuleFactoryByNameMap(): Map<
|
|
10
|
+
string,
|
|
11
|
+
(ctx: UITurboModuleContext) => UITurboModule | null
|
|
12
|
+
> {
|
|
13
|
+
return new Map<string, (ctx: UITurboModuleContext) => UITurboModule>()
|
|
14
|
+
.set(PushyTurboModule.NAME, (ctx) => new PushyTurboModule(ctx));
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
UITurboModule,
|
|
3
|
+
UITurboModuleContext,
|
|
4
4
|
} from '@rnoh/react-native-openharmony/ts';
|
|
5
5
|
import common from '@ohos.app.ability.common';
|
|
6
6
|
import { bundleManager } from '@kit.AbilityKit';
|
|
@@ -12,11 +12,13 @@ import { util } from '@kit.ArkTS';
|
|
|
12
12
|
|
|
13
13
|
const TAG = 'PushyTurboModule';
|
|
14
14
|
|
|
15
|
-
export class PushyTurboModule extends
|
|
15
|
+
export class PushyTurboModule extends UITurboModule {
|
|
16
|
+
public static readonly NAME = 'Pushy';
|
|
17
|
+
|
|
16
18
|
mUiCtx: common.UIAbilityContext;
|
|
17
19
|
context: UpdateContext;
|
|
18
20
|
|
|
19
|
-
constructor(protected ctx:
|
|
21
|
+
constructor(protected ctx: UITurboModuleContext) {
|
|
20
22
|
super(ctx);
|
|
21
23
|
logger.debug(TAG, ',PushyTurboModule constructor');
|
|
22
24
|
this.mUiCtx = ctx.uiAbilityContext;
|
package/harmony/pushy.har
CHANGED
|
Binary file
|
package/package.json
CHANGED
|
@@ -91,9 +91,8 @@ Pod::Spec.new do |s|
|
|
|
91
91
|
s.source = { :git => 'https://github.com/reactnativecn/react-native-update.git', :tag => '#{s.version}' }
|
|
92
92
|
|
|
93
93
|
s.libraries = 'bz2', 'z'
|
|
94
|
-
s.vendored_libraries = 'RCTPushy/libRCTPushy.a'
|
|
95
94
|
s.pod_target_xcconfig = {
|
|
96
|
-
'USER_HEADER_SEARCH_PATHS' => "#{podspec_dir}/ios",
|
|
95
|
+
'USER_HEADER_SEARCH_PATHS' => "#{podspec_dir}/ios \"$(PODS_ROOT)/Headers/Public/SSZipArchive\" \"$(PODS_ROOT)/Headers/Public/React-Codegen/RCTPushySpec\"",
|
|
97
96
|
"DEFINES_MODULE" => "YES"
|
|
98
97
|
}
|
|
99
98
|
s.resource = 'ios/pushy_build_time.txt'
|
package/src/utils.ts
CHANGED
|
@@ -124,12 +124,6 @@ export const fetchWithTimeout = (
|
|
|
124
124
|
});
|
|
125
125
|
};
|
|
126
126
|
|
|
127
|
-
// export const isAndroid70AndBelow = () => {
|
|
128
|
-
// // android 7.0 and below devices do not support letsencrypt cert
|
|
129
|
-
// // https://letsencrypt.org/2023/07/10/cross-sign-expiration/
|
|
130
|
-
// return Platform.OS === 'android' && Platform.Version <= 24;
|
|
131
|
-
// };
|
|
132
|
-
|
|
133
127
|
export const enhancedFetch = async (
|
|
134
128
|
url: string,
|
|
135
129
|
params: Parameters<typeof fetch>[1],
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { RNPackage, TurboModulesFactory } from '@rnoh/react-native-openharmony/ts';
|
|
2
|
-
import type { TurboModule, TurboModuleContext } from '@rnoh/react-native-openharmony/ts';
|
|
3
|
-
import { PushyTurboModule } from './PushyTurboModule';
|
|
4
|
-
|
|
5
|
-
class PushyTurboModulesFactory extends TurboModulesFactory {
|
|
6
|
-
createTurboModule(name: string): TurboModule | null {
|
|
7
|
-
if (name === 'Pushy') {
|
|
8
|
-
return new PushyTurboModule(this.ctx);
|
|
9
|
-
}
|
|
10
|
-
return null;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
hasTurboModule(name: string): boolean {
|
|
14
|
-
return name === 'Pushy';
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export class PushyPackage extends RNPackage {
|
|
19
|
-
createTurboModulesFactory(ctx: TurboModuleContext): TurboModulesFactory {
|
|
20
|
-
return new PushyTurboModulesFactory(ctx);
|
|
21
|
-
}
|
|
22
|
-
}
|