expo-splash-screen2 1.3.0 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-splash-screen2",
3
- "version": "1.3.0",
3
+ "version": "1.3.1",
4
4
  "description": "Enhance the development experience on the basis of expo-splash-screen with web and .9",
5
5
  "main": "build",
6
6
  "types": "build",
@@ -17,6 +17,7 @@
17
17
  "expo-module.config.json",
18
18
  "scripts",
19
19
  "expo-splash-web",
20
+ "!tests",
20
21
  "!**/__tests__",
21
22
  "!**/__fixtures__",
22
23
  "!**/__mocks__",
@@ -71,6 +72,7 @@
71
72
  "@types/xml2js": "^0.4.14",
72
73
  "expo-module-scripts": "^3.0.0",
73
74
  "expo-modules-core": "*",
75
+ "js-yaml": "^4.1.1",
74
76
  "standard-version": "^9.5.0",
75
77
  "typescript": "^5.0.0"
76
78
  },
@@ -84,6 +84,7 @@ function executeBuildSplashWeb(projectRoot) {
84
84
  async function bundleSplashWeb(projectRoot) {
85
85
  const sourceDir = 'expo-splash-web';
86
86
  const dir = path.join(projectRoot, sourceDir);
87
+ console.log('[expo-splash-screen8] bundleSplashWeb dir:', dir);
87
88
  if (!fs.existsSync(dir)) {
88
89
  throw new Error(`[expo-splash-screen2] Directory "${sourceDir}" not found in project root. Please create the "${sourceDir}" directory with your HTML, CSS, and JavaScript files.`);
89
90
  }
@@ -661,17 +662,23 @@ function copyHtmlFile(projectRoot, androidMainPath, localHtmlPath) {
661
662
  console.error(`[expo-splash-screen2] Error copying HTML file: ${error}`);
662
663
  }
663
664
  }
664
- function generateCustomSplashActivity(packageName, projectRoot, androidMainPath, backgroundColor) {
665
+ function generateCustomSplashActivity(packageName, projectRoot, androidMainPath, backgroundColor, edgeToEdgeEnabled) {
666
+ console.log(`[expo-splash-screen5] 666:`, edgeToEdgeEnabled);
665
667
  const javaDir = path.join(androidMainPath, 'java', ...packageName.split('.'));
666
668
  if (!fs.existsSync(javaDir)) {
667
669
  fs.mkdirSync(javaDir, { recursive: true });
668
670
  }
669
671
  const activityPath = path.join(javaDir, `SplashScreen2Activity.kt`);
670
- const activityContent = (0, android_1.replaceTemplatePlaceholders)(android_1.ANDROID_TEMPLATES.customSplashActivity, {
672
+ let activityContent = (0, android_1.replaceTemplatePlaceholders)(android_1.ANDROID_TEMPLATES.customSplashActivity, {
671
673
  packageName,
672
674
  activityName: CUSTOM_SPLASH_ACTIVITY_NAME,
673
675
  backgroundColor,
674
676
  });
677
+ console.error(`[expo-splash-screen3] 8888:`, edgeToEdgeEnabled);
678
+ if (edgeToEdgeEnabled === true) {
679
+ activityContent = activityContent.replace(/import androidx\.core\.view\.WindowCompat\s*\n/, '');
680
+ activityContent = activityContent.replace(/\s*\/\/\s*Let content extend below status bar and navigation bar instead of hiding them\s*\n\s*WindowCompat\.setDecorFitsSystemWindows\(window,\s*false\)\s*\n/, '');
681
+ }
675
682
  try {
676
683
  fs.writeFileSync(activityPath, activityContent);
677
684
  }
@@ -680,17 +687,21 @@ function generateCustomSplashActivity(packageName, projectRoot, androidMainPath,
680
687
  throw error;
681
688
  }
682
689
  }
683
- function generateCustomSplashActivityForBlendMode(packageName, projectRoot, androidMainPath, imageResourceName) {
690
+ function generateCustomSplashActivityForBlendMode(packageName, projectRoot, androidMainPath, imageResourceName, edgeToEdgeEnabled) {
684
691
  const javaDir = path.join(androidMainPath, 'java', ...packageName.split('.'));
685
692
  if (!fs.existsSync(javaDir)) {
686
693
  fs.mkdirSync(javaDir, { recursive: true });
687
694
  }
688
695
  const activityPath = path.join(javaDir, `SplashScreen2Activity.kt`);
689
- const activityContent = (0, android_1.replaceTemplatePlaceholders)(android_1.ANDROID_TEMPLATES.customSplashActivity, {
696
+ let activityContent = (0, android_1.replaceTemplatePlaceholders)(android_1.ANDROID_TEMPLATES.customSplashActivity, {
690
697
  packageName,
691
698
  activityName: CUSTOM_SPLASH_ACTIVITY_NAME,
692
699
  backgroundColor: '#ffffff',
693
700
  });
701
+ if (edgeToEdgeEnabled === true) {
702
+ activityContent = activityContent.replace(/import androidx\.core\.view\.WindowCompat\s*\n/, '');
703
+ activityContent = activityContent.replace(/\s*\/\/\s*Let content extend below status bar and navigation bar instead of hiding them\s*\n\s*WindowCompat\.setDecorFitsSystemWindows\(window,\s*false\)\s*\n/, '');
704
+ }
694
705
  try {
695
706
  fs.writeFileSync(activityPath, activityContent);
696
707
  }
@@ -1248,7 +1259,7 @@ function modifyMainActivityForNormalMode(content, packageName, backgroundColor,
1248
1259
  modifiedContent.substring(lastBraceIndex));
1249
1260
  }
1250
1261
  }
1251
- function modifyMainActivity(content, packageName, backgroundColor) {
1262
+ function modifyMainActivity(content, packageName, backgroundColor, edgeToEdgeEnabled) {
1252
1263
  const classMatch = content.match(/class\s+MainActivity\s*[^:]*:/);
1253
1264
  if (!classMatch) {
1254
1265
  console.warn('[expo-splash-screen2] MainActivity class not found');
@@ -1256,6 +1267,7 @@ function modifyMainActivity(content, packageName, backgroundColor) {
1256
1267
  }
1257
1268
  const hasWebViewCode = content.includes('setupWebViewContainer') || content.includes('webViewContainer');
1258
1269
  const hasCompanionObject = content.includes('companion object') && content.includes('actionStart');
1270
+ const needsWindowCompat = edgeToEdgeEnabled !== true;
1259
1271
  let hasImports = content.includes('import android.os.Build') &&
1260
1272
  content.includes('import android.os.Handler') &&
1261
1273
  content.includes('import android.os.Looper') &&
@@ -1263,8 +1275,11 @@ function modifyMainActivity(content, packageName, backgroundColor) {
1263
1275
  content.includes('import android.view.ViewGroup') &&
1264
1276
  content.includes('import android.webkit.WebView') &&
1265
1277
  content.includes('import android.webkit.WebViewClient') &&
1266
- content.includes('import androidx.core.view.WindowCompat');
1278
+ (!needsWindowCompat || content.includes('import androidx.core.view.WindowCompat'));
1267
1279
  let modifiedContent = content;
1280
+ if (edgeToEdgeEnabled === true && content.includes('import androidx.core.view.WindowCompat')) {
1281
+ modifiedContent = modifiedContent.replace(/import androidx\.core\.view\.WindowCompat\s*\n/, '');
1282
+ }
1268
1283
  if (!hasImports) {
1269
1284
  const importsToAddList = [
1270
1285
  'import android.os.Build',
@@ -1274,9 +1289,11 @@ function modifyMainActivity(content, packageName, backgroundColor) {
1274
1289
  'import android.view.ViewGroup',
1275
1290
  'import android.webkit.WebView',
1276
1291
  'import android.webkit.WebViewClient',
1277
- 'import androidx.core.view.WindowCompat'
1278
1292
  ];
1279
- const missingImports = importsToAddList.filter(imp => !content.includes(imp));
1293
+ if (needsWindowCompat) {
1294
+ importsToAddList.push('import androidx.core.view.WindowCompat');
1295
+ }
1296
+ const missingImports = importsToAddList.filter(imp => !modifiedContent.includes(imp));
1280
1297
  if (missingImports.length > 0) {
1281
1298
  const lastImportIndex = modifiedContent.lastIndexOf('import ');
1282
1299
  if (lastImportIndex !== -1) {
@@ -1319,7 +1336,10 @@ function modifyMainActivity(content, packageName, backgroundColor) {
1319
1336
  const hasSetupWebViewContainer = onCreateContent.includes('setupWebViewContainer');
1320
1337
  const hasHandlerPost = onCreateContent.includes('Handler(Looper.getMainLooper()).post');
1321
1338
  const codeBlockExists = hasWindowCompat && hasSetupWebViewContainer && hasHandlerPost;
1322
- if (codeBlockExists) {
1339
+ if (edgeToEdgeEnabled === true && hasWindowCompat) {
1340
+ cleanedOnCreateContent = cleanedOnCreateContent.replace(/\s*\/\/\s*Let content extend below status bar and navigation bar\s*\n\s*WindowCompat\.setDecorFitsSystemWindows\(window,\s*false\)\s*\n\s*/, '');
1341
+ }
1342
+ else if (codeBlockExists && edgeToEdgeEnabled !== true) {
1323
1343
  const lines = onCreateContent.split('\n');
1324
1344
  const filteredLines = [];
1325
1345
  let inCodeBlock = false;
@@ -1351,6 +1371,26 @@ function modifyMainActivity(content, packageName, backgroundColor) {
1351
1371
  filteredLines.push(line);
1352
1372
  }
1353
1373
  cleanedOnCreateContent = filteredLines.join('\n');
1374
+ const superOnCreateIndex = cleanedOnCreateContent.indexOf('super.onCreate');
1375
+ if (superOnCreateIndex !== -1) {
1376
+ let superOnCreateEndIndex = cleanedOnCreateContent.indexOf('\n', superOnCreateIndex);
1377
+ if (superOnCreateEndIndex === -1) {
1378
+ superOnCreateEndIndex = cleanedOnCreateContent.length;
1379
+ }
1380
+ else {
1381
+ superOnCreateEndIndex = superOnCreateEndIndex + 1;
1382
+ }
1383
+ const windowSettingsCode = `
1384
+ WindowCompat.setDecorFitsSystemWindows(window, false)
1385
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
1386
+ window.isNavigationBarContrastEnforced = false
1387
+ }
1388
+ window.statusBarColor = android.graphics.Color.TRANSPARENT
1389
+ window.navigationBarColor = android.graphics.Color.TRANSPARENT`;
1390
+ cleanedOnCreateContent = cleanedOnCreateContent.substring(0, superOnCreateEndIndex) +
1391
+ windowSettingsCode + '\n' +
1392
+ cleanedOnCreateContent.substring(superOnCreateEndIndex);
1393
+ }
1354
1394
  }
1355
1395
  const setThemeRegex = /(\s*\/\/\s*[^\n]*\n)*\s*setTheme\s*\(\s*R\.style\.AppTheme\s*\)\s*;?\s*\n?/g;
1356
1396
  cleanedOnCreateContent = cleanedOnCreateContent.replace(setThemeRegex, '');
@@ -1383,16 +1423,40 @@ function modifyMainActivity(content, packageName, backgroundColor) {
1383
1423
  const hasSetupWebViewContainer = onCreateContent.includes('setupWebViewContainer');
1384
1424
  const hasHandlerPost = onCreateContent.includes('Handler(Looper.getMainLooper()).post');
1385
1425
  const codeBlockExists = hasWindowCompat && hasSetupWebViewContainer && hasHandlerPost;
1386
- if (!codeBlockExists) {
1387
- const superOnCreateIndex = onCreateContent.indexOf('super.onCreate');
1426
+ const hasOnlyWindowSettings = hasWindowCompat && !hasSetupWebViewContainer && !hasHandlerPost;
1427
+ const needsWindowCompat = edgeToEdgeEnabled !== true;
1428
+ const shouldAddCode = !codeBlockExists && !hasOnlyWindowSettings && (needsWindowCompat && !hasWindowCompat || (!hasSetupWebViewContainer || !hasHandlerPost));
1429
+ if (shouldAddCode) {
1430
+ let cleanedOnCreateForAdd = onCreateContent;
1431
+ const hasExistingWindowColors = cleanedOnCreateForAdd.includes('window.statusBarColor') || cleanedOnCreateForAdd.includes('window.navigationBarColor');
1432
+ const hasExistingWindowCompat = cleanedOnCreateForAdd.includes('WindowCompat.setDecorFitsSystemWindows');
1433
+ if (hasExistingWindowColors) {
1434
+ cleanedOnCreateForAdd = cleanedOnCreateForAdd.replace(/\s*if\s*\(Build\.VERSION\.SDK_INT\s*>=\s*Build\.VERSION_CODES\.Q\)\s*\{\s*\n\s*window\.isNavigationBarContrastEnforced\s*=\s*false\s*\n\s*\}\s*\n?/g, '');
1435
+ cleanedOnCreateForAdd = cleanedOnCreateForAdd.replace(/\s*window\.statusBarColor\s*=\s*android\.graphics\.Color\.TRANSPARENT\s*\n?/g, '');
1436
+ cleanedOnCreateForAdd = cleanedOnCreateForAdd.replace(/\s*window\.navigationBarColor\s*=\s*android\.graphics\.Color\.TRANSPARENT\s*\n?/g, '');
1437
+ }
1438
+ if (hasExistingWindowCompat) {
1439
+ cleanedOnCreateForAdd = cleanedOnCreateForAdd.replace(/\s*\/\/\s*Let content extend below status bar and navigation bar\s*\n\s*WindowCompat\.setDecorFitsSystemWindows\(window,\s*false\)\s*\n\s*/, '');
1440
+ }
1441
+ const superOnCreateIndex = cleanedOnCreateForAdd.indexOf('super.onCreate');
1388
1442
  if (superOnCreateIndex !== -1) {
1389
- const superOnCreateEndIndex = onCreateContent.indexOf('\n', superOnCreateIndex);
1390
- if (superOnCreateEndIndex !== -1) {
1391
- const setupCall = android_1.ANDROID_TEMPLATES.mainActivityOnCreateCode;
1392
- modifiedContent = modifiedContent.substring(0, onCreateIndex + superOnCreateEndIndex + 1) +
1393
- setupCall + '\n' +
1394
- modifiedContent.substring(onCreateIndex + superOnCreateEndIndex + 1);
1443
+ let superOnCreateEndIndex = cleanedOnCreateForAdd.indexOf('\n', superOnCreateIndex);
1444
+ if (superOnCreateEndIndex === -1) {
1445
+ superOnCreateEndIndex = cleanedOnCreateForAdd.length;
1395
1446
  }
1447
+ else {
1448
+ superOnCreateEndIndex = superOnCreateEndIndex + 1;
1449
+ }
1450
+ let setupCall = android_1.ANDROID_TEMPLATES.mainActivityOnCreateCode;
1451
+ if (edgeToEdgeEnabled === true) {
1452
+ setupCall = setupCall.replace(/\s*\/\/\s*Let content extend below status bar and navigation bar\s*\n\s*WindowCompat\.setDecorFitsSystemWindows\(window,\s*false\)\s*\n\s*/, '');
1453
+ }
1454
+ const newOnCreateContent = cleanedOnCreateForAdd.substring(0, superOnCreateEndIndex) +
1455
+ setupCall + '\n' +
1456
+ cleanedOnCreateForAdd.substring(superOnCreateEndIndex);
1457
+ modifiedContent = modifiedContent.substring(0, onCreateIndex) +
1458
+ newOnCreateContent +
1459
+ modifiedContent.substring(onCreateEndIndex);
1396
1460
  }
1397
1461
  }
1398
1462
  }
@@ -1490,12 +1554,12 @@ function modifyMainActivity(content, packageName, backgroundColor) {
1490
1554
  }
1491
1555
  return modifiedContent;
1492
1556
  }
1493
- function modifyMainActivityForBlendMode(content, packageName, imageResourceName) {
1557
+ function modifyMainActivityForBlendMode(content, packageName, imageResourceName, edgeToEdgeEnabled) {
1494
1558
  if (content.includes('splashImageViewContainer') && content.includes('setupSplashImageView') &&
1495
1559
  content.includes('onContentChanged') && content.includes('setupWebViewContainer')) {
1496
1560
  return content;
1497
1561
  }
1498
- let modifiedContent = modifyMainActivity(content, packageName, '#ffffff');
1562
+ let modifiedContent = modifyMainActivity(content, packageName, '#ffffff', edgeToEdgeEnabled);
1499
1563
  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
1564
  if (fitsSystemWindowsPattern.test(modifiedContent)) {
1501
1565
  modifiedContent = modifiedContent.replace(fitsSystemWindowsPattern, '$1$3');
@@ -1521,8 +1585,73 @@ function modifyMainActivityForBlendMode(content, packageName, imageResourceName)
1521
1585
  }
1522
1586
  const onCreateContent = modifiedContent.substring(onCreateIndex, onCreateEndIndex);
1523
1587
  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, '');
1588
+ const hasWindowCompat = onCreateContent.includes('WindowCompat.setDecorFitsSystemWindows');
1589
+ const hasSetupWebViewContainer = onCreateContent.includes('setupWebViewContainer');
1590
+ const hasHandlerPost = onCreateContent.includes('Handler(Looper.getMainLooper()).post');
1591
+ if (hasSetupWebViewContainer || hasHandlerPost) {
1592
+ const lines = onCreateContent.split('\n');
1593
+ const filteredLines = [];
1594
+ let inHandlerBlock = false;
1595
+ let handlerBraceCount = 0;
1596
+ let foundHandlerStart = false;
1597
+ for (let i = 0; i < lines.length; i++) {
1598
+ const line = lines[i];
1599
+ if (!inHandlerBlock && line.includes('Handler(Looper.getMainLooper()).post')) {
1600
+ inHandlerBlock = true;
1601
+ foundHandlerStart = true;
1602
+ continue;
1603
+ }
1604
+ if (inHandlerBlock) {
1605
+ handlerBraceCount += (line.match(/\{/g) || []).length;
1606
+ handlerBraceCount -= (line.match(/\}/g) || []).length;
1607
+ if (handlerBraceCount <= 0) {
1608
+ inHandlerBlock = false;
1609
+ handlerBraceCount = 0;
1610
+ foundHandlerStart = false;
1611
+ continue;
1612
+ }
1613
+ continue;
1614
+ }
1615
+ if (line.includes('setupWebViewContainer')) {
1616
+ continue;
1617
+ }
1618
+ filteredLines.push(line);
1619
+ }
1620
+ cleanedOnCreateContent = filteredLines.join('\n');
1621
+ }
1622
+ if (edgeToEdgeEnabled !== true) {
1623
+ const hasWindowCompatAfterClean = cleanedOnCreateContent.includes('WindowCompat.setDecorFitsSystemWindows');
1624
+ const hasStatusBarColor = cleanedOnCreateContent.includes('window.statusBarColor');
1625
+ const hasNavigationBarColor = cleanedOnCreateContent.includes('window.navigationBarColor');
1626
+ const hasIsNavigationBarContrastEnforced = cleanedOnCreateContent.includes('isNavigationBarContrastEnforced');
1627
+ if (!hasWindowCompatAfterClean || !hasStatusBarColor || !hasNavigationBarColor || !hasIsNavigationBarContrastEnforced) {
1628
+ cleanedOnCreateContent = cleanedOnCreateContent.replace(/\s*\/\/\s*Let content extend below status bar and navigation bar\s*\n\s*/g, '');
1629
+ cleanedOnCreateContent = cleanedOnCreateContent.replace(/\s*WindowCompat\.setDecorFitsSystemWindows\(window,\s*false\)\s*\n?/g, '');
1630
+ cleanedOnCreateContent = cleanedOnCreateContent.replace(/\s*if\s*\(Build\.VERSION\.SDK_INT\s*>=\s*Build\.VERSION_CODES\.Q\)\s*\{\s*\n\s*window\.isNavigationBarContrastEnforced\s*=\s*false\s*\n\s*\}\s*\n?/g, '');
1631
+ cleanedOnCreateContent = cleanedOnCreateContent.replace(/\s*window\.statusBarColor\s*=\s*android\.graphics\.Color\.TRANSPARENT\s*\n?/g, '');
1632
+ cleanedOnCreateContent = cleanedOnCreateContent.replace(/\s*window\.navigationBarColor\s*=\s*android\.graphics\.Color\.TRANSPARENT\s*\n?/g, '');
1633
+ const superOnCreateIndex = cleanedOnCreateContent.indexOf('super.onCreate');
1634
+ if (superOnCreateIndex !== -1) {
1635
+ let superOnCreateEndIndex = cleanedOnCreateContent.indexOf('\n', superOnCreateIndex);
1636
+ if (superOnCreateEndIndex === -1) {
1637
+ superOnCreateEndIndex = cleanedOnCreateContent.length;
1638
+ }
1639
+ else {
1640
+ superOnCreateEndIndex = superOnCreateEndIndex + 1;
1641
+ }
1642
+ const windowSettingsCode = `
1643
+ WindowCompat.setDecorFitsSystemWindows(window, false)
1644
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
1645
+ window.isNavigationBarContrastEnforced = false
1646
+ }
1647
+ window.statusBarColor = android.graphics.Color.TRANSPARENT
1648
+ window.navigationBarColor = android.graphics.Color.TRANSPARENT`;
1649
+ cleanedOnCreateContent = cleanedOnCreateContent.substring(0, superOnCreateEndIndex) +
1650
+ windowSettingsCode + '\n' +
1651
+ cleanedOnCreateContent.substring(superOnCreateEndIndex);
1652
+ }
1653
+ }
1654
+ }
1526
1655
  if (cleanedOnCreateContent !== onCreateContent) {
1527
1656
  modifiedContent = modifiedContent.substring(0, onCreateIndex) +
1528
1657
  cleanedOnCreateContent +
@@ -1561,6 +1690,47 @@ function modifyMainActivityForBlendMode(content, packageName, imageResourceName)
1561
1690
  private var splashImageViewContainer: ViewGroup? = null
1562
1691
  private var preventAutoHideImageView = false
1563
1692
 
1693
+ /**
1694
+ * 安全地设置视图可见性,自动处理异常
1695
+ */
1696
+ private fun safeSetVisibility(view: View?, visibility: Int, viewName: String) {
1697
+ try {
1698
+ view?.visibility = visibility
1699
+ } catch (e: Exception) {
1700
+ android.util.Log.e("MainActivity", "Error setting $viewName visibility", e)
1701
+ }
1702
+ }
1703
+
1704
+ private fun safeRemoveViewFromParent(child: View?, parent: ViewGroup?, viewName: String): Boolean {
1705
+ if (child == null || parent == null) {
1706
+ android.util.Log.d("MainActivity", "$viewName or parent is null, cannot remove")
1707
+ return false
1708
+ }
1709
+
1710
+ try {
1711
+ val childCount = parent.childCount
1712
+ var found = false
1713
+ for (i in 0 until childCount) {
1714
+ if (parent.getChildAt(i) == child) {
1715
+ found = true
1716
+ break
1717
+ }
1718
+ }
1719
+
1720
+ if (found) {
1721
+ parent.removeView(child)
1722
+ android.util.Log.d("MainActivity", "$viewName removed from parent")
1723
+ return true
1724
+ } else {
1725
+ android.util.Log.d("MainActivity", "$viewName not found in parent")
1726
+ return false
1727
+ }
1728
+ } catch (e: Exception) {
1729
+ android.util.Log.e("MainActivity", "Error removing $viewName", e)
1730
+ return false
1731
+ }
1732
+ }
1733
+
1564
1734
  private fun setupSplashImageView() {
1565
1735
  try {
1566
1736
  // If container already exists, return directly
@@ -1637,6 +1807,18 @@ function modifyMainActivityForBlendMode(content, packageName, imageResourceName)
1637
1807
  }
1638
1808
 
1639
1809
  fun hideSplashImageViewContainer(force: Boolean = false) {
1810
+ // Ensure all UI operations execute on main thread
1811
+ if (Looper.myLooper() == Looper.getMainLooper()) {
1812
+ hideSplashImageViewContainerInternal(force)
1813
+ } else {
1814
+ android.util.Log.d("MainActivity", "hideSplashImageViewContainer called from background thread, posting to main thread")
1815
+ Handler(Looper.getMainLooper()).post {
1816
+ hideSplashImageViewContainerInternal(force)
1817
+ }
1818
+ }
1819
+ }
1820
+
1821
+ private fun hideSplashImageViewContainerInternal(force: Boolean = false) {
1640
1822
  try {
1641
1823
  // If preventAutoHideImageView is true and not force hide, don't execute hide operation
1642
1824
  if (preventAutoHideImageView && !force) {
@@ -1644,14 +1826,55 @@ function modifyMainActivityForBlendMode(content, packageName, imageResourceName)
1644
1826
  return
1645
1827
  }
1646
1828
 
1647
- val parent = splashImageViewContainer?.parent as? ViewGroup
1648
- parent?.removeView(splashImageViewContainer)
1829
+ if (splashImageViewContainer != null) {
1830
+ android.util.Log.d("MainActivity", "Hiding Splash ImageView container")
1649
1831
 
1650
- splashImageViewContainer?.visibility = View.GONE
1651
- splashImageViewContainer?.removeAllViews()
1652
- splashImageViewContainer = null
1653
- preventAutoHideImageView = false
1654
- android.util.Log.d("MainActivity", "Splash ImageView container hidden")
1832
+ // First set visibility to GONE to ensure invisible
1833
+ safeSetVisibility(splashImageViewContainer, View.GONE, "Splash ImageView container")
1834
+
1835
+ // Try to remove from parent view with validation
1836
+ val parent = splashImageViewContainer?.parent as? ViewGroup
1837
+ if (parent != null) {
1838
+ val removed = safeRemoveViewFromParent(splashImageViewContainer, parent, "Splash ImageView container")
1839
+ if (!removed) {
1840
+ android.util.Log.d("MainActivity", "Splash ImageView container not found in parent, trying to remove from decorView")
1841
+ // If not found in parent, try to remove directly from decorView
1842
+ try {
1843
+ val decorView = window.decorView as? ViewGroup
1844
+ if (decorView != null) {
1845
+ safeRemoveViewFromParent(splashImageViewContainer, decorView, "Splash ImageView container from decorView")
1846
+ }
1847
+ } catch (e: Exception) {
1848
+ android.util.Log.e("MainActivity", "Error removing Splash ImageView container from decorView", e)
1849
+ }
1850
+ }
1851
+ } else {
1852
+ android.util.Log.d("MainActivity", "Splash ImageView container has no parent, trying to remove from decorView")
1853
+ // If parent is null, try to remove directly from decorView
1854
+ try {
1855
+ val decorView = window.decorView as? ViewGroup
1856
+ if (decorView != null) {
1857
+ safeRemoveViewFromParent(splashImageViewContainer, decorView, "Splash ImageView container from decorView")
1858
+ }
1859
+ } catch (e: Exception) {
1860
+ android.util.Log.e("MainActivity", "Error removing Splash ImageView container from decorView", e)
1861
+ }
1862
+ }
1863
+
1864
+ // Clean up all child views
1865
+ try {
1866
+ splashImageViewContainer?.removeAllViews()
1867
+ } catch (e: Exception) {
1868
+ android.util.Log.e("MainActivity", "Error removing all views from Splash ImageView container", e)
1869
+ }
1870
+
1871
+ // Clear reference
1872
+ splashImageViewContainer = null
1873
+ preventAutoHideImageView = false
1874
+ android.util.Log.d("MainActivity", "Splash ImageView container hidden")
1875
+ } else {
1876
+ android.util.Log.d("MainActivity", "Splash ImageView container is null, skipping")
1877
+ }
1655
1878
  } catch (e: Exception) {
1656
1879
  android.util.Log.e("MainActivity", "Error hiding splash ImageView container", e)
1657
1880
  }
@@ -1763,31 +1986,43 @@ function modifyMainActivityForBlendMode(content, packageName, imageResourceName)
1763
1986
  android.util.Log.d("MainActivity", "Hiding MainActivity WebView container")
1764
1987
 
1765
1988
  // First set visibility to GONE to ensure invisible
1766
- webViewContainer?.visibility = View.GONE
1989
+ safeSetVisibility(webViewContainer, View.GONE, "WebView container")
1767
1990
 
1768
- // Try to remove from parent view
1991
+ // Try to remove from parent view with validation
1769
1992
  val parent = webViewContainer?.parent as? ViewGroup
1770
1993
  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)
1994
+ val removed = safeRemoveViewFromParent(webViewContainer, parent, "WebView container")
1995
+ if (!removed) {
1996
+ android.util.Log.d("MainActivity", "WebView container not found in parent, trying to remove from decorView")
1997
+ // If not found in parent, try to remove directly from decorView
1998
+ try {
1999
+ val decorView = window.decorView as? ViewGroup
2000
+ if (decorView != null) {
2001
+ safeRemoveViewFromParent(webViewContainer, decorView, "WebView container from decorView")
2002
+ }
2003
+ } catch (e: Exception) {
2004
+ android.util.Log.e("MainActivity", "Error removing WebView container from decorView", e)
2005
+ }
1776
2006
  }
1777
2007
  } else {
1778
2008
  android.util.Log.d("MainActivity", "WebView container has no parent, trying to remove from decorView")
1779
2009
  // If parent is null, try to remove directly from decorView
1780
2010
  try {
1781
2011
  val decorView = window.decorView as? ViewGroup
1782
- decorView?.removeView(webViewContainer)
1783
- android.util.Log.d("MainActivity", "WebView container removed from decorView")
2012
+ if (decorView != null) {
2013
+ safeRemoveViewFromParent(webViewContainer, decorView, "WebView container from decorView")
2014
+ }
1784
2015
  } catch (e: Exception) {
1785
2016
  android.util.Log.e("MainActivity", "Error removing WebView container from decorView", e)
1786
2017
  }
1787
2018
  }
1788
2019
 
1789
2020
  // Clean up all child views
1790
- webViewContainer?.removeAllViews()
2021
+ try {
2022
+ webViewContainer?.removeAllViews()
2023
+ } catch (e: Exception) {
2024
+ android.util.Log.e("MainActivity", "Error removing all views from WebView container", e)
2025
+ }
1791
2026
 
1792
2027
  // Clear reference
1793
2028
  webViewContainer = null
@@ -1840,19 +2075,36 @@ function modifyMainActivityForBlendMode(content, packageName, imageResourceName)
1840
2075
  const onContentChangedCode = `
1841
2076
  override fun onContentChanged() {
1842
2077
  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 {
2078
+ // 同步添加视图,避免在渲染过程中出现 null 引用导致崩溃
2079
+ try {
1846
2080
  setupSplashImageView()
1847
- // Small delay to ensure ImageView container is added first
1848
- Handler(Looper.getMainLooper()).postDelayed({
1849
- setupWebViewContainer()
1850
- }, 50)
2081
+ setupWebViewContainer()
2082
+ } catch (e: Exception) {
2083
+ android.util.Log.e("MainActivity", "Error in onContentChanged", e)
1851
2084
  }
1852
2085
  }`;
1853
2086
  const onCreateMatchForStatusBar = modifiedContent.match(/override\s+fun\s+onCreate\s*\([^)]*\)\s*\{/);
1854
2087
  if (onCreateMatchForStatusBar) {
1855
- const onCreateIndex = modifiedContent.indexOf(onCreateMatchForStatusBar[0]);
2088
+ let onCreateIndex = -1;
2089
+ const allOnCreateMatches = [];
2090
+ let searchStart = 0;
2091
+ while (true) {
2092
+ const match = modifiedContent.substring(searchStart).match(/override\s+fun\s+onCreate\s*\([^)]*\)\s*\{/);
2093
+ if (!match)
2094
+ break;
2095
+ const matchIndex = searchStart + match.index;
2096
+ allOnCreateMatches.push(matchIndex);
2097
+ searchStart = matchIndex + match[0].length;
2098
+ }
2099
+ if (allOnCreateMatches.length > 0) {
2100
+ onCreateIndex = allOnCreateMatches[allOnCreateMatches.length - 1];
2101
+ }
2102
+ else {
2103
+ onCreateIndex = modifiedContent.indexOf(onCreateMatchForStatusBar[0]);
2104
+ }
2105
+ const lineStartIndex = modifiedContent.lastIndexOf('\n', onCreateIndex - 1) + 1;
2106
+ const onCreateLinePrefix = modifiedContent.substring(lineStartIndex, onCreateIndex);
2107
+ const hasWrongIndentation = onCreateLinePrefix !== ' ';
1856
2108
  let braceCount = 0;
1857
2109
  let onCreateEndIndex = onCreateIndex + onCreateMatchForStatusBar[0].length;
1858
2110
  let foundStart = false;
@@ -1870,20 +2122,43 @@ function modifyMainActivityForBlendMode(content, packageName, imageResourceName)
1870
2122
  }
1871
2123
  }
1872
2124
  const onCreateContent = modifiedContent.substring(onCreateIndex, onCreateEndIndex);
2125
+ const openBraceIndex = onCreateContent.indexOf('{');
2126
+ const superOnCreateIndex = onCreateContent.indexOf('super.onCreate');
2127
+ const betweenBraceAndSuper = onCreateContent.substring(openBraceIndex + 1, superOnCreateIndex);
2128
+ const hasFormatIssue = openBraceIndex !== -1 &&
2129
+ superOnCreateIndex !== -1 &&
2130
+ openBraceIndex < superOnCreateIndex &&
2131
+ !betweenBraceAndSuper.includes('\n') &&
2132
+ !onCreateContent.includes('\n super.onCreate') &&
2133
+ !onCreateContent.includes('\n super.onCreate(null)');
1873
2134
  const isCompressed = /override\s+fun\s+onCreate\s*\([^)]*\)\s*\{[^}]*super\.onCreate[^}]*window\.statusBarColor[^}]*window\.navigationBarColor[^}]*\}/.test(onCreateContent) &&
1874
2135
  !onCreateContent.includes('\n super.onCreate') &&
1875
2136
  !onCreateContent.includes('\n window.statusBarColor');
1876
- if (isCompressed) {
2137
+ if (hasFormatIssue || isCompressed || hasWrongIndentation) {
1877
2138
  const superOnCreateMatch = onCreateContent.match(/super\.onCreate\([^)]*\)/);
2139
+ const windowCompatMatch = onCreateContent.match(/WindowCompat\.setDecorFitsSystemWindows\(window,\s*false\)/);
2140
+ const isNavigationBarContrastEnforcedMatch = onCreateContent.match(/if\s*\(Build\.VERSION\.SDK_INT\s*>=\s*Build\.VERSION_CODES\.Q\)\s*\{[\s\S]*?window\.isNavigationBarContrastEnforced\s*=\s*false[\s\S]*?\}/);
1878
2141
  const statusBarMatch = onCreateContent.match(/window\.statusBarColor\s*=\s*[^}\n]+/);
1879
2142
  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
- }`;
2143
+ if (superOnCreateMatch) {
2144
+ const indentation = ' ';
2145
+ let formattedOnCreate = `${indentation}override fun onCreate(savedInstanceState: Bundle?) {
2146
+ ${indentation} ${superOnCreateMatch[0]}`;
2147
+ if (windowCompatMatch) {
2148
+ formattedOnCreate += `\n${indentation} WindowCompat.setDecorFitsSystemWindows(window, false)`;
2149
+ }
2150
+ if (isNavigationBarContrastEnforcedMatch) {
2151
+ formattedOnCreate += `\n${indentation} if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
2152
+ ${indentation} window.isNavigationBarContrastEnforced = false
2153
+ ${indentation} }`;
2154
+ }
2155
+ if (statusBarMatch) {
2156
+ formattedOnCreate += `\n${indentation} ${statusBarMatch[0].replace(/[}]/g, '').trim()}`;
2157
+ }
2158
+ if (navBarMatch) {
2159
+ formattedOnCreate += `\n${indentation} ${navBarMatch[0].replace(/[}]/g, '').trim()}`;
2160
+ }
2161
+ formattedOnCreate += `\n${indentation}}`;
1887
2162
  modifiedContent = modifiedContent.substring(0, onCreateIndex) +
1888
2163
  formattedOnCreate +
1889
2164
  modifiedContent.substring(onCreateEndIndex);
@@ -1911,7 +2186,23 @@ function modifyMainActivityForBlendMode(content, packageName, imageResourceName)
1911
2186
  if (!modifiedContent.includes('override fun onContentChanged()')) {
1912
2187
  const onCreateMatchForInsert = modifiedContent.match(/override\s+fun\s+onCreate\s*\([^)]*\)\s*\{/);
1913
2188
  if (onCreateMatchForInsert) {
1914
- const onCreateIndexForInsert = modifiedContent.indexOf(onCreateMatchForInsert[0]);
2189
+ let onCreateIndexForInsert = -1;
2190
+ const allMatches = [];
2191
+ let searchStart = 0;
2192
+ while (true) {
2193
+ const match = modifiedContent.substring(searchStart).match(/override\s+fun\s+onCreate\s*\([^)]*\)\s*\{/);
2194
+ if (!match)
2195
+ break;
2196
+ const matchIndex = searchStart + match.index;
2197
+ allMatches.push(matchIndex);
2198
+ searchStart = matchIndex + match[0].length;
2199
+ }
2200
+ if (allMatches.length > 0) {
2201
+ onCreateIndexForInsert = allMatches[allMatches.length - 1];
2202
+ }
2203
+ else {
2204
+ onCreateIndexForInsert = modifiedContent.indexOf(onCreateMatchForInsert[0]);
2205
+ }
1915
2206
  let braceCount = 0;
1916
2207
  let onCreateEndIndexForInsert = onCreateIndexForInsert + onCreateMatchForInsert[0].length;
1917
2208
  let foundStart = false;
@@ -1931,6 +2222,48 @@ function modifyMainActivityForBlendMode(content, packageName, imageResourceName)
1931
2222
  modifiedContent = modifiedContent.substring(0, onCreateEndIndexForInsert) +
1932
2223
  '\n' + onContentChangedCode + '\n' +
1933
2224
  modifiedContent.substring(onCreateEndIndexForInsert);
2225
+ const onContentChangedMethodStart = modifiedContent.indexOf('override fun onContentChanged()');
2226
+ if (onContentChangedMethodStart !== -1) {
2227
+ const methodSignatureEnd = modifiedContent.indexOf('{', onContentChangedMethodStart);
2228
+ if (methodSignatureEnd !== -1) {
2229
+ let braceCount = 0;
2230
+ let methodEnd = methodSignatureEnd + 1;
2231
+ let foundStart = false;
2232
+ for (let i = methodSignatureEnd; i < modifiedContent.length; i++) {
2233
+ if (modifiedContent[i] === '{') {
2234
+ braceCount++;
2235
+ foundStart = true;
2236
+ }
2237
+ else if (modifiedContent[i] === '}') {
2238
+ braceCount--;
2239
+ if (foundStart && braceCount === 0) {
2240
+ methodEnd = i + 1;
2241
+ break;
2242
+ }
2243
+ }
2244
+ }
2245
+ const afterOnContentChanged = modifiedContent.substring(methodEnd);
2246
+ const trimmedAfter = afterOnContentChanged.trimStart();
2247
+ if (trimmedAfter.startsWith('}')) {
2248
+ const duplicateBracePos = methodEnd + afterOnContentChanged.indexOf('}');
2249
+ const afterDuplicateBrace = modifiedContent.substring(duplicateBracePos + 1).trimStart();
2250
+ if (afterDuplicateBrace.startsWith('/**') ||
2251
+ afterDuplicateBrace.match(/^\s*override\s+fun/) ||
2252
+ afterDuplicateBrace.startsWith('companion object')) {
2253
+ let removeEndIndex = duplicateBracePos + 1;
2254
+ while (removeEndIndex < modifiedContent.length &&
2255
+ (modifiedContent[removeEndIndex] === ' ' || modifiedContent[removeEndIndex] === '\t')) {
2256
+ removeEndIndex++;
2257
+ }
2258
+ if (removeEndIndex < modifiedContent.length && modifiedContent[removeEndIndex] === '\n') {
2259
+ removeEndIndex++;
2260
+ }
2261
+ modifiedContent = modifiedContent.substring(0, methodEnd) +
2262
+ modifiedContent.substring(removeEndIndex);
2263
+ }
2264
+ }
2265
+ }
2266
+ }
1934
2267
  }
1935
2268
  else {
1936
2269
  const getMainComponentMatch = modifiedContent.match(/override\s+fun\s+getMainComponentName/);
@@ -1942,6 +2275,11 @@ function modifyMainActivityForBlendMode(content, packageName, imageResourceName)
1942
2275
  }
1943
2276
  }
1944
2277
  }
2278
+ const companionObjectIndentPattern = /^(\s*)companion\s+object\s*\{/m;
2279
+ const companionObjectIndentMatch = modifiedContent.match(companionObjectIndentPattern);
2280
+ if (companionObjectIndentMatch && companionObjectIndentMatch[1] !== ' ') {
2281
+ modifiedContent = modifiedContent.replace(companionObjectIndentPattern, ' companion object {');
2282
+ }
1945
2283
  return modifiedContent;
1946
2284
  }
1947
2285
  function generatePrivacyPolicyActivity(packageName, projectRoot, androidMainPath) {
@@ -4290,8 +4628,9 @@ function setupWebViewMode(config, pluginConfig) {
4290
4628
  }
4291
4629
  updateIcLauncherBackground(androidMainPath, pluginConfig.backgroundColor || '#ffffff', imageWidth);
4292
4630
  createSplashColorsXml(androidMainPath, pluginConfig.backgroundColor || '#ffffff');
4631
+ const edgeToEdgeEnabled = config.android?.edgeToEdgeEnabled;
4293
4632
  if (projectRoot) {
4294
- generateCustomSplashActivity(packageName, projectRoot, androidMainPath, pluginConfig.backgroundColor || '#ffffff');
4633
+ generateCustomSplashActivity(packageName, projectRoot, androidMainPath, pluginConfig.backgroundColor || '#ffffff', edgeToEdgeEnabled);
4295
4634
  generatePrivacyPolicyActivity(packageName, projectRoot, androidMainPath);
4296
4635
  }
4297
4636
  else {
@@ -4305,7 +4644,8 @@ function setupWebViewMode(config, pluginConfig) {
4305
4644
  return config;
4306
4645
  });
4307
4646
  config = (0, config_plugins_1.withMainActivity)(config, (config) => {
4308
- config.modResults.contents = modifyMainActivity(config.modResults.contents, packageName, pluginConfig.backgroundColor || '#ffffff');
4647
+ const edgeToEdgeEnabled = config.android?.edgeToEdgeEnabled;
4648
+ config.modResults.contents = modifyMainActivity(config.modResults.contents, packageName, pluginConfig.backgroundColor || '#ffffff', edgeToEdgeEnabled);
4309
4649
  return config;
4310
4650
  });
4311
4651
  config = (0, config_plugins_1.withDangerousMod)(config, [
@@ -4762,8 +5102,9 @@ function setupBlendMode(config, pluginConfig) {
4762
5102
  copyHtmlFile(projectRoot, androidMainPath, resolvedHtml);
4763
5103
  }
4764
5104
  createSplashColorsXml(androidMainPath, pluginConfig.backgroundColor || '#ffffff');
5105
+ const edgeToEdgeEnabled = config.android?.edgeToEdgeEnabled;
4765
5106
  if (projectRoot) {
4766
- generateCustomSplashActivityForBlendMode(packageName, projectRoot, androidMainPath, imageResourceName);
5107
+ generateCustomSplashActivityForBlendMode(packageName, projectRoot, androidMainPath, imageResourceName, edgeToEdgeEnabled);
4767
5108
  generatePrivacyPolicyActivity(packageName, projectRoot, androidMainPath);
4768
5109
  }
4769
5110
  else {
@@ -4777,7 +5118,8 @@ function setupBlendMode(config, pluginConfig) {
4777
5118
  return config;
4778
5119
  });
4779
5120
  config = (0, config_plugins_1.withMainActivity)(config, (config) => {
4780
- config.modResults.contents = modifyMainActivityForBlendMode(config.modResults.contents, packageName, savedImageResourceName);
5121
+ const edgeToEdgeEnabled = config.android?.edgeToEdgeEnabled;
5122
+ config.modResults.contents = modifyMainActivityForBlendMode(config.modResults.contents, packageName, savedImageResourceName, edgeToEdgeEnabled);
4781
5123
  return config;
4782
5124
  });
4783
5125
  config = (0, config_plugins_1.withAndroidStyles)(config, (config) => {
@@ -682,7 +682,8 @@ class SplashScreen2PrivacyPolicyActivity : AppCompatActivity() {
682
682
  context.overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out)
683
683
  }
684
684
  }
685
- }`,
685
+ }
686
+ `,
686
687
  mainActivityWebViewCode: `
687
688
  private var webViewContainer: ViewGroup? = null
688
689
  private var preventAutoHide = false