@capgo/capacitor-native-navigation 8.2.2 → 8.2.4
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/README.md +30 -28
- package/android/src/main/java/app/capgo/nativenavigation/NativeNavigationPlugin.java +412 -15
- package/dist/docs.json +15 -1
- package/dist/esm/components.js +2 -0
- package/dist/esm/components.js.map +1 -1
- package/dist/esm/definitions.d.ts +14 -1
- package/dist/esm/definitions.js.map +1 -1
- package/dist/plugin.cjs.js +2 -0
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +2 -0
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/NativeNavigationPlugin/NativeNavigationPlugin.swift +234 -45
- package/package.json +1 -1
|
@@ -25,12 +25,14 @@ import android.view.MenuItem;
|
|
|
25
25
|
import android.view.View;
|
|
26
26
|
import android.view.ViewGroup;
|
|
27
27
|
import android.view.ViewOutlineProvider;
|
|
28
|
+
import android.view.ViewTreeObserver;
|
|
28
29
|
import android.view.Window;
|
|
29
30
|
import android.view.WindowInsets;
|
|
30
31
|
import android.widget.FrameLayout;
|
|
31
32
|
import android.widget.ImageView;
|
|
32
33
|
import android.widget.LinearLayout;
|
|
33
34
|
import android.widget.TextView;
|
|
35
|
+
import androidx.annotation.RequiresApi;
|
|
34
36
|
import androidx.appcompat.content.res.AppCompatResources;
|
|
35
37
|
import androidx.appcompat.widget.Toolbar;
|
|
36
38
|
import androidx.core.graphics.PathParser;
|
|
@@ -67,15 +69,25 @@ public class NativeNavigationPlugin extends Plugin {
|
|
|
67
69
|
private FrameLayout tabbarContainer;
|
|
68
70
|
private Toolbar toolbar;
|
|
69
71
|
private NativeTabbarLayout tabbar;
|
|
72
|
+
private GlassBackdropView navbarGlassBackdrop;
|
|
73
|
+
private View navbarGlassSurface;
|
|
74
|
+
private GlassBackdropView tabbarGlassBackdrop;
|
|
75
|
+
private View tabbarGlassSurface;
|
|
70
76
|
private ImageView transitionSnapshot;
|
|
71
77
|
private boolean enabled = true;
|
|
72
78
|
private boolean navbarVisible = false;
|
|
73
79
|
private boolean tabbarVisible = false;
|
|
74
80
|
private String contentInsetMode = "css";
|
|
81
|
+
private GlassOptions defaultGlassOptions = GlassOptions.defaults();
|
|
82
|
+
private GlassOptions navbarGlassOptions = GlassOptions.defaults();
|
|
83
|
+
private GlassOptions tabbarGlassOptions = GlassOptions.defaults();
|
|
84
|
+
private JSObject navbarGlassConfig;
|
|
85
|
+
private JSObject tabbarGlassConfig;
|
|
75
86
|
private int defaultTransitionMs = DEFAULT_TRANSITION_MS;
|
|
76
87
|
private int activeTransitionMs = DEFAULT_TRANSITION_MS;
|
|
77
88
|
private int tintColor = Color.rgb(0, 122, 255);
|
|
78
89
|
private int inactiveTintColor = Color.rgb(120, 126, 137);
|
|
90
|
+
private int navbarBackgroundColor = Color.argb(225, 255, 255, 255);
|
|
79
91
|
private int tabbarBackgroundColor = Color.argb(235, 255, 255, 255);
|
|
80
92
|
private int badgeBackgroundColor = Color.rgb(255, 59, 48);
|
|
81
93
|
private int badgeTextColor = Color.WHITE;
|
|
@@ -105,6 +117,9 @@ public class NativeNavigationPlugin extends Plugin {
|
|
|
105
117
|
runOnUiThread(() -> {
|
|
106
118
|
enabled = call.getBoolean("enabled", true);
|
|
107
119
|
contentInsetMode = call.getString("contentInsetMode", contentInsetMode);
|
|
120
|
+
defaultGlassOptions = GlassOptions.from(call.getObject("glass", null), defaultGlassOptions);
|
|
121
|
+
navbarGlassOptions = GlassOptions.from(navbarGlassConfig, defaultGlassOptions);
|
|
122
|
+
tabbarGlassOptions = GlassOptions.from(tabbarGlassConfig, defaultGlassOptions);
|
|
108
123
|
Double duration = call.getDouble("animationDuration");
|
|
109
124
|
if (duration != null) {
|
|
110
125
|
defaultTransitionMs = Math.max(0, duration.intValue());
|
|
@@ -120,6 +135,9 @@ public class NativeNavigationPlugin extends Plugin {
|
|
|
120
135
|
tabbarContainer.setVisibility(View.GONE);
|
|
121
136
|
}
|
|
122
137
|
}
|
|
138
|
+
if (enabled) {
|
|
139
|
+
reapplyVisibleChromeBackgrounds();
|
|
140
|
+
}
|
|
123
141
|
updateInsetsAndNotify();
|
|
124
142
|
call.resolve(insetsResult());
|
|
125
143
|
});
|
|
@@ -167,6 +185,8 @@ public class NativeNavigationPlugin extends Plugin {
|
|
|
167
185
|
|
|
168
186
|
addToolbarItems(nativeToolbar, call.getArray("rightItems", new JSArray()), "right");
|
|
169
187
|
JSObject colors = call.getObject("colors", new JSObject());
|
|
188
|
+
navbarGlassConfig = call.getObject("glass", null);
|
|
189
|
+
navbarGlassOptions = GlassOptions.from(navbarGlassConfig, defaultGlassOptions);
|
|
170
190
|
applyToolbarColors(nativeToolbar, colors);
|
|
171
191
|
navbarContainer.setVisibility(View.VISIBLE);
|
|
172
192
|
layoutChrome();
|
|
@@ -210,32 +230,52 @@ public class NativeNavigationPlugin extends Plugin {
|
|
|
210
230
|
JSONArray tabs = call.getArray("tabs", new JSArray());
|
|
211
231
|
String selectedId = call.getString("selectedId", null);
|
|
212
232
|
JSObject colors = call.getObject("colors", new JSObject());
|
|
233
|
+
tabbarGlassConfig = call.getObject("glass", null);
|
|
234
|
+
tabbarGlassOptions = GlassOptions.from(tabbarGlassConfig, defaultGlassOptions);
|
|
213
235
|
badgeBackgroundColor = colorOption(call, colors, "badgeBackgroundColor", "badgeBackground", Color.rgb(255, 59, 48));
|
|
214
236
|
badgeTextColor = colorOption(call, colors, "badgeTextColor", "badgeText", Color.WHITE);
|
|
215
237
|
|
|
216
|
-
for (int
|
|
217
|
-
JSONObject tab = tabs.optJSONObject(
|
|
238
|
+
for (int sourceIndex = 0; sourceIndex < tabs.length(); sourceIndex++) {
|
|
239
|
+
JSONObject tab = tabs.optJSONObject(sourceIndex);
|
|
218
240
|
if (tab == null) {
|
|
219
241
|
continue;
|
|
220
242
|
}
|
|
221
|
-
String id = tab.optString("id", "tab-" +
|
|
243
|
+
String id = tab.optString("id", "tab-" + sourceIndex);
|
|
244
|
+
boolean isHidden = tab.optBoolean("hidden", false);
|
|
245
|
+
if (isHidden && !id.equals(selectedId)) {
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
int visibleIndex = tabItems.size();
|
|
222
250
|
String title = tab.optString("title", "");
|
|
223
251
|
Drawable icon = icons ? iconFrom(tab.optJSONObject("icon")) : new ColorDrawable(Color.TRANSPARENT);
|
|
224
252
|
Drawable selectedIcon = icons ? iconFrom(tab.optJSONObject("selectedIcon")) : null;
|
|
225
253
|
String badge = tab.has("badge") ? String.valueOf(tab.opt("badge")) : null;
|
|
226
|
-
tabItems.add(new NativeTabItem(id, title, icon, selectedIcon, badge, tab.optBoolean("enabled", true)));
|
|
254
|
+
tabItems.add(new NativeTabItem(id, title, icon, selectedIcon, badge, tab.optBoolean("enabled", true), sourceIndex));
|
|
227
255
|
if (id.equals(selectedId)) {
|
|
228
|
-
selectedTabIndex =
|
|
256
|
+
selectedTabIndex = visibleIndex;
|
|
229
257
|
}
|
|
230
258
|
}
|
|
231
259
|
|
|
232
|
-
if (
|
|
260
|
+
if (tabItems.isEmpty()) {
|
|
261
|
+
tabbarVisible = false;
|
|
262
|
+
if (tabbarContainer != null) {
|
|
263
|
+
tabbarContainer.setVisibility(View.GONE);
|
|
264
|
+
}
|
|
265
|
+
nativeTabbar.setVisibility(View.GONE);
|
|
266
|
+
layoutChrome();
|
|
267
|
+
updateInsetsAndNotify();
|
|
268
|
+
call.resolve(insetsResult());
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (selectedTabIndex < 0 || selectedTabIndex >= tabItems.size()) {
|
|
233
273
|
selectedTabIndex = 0;
|
|
234
274
|
}
|
|
235
275
|
|
|
236
276
|
applyTabbarColors(call, colors);
|
|
237
277
|
tabbarStyle = makeTabbarStyle(call.getObject("style", new JSObject()));
|
|
238
|
-
|
|
278
|
+
applyTabbarBackground(centerTabIndex());
|
|
239
279
|
renderTabbarItems(labelVisibilityMode, icons);
|
|
240
280
|
if (tabbarContainer != null) {
|
|
241
281
|
tabbarContainer.setVisibility(View.VISIBLE);
|
|
@@ -577,6 +617,11 @@ public class NativeNavigationPlugin extends Plugin {
|
|
|
577
617
|
navbarContainer.setElevation(dp(8));
|
|
578
618
|
toolbar = new Toolbar(getContext());
|
|
579
619
|
toolbar.setPopupTheme(androidx.appcompat.R.style.ThemeOverlay_AppCompat_Light);
|
|
620
|
+
navbarGlassBackdrop = new GlassBackdropView(getContext());
|
|
621
|
+
navbarGlassSurface = new View(getContext());
|
|
622
|
+
navbarGlassBackdrop.setVisibility(View.GONE);
|
|
623
|
+
navbarGlassSurface.setVisibility(View.GONE);
|
|
624
|
+
|
|
580
625
|
toolbar.setOnMenuItemClickListener((item) -> {
|
|
581
626
|
int itemId = item.getItemId();
|
|
582
627
|
JSObject event = new JSObject();
|
|
@@ -587,6 +632,8 @@ public class NativeNavigationPlugin extends Plugin {
|
|
|
587
632
|
return true;
|
|
588
633
|
});
|
|
589
634
|
|
|
635
|
+
navbarContainer.addView(navbarGlassBackdrop);
|
|
636
|
+
navbarContainer.addView(navbarGlassSurface);
|
|
590
637
|
navbarContainer.addView(toolbar);
|
|
591
638
|
if (root != null) {
|
|
592
639
|
root.addView(navbarContainer);
|
|
@@ -609,10 +656,17 @@ public class NativeNavigationPlugin extends Plugin {
|
|
|
609
656
|
tabbarContainer.setClipToPadding(false);
|
|
610
657
|
tabbarContainer.setElevation(dp(12));
|
|
611
658
|
|
|
659
|
+
tabbarGlassBackdrop = new GlassBackdropView(getContext());
|
|
660
|
+
tabbarGlassSurface = new View(getContext());
|
|
661
|
+
tabbarGlassBackdrop.setVisibility(View.GONE);
|
|
662
|
+
tabbarGlassSurface.setVisibility(View.GONE);
|
|
663
|
+
|
|
612
664
|
tabbar = new NativeTabbarLayout(getContext());
|
|
613
665
|
tabbar.setClipChildren(false);
|
|
614
666
|
tabbar.setClipToPadding(false);
|
|
615
667
|
tabbar.setBackgroundColor(Color.TRANSPARENT);
|
|
668
|
+
tabbarContainer.addView(tabbarGlassBackdrop);
|
|
669
|
+
tabbarContainer.addView(tabbarGlassSurface);
|
|
616
670
|
tabbarContainer.addView(
|
|
617
671
|
tabbar,
|
|
618
672
|
new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
|
|
@@ -634,7 +688,7 @@ public class NativeNavigationPlugin extends Plugin {
|
|
|
634
688
|
}
|
|
635
689
|
tabbar.removeAllViews();
|
|
636
690
|
int centerIndex = centerTabIndex();
|
|
637
|
-
|
|
691
|
+
applyTabbarBackground(centerIndex);
|
|
638
692
|
for (int index = 0; index < tabItems.size(); index++) {
|
|
639
693
|
final int itemIndex = index;
|
|
640
694
|
NativeTabItem item = tabItems.get(index);
|
|
@@ -652,7 +706,7 @@ public class NativeNavigationPlugin extends Plugin {
|
|
|
652
706
|
renderTabbarItems(labelVisibilityMode, icons);
|
|
653
707
|
JSObject event = new JSObject();
|
|
654
708
|
event.put("id", item.id);
|
|
655
|
-
event.put("index",
|
|
709
|
+
event.put("index", item.sourceIndex);
|
|
656
710
|
event.put("title", item.title);
|
|
657
711
|
notifyListeners("tabSelect", event);
|
|
658
712
|
});
|
|
@@ -910,14 +964,16 @@ public class NativeNavigationPlugin extends Plugin {
|
|
|
910
964
|
final Drawable selectedIcon;
|
|
911
965
|
final String badge;
|
|
912
966
|
final boolean enabled;
|
|
967
|
+
final int sourceIndex;
|
|
913
968
|
|
|
914
|
-
NativeTabItem(String id, String title, Drawable icon, Drawable selectedIcon, String badge, boolean enabled) {
|
|
969
|
+
NativeTabItem(String id, String title, Drawable icon, Drawable selectedIcon, String badge, boolean enabled, int sourceIndex) {
|
|
915
970
|
this.id = id;
|
|
916
971
|
this.title = title;
|
|
917
972
|
this.icon = icon;
|
|
918
973
|
this.selectedIcon = selectedIcon;
|
|
919
974
|
this.badge = badge;
|
|
920
975
|
this.enabled = enabled;
|
|
976
|
+
this.sourceIndex = sourceIndex;
|
|
921
977
|
}
|
|
922
978
|
}
|
|
923
979
|
|
|
@@ -1309,6 +1365,7 @@ public class NativeNavigationPlugin extends Plugin {
|
|
|
1309
1365
|
: Color.rgb(20, 24, 32);
|
|
1310
1366
|
int tint = parseColor(colors.getString("tint", null), tintFallback);
|
|
1311
1367
|
int background = parseColor(colors.getString("background", null), backgroundFallback);
|
|
1368
|
+
navbarBackgroundColor = background;
|
|
1312
1369
|
int foreground = parseColor(colors.getString("foreground", null), foregroundFallback);
|
|
1313
1370
|
tintColor = tint;
|
|
1314
1371
|
nativeToolbar.setTitleTextColor(foreground);
|
|
@@ -1319,9 +1376,8 @@ public class NativeNavigationPlugin extends Plugin {
|
|
|
1319
1376
|
tintedIcon.setTint(tint);
|
|
1320
1377
|
nativeToolbar.setNavigationIcon(tintedIcon);
|
|
1321
1378
|
}
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
}
|
|
1379
|
+
nativeToolbar.setBackgroundColor(Color.TRANSPARENT);
|
|
1380
|
+
applyChromeBackground(navbarContainer, navbarGlassBackdrop, navbarGlassSurface, background, navbarGlassOptions, 0f);
|
|
1325
1381
|
for (int index = 0; index < nativeToolbar.getMenu().size(); index++) {
|
|
1326
1382
|
Drawable icon = nativeToolbar.getMenu().getItem(index).getIcon();
|
|
1327
1383
|
if (icon != null) {
|
|
@@ -1388,9 +1444,115 @@ public class NativeNavigationPlugin extends Plugin {
|
|
|
1388
1444
|
tintColor = parseColor(colors.getString("tint", null), tintFallback);
|
|
1389
1445
|
inactiveTintColor = parseColor(colors.getString("inactiveTint", null), inactiveFallback);
|
|
1390
1446
|
tabbarBackgroundColor = parseColor(colors.getString("background", null), backgroundFallback);
|
|
1391
|
-
|
|
1392
|
-
|
|
1447
|
+
applyTabbarBackground();
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1450
|
+
private void applyTabbarBackground() {
|
|
1451
|
+
applyTabbarBackground(centerTabIndex());
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
private void applyTabbarBackground(int centerIndex) {
|
|
1455
|
+
if (tabbar == null) {
|
|
1456
|
+
return;
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
int drawColor = tabbarBackgroundColor;
|
|
1460
|
+
GlassOptions resolvedGlassOptions = tabbarGlassOptions == null ? GlassOptions.defaults() : tabbarGlassOptions;
|
|
1461
|
+
if (resolvedGlassOptions.isLiquidGlass()) {
|
|
1462
|
+
drawColor = glassSurfaceColor(tabbarBackgroundColor, resolvedGlassOptions);
|
|
1463
|
+
if (tabbarContainer != null) {
|
|
1464
|
+
tabbarContainer.setBackgroundColor(Color.TRANSPARENT);
|
|
1465
|
+
}
|
|
1466
|
+
hideGlassBackground(null, tabbarGlassSurface);
|
|
1467
|
+
if (tabbarGlassBackdrop != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
|
1468
|
+
View webView = getBridge() == null ? null : getBridge().getWebView();
|
|
1469
|
+
tabbarGlassBackdrop.setClipPathProvider((width, height) -> tabbar.backgroundPath(width, height));
|
|
1470
|
+
tabbarGlassBackdrop.configure(webView, dp(resolvedGlassOptions.blurRadiusDp), drawColor);
|
|
1471
|
+
tabbarGlassBackdrop.setVisibility(View.VISIBLE);
|
|
1472
|
+
} else if (tabbarGlassBackdrop != null) {
|
|
1473
|
+
tabbarGlassBackdrop.setClipPathProvider((width, height) -> tabbar.backgroundPath(width, height));
|
|
1474
|
+
tabbarGlassBackdrop.clearEffect();
|
|
1475
|
+
tabbarGlassBackdrop.setVisibility(View.GONE);
|
|
1476
|
+
}
|
|
1477
|
+
} else {
|
|
1478
|
+
hideGlassBackground(tabbarGlassBackdrop, tabbarGlassSurface);
|
|
1479
|
+
if (tabbarGlassBackdrop != null) {
|
|
1480
|
+
tabbarGlassBackdrop.setClipPathProvider(null);
|
|
1481
|
+
}
|
|
1482
|
+
if (tabbarContainer != null) {
|
|
1483
|
+
tabbarContainer.setBackgroundColor(Color.TRANSPARENT);
|
|
1484
|
+
}
|
|
1393
1485
|
}
|
|
1486
|
+
|
|
1487
|
+
tabbar.setTabbarStyle(tabbarStyle, drawColor, centerIndex);
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
private void reapplyVisibleChromeBackgrounds() {
|
|
1491
|
+
if (navbarContainer != null && navbarContainer.getVisibility() == View.VISIBLE) {
|
|
1492
|
+
applyChromeBackground(navbarContainer, navbarGlassBackdrop, navbarGlassSurface, navbarBackgroundColor, navbarGlassOptions, 0f);
|
|
1493
|
+
}
|
|
1494
|
+
if (tabbarContainer != null && tabbarContainer.getVisibility() == View.VISIBLE) {
|
|
1495
|
+
applyTabbarBackground();
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
|
|
1499
|
+
private void applyChromeBackground(
|
|
1500
|
+
ViewGroup container,
|
|
1501
|
+
GlassBackdropView backdrop,
|
|
1502
|
+
View surface,
|
|
1503
|
+
int background,
|
|
1504
|
+
GlassOptions glassOptions,
|
|
1505
|
+
float cornerRadius
|
|
1506
|
+
) {
|
|
1507
|
+
if (container == null) {
|
|
1508
|
+
return;
|
|
1509
|
+
}
|
|
1510
|
+
GlassOptions resolvedGlassOptions = glassOptions == null ? GlassOptions.defaults() : glassOptions;
|
|
1511
|
+
if (!resolvedGlassOptions.isLiquidGlass()) {
|
|
1512
|
+
hideGlassBackground(backdrop, surface);
|
|
1513
|
+
container.setBackground(chromeBackgroundDrawable(background, cornerRadius));
|
|
1514
|
+
return;
|
|
1515
|
+
}
|
|
1516
|
+
|
|
1517
|
+
container.setBackgroundColor(Color.TRANSPARENT);
|
|
1518
|
+
int surfaceColor = glassSurfaceColor(background, resolvedGlassOptions);
|
|
1519
|
+
if (surface != null) {
|
|
1520
|
+
surface.setBackground(chromeBackgroundDrawable(surfaceColor, cornerRadius));
|
|
1521
|
+
surface.setVisibility(View.VISIBLE);
|
|
1522
|
+
}
|
|
1523
|
+
if (backdrop != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
|
1524
|
+
View webView = getBridge() == null ? null : getBridge().getWebView();
|
|
1525
|
+
backdrop.setClipPathProvider(null);
|
|
1526
|
+
backdrop.configure(webView, dp(resolvedGlassOptions.blurRadiusDp), surfaceColor);
|
|
1527
|
+
backdrop.setVisibility(View.VISIBLE);
|
|
1528
|
+
} else if (backdrop != null) {
|
|
1529
|
+
backdrop.clearEffect();
|
|
1530
|
+
backdrop.setVisibility(View.GONE);
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
private void hideGlassBackground(GlassBackdropView backdrop, View surface) {
|
|
1535
|
+
if (backdrop != null) {
|
|
1536
|
+
backdrop.clearEffect();
|
|
1537
|
+
backdrop.setVisibility(View.GONE);
|
|
1538
|
+
}
|
|
1539
|
+
if (surface != null) {
|
|
1540
|
+
surface.setBackground(null);
|
|
1541
|
+
surface.setVisibility(View.GONE);
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
private Drawable chromeBackgroundDrawable(int color, float cornerRadius) {
|
|
1546
|
+
GradientDrawable drawable = new GradientDrawable();
|
|
1547
|
+
drawable.setColor(color);
|
|
1548
|
+
if (cornerRadius > 0f) {
|
|
1549
|
+
drawable.setCornerRadius(cornerRadius);
|
|
1550
|
+
}
|
|
1551
|
+
return drawable;
|
|
1552
|
+
}
|
|
1553
|
+
|
|
1554
|
+
private int glassSurfaceColor(int background, GlassOptions glassOptions) {
|
|
1555
|
+
return withAlpha(background, Math.round(Color.alpha(background) * (float) glassOptions.surfaceAlpha));
|
|
1394
1556
|
}
|
|
1395
1557
|
|
|
1396
1558
|
private int parseColor(String value, int fallback) {
|
|
@@ -1462,6 +1624,12 @@ public class NativeNavigationPlugin extends Plugin {
|
|
|
1462
1624
|
return mode == Configuration.UI_MODE_NIGHT_YES;
|
|
1463
1625
|
}
|
|
1464
1626
|
|
|
1627
|
+
private void fillContainer(View view) {
|
|
1628
|
+
if (view != null) {
|
|
1629
|
+
view.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
|
|
1465
1633
|
private void layoutChrome() {
|
|
1466
1634
|
FrameLayout root = contentRoot();
|
|
1467
1635
|
if (root == null) {
|
|
@@ -1487,6 +1655,8 @@ public class NativeNavigationPlugin extends Plugin {
|
|
|
1487
1655
|
);
|
|
1488
1656
|
toolbarParams.topMargin = status;
|
|
1489
1657
|
toolbar.setLayoutParams(toolbarParams);
|
|
1658
|
+
fillContainer(navbarGlassBackdrop);
|
|
1659
|
+
fillContainer(navbarGlassSurface);
|
|
1490
1660
|
}
|
|
1491
1661
|
|
|
1492
1662
|
if (tabbarContainer != null) {
|
|
@@ -1501,6 +1671,8 @@ public class NativeNavigationPlugin extends Plugin {
|
|
|
1501
1671
|
);
|
|
1502
1672
|
tabbarContainerParams.bottomMargin = tabbarBottomMargin;
|
|
1503
1673
|
tabbarContainer.setLayoutParams(tabbarContainerParams);
|
|
1674
|
+
fillContainer(tabbarGlassBackdrop);
|
|
1675
|
+
fillContainer(tabbarGlassSurface);
|
|
1504
1676
|
}
|
|
1505
1677
|
|
|
1506
1678
|
if (tabbar != null) {
|
|
@@ -1510,6 +1682,7 @@ public class NativeNavigationPlugin extends Plugin {
|
|
|
1510
1682
|
);
|
|
1511
1683
|
tabbar.setLayoutParams(tabbarParams);
|
|
1512
1684
|
tabbar.setPadding(0, 0, 0, 0);
|
|
1685
|
+
applyTabbarBackground();
|
|
1513
1686
|
}
|
|
1514
1687
|
|
|
1515
1688
|
bringChromeToFront();
|
|
@@ -1688,6 +1861,230 @@ public class NativeNavigationPlugin extends Plugin {
|
|
|
1688
1861
|
return Math.round(value * getContext().getResources().getDisplayMetrics().density);
|
|
1689
1862
|
}
|
|
1690
1863
|
|
|
1864
|
+
private float dp(double value) {
|
|
1865
|
+
return (float) (value * getContext().getResources().getDisplayMetrics().density);
|
|
1866
|
+
}
|
|
1867
|
+
|
|
1868
|
+
private static final class GlassOptions {
|
|
1869
|
+
|
|
1870
|
+
private static final String EFFECT_NONE = "none";
|
|
1871
|
+
private static final String EFFECT_LIQUID_GLASS = "liquidGlass";
|
|
1872
|
+
private static final double DEFAULT_BLUR_RADIUS_DP = 18d;
|
|
1873
|
+
private static final double DEFAULT_SURFACE_ALPHA = 0.62d;
|
|
1874
|
+
|
|
1875
|
+
final String effect;
|
|
1876
|
+
final double blurRadiusDp;
|
|
1877
|
+
final double surfaceAlpha;
|
|
1878
|
+
|
|
1879
|
+
GlassOptions(String effect, double blurRadiusDp, double surfaceAlpha) {
|
|
1880
|
+
this.effect = effect;
|
|
1881
|
+
this.blurRadiusDp = Math.max(0d, blurRadiusDp);
|
|
1882
|
+
this.surfaceAlpha = Math.max(0d, Math.min(1d, surfaceAlpha));
|
|
1883
|
+
}
|
|
1884
|
+
|
|
1885
|
+
static GlassOptions defaults() {
|
|
1886
|
+
return new GlassOptions(EFFECT_NONE, DEFAULT_BLUR_RADIUS_DP, DEFAULT_SURFACE_ALPHA);
|
|
1887
|
+
}
|
|
1888
|
+
|
|
1889
|
+
static GlassOptions from(JSObject raw, GlassOptions fallback) {
|
|
1890
|
+
GlassOptions base = fallback == null ? defaults() : fallback;
|
|
1891
|
+
if (raw == null) {
|
|
1892
|
+
return base;
|
|
1893
|
+
}
|
|
1894
|
+
|
|
1895
|
+
String effect = raw.optString("effect", base.effect);
|
|
1896
|
+
if (!EFFECT_NONE.equals(effect) && !EFFECT_LIQUID_GLASS.equals(effect)) {
|
|
1897
|
+
effect = base.effect;
|
|
1898
|
+
}
|
|
1899
|
+
double blurRadiusDp = raw.has("blurRadius") ? raw.optDouble("blurRadius", base.blurRadiusDp) : base.blurRadiusDp;
|
|
1900
|
+
double surfaceAlpha = raw.has("surfaceAlpha") ? raw.optDouble("surfaceAlpha", base.surfaceAlpha) : base.surfaceAlpha;
|
|
1901
|
+
return new GlassOptions(effect, blurRadiusDp, surfaceAlpha);
|
|
1902
|
+
}
|
|
1903
|
+
|
|
1904
|
+
boolean isLiquidGlass() {
|
|
1905
|
+
return EFFECT_LIQUID_GLASS.equals(effect);
|
|
1906
|
+
}
|
|
1907
|
+
}
|
|
1908
|
+
|
|
1909
|
+
private static final class GlassBackdropView extends View {
|
|
1910
|
+
|
|
1911
|
+
interface PathProvider {
|
|
1912
|
+
Path path(int width, int height);
|
|
1913
|
+
}
|
|
1914
|
+
|
|
1915
|
+
private static final long SOURCE_CONTENT_REFRESH_INTERVAL_MS = 250L;
|
|
1916
|
+
|
|
1917
|
+
private final Paint fallbackPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
|
1918
|
+
private final int[] sourceLocation = new int[2];
|
|
1919
|
+
private final int[] viewLocation = new int[2];
|
|
1920
|
+
private final ViewTreeObserver.OnScrollChangedListener sourceScrollListener = this::markDirty;
|
|
1921
|
+
private final ViewTreeObserver.OnPreDrawListener sourcePreDrawListener = () -> {
|
|
1922
|
+
markDirtyFromSourcePreDraw();
|
|
1923
|
+
return true;
|
|
1924
|
+
};
|
|
1925
|
+
private final View.OnLayoutChangeListener sourceLayoutListener = (
|
|
1926
|
+
view,
|
|
1927
|
+
left,
|
|
1928
|
+
top,
|
|
1929
|
+
right,
|
|
1930
|
+
bottom,
|
|
1931
|
+
oldLeft,
|
|
1932
|
+
oldTop,
|
|
1933
|
+
oldRight,
|
|
1934
|
+
oldBottom
|
|
1935
|
+
) -> markDirty();
|
|
1936
|
+
private View source;
|
|
1937
|
+
private PathProvider clipPathProvider;
|
|
1938
|
+
private int fallbackColor = Color.TRANSPARENT;
|
|
1939
|
+
private boolean dirty;
|
|
1940
|
+
private boolean redrawPending;
|
|
1941
|
+
private long lastSourcePreDrawRefreshMs;
|
|
1942
|
+
|
|
1943
|
+
GlassBackdropView(android.content.Context context) {
|
|
1944
|
+
super(context);
|
|
1945
|
+
setWillNotDraw(false);
|
|
1946
|
+
}
|
|
1947
|
+
|
|
1948
|
+
void configure(View source, float blurRadiusPx, int fallbackColor) {
|
|
1949
|
+
this.fallbackColor = fallbackColor;
|
|
1950
|
+
attachSource(source);
|
|
1951
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
|
1952
|
+
Api31RenderEffects.setBlur(this, blurRadiusPx);
|
|
1953
|
+
}
|
|
1954
|
+
markDirty();
|
|
1955
|
+
}
|
|
1956
|
+
|
|
1957
|
+
void clearEffect() {
|
|
1958
|
+
attachSource(null);
|
|
1959
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
|
1960
|
+
Api31RenderEffects.clear(this);
|
|
1961
|
+
}
|
|
1962
|
+
markDirty();
|
|
1963
|
+
}
|
|
1964
|
+
|
|
1965
|
+
void setClipPathProvider(PathProvider clipPathProvider) {
|
|
1966
|
+
this.clipPathProvider = clipPathProvider;
|
|
1967
|
+
markDirty();
|
|
1968
|
+
}
|
|
1969
|
+
|
|
1970
|
+
private void attachSource(View nextSource) {
|
|
1971
|
+
if (source == nextSource) {
|
|
1972
|
+
return;
|
|
1973
|
+
}
|
|
1974
|
+
if (source != null) {
|
|
1975
|
+
source.removeOnLayoutChangeListener(sourceLayoutListener);
|
|
1976
|
+
ViewTreeObserver observer = source.getViewTreeObserver();
|
|
1977
|
+
if (observer.isAlive()) {
|
|
1978
|
+
observer.removeOnScrollChangedListener(sourceScrollListener);
|
|
1979
|
+
observer.removeOnPreDrawListener(sourcePreDrawListener);
|
|
1980
|
+
}
|
|
1981
|
+
}
|
|
1982
|
+
source = nextSource;
|
|
1983
|
+
if (source != null) {
|
|
1984
|
+
source.addOnLayoutChangeListener(sourceLayoutListener);
|
|
1985
|
+
ViewTreeObserver observer = source.getViewTreeObserver();
|
|
1986
|
+
if (observer.isAlive()) {
|
|
1987
|
+
observer.addOnScrollChangedListener(sourceScrollListener);
|
|
1988
|
+
observer.addOnPreDrawListener(sourcePreDrawListener);
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1991
|
+
markDirty();
|
|
1992
|
+
}
|
|
1993
|
+
|
|
1994
|
+
private void markDirty() {
|
|
1995
|
+
dirty = true;
|
|
1996
|
+
scheduleRedrawIfVisible();
|
|
1997
|
+
}
|
|
1998
|
+
|
|
1999
|
+
private void markDirtyFromSourcePreDraw() {
|
|
2000
|
+
dirty = true;
|
|
2001
|
+
long now = System.currentTimeMillis();
|
|
2002
|
+
if (now - lastSourcePreDrawRefreshMs < SOURCE_CONTENT_REFRESH_INTERVAL_MS) {
|
|
2003
|
+
return;
|
|
2004
|
+
}
|
|
2005
|
+
lastSourcePreDrawRefreshMs = now;
|
|
2006
|
+
scheduleRedrawIfVisible();
|
|
2007
|
+
}
|
|
2008
|
+
|
|
2009
|
+
private void scheduleRedrawIfVisible() {
|
|
2010
|
+
if (redrawPending || getVisibility() != View.VISIBLE || !isShown()) {
|
|
2011
|
+
return;
|
|
2012
|
+
}
|
|
2013
|
+
redrawPending = true;
|
|
2014
|
+
postInvalidateOnAnimation();
|
|
2015
|
+
}
|
|
2016
|
+
|
|
2017
|
+
@Override
|
|
2018
|
+
protected void onDraw(Canvas canvas) {
|
|
2019
|
+
super.onDraw(canvas);
|
|
2020
|
+
Path clipPath = clipPathProvider == null ? null : clipPathProvider.path(getWidth(), getHeight());
|
|
2021
|
+
if (clipPath == null) {
|
|
2022
|
+
drawSource(canvas);
|
|
2023
|
+
} else {
|
|
2024
|
+
int save = canvas.save();
|
|
2025
|
+
canvas.clipPath(clipPath);
|
|
2026
|
+
drawSource(canvas);
|
|
2027
|
+
canvas.restoreToCount(save);
|
|
2028
|
+
}
|
|
2029
|
+
|
|
2030
|
+
dirty = false;
|
|
2031
|
+
redrawPending = false;
|
|
2032
|
+
}
|
|
2033
|
+
|
|
2034
|
+
private void drawSource(Canvas canvas) {
|
|
2035
|
+
View currentSource = source;
|
|
2036
|
+
if (currentSource == null || currentSource.getWidth() <= 0 || currentSource.getHeight() <= 0) {
|
|
2037
|
+
fallbackPaint.setColor(fallbackColor);
|
|
2038
|
+
canvas.drawRect(0, 0, getWidth(), getHeight(), fallbackPaint);
|
|
2039
|
+
return;
|
|
2040
|
+
}
|
|
2041
|
+
currentSource.getLocationOnScreen(sourceLocation);
|
|
2042
|
+
getLocationOnScreen(viewLocation);
|
|
2043
|
+
canvas.save();
|
|
2044
|
+
canvas.translate(sourceLocation[0] - viewLocation[0], sourceLocation[1] - viewLocation[1]);
|
|
2045
|
+
currentSource.draw(canvas);
|
|
2046
|
+
canvas.restore();
|
|
2047
|
+
}
|
|
2048
|
+
|
|
2049
|
+
@Override
|
|
2050
|
+
protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) {
|
|
2051
|
+
super.onSizeChanged(width, height, oldWidth, oldHeight);
|
|
2052
|
+
if (width != oldWidth || height != oldHeight) {
|
|
2053
|
+
markDirty();
|
|
2054
|
+
}
|
|
2055
|
+
}
|
|
2056
|
+
|
|
2057
|
+
@Override
|
|
2058
|
+
protected void onVisibilityChanged(View changedView, int visibility) {
|
|
2059
|
+
super.onVisibilityChanged(changedView, visibility);
|
|
2060
|
+
if (visibility != View.VISIBLE) {
|
|
2061
|
+
redrawPending = false;
|
|
2062
|
+
return;
|
|
2063
|
+
}
|
|
2064
|
+
if (dirty) {
|
|
2065
|
+
scheduleRedrawIfVisible();
|
|
2066
|
+
}
|
|
2067
|
+
}
|
|
2068
|
+
}
|
|
2069
|
+
|
|
2070
|
+
@RequiresApi(Build.VERSION_CODES.S)
|
|
2071
|
+
private static final class Api31RenderEffects {
|
|
2072
|
+
|
|
2073
|
+
static void setBlur(View view, float blurRadiusPx) {
|
|
2074
|
+
if (blurRadiusPx <= 0f) {
|
|
2075
|
+
view.setRenderEffect(null);
|
|
2076
|
+
return;
|
|
2077
|
+
}
|
|
2078
|
+
view.setRenderEffect(
|
|
2079
|
+
android.graphics.RenderEffect.createBlurEffect(blurRadiusPx, blurRadiusPx, android.graphics.Shader.TileMode.CLAMP)
|
|
2080
|
+
);
|
|
2081
|
+
}
|
|
2082
|
+
|
|
2083
|
+
static void clear(View view) {
|
|
2084
|
+
view.setRenderEffect(null);
|
|
2085
|
+
}
|
|
2086
|
+
}
|
|
2087
|
+
|
|
1691
2088
|
private void runOnUiThread(Runnable runnable) {
|
|
1692
2089
|
Activity activity = getActivity();
|
|
1693
2090
|
if (activity == null) {
|
package/dist/docs.json
CHANGED
|
@@ -412,7 +412,7 @@
|
|
|
412
412
|
{
|
|
413
413
|
"name": "inactiveTint",
|
|
414
414
|
"tags": [],
|
|
415
|
-
"docs": "Color for inactive tab items.",
|
|
415
|
+
"docs": "Color for inactive tab items. Ignored on iOS 26+ unless\n`experimentalBakedTintColors` is enabled.",
|
|
416
416
|
"complexTypes": [],
|
|
417
417
|
"type": "string | undefined"
|
|
418
418
|
},
|
|
@@ -797,6 +797,13 @@
|
|
|
797
797
|
],
|
|
798
798
|
"type": "NativeNavigationGlassOptions"
|
|
799
799
|
},
|
|
800
|
+
{
|
|
801
|
+
"name": "experimentalBakedTintColors",
|
|
802
|
+
"tags": [],
|
|
803
|
+
"docs": "Opt into the iOS 26 Liquid Glass tint workaround that renders active and\ninactive tab items into baked images. This can affect badge positioning\nand icon sizing, so it is disabled by default.",
|
|
804
|
+
"complexTypes": [],
|
|
805
|
+
"type": "boolean | undefined"
|
|
806
|
+
},
|
|
800
807
|
{
|
|
801
808
|
"name": "disableTransparentOnScrollEdge",
|
|
802
809
|
"tags": [],
|
|
@@ -909,6 +916,13 @@
|
|
|
909
916
|
"docs": "Whether the tab is enabled. Defaults to `true`.",
|
|
910
917
|
"complexTypes": [],
|
|
911
918
|
"type": "boolean | undefined"
|
|
919
|
+
},
|
|
920
|
+
{
|
|
921
|
+
"name": "hidden",
|
|
922
|
+
"tags": [],
|
|
923
|
+
"docs": "Hide the tab item from the native tabbar. When the hidden tab is selected,\nnative platform constraints may keep it visible until another tab is\nselected.",
|
|
924
|
+
"complexTypes": [],
|
|
925
|
+
"type": "boolean | undefined"
|
|
912
926
|
}
|
|
913
927
|
]
|
|
914
928
|
},
|
package/dist/esm/components.js
CHANGED
|
@@ -118,6 +118,7 @@ export function defineNativeNavigationElements() {
|
|
|
118
118
|
'ripple-color',
|
|
119
119
|
'badge-background-color',
|
|
120
120
|
'badge-text-color',
|
|
121
|
+
'experimental-baked-tint-colors',
|
|
121
122
|
'animated',
|
|
122
123
|
];
|
|
123
124
|
}
|
|
@@ -144,6 +145,7 @@ export function defineNativeNavigationElements() {
|
|
|
144
145
|
indicatorColor: normalizeAttribute(this.getAttribute('indicator-color')),
|
|
145
146
|
rippleColor: normalizeAttribute(this.getAttribute('ripple-color')),
|
|
146
147
|
badgeBackgroundColor: normalizeAttribute(this.getAttribute('badge-background-color')),
|
|
148
|
+
experimentalBakedTintColors: parseBoolean(this.getAttribute('experimental-baked-tint-colors')),
|
|
147
149
|
badgeTextColor: normalizeAttribute(this.getAttribute('badge-text-color')),
|
|
148
150
|
animated: parseBoolean(this.getAttribute('animated')),
|
|
149
151
|
};
|