expo-splash-screen2 1.2.0 → 1.3.0
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/package.json +1 -1
- package/plugin/build/index.js +721 -1499
package/plugin/build/index.js
CHANGED
|
@@ -39,7 +39,6 @@ const fs = __importStar(require("fs"));
|
|
|
39
39
|
const path = __importStar(require("path"));
|
|
40
40
|
const child_process_1 = require("child_process");
|
|
41
41
|
const android_1 = require("./templates/android");
|
|
42
|
-
const ios_1 = require("./templates/ios");
|
|
43
42
|
const CUSTOM_SPLASH_ACTIVITY_NAME = 'SplashScreen2Activity';
|
|
44
43
|
const STORYBOARD_FILE_PATH = './SplashScreen.storyboard';
|
|
45
44
|
const STORYBOARD_MOD_NAME = 'splashScreenStoryboard';
|
|
@@ -364,31 +363,6 @@ function createSplashScreenLogoForNormalMode(projectRoot, androidMainPath, image
|
|
|
364
363
|
console.error(`[expo-splash-screen2] Error creating splashscreen_logo for normal mode: ${error}`);
|
|
365
364
|
}
|
|
366
365
|
}
|
|
367
|
-
function createBackgroundDrawable(androidResPath, backgroundColor) {
|
|
368
|
-
const drawableDir = path.join(androidResPath, 'res', 'drawable');
|
|
369
|
-
if (!fs.existsSync(drawableDir)) {
|
|
370
|
-
fs.mkdirSync(drawableDir, { recursive: true });
|
|
371
|
-
}
|
|
372
|
-
const xmlPath = path.join(drawableDir, 'splash_html_background.xml');
|
|
373
|
-
const xmlContent = `<?xml version="1.0" encoding="utf-8"?>
|
|
374
|
-
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
|
375
|
-
<item>
|
|
376
|
-
<color android:color="${backgroundColor}" />
|
|
377
|
-
</item>
|
|
378
|
-
<item>
|
|
379
|
-
<bitmap
|
|
380
|
-
android:gravity="center"
|
|
381
|
-
android:src="@drawable/splash_icon" />
|
|
382
|
-
</item>
|
|
383
|
-
</layer-list>
|
|
384
|
-
`;
|
|
385
|
-
try {
|
|
386
|
-
fs.writeFileSync(xmlPath, xmlContent);
|
|
387
|
-
}
|
|
388
|
-
catch (error) {
|
|
389
|
-
console.error(`[expo-splash-screen2] Error creating background drawable: ${error}`);
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
366
|
function removeHashFromFileName(fileName) {
|
|
393
367
|
const hashPattern = /^(.+)\.([0-9a-f]{32,})\.([^.]+)$/i;
|
|
394
368
|
const match = fileName.match(hashPattern);
|
|
@@ -766,13 +740,6 @@ function modifyMainActivityForImageMode(content, packageName, imageResourceName)
|
|
|
766
740
|
console.warn('[expo-splash-screen2] MainActivity class not found');
|
|
767
741
|
return content;
|
|
768
742
|
}
|
|
769
|
-
const importsToAdd = `
|
|
770
|
-
import android.os.Handler
|
|
771
|
-
import android.os.Looper
|
|
772
|
-
import android.view.View
|
|
773
|
-
import android.view.ViewGroup
|
|
774
|
-
import android.widget.ImageView
|
|
775
|
-
import android.graphics.drawable.Drawable`;
|
|
776
743
|
let hasImports = content.includes('import android.os.Handler') &&
|
|
777
744
|
content.includes('import android.os.Looper') &&
|
|
778
745
|
content.includes('import android.view.View') &&
|
|
@@ -844,7 +811,7 @@ import android.graphics.drawable.Drawable`;
|
|
|
844
811
|
android.util.Log.e("MainActivity", "Error setting background drawable", e)
|
|
845
812
|
}
|
|
846
813
|
}
|
|
847
|
-
|
|
814
|
+
|
|
848
815
|
// Create ImageView, display .9 patch background
|
|
849
816
|
// Use FIT_XY scaleType to ensure .9 patch correctly stretches and fills, completely consistent with system splash screen
|
|
850
817
|
val imageView = ImageView(this).apply {
|
|
@@ -885,6 +852,18 @@ import android.graphics.drawable.Drawable`;
|
|
|
885
852
|
fun preventAutoHide() {
|
|
886
853
|
preventAutoHide = true
|
|
887
854
|
android.util.Log.d("MainActivity", "preventAutoHide called, preventAutoHide: $preventAutoHide")
|
|
855
|
+
// Show splash image view when preventAutoHide is called
|
|
856
|
+
Handler(Looper.getMainLooper()).post {
|
|
857
|
+
setupSplashImageView()
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
override fun onContentChanged() {
|
|
862
|
+
super.onContentChanged()
|
|
863
|
+
// Show splash image view when content changed
|
|
864
|
+
Handler(Looper.getMainLooper()).post {
|
|
865
|
+
setupSplashImageView()
|
|
866
|
+
}
|
|
888
867
|
}
|
|
889
868
|
|
|
890
869
|
fun hideSplashImageViewContainer(force: Boolean = false) {
|
|
@@ -927,21 +906,17 @@ import android.graphics.drawable.Drawable`;
|
|
|
927
906
|
}
|
|
928
907
|
}
|
|
929
908
|
const onCreateContent = modifiedContent.substring(onCreateIndex, onCreateEndIndex);
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
setupCall + '\n' +
|
|
942
|
-
modifiedContent.substring(onCreateIndex + superOnCreateEndIndex + 1);
|
|
943
|
-
}
|
|
944
|
-
}
|
|
909
|
+
let cleanedOnCreateContent = onCreateContent;
|
|
910
|
+
const themeCommentRegex = /(\s*\/\/\s*Set the theme to AppTheme[^\n]*\n)+(\s*\/\/\s*[^\n]*\n)*(\s*\/\/\s*This is required for expo-splash-screen[^\n]*\n)?/g;
|
|
911
|
+
cleanedOnCreateContent = cleanedOnCreateContent.replace(themeCommentRegex, '');
|
|
912
|
+
const setThemeRegex = /(\s*\/\/\s*[^\n]*\n)*\s*setTheme\s*\(\s*R\.style\.AppTheme\s*\)\s*;?\s*\n?/g;
|
|
913
|
+
cleanedOnCreateContent = cleanedOnCreateContent.replace(setThemeRegex, '');
|
|
914
|
+
const setupSplashRegex = /(\s*\/\/\s*[^\n]*\n)*\s*Handler\s*\(\s*Looper\.getMainLooper\(\)\s*\)\.post\s*\{[\s\S]*?setupSplashImageView\s*\(\)[\s\S]*?\}\s*/g;
|
|
915
|
+
cleanedOnCreateContent = cleanedOnCreateContent.replace(setupSplashRegex, '');
|
|
916
|
+
if (cleanedOnCreateContent !== onCreateContent) {
|
|
917
|
+
modifiedContent = modifiedContent.substring(0, onCreateIndex) +
|
|
918
|
+
cleanedOnCreateContent +
|
|
919
|
+
modifiedContent.substring(onCreateEndIndex);
|
|
945
920
|
}
|
|
946
921
|
}
|
|
947
922
|
const classIndex = modifiedContent.indexOf(classMatch[0]) + classMatch[0].length;
|
|
@@ -1454,21 +1429,55 @@ function modifyMainActivity(content, packageName, backgroundColor) {
|
|
|
1454
1429
|
if (!hasWebViewCode || !hasCompanionObject) {
|
|
1455
1430
|
const classIndex = modifiedContent.indexOf(classMatch[0]) + classMatch[0].length;
|
|
1456
1431
|
const firstMethodMatch = modifiedContent.substring(classIndex).match(/\s+(override\s+)?fun\s+/);
|
|
1457
|
-
if (
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1432
|
+
if (!hasWebViewCode) {
|
|
1433
|
+
if (firstMethodMatch) {
|
|
1434
|
+
const insertIndex = classIndex + firstMethodMatch.index;
|
|
1435
|
+
modifiedContent = (modifiedContent.substring(0, insertIndex) +
|
|
1436
|
+
webViewCode +
|
|
1437
|
+
'\n' +
|
|
1438
|
+
modifiedContent.substring(insertIndex));
|
|
1439
|
+
}
|
|
1440
|
+
else {
|
|
1441
|
+
const lastBraceIndex = modifiedContent.lastIndexOf('}');
|
|
1442
|
+
modifiedContent = (modifiedContent.substring(0, lastBraceIndex) +
|
|
1443
|
+
webViewCode +
|
|
1444
|
+
'\n' +
|
|
1445
|
+
modifiedContent.substring(lastBraceIndex));
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
if (!hasCompanionObject) {
|
|
1449
|
+
let braceCount = 0;
|
|
1450
|
+
let classStartIndex = modifiedContent.indexOf(classMatch[0]);
|
|
1451
|
+
let foundClassStart = false;
|
|
1452
|
+
let classEndIndex = -1;
|
|
1453
|
+
for (let i = classStartIndex; i < modifiedContent.length; i++) {
|
|
1454
|
+
if (modifiedContent[i] === '{') {
|
|
1455
|
+
braceCount++;
|
|
1456
|
+
foundClassStart = true;
|
|
1457
|
+
}
|
|
1458
|
+
else if (modifiedContent[i] === '}') {
|
|
1459
|
+
braceCount--;
|
|
1460
|
+
if (foundClassStart && braceCount === 0) {
|
|
1461
|
+
classEndIndex = i;
|
|
1462
|
+
break;
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
if (classEndIndex !== -1) {
|
|
1467
|
+
modifiedContent = (modifiedContent.substring(0, classEndIndex) +
|
|
1468
|
+
'\n' +
|
|
1469
|
+
companionObjectCode +
|
|
1470
|
+
'\n' +
|
|
1471
|
+
modifiedContent.substring(classEndIndex));
|
|
1472
|
+
}
|
|
1473
|
+
else {
|
|
1474
|
+
const lastBraceIndex = modifiedContent.lastIndexOf('}');
|
|
1475
|
+
modifiedContent = (modifiedContent.substring(0, lastBraceIndex) +
|
|
1476
|
+
'\n' +
|
|
1477
|
+
companionObjectCode +
|
|
1478
|
+
'\n' +
|
|
1479
|
+
modifiedContent.substring(lastBraceIndex));
|
|
1480
|
+
}
|
|
1472
1481
|
}
|
|
1473
1482
|
}
|
|
1474
1483
|
if (modifiedContent.includes('设置背景色为传入的 backgroundColor')) {
|
|
@@ -1482,68 +1491,525 @@ function modifyMainActivity(content, packageName, backgroundColor) {
|
|
|
1482
1491
|
return modifiedContent;
|
|
1483
1492
|
}
|
|
1484
1493
|
function modifyMainActivityForBlendMode(content, packageName, imageResourceName) {
|
|
1494
|
+
if (content.includes('splashImageViewContainer') && content.includes('setupSplashImageView') &&
|
|
1495
|
+
content.includes('onContentChanged') && content.includes('setupWebViewContainer')) {
|
|
1496
|
+
return content;
|
|
1497
|
+
}
|
|
1485
1498
|
let modifiedContent = modifyMainActivity(content, packageName, '#ffffff');
|
|
1486
|
-
|
|
1499
|
+
const fitsSystemWindowsPattern = /(\s*\/\/\s*Ensure container is not affected by system window insets[^\n]*\n\s*fitsSystemWindows\s*=\s*false\s*)(\s*\/\/\s*Set \.9 patch image as container background[\s\S]*?android\.util\.Log\.e\("MainActivity", "Error setting background drawable", e\)[\s\S]*?\}\s*)(\n\s*\})/;
|
|
1500
|
+
if (fitsSystemWindowsPattern.test(modifiedContent)) {
|
|
1501
|
+
modifiedContent = modifiedContent.replace(fitsSystemWindowsPattern, '$1$3');
|
|
1502
|
+
}
|
|
1503
|
+
const onCreateMatch = modifiedContent.match(/override\s+fun\s+onCreate\s*\([^)]*\)\s*\{/);
|
|
1504
|
+
if (onCreateMatch) {
|
|
1505
|
+
const onCreateIndex = modifiedContent.indexOf(onCreateMatch[0]);
|
|
1506
|
+
let braceCount = 0;
|
|
1507
|
+
let onCreateEndIndex = onCreateIndex + onCreateMatch[0].length;
|
|
1508
|
+
let foundStart = false;
|
|
1509
|
+
for (let i = onCreateIndex; i < modifiedContent.length; i++) {
|
|
1510
|
+
if (modifiedContent[i] === '{') {
|
|
1511
|
+
braceCount++;
|
|
1512
|
+
foundStart = true;
|
|
1513
|
+
}
|
|
1514
|
+
else if (modifiedContent[i] === '}') {
|
|
1515
|
+
braceCount--;
|
|
1516
|
+
if (foundStart && braceCount === 0) {
|
|
1517
|
+
onCreateEndIndex = i + 1;
|
|
1518
|
+
break;
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
const onCreateContent = modifiedContent.substring(onCreateIndex, onCreateEndIndex);
|
|
1523
|
+
let cleanedOnCreateContent = onCreateContent;
|
|
1524
|
+
const setupWebViewRegex = /(\s*\/\/\s*[^\n]*\n)*\s*(WindowCompat\.setDecorFitsSystemWindows|setupWebViewContainer|Handler\s*\(\s*Looper\.getMainLooper\(\)\s*\)\.post)[\s\S]*?\}\s*/g;
|
|
1525
|
+
cleanedOnCreateContent = cleanedOnCreateContent.replace(setupWebViewRegex, '');
|
|
1526
|
+
if (cleanedOnCreateContent !== onCreateContent) {
|
|
1527
|
+
modifiedContent = modifiedContent.substring(0, onCreateIndex) +
|
|
1528
|
+
cleanedOnCreateContent +
|
|
1529
|
+
modifiedContent.substring(onCreateEndIndex);
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
const classMatch = modifiedContent.match(/class\s+MainActivity\s*[^:]*:/);
|
|
1533
|
+
if (!classMatch) {
|
|
1534
|
+
console.warn('[expo-splash-screen2] MainActivity class not found');
|
|
1487
1535
|
return modifiedContent;
|
|
1488
1536
|
}
|
|
1489
|
-
const
|
|
1490
|
-
|
|
1491
|
-
|
|
1537
|
+
const importsToAdd = [
|
|
1538
|
+
'import android.os.Handler',
|
|
1539
|
+
'import android.os.Looper',
|
|
1540
|
+
'import android.view.View',
|
|
1541
|
+
'import android.view.ViewGroup',
|
|
1542
|
+
'import android.widget.ImageView'
|
|
1543
|
+
];
|
|
1544
|
+
let hasImports = importsToAdd.every(imp => modifiedContent.includes(imp));
|
|
1545
|
+
if (!hasImports) {
|
|
1546
|
+
const missingImports = importsToAdd.filter(imp => !modifiedContent.includes(imp));
|
|
1547
|
+
if (missingImports.length > 0) {
|
|
1548
|
+
const lastImportIndex = modifiedContent.lastIndexOf('import ');
|
|
1549
|
+
if (lastImportIndex !== -1) {
|
|
1550
|
+
const nextLineIndex = modifiedContent.indexOf('\n', lastImportIndex);
|
|
1551
|
+
if (nextLineIndex !== -1) {
|
|
1552
|
+
const missingImportsText = missingImports.join('\n') + '\n';
|
|
1553
|
+
modifiedContent = modifiedContent.substring(0, nextLineIndex + 1) +
|
|
1554
|
+
missingImportsText +
|
|
1555
|
+
modifiedContent.substring(nextLineIndex + 1);
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
}
|
|
1560
|
+
const imageViewCode = `
|
|
1561
|
+
private var splashImageViewContainer: ViewGroup? = null
|
|
1562
|
+
private var preventAutoHideImageView = false
|
|
1563
|
+
|
|
1564
|
+
private fun setupSplashImageView() {
|
|
1565
|
+
try {
|
|
1566
|
+
// If container already exists, return directly
|
|
1567
|
+
if (splashImageViewContainer != null) {
|
|
1568
|
+
android.util.Log.d("MainActivity", "Splash ImageView container already exists")
|
|
1569
|
+
return
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1572
|
+
android.util.Log.d("MainActivity", "Creating splash ImageView container")
|
|
1573
|
+
|
|
1574
|
+
// Create container
|
|
1575
|
+
splashImageViewContainer = object : ViewGroup(this) {
|
|
1576
|
+
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
|
|
1577
|
+
val childCount = childCount
|
|
1578
|
+
val width = r - l
|
|
1579
|
+
val height = b - t
|
|
1580
|
+
for (i in 0 until childCount) {
|
|
1581
|
+
val child = getChildAt(i)
|
|
1582
|
+
child.layout(0, 0, width, height)
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
}.apply {
|
|
1586
|
+
layoutParams = ViewGroup.LayoutParams(
|
|
1587
|
+
ViewGroup.LayoutParams.MATCH_PARENT,
|
|
1588
|
+
ViewGroup.LayoutParams.MATCH_PARENT
|
|
1589
|
+
)
|
|
1590
|
+
// Set background to .9 patch image, ensure consistency with system splash screen
|
|
1492
1591
|
try {
|
|
1493
1592
|
val drawable = resources.getDrawable(
|
|
1494
1593
|
resources.getIdentifier("${imageResourceName}", "drawable", packageName),
|
|
1495
1594
|
null
|
|
1496
1595
|
)
|
|
1497
|
-
|
|
1596
|
+
background = drawable
|
|
1498
1597
|
} catch (e: Exception) {
|
|
1499
1598
|
android.util.Log.e("MainActivity", "Error setting background drawable", e)
|
|
1500
|
-
}
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1602
|
+
// Create ImageView, display .9 patch background
|
|
1603
|
+
// Use FIT_XY scaleType to ensure .9 patch correctly stretches and fills, completely consistent with system splash screen
|
|
1604
|
+
val imageView = ImageView(this).apply {
|
|
1605
|
+
layoutParams = ViewGroup.LayoutParams(
|
|
1606
|
+
ViewGroup.LayoutParams.MATCH_PARENT,
|
|
1607
|
+
ViewGroup.LayoutParams.MATCH_PARENT
|
|
1608
|
+
)
|
|
1609
|
+
// For .9 patch images, use FIT_XY to ensure complete fill, .9 patch stretch areas will work correctly
|
|
1610
|
+
scaleType = ImageView.ScaleType.FIT_XY
|
|
1611
|
+
try {
|
|
1612
|
+
val drawable = resources.getDrawable(
|
|
1613
|
+
resources.getIdentifier("${imageResourceName}", "drawable", packageName),
|
|
1614
|
+
null
|
|
1615
|
+
)
|
|
1616
|
+
setImageDrawable(drawable)
|
|
1617
|
+
} catch (e: Exception) {
|
|
1618
|
+
android.util.Log.e("MainActivity", "Error setting image drawable", e)
|
|
1619
|
+
}
|
|
1620
|
+
visibility = View.VISIBLE
|
|
1621
|
+
}
|
|
1622
|
+
|
|
1623
|
+
splashImageViewContainer?.addView(imageView)
|
|
1624
|
+
|
|
1625
|
+
// Use window.decorView to ensure on top layer (but below WebView container)
|
|
1626
|
+
val decorView = window.decorView as? ViewGroup
|
|
1627
|
+
if (decorView != null) {
|
|
1628
|
+
decorView.addView(splashImageViewContainer)
|
|
1629
|
+
// ImageView container should be below WebView container
|
|
1630
|
+
splashImageViewContainer?.visibility = View.VISIBLE
|
|
1631
|
+
splashImageViewContainer?.elevation = Float.MAX_VALUE - 1 // Below WebView container
|
|
1632
|
+
android.util.Log.d("MainActivity", "Splash ImageView container added to decorView")
|
|
1633
|
+
}
|
|
1634
|
+
} catch (e: Exception) {
|
|
1635
|
+
android.util.Log.e("MainActivity", "Error creating splash ImageView container", e)
|
|
1510
1636
|
}
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
});
|
|
1637
|
+
}
|
|
1638
|
+
|
|
1639
|
+
fun hideSplashImageViewContainer(force: Boolean = false) {
|
|
1515
1640
|
try {
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1641
|
+
// If preventAutoHideImageView is true and not force hide, don't execute hide operation
|
|
1642
|
+
if (preventAutoHideImageView && !force) {
|
|
1643
|
+
android.util.Log.d("MainActivity", "hideSplashImageViewContainer prevented by preventAutoHideImageView flag")
|
|
1644
|
+
return
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1647
|
+
val parent = splashImageViewContainer?.parent as? ViewGroup
|
|
1648
|
+
parent?.removeView(splashImageViewContainer)
|
|
1649
|
+
|
|
1650
|
+
splashImageViewContainer?.visibility = View.GONE
|
|
1651
|
+
splashImageViewContainer?.removeAllViews()
|
|
1652
|
+
splashImageViewContainer = null
|
|
1653
|
+
preventAutoHideImageView = false
|
|
1654
|
+
android.util.Log.d("MainActivity", "Splash ImageView container hidden")
|
|
1655
|
+
} catch (e: Exception) {
|
|
1656
|
+
android.util.Log.e("MainActivity", "Error hiding splash ImageView container", e)
|
|
1529
1657
|
}
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1658
|
+
}`;
|
|
1659
|
+
const classIndex = modifiedContent.indexOf(classMatch[0]) + classMatch[0].length;
|
|
1660
|
+
const afterClass = modifiedContent.substring(classIndex);
|
|
1661
|
+
const companionObjectMatch = afterClass.match(/companion\s+object\s*\{/);
|
|
1662
|
+
if (companionObjectMatch) {
|
|
1663
|
+
const companionIndex = classIndex + companionObjectMatch.index;
|
|
1664
|
+
modifiedContent = modifiedContent.substring(0, companionIndex) +
|
|
1665
|
+
imageViewCode +
|
|
1666
|
+
'\n' +
|
|
1667
|
+
modifiedContent.substring(companionIndex);
|
|
1540
1668
|
}
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1669
|
+
else {
|
|
1670
|
+
const firstMethodMatch = afterClass.match(/\s+(override\s+)?fun\s+/);
|
|
1671
|
+
if (firstMethodMatch) {
|
|
1672
|
+
const insertIndex = classIndex + firstMethodMatch.index;
|
|
1673
|
+
modifiedContent = modifiedContent.substring(0, insertIndex) +
|
|
1674
|
+
imageViewCode +
|
|
1675
|
+
'\n' +
|
|
1676
|
+
modifiedContent.substring(insertIndex);
|
|
1677
|
+
}
|
|
1678
|
+
else {
|
|
1679
|
+
const lastBraceIndex = modifiedContent.lastIndexOf('}');
|
|
1680
|
+
modifiedContent = modifiedContent.substring(0, lastBraceIndex) +
|
|
1681
|
+
imageViewCode +
|
|
1682
|
+
'\n' +
|
|
1683
|
+
modifiedContent.substring(lastBraceIndex);
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1686
|
+
const preventAutoHideMethodStart = modifiedContent.indexOf('fun preventAutoHide()');
|
|
1687
|
+
if (preventAutoHideMethodStart !== -1) {
|
|
1688
|
+
const methodSignatureEnd = modifiedContent.indexOf('{', preventAutoHideMethodStart);
|
|
1689
|
+
if (methodSignatureEnd !== -1) {
|
|
1690
|
+
let braceCount = 0;
|
|
1691
|
+
let methodEnd = methodSignatureEnd + 1;
|
|
1692
|
+
let foundStart = false;
|
|
1693
|
+
for (let i = methodSignatureEnd; i < modifiedContent.length; i++) {
|
|
1694
|
+
if (modifiedContent[i] === '{') {
|
|
1695
|
+
braceCount++;
|
|
1696
|
+
foundStart = true;
|
|
1697
|
+
}
|
|
1698
|
+
else if (modifiedContent[i] === '}') {
|
|
1699
|
+
braceCount--;
|
|
1700
|
+
if (foundStart && braceCount === 0) {
|
|
1701
|
+
methodEnd = i + 1;
|
|
1702
|
+
break;
|
|
1703
|
+
}
|
|
1704
|
+
}
|
|
1705
|
+
}
|
|
1706
|
+
const preventAutoHideCode = `
|
|
1707
|
+
fun preventAutoHide() {
|
|
1708
|
+
runOnUiThread {
|
|
1709
|
+
preventAutoHide = true
|
|
1710
|
+
preventAutoHideImageView = true
|
|
1711
|
+
android.util.Log.d("MainActivity", "preventAutoHide called, preventAutoHide: $preventAutoHide, preventAutoHideImageView: $preventAutoHideImageView")
|
|
1712
|
+
// Show ImageView container
|
|
1713
|
+
Handler(Looper.getMainLooper()).post {
|
|
1714
|
+
setupSplashImageView()
|
|
1715
|
+
}
|
|
1716
|
+
// If WebView container doesn't exist, create it
|
|
1717
|
+
if (webViewContainer == null) {
|
|
1718
|
+
android.util.Log.d("MainActivity", "WebView container is null, creating it")
|
|
1719
|
+
setupWebViewContainer()
|
|
1720
|
+
}
|
|
1544
1721
|
}
|
|
1545
|
-
|
|
1546
|
-
|
|
1722
|
+
}`;
|
|
1723
|
+
modifiedContent = modifiedContent.substring(0, preventAutoHideMethodStart) +
|
|
1724
|
+
preventAutoHideCode +
|
|
1725
|
+
modifiedContent.substring(methodEnd);
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
const hideWebViewMethodStart = modifiedContent.indexOf('private fun hideWebViewContainerInternal');
|
|
1729
|
+
if (hideWebViewMethodStart !== -1) {
|
|
1730
|
+
const methodSignatureEnd = modifiedContent.indexOf('{', hideWebViewMethodStart);
|
|
1731
|
+
if (methodSignatureEnd !== -1) {
|
|
1732
|
+
let braceCount = 0;
|
|
1733
|
+
let methodEnd = methodSignatureEnd + 1;
|
|
1734
|
+
let foundStart = false;
|
|
1735
|
+
for (let i = methodSignatureEnd; i < modifiedContent.length; i++) {
|
|
1736
|
+
if (modifiedContent[i] === '{') {
|
|
1737
|
+
braceCount++;
|
|
1738
|
+
foundStart = true;
|
|
1739
|
+
}
|
|
1740
|
+
else if (modifiedContent[i] === '}') {
|
|
1741
|
+
braceCount--;
|
|
1742
|
+
if (foundStart && braceCount === 0) {
|
|
1743
|
+
methodEnd = i + 1;
|
|
1744
|
+
break;
|
|
1745
|
+
}
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
1748
|
+
const hideWebViewMatch = [modifiedContent.substring(hideWebViewMethodStart, methodEnd)];
|
|
1749
|
+
if (hideWebViewMatch[0]) {
|
|
1750
|
+
const hideWebViewCode = `
|
|
1751
|
+
private fun hideWebViewContainerInternal(force: Boolean = false) {
|
|
1752
|
+
try {
|
|
1753
|
+
android.util.Log.d("MainActivity", "hideWebViewContainer called, force=$force, preventAutoHide=$preventAutoHide, webViewContainer=\${webViewContainer != null}")
|
|
1754
|
+
|
|
1755
|
+
// If preventAutoHide is true and not force hide, don't execute hide operation
|
|
1756
|
+
if (preventAutoHide && !force) {
|
|
1757
|
+
android.util.Log.d("MainActivity", "hideWebViewContainer prevented by preventAutoHide flag")
|
|
1758
|
+
return
|
|
1759
|
+
}
|
|
1760
|
+
|
|
1761
|
+
// Hide WebView container
|
|
1762
|
+
if (webViewContainer != null) {
|
|
1763
|
+
android.util.Log.d("MainActivity", "Hiding MainActivity WebView container")
|
|
1764
|
+
|
|
1765
|
+
// First set visibility to GONE to ensure invisible
|
|
1766
|
+
webViewContainer?.visibility = View.GONE
|
|
1767
|
+
|
|
1768
|
+
// Try to remove from parent view
|
|
1769
|
+
val parent = webViewContainer?.parent as? ViewGroup
|
|
1770
|
+
if (parent != null) {
|
|
1771
|
+
try {
|
|
1772
|
+
parent.removeView(webViewContainer)
|
|
1773
|
+
android.util.Log.d("MainActivity", "WebView container removed from parent")
|
|
1774
|
+
} catch (e: Exception) {
|
|
1775
|
+
android.util.Log.e("MainActivity", "Error removing WebView container from parent", e)
|
|
1776
|
+
}
|
|
1777
|
+
} else {
|
|
1778
|
+
android.util.Log.d("MainActivity", "WebView container has no parent, trying to remove from decorView")
|
|
1779
|
+
// If parent is null, try to remove directly from decorView
|
|
1780
|
+
try {
|
|
1781
|
+
val decorView = window.decorView as? ViewGroup
|
|
1782
|
+
decorView?.removeView(webViewContainer)
|
|
1783
|
+
android.util.Log.d("MainActivity", "WebView container removed from decorView")
|
|
1784
|
+
} catch (e: Exception) {
|
|
1785
|
+
android.util.Log.e("MainActivity", "Error removing WebView container from decorView", e)
|
|
1786
|
+
}
|
|
1787
|
+
}
|
|
1788
|
+
|
|
1789
|
+
// Clean up all child views
|
|
1790
|
+
webViewContainer?.removeAllViews()
|
|
1791
|
+
|
|
1792
|
+
// Clear reference
|
|
1793
|
+
webViewContainer = null
|
|
1794
|
+
android.util.Log.d("MainActivity", "MainActivity WebView container hidden and removed")
|
|
1795
|
+
} else {
|
|
1796
|
+
android.util.Log.d("MainActivity", "MainActivity WebView container is null, skipping")
|
|
1797
|
+
}
|
|
1798
|
+
|
|
1799
|
+
// Also hide ImageView container
|
|
1800
|
+
hideSplashImageViewContainer(force)
|
|
1801
|
+
|
|
1802
|
+
// Also hide SplashScreen2Activity's WebView container
|
|
1803
|
+
// Even if SplashScreen2Activity has finish(), instance may still exist, container may still be on window
|
|
1804
|
+
try {
|
|
1805
|
+
val customSplashActivity = SplashScreen2Activity.getInstance()
|
|
1806
|
+
if (customSplashActivity != null) {
|
|
1807
|
+
android.util.Log.d("MainActivity", "Hiding SplashScreen2Activity WebView container")
|
|
1808
|
+
customSplashActivity.hideWebViewContainer(force)
|
|
1809
|
+
} else {
|
|
1810
|
+
android.util.Log.d("MainActivity", "SplashScreen2Activity instance is null (already finished or not created)")
|
|
1811
|
+
}
|
|
1812
|
+
} catch (e: Exception) {
|
|
1813
|
+
android.util.Log.d("MainActivity", "SplashScreen2Activity not available: " + e.message)
|
|
1814
|
+
}
|
|
1815
|
+
|
|
1816
|
+
// Manually set MainActivity theme to @style/AppTheme after hiding splash screen
|
|
1817
|
+
try {
|
|
1818
|
+
setTheme(R.style.AppTheme)
|
|
1819
|
+
android.util.Log.d("MainActivity", "MainActivity theme set to @style/AppTheme")
|
|
1820
|
+
} catch (e: Exception) {
|
|
1821
|
+
android.util.Log.e("MainActivity", "Error setting theme to AppTheme", e)
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1824
|
+
// Reset preventAutoHide flag (regardless of whether hide was successful)
|
|
1825
|
+
preventAutoHide = false
|
|
1826
|
+
android.util.Log.d("MainActivity", "preventAutoHide reset to false")
|
|
1827
|
+
} catch (e: Exception) {
|
|
1828
|
+
android.util.Log.e("MainActivity", "Error hiding WebView container", e)
|
|
1829
|
+
e.printStackTrace()
|
|
1830
|
+
// Even if error occurs, reset preventAutoHide
|
|
1831
|
+
preventAutoHide = false
|
|
1832
|
+
}
|
|
1833
|
+
}`;
|
|
1834
|
+
modifiedContent = modifiedContent.substring(0, hideWebViewMethodStart) +
|
|
1835
|
+
hideWebViewCode +
|
|
1836
|
+
modifiedContent.substring(methodEnd);
|
|
1837
|
+
}
|
|
1838
|
+
}
|
|
1839
|
+
}
|
|
1840
|
+
const onContentChangedCode = `
|
|
1841
|
+
override fun onContentChanged() {
|
|
1842
|
+
super.onContentChanged()
|
|
1843
|
+
// Show ImageView container and WebView container when content changed
|
|
1844
|
+
// ImageView container (.9图背景) should be added first, then WebView container (透明背景) on top
|
|
1845
|
+
Handler(Looper.getMainLooper()).post {
|
|
1846
|
+
setupSplashImageView()
|
|
1847
|
+
// Small delay to ensure ImageView container is added first
|
|
1848
|
+
Handler(Looper.getMainLooper()).postDelayed({
|
|
1849
|
+
setupWebViewContainer()
|
|
1850
|
+
}, 50)
|
|
1851
|
+
}
|
|
1852
|
+
}`;
|
|
1853
|
+
const onCreateMatchForStatusBar = modifiedContent.match(/override\s+fun\s+onCreate\s*\([^)]*\)\s*\{/);
|
|
1854
|
+
if (onCreateMatchForStatusBar) {
|
|
1855
|
+
const onCreateIndex = modifiedContent.indexOf(onCreateMatchForStatusBar[0]);
|
|
1856
|
+
let braceCount = 0;
|
|
1857
|
+
let onCreateEndIndex = onCreateIndex + onCreateMatchForStatusBar[0].length;
|
|
1858
|
+
let foundStart = false;
|
|
1859
|
+
for (let i = onCreateIndex; i < modifiedContent.length; i++) {
|
|
1860
|
+
if (modifiedContent[i] === '{') {
|
|
1861
|
+
braceCount++;
|
|
1862
|
+
foundStart = true;
|
|
1863
|
+
}
|
|
1864
|
+
else if (modifiedContent[i] === '}') {
|
|
1865
|
+
braceCount--;
|
|
1866
|
+
if (foundStart && braceCount === 0) {
|
|
1867
|
+
onCreateEndIndex = i + 1;
|
|
1868
|
+
break;
|
|
1869
|
+
}
|
|
1870
|
+
}
|
|
1871
|
+
}
|
|
1872
|
+
const onCreateContent = modifiedContent.substring(onCreateIndex, onCreateEndIndex);
|
|
1873
|
+
const isCompressed = /override\s+fun\s+onCreate\s*\([^)]*\)\s*\{[^}]*super\.onCreate[^}]*window\.statusBarColor[^}]*window\.navigationBarColor[^}]*\}/.test(onCreateContent) &&
|
|
1874
|
+
!onCreateContent.includes('\n super.onCreate') &&
|
|
1875
|
+
!onCreateContent.includes('\n window.statusBarColor');
|
|
1876
|
+
if (isCompressed) {
|
|
1877
|
+
const superOnCreateMatch = onCreateContent.match(/super\.onCreate\([^)]*\)/);
|
|
1878
|
+
const statusBarMatch = onCreateContent.match(/window\.statusBarColor\s*=\s*[^}\n]+/);
|
|
1879
|
+
const navBarMatch = onCreateContent.match(/window\.navigationBarColor\s*=\s*[^}\n]+/);
|
|
1880
|
+
if (superOnCreateMatch && statusBarMatch && navBarMatch) {
|
|
1881
|
+
const formattedOnCreate = `
|
|
1882
|
+
override fun onCreate(savedInstanceState: Bundle?) {
|
|
1883
|
+
${superOnCreateMatch[0]}
|
|
1884
|
+
${statusBarMatch[0].trim()}
|
|
1885
|
+
${navBarMatch[0].replace(/[}]/g, '').trim()}
|
|
1886
|
+
}`;
|
|
1887
|
+
modifiedContent = modifiedContent.substring(0, onCreateIndex) +
|
|
1888
|
+
formattedOnCreate +
|
|
1889
|
+
modifiedContent.substring(onCreateEndIndex);
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
else {
|
|
1893
|
+
const hasStatusBarColor = onCreateContent.includes('window.statusBarColor');
|
|
1894
|
+
const hasNavigationBarColor = onCreateContent.includes('window.navigationBarColor');
|
|
1895
|
+
if (!hasStatusBarColor || !hasNavigationBarColor) {
|
|
1896
|
+
const superOnCreateMatch = onCreateContent.match(/super\.onCreate\([^)]*\)/);
|
|
1897
|
+
if (superOnCreateMatch) {
|
|
1898
|
+
const superOnCreateEndIndex = onCreateContent.indexOf(superOnCreateMatch[0]) + superOnCreateMatch[0].length;
|
|
1899
|
+
const nextLineIndex = onCreateContent.indexOf('\n', superOnCreateEndIndex);
|
|
1900
|
+
const insertPos = onCreateIndex + (nextLineIndex !== -1 ? nextLineIndex + 1 : superOnCreateEndIndex);
|
|
1901
|
+
const statusBarCode = `
|
|
1902
|
+
window.statusBarColor = android.graphics.Color.TRANSPARENT
|
|
1903
|
+
window.navigationBarColor = android.graphics.Color.TRANSPARENT`;
|
|
1904
|
+
modifiedContent = modifiedContent.substring(0, insertPos) +
|
|
1905
|
+
statusBarCode + '\n' +
|
|
1906
|
+
modifiedContent.substring(insertPos);
|
|
1907
|
+
}
|
|
1908
|
+
}
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1911
|
+
if (!modifiedContent.includes('override fun onContentChanged()')) {
|
|
1912
|
+
const onCreateMatchForInsert = modifiedContent.match(/override\s+fun\s+onCreate\s*\([^)]*\)\s*\{/);
|
|
1913
|
+
if (onCreateMatchForInsert) {
|
|
1914
|
+
const onCreateIndexForInsert = modifiedContent.indexOf(onCreateMatchForInsert[0]);
|
|
1915
|
+
let braceCount = 0;
|
|
1916
|
+
let onCreateEndIndexForInsert = onCreateIndexForInsert + onCreateMatchForInsert[0].length;
|
|
1917
|
+
let foundStart = false;
|
|
1918
|
+
for (let i = onCreateIndexForInsert; i < modifiedContent.length; i++) {
|
|
1919
|
+
if (modifiedContent[i] === '{') {
|
|
1920
|
+
braceCount++;
|
|
1921
|
+
foundStart = true;
|
|
1922
|
+
}
|
|
1923
|
+
else if (modifiedContent[i] === '}') {
|
|
1924
|
+
braceCount--;
|
|
1925
|
+
if (foundStart && braceCount === 0) {
|
|
1926
|
+
onCreateEndIndexForInsert = i + 1;
|
|
1927
|
+
break;
|
|
1928
|
+
}
|
|
1929
|
+
}
|
|
1930
|
+
}
|
|
1931
|
+
modifiedContent = modifiedContent.substring(0, onCreateEndIndexForInsert) +
|
|
1932
|
+
'\n' + onContentChangedCode + '\n' +
|
|
1933
|
+
modifiedContent.substring(onCreateEndIndexForInsert);
|
|
1934
|
+
}
|
|
1935
|
+
else {
|
|
1936
|
+
const getMainComponentMatch = modifiedContent.match(/override\s+fun\s+getMainComponentName/);
|
|
1937
|
+
if (getMainComponentMatch) {
|
|
1938
|
+
const insertPos = modifiedContent.indexOf(getMainComponentMatch[0]);
|
|
1939
|
+
modifiedContent = modifiedContent.substring(0, insertPos) +
|
|
1940
|
+
onContentChangedCode + '\n\n' +
|
|
1941
|
+
modifiedContent.substring(insertPos);
|
|
1942
|
+
}
|
|
1943
|
+
}
|
|
1944
|
+
}
|
|
1945
|
+
return modifiedContent;
|
|
1946
|
+
}
|
|
1947
|
+
function generatePrivacyPolicyActivity(packageName, projectRoot, androidMainPath) {
|
|
1948
|
+
const javaDir = path.join(androidMainPath, 'java', ...packageName.split('.'));
|
|
1949
|
+
if (!fs.existsSync(javaDir)) {
|
|
1950
|
+
fs.mkdirSync(javaDir, { recursive: true });
|
|
1951
|
+
}
|
|
1952
|
+
const activityPath = path.join(javaDir, 'SplashScreen2PrivacyPolicyActivity.kt');
|
|
1953
|
+
const activityContent = (0, android_1.replaceTemplatePlaceholders)(android_1.ANDROID_TEMPLATES.privacyPolicyActivity, {
|
|
1954
|
+
packageName,
|
|
1955
|
+
});
|
|
1956
|
+
try {
|
|
1957
|
+
fs.writeFileSync(activityPath, activityContent, 'utf-8');
|
|
1958
|
+
}
|
|
1959
|
+
catch (error) {
|
|
1960
|
+
console.error(`[expo-splash-screen2] Failed to generate SplashScreen2PrivacyPolicyActivity.kt:`, error);
|
|
1961
|
+
}
|
|
1962
|
+
}
|
|
1963
|
+
function modifyAndroidManifestForImageMode(manifest, packageName) {
|
|
1964
|
+
const application = manifest.manifest.application?.[0];
|
|
1965
|
+
const mainApplication = application && typeof application === 'object' && 'activity' in application
|
|
1966
|
+
? application
|
|
1967
|
+
: null;
|
|
1968
|
+
if (!mainApplication || !mainApplication.activity) {
|
|
1969
|
+
return manifest;
|
|
1970
|
+
}
|
|
1971
|
+
const mainActivityIndex = mainApplication.activity.findIndex((activity) => {
|
|
1972
|
+
const name = activity.$?.['android:name'];
|
|
1973
|
+
return (name === '.MainActivity' ||
|
|
1974
|
+
name === 'MainActivity' ||
|
|
1975
|
+
name?.endsWith('.MainActivity') ||
|
|
1976
|
+
name === `${packageName}.MainActivity`);
|
|
1977
|
+
});
|
|
1978
|
+
if (mainActivityIndex === -1) {
|
|
1979
|
+
console.warn('[expo-splash-screen2] MainActivity not found in AndroidManifest');
|
|
1980
|
+
return manifest;
|
|
1981
|
+
}
|
|
1982
|
+
const mainActivity = mainApplication.activity[mainActivityIndex];
|
|
1983
|
+
if (mainActivity && mainActivity.$) {
|
|
1984
|
+
mainActivity.$['android:theme'] = '@style/AppTheme';
|
|
1985
|
+
}
|
|
1986
|
+
return manifest;
|
|
1987
|
+
}
|
|
1988
|
+
function modifyAndroidManifestForBlendMode(manifest, packageName) {
|
|
1989
|
+
const application = manifest.manifest.application?.[0];
|
|
1990
|
+
const mainApplication = application && typeof application === 'object' && 'activity' in application
|
|
1991
|
+
? application
|
|
1992
|
+
: null;
|
|
1993
|
+
if (!mainApplication || !mainApplication.activity) {
|
|
1994
|
+
return manifest;
|
|
1995
|
+
}
|
|
1996
|
+
const mainActivityIndex = mainApplication.activity.findIndex((activity) => {
|
|
1997
|
+
const name = activity.$?.['android:name'];
|
|
1998
|
+
return (name === '.MainActivity' ||
|
|
1999
|
+
name === 'MainActivity' ||
|
|
2000
|
+
name?.endsWith('.MainActivity') ||
|
|
2001
|
+
name === `${packageName}.MainActivity`);
|
|
2002
|
+
});
|
|
2003
|
+
if (mainActivityIndex === -1) {
|
|
2004
|
+
console.warn('[expo-splash-screen2] MainActivity not found in AndroidManifest');
|
|
2005
|
+
return manifest;
|
|
2006
|
+
}
|
|
2007
|
+
const mainActivity = mainApplication.activity[mainActivityIndex];
|
|
2008
|
+
if (mainActivity && mainActivity.$) {
|
|
2009
|
+
mainActivity.$['android:theme'] = '@style/Theme.App.SplashScreen';
|
|
2010
|
+
}
|
|
2011
|
+
const customSplashActivityIndex = mainApplication.activity.findIndex((activity) => {
|
|
2012
|
+
const name = activity.$?.['android:name'];
|
|
1547
2013
|
return (name === `.SplashScreen2Activity` ||
|
|
1548
2014
|
name === 'SplashScreen2Activity' ||
|
|
1549
2015
|
name?.endsWith(`.SplashScreen2Activity`) ||
|
|
@@ -1829,1295 +2295,155 @@ function copyHtmlFileForIOS(projectRoot, iosPath, localHtmlPath) {
|
|
|
1829
2295
|
}
|
|
1830
2296
|
let targetDir = path.join(iosPath, 'MyNewExpoSplashDemo');
|
|
1831
2297
|
try {
|
|
1832
|
-
const entries = fs.readdirSync(iosPath, { withFileTypes: true });
|
|
1833
|
-
const projectDir = entries
|
|
1834
|
-
.filter((e) => e.isDirectory())
|
|
1835
|
-
.map((e) => e.name)
|
|
1836
|
-
.find((d) => fs.existsSync(path.join(iosPath, `${d}.xcodeproj`)));
|
|
1837
|
-
if (projectDir) {
|
|
1838
|
-
targetDir = path.join(iosPath, projectDir);
|
|
1839
|
-
}
|
|
1840
|
-
}
|
|
1841
|
-
catch { }
|
|
1842
|
-
console.log(`[expo-splash-screen2] [iOS] targetDir: ${targetDir}`);
|
|
1843
|
-
if (!fs.existsSync(targetDir)) {
|
|
1844
|
-
fs.mkdirSync(targetDir, { recursive: true });
|
|
1845
|
-
}
|
|
1846
|
-
const htmlContent = fs.readFileSync(sourcePath, 'utf-8');
|
|
1847
|
-
const htmlDir = path.dirname(sourcePath);
|
|
1848
|
-
console.log(`[expo-splash-screen2] [iOS] htmlDir: ${htmlDir}`);
|
|
1849
|
-
const imagePaths = extractImagePaths(htmlContent, htmlDir);
|
|
1850
|
-
console.log(`[expo-splash-screen2] [iOS] extractImagePaths found: ${imagePaths.length} images`);
|
|
1851
|
-
imagePaths.forEach(({ original, absolute }) => {
|
|
1852
|
-
console.log(`[expo-splash-screen2] [iOS] - original: ${original}, absolute: ${absolute}`);
|
|
1853
|
-
});
|
|
1854
|
-
const assetsDir = path.join(htmlDir, 'assets');
|
|
1855
|
-
console.log(`[expo-splash-screen2] [iOS] checking assetsDir: ${assetsDir}`);
|
|
1856
|
-
console.log(`[expo-splash-screen2] [iOS] assetsDir exists: ${fs.existsSync(assetsDir)}`);
|
|
1857
|
-
if (fs.existsSync(assetsDir) && fs.statSync(assetsDir).isDirectory()) {
|
|
1858
|
-
const allFiles = fs.readdirSync(assetsDir);
|
|
1859
|
-
console.log(`[expo-splash-screen2] [iOS] assetsDir all files: ${allFiles.join(', ')}`);
|
|
1860
|
-
const imageFiles = allFiles.filter(f => /\.(png|jpg|jpeg|gif|svg|webp|ico)$/i.test(f));
|
|
1861
|
-
console.log(`[expo-splash-screen2] [iOS] assetsDir image files: ${imageFiles.join(', ')}`);
|
|
1862
|
-
imageFiles.forEach(imgFile => {
|
|
1863
|
-
const srcPath = path.join(assetsDir, imgFile);
|
|
1864
|
-
const absolutePath = srcPath;
|
|
1865
|
-
imagePaths.push({ original: `./assets/${imgFile}`, absolute: absolutePath });
|
|
1866
|
-
console.log(`[expo-splash-screen2] [iOS] added from assets: ./assets/${imgFile} -> ${absolutePath}`);
|
|
1867
|
-
});
|
|
1868
|
-
}
|
|
1869
|
-
const imagesDir = path.join(htmlDir, 'images');
|
|
1870
|
-
console.log(`[expo-splash-screen2] [iOS] checking imagesDir: ${imagesDir}`);
|
|
1871
|
-
console.log(`[expo-splash-screen2] [iOS] imagesDir exists: ${fs.existsSync(imagesDir)}`);
|
|
1872
|
-
if (fs.existsSync(imagesDir) && fs.statSync(imagesDir).isDirectory()) {
|
|
1873
|
-
const imageFiles = fs.readdirSync(imagesDir).filter(f => /\.(png|jpg|jpeg|gif|svg|webp|ico)$/i.test(f));
|
|
1874
|
-
console.log(`[expo-splash-screen2] [iOS] imagesDir image files: ${imageFiles.join(', ')}`);
|
|
1875
|
-
imageFiles.forEach(imgFile => {
|
|
1876
|
-
const srcPath = path.join(imagesDir, imgFile);
|
|
1877
|
-
const absolutePath = srcPath;
|
|
1878
|
-
imagePaths.push({ original: `./images/${imgFile}`, absolute: absolutePath });
|
|
1879
|
-
});
|
|
1880
|
-
}
|
|
1881
|
-
console.log(`[expo-splash-screen2] [iOS] total imagePaths after scanning: ${imagePaths.length}`);
|
|
1882
|
-
const imagePathMap = new Map();
|
|
1883
|
-
imagePaths.forEach(({ original, absolute }) => {
|
|
1884
|
-
const fileNameWithHash = path.basename(absolute);
|
|
1885
|
-
const fileNameWithoutHash = removeHashFromFileName(fileNameWithHash);
|
|
1886
|
-
const newPath = `./${fileNameWithoutHash}`;
|
|
1887
|
-
console.log(`[expo-splash-screen2] [iOS] processing: ${original} -> ${newPath} (file: ${absolute})`);
|
|
1888
|
-
const targetImagePath = path.join(targetDir, fileNameWithoutHash);
|
|
1889
|
-
console.log(`[expo-splash-screen2] [iOS] copying to: ${targetImagePath}`);
|
|
1890
|
-
if (fs.existsSync(absolute)) {
|
|
1891
|
-
fs.copyFileSync(absolute, targetImagePath);
|
|
1892
|
-
console.log(`[expo-splash-screen2] [iOS] copied successfully: ${fileNameWithoutHash}`);
|
|
1893
|
-
imagePathMap.set(original, newPath);
|
|
1894
|
-
const normalizedOriginal = original.startsWith('./') ? original : `./${original}`;
|
|
1895
|
-
imagePathMap.set(normalizedOriginal, newPath);
|
|
1896
|
-
if (original.startsWith('./')) {
|
|
1897
|
-
imagePathMap.set(original.substring(2), newPath);
|
|
1898
|
-
}
|
|
1899
|
-
if (original.startsWith('./images/')) {
|
|
1900
|
-
imagePathMap.set(original, newPath);
|
|
1901
|
-
imagePathMap.set(original.substring(2), newPath);
|
|
1902
|
-
imagePathMap.set(original.substring(10), newPath);
|
|
1903
|
-
}
|
|
1904
|
-
if (original.startsWith('./assets/')) {
|
|
1905
|
-
imagePathMap.set(original, newPath);
|
|
1906
|
-
imagePathMap.set(original.substring(2), newPath);
|
|
1907
|
-
const originalFileName = path.basename(original);
|
|
1908
|
-
if (originalFileName !== fileNameWithoutHash) {
|
|
1909
|
-
imagePathMap.set(original.replace(originalFileName, fileNameWithoutHash), newPath);
|
|
1910
|
-
}
|
|
1911
|
-
}
|
|
1912
|
-
}
|
|
1913
|
-
});
|
|
1914
|
-
let updatedHtmlContent = htmlContent;
|
|
1915
|
-
updatedHtmlContent = updatedHtmlContent.replace(/<img([^>]+)src\s*=\s*["']([^"']+)["']/gi, (match, attrs, srcPath) => {
|
|
1916
|
-
if (srcPath.startsWith('http') || srcPath.startsWith('data:')) {
|
|
1917
|
-
return match;
|
|
1918
|
-
}
|
|
1919
|
-
const newPath = imagePathMap.get(srcPath) || imagePathMap.get(`./${srcPath}`) || imagePathMap.get(srcPath.replace(/^\.\//, ''));
|
|
1920
|
-
if (newPath) {
|
|
1921
|
-
return `<img${attrs}src="${newPath}"`;
|
|
1922
|
-
}
|
|
1923
|
-
return match;
|
|
1924
|
-
});
|
|
1925
|
-
updatedHtmlContent = updatedHtmlContent.replace(/url\s*\(\s*["']?([^"')]+)["']?\s*\)/gi, (match, urlPath) => {
|
|
1926
|
-
if (urlPath.startsWith('http') || urlPath.startsWith('data:')) {
|
|
1927
|
-
return match;
|
|
1928
|
-
}
|
|
1929
|
-
const imageExtensions = ['.png', '.jpg', '.jpeg', '.gif', '.webp', '.svg', '.ico'];
|
|
1930
|
-
const lowerPath = urlPath.toLowerCase();
|
|
1931
|
-
if (!imageExtensions.some(ext => lowerPath.includes(ext))) {
|
|
1932
|
-
return match;
|
|
1933
|
-
}
|
|
1934
|
-
const newPath = imagePathMap.get(urlPath) || imagePathMap.get(`./${urlPath}`) || imagePathMap.get(urlPath.replace(/^\.\//, ''));
|
|
1935
|
-
if (newPath) {
|
|
1936
|
-
return `url("${newPath}")`;
|
|
1937
|
-
}
|
|
1938
|
-
return match;
|
|
1939
|
-
});
|
|
1940
|
-
updatedHtmlContent = updatedHtmlContent.replace(/(["'])(\.\/images\/[^"']+\.(png|jpg|jpeg|gif|svg|webp|ico))(["'])/gi, (match, quote1, imgPath, ext, quote2) => {
|
|
1941
|
-
const fileName = path.basename(imgPath);
|
|
1942
|
-
const fileNameWithoutHash = removeHashFromFileName(fileName);
|
|
1943
|
-
const newPath = imagePathMap.get(`./images/${fileName}`) ||
|
|
1944
|
-
imagePathMap.get(`./images/${fileNameWithoutHash}`) ||
|
|
1945
|
-
imagePathMap.get(`./${fileName}`) ||
|
|
1946
|
-
imagePathMap.get(`./${fileNameWithoutHash}`) ||
|
|
1947
|
-
`./${fileNameWithoutHash}`;
|
|
1948
|
-
return `${quote1}${newPath}${quote2}`;
|
|
1949
|
-
});
|
|
1950
|
-
updatedHtmlContent = updatedHtmlContent.replace(/(["'])(\.\/assets\/[^"']+\.(png|jpg|jpeg|gif|svg|webp|ico))(["'])/gi, (match, quote1, imgPath, ext, quote2) => {
|
|
1951
|
-
const fileName = path.basename(imgPath);
|
|
1952
|
-
const fileNameWithoutHash = removeHashFromFileName(fileName);
|
|
1953
|
-
const newPath = imagePathMap.get(imgPath) ||
|
|
1954
|
-
imagePathMap.get(`./assets/${imgPath.substring(2)}`) ||
|
|
1955
|
-
`./${fileNameWithoutHash}`;
|
|
1956
|
-
return `${quote1}${newPath}${quote2}`;
|
|
1957
|
-
});
|
|
1958
|
-
updatedHtmlContent = updatedHtmlContent.replace(/(\.\/images\/[^\s"'`;,\)]+\.(png|jpg|jpeg|gif|svg|webp|ico))/gi, (match, imgPath) => {
|
|
1959
|
-
const fileName = path.basename(imgPath);
|
|
1960
|
-
const fileNameWithoutHash = removeHashFromFileName(fileName);
|
|
1961
|
-
const newPath = imagePathMap.get(`./images/${fileName}`) ||
|
|
1962
|
-
imagePathMap.get(`./images/${fileNameWithoutHash}`) ||
|
|
1963
|
-
imagePathMap.get(`./${fileName}`) ||
|
|
1964
|
-
imagePathMap.get(`./${fileNameWithoutHash}`) ||
|
|
1965
|
-
`./${fileNameWithoutHash}`;
|
|
1966
|
-
return newPath;
|
|
1967
|
-
});
|
|
1968
|
-
updatedHtmlContent = updatedHtmlContent.replace(/(\.\/assets\/[^\s"'`;,\)]+\.(png|jpg|jpeg|gif|svg|webp|ico))/gi, (match, imgPath) => {
|
|
1969
|
-
const fileName = path.basename(imgPath);
|
|
1970
|
-
const fileNameWithoutHash = removeHashFromFileName(fileName);
|
|
1971
|
-
const newPath = imagePathMap.get(imgPath) ||
|
|
1972
|
-
imagePathMap.get(`./assets/${imgPath.substring(2)}`) ||
|
|
1973
|
-
`./${fileNameWithoutHash}`;
|
|
1974
|
-
return newPath;
|
|
1975
|
-
});
|
|
1976
|
-
const targetPath = path.join(targetDir, 'index.html');
|
|
1977
|
-
fs.writeFileSync(targetPath, updatedHtmlContent, 'utf-8');
|
|
1978
|
-
}
|
|
1979
|
-
catch (error) {
|
|
1980
|
-
console.error(`[expo-splash-screen2] Error copying HTML file for iOS: ${error}`);
|
|
1981
|
-
}
|
|
1982
|
-
}
|
|
1983
|
-
function generateSplashScreen2Service(bundleIdentifier, projectRoot, iosPath, projectName) {
|
|
1984
|
-
const targetDir = path.join(iosPath, projectName);
|
|
1985
|
-
if (!fs.existsSync(targetDir)) {
|
|
1986
|
-
fs.mkdirSync(targetDir, { recursive: true });
|
|
1987
|
-
}
|
|
1988
|
-
const servicePath = path.join(targetDir, 'SplashScreen2Service.swift');
|
|
1989
|
-
const serviceContent = `import UIKit
|
|
1990
|
-
import WebKit
|
|
1991
|
-
|
|
1992
|
-
// Protocol definition, used to replace AppDelegate type
|
|
1993
|
-
@objc public protocol AppDelegateProtocol {
|
|
1994
|
-
@objc func startReactNativeIfNeeded()
|
|
1995
|
-
}
|
|
1996
|
-
|
|
1997
|
-
// Similar to EXSplashScreenService, manages splash screen display and hiding
|
|
1998
|
-
public class SplashScreen2Service: NSObject {
|
|
1999
|
-
private var splashScreenControllers: [UIViewController: SplashScreen2ViewController] = [:]
|
|
2000
|
-
private weak var observingRootViewController: UIViewController?
|
|
2001
|
-
// Global preventAutoHide state, applied to newly created splash screens
|
|
2002
|
-
private var globalPreventAutoHide: Bool = false
|
|
2003
|
-
private static let sharedInstance = SplashScreen2Service()
|
|
2004
|
-
|
|
2005
|
-
public static var shared: SplashScreen2Service {
|
|
2006
|
-
return sharedInstance
|
|
2007
|
-
}
|
|
2008
|
-
|
|
2009
|
-
private override init() {
|
|
2010
|
-
super.init()
|
|
2011
|
-
}
|
|
2012
|
-
|
|
2013
|
-
// Show splash screen (similar to EXSplashScreenService.showSplashScreenFor)
|
|
2014
|
-
public func showSplashScreenFor(_ viewController: UIViewController) {
|
|
2015
|
-
print("[SplashScreen2Service] showSplashScreenFor called for viewController: \\(viewController)")
|
|
2016
|
-
print("[SplashScreen2Service] showSplashScreenFor - globalPreventAutoHide: \\(globalPreventAutoHide)")
|
|
2017
|
-
|
|
2018
|
-
// If already exists, clean up old one first
|
|
2019
|
-
// Note: Using force=true here because we want to replace the old splash screen
|
|
2020
|
-
// But if globalPreventAutoHide=true, we should keep the old one instead of cleaning it up
|
|
2021
|
-
if let existingController = splashScreenControllers[viewController] {
|
|
2022
|
-
if globalPreventAutoHide {
|
|
2023
|
-
print("[SplashScreen2Service] showSplashScreenFor - globalPreventAutoHide is true, keeping existing splash screen")
|
|
2024
|
-
// If preventAutoHide is already set, no need to recreate
|
|
2025
|
-
// Ensure splash screen is on top layer and visible
|
|
2026
|
-
if let splashVC = existingController.splashViewControllerInstance {
|
|
2027
|
-
splashVC.view.isHidden = false
|
|
2028
|
-
splashVC.view.alpha = 1.0
|
|
2029
|
-
viewController.view.bringSubviewToFront(splashVC.view)
|
|
2030
|
-
print("[SplashScreen2Service] showSplashScreenFor - Brought existing splash screen to front and ensured visibility")
|
|
2031
|
-
}
|
|
2032
|
-
return
|
|
2033
|
-
} else {
|
|
2034
|
-
print("[SplashScreen2Service] Splash screen already exists for view controller, cleaning up old one")
|
|
2035
|
-
existingController.hide(force: true)
|
|
2036
|
-
splashScreenControllers.removeValue(forKey: viewController)
|
|
2037
|
-
}
|
|
2038
|
-
}
|
|
2039
|
-
|
|
2040
|
-
// Create SplashScreen2ViewController instance
|
|
2041
|
-
let splashVC = SplashScreen2ViewController()
|
|
2042
|
-
let splashScreenController = SplashScreen2ViewController(splashViewController: splashVC)
|
|
2043
|
-
|
|
2044
|
-
// If global preventAutoHide state is true, apply immediately
|
|
2045
|
-
if globalPreventAutoHide {
|
|
2046
|
-
print("[SplashScreen2Service] showSplashScreenFor - Applying global preventAutoHide state")
|
|
2047
|
-
splashScreenController.preventAutoHide()
|
|
2048
|
-
// Ensure splash screen is visible (set before adding to parent view)
|
|
2049
|
-
splashVC.view.isHidden = false
|
|
2050
|
-
splashVC.view.alpha = 1.0
|
|
2051
|
-
}
|
|
2052
|
-
|
|
2053
|
-
// Set view's frame first, ensure correct size
|
|
2054
|
-
// This must be done before adding to parent view
|
|
2055
|
-
splashVC.view.frame = viewController.view.bounds
|
|
2056
|
-
|
|
2057
|
-
// Print size information for debugging
|
|
2058
|
-
print("[SplashScreen2Service] showSplashScreenFor - viewController.view.frame: \\(viewController.view.frame)")
|
|
2059
|
-
print("[SplashScreen2Service] showSplashScreenFor - viewController.view.bounds: \\(viewController.view.bounds)")
|
|
2060
|
-
print("[SplashScreen2Service] showSplashScreenFor - UIScreen.main.bounds: \\(UIScreen.main.bounds)")
|
|
2061
|
-
print("[SplashScreen2Service] showSplashScreenFor - splashVC.view.frame (before addSubview): \\(splashVC.view.frame)")
|
|
2062
|
-
|
|
2063
|
-
// Add SplashScreen2ViewController as child view controller (maintain lifecycle)
|
|
2064
|
-
// This must be called before addSubview to ensure viewDidLoad is called at the right time
|
|
2065
|
-
viewController.addChild(splashVC)
|
|
2066
|
-
|
|
2067
|
-
// Add SplashScreen2ViewController's view to target view controller's view
|
|
2068
|
-
viewController.view.addSubview(splashVC.view)
|
|
2069
|
-
splashVC.view.translatesAutoresizingMaskIntoConstraints = false
|
|
2070
|
-
|
|
2071
|
-
// Set constraints to ensure full screen display
|
|
2072
|
-
NSLayoutConstraint.activate([
|
|
2073
|
-
splashVC.view.topAnchor.constraint(equalTo: viewController.view.topAnchor),
|
|
2074
|
-
splashVC.view.leadingAnchor.constraint(equalTo: viewController.view.leadingAnchor),
|
|
2075
|
-
splashVC.view.trailingAnchor.constraint(equalTo: viewController.view.trailingAnchor),
|
|
2076
|
-
splashVC.view.bottomAnchor.constraint(equalTo: viewController.view.bottomAnchor)
|
|
2077
|
-
])
|
|
2078
|
-
|
|
2079
|
-
// 确保在最上层
|
|
2080
|
-
viewController.view.bringSubviewToFront(splashVC.view)
|
|
2081
|
-
|
|
2082
|
-
// 完成子 view controller 的添加
|
|
2083
|
-
splashVC.didMove(toParent: viewController)
|
|
2084
|
-
|
|
2085
|
-
// 强制布局更新,确保约束生效
|
|
2086
|
-
viewController.view.setNeedsLayout()
|
|
2087
|
-
viewController.view.layoutIfNeeded()
|
|
2088
|
-
splashVC.view.setNeedsLayout()
|
|
2089
|
-
splashVC.view.layoutIfNeeded()
|
|
2090
|
-
|
|
2091
|
-
// 打印约束后的尺寸
|
|
2092
|
-
print("[SplashScreen2Service] showSplashScreenFor - After constraints, splashVC.view.frame: \\(splashVC.view.frame)")
|
|
2093
|
-
print("[SplashScreen2Service] showSplashScreenFor - After constraints, splashVC.view.bounds: \\(splashVC.view.bounds)")
|
|
2094
|
-
print("[SplashScreen2Service] showSplashScreenFor - splashVC.view.superview: \\(String(describing: splashVC.view.superview))")
|
|
2095
|
-
print("[SplashScreen2Service] showSplashScreenFor - splashVC.view.window: \\(String(describing: splashVC.view.window))")
|
|
2096
|
-
|
|
2097
|
-
// 确保 WebView 已经正确挂载
|
|
2098
|
-
// 延迟一点时间,确保 viewDidLoad 和 setupWebView 已经完成
|
|
2099
|
-
DispatchQueue.main.async {
|
|
2100
|
-
print("[SplashScreen2Service] showSplashScreenFor - After async, splashVC.view.frame: \\(splashVC.view.frame)")
|
|
2101
|
-
print("[SplashScreen2Service] showSplashScreenFor - After async, splashVC.view.subviews.count: \\(splashVC.view.subviews.count)")
|
|
2102
|
-
print("[SplashScreen2Service] showSplashScreenFor - After async, splashVC.view.isHidden: \\(splashVC.view.isHidden)")
|
|
2103
|
-
print("[SplashScreen2Service] showSplashScreenFor - After async, splashVC.view.alpha: \\(splashVC.view.alpha)")
|
|
2104
|
-
print("[SplashScreen2Service] showSplashScreenFor - After async, splashVC.view.superview: \\(String(describing: splashVC.view.superview))")
|
|
2105
|
-
print("[SplashScreen2Service] showSplashScreenFor - After async, splashVC.view.window: \\(String(describing: splashVC.view.window))")
|
|
2106
|
-
|
|
2107
|
-
// 检查是否有其他视图遮挡
|
|
2108
|
-
if let superview = splashVC.view.superview {
|
|
2109
|
-
print("[SplashScreen2Service] showSplashScreenFor - superview.subviews.count: \\(superview.subviews.count)")
|
|
2110
|
-
for (index, subview) in superview.subviews.enumerated() {
|
|
2111
|
-
print("[SplashScreen2Service] showSplashScreenFor - superview.subview[\\(index)]: \\(type(of: subview)), frame: \\(subview.frame), isHidden: \\(subview.isHidden), alpha: \\(subview.alpha)")
|
|
2112
|
-
}
|
|
2113
|
-
}
|
|
2114
|
-
|
|
2115
|
-
for (index, subview) in splashVC.view.subviews.enumerated() {
|
|
2116
|
-
print("[SplashScreen2Service] showSplashScreenFor - subview[\\(index)]: \\(type(of: subview)), frame: \\(subview.frame), isHidden: \\(subview.isHidden), alpha: \\(subview.alpha)")
|
|
2117
|
-
|
|
2118
|
-
// 确保 WebView 可见
|
|
2119
|
-
if let webView = subview as? WKWebView {
|
|
2120
|
-
webView.isHidden = false
|
|
2121
|
-
webView.alpha = 1.0
|
|
2122
|
-
print("[SplashScreen2Service] showSplashScreenFor - WebView visibility set: isHidden=\\(webView.isHidden), alpha=\\(webView.alpha)")
|
|
2123
|
-
}
|
|
2124
|
-
}
|
|
2125
|
-
|
|
2126
|
-
// 确保 view 在最上层
|
|
2127
|
-
if let superview = splashVC.view.superview {
|
|
2128
|
-
superview.bringSubviewToFront(splashVC.view)
|
|
2129
|
-
print("[SplashScreen2Service] showSplashScreenFor - Brought splashVC.view to front")
|
|
2130
|
-
}
|
|
2131
|
-
}
|
|
2132
|
-
|
|
2133
|
-
splashScreenControllers[viewController] = splashScreenController
|
|
2134
|
-
splashScreenController.show()
|
|
2135
|
-
}
|
|
2136
|
-
|
|
2137
|
-
// 隐藏 splash screen(类似 EXSplashScreenService.hideSplashScreenFor)
|
|
2138
|
-
public func hideSplashScreenFor(_ viewController: UIViewController, force: Bool = false) {
|
|
2139
|
-
print("[SplashScreen2Service] hideSplashScreenFor called for viewController: \\(viewController), force: \\(force)")
|
|
2140
|
-
print("[SplashScreen2Service] hideSplashScreenFor - globalPreventAutoHide: \\(globalPreventAutoHide)")
|
|
2141
|
-
|
|
2142
|
-
guard let controller = splashScreenControllers[viewController] else {
|
|
2143
|
-
print("[SplashScreen2Service] No splash screen found for view controller")
|
|
2144
|
-
return
|
|
2145
|
-
}
|
|
2146
|
-
|
|
2147
|
-
// 如果 globalPreventAutoHide 为 true 且 force 为 false,不执行隐藏操作
|
|
2148
|
-
if globalPreventAutoHide && !force {
|
|
2149
|
-
print("[SplashScreen2Service] hideSplashScreenFor - globalPreventAutoHide is true and force is false, ignoring hide call")
|
|
2150
|
-
print("[SplashScreen2Service] hideSplashScreenFor - Stack trace: \\(Thread.callStackSymbols.prefix(5).joined(separator: "\\n"))")
|
|
2151
|
-
// 确保 splash screen 仍然可见且在最上层
|
|
2152
|
-
if let splashVC = controller.splashViewControllerInstance {
|
|
2153
|
-
splashVC.view.isHidden = false
|
|
2154
|
-
splashVC.view.alpha = 1.0
|
|
2155
|
-
if let parent = splashVC.parent {
|
|
2156
|
-
parent.view.bringSubviewToFront(splashVC.view)
|
|
2157
|
-
} else if let superview = splashVC.view.superview {
|
|
2158
|
-
superview.bringSubviewToFront(splashVC.view)
|
|
2159
|
-
}
|
|
2160
|
-
print("[SplashScreen2Service] hideSplashScreenFor - Ensured splash screen is still visible and on top")
|
|
2161
|
-
}
|
|
2162
|
-
return
|
|
2163
|
-
}
|
|
2164
|
-
|
|
2165
|
-
print("[SplashScreen2Service] hideSplashScreenFor - Proceeding with hide, force: \\(force)")
|
|
2166
|
-
// 使用 force=true 强制隐藏,即使 preventAutoHide 被调用
|
|
2167
|
-
controller.hide(force: force)
|
|
2168
|
-
splashScreenControllers.removeValue(forKey: viewController)
|
|
2169
|
-
}
|
|
2170
|
-
|
|
2171
|
-
// 隐藏所有 splash screen(用于强制隐藏所有已知的 splash screen)
|
|
2172
|
-
public func hideAllSplashScreens(force: Bool = true) {
|
|
2173
|
-
print("[SplashScreen2Service] hideAllSplashScreens called, force: \\(force)")
|
|
2174
|
-
print("[SplashScreen2Service] hideAllSplashScreens - splashScreenControllers count: \\(splashScreenControllers.count)")
|
|
2175
|
-
|
|
2176
|
-
// 复制字典的键,因为我们在迭代过程中会修改字典
|
|
2177
|
-
let allViewControllers = Array(splashScreenControllers.keys)
|
|
2178
|
-
|
|
2179
|
-
for viewController in allViewControllers {
|
|
2180
|
-
print("[SplashScreen2Service] hideAllSplashScreens - Hiding splash screen for: \\(viewController)")
|
|
2181
|
-
hideSplashScreenFor(viewController, force: force)
|
|
2182
|
-
}
|
|
2183
|
-
|
|
2184
|
-
print("[SplashScreen2Service] hideAllSplashScreens - Completed, remaining count: \\(splashScreenControllers.count)")
|
|
2185
|
-
}
|
|
2186
|
-
|
|
2187
|
-
// 防止自动隐藏(类似 EXSplashScreenService.preventSplashScreenAutoHideFor)
|
|
2188
|
-
public func preventAutoHideFor(_ viewController: UIViewController) {
|
|
2189
|
-
print("[SplashScreen2Service] preventAutoHideFor called for viewController: \\(viewController)")
|
|
2190
|
-
print("[SplashScreen2Service] preventAutoHideFor - Stack trace: \\(Thread.callStackSymbols.prefix(5).joined(separator: "\\n"))")
|
|
2191
|
-
|
|
2192
|
-
// 设置全局 preventAutoHide 状态(必须在最开始设置)
|
|
2193
|
-
globalPreventAutoHide = true
|
|
2194
|
-
print("[SplashScreen2Service] preventAutoHideFor - Set globalPreventAutoHide to true")
|
|
2195
|
-
|
|
2196
|
-
// 如果还没有 splash screen,先创建一个
|
|
2197
|
-
if splashScreenControllers[viewController] == nil {
|
|
2198
|
-
print("[SplashScreen2Service] preventAutoHideFor - No splash screen found, creating one first")
|
|
2199
|
-
showSplashScreenFor(viewController)
|
|
2200
|
-
}
|
|
2201
|
-
|
|
2202
|
-
// 对所有现有的 splash screen 应用 preventAutoHide
|
|
2203
|
-
for (vc, controller) in splashScreenControllers {
|
|
2204
|
-
print("[SplashScreen2Service] preventAutoHideFor - Applying preventAutoHide to existing splash screen for viewController: \\(vc)")
|
|
2205
|
-
controller.preventAutoHide()
|
|
2206
|
-
// 确保 splash screen 可见且在最上层
|
|
2207
|
-
if let splashVC = controller.splashViewControllerInstance {
|
|
2208
|
-
splashVC.view.isHidden = false
|
|
2209
|
-
splashVC.view.alpha = 1.0
|
|
2210
|
-
if let parent = splashVC.parent {
|
|
2211
|
-
parent.view.bringSubviewToFront(splashVC.view)
|
|
2212
|
-
} else if let superview = splashVC.view.superview {
|
|
2213
|
-
superview.bringSubviewToFront(splashVC.view)
|
|
2214
|
-
}
|
|
2215
|
-
print("[SplashScreen2Service] preventAutoHideFor - Ensured splash screen is visible and on top for viewController: \\(vc)")
|
|
2216
|
-
}
|
|
2217
|
-
}
|
|
2218
|
-
|
|
2219
|
-
guard let controller = splashScreenControllers[viewController] else {
|
|
2220
|
-
print("[SplashScreen2Service] preventAutoHideFor - Failed to create or find splash screen")
|
|
2221
|
-
return
|
|
2222
|
-
}
|
|
2223
|
-
|
|
2224
|
-
print("[SplashScreen2Service] preventAutoHideFor - Calling preventAutoHide on controller")
|
|
2225
|
-
controller.preventAutoHide()
|
|
2226
|
-
|
|
2227
|
-
// 确保 splash screen 可见且在最上层
|
|
2228
|
-
if let splashVC = controller.splashViewControllerInstance {
|
|
2229
|
-
splashVC.view.isHidden = false
|
|
2230
|
-
splashVC.view.alpha = 1.0
|
|
2231
|
-
if let parent = splashVC.parent {
|
|
2232
|
-
parent.view.bringSubviewToFront(splashVC.view)
|
|
2233
|
-
} else if let superview = splashVC.view.superview {
|
|
2234
|
-
superview.bringSubviewToFront(splashVC.view)
|
|
2235
|
-
}
|
|
2236
|
-
print("[SplashScreen2Service] preventAutoHideFor - Ensured splash screen is visible and on top")
|
|
2237
|
-
}
|
|
2238
|
-
|
|
2239
|
-
print("[SplashScreen2Service] preventAutoHideFor - preventAutoHide called successfully")
|
|
2240
|
-
}
|
|
2241
|
-
|
|
2242
|
-
// 添加 rootViewController 监听(类似 EXSplashScreenService.addRootViewControllerListener)
|
|
2243
|
-
public func addRootViewControllerListener() {
|
|
2244
|
-
guard Thread.isMainThread else {
|
|
2245
|
-
DispatchQueue.main.async { [weak self] in
|
|
2246
|
-
self?.addRootViewControllerListener()
|
|
2247
|
-
}
|
|
2248
|
-
return
|
|
2249
|
-
}
|
|
2250
|
-
|
|
2251
|
-
// 如果已经有监听器,先移除旧的
|
|
2252
|
-
if observingRootViewController != nil {
|
|
2253
|
-
print("[SplashScreen2Service] addRootViewControllerListener: Already observing, removing old listener first")
|
|
2254
|
-
removeRootViewControllerListener()
|
|
2255
|
-
}
|
|
2256
|
-
|
|
2257
|
-
if let window = UIApplication.shared.keyWindow {
|
|
2258
|
-
window.addObserver(self, forKeyPath: "rootViewController", options: .new, context: nil)
|
|
2259
|
-
|
|
2260
|
-
// 如果已经有 rootViewController,立即显示 splash screen
|
|
2261
|
-
if let rootViewController = window.rootViewController {
|
|
2262
|
-
print("[SplashScreen2Service] addRootViewControllerListener: Found existing rootViewController: \\(rootViewController)")
|
|
2263
|
-
print("[SplashScreen2Service] addRootViewControllerListener - globalPreventAutoHide: \\(globalPreventAutoHide)")
|
|
2264
|
-
|
|
2265
|
-
// 只有当 rootViewController 不是当前观察的对象时才添加监听器
|
|
2266
|
-
if rootViewController != observingRootViewController {
|
|
2267
|
-
rootViewController.addObserver(self, forKeyPath: "view", options: .new, context: nil)
|
|
2268
|
-
observingRootViewController = rootViewController
|
|
2269
|
-
|
|
2270
|
-
// 立即显示 splash screen(只有当还没有显示时才显示)
|
|
2271
|
-
// 如果 globalPreventAutoHide 为 true,且已经存在 splash screen,不需要重新创建
|
|
2272
|
-
if splashScreenControllers[rootViewController] == nil {
|
|
2273
|
-
if globalPreventAutoHide {
|
|
2274
|
-
print("[SplashScreen2Service] addRootViewControllerListener - globalPreventAutoHide is true but no splash screen found, this should not happen")
|
|
2275
|
-
}
|
|
2276
|
-
showSplashScreenFor(rootViewController)
|
|
2277
|
-
} else {
|
|
2278
|
-
print("[SplashScreen2Service] addRootViewControllerListener: Splash screen already exists for rootViewController, skipping")
|
|
2279
|
-
// 如果 globalPreventAutoHide 为 true,确保 splash screen 在最上层
|
|
2280
|
-
if globalPreventAutoHide, let controller = splashScreenControllers[rootViewController] {
|
|
2281
|
-
print("[SplashScreen2Service] addRootViewControllerListener - Ensuring splash screen is on top")
|
|
2282
|
-
if let splashVC = controller.splashViewControllerInstance {
|
|
2283
|
-
rootViewController.view.bringSubviewToFront(splashVC.view)
|
|
2284
|
-
}
|
|
2285
|
-
}
|
|
2286
|
-
}
|
|
2287
|
-
}
|
|
2288
|
-
} else {
|
|
2289
|
-
// 如果没有 rootViewController,创建一个临时的 view controller 来显示 splash screen
|
|
2290
|
-
// 这确保在 RN 启动之前就能看到 splash screen
|
|
2291
|
-
print("[SplashScreen2Service] addRootViewControllerListener: No rootViewController, creating temp one")
|
|
2292
|
-
let tempViewController = UIViewController()
|
|
2293
|
-
tempViewController.view.backgroundColor = .clear
|
|
2294
|
-
window.rootViewController = tempViewController
|
|
2295
|
-
window.makeKeyAndVisible()
|
|
2296
|
-
|
|
2297
|
-
tempViewController.addObserver(self, forKeyPath: "view", options: .new, context: nil)
|
|
2298
|
-
observingRootViewController = tempViewController
|
|
2299
|
-
|
|
2300
|
-
// 立即显示 splash screen
|
|
2301
|
-
showSplashScreenFor(tempViewController)
|
|
2302
|
-
}
|
|
2303
|
-
} else {
|
|
2304
|
-
print("[SplashScreen2Service] addRootViewControllerListener: No keyWindow found")
|
|
2305
|
-
}
|
|
2306
|
-
}
|
|
2307
|
-
|
|
2308
|
-
// 移除 rootViewController 监听(类似 EXSplashScreenService.removeRootViewControllerListener)
|
|
2309
|
-
public func removeRootViewControllerListener() {
|
|
2310
|
-
guard Thread.isMainThread else {
|
|
2311
|
-
DispatchQueue.main.async { [weak self] in
|
|
2312
|
-
self?.removeRootViewControllerListener()
|
|
2313
|
-
}
|
|
2314
|
-
return
|
|
2315
|
-
}
|
|
2316
|
-
|
|
2317
|
-
if let rootViewController = observingRootViewController {
|
|
2318
|
-
if let window = rootViewController.view.window {
|
|
2319
|
-
window.removeObserver(self, forKeyPath: "rootViewController")
|
|
2320
|
-
}
|
|
2321
|
-
rootViewController.removeObserver(self, forKeyPath: "view")
|
|
2322
|
-
observingRootViewController = nil
|
|
2323
|
-
}
|
|
2324
|
-
}
|
|
2325
|
-
|
|
2326
|
-
// KVO 监听(类似 EXSplashScreenService.observeValueForKeyPath)
|
|
2327
|
-
public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
|
|
2328
|
-
if let window = object as? UIWindow, keyPath == "rootViewController" {
|
|
2329
|
-
if let newRootViewController = change?[.newKey] as? UIViewController,
|
|
2330
|
-
newRootViewController != observingRootViewController {
|
|
2331
|
-
print("[SplashScreen2Service] rootViewController changed from \\(String(describing: observingRootViewController)) to \\(newRootViewController)")
|
|
2332
|
-
print("[SplashScreen2Service] rootViewController changed - globalPreventAutoHide: \\(globalPreventAutoHide)")
|
|
2333
|
-
|
|
2334
|
-
// 尝试复用已有的 splash screen(无论是否调用 preventAutoHide)
|
|
2335
|
-
if let oldRootViewController = observingRootViewController,
|
|
2336
|
-
let oldController = splashScreenControllers[oldRootViewController],
|
|
2337
|
-
let splashVC = oldController.splashViewControllerInstance {
|
|
2338
|
-
print("[SplashScreen2Service] rootViewController changed - Reusing existing splash screen instance")
|
|
2339
|
-
|
|
2340
|
-
// 更新字典中的引用
|
|
2341
|
-
splashScreenControllers.removeValue(forKey: oldRootViewController)
|
|
2342
|
-
splashScreenControllers[newRootViewController] = oldController
|
|
2343
|
-
|
|
2344
|
-
// 从旧的父控制器分离
|
|
2345
|
-
splashVC.view.removeFromSuperview()
|
|
2346
|
-
splashVC.willMove(toParent: nil)
|
|
2347
|
-
if let oldParent = splashVC.parent {
|
|
2348
|
-
splashVC.removeFromParent()
|
|
2349
|
-
}
|
|
2350
|
-
|
|
2351
|
-
// 添加到新的 rootViewController
|
|
2352
|
-
newRootViewController.addChild(splashVC)
|
|
2353
|
-
newRootViewController.view.addSubview(splashVC.view)
|
|
2354
|
-
splashVC.view.translatesAutoresizingMaskIntoConstraints = false
|
|
2355
|
-
NSLayoutConstraint.activate([
|
|
2356
|
-
splashVC.view.topAnchor.constraint(equalTo: newRootViewController.view.topAnchor),
|
|
2357
|
-
splashVC.view.leadingAnchor.constraint(equalTo: newRootViewController.view.leadingAnchor),
|
|
2358
|
-
splashVC.view.trailingAnchor.constraint(equalTo: newRootViewController.view.trailingAnchor),
|
|
2359
|
-
splashVC.view.bottomAnchor.constraint(equalTo: newRootViewController.view.bottomAnchor)
|
|
2360
|
-
])
|
|
2361
|
-
newRootViewController.view.bringSubviewToFront(splashVC.view)
|
|
2362
|
-
splashVC.didMove(toParent: newRootViewController)
|
|
2363
|
-
splashVC.view.isHidden = false
|
|
2364
|
-
splashVC.view.alpha = 1.0
|
|
2365
|
-
|
|
2366
|
-
// 迁移完成后,确保隐私弹框被隐藏(如果用户已同意)
|
|
2367
|
-
splashVC.ensurePrivacyDialogHidden()
|
|
2368
|
-
|
|
2369
|
-
print("[SplashScreen2Service] rootViewController changed - Splash screen reused successfully")
|
|
2370
|
-
} else if let oldRootViewController = observingRootViewController,
|
|
2371
|
-
splashScreenControllers[oldRootViewController] == nil {
|
|
2372
|
-
// 旧 rootViewController 没有记录,说明之前没有成功创建 splash screen
|
|
2373
|
-
print("[SplashScreen2Service] rootViewController changed - No existing splash screen to reuse, creating new one")
|
|
2374
|
-
showSplashScreenFor(newRootViewController)
|
|
2375
|
-
}
|
|
2376
|
-
|
|
2377
|
-
// 先移除旧的监听器
|
|
2378
|
-
removeRootViewControllerListener()
|
|
2379
|
-
|
|
2380
|
-
// 重新添加监听器(这会设置新的 observingRootViewController 并显示 splash screen)
|
|
2381
|
-
// 注意:addRootViewControllerListener() 内部会调用 showSplashScreenFor,所以不需要在这里单独调用
|
|
2382
|
-
// 但如果 globalPreventAutoHide 为 true,且已经迁移了 splash screen,不需要重新添加监听器
|
|
2383
|
-
if !globalPreventAutoHide || splashScreenControllers[newRootViewController] == nil {
|
|
2384
|
-
addRootViewControllerListener()
|
|
2385
|
-
} else {
|
|
2386
|
-
print("[SplashScreen2Service] rootViewController changed - globalPreventAutoHide is true and splash screen already migrated, skipping addRootViewControllerListener")
|
|
2387
|
-
// 仍然需要更新 observingRootViewController 和添加监听器
|
|
2388
|
-
if let window = UIApplication.shared.keyWindow {
|
|
2389
|
-
window.addObserver(self, forKeyPath: "rootViewController", options: .new, context: nil)
|
|
2390
|
-
newRootViewController.addObserver(self, forKeyPath: "view", options: .new, context: nil)
|
|
2391
|
-
observingRootViewController = newRootViewController
|
|
2392
|
-
// 确保 splash screen 在最上层且可见
|
|
2393
|
-
if let controller = splashScreenControllers[newRootViewController],
|
|
2394
|
-
let splashVC = controller.splashViewControllerInstance {
|
|
2395
|
-
splashVC.view.isHidden = false
|
|
2396
|
-
splashVC.view.alpha = 1.0
|
|
2397
|
-
newRootViewController.view.bringSubviewToFront(splashVC.view)
|
|
2398
|
-
print("[SplashScreen2Service] rootViewController changed - Brought migrated splash screen to front and ensured visibility")
|
|
2399
|
-
}
|
|
2400
|
-
}
|
|
2401
|
-
}
|
|
2402
|
-
}
|
|
2403
|
-
} else if let rootViewController = object as? UIViewController, keyPath == "view" {
|
|
2404
|
-
if let newView = change?[.newKey] as? UIView,
|
|
2405
|
-
let viewController = newView.next as? UIViewController {
|
|
2406
|
-
print("[SplashScreen2Service] view changed for viewController: \\(viewController)")
|
|
2407
|
-
print("[SplashScreen2Service] view changed - globalPreventAutoHide: \\(globalPreventAutoHide)")
|
|
2408
|
-
|
|
2409
|
-
// 如果 globalPreventAutoHide 为 true,确保现有的 splash screen 保持显示
|
|
2410
|
-
if globalPreventAutoHide {
|
|
2411
|
-
if let controller = splashScreenControllers[viewController] {
|
|
2412
|
-
print("[SplashScreen2Service] view changed - globalPreventAutoHide is true, ensuring splash screen is visible")
|
|
2413
|
-
if let splashVC = controller.splashViewControllerInstance {
|
|
2414
|
-
splashVC.view.isHidden = false
|
|
2415
|
-
splashVC.view.alpha = 1.0
|
|
2416
|
-
viewController.view.bringSubviewToFront(splashVC.view)
|
|
2417
|
-
}
|
|
2418
|
-
return
|
|
2419
|
-
} else {
|
|
2420
|
-
// 如果 globalPreventAutoHide 为 true 但没有 splash screen,创建一个
|
|
2421
|
-
print("[SplashScreen2Service] view changed - globalPreventAutoHide is true but no splash screen, creating one")
|
|
2422
|
-
showSplashScreenFor(viewController)
|
|
2423
|
-
return
|
|
2424
|
-
}
|
|
2425
|
-
}
|
|
2426
|
-
|
|
2427
|
-
// 只有当 view 真正加载完成时才重新显示 splash screen
|
|
2428
|
-
// 避免在 view 创建过程中重复调用
|
|
2429
|
-
if viewController.view.superview != nil && splashScreenControllers[viewController] == nil {
|
|
2430
|
-
print("[SplashScreen2Service] View loaded, showing splash screen")
|
|
2431
|
-
showSplashScreenFor(viewController)
|
|
2432
|
-
} else if splashScreenControllers[viewController] != nil {
|
|
2433
|
-
print("[SplashScreen2Service] Splash screen already exists for this view controller, skipping")
|
|
2434
|
-
}
|
|
2435
|
-
}
|
|
2436
|
-
}
|
|
2437
|
-
}
|
|
2438
|
-
}
|
|
2439
|
-
|
|
2440
|
-
// 类似 EXSplashScreenViewController,管理单个 splash screen 的显示和隐藏
|
|
2441
|
-
public class SplashScreen2ViewController {
|
|
2442
|
-
private weak var splashViewController: SplashScreen2ViewController?
|
|
2443
|
-
private var autoHideEnabled: Bool = true
|
|
2444
|
-
private var splashScreenShown: Bool = false
|
|
2445
|
-
private var appContentAppeared: Bool = false
|
|
2446
|
-
|
|
2447
|
-
// 添加一个属性来访问 splashViewController,用于迁移
|
|
2448
|
-
var splashViewControllerInstance: SplashScreen2ViewController? {
|
|
2449
|
-
return splashViewController
|
|
2450
|
-
}
|
|
2451
|
-
|
|
2452
|
-
init(splashViewController: SplashScreen2ViewController) {
|
|
2453
|
-
self.splashViewController = splashViewController
|
|
2454
|
-
}
|
|
2455
|
-
|
|
2456
|
-
func show() {
|
|
2457
|
-
guard Thread.isMainThread else {
|
|
2458
|
-
DispatchQueue.main.async { [weak self] in
|
|
2459
|
-
self?.show()
|
|
2460
|
-
}
|
|
2461
|
-
return
|
|
2462
|
-
}
|
|
2463
|
-
|
|
2464
|
-
guard let splashVC = splashViewController else { return }
|
|
2465
|
-
|
|
2466
|
-
print("[SplashScreen2ViewController] show() called")
|
|
2467
|
-
print("[SplashScreen2ViewController] show() - splashVC.view.isHidden: \\(splashVC.view.isHidden)")
|
|
2468
|
-
print("[SplashScreen2ViewController] show() - splashVC.view.alpha: \\(splashVC.view.alpha)")
|
|
2469
|
-
print("[SplashScreen2ViewController] show() - splashVC.view.superview: \\(String(describing: splashVC.view.superview))")
|
|
2470
|
-
print("[SplashScreen2ViewController] show() - splashVC.view.window: \\(String(describing: splashVC.view.window))")
|
|
2471
|
-
|
|
2472
|
-
// 确保 view 可见
|
|
2473
|
-
splashVC.view.isHidden = false
|
|
2474
|
-
splashVC.view.alpha = 1.0
|
|
2475
|
-
|
|
2476
|
-
// 确保 WebView 也可见
|
|
2477
|
-
for subview in splashVC.view.subviews {
|
|
2478
|
-
if let webView = subview as? WKWebView {
|
|
2479
|
-
webView.isHidden = false
|
|
2480
|
-
webView.alpha = 1.0
|
|
2481
|
-
print("[SplashScreen2ViewController] show() - WebView visibility set: isHidden=\\(webView.isHidden), alpha=\\(webView.alpha)")
|
|
2482
|
-
}
|
|
2483
|
-
}
|
|
2484
|
-
|
|
2485
|
-
// 确保在最上层
|
|
2486
|
-
if let parent = splashVC.parent {
|
|
2487
|
-
parent.view.bringSubviewToFront(splashVC.view)
|
|
2488
|
-
print("[SplashScreen2ViewController] show() - Brought splashVC.view to front in parent")
|
|
2489
|
-
} else if let superview = splashVC.view.superview {
|
|
2490
|
-
superview.bringSubviewToFront(splashVC.view)
|
|
2491
|
-
print("[SplashScreen2ViewController] show() - Brought splashVC.view to front in superview")
|
|
2492
|
-
}
|
|
2493
|
-
|
|
2494
|
-
// 强制布局更新
|
|
2495
|
-
splashVC.view.setNeedsLayout()
|
|
2496
|
-
splashVC.view.layoutIfNeeded()
|
|
2497
|
-
|
|
2498
|
-
print("[SplashScreen2ViewController] show() - After show, splashVC.view.isHidden: \\(splashVC.view.isHidden)")
|
|
2499
|
-
print("[SplashScreen2ViewController] show() - After show, splashVC.view.alpha: \\(splashVC.view.alpha)")
|
|
2500
|
-
print("[SplashScreen2ViewController] show() - After show, splashVC.view.subviews.count: \\(splashVC.view.subviews.count)")
|
|
2501
|
-
|
|
2502
|
-
splashScreenShown = true
|
|
2503
|
-
}
|
|
2504
|
-
|
|
2505
|
-
func hide(force: Bool = false) {
|
|
2506
|
-
guard Thread.isMainThread else {
|
|
2507
|
-
DispatchQueue.main.async { [weak self] in
|
|
2508
|
-
self?.hide(force: force)
|
|
2509
|
-
}
|
|
2510
|
-
return
|
|
2511
|
-
}
|
|
2512
|
-
|
|
2513
|
-
print("[SplashScreen2ViewController] hide called, force: \\(force), autoHideEnabled: \\(autoHideEnabled)")
|
|
2514
|
-
print("[SplashScreen2ViewController] hide - Stack trace: \\(Thread.callStackSymbols.prefix(5).joined(separator: "\\n"))")
|
|
2515
|
-
|
|
2516
|
-
// 如果 preventAutoHide 被调用,且不是强制隐藏,则不执行隐藏操作
|
|
2517
|
-
if !force && !autoHideEnabled {
|
|
2518
|
-
print("[SplashScreen2ViewController] Auto hide is prevented, ignoring hide call (use force=true to override)")
|
|
2519
|
-
// 确保 splash screen 仍然可见
|
|
2520
|
-
if let splashVC = splashViewController {
|
|
2521
|
-
splashVC.view.isHidden = false
|
|
2522
|
-
splashVC.view.alpha = 1.0
|
|
2523
|
-
if let parent = splashVC.parent {
|
|
2524
|
-
parent.view.bringSubviewToFront(splashVC.view)
|
|
2525
|
-
} else if let superview = splashVC.view.superview {
|
|
2526
|
-
superview.bringSubviewToFront(splashVC.view)
|
|
2527
|
-
}
|
|
2528
|
-
}
|
|
2529
|
-
return
|
|
2530
|
-
}
|
|
2531
|
-
|
|
2532
|
-
guard let splashVC = splashViewController else {
|
|
2533
|
-
print("[SplashScreen2ViewController] hide - splashViewController is nil")
|
|
2534
|
-
return
|
|
2535
|
-
}
|
|
2536
|
-
|
|
2537
|
-
print("[SplashScreen2ViewController] hide - Proceeding with hide animation")
|
|
2538
|
-
|
|
2539
|
-
UIView.animate(withDuration: 0.3, animations: {
|
|
2540
|
-
splashVC.view.alpha = 0.0
|
|
2541
|
-
}) { _ in
|
|
2542
|
-
print("[SplashScreen2ViewController] hide - Animation completed, removing from superview")
|
|
2543
|
-
splashVC.view.removeFromSuperview()
|
|
2544
|
-
splashVC.willMove(toParent: nil)
|
|
2545
|
-
if let parent = splashVC.parent {
|
|
2546
|
-
splashVC.removeFromParent()
|
|
2547
|
-
}
|
|
2548
|
-
}
|
|
2549
|
-
|
|
2550
|
-
splashScreenShown = false
|
|
2551
|
-
// 注意:只有在强制隐藏时才重置 autoHideEnabled
|
|
2552
|
-
// 如果 preventAutoHide 被调用,autoHideEnabled 应该保持为 false
|
|
2553
|
-
if force {
|
|
2554
|
-
autoHideEnabled = true
|
|
2555
|
-
}
|
|
2556
|
-
}
|
|
2557
|
-
|
|
2558
|
-
func preventAutoHide() {
|
|
2559
|
-
print("[SplashScreen2ViewController] preventAutoHide called, autoHideEnabled: \\(autoHideEnabled)")
|
|
2560
|
-
guard autoHideEnabled else {
|
|
2561
|
-
print("[SplashScreen2ViewController] preventAutoHide - Already prevented, skipping")
|
|
2562
|
-
return
|
|
2563
|
-
}
|
|
2564
|
-
autoHideEnabled = false
|
|
2565
|
-
print("[SplashScreen2ViewController] preventAutoHide - Set autoHideEnabled to false")
|
|
2566
|
-
}
|
|
2567
|
-
|
|
2568
|
-
func needsHideOnAppContentDidAppear() -> Bool {
|
|
2569
|
-
if !appContentAppeared && autoHideEnabled {
|
|
2570
|
-
appContentAppeared = true
|
|
2571
|
-
return true
|
|
2572
|
-
}
|
|
2573
|
-
return false
|
|
2574
|
-
}
|
|
2575
|
-
|
|
2576
|
-
func needsShowOnAppContentWillReload() -> Bool {
|
|
2577
|
-
if !appContentAppeared {
|
|
2578
|
-
// 注意:如果 preventAutoHide 已经被调用,不应该重置 autoHideEnabled
|
|
2579
|
-
// 只有在 preventAutoHide 没有被调用时才重置
|
|
2580
|
-
if autoHideEnabled {
|
|
2581
|
-
autoHideEnabled = true
|
|
2582
|
-
}
|
|
2583
|
-
appContentAppeared = false
|
|
2584
|
-
return true
|
|
2585
|
-
}
|
|
2586
|
-
return false
|
|
2587
|
-
}
|
|
2588
|
-
}
|
|
2589
|
-
`;
|
|
2590
|
-
try {
|
|
2591
|
-
fs.writeFileSync(servicePath, serviceContent);
|
|
2592
|
-
}
|
|
2593
|
-
catch (error) {
|
|
2594
|
-
console.error(`[expo-splash-screen2] Failed to generate SplashScreen2Service.swift:`, error);
|
|
2595
|
-
throw error;
|
|
2596
|
-
}
|
|
2597
|
-
}
|
|
2598
|
-
function generateSplashScreen2ViewController(bundleIdentifier, projectRoot, iosPath, backgroundColor, projectName) {
|
|
2599
|
-
const targetDir = path.join(iosPath, projectName);
|
|
2600
|
-
if (!fs.existsSync(targetDir)) {
|
|
2601
|
-
fs.mkdirSync(targetDir, { recursive: true });
|
|
2602
|
-
}
|
|
2603
|
-
const viewControllerPath = path.join(targetDir, 'SplashScreen2ViewController.swift');
|
|
2604
|
-
const viewControllerContent = `import UIKit
|
|
2605
|
-
import WebKit
|
|
2606
|
-
|
|
2607
|
-
// 简化版 SplashScreen2ViewController,只用于 WebView 显示 HTML
|
|
2608
|
-
// 参考 expo-splash-screen 的架构,但使用 WebView 显示 HTML
|
|
2609
|
-
public class SplashScreen2ViewController: UIViewController {
|
|
2610
|
-
private var webView: WKWebView?
|
|
2611
|
-
private var webViewContainer: UIView?
|
|
2612
|
-
private let userDefaults = UserDefaults.standard
|
|
2613
|
-
|
|
2614
|
-
public static weak var appDelegate: AppDelegateProtocol?
|
|
2615
|
-
|
|
2616
|
-
public override func viewDidLoad() {
|
|
2617
|
-
super.viewDidLoad()
|
|
2618
|
-
|
|
2619
|
-
print("[SplashScreen2ViewController] viewDidLoad called")
|
|
2620
|
-
print("[SplashScreen2ViewController] viewDidLoad - view.frame: \\(view.frame)")
|
|
2621
|
-
print("[SplashScreen2ViewController] viewDidLoad - view.bounds: \\(view.bounds)")
|
|
2622
|
-
print("[SplashScreen2ViewController] viewDidLoad - view.superview: \\(String(describing: view.superview))")
|
|
2623
|
-
print("[SplashScreen2ViewController] viewDidLoad - view.window: \\(String(describing: view.window))")
|
|
2624
|
-
|
|
2625
|
-
// 设置 view 的背景色为传入的 backgroundColor
|
|
2626
|
-
// 将十六进制颜色转换为 UIColor
|
|
2627
|
-
let hexColor = "${backgroundColor}".uppercased().replacingOccurrences(of: "#", with: "")
|
|
2628
|
-
if hexColor.count == 6 {
|
|
2629
|
-
let r = CGFloat(Int(hexColor.prefix(2), radix: 16) ?? 0) / 255.0
|
|
2630
|
-
let g = CGFloat(Int(String(hexColor.dropFirst(2).prefix(2)), radix: 16) ?? 0) / 255.0
|
|
2631
|
-
let b = CGFloat(Int(hexColor.suffix(2), radix: 16) ?? 0) / 255.0
|
|
2632
|
-
view.backgroundColor = UIColor(red: r, green: g, blue: b, alpha: 1.0)
|
|
2633
|
-
} else {
|
|
2634
|
-
view.backgroundColor = .clear
|
|
2635
|
-
}
|
|
2636
|
-
|
|
2637
|
-
// 确保全屏显示
|
|
2638
|
-
edgesForExtendedLayout = .all
|
|
2639
|
-
|
|
2640
|
-
// 如果 view 已经有 superview,确保 frame 正确
|
|
2641
|
-
if let superview = view.superview {
|
|
2642
|
-
view.frame = superview.bounds
|
|
2643
|
-
print("[SplashScreen2ViewController] viewDidLoad - Updated view.frame to superview.bounds: \\(view.frame)")
|
|
2644
|
-
} else {
|
|
2645
|
-
// 如果没有 superview,使用屏幕尺寸
|
|
2646
|
-
view.frame = UIScreen.main.bounds
|
|
2647
|
-
print("[SplashScreen2ViewController] viewDidLoad - Set view.frame to UIScreen.main.bounds: \\(view.frame)")
|
|
2648
|
-
}
|
|
2649
|
-
|
|
2650
|
-
// 注册通知监听
|
|
2651
|
-
NotificationCenter.default.addObserver(
|
|
2652
|
-
self,
|
|
2653
|
-
selector: #selector(handlePreventAutoHide),
|
|
2654
|
-
name: NSNotification.Name("SplashHtmlPreventAutoHide"),
|
|
2655
|
-
object: nil
|
|
2656
|
-
)
|
|
2657
|
-
NotificationCenter.default.addObserver(
|
|
2658
|
-
self,
|
|
2659
|
-
selector: #selector(handleHide),
|
|
2660
|
-
name: NSNotification.Name("SplashHtmlHide"),
|
|
2661
|
-
object: nil
|
|
2662
|
-
)
|
|
2663
|
-
|
|
2664
|
-
setupWebView()
|
|
2665
|
-
}
|
|
2666
|
-
|
|
2667
|
-
deinit {
|
|
2668
|
-
// 移除通知监听
|
|
2669
|
-
NotificationCenter.default.removeObserver(self)
|
|
2670
|
-
}
|
|
2671
|
-
|
|
2672
|
-
@objc private func handlePreventAutoHide() {
|
|
2673
|
-
print("[SplashScreen2ViewController] handlePreventAutoHide called")
|
|
2674
|
-
// 通过 SplashScreen2Service 防止自动隐藏
|
|
2675
|
-
// 需要传递 parent view controller(通常是 rootViewController)
|
|
2676
|
-
if let parentVC = parent {
|
|
2677
|
-
SplashScreen2Service.shared.preventAutoHideFor(parentVC)
|
|
2678
|
-
} else if let rootVC = view.window?.rootViewController {
|
|
2679
|
-
SplashScreen2Service.shared.preventAutoHideFor(rootVC)
|
|
2680
|
-
} else {
|
|
2681
|
-
print("[SplashScreen2ViewController] handlePreventAutoHide - No parent or rootViewController found")
|
|
2682
|
-
}
|
|
2683
|
-
}
|
|
2684
|
-
|
|
2685
|
-
@objc private func handleHide() {
|
|
2686
|
-
print("[SplashScreen2ViewController] handleHide called")
|
|
2687
|
-
// 通过 SplashScreen2Service 隐藏开屏
|
|
2688
|
-
// 需要传递 parent view controller(通常是 rootViewController)
|
|
2689
|
-
if let parentVC = parent {
|
|
2690
|
-
SplashScreen2Service.shared.hideSplashScreenFor(parentVC, force: true)
|
|
2691
|
-
} else if let rootVC = view.window?.rootViewController {
|
|
2692
|
-
SplashScreen2Service.shared.hideSplashScreenFor(rootVC, force: true)
|
|
2693
|
-
} else {
|
|
2694
|
-
print("[SplashScreen2ViewController] handleHide - No parent or rootViewController found")
|
|
2695
|
-
}
|
|
2696
|
-
}
|
|
2697
|
-
|
|
2698
|
-
public override func viewWillAppear(_ animated: Bool) {
|
|
2699
|
-
super.viewWillAppear(animated)
|
|
2700
|
-
|
|
2701
|
-
// 强制设置 view 的 frame 为全屏
|
|
2702
|
-
if let window = view.window {
|
|
2703
|
-
view.frame = window.bounds
|
|
2704
|
-
} else {
|
|
2705
|
-
view.frame = UIScreen.main.bounds
|
|
2706
|
-
}
|
|
2707
|
-
|
|
2708
|
-
print("[SplashScreen2ViewController] viewWillAppear - view.frame: \\(view.frame)")
|
|
2709
|
-
print("[SplashScreen2ViewController] viewWillAppear - view.bounds: \\(view.bounds)")
|
|
2710
|
-
print("[SplashScreen2ViewController] viewWillAppear - UIScreen.main.bounds: \\(UIScreen.main.bounds)")
|
|
2711
|
-
}
|
|
2712
|
-
|
|
2713
|
-
public override func viewDidLayoutSubviews() {
|
|
2714
|
-
super.viewDidLayoutSubviews()
|
|
2715
|
-
|
|
2716
|
-
// 强制设置 view 的 frame 为全屏
|
|
2717
|
-
if let window = view.window {
|
|
2718
|
-
view.frame = window.bounds
|
|
2719
|
-
} else {
|
|
2720
|
-
view.frame = UIScreen.main.bounds
|
|
2721
|
-
}
|
|
2722
|
-
|
|
2723
|
-
// 确保 webView 也是全屏
|
|
2724
|
-
if let webView = webView {
|
|
2725
|
-
webView.frame = view.bounds
|
|
2726
|
-
print("[SplashScreen2ViewController] viewDidLayoutSubviews - webView.frame: \\(webView.frame)")
|
|
2727
|
-
print("[SplashScreen2ViewController] viewDidLayoutSubviews - webView.bounds: \\(webView.bounds)")
|
|
2728
|
-
}
|
|
2729
|
-
}
|
|
2730
|
-
|
|
2731
|
-
private func setupWebView() {
|
|
2732
|
-
let config = WKWebViewConfiguration()
|
|
2733
|
-
config.preferences.javaScriptEnabled = true
|
|
2734
|
-
config.allowsInlineMediaPlayback = true
|
|
2735
|
-
config.mediaTypesRequiringUserActionForPlayback = []
|
|
2736
|
-
|
|
2737
|
-
// 添加 JavaScript 接口
|
|
2738
|
-
let contentController = WKUserContentController()
|
|
2739
|
-
contentController.add(self, name: "agreePrivacyPolicy")
|
|
2740
|
-
contentController.add(self, name: "disagreePrivacyPolicy")
|
|
2741
|
-
contentController.add(self, name: "openPrivacyPolicy")
|
|
2742
|
-
config.userContentController = contentController
|
|
2743
|
-
|
|
2744
|
-
webView = WKWebView(frame: view.bounds, configuration: config)
|
|
2745
|
-
// 设置透明背景
|
|
2746
|
-
webView?.backgroundColor = .clear
|
|
2747
|
-
webView?.isOpaque = false
|
|
2748
|
-
webView?.scrollView.backgroundColor = .clear
|
|
2749
|
-
webView?.scrollView.showsVerticalScrollIndicator = false
|
|
2750
|
-
webView?.scrollView.showsHorizontalScrollIndicator = false
|
|
2751
|
-
webView?.scrollView.bounces = false
|
|
2752
|
-
webView?.scrollView.isScrollEnabled = false
|
|
2753
|
-
webView?.allowsLinkPreview = false
|
|
2754
|
-
webView?.allowsBackForwardNavigationGestures = false
|
|
2755
|
-
|
|
2756
|
-
if #available(iOS 11.0, *) {
|
|
2757
|
-
webView?.scrollView.contentInsetAdjustmentBehavior = .never
|
|
2758
|
-
}
|
|
2759
|
-
|
|
2760
|
-
guard let webView = webView else { return }
|
|
2761
|
-
|
|
2762
|
-
// 确保 view 的 frame 正确
|
|
2763
|
-
if view.frame == .zero {
|
|
2764
|
-
if let superview = view.superview {
|
|
2765
|
-
view.frame = superview.bounds
|
|
2766
|
-
} else {
|
|
2767
|
-
view.frame = UIScreen.main.bounds
|
|
2768
|
-
}
|
|
2769
|
-
print("[SplashScreen2ViewController] setupWebView - view.frame was zero, updated to: \\(view.frame)")
|
|
2770
|
-
}
|
|
2771
|
-
|
|
2772
|
-
// 确保 webView 的 frame 正确
|
|
2773
|
-
if webView.frame == .zero {
|
|
2774
|
-
webView.frame = view.bounds
|
|
2775
|
-
print("[SplashScreen2ViewController] setupWebView - webView.frame was zero, updated to: \\(webView.frame)")
|
|
2776
|
-
}
|
|
2777
|
-
|
|
2778
|
-
// 将 WebView 添加到 view 上
|
|
2779
|
-
view.addSubview(webView)
|
|
2780
|
-
print("[SplashScreen2ViewController] setupWebView - WebView added to view, view.subviews.count: \\(view.subviews.count)")
|
|
2781
|
-
|
|
2782
|
-
webView.translatesAutoresizingMaskIntoConstraints = false
|
|
2783
|
-
NSLayoutConstraint.activate([
|
|
2784
|
-
webView.topAnchor.constraint(equalTo: view.topAnchor),
|
|
2785
|
-
webView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
|
2786
|
-
webView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
|
|
2787
|
-
webView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
|
|
2788
|
-
])
|
|
2789
|
-
|
|
2790
|
-
// 强制布局更新
|
|
2791
|
-
view.setNeedsLayout()
|
|
2792
|
-
view.layoutIfNeeded()
|
|
2793
|
-
webView.setNeedsLayout()
|
|
2794
|
-
webView.layoutIfNeeded()
|
|
2795
|
-
|
|
2796
|
-
// 打印尺寸信息用于调试
|
|
2797
|
-
print("[SplashScreen2ViewController] setupWebView - view.frame: \\(view.frame)")
|
|
2798
|
-
print("[SplashScreen2ViewController] setupWebView - view.bounds: \\(view.bounds)")
|
|
2799
|
-
print("[SplashScreen2ViewController] setupWebView - view.superview: \\(String(describing: view.superview))")
|
|
2800
|
-
print("[SplashScreen2ViewController] setupWebView - view.window: \\(String(describing: view.window))")
|
|
2801
|
-
print("[SplashScreen2ViewController] setupWebView - view.isHidden: \\(view.isHidden)")
|
|
2802
|
-
print("[SplashScreen2ViewController] setupWebView - view.alpha: \\(view.alpha)")
|
|
2803
|
-
print("[SplashScreen2ViewController] setupWebView - webView.frame: \\(webView.frame)")
|
|
2804
|
-
print("[SplashScreen2ViewController] setupWebView - webView.bounds: \\(webView.bounds)")
|
|
2805
|
-
print("[SplashScreen2ViewController] setupWebView - webView.superview: \\(String(describing: webView.superview))")
|
|
2806
|
-
print("[SplashScreen2ViewController] setupWebView - webView.isHidden: \\(webView.isHidden)")
|
|
2807
|
-
print("[SplashScreen2ViewController] setupWebView - webView.alpha: \\(webView.alpha)")
|
|
2808
|
-
print("[SplashScreen2ViewController] setupWebView - webView.isOpaque: \\(webView.isOpaque)")
|
|
2809
|
-
print("[SplashScreen2ViewController] setupWebView - webView.backgroundColor: \\(String(describing: webView.backgroundColor))")
|
|
2810
|
-
print("[SplashScreen2ViewController] setupWebView - UIScreen.main.bounds: \\(UIScreen.main.bounds)")
|
|
2811
|
-
|
|
2812
|
-
// 确保 WebView 可见
|
|
2813
|
-
webView.isHidden = false
|
|
2814
|
-
webView.alpha = 1.0
|
|
2815
|
-
view.isHidden = false
|
|
2816
|
-
view.alpha = 1.0
|
|
2817
|
-
|
|
2818
|
-
print("[SplashScreen2ViewController] setupWebView - After setting visibility, webView.isHidden: \\(webView.isHidden), webView.alpha: \\(webView.alpha)")
|
|
2819
|
-
|
|
2820
|
-
webView.navigationDelegate = self
|
|
2821
|
-
|
|
2822
|
-
// 加载 HTML 文件
|
|
2823
|
-
if let htmlPath = Bundle.main.path(forResource: "index", ofType: "html") {
|
|
2824
|
-
if let htmlString = try? String(contentsOfFile: htmlPath, encoding: .utf8) {
|
|
2825
|
-
let baseURL = URL(fileURLWithPath: htmlPath).deletingLastPathComponent()
|
|
2826
|
-
webView.loadHTMLString(htmlString, baseURL: baseURL)
|
|
2827
|
-
}
|
|
2828
|
-
}
|
|
2829
|
-
}
|
|
2830
|
-
|
|
2831
|
-
private func handleAgreePrivacyPolicy() {
|
|
2832
|
-
userDefaults.set(true, forKey: "isAuth")
|
|
2833
|
-
userDefaults.synchronize()
|
|
2834
|
-
|
|
2835
|
-
let hideDialogJS = """
|
|
2836
|
-
(function() {
|
|
2837
|
-
try {
|
|
2838
|
-
if (typeof closePrivacyDialog === 'function') {
|
|
2839
|
-
closePrivacyDialog();
|
|
2840
|
-
}
|
|
2841
|
-
if (typeof hidePrivacyDialog === 'function') {
|
|
2842
|
-
hidePrivacyDialog();
|
|
2843
|
-
}
|
|
2844
|
-
return true;
|
|
2845
|
-
} catch (e) {
|
|
2846
|
-
return false;
|
|
2298
|
+
const entries = fs.readdirSync(iosPath, { withFileTypes: true });
|
|
2299
|
+
const projectDir = entries
|
|
2300
|
+
.filter((e) => e.isDirectory())
|
|
2301
|
+
.map((e) => e.name)
|
|
2302
|
+
.find((d) => fs.existsSync(path.join(iosPath, `${d}.xcodeproj`)));
|
|
2303
|
+
if (projectDir) {
|
|
2304
|
+
targetDir = path.join(iosPath, projectDir);
|
|
2305
|
+
}
|
|
2847
2306
|
}
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
DispatchQueue.main.async {
|
|
2853
|
-
if let appDelegate = SplashScreen2ViewController.appDelegate {
|
|
2854
|
-
appDelegate.startReactNativeIfNeeded()
|
|
2307
|
+
catch { }
|
|
2308
|
+
console.log(`[expo-splash-screen2] [iOS] targetDir: ${targetDir}`);
|
|
2309
|
+
if (!fs.existsSync(targetDir)) {
|
|
2310
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
2855
2311
|
}
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
try {
|
|
2879
|
-
// 重新注入 isAuth 状态
|
|
2880
|
-
window.isAuth = true;
|
|
2881
|
-
if (window.iOS) {
|
|
2882
|
-
window.iOS.getIsAuth = function() {
|
|
2883
|
-
return true;
|
|
2884
|
-
};
|
|
2885
|
-
}
|
|
2886
|
-
|
|
2887
|
-
// 确保弹框被隐藏
|
|
2888
|
-
if (typeof closePrivacyDialog === 'function') {
|
|
2889
|
-
closePrivacyDialog();
|
|
2890
|
-
}
|
|
2891
|
-
if (typeof hidePrivacyDialog === 'function') {
|
|
2892
|
-
hidePrivacyDialog();
|
|
2893
|
-
}
|
|
2894
|
-
|
|
2895
|
-
// 强制设置弹框状态为隐藏(如果 HTML 中有状态变量)
|
|
2896
|
-
if (typeof setShowModal === 'function') {
|
|
2897
|
-
setShowModal(false);
|
|
2898
|
-
}
|
|
2899
|
-
|
|
2900
|
-
return true;
|
|
2901
|
-
} catch (e) {
|
|
2902
|
-
console.error('Error hiding privacy dialog:', e);
|
|
2903
|
-
return false;
|
|
2904
|
-
}
|
|
2905
|
-
})();
|
|
2906
|
-
"""
|
|
2907
|
-
|
|
2908
|
-
webView?.evaluateJavaScript(hideDialogJS) { result, error in
|
|
2909
|
-
if let error = error {
|
|
2910
|
-
print("[SplashScreen2ViewController] ensurePrivacyDialogHidden JS error: \\(error)")
|
|
2911
|
-
} else {
|
|
2912
|
-
print("[SplashScreen2ViewController] ensurePrivacyDialogHidden - Privacy dialog hidden successfully")
|
|
2913
|
-
}
|
|
2914
|
-
}
|
|
2915
|
-
}
|
|
2916
|
-
|
|
2917
|
-
private func handleDisagreePrivacyPolicy() {
|
|
2918
|
-
exit(0)
|
|
2919
|
-
}
|
|
2920
|
-
|
|
2921
|
-
private func handleOpenPrivacyPolicy(url: String) {
|
|
2922
|
-
DispatchQueue.main.async {
|
|
2923
|
-
let privacyVC = SplashScreen2PrivacyPolicyViewController()
|
|
2924
|
-
privacyVC.url = url
|
|
2925
|
-
self.present(privacyVC, animated: true, completion: nil)
|
|
2926
|
-
}
|
|
2927
|
-
}
|
|
2928
|
-
}
|
|
2929
|
-
|
|
2930
|
-
extension SplashScreen2ViewController: WKNavigationDelegate {
|
|
2931
|
-
public func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
|
|
2932
|
-
// 确保 WebView 和 view 都可见
|
|
2933
|
-
webView.isHidden = false
|
|
2934
|
-
webView.alpha = 1.0
|
|
2935
|
-
view.isHidden = false
|
|
2936
|
-
view.alpha = 1.0
|
|
2937
|
-
|
|
2938
|
-
// 确保在最上层
|
|
2939
|
-
if let superview = view.superview {
|
|
2940
|
-
superview.bringSubviewToFront(view)
|
|
2941
|
-
}
|
|
2942
|
-
|
|
2943
|
-
// 获取 isAuth 状态并注入到 HTML
|
|
2944
|
-
let isAuth = userDefaults.bool(forKey: "isAuth")
|
|
2945
|
-
|
|
2946
|
-
// 注入 CSS 确保内容全屏显示,但不覆盖 HTML 中的背景色
|
|
2947
|
-
let css = """
|
|
2948
|
-
(function() {
|
|
2949
|
-
var style = document.createElement('style');
|
|
2950
|
-
style.innerHTML = "html, body { margin: 0 !important; padding: 0 !important; width: 100% !important; height: 100% !important; overflow: hidden !important; position: fixed !important; top: 0 !important; left: 0 !important; }";
|
|
2951
|
-
document.head.appendChild(style);
|
|
2952
|
-
})();
|
|
2953
|
-
"""
|
|
2954
|
-
webView.evaluateJavaScript(css, completionHandler: nil)
|
|
2955
|
-
|
|
2956
|
-
// 延迟执行,确保 HTML 中的函数已经定义
|
|
2957
|
-
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { [weak self] in
|
|
2958
|
-
guard let self = self else { return }
|
|
2959
|
-
|
|
2960
|
-
// 首先检查 HTML 中是否存在隐私协议相关的函数
|
|
2961
|
-
let checkPrivacyFunctionsJS = """
|
|
2962
|
-
(function() {
|
|
2963
|
-
var hasPrivacyFunctions =
|
|
2964
|
-
typeof checkAuthStatus === 'function' ||
|
|
2965
|
-
typeof showPrivacyDialog === 'function' ||
|
|
2966
|
-
typeof hidePrivacyDialog === 'function' ||
|
|
2967
|
-
typeof closePrivacyDialog === 'function' ||
|
|
2968
|
-
typeof agreePrivacyPolicy === 'function' ||
|
|
2969
|
-
typeof disagreePrivacyPolicy === 'function';
|
|
2970
|
-
return hasPrivacyFunctions;
|
|
2971
|
-
})();
|
|
2972
|
-
"""
|
|
2973
|
-
|
|
2974
|
-
self.webView?.evaluateJavaScript(checkPrivacyFunctionsJS) { result, error in
|
|
2975
|
-
if let error = error {
|
|
2976
|
-
print("[SplashScreen2ViewController] Error checking privacy functions: \\(error)")
|
|
2977
|
-
// 如果检查出错,默认认为没有隐私协议,设置 isAuth 为 true
|
|
2978
|
-
self.userDefaults.set(true, forKey: "isAuth")
|
|
2979
|
-
// 直接启动 React Native
|
|
2980
|
-
SplashScreen2ViewController.appDelegate?.startReactNativeIfNeeded()
|
|
2981
|
-
return
|
|
2312
|
+
const htmlContent = fs.readFileSync(sourcePath, 'utf-8');
|
|
2313
|
+
const htmlDir = path.dirname(sourcePath);
|
|
2314
|
+
console.log(`[expo-splash-screen2] [iOS] htmlDir: ${htmlDir}`);
|
|
2315
|
+
const imagePaths = extractImagePaths(htmlContent, htmlDir);
|
|
2316
|
+
console.log(`[expo-splash-screen2] [iOS] extractImagePaths found: ${imagePaths.length} images`);
|
|
2317
|
+
imagePaths.forEach(({ original, absolute }) => {
|
|
2318
|
+
console.log(`[expo-splash-screen2] [iOS] - original: ${original}, absolute: ${absolute}`);
|
|
2319
|
+
});
|
|
2320
|
+
const assetsDir = path.join(htmlDir, 'assets');
|
|
2321
|
+
console.log(`[expo-splash-screen2] [iOS] checking assetsDir: ${assetsDir}`);
|
|
2322
|
+
console.log(`[expo-splash-screen2] [iOS] assetsDir exists: ${fs.existsSync(assetsDir)}`);
|
|
2323
|
+
if (fs.existsSync(assetsDir) && fs.statSync(assetsDir).isDirectory()) {
|
|
2324
|
+
const allFiles = fs.readdirSync(assetsDir);
|
|
2325
|
+
console.log(`[expo-splash-screen2] [iOS] assetsDir all files: ${allFiles.join(', ')}`);
|
|
2326
|
+
const imageFiles = allFiles.filter(f => /\.(png|jpg|jpeg|gif|svg|webp|ico)$/i.test(f));
|
|
2327
|
+
console.log(`[expo-splash-screen2] [iOS] assetsDir image files: ${imageFiles.join(', ')}`);
|
|
2328
|
+
imageFiles.forEach(imgFile => {
|
|
2329
|
+
const srcPath = path.join(assetsDir, imgFile);
|
|
2330
|
+
const absolutePath = srcPath;
|
|
2331
|
+
imagePaths.push({ original: `./assets/${imgFile}`, absolute: absolutePath });
|
|
2332
|
+
console.log(`[expo-splash-screen2] [iOS] added from assets: ./assets/${imgFile} -> ${absolutePath}`);
|
|
2333
|
+
});
|
|
2982
2334
|
}
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
console.log('Calling closePrivacyDialog');
|
|
3013
|
-
closePrivacyDialog();
|
|
3014
|
-
}
|
|
3015
|
-
})();
|
|
3016
|
-
"""
|
|
3017
|
-
self.webView?.evaluateJavaScript(jsCode, completionHandler: { result, error in
|
|
3018
|
-
if let error = error {
|
|
3019
|
-
print("[SplashScreen2ViewController] Error evaluating JavaScript: \\(error)")
|
|
3020
|
-
}
|
|
3021
|
-
// 启动 React Native(isAuth=true 的逻辑)
|
|
3022
|
-
print("[SplashScreen2ViewController] Starting React Native with isAuth=true")
|
|
3023
|
-
SplashScreen2ViewController.appDelegate?.startReactNativeIfNeeded()
|
|
3024
|
-
})
|
|
3025
|
-
} else {
|
|
3026
|
-
// 如果存在隐私协议相关代码,按原来的逻辑处理
|
|
3027
|
-
let jsCode = """
|
|
3028
|
-
(function() {
|
|
3029
|
-
// 注入 isAuth 状态
|
|
3030
|
-
window.isAuth = \\(isAuth);
|
|
3031
|
-
window.iOS = {
|
|
3032
|
-
getIsAuth: function() {
|
|
3033
|
-
return \\(isAuth);
|
|
2335
|
+
const imagesDir = path.join(htmlDir, 'images');
|
|
2336
|
+
console.log(`[expo-splash-screen2] [iOS] checking imagesDir: ${imagesDir}`);
|
|
2337
|
+
console.log(`[expo-splash-screen2] [iOS] imagesDir exists: ${fs.existsSync(imagesDir)}`);
|
|
2338
|
+
if (fs.existsSync(imagesDir) && fs.statSync(imagesDir).isDirectory()) {
|
|
2339
|
+
const imageFiles = fs.readdirSync(imagesDir).filter(f => /\.(png|jpg|jpeg|gif|svg|webp|ico)$/i.test(f));
|
|
2340
|
+
console.log(`[expo-splash-screen2] [iOS] imagesDir image files: ${imageFiles.join(', ')}`);
|
|
2341
|
+
imageFiles.forEach(imgFile => {
|
|
2342
|
+
const srcPath = path.join(imagesDir, imgFile);
|
|
2343
|
+
const absolutePath = srcPath;
|
|
2344
|
+
imagePaths.push({ original: `./images/${imgFile}`, absolute: absolutePath });
|
|
2345
|
+
});
|
|
2346
|
+
}
|
|
2347
|
+
console.log(`[expo-splash-screen2] [iOS] total imagePaths after scanning: ${imagePaths.length}`);
|
|
2348
|
+
const imagePathMap = new Map();
|
|
2349
|
+
imagePaths.forEach(({ original, absolute }) => {
|
|
2350
|
+
const fileNameWithHash = path.basename(absolute);
|
|
2351
|
+
const fileNameWithoutHash = removeHashFromFileName(fileNameWithHash);
|
|
2352
|
+
const newPath = `./${fileNameWithoutHash}`;
|
|
2353
|
+
console.log(`[expo-splash-screen2] [iOS] processing: ${original} -> ${newPath} (file: ${absolute})`);
|
|
2354
|
+
const targetImagePath = path.join(targetDir, fileNameWithoutHash);
|
|
2355
|
+
console.log(`[expo-splash-screen2] [iOS] copying to: ${targetImagePath}`);
|
|
2356
|
+
if (fs.existsSync(absolute)) {
|
|
2357
|
+
fs.copyFileSync(absolute, targetImagePath);
|
|
2358
|
+
console.log(`[expo-splash-screen2] [iOS] copied successfully: ${fileNameWithoutHash}`);
|
|
2359
|
+
imagePathMap.set(original, newPath);
|
|
2360
|
+
const normalizedOriginal = original.startsWith('./') ? original : `./${original}`;
|
|
2361
|
+
imagePathMap.set(normalizedOriginal, newPath);
|
|
2362
|
+
if (original.startsWith('./')) {
|
|
2363
|
+
imagePathMap.set(original.substring(2), newPath);
|
|
3034
2364
|
}
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
// 如果已同意,隐藏弹框
|
|
3040
|
-
if (typeof hidePrivacyDialog === 'function') {
|
|
3041
|
-
hidePrivacyDialog();
|
|
2365
|
+
if (original.startsWith('./images/')) {
|
|
2366
|
+
imagePathMap.set(original, newPath);
|
|
2367
|
+
imagePathMap.set(original.substring(2), newPath);
|
|
2368
|
+
imagePathMap.set(original.substring(10), newPath);
|
|
3042
2369
|
}
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
2370
|
+
if (original.startsWith('./assets/')) {
|
|
2371
|
+
imagePathMap.set(original, newPath);
|
|
2372
|
+
imagePathMap.set(original.substring(2), newPath);
|
|
2373
|
+
const originalFileName = path.basename(original);
|
|
2374
|
+
if (originalFileName !== fileNameWithoutHash) {
|
|
2375
|
+
imagePathMap.set(original.replace(originalFileName, fileNameWithoutHash), newPath);
|
|
2376
|
+
}
|
|
3049
2377
|
}
|
|
3050
|
-
}
|
|
3051
|
-
})();
|
|
3052
|
-
"""
|
|
3053
|
-
self.webView?.evaluateJavaScript(jsCode, completionHandler: { result, error in
|
|
3054
|
-
if let error = error {
|
|
3055
|
-
print("[SplashScreen2ViewController] Error evaluating JavaScript: \\(error)")
|
|
3056
|
-
} else {
|
|
3057
|
-
// 在 JavaScript 执行完成后,根据 isAuth 状态决定是否启动 React Native
|
|
3058
|
-
if isAuth {
|
|
3059
|
-
SplashScreen2ViewController.appDelegate?.startReactNativeIfNeeded()
|
|
3060
|
-
}
|
|
3061
2378
|
}
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
}
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
`;
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
}
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
2379
|
+
});
|
|
2380
|
+
let updatedHtmlContent = htmlContent;
|
|
2381
|
+
updatedHtmlContent = updatedHtmlContent.replace(/<img([^>]+)src\s*=\s*["']([^"']+)["']/gi, (match, attrs, srcPath) => {
|
|
2382
|
+
if (srcPath.startsWith('http') || srcPath.startsWith('data:')) {
|
|
2383
|
+
return match;
|
|
2384
|
+
}
|
|
2385
|
+
const newPath = imagePathMap.get(srcPath) || imagePathMap.get(`./${srcPath}`) || imagePathMap.get(srcPath.replace(/^\.\//, ''));
|
|
2386
|
+
if (newPath) {
|
|
2387
|
+
return `<img${attrs}src="${newPath}"`;
|
|
2388
|
+
}
|
|
2389
|
+
return match;
|
|
2390
|
+
});
|
|
2391
|
+
updatedHtmlContent = updatedHtmlContent.replace(/url\s*\(\s*["']?([^"')]+)["']?\s*\)/gi, (match, urlPath) => {
|
|
2392
|
+
if (urlPath.startsWith('http') || urlPath.startsWith('data:')) {
|
|
2393
|
+
return match;
|
|
2394
|
+
}
|
|
2395
|
+
const imageExtensions = ['.png', '.jpg', '.jpeg', '.gif', '.webp', '.svg', '.ico'];
|
|
2396
|
+
const lowerPath = urlPath.toLowerCase();
|
|
2397
|
+
if (!imageExtensions.some(ext => lowerPath.includes(ext))) {
|
|
2398
|
+
return match;
|
|
2399
|
+
}
|
|
2400
|
+
const newPath = imagePathMap.get(urlPath) || imagePathMap.get(`./${urlPath}`) || imagePathMap.get(urlPath.replace(/^\.\//, ''));
|
|
2401
|
+
if (newPath) {
|
|
2402
|
+
return `url("${newPath}")`;
|
|
2403
|
+
}
|
|
2404
|
+
return match;
|
|
2405
|
+
});
|
|
2406
|
+
updatedHtmlContent = updatedHtmlContent.replace(/(["'])(\.\/images\/[^"']+\.(png|jpg|jpeg|gif|svg|webp|ico))(["'])/gi, (match, quote1, imgPath, ext, quote2) => {
|
|
2407
|
+
const fileName = path.basename(imgPath);
|
|
2408
|
+
const fileNameWithoutHash = removeHashFromFileName(fileName);
|
|
2409
|
+
const newPath = imagePathMap.get(`./images/${fileName}`) ||
|
|
2410
|
+
imagePathMap.get(`./images/${fileNameWithoutHash}`) ||
|
|
2411
|
+
imagePathMap.get(`./${fileName}`) ||
|
|
2412
|
+
imagePathMap.get(`./${fileNameWithoutHash}`) ||
|
|
2413
|
+
`./${fileNameWithoutHash}`;
|
|
2414
|
+
return `${quote1}${newPath}${quote2}`;
|
|
2415
|
+
});
|
|
2416
|
+
updatedHtmlContent = updatedHtmlContent.replace(/(["'])(\.\/assets\/[^"']+\.(png|jpg|jpeg|gif|svg|webp|ico))(["'])/gi, (match, quote1, imgPath, ext, quote2) => {
|
|
2417
|
+
const fileName = path.basename(imgPath);
|
|
2418
|
+
const fileNameWithoutHash = removeHashFromFileName(fileName);
|
|
2419
|
+
const newPath = imagePathMap.get(imgPath) ||
|
|
2420
|
+
imagePathMap.get(`./assets/${imgPath.substring(2)}`) ||
|
|
2421
|
+
`./${fileNameWithoutHash}`;
|
|
2422
|
+
return `${quote1}${newPath}${quote2}`;
|
|
2423
|
+
});
|
|
2424
|
+
updatedHtmlContent = updatedHtmlContent.replace(/(\.\/images\/[^\s"'`;,\)]+\.(png|jpg|jpeg|gif|svg|webp|ico))/gi, (match, imgPath) => {
|
|
2425
|
+
const fileName = path.basename(imgPath);
|
|
2426
|
+
const fileNameWithoutHash = removeHashFromFileName(fileName);
|
|
2427
|
+
const newPath = imagePathMap.get(`./images/${fileName}`) ||
|
|
2428
|
+
imagePathMap.get(`./images/${fileNameWithoutHash}`) ||
|
|
2429
|
+
imagePathMap.get(`./${fileName}`) ||
|
|
2430
|
+
imagePathMap.get(`./${fileNameWithoutHash}`) ||
|
|
2431
|
+
`./${fileNameWithoutHash}`;
|
|
2432
|
+
return newPath;
|
|
2433
|
+
});
|
|
2434
|
+
updatedHtmlContent = updatedHtmlContent.replace(/(\.\/assets\/[^\s"'`;,\)]+\.(png|jpg|jpeg|gif|svg|webp|ico))/gi, (match, imgPath) => {
|
|
2435
|
+
const fileName = path.basename(imgPath);
|
|
2436
|
+
const fileNameWithoutHash = removeHashFromFileName(fileName);
|
|
2437
|
+
const newPath = imagePathMap.get(imgPath) ||
|
|
2438
|
+
imagePathMap.get(`./assets/${imgPath.substring(2)}`) ||
|
|
2439
|
+
`./${fileNameWithoutHash}`;
|
|
2440
|
+
return newPath;
|
|
2441
|
+
});
|
|
2442
|
+
const targetPath = path.join(targetDir, 'index.html');
|
|
2443
|
+
fs.writeFileSync(targetPath, updatedHtmlContent, 'utf-8');
|
|
3117
2444
|
}
|
|
3118
2445
|
catch (error) {
|
|
3119
|
-
console.error(`[expo-splash-screen2]
|
|
3120
|
-
throw error;
|
|
2446
|
+
console.error(`[expo-splash-screen2] Error copying HTML file for iOS: ${error}`);
|
|
3121
2447
|
}
|
|
3122
2448
|
}
|
|
3123
2449
|
function copyBackgroundImageToIOS(projectRoot, backgroundImagePath, iosPath, projectName) {
|
|
@@ -3943,17 +3269,6 @@ function modifyAppDelegate(content, backgroundColor = '#ffffff') {
|
|
|
3943
3269
|
}
|
|
3944
3270
|
return content;
|
|
3945
3271
|
}
|
|
3946
|
-
function hexToStoryboardColor(hex) {
|
|
3947
|
-
hex = hex.replace('#', '');
|
|
3948
|
-
if (hex.length === 3) {
|
|
3949
|
-
hex = hex.split('').map(char => char + char).join('');
|
|
3950
|
-
}
|
|
3951
|
-
const r = parseInt(hex.substring(0, 2), 16) / 255;
|
|
3952
|
-
const g = parseInt(hex.substring(2, 4), 16) / 255;
|
|
3953
|
-
const b = parseInt(hex.substring(4, 6), 16) / 255;
|
|
3954
|
-
const a = hex.length === 8 ? parseInt(hex.substring(6, 8), 16) / 255 : 1;
|
|
3955
|
-
return { red: r, green: g, blue: b, alpha: a };
|
|
3956
|
-
}
|
|
3957
3272
|
function copyIconToIOS(projectRoot, iconPath, iosPath, projectName) {
|
|
3958
3273
|
try {
|
|
3959
3274
|
const sourcePath = path.resolve(projectRoot, iconPath);
|
|
@@ -4395,106 +3710,9 @@ const withIosSplashScreenStoryboard = (config, action) => {
|
|
|
4395
3710
|
action
|
|
4396
3711
|
});
|
|
4397
3712
|
};
|
|
4398
|
-
const withIosSplashScreenStoryboardBaseMod = (config) => {
|
|
4399
|
-
return config_plugins_1.BaseMods.withGeneratedBaseMods(config, {
|
|
4400
|
-
platform: 'ios',
|
|
4401
|
-
saveToInternal: true,
|
|
4402
|
-
skipEmptyMod: false,
|
|
4403
|
-
providers: {
|
|
4404
|
-
[STORYBOARD_MOD_NAME]: config_plugins_1.BaseMods.provider({
|
|
4405
|
-
isIntrospective: true,
|
|
4406
|
-
async getFilePath({ modRequest }) {
|
|
4407
|
-
return path.join(modRequest.platformProjectRoot, modRequest.projectName || 'MyNewExpoSplashDemo', STORYBOARD_FILE_PATH);
|
|
4408
|
-
},
|
|
4409
|
-
async read(filePath) {
|
|
4410
|
-
try {
|
|
4411
|
-
const contents = await fs.promises.readFile(filePath, 'utf8');
|
|
4412
|
-
try {
|
|
4413
|
-
const { Parser } = require('xml2js');
|
|
4414
|
-
const xml = await new Parser().parseStringPromise(contents);
|
|
4415
|
-
if (!xml || !xml.document) {
|
|
4416
|
-
console.warn('[expo-splash-screen2] Invalid XML structure: xml.document is missing, using template');
|
|
4417
|
-
return getTemplateAsync();
|
|
4418
|
-
}
|
|
4419
|
-
if (!xml.document.scenes || !Array.isArray(xml.document.scenes) || !xml.document.scenes[0]) {
|
|
4420
|
-
console.warn('[expo-splash-screen2] Invalid XML structure: scenes missing, using template');
|
|
4421
|
-
return getTemplateAsync();
|
|
4422
|
-
}
|
|
4423
|
-
if (!xml.document.resources) {
|
|
4424
|
-
xml.document.resources = [{}];
|
|
4425
|
-
}
|
|
4426
|
-
if (!Array.isArray(xml.document.resources)) {
|
|
4427
|
-
xml.document.resources = [xml.document.resources];
|
|
4428
|
-
}
|
|
4429
|
-
if (!xml.document.resources[0]) {
|
|
4430
|
-
xml.document.resources[0] = {};
|
|
4431
|
-
}
|
|
4432
|
-
if (!xml.document.resources[0].image) {
|
|
4433
|
-
xml.document.resources[0].image = [];
|
|
4434
|
-
}
|
|
4435
|
-
if (!xml.document.resources[0].namedColor) {
|
|
4436
|
-
xml.document.resources[0].namedColor = [];
|
|
4437
|
-
}
|
|
4438
|
-
return xml;
|
|
4439
|
-
}
|
|
4440
|
-
catch (parseError) {
|
|
4441
|
-
console.warn(`[expo-splash-screen2] Failed to parse XML: ${parseError}, using template`);
|
|
4442
|
-
return getTemplateAsync();
|
|
4443
|
-
}
|
|
4444
|
-
}
|
|
4445
|
-
catch (readError) {
|
|
4446
|
-
console.warn(`[expo-splash-screen2] Failed to read storyboard file: ${readError}, using template`);
|
|
4447
|
-
return getTemplateAsync();
|
|
4448
|
-
}
|
|
4449
|
-
},
|
|
4450
|
-
async write(filePath, { modResults, modRequest: { introspect } }) {
|
|
4451
|
-
if (introspect) {
|
|
4452
|
-
return;
|
|
4453
|
-
}
|
|
4454
|
-
await fs.promises.writeFile(filePath, toString(modResults));
|
|
4455
|
-
}
|
|
4456
|
-
})
|
|
4457
|
-
}
|
|
4458
|
-
});
|
|
4459
|
-
};
|
|
4460
3713
|
function modifyInfoPlist(plist) {
|
|
4461
3714
|
return plist;
|
|
4462
3715
|
}
|
|
4463
|
-
const addSplashSourceFiles = (proj, projectName, iosPath, projectRoot) => {
|
|
4464
|
-
const sourceFiles = [
|
|
4465
|
-
'SplashScreen2ViewController.swift',
|
|
4466
|
-
'SplashScreen2PrivacyPolicyViewController.swift',
|
|
4467
|
-
'SplashScreen2Module.swift',
|
|
4468
|
-
'SplashScreen2Service.swift'
|
|
4469
|
-
];
|
|
4470
|
-
sourceFiles.forEach((fileName) => {
|
|
4471
|
-
const filePath = path.join(iosPath, projectName, fileName);
|
|
4472
|
-
if (!fs.existsSync(filePath)) {
|
|
4473
|
-
console.warn(`[expo-splash-screen2] Swift file ${fileName} does not exist at ${filePath}, skipping`);
|
|
4474
|
-
return;
|
|
4475
|
-
}
|
|
4476
|
-
const relativeFilePath = `${projectName}/${fileName}`;
|
|
4477
|
-
if (proj.hasFile(relativeFilePath) || proj.hasFile(`../${projectName}/${fileName}`)) {
|
|
4478
|
-
return;
|
|
4479
|
-
}
|
|
4480
|
-
try {
|
|
4481
|
-
const target = proj.getFirstTarget();
|
|
4482
|
-
if (!target) {
|
|
4483
|
-
console.error(`[expo-splash-screen2] Failed to find target for source file ${fileName}`);
|
|
4484
|
-
return;
|
|
4485
|
-
}
|
|
4486
|
-
const groupUuid = proj.findPBXGroupKey({ name: projectName });
|
|
4487
|
-
if (!groupUuid) {
|
|
4488
|
-
console.error(`[expo-splash-screen2] Failed to find group "${projectName}" for source file ${fileName}`);
|
|
4489
|
-
return;
|
|
4490
|
-
}
|
|
4491
|
-
proj.addSourceFile(`${projectName}/${fileName}`, { target: target.uuid }, groupUuid);
|
|
4492
|
-
}
|
|
4493
|
-
catch (error) {
|
|
4494
|
-
console.error(`[expo-splash-screen2] Error adding source file ${fileName}:`, error);
|
|
4495
|
-
}
|
|
4496
|
-
});
|
|
4497
|
-
};
|
|
4498
3716
|
const addSplashResourceFiles = (proj, projectName, iosPath, projectRoot, pluginConfig, config) => {
|
|
4499
3717
|
const resourceFiles = ['index.html'];
|
|
4500
3718
|
if (config?.icon) {
|
|
@@ -4748,6 +3966,10 @@ function setupImageMode(config, pluginConfig) {
|
|
|
4748
3966
|
stylesJSON.resources.style = modifyStylesForImageMode(stylesJSON.resources.style);
|
|
4749
3967
|
return config;
|
|
4750
3968
|
});
|
|
3969
|
+
config = (0, config_plugins_1.withAndroidManifest)(config, (config) => {
|
|
3970
|
+
config.modResults = modifyAndroidManifestForImageMode(config.modResults, packageName);
|
|
3971
|
+
return config;
|
|
3972
|
+
});
|
|
4751
3973
|
config = (0, config_plugins_1.withMainActivity)(config, (config) => {
|
|
4752
3974
|
const imageResourceName = 'splash_background_image';
|
|
4753
3975
|
config.modResults.contents = modifyMainActivityForImageMode(config.modResults.contents, packageName, imageResourceName);
|