From 2949c5654570812347e112123d77c560b85dfad2 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Sat, 13 Jul 2024 09:04:16 +0100 Subject: [PATCH 01/60] Supported automatic back handling Turned toolbar into action bar. If it's no first crumb then enable home as up - android puts back arrow and on press the navigation router navigates back --- NavigationReactNative/src/NavigationBar.tsx | 11 +++++--- .../reactnative/ToolbarManager.java | 5 ++++ .../navigation/reactnative/ToolbarView.java | 27 +++++++++++++++---- 3 files changed, 35 insertions(+), 8 deletions(-) diff --git a/NavigationReactNative/src/NavigationBar.tsx b/NavigationReactNative/src/NavigationBar.tsx index 1cc77c6d7..1e13cce4c 100644 --- a/NavigationReactNative/src/NavigationBar.tsx +++ b/NavigationReactNative/src/NavigationBar.tsx @@ -32,7 +32,7 @@ class NavigationBar extends React.Component { } } render() { - var {navigationEvent, bottomBar, hidden, logo, navigationImage, overflowImage, backTitle, backImage, titleCentered, shadowColor, children, onNavigationPress, style = {height: undefined}, ...otherProps} = this.props; + var {navigationEvent, actionBar, bottomBar, hidden, logo, navigationImage, overflowImage, backTitle, backImage, titleCentered, shadowColor, children, onNavigationPress, style = {height: undefined}, ...otherProps} = this.props; const Material3 = global.__turboModuleProxy != null ? require("./NativeMaterial3Module").default : NativeModules.Material3; const { on: material3 } = Platform.OS === 'android' ? Material3.getConstants() : { on: false }; var scrollEdgeProps = this.getScrollEdgeProps() @@ -51,7 +51,8 @@ class NavigationBar extends React.Component { searchBarTintColor = typeof searchBarTintColor === 'function' ? searchBarTintColor(true) : searchBarTintColor; var toolbarTintColor = searchToolbar ? searchBarTintColor : scrollEdgeProps.barTintColor; var placeholder = searchBar?.props.placeholder; - var crumb = navigationEvent.stateNavigator.stateContext.crumbs.length; + var {stateNavigator} = navigationEvent; + var crumb = stateNavigator.stateContext.crumbs.length; return ( <> { {collapsingBar && collapsingBar.props.children} 0} navigationImage={Image.resolveAssetSource(navigationImage)} overflowImage={Image.resolveAssetSource(overflowImage)} pin={!!collapsingBar} @@ -89,7 +91,10 @@ class NavigationBar extends React.Component { titleCentered={!!titleCentered} barHeight={!material3 || searchToolbar ? 56 : 64} navigationDecorative={!onNavigationPress} - onNavigationPress={onNavigationPress} + onNavigationPress={() => { + if (actionBar && crumb > 0) stateNavigator.navigateBack(1); + else onNavigationPress(); + }} style={{height: !material3 || searchToolbar ? 56 : 64, margin: searchToolbar ? 16 : undefined}}> {[ !searchToolbar && childrenArray.find(({type}) => type === TitleBar), diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarManager.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarManager.java index aa5b7746c..88261dff7 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarManager.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarManager.java @@ -31,6 +31,11 @@ protected ToolbarView createViewInstance(@Nonnull ThemedReactContext reactContex return new ToolbarView(reactContext); } + @ReactProp(name = "showHome") + public void setShowHome(ToolbarView view, boolean showHome) { + view.setShowHome(showHome); + } + @ReactProp(name = "title") public void setTitle(ToolbarView view, String title) { view.setPlainTitle(title); diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarView.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarView.java index 64eb10382..8b68a9c49 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarView.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarView.java @@ -19,6 +19,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.ActionMenuView; import androidx.appcompat.widget.AppCompatImageButton; import androidx.appcompat.widget.AppCompatImageView; @@ -79,11 +80,7 @@ public ToolbarView(Context context) { setOverflowIcon(d); setTintColor(getOverflowIcon()); }; - setNavigationOnClickListener(view -> { - ReactContext reactContext = (ReactContext) getContext(); - EventDispatcher eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, getId()); - eventDispatcher.dispatchEvent(new NavigationPressEvent(getId())); - }); + setNavigationOnClickListener(this::onNavigationClick); setOnMenuItemClickListener(item -> { for (int i = 0; i < children.size(); i++) { if (children.get(i) instanceof BarButtonView) { @@ -141,6 +138,20 @@ void setTitleFontSize(Integer titleFontSize) { titleChanged = true; } + void setShowHome(boolean showHome) { + AppCompatActivity activity = (AppCompatActivity) ((ReactContext) getContext()).getCurrentActivity(); + assert activity != null; + Drawable navigationIcon = getNavigationIcon(); + activity.setSupportActionBar(this); + assert activity.getSupportActionBar() != null; + activity.getSupportActionBar().setDisplayHomeAsUpEnabled(showHome); + activity.setSupportActionBar(null); + setNavigationOnClickListener(this::onNavigationClick); + if (navigationIcon != null) setNavigationIcon(navigationIcon); + setTintColor(getNavigationIcon()); + setTestID(); + } + void setLogoSource(@Nullable ReadableMap source) { IconResolver.setIconSource(source, logoResolverListener, getContext()); } @@ -284,6 +295,12 @@ void styleTitle() { } } + private void onNavigationClick(View view) { + ReactContext reactContext = (ReactContext) getContext(); + EventDispatcher eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, getId()); + eventDispatcher.dispatchEvent(new NavigationPressEvent(getId())); + } + public void setOnSearchListener(OnSearchListener onSearchListener) { this.onSearchAddedListener = onSearchListener; if (searchMenuItem != null) From ab6fa9c58d2e2513396fb9af035a45c939720950 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Sat, 13 Jul 2024 13:30:25 +0100 Subject: [PATCH 02/60] Sketched in drawer component Used flex 1 for the drawer style so that it can be positioned anywhere. Tried it with absolute (like the react native one) but then it took up whole view - it should show in place so can put a drawer above or below the navigation bar, for example. --- NavigationReactNative/src/Drawer.tsx | 30 +++++++++++++++++++ .../src/NavigationReactNative.ts | 3 +- NavigationReactNative/src/Sheet.tsx | 2 +- .../reactnative/DrawerLayoutManager.java | 25 ++++++++++++++++ .../reactnative/DrawerLayoutView.java | 12 ++++++++ .../navigation/reactnative/DrawerManager.java | 20 +++++++++++++ .../navigation/reactnative/DrawerView.java | 19 ++++++++++++ .../reactnative/NavigationPackage.java | 4 ++- 8 files changed, 112 insertions(+), 3 deletions(-) create mode 100644 NavigationReactNative/src/Drawer.tsx create mode 100644 NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutManager.java create mode 100644 NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutView.java create mode 100644 NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerManager.java create mode 100644 NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerView.java diff --git a/NavigationReactNative/src/Drawer.tsx b/NavigationReactNative/src/Drawer.tsx new file mode 100644 index 000000000..d3ca7d3a0 --- /dev/null +++ b/NavigationReactNative/src/Drawer.tsx @@ -0,0 +1,30 @@ +import React from 'react'; +import { requireNativeComponent, StyleSheet } from 'react-native'; + +const Drawer = ({ view, children }) => { + return ( + + {children} + + {view} + + + ); +}; + +const NVDrawerLayout = requireNativeComponent('NVDrawerLayout'); +const NVDrawer = requireNativeComponent('NVDrawer'); + +const styles = StyleSheet.create({ + drawer: { + flex: 1, + }, + view: { + position: 'absolute', + top: 0, + bottom: 0, + elevation: 5, + } +}); + +export default Drawer; diff --git a/NavigationReactNative/src/NavigationReactNative.ts b/NavigationReactNative/src/NavigationReactNative.ts index dab1921a0..75d2654b0 100644 --- a/NavigationReactNative/src/NavigationReactNative.ts +++ b/NavigationReactNative/src/NavigationReactNative.ts @@ -16,10 +16,11 @@ import CollapsingBar from './CollapsingBar'; import ActionBar from './ActionBar'; import StatusBar from './StatusBar'; import Sheet, {BottomSheet} from './Sheet' +import Drawer from './Drawer'; import FloatingActionButton from './FloatingActionButton'; import useNavigating from './useNavigating'; import useNavigated from './useNavigated'; import useUnloading from './useUnloading'; import useUnloaded from './useUnloaded'; const Scene = NavigationStack.Scene; -export { NavigationStack, Scene, NavigationBar, LeftBar, RightBar, BarButton, TitleBar, SearchBar, TabBar, TabBarItem, TabBarItemContext, SharedElement, BackHandlerContext, ModalBackHandler, CoordinatorLayout, CollapsingBar, ActionBar, StatusBar, Sheet, BottomSheet, FloatingActionButton, useNavigating, useNavigated, useUnloading, useUnloaded }; +export { NavigationStack, Scene, NavigationBar, LeftBar, RightBar, BarButton, TitleBar, SearchBar, TabBar, TabBarItem, TabBarItemContext, SharedElement, BackHandlerContext, ModalBackHandler, CoordinatorLayout, CollapsingBar, ActionBar, StatusBar, Sheet, BottomSheet, Drawer, FloatingActionButton, useNavigating, useNavigated, useUnloading, useUnloaded }; diff --git a/NavigationReactNative/src/Sheet.tsx b/NavigationReactNative/src/Sheet.tsx index cdcd708e3..1d5e51404 100644 --- a/NavigationReactNative/src/Sheet.tsx +++ b/NavigationReactNative/src/Sheet.tsx @@ -1,4 +1,4 @@ -import React, { useMemo, useContext, useState, useRef, createContext, useEffect } from 'react'; +import React, { useMemo, useContext, useState, useRef, createContext } from 'react'; import { requireNativeComponent, Platform, UIManager, StyleSheet } from 'react-native'; import { NavigationContext } from 'navigation-react'; import useNavigated from './useNavigated'; diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutManager.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutManager.java new file mode 100644 index 000000000..be03610bb --- /dev/null +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutManager.java @@ -0,0 +1,25 @@ +package com.navigation.reactnative; + +import androidx.annotation.NonNull; + +import com.facebook.react.uimanager.ThemedReactContext; +import com.facebook.react.uimanager.ViewGroupManager; + +public class DrawerLayoutManager extends ViewGroupManager { + @NonNull + @Override + public String getName() { + return "NVDrawerLayout"; + } + + @NonNull + @Override + protected DrawerLayoutView createViewInstance(@NonNull ThemedReactContext themedReactContext) { + return new DrawerLayoutView(themedReactContext); + } + + @Override + public boolean needsCustomLayoutForChildren() { + return true; + } +} diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutView.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutView.java new file mode 100644 index 000000000..71598aa9a --- /dev/null +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutView.java @@ -0,0 +1,12 @@ +package com.navigation.reactnative; + +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.drawerlayout.widget.DrawerLayout; + +public class DrawerLayoutView extends DrawerLayout { + public DrawerLayoutView(@NonNull Context context) { + super(context); + } +} diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerManager.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerManager.java new file mode 100644 index 000000000..8f71890be --- /dev/null +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerManager.java @@ -0,0 +1,20 @@ +package com.navigation.reactnative; + +import androidx.annotation.NonNull; + +import com.facebook.react.uimanager.ThemedReactContext; +import com.facebook.react.uimanager.ViewGroupManager; + +public class DrawerManager extends ViewGroupManager { + @NonNull + @Override + public String getName() { + return "NVDrawer"; + } + + @NonNull + @Override + protected DrawerView createViewInstance(@NonNull ThemedReactContext themedReactContext) { + return new DrawerView(themedReactContext); + } +} diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerView.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerView.java new file mode 100644 index 000000000..b0c7d505b --- /dev/null +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerView.java @@ -0,0 +1,19 @@ +package com.navigation.reactnative; + +import android.content.Context; +import android.view.Gravity; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.drawerlayout.widget.DrawerLayout; + +import com.google.android.material.navigation.NavigationView; + +public class DrawerView extends NavigationView { + public DrawerView(@NonNull Context context) { + super(context); + DrawerLayout.LayoutParams params = new DrawerLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); + params.gravity = Gravity.LEFT; + setLayoutParams(params); + } +} diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/NavigationPackage.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/NavigationPackage.java index 5f486f8c8..0f6153890 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/NavigationPackage.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/NavigationPackage.java @@ -42,7 +42,9 @@ public List createViewManagers(ReactApplicationContext reactContext new BottomSheetDialogManager(), new FloatingActionButtonManager(), new ExtendedFloatingActionButtonManager(), - new BottomAppBarManager() + new BottomAppBarManager(), + new DrawerLayoutManager(), + new DrawerManager() ); } From 1dc30bce6fd544dcc901fa1a0904becd70523513 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Sat, 13 Jul 2024 13:51:32 +0100 Subject: [PATCH 03/60] Prevented scene interaction when drawer open The drawer is modal so should block interaction with the content behind. Copied from react native ReactDrawerLayout https://github.com/facebook/react-native/blob/65a3259f039416394d2b945ec10565d38b3cad9e/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/drawer/ReactDrawerLayout.java#L67-L92 (dropped the try/catch because not sure what it's for) --- .../reactnative/DrawerLayoutView.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutView.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutView.java index 71598aa9a..ec3d88a46 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutView.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutView.java @@ -1,12 +1,36 @@ package com.navigation.reactnative; import android.content.Context; +import android.view.MotionEvent; import androidx.annotation.NonNull; import androidx.drawerlayout.widget.DrawerLayout; +import com.facebook.react.uimanager.events.NativeGestureUtil; + public class DrawerLayoutView extends DrawerLayout { + boolean dragging; public DrawerLayoutView(@NonNull Context context) { super(context); } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + if (super.onInterceptTouchEvent(ev)) { + NativeGestureUtil.notifyNativeGestureStarted(this, ev); + dragging = true; + return true; + } + return false; + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + int action = ev.getActionMasked(); + if (action == MotionEvent.ACTION_UP && dragging) { + NativeGestureUtil.notifyNativeGestureEnded(this, ev); + dragging = false; + } + return super.onTouchEvent(ev); + } } From b04b5c31d6d3a480eafe2c826b3a5d42a1212ab5 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Sat, 13 Jul 2024 14:51:00 +0100 Subject: [PATCH 04/60] Ensured predictive back closes drawer Requested layout after attached to window. Without this the drawer appears again after the predictive back closes it. Not sure why but got the idea from the SceneView requesting layout on the React Native drawer otherwise it wouldn't open. Don't think they're the same issue?! --- NavigationReactNative/src/Drawer.tsx | 1 + .../reactnative/DrawerLayoutView.java | 25 +++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/NavigationReactNative/src/Drawer.tsx b/NavigationReactNative/src/Drawer.tsx index d3ca7d3a0..fa5d04552 100644 --- a/NavigationReactNative/src/Drawer.tsx +++ b/NavigationReactNative/src/Drawer.tsx @@ -24,6 +24,7 @@ const styles = StyleSheet.create({ top: 0, bottom: 0, elevation: 5, + backgroundColor: 'transparent', } }); diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutView.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutView.java index ec3d88a46..7cc90cd41 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutView.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutView.java @@ -10,6 +10,8 @@ public class DrawerLayoutView extends DrawerLayout { boolean dragging; + private boolean layoutRequested = false; + public DrawerLayoutView(@NonNull Context context) { super(context); } @@ -33,4 +35,27 @@ public boolean onTouchEvent(MotionEvent ev) { } return super.onTouchEvent(ev); } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + requestLayout(); + } + + @Override + public void requestLayout() { + super.requestLayout(); + if (!layoutRequested) { + layoutRequested = true; + post(measureAndLayout); + } + } + + private final Runnable measureAndLayout = () -> { + layoutRequested = false; + measure( + MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.EXACTLY)); + layout(getLeft(), getTop(), getRight(), getBottom()); + }; } From 9c23aad08c150c44afe2cba9312af3ece1c5cfa7 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Sat, 13 Jul 2024 15:01:33 +0100 Subject: [PATCH 05/60] Moved for consistency Matches the same code's position in coordinatorlayoutview --- .../reactnative/DrawerLayoutView.java | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutView.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutView.java index 7cc90cd41..d63982635 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutView.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutView.java @@ -16,26 +16,6 @@ public DrawerLayoutView(@NonNull Context context) { super(context); } - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - if (super.onInterceptTouchEvent(ev)) { - NativeGestureUtil.notifyNativeGestureStarted(this, ev); - dragging = true; - return true; - } - return false; - } - - @Override - public boolean onTouchEvent(MotionEvent ev) { - int action = ev.getActionMasked(); - if (action == MotionEvent.ACTION_UP && dragging) { - NativeGestureUtil.notifyNativeGestureEnded(this, ev); - dragging = false; - } - return super.onTouchEvent(ev); - } - @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); @@ -54,8 +34,28 @@ public void requestLayout() { private final Runnable measureAndLayout = () -> { layoutRequested = false; measure( - MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY), - MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.EXACTLY)); + MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.EXACTLY)); layout(getLeft(), getTop(), getRight(), getBottom()); }; + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + if (super.onInterceptTouchEvent(ev)) { + NativeGestureUtil.notifyNativeGestureStarted(this, ev); + dragging = true; + return true; + } + return false; + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + int action = ev.getActionMasked(); + if (action == MotionEvent.ACTION_UP && dragging) { + NativeGestureUtil.notifyNativeGestureEnded(this, ev); + dragging = false; + } + return super.onTouchEvent(ev); + } } From 0f1594ccafaf12de2506e85c8064118393920f65 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Sun, 14 Jul 2024 09:26:29 +0100 Subject: [PATCH 06/60] Made open a controlled prop on Drawer So it can be opened and closed declaratively --- NavigationReactNative/src/Drawer.tsx | 23 ++++++++++++++++--- .../reactnative/DrawerLayoutManager.java | 17 ++++++++++++++ .../reactnative/DrawerLayoutView.java | 12 ++++++++++ 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/NavigationReactNative/src/Drawer.tsx b/NavigationReactNative/src/Drawer.tsx index fa5d04552..349525f53 100644 --- a/NavigationReactNative/src/Drawer.tsx +++ b/NavigationReactNative/src/Drawer.tsx @@ -1,9 +1,26 @@ -import React from 'react'; +import React, {useState} from 'react'; import { requireNativeComponent, StyleSheet } from 'react-native'; -const Drawer = ({ view, children }) => { +const Drawer = ({view, open = false, onChangeOpen, children}) => { + const [show, setShow] = useState(false); + const [mostRecentEventCount, setMostRecentEventCount] = useState(0); + if (open != null && show !== open) setShow(open); + const onChangeShow = ({nativeEvent}) => { + const {eventCount: mostRecentEventCount, open: newOpen} = nativeEvent; + if (show !== newOpen) { + if (open == null) + setShow(newOpen); + if (!!onChangeOpen) + onChangeOpen(newOpen); + } + setMostRecentEventCount(mostRecentEventCount); + } return ( - + {children} {view} diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutManager.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutManager.java index be03610bb..192f996c5 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutManager.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutManager.java @@ -4,6 +4,7 @@ import com.facebook.react.uimanager.ThemedReactContext; import com.facebook.react.uimanager.ViewGroupManager; +import com.facebook.react.uimanager.annotations.ReactProp; public class DrawerLayoutManager extends ViewGroupManager { @NonNull @@ -12,6 +13,22 @@ public String getName() { return "NVDrawerLayout"; } + @ReactProp(name = "open") + public void setOpen(DrawerLayoutView view, boolean open) { + view.pendingOpen = open; + } + + @ReactProp(name = "mostRecentEventCount") + public void setMostRecentEventCount(DrawerLayoutView view, int mostRecentEventCount) { + view.mostRecentEventCount = mostRecentEventCount; + } + + @Override + protected void onAfterUpdateTransaction(@NonNull DrawerLayoutView view) { + super.onAfterUpdateTransaction(view); + view.onAfterUpdateTransaction(); + } + @NonNull @Override protected DrawerLayoutView createViewInstance(@NonNull ThemedReactContext themedReactContext) { diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutView.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutView.java index d63982635..55de830b0 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutView.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutView.java @@ -1,6 +1,7 @@ package com.navigation.reactnative; import android.content.Context; +import android.view.Gravity; import android.view.MotionEvent; import androidx.annotation.NonNull; @@ -9,6 +10,9 @@ import com.facebook.react.uimanager.events.NativeGestureUtil; public class DrawerLayoutView extends DrawerLayout { + int nativeEventCount; + int mostRecentEventCount; + boolean pendingOpen; boolean dragging; private boolean layoutRequested = false; @@ -16,6 +20,14 @@ public DrawerLayoutView(@NonNull Context context) { super(context); } + void onAfterUpdateTransaction() { + int eventLag = nativeEventCount - mostRecentEventCount; + if (eventLag == 0 && isOpen() != pendingOpen) { + if (pendingOpen) openDrawer(Gravity.LEFT); + else closeDrawer(Gravity.LEFT); + } + } + @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); From 9f68565aac422ed7b2c4123bc20ed9f6913c30d9 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Sun, 14 Jul 2024 09:36:23 +0100 Subject: [PATCH 07/60] Kept open in js in sync with open on native Raised the native event to sync properties --- .../reactnative/DrawerLayoutManager.java | 10 ++++ .../reactnative/DrawerLayoutView.java | 48 +++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutManager.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutManager.java index 192f996c5..229165333 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutManager.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutManager.java @@ -2,10 +2,13 @@ import androidx.annotation.NonNull; +import com.facebook.react.common.MapBuilder; import com.facebook.react.uimanager.ThemedReactContext; import com.facebook.react.uimanager.ViewGroupManager; import com.facebook.react.uimanager.annotations.ReactProp; +import java.util.Map; + public class DrawerLayoutManager extends ViewGroupManager { @NonNull @Override @@ -39,4 +42,11 @@ protected DrawerLayoutView createViewInstance(@NonNull ThemedReactContext themed public boolean needsCustomLayoutForChildren() { return true; } + + @Override + public Map getExportedCustomDirectEventTypeConstants() { + return MapBuilder.builder() + .put("topChangeOpen", MapBuilder.of("registrationName", "onChangeOpen")) + .build(); + } } diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutView.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutView.java index 55de830b0..4f171f989 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutView.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutView.java @@ -3,11 +3,19 @@ import android.content.Context; import android.view.Gravity; import android.view.MotionEvent; +import android.view.View; import androidx.annotation.NonNull; import androidx.drawerlayout.widget.DrawerLayout; +import com.facebook.react.bridge.Arguments; +import com.facebook.react.bridge.ReactContext; +import com.facebook.react.bridge.WritableMap; +import com.facebook.react.uimanager.UIManagerHelper; +import com.facebook.react.uimanager.events.Event; +import com.facebook.react.uimanager.events.EventDispatcher; import com.facebook.react.uimanager.events.NativeGestureUtil; +import com.facebook.react.uimanager.events.RCTEventEmitter; public class DrawerLayoutView extends DrawerLayout { int nativeEventCount; @@ -18,6 +26,23 @@ public class DrawerLayoutView extends DrawerLayout { public DrawerLayoutView(@NonNull Context context) { super(context); + addDrawerListener(new SimpleDrawerListener() { + @Override + public void onDrawerOpened(View drawerView) { + nativeEventCount++; + ReactContext reactContext = (ReactContext) getContext(); + EventDispatcher eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, getId()); + eventDispatcher.dispatchEvent(new DrawerLayoutView.ChangeOpenEvent(getId(), true, nativeEventCount)); + } + + @Override + public void onDrawerClosed(View drawerView) { + nativeEventCount++; + ReactContext reactContext = (ReactContext) getContext(); + EventDispatcher eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, getId()); + eventDispatcher.dispatchEvent(new DrawerLayoutView.ChangeOpenEvent(getId(), false, nativeEventCount)); + } + }); } void onAfterUpdateTransaction() { @@ -70,4 +95,27 @@ public boolean onTouchEvent(MotionEvent ev) { } return super.onTouchEvent(ev); } + + static class ChangeOpenEvent extends Event { + private final boolean open; + private final int eventCount; + public ChangeOpenEvent(int viewId, boolean open, int eventCount) { + super(viewId); + this.open = open; + this.eventCount = eventCount; + } + + @Override + public String getEventName() { + return "topChangeOpen"; + } + + @Override + public void dispatch(RCTEventEmitter rctEventEmitter) { + WritableMap event = Arguments.createMap(); + event.putBoolean("open", this.open); + event.putInt("eventCount", this.eventCount); + rctEventEmitter.receiveEvent(getViewTag(), getEventName(), event); + } + } } From 6fe2f00d51709c14163bcf2938e9d8a01366e67a Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Sun, 14 Jul 2024 09:42:46 +0100 Subject: [PATCH 08/60] Tweaked format for consistency --- .../java/com/navigation/reactnative/DrawerLayoutView.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutView.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutView.java index 4f171f989..52a27a5c3 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutView.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutView.java @@ -71,8 +71,8 @@ public void requestLayout() { private final Runnable measureAndLayout = () -> { layoutRequested = false; measure( - MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY), - MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.EXACTLY)); + MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.EXACTLY)); layout(getLeft(), getTop(), getRight(), getBottom()); }; From 38d599d72569c0606d6a83675a6e4dd1025b4eac Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Sun, 14 Jul 2024 10:07:13 +0100 Subject: [PATCH 09/60] Matched margin to native Need the js width to match the native width. The native drawerlayout adds a 64dp margin --- NavigationReactNative/src/Drawer.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/NavigationReactNative/src/Drawer.tsx b/NavigationReactNative/src/Drawer.tsx index 349525f53..4059e9b4f 100644 --- a/NavigationReactNative/src/Drawer.tsx +++ b/NavigationReactNative/src/Drawer.tsx @@ -22,7 +22,7 @@ const Drawer = ({view, open = false, onChangeOpen, children}) => { onChangeOpen={onChangeShow} style={styles.drawer}> {children} - + {view} @@ -40,6 +40,8 @@ const styles = StyleSheet.create({ position: 'absolute', top: 0, bottom: 0, + left: 0, + right: 64, elevation: 5, backgroundColor: 'transparent', } From e65b46431855da4f27e05202282c0a47a374eb5c Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Sun, 14 Jul 2024 11:07:21 +0100 Subject: [PATCH 10/60] Ensured drawer content is touchable The NavigationView inserts its own NavigationMenuView child (the content is meant to be children of this) and its receiving the events instead of the actual content. This menu view is the last child so it gets the events. Could implement ReactZIndexedViewGroup to stop it receiving them - but simpler to exclude it from the tree. Think it's to give some material styling - can't support that because React Native does the styling. Can support material styling when there's no actual child content (like in BottomNavigationView) but not for the drawer where there's actual content --- .../main/java/com/navigation/reactnative/DrawerView.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerView.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerView.java index b0c7d505b..97c71fd6b 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerView.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerView.java @@ -2,10 +2,12 @@ import android.content.Context; import android.view.Gravity; +import android.view.View; import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.drawerlayout.widget.DrawerLayout; +import androidx.recyclerview.widget.RecyclerView; import com.google.android.material.navigation.NavigationView; @@ -16,4 +18,10 @@ public DrawerView(@NonNull Context context) { params.gravity = Gravity.LEFT; setLayoutParams(params); } + + @Override + public void addView(View child, int index) { + if (child instanceof RecyclerView) return; + super.addView(child, index); + } } From 9f96981aa308119332486998d5fafd4e6079029a Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Sun, 14 Jul 2024 14:27:37 +0100 Subject: [PATCH 11/60] Change way of blocking NavigationMenuView Not sure of the best way to stop the NavigationView adding NavigationMenuView child. React Native always calls addView override with index so going with this approach --- .../src/main/java/com/navigation/reactnative/DrawerView.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerView.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerView.java index 97c71fd6b..29f4b484a 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerView.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerView.java @@ -20,8 +20,6 @@ public DrawerView(@NonNull Context context) { } @Override - public void addView(View child, int index) { - if (child instanceof RecyclerView) return; - super.addView(child, index); + public void addView(View child) { } } From b84df42024a43ed27ae6b390f531321d341b0734 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Sun, 14 Jul 2024 14:30:07 +0100 Subject: [PATCH 12/60] Renamed for clarity There's already an ActionBar component so actionBar is confusing. Plus not making the Toolbar an ActionBar, just hooking up the navigation (back or drawer) --- NavigationReactNative/src/NavigationBar.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/NavigationReactNative/src/NavigationBar.tsx b/NavigationReactNative/src/NavigationBar.tsx index 1e13cce4c..5f4593c37 100644 --- a/NavigationReactNative/src/NavigationBar.tsx +++ b/NavigationReactNative/src/NavigationBar.tsx @@ -32,7 +32,7 @@ class NavigationBar extends React.Component { } } render() { - var {navigationEvent, actionBar, bottomBar, hidden, logo, navigationImage, overflowImage, backTitle, backImage, titleCentered, shadowColor, children, onNavigationPress, style = {height: undefined}, ...otherProps} = this.props; + var {navigationEvent, bottomBar, hidden, logo, autoNavigation, navigationImage, overflowImage, backTitle, backImage, titleCentered, shadowColor, children, onNavigationPress, style = {height: undefined}, ...otherProps} = this.props; const Material3 = global.__turboModuleProxy != null ? require("./NativeMaterial3Module").default : NativeModules.Material3; const { on: material3 } = Platform.OS === 'android' ? Material3.getConstants() : { on: false }; var scrollEdgeProps = this.getScrollEdgeProps() @@ -76,7 +76,7 @@ class NavigationBar extends React.Component { {collapsingBar && collapsingBar.props.children} 0} + showHome={!navigationImage && autoNavigation && crumb > 0} navigationImage={Image.resolveAssetSource(navigationImage)} overflowImage={Image.resolveAssetSource(overflowImage)} pin={!!collapsingBar} @@ -92,7 +92,7 @@ class NavigationBar extends React.Component { barHeight={!material3 || searchToolbar ? 56 : 64} navigationDecorative={!onNavigationPress} onNavigationPress={() => { - if (actionBar && crumb > 0) stateNavigator.navigateBack(1); + if (autoNavigation && crumb > 0) stateNavigator.navigateBack(1); else onNavigationPress(); }} style={{height: !material3 || searchToolbar ? 56 : 64, margin: searchToolbar ? 16 : undefined}}> From df7088cc30d4374528fdba9530a93dfd5f83944a Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Sun, 14 Jul 2024 14:38:41 +0100 Subject: [PATCH 13/60] Removed redundant import --- .../src/main/java/com/navigation/reactnative/DrawerView.java | 1 - 1 file changed, 1 deletion(-) diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerView.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerView.java index 29f4b484a..6f133168a 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerView.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerView.java @@ -7,7 +7,6 @@ import androidx.annotation.NonNull; import androidx.drawerlayout.widget.DrawerLayout; -import androidx.recyclerview.widget.RecyclerView; import com.google.android.material.navigation.NavigationView; From a5b7546cc64c3a127cbc879fb4d763b76cb274b5 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Sun, 14 Jul 2024 14:44:36 +0100 Subject: [PATCH 14/60] Changed for consistency --- NavigationReactNative/src/Drawer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NavigationReactNative/src/Drawer.tsx b/NavigationReactNative/src/Drawer.tsx index 4059e9b4f..1ec27c994 100644 --- a/NavigationReactNative/src/Drawer.tsx +++ b/NavigationReactNative/src/Drawer.tsx @@ -1,4 +1,4 @@ -import React, {useState} from 'react'; +import React, { useState } from 'react'; import { requireNativeComponent, StyleSheet } from 'react-native'; const Drawer = ({view, open = false, onChangeOpen, children}) => { From fcc649d3a43be13951c215effcba012dc01a329c Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Sun, 14 Jul 2024 16:30:46 +0100 Subject: [PATCH 15/60] Raised onOpen and onClose events Might want to do something when the drawer animation completes --- NavigationReactNative/src/Drawer.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/NavigationReactNative/src/Drawer.tsx b/NavigationReactNative/src/Drawer.tsx index 1ec27c994..a61426434 100644 --- a/NavigationReactNative/src/Drawer.tsx +++ b/NavigationReactNative/src/Drawer.tsx @@ -1,7 +1,7 @@ import React, { useState } from 'react'; import { requireNativeComponent, StyleSheet } from 'react-native'; -const Drawer = ({view, open = false, onChangeOpen, children}) => { +const Drawer = ({view, open = false, onChangeOpen, onOpen, onClose, children}) => { const [show, setShow] = useState(false); const [mostRecentEventCount, setMostRecentEventCount] = useState(0); if (open != null && show !== open) setShow(open); @@ -13,6 +13,8 @@ const Drawer = ({view, open = false, onChangeOpen, children}) => { if (!!onChangeOpen) onChangeOpen(newOpen); } + if (newOpen) onOpen?.(); + else onClose?.(); setMostRecentEventCount(mostRecentEventCount); } return ( From 34909f111b7d18e31e87d03b9b9fc18c3e2da8eb Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Sun, 14 Jul 2024 17:57:06 +0100 Subject: [PATCH 16/60] Supported drawers from the right (and left) --- NavigationReactNative/src/Drawer.tsx | 5 +++-- .../com/navigation/reactnative/DrawerLayoutManager.java | 8 ++++++++ .../com/navigation/reactnative/DrawerLayoutView.java | 5 +++-- .../java/com/navigation/reactnative/DrawerManager.java | 9 +++++++++ 4 files changed, 23 insertions(+), 4 deletions(-) diff --git a/NavigationReactNative/src/Drawer.tsx b/NavigationReactNative/src/Drawer.tsx index a61426434..0a258f4af 100644 --- a/NavigationReactNative/src/Drawer.tsx +++ b/NavigationReactNative/src/Drawer.tsx @@ -1,7 +1,7 @@ import React, { useState } from 'react'; import { requireNativeComponent, StyleSheet } from 'react-native'; -const Drawer = ({view, open = false, onChangeOpen, onOpen, onClose, children}) => { +const Drawer = ({view, open = false, fromRight = false, onChangeOpen, onOpen, onClose, children}) => { const [show, setShow] = useState(false); const [mostRecentEventCount, setMostRecentEventCount] = useState(0); if (open != null && show !== open) setShow(open); @@ -20,11 +20,12 @@ const Drawer = ({view, open = false, onChangeOpen, onOpen, onClose, children}) = return ( {children} - + {view} diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutManager.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutManager.java index 229165333..0221a8b7f 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutManager.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutManager.java @@ -1,5 +1,7 @@ package com.navigation.reactnative; +import android.view.Gravity; + import androidx.annotation.NonNull; import com.facebook.react.common.MapBuilder; @@ -21,6 +23,12 @@ public void setOpen(DrawerLayoutView view, boolean open) { view.pendingOpen = open; } + @ReactProp(name = "fromRight") + public void setFromRight(DrawerLayoutView view, boolean fromRight) { + view.gravity = !fromRight ? Gravity.LEFT : Gravity.RIGHT; + view.requestLayout(); + } + @ReactProp(name = "mostRecentEventCount") public void setMostRecentEventCount(DrawerLayoutView view, int mostRecentEventCount) { view.mostRecentEventCount = mostRecentEventCount; diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutView.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutView.java index 52a27a5c3..41fa90558 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutView.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutView.java @@ -21,6 +21,7 @@ public class DrawerLayoutView extends DrawerLayout { int nativeEventCount; int mostRecentEventCount; boolean pendingOpen; + int gravity; boolean dragging; private boolean layoutRequested = false; @@ -48,8 +49,8 @@ public void onDrawerClosed(View drawerView) { void onAfterUpdateTransaction() { int eventLag = nativeEventCount - mostRecentEventCount; if (eventLag == 0 && isOpen() != pendingOpen) { - if (pendingOpen) openDrawer(Gravity.LEFT); - else closeDrawer(Gravity.LEFT); + if (pendingOpen) openDrawer(gravity); + else closeDrawer(gravity); } } diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerManager.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerManager.java index 8f71890be..516a8ca26 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerManager.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerManager.java @@ -1,9 +1,13 @@ package com.navigation.reactnative; +import android.view.Gravity; + import androidx.annotation.NonNull; +import androidx.drawerlayout.widget.DrawerLayout; import com.facebook.react.uimanager.ThemedReactContext; import com.facebook.react.uimanager.ViewGroupManager; +import com.facebook.react.uimanager.annotations.ReactProp; public class DrawerManager extends ViewGroupManager { @NonNull @@ -12,6 +16,11 @@ public String getName() { return "NVDrawer"; } + @ReactProp(name = "fromRight") + public void setFromRight(DrawerView view, boolean fromRight) { + ((DrawerLayout.LayoutParams) view.getLayoutParams()).gravity = !fromRight ? Gravity.LEFT : Gravity.RIGHT; + } + @NonNull @Override protected DrawerView createViewInstance(@NonNull ThemedReactContext themedReactContext) { From c0fbf55a39ed68816d3cc3364ef26500302f3e3e Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Tue, 16 Jul 2024 17:42:41 +0100 Subject: [PATCH 17/60] Handled back when gesture back not enabled Closed drawer using React Native back handling. Also removed default open value so it can work uncontrolled (same as active prop on SearchBar) --- NavigationReactNative/src/Drawer.tsx | 37 +++++++++++++++++++--------- 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/NavigationReactNative/src/Drawer.tsx b/NavigationReactNative/src/Drawer.tsx index 0a258f4af..64d1afd95 100644 --- a/NavigationReactNative/src/Drawer.tsx +++ b/NavigationReactNative/src/Drawer.tsx @@ -1,7 +1,8 @@ import React, { useState } from 'react'; import { requireNativeComponent, StyleSheet } from 'react-native'; +import BackButton from './BackButton'; -const Drawer = ({view, open = false, fromRight = false, onChangeOpen, onOpen, onClose, children}) => { +const Drawer = ({view, open, fromRight = false, onChangeOpen, onOpen, onClose, children}) => { const [show, setShow] = useState(false); const [mostRecentEventCount, setMostRecentEventCount] = useState(0); if (open != null && show !== open) setShow(open); @@ -18,17 +19,29 @@ const Drawer = ({view, open = false, fromRight = false, onChangeOpen, onOpen, on setMostRecentEventCount(mostRecentEventCount); } return ( - - {children} - - {view} - - + <> + { + if (show) { + if (open == null) + setShow(false); + if (!!onChangeOpen) + onChangeOpen(false); + return true; + } + return false; + }} /> + + {children} + + {view} + + + ); }; From ddde4bca6627940026fb9b3133f13bb6b8444cef Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Tue, 16 Jul 2024 17:46:14 +0100 Subject: [PATCH 18/60] Removed code for old drawer I checked back and this seemed to be for handling activities - apparently drawer didn't open on second activity (https://github.com/grahammendick/navigation/pull/266). This seems to be a good time to remove this old code --- .../com/navigation/reactnative/SceneView.java | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SceneView.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SceneView.java index 75198033e..2ffb4738f 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SceneView.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SceneView.java @@ -47,11 +47,6 @@ protected void setLandscape(boolean landscape) { protected void onAttachedToWindow() { super.onAttachedToWindow(); setOrientation(); - View child = getChildAt(0); - if (child != null && child.getClass().getSimpleName().contains("DrawerLayout")) { - child.requestLayout(); - post(measureAndLayoutDrawer); - } } private void setOrientation() { @@ -61,18 +56,6 @@ private void setOrientation() { } } - private final Runnable measureAndLayoutDrawer = new Runnable() { - @Override - public void run() { - View drawer = getChildAt(0); - if (drawer == null) return; - drawer.measure( - MeasureSpec.makeMeasureSpec(drawer.getWidth(), MeasureSpec.EXACTLY), - MeasureSpec.makeMeasureSpec(drawer.getHeight(), MeasureSpec.EXACTLY)); - drawer.layout(drawer.getLeft(), drawer.getTop(), drawer.getRight(), drawer.getBottom()); - } - }; - protected void popped() { ReactContext reactContext = (ReactContext) getContext(); EventDispatcher eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, getId()); From f9217a8777ca741e3f7367ade0bf91985077cc21 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Fri, 19 Jul 2024 18:47:23 +0100 Subject: [PATCH 19/60] Connected toolbar and drawer for auto toggling The ActionBarDrawerToggle handles connecting the toolbar to the drawer. Made weak references so that the scene view doesn't stop the toolbar and drawer from being disposed --- .../reactnative/DrawerLayoutView.java | 38 ++++++++++++++++++- .../com/navigation/reactnative/SceneView.java | 36 +++++++++++++++++- .../reactnative/ToolbarDrawerView.java | 7 ++++ .../navigation/reactnative/ToolbarView.java | 24 +++++++++++- 4 files changed, 100 insertions(+), 5 deletions(-) create mode 100644 NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarDrawerView.java diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutView.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutView.java index 41fa90558..b88e51d8d 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutView.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutView.java @@ -1,11 +1,12 @@ package com.navigation.reactnative; import android.content.Context; -import android.view.Gravity; import android.view.MotionEvent; import android.view.View; +import android.view.ViewParent; import androidx.annotation.NonNull; +import androidx.appcompat.app.ActionBarDrawerToggle; import androidx.drawerlayout.widget.DrawerLayout; import com.facebook.react.bridge.Arguments; @@ -17,13 +18,14 @@ import com.facebook.react.uimanager.events.NativeGestureUtil; import com.facebook.react.uimanager.events.RCTEventEmitter; -public class DrawerLayoutView extends DrawerLayout { +public class DrawerLayoutView extends DrawerLayout implements ToolbarDrawerView { int nativeEventCount; int mostRecentEventCount; boolean pendingOpen; int gravity; boolean dragging; private boolean layoutRequested = false; + ActionBarDrawerToggle toolbarDrawerToggle; public DrawerLayoutView(@NonNull Context context) { super(context); @@ -34,6 +36,8 @@ public void onDrawerOpened(View drawerView) { ReactContext reactContext = (ReactContext) getContext(); EventDispatcher eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, getId()); eventDispatcher.dispatchEvent(new DrawerLayoutView.ChangeOpenEvent(getId(), true, nativeEventCount)); + if (toolbarDrawerToggle != null) + toolbarDrawerToggle.onDrawerOpened(drawerView); } @Override @@ -42,6 +46,23 @@ public void onDrawerClosed(View drawerView) { ReactContext reactContext = (ReactContext) getContext(); EventDispatcher eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, getId()); eventDispatcher.dispatchEvent(new DrawerLayoutView.ChangeOpenEvent(getId(), false, nativeEventCount)); + if (toolbarDrawerToggle != null) + toolbarDrawerToggle.onDrawerClosed(drawerView); + } + + @Override + public void onDrawerSlide(View drawerView, float slideOffset) { + super.onDrawerSlide(drawerView, slideOffset); + if (toolbarDrawerToggle != null) + toolbarDrawerToggle.onDrawerSlide(drawerView, slideOffset); + + } + + @Override + public void onDrawerStateChanged(int newState) { + super.onDrawerStateChanged(newState); + if (toolbarDrawerToggle != null) + toolbarDrawerToggle.onDrawerStateChanged(newState); } }); } @@ -57,6 +78,14 @@ void onAfterUpdateTransaction() { @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); + ViewParent parent = this; + while(parent != null) { + parent = parent.getParent(); + if (parent instanceof SceneView sceneView) { + sceneView.setDrawer(this); + parent = null; + } + } requestLayout(); } @@ -97,6 +126,11 @@ public boolean onTouchEvent(MotionEvent ev) { return super.onTouchEvent(ev); } + @Override + public void handleToggle(ActionBarDrawerToggle toolbarDrawerToggle) { + this.toolbarDrawerToggle = toolbarDrawerToggle; + } + static class ChangeOpenEvent extends Event { private final boolean open; private final int eventCount; diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SceneView.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SceneView.java index 2ffb4738f..351b145f0 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SceneView.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SceneView.java @@ -3,9 +3,11 @@ import android.app.Activity; import android.content.Context; import android.content.pm.ActivityInfo; -import android.view.View; -import android.view.animation.Animation; +import android.graphics.drawable.Drawable; +import androidx.appcompat.app.ActionBarDrawerToggle; +import androidx.appcompat.widget.Toolbar; +import androidx.drawerlayout.widget.DrawerLayout; import androidx.transition.Transition; import com.facebook.react.bridge.Arguments; @@ -16,6 +18,7 @@ import com.facebook.react.uimanager.events.RCTEventEmitter; import com.facebook.react.views.view.ReactViewGroup; +import java.lang.ref.WeakReference; import java.util.HashSet; public class SceneView extends ReactViewGroup { @@ -30,6 +33,8 @@ public class SceneView extends ReactViewGroup { private boolean landscape; public final HashSet sharedElements = new HashSet<>(); public SharedElementMotion sharedElementMotion; + private WeakReference toolbar; + private WeakReference drawer; public SceneView(Context context) { super(context); @@ -49,6 +54,33 @@ protected void onAttachedToWindow() { setOrientation(); } + protected void setToolbar(Toolbar toolbar) { + this.toolbar = new WeakReference<>(toolbar); + this.createToolbarDrawerToggle(); + } + + protected void setDrawer(DrawerLayoutView drawer) { + this.drawer = new WeakReference<>(drawer); + this.createToolbarDrawerToggle(); + } + + private void createToolbarDrawerToggle() { + if (toolbar != null && drawer != null) { + Toolbar toolbarView = toolbar.get(); + DrawerLayoutView drawerView = drawer.get(); + if (toolbarView != null && drawerView != null) { + Drawable navigationIcon = toolbarView.getNavigationIcon(); + Activity activity = ((ReactContext) getContext()).getCurrentActivity(); + ActionBarDrawerToggle toolbarDrawerToggle = new ActionBarDrawerToggle(activity, drawerView, toolbarView, 0, 0); + toolbarDrawerToggle.setDrawerIndicatorEnabled(true); + toolbarDrawerToggle.syncState(); + if (navigationIcon != null) toolbarView.setNavigationIcon(navigationIcon); + ((ToolbarDrawerView) toolbarView).handleToggle(toolbarDrawerToggle); + drawerView.handleToggle(toolbarDrawerToggle); + } + } + } + private void setOrientation() { if (getVisibility() == VISIBLE) { Activity activity = ((ReactContext) getContext()).getCurrentActivity(); diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarDrawerView.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarDrawerView.java new file mode 100644 index 000000000..5612f3ffa --- /dev/null +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarDrawerView.java @@ -0,0 +1,7 @@ +package com.navigation.reactnative; + +import androidx.appcompat.app.ActionBarDrawerToggle; + +public interface ToolbarDrawerView { + void handleToggle(ActionBarDrawerToggle toolbarDrawerToggle); +} diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarView.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarView.java index 8b68a9c49..033f7a689 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarView.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarView.java @@ -14,11 +14,13 @@ import android.view.Menu; import android.view.MenuItem; import android.view.View; +import android.view.ViewParent; import android.widget.ImageButton; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.appcompat.app.ActionBarDrawerToggle; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.ActionMenuView; import androidx.appcompat.widget.AppCompatImageButton; @@ -40,7 +42,7 @@ import java.util.ArrayList; import java.util.HashMap; -public class ToolbarView extends MaterialToolbar implements ActionView { +public class ToolbarView extends MaterialToolbar implements ActionView, ToolbarDrawerView { private MenuItem searchMenuItem; private String title; private String titleFontFamily; @@ -295,6 +297,20 @@ void styleTitle() { } } + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + // check if autoNavigation + ViewParent parent = this; + while(parent != null) { + parent = parent.getParent(); + if (parent instanceof SceneView sceneView) { + sceneView.setToolbar(this); + parent = null; + } + } + } + private void onNavigationClick(View view) { ReactContext reactContext = (ReactContext) getContext(); EventDispatcher eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, getId()); @@ -339,6 +355,12 @@ public void requestLayout() { layout(getLeft(), getTop(), getRight(), getBottom()); }; + @Override + public void handleToggle(ActionBarDrawerToggle toolbarDrawerToggle) { + setTintColor(getNavigationIcon()); + setTestID(); + } + static class NavigationPressEvent extends Event { public NavigationPressEvent(int viewId) { super(viewId); From dc6517f54f173d4378f547f1ef4a57fbf7c54249 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Fri, 19 Jul 2024 22:47:20 +0100 Subject: [PATCH 20/60] Removed custom navigation icon if auto No point using a custom icon with drawer toggle because the whole point is the animation. Plus it got messy because creating the toggle again when navigating back (onAttached called again which created the toggle) stopped the animation from working. It replaced the navigation icon with the one from the previous toggle. Makes most sense to use the built in icons when auto navigate. If user wants custom icon then can manually navigate --- NavigationReactNative/src/NavigationBar.tsx | 4 ++-- .../src/main/java/com/navigation/reactnative/SceneView.java | 6 ++---- .../main/java/com/navigation/reactnative/ToolbarView.java | 2 -- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/NavigationReactNative/src/NavigationBar.tsx b/NavigationReactNative/src/NavigationBar.tsx index 5f4593c37..eac8cae65 100644 --- a/NavigationReactNative/src/NavigationBar.tsx +++ b/NavigationReactNative/src/NavigationBar.tsx @@ -76,8 +76,8 @@ class NavigationBar extends React.Component { {collapsingBar && collapsingBar.props.children} 0} - navigationImage={Image.resolveAssetSource(navigationImage)} + showHome={autoNavigation && crumb > 0} + navigationImage={!autoNavigation ? Image.resolveAssetSource(navigationImage) : undefined} overflowImage={Image.resolveAssetSource(overflowImage)} pin={!!collapsingBar} {...otherProps} diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SceneView.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SceneView.java index 351b145f0..bcda0806a 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SceneView.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SceneView.java @@ -56,12 +56,12 @@ protected void onAttachedToWindow() { protected void setToolbar(Toolbar toolbar) { this.toolbar = new WeakReference<>(toolbar); - this.createToolbarDrawerToggle(); + createToolbarDrawerToggle(); } protected void setDrawer(DrawerLayoutView drawer) { this.drawer = new WeakReference<>(drawer); - this.createToolbarDrawerToggle(); + createToolbarDrawerToggle(); } private void createToolbarDrawerToggle() { @@ -69,12 +69,10 @@ private void createToolbarDrawerToggle() { Toolbar toolbarView = toolbar.get(); DrawerLayoutView drawerView = drawer.get(); if (toolbarView != null && drawerView != null) { - Drawable navigationIcon = toolbarView.getNavigationIcon(); Activity activity = ((ReactContext) getContext()).getCurrentActivity(); ActionBarDrawerToggle toolbarDrawerToggle = new ActionBarDrawerToggle(activity, drawerView, toolbarView, 0, 0); toolbarDrawerToggle.setDrawerIndicatorEnabled(true); toolbarDrawerToggle.syncState(); - if (navigationIcon != null) toolbarView.setNavigationIcon(navigationIcon); ((ToolbarDrawerView) toolbarView).handleToggle(toolbarDrawerToggle); drawerView.handleToggle(toolbarDrawerToggle); } diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarView.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarView.java index 033f7a689..1279c8028 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarView.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarView.java @@ -143,13 +143,11 @@ void setTitleFontSize(Integer titleFontSize) { void setShowHome(boolean showHome) { AppCompatActivity activity = (AppCompatActivity) ((ReactContext) getContext()).getCurrentActivity(); assert activity != null; - Drawable navigationIcon = getNavigationIcon(); activity.setSupportActionBar(this); assert activity.getSupportActionBar() != null; activity.getSupportActionBar().setDisplayHomeAsUpEnabled(showHome); activity.setSupportActionBar(null); setNavigationOnClickListener(this::onNavigationClick); - if (navigationIcon != null) setNavigationIcon(navigationIcon); setTintColor(getNavigationIcon()); setTestID(); } From 2c4b7f35fdf02e189d9636f6edda9f0a759a3ab5 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Fri, 19 Jul 2024 22:48:11 +0100 Subject: [PATCH 21/60] Resync'ed when attached to window If drawer opened/closed during the navigation then the arrow wasn't sync'ed when navigating back --- .../main/java/com/navigation/reactnative/DrawerLayoutView.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutView.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutView.java index b88e51d8d..a2ffe74ca 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutView.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutView.java @@ -78,6 +78,8 @@ void onAfterUpdateTransaction() { @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); + if (toolbarDrawerToggle != null) + toolbarDrawerToggle.syncState(); ViewParent parent = this; while(parent != null) { parent = parent.getParent(); From d3ff56b84eda453727a42e618b63b7afb6381633 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Sat, 20 Jul 2024 15:09:34 +0100 Subject: [PATCH 22/60] Allowed one of autoNavigation or navigation icon Turning on autoNavigation means let the Navigation router handle the navigation icon and navigation. Turning it off means user handles it. Keeps things simple - too complicated to allow autoNavigation with custom icon. Also clearer this way because there's only one navigation click listener which have to give to android when doing drawer toggle anyway. --- NavigationReactNative/src/NavigationBar.tsx | 5 +- .../reactnative/DrawerLayoutView.java | 7 ++- .../com/navigation/reactnative/SceneView.java | 2 - .../reactnative/ToolbarManager.java | 13 +++-- .../navigation/reactnative/ToolbarView.java | 52 ++++++++++++------- 5 files changed, 51 insertions(+), 28 deletions(-) diff --git a/NavigationReactNative/src/NavigationBar.tsx b/NavigationReactNative/src/NavigationBar.tsx index eac8cae65..5218f1e95 100644 --- a/NavigationReactNative/src/NavigationBar.tsx +++ b/NavigationReactNative/src/NavigationBar.tsx @@ -75,9 +75,10 @@ class NavigationBar extends React.Component { {...(collapsingBar && collapsingBar.props)}> {collapsingBar && collapsingBar.props.children} 0} - navigationImage={!autoNavigation ? Image.resolveAssetSource(navigationImage) : undefined} + navigationImage={Image.resolveAssetSource(navigationImage)} overflowImage={Image.resolveAssetSource(overflowImage)} pin={!!collapsingBar} {...otherProps} diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutView.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutView.java index a2ffe74ca..ee3850ec6 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutView.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutView.java @@ -68,6 +68,7 @@ public void onDrawerStateChanged(int newState) { } void onAfterUpdateTransaction() { + setupDrawerToggle(); int eventLag = nativeEventCount - mostRecentEventCount; if (eventLag == 0 && isOpen() != pendingOpen) { if (pendingOpen) openDrawer(gravity); @@ -80,6 +81,11 @@ protected void onAttachedToWindow() { super.onAttachedToWindow(); if (toolbarDrawerToggle != null) toolbarDrawerToggle.syncState(); + setupDrawerToggle(); + requestLayout(); + } + + private void setupDrawerToggle() { ViewParent parent = this; while(parent != null) { parent = parent.getParent(); @@ -88,7 +94,6 @@ protected void onAttachedToWindow() { parent = null; } } - requestLayout(); } @Override diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SceneView.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SceneView.java index bcda0806a..c868fcc07 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SceneView.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SceneView.java @@ -3,11 +3,9 @@ import android.app.Activity; import android.content.Context; import android.content.pm.ActivityInfo; -import android.graphics.drawable.Drawable; import androidx.appcompat.app.ActionBarDrawerToggle; import androidx.appcompat.widget.Toolbar; -import androidx.drawerlayout.widget.DrawerLayout; import androidx.transition.Transition; import com.facebook.react.bridge.Arguments; diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarManager.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarManager.java index 88261dff7..1decfed5d 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarManager.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarManager.java @@ -31,9 +31,14 @@ protected ToolbarView createViewInstance(@Nonnull ThemedReactContext reactContex return new ToolbarView(reactContext); } - @ReactProp(name = "showHome") - public void setShowHome(ToolbarView view, boolean showHome) { - view.setShowHome(showHome); + @ReactProp(name = "crumb") + public void setCrumb(ToolbarView view, int crumb) { + view.crumb = crumb; + } + + @ReactProp(name = "autoNavigation") + public void setAutoNavigation(ToolbarView view, boolean autoNavigation) { + view.autoNavigation = autoNavigation; } @ReactProp(name = "title") @@ -168,7 +173,7 @@ public boolean needsCustomLayoutForChildren() { @Override protected void onAfterUpdateTransaction(@NonNull ToolbarView view) { super.onAfterUpdateTransaction(view); - view.styleTitle(); + view.onAfterUpdateTransaction(); } @Override diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarView.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarView.java index 1279c8028..2cd6ba175 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarView.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarView.java @@ -44,6 +44,8 @@ public class ToolbarView extends MaterialToolbar implements ActionView, ToolbarDrawerView { private MenuItem searchMenuItem; + int crumb; + boolean autoNavigation; private String title; private String titleFontFamily; private String titleFontWeight; @@ -55,6 +57,7 @@ public class ToolbarView extends MaterialToolbar implements ActionView, ToolbarD private OnSearchListener onSearchAddedListener; final int defaultTitleTextColor; final Drawable defaultOverflowIcon; + private Drawable navigationIcon; private Integer defaultMenuTintColor; private String navigationTestID; private String overflowTestID; @@ -74,9 +77,13 @@ public ToolbarView(Context context) { setTintColor(getLogo()); }; navIconResolverListener = d -> { - setNavigationIcon(d); - setTintColor(getNavigationIcon()); - setTestID(); + if (!autoNavigation) { + setNavigationIcon(d); + setTintColor(getNavigationIcon()); + setTestID(); + } else { + navigationIcon = d; + } }; overflowIconResolverListener = d -> { setOverflowIcon(d); @@ -85,8 +92,7 @@ public ToolbarView(Context context) { setNavigationOnClickListener(this::onNavigationClick); setOnMenuItemClickListener(item -> { for (int i = 0; i < children.size(); i++) { - if (children.get(i) instanceof BarButtonView) { - BarButtonView barButtonView = (BarButtonView) children.get(i); + if (children.get(i) instanceof BarButtonView barButtonView) { if (barButtonView.getMenuItem() != item) barButtonView.getMenuItem().collapseActionView(); else @@ -140,18 +146,6 @@ void setTitleFontSize(Integer titleFontSize) { titleChanged = true; } - void setShowHome(boolean showHome) { - AppCompatActivity activity = (AppCompatActivity) ((ReactContext) getContext()).getCurrentActivity(); - assert activity != null; - activity.setSupportActionBar(this); - assert activity.getSupportActionBar() != null; - activity.getSupportActionBar().setDisplayHomeAsUpEnabled(showHome); - activity.setSupportActionBar(null); - setNavigationOnClickListener(this::onNavigationClick); - setTintColor(getNavigationIcon()); - setTestID(); - } - void setLogoSource(@Nullable ReadableMap source) { IconResolver.setIconSource(source, logoResolverListener, getContext()); } @@ -272,7 +266,24 @@ private void setTestID() { } } - void styleTitle() { + void onAfterUpdateTransaction() { + AppCompatActivity activity = (AppCompatActivity) ((ReactContext) getContext()).getCurrentActivity(); + assert activity != null; + if (autoNavigation) { + if (crumb > 0) { + setNavigationIcon(null); + activity.setSupportActionBar(this); + assert activity.getSupportActionBar() != null; + activity.getSupportActionBar().setDisplayHomeAsUpEnabled(true); + activity.setSupportActionBar(null); + } + setupDrawerToggle(); + } else { + setNavigationIcon(navigationIcon); + } + setNavigationOnClickListener(this::onNavigationClick); + setTintColor(getNavigationIcon()); + setTestID(); if (titleChanged) { SpannableString titleSpannable = null; if (title != null) { @@ -298,7 +309,10 @@ void styleTitle() { @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); - // check if autoNavigation + if (autoNavigation) setupDrawerToggle(); + } + + private void setupDrawerToggle() { ViewParent parent = this; while(parent != null) { parent = parent.getParent(); From 76540c8e639ddd43c41ada81d18f928b8a899af6 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Sat, 20 Jul 2024 15:14:47 +0100 Subject: [PATCH 23/60] Restored icon when toggling autoNavigation On drawer scenario had custom navigationIcon and autoNavigation off. Then toggled autoNavigation to on then back to off and then custom icon didn't appear. That's because it hadn't changed - so stored it even when autoNavigation off so can restore it later --- .../src/main/java/com/navigation/reactnative/ToolbarView.java | 1 + 1 file changed, 1 insertion(+) diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarView.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarView.java index 2cd6ba175..b295a66b5 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarView.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarView.java @@ -78,6 +78,7 @@ public ToolbarView(Context context) { }; navIconResolverListener = d -> { if (!autoNavigation) { + navigationIcon = d; setNavigationIcon(d); setTintColor(getNavigationIcon()); setTestID(); From 98f9799801539d63b664d8ad2c47259e8ab3ac8e Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Sat, 20 Jul 2024 21:27:01 +0100 Subject: [PATCH 24/60] Renamed for clarity --- .../reactnative/DrawerLayoutView.java | 36 +++++++++---------- .../reactnative/DrawerToggleHandler.java | 7 ++++ .../com/navigation/reactnative/SceneView.java | 26 +++++++------- .../navigation/reactnative/ToolbarView.java | 12 +++---- 4 files changed, 43 insertions(+), 38 deletions(-) create mode 100644 NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerToggleHandler.java diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutView.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutView.java index ee3850ec6..5da2d7179 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutView.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutView.java @@ -18,14 +18,14 @@ import com.facebook.react.uimanager.events.NativeGestureUtil; import com.facebook.react.uimanager.events.RCTEventEmitter; -public class DrawerLayoutView extends DrawerLayout implements ToolbarDrawerView { +public class DrawerLayoutView extends DrawerLayout implements DrawerToggleHandler { int nativeEventCount; int mostRecentEventCount; boolean pendingOpen; int gravity; boolean dragging; private boolean layoutRequested = false; - ActionBarDrawerToggle toolbarDrawerToggle; + ActionBarDrawerToggle drawerToggle; public DrawerLayoutView(@NonNull Context context) { super(context); @@ -36,8 +36,8 @@ public void onDrawerOpened(View drawerView) { ReactContext reactContext = (ReactContext) getContext(); EventDispatcher eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, getId()); eventDispatcher.dispatchEvent(new DrawerLayoutView.ChangeOpenEvent(getId(), true, nativeEventCount)); - if (toolbarDrawerToggle != null) - toolbarDrawerToggle.onDrawerOpened(drawerView); + if (drawerToggle != null) + drawerToggle.onDrawerOpened(drawerView); } @Override @@ -46,29 +46,29 @@ public void onDrawerClosed(View drawerView) { ReactContext reactContext = (ReactContext) getContext(); EventDispatcher eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, getId()); eventDispatcher.dispatchEvent(new DrawerLayoutView.ChangeOpenEvent(getId(), false, nativeEventCount)); - if (toolbarDrawerToggle != null) - toolbarDrawerToggle.onDrawerClosed(drawerView); + if (drawerToggle != null) + drawerToggle.onDrawerClosed(drawerView); } @Override public void onDrawerSlide(View drawerView, float slideOffset) { super.onDrawerSlide(drawerView, slideOffset); - if (toolbarDrawerToggle != null) - toolbarDrawerToggle.onDrawerSlide(drawerView, slideOffset); + if (drawerToggle != null) + drawerToggle.onDrawerSlide(drawerView, slideOffset); } @Override public void onDrawerStateChanged(int newState) { super.onDrawerStateChanged(newState); - if (toolbarDrawerToggle != null) - toolbarDrawerToggle.onDrawerStateChanged(newState); + if (drawerToggle != null) + drawerToggle.onDrawerStateChanged(newState); } }); } void onAfterUpdateTransaction() { - setupDrawerToggle(); + registerDrawerToggleHandler(); int eventLag = nativeEventCount - mostRecentEventCount; if (eventLag == 0 && isOpen() != pendingOpen) { if (pendingOpen) openDrawer(gravity); @@ -79,18 +79,18 @@ void onAfterUpdateTransaction() { @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); - if (toolbarDrawerToggle != null) - toolbarDrawerToggle.syncState(); - setupDrawerToggle(); + if (drawerToggle != null) + drawerToggle.syncState(); + registerDrawerToggleHandler(); requestLayout(); } - private void setupDrawerToggle() { + private void registerDrawerToggleHandler() { ViewParent parent = this; while(parent != null) { parent = parent.getParent(); if (parent instanceof SceneView sceneView) { - sceneView.setDrawer(this); + sceneView.registerDrawerToggleHandler(this); parent = null; } } @@ -134,8 +134,8 @@ public boolean onTouchEvent(MotionEvent ev) { } @Override - public void handleToggle(ActionBarDrawerToggle toolbarDrawerToggle) { - this.toolbarDrawerToggle = toolbarDrawerToggle; + public void initDrawerToggle(ActionBarDrawerToggle drawerToggle) { + this.drawerToggle = drawerToggle; } static class ChangeOpenEvent extends Event { diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerToggleHandler.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerToggleHandler.java new file mode 100644 index 000000000..216a89d37 --- /dev/null +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerToggleHandler.java @@ -0,0 +1,7 @@ +package com.navigation.reactnative; + +import androidx.appcompat.app.ActionBarDrawerToggle; + +public interface DrawerToggleHandler { + void initDrawerToggle(ActionBarDrawerToggle drawerToggle); +} diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SceneView.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SceneView.java index c868fcc07..efaecb0a0 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SceneView.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SceneView.java @@ -52,27 +52,25 @@ protected void onAttachedToWindow() { setOrientation(); } - protected void setToolbar(Toolbar toolbar) { - this.toolbar = new WeakReference<>(toolbar); - createToolbarDrawerToggle(); + protected void registerDrawerToggleHandler(DrawerToggleHandler drawerToggleHandler) { + if (drawerToggleHandler instanceof Toolbar toolbarView) + this.toolbar = new WeakReference<>(toolbarView); + if (drawerToggleHandler instanceof DrawerLayoutView drawerView) + this.drawer = new WeakReference<>(drawerView); + initDrawerToggle(); } - protected void setDrawer(DrawerLayoutView drawer) { - this.drawer = new WeakReference<>(drawer); - createToolbarDrawerToggle(); - } - - private void createToolbarDrawerToggle() { + private void initDrawerToggle() { if (toolbar != null && drawer != null) { Toolbar toolbarView = toolbar.get(); DrawerLayoutView drawerView = drawer.get(); if (toolbarView != null && drawerView != null) { Activity activity = ((ReactContext) getContext()).getCurrentActivity(); - ActionBarDrawerToggle toolbarDrawerToggle = new ActionBarDrawerToggle(activity, drawerView, toolbarView, 0, 0); - toolbarDrawerToggle.setDrawerIndicatorEnabled(true); - toolbarDrawerToggle.syncState(); - ((ToolbarDrawerView) toolbarView).handleToggle(toolbarDrawerToggle); - drawerView.handleToggle(toolbarDrawerToggle); + ActionBarDrawerToggle drawerToggle = new ActionBarDrawerToggle(activity, drawerView, toolbarView, 0, 0); + drawerToggle.setDrawerIndicatorEnabled(true); + drawerToggle.syncState(); + ((DrawerToggleHandler) toolbarView).initDrawerToggle(drawerToggle); + drawerView.initDrawerToggle(drawerToggle); } } } diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarView.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarView.java index b295a66b5..004e76d54 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarView.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarView.java @@ -42,7 +42,7 @@ import java.util.ArrayList; import java.util.HashMap; -public class ToolbarView extends MaterialToolbar implements ActionView, ToolbarDrawerView { +public class ToolbarView extends MaterialToolbar implements ActionView, DrawerToggleHandler { private MenuItem searchMenuItem; int crumb; boolean autoNavigation; @@ -278,7 +278,7 @@ void onAfterUpdateTransaction() { activity.getSupportActionBar().setDisplayHomeAsUpEnabled(true); activity.setSupportActionBar(null); } - setupDrawerToggle(); + registerDrawerToggleHandler(); } else { setNavigationIcon(navigationIcon); } @@ -310,15 +310,15 @@ void onAfterUpdateTransaction() { @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); - if (autoNavigation) setupDrawerToggle(); + if (autoNavigation) registerDrawerToggleHandler(); } - private void setupDrawerToggle() { + private void registerDrawerToggleHandler() { ViewParent parent = this; while(parent != null) { parent = parent.getParent(); if (parent instanceof SceneView sceneView) { - sceneView.setToolbar(this); + sceneView.registerDrawerToggleHandler(this); parent = null; } } @@ -369,7 +369,7 @@ public void requestLayout() { }; @Override - public void handleToggle(ActionBarDrawerToggle toolbarDrawerToggle) { + public void initDrawerToggle(ActionBarDrawerToggle drawerToggle) { setTintColor(getNavigationIcon()); setTestID(); } From d4e16a0f4df8d423fd85ad63176a04766187806b Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Sun, 21 Jul 2024 16:31:54 +0100 Subject: [PATCH 25/60] Provided open and close drawer string resources --- .../src/main/java/com/navigation/reactnative/SceneView.java | 2 +- .../src/android/src/main/res/values/strings.xml | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SceneView.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SceneView.java index efaecb0a0..ee528b5a6 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SceneView.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SceneView.java @@ -66,7 +66,7 @@ private void initDrawerToggle() { DrawerLayoutView drawerView = drawer.get(); if (toolbarView != null && drawerView != null) { Activity activity = ((ReactContext) getContext()).getCurrentActivity(); - ActionBarDrawerToggle drawerToggle = new ActionBarDrawerToggle(activity, drawerView, toolbarView, 0, 0); + ActionBarDrawerToggle drawerToggle = new ActionBarDrawerToggle(activity, drawerView, toolbarView, R.string.drawer_open, R.string.drawer_close); drawerToggle.setDrawerIndicatorEnabled(true); drawerToggle.syncState(); ((DrawerToggleHandler) toolbarView).initDrawerToggle(drawerToggle); diff --git a/NavigationReactNative/src/android/src/main/res/values/strings.xml b/NavigationReactNative/src/android/src/main/res/values/strings.xml index 55344e519..3d8d96fc0 100644 --- a/NavigationReactNative/src/android/src/main/res/values/strings.xml +++ b/NavigationReactNative/src/android/src/main/res/values/strings.xml @@ -1,3 +1,5 @@ + Open drawer + Close drawer \ No newline at end of file From 08068826a249f6647d4c8571d5a280a4a85ef04c Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Sun, 21 Jul 2024 16:32:44 +0100 Subject: [PATCH 26/60] Simplified now navigation icon always needed Need it even if setting because might turn auto navigation back on without changing the navigation icon --- .../src/main/java/com/navigation/reactnative/ToolbarView.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarView.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarView.java index 004e76d54..5a11abfc7 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarView.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarView.java @@ -77,13 +77,11 @@ public ToolbarView(Context context) { setTintColor(getLogo()); }; navIconResolverListener = d -> { + navigationIcon = d; if (!autoNavigation) { - navigationIcon = d; setNavigationIcon(d); setTintColor(getNavigationIcon()); setTestID(); - } else { - navigationIcon = d; } }; overflowIconResolverListener = d -> { From 28233d101a0427c60ab1db4d4476cb0a07adb623 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Sun, 21 Jul 2024 16:40:38 +0100 Subject: [PATCH 27/60] Added newline --- .../src/android/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NavigationReactNative/src/android/src/main/res/values/strings.xml b/NavigationReactNative/src/android/src/main/res/values/strings.xml index 3d8d96fc0..4ec311592 100644 --- a/NavigationReactNative/src/android/src/main/res/values/strings.xml +++ b/NavigationReactNative/src/android/src/main/res/values/strings.xml @@ -2,4 +2,4 @@ Open drawer Close drawer - \ No newline at end of file + From a5413ab53ed9423665e7c2e99bd3b4d6747ae0bc Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Sun, 21 Jul 2024 16:56:06 +0100 Subject: [PATCH 28/60] Implemented auto navigation back on bottom bar Copied over changes from toolbar to bottombar. This is just for the home/back - will do drawer toggle too though --- NavigationReactNative/src/BottomAppBar.tsx | 16 +++++-- .../reactnative/BottomAppBarManager.java | 12 ++++- .../reactnative/BottomAppBarView.java | 48 ++++++++++++++----- 3 files changed, 61 insertions(+), 15 deletions(-) diff --git a/NavigationReactNative/src/BottomAppBar.tsx b/NavigationReactNative/src/BottomAppBar.tsx index 1fb51f685..f21e397ef 100644 --- a/NavigationReactNative/src/BottomAppBar.tsx +++ b/NavigationReactNative/src/BottomAppBar.tsx @@ -1,8 +1,12 @@ -import React, { cloneElement, ReactElement } from 'react'; +import React, { cloneElement, ReactElement, useContext } from 'react'; import { Platform, Image, requireNativeComponent, NativeModules } from 'react-native'; +import { NavigationContext } from 'navigation-react'; import SearchBar from './SearchBar'; -const BottomAppBar = ({ navigationImage, overflowImage, children, style, ...props }: any) => { +const BottomAppBar = ({ autoNavigation, navigationImage, overflowImage, onNavigationPress, children, style, ...props }: any) => { + const navigationEvent = useContext(NavigationContext); + var {stateNavigator} = navigationEvent; + var crumb = stateNavigator.stateContext.crumbs.length; if (Platform.OS === 'ios') return null; const Material3 = global.__turboModuleProxy != null ? require("./NativeMaterial3Module").default : NativeModules.Material3; const { on: material3 } = Platform.OS === 'android' ? Material3.getConstants() : { on: false }; @@ -12,11 +16,17 @@ const BottomAppBar = ({ navigationImage, overflowImage, children, style, ...prop return ( <> + {...props} + onNavigationPress={() => { + if (autoNavigation && crumb > 0) stateNavigator.navigateBack(1); + else onNavigationPress?.(); + }}> {childrenArray.filter(({type}) => type !== SearchBar)} {searchBar} diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/BottomAppBarManager.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/BottomAppBarManager.java index 78ea47c17..5f37e90ba 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/BottomAppBarManager.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/BottomAppBarManager.java @@ -28,6 +28,16 @@ protected BottomAppBarView createViewInstance(@NonNull ThemedReactContext reactC return new BottomAppBarView(reactContext); } + @ReactProp(name = "crumb") + public void setCrumb(BottomAppBarView view, int crumb) { + view.crumb = crumb; + } + + @ReactProp(name = "autoNavigation") + public void setAutoNavigation(BottomAppBarView view, boolean autoNavigation) { + view.autoNavigation = autoNavigation; + } + @ReactProp(name = "navigationImage") public void setNavIcon(BottomAppBarView view, ReadableMap navIcon) { view.setNavIconSource(navIcon); @@ -122,7 +132,7 @@ public View getChildAt(BottomAppBarView parent, int index) { @Override protected void onAfterUpdateTransaction(@NonNull BottomAppBarView view) { super.onAfterUpdateTransaction(view); - view.setFabAlignmentMode(view.fabAlignmentMode); + view.onAfterUpdateTransaction(); } @Override diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/BottomAppBarView.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/BottomAppBarView.java index f848db62f..ccdb57b4b 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/BottomAppBarView.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/BottomAppBarView.java @@ -15,6 +15,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.ActionMenuView; import androidx.appcompat.widget.AppCompatImageButton; import androidx.appcompat.widget.AppCompatImageView; @@ -36,9 +37,12 @@ public class BottomAppBarView extends BottomAppBar implements ActionView { private MenuItem searchMenuItem; + int crumb; + boolean autoNavigation; private Integer tintColor; final int defaultBackgroundColor; final Drawable defaultOverflowIcon; + private Drawable navigationIcon; int fabAlignmentMode; int defaultFabAlignmentMode; int defaultFabAnimationMode; @@ -71,9 +75,12 @@ public BottomAppBarView(@NonNull Context context) { navIconResolverListener = new IconResolver.IconResolverListener() { @Override public void setDrawable(Drawable d) { - setNavigationIcon(d); - setTintColor(getNavigationIcon()); - setTestID(); + navigationIcon = d; + if (!autoNavigation) { + setNavigationIcon(d); + setTintColor(getNavigationIcon()); + setTestID(); + } } }; overflowIconResolverListener = new IconResolver.IconResolverListener() { @@ -83,14 +90,7 @@ public void setDrawable(Drawable d) { setTintColor(getOverflowIcon()); } }; - setNavigationOnClickListener(new OnClickListener() { - @Override - public void onClick(View view) { - ReactContext reactContext = (ReactContext) (getContext() instanceof ReactContext ? getContext() : ((ContextWrapper) getContext()).getBaseContext()); - EventDispatcher eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, getId()); - eventDispatcher.dispatchEvent(new BottomAppBarView.NavigationPressEvent(getId())); - } - }); + setNavigationOnClickListener(this::onNavigationClick); setOnMenuItemClickListener(new OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { @@ -228,6 +228,32 @@ private void setTestID() { } } + void onAfterUpdateTransaction() { + AppCompatActivity activity = (AppCompatActivity) ((ReactContext) getContext()).getCurrentActivity(); + assert activity != null; + if (autoNavigation) { + if (crumb > 0) { + setNavigationIcon(null); + activity.setSupportActionBar(this); + assert activity.getSupportActionBar() != null; + activity.getSupportActionBar().setDisplayHomeAsUpEnabled(true); + activity.setSupportActionBar(null); + } + } else { + setNavigationIcon(navigationIcon); + } + setNavigationOnClickListener(this::onNavigationClick); + setTintColor(getNavigationIcon()); + setTestID(); + setFabAlignmentMode(fabAlignmentMode); + } + + private void onNavigationClick(View view) { + ReactContext reactContext = (ReactContext) (getContext() instanceof ReactContext ? getContext() : ((ContextWrapper) getContext()).getBaseContext()); + EventDispatcher eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, getId()); + eventDispatcher.dispatchEvent(new BottomAppBarView.NavigationPressEvent(getId())); + } + public void setOnSearchListener(OnSearchListener onSearchListener) { this.onSearchAddedListener = onSearchListener; if (searchMenuItem != null) From a754a909b31bb7007066e9df8a7d73d6b00a8cec Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Sun, 21 Jul 2024 17:06:24 +0100 Subject: [PATCH 29/60] Implemented auto navigation toggle on bottom bar Copied over drawer toggle from toolbar to bottombar --- .../reactnative/BottomAppBarView.java | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/BottomAppBarView.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/BottomAppBarView.java index ccdb57b4b..66f10950c 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/BottomAppBarView.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/BottomAppBarView.java @@ -10,11 +10,13 @@ import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; +import android.view.ViewParent; import android.widget.ImageButton; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.appcompat.app.ActionBarDrawerToggle; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.ActionMenuView; import androidx.appcompat.widget.AppCompatImageButton; @@ -35,7 +37,7 @@ import java.util.ArrayList; import java.util.HashMap; -public class BottomAppBarView extends BottomAppBar implements ActionView { +public class BottomAppBarView extends BottomAppBar implements ActionView, DrawerToggleHandler { private MenuItem searchMenuItem; int crumb; boolean autoNavigation; @@ -111,6 +113,7 @@ public boolean onMenuItemClick(MenuItem item) { @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); + if (autoNavigation) registerDrawerToggleHandler(); requestLayout(); } @@ -239,6 +242,7 @@ void onAfterUpdateTransaction() { activity.getSupportActionBar().setDisplayHomeAsUpEnabled(true); activity.setSupportActionBar(null); } + registerDrawerToggleHandler(); } else { setNavigationIcon(navigationIcon); } @@ -248,6 +252,16 @@ void onAfterUpdateTransaction() { setFabAlignmentMode(fabAlignmentMode); } + private void registerDrawerToggleHandler() { + ViewParent parent = this; + while(parent != null) { + parent = parent.getParent(); + if (parent instanceof SceneView sceneView) { + sceneView.registerDrawerToggleHandler(this); + parent = null; + } + } + } private void onNavigationClick(View view) { ReactContext reactContext = (ReactContext) (getContext() instanceof ReactContext ? getContext() : ((ContextWrapper) getContext()).getBaseContext()); EventDispatcher eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, getId()); @@ -295,6 +309,12 @@ public void run() { } }; + @Override + public void initDrawerToggle(ActionBarDrawerToggle drawerToggle) { + setTintColor(getNavigationIcon()); + setTestID(); + } + static class NavigationPressEvent extends Event { public NavigationPressEvent(int viewId) { super(viewId); From 426adcfb926055b18a655a60c9d1cbba7574f9fe Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Sun, 21 Jul 2024 17:11:11 +0100 Subject: [PATCH 30/60] Removed warnings following android studio advice Main one was using pattern matching for instanceof --- .../reactnative/BottomAppBarView.java | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/BottomAppBarView.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/BottomAppBarView.java index 66f10950c..a2aeafe0b 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/BottomAppBarView.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/BottomAppBarView.java @@ -22,7 +22,6 @@ import androidx.appcompat.widget.AppCompatImageButton; import androidx.appcompat.widget.AppCompatImageView; import androidx.coordinatorlayout.widget.CoordinatorLayout; -import androidx.core.view.ViewCompat; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.ReactContext; @@ -63,7 +62,7 @@ public class BottomAppBarView extends BottomAppBar implements ActionView, Drawer public BottomAppBarView(@NonNull Context context) { super(context, null); - ViewCompat.setLayoutDirection(this, !I18nUtil.getInstance().isRTL(context) ? ViewCompat.LAYOUT_DIRECTION_LTR : ViewCompat.LAYOUT_DIRECTION_RTL); + setLayoutDirection(!I18nUtil.getInstance().isRTL(context) ? LAYOUT_DIRECTION_LTR : LAYOUT_DIRECTION_RTL); defaultBackgroundColor = getBackgroundTint() != null ? getBackgroundTint().getDefaultColor() : Color.WHITE; defaultOverflowIcon = getOverflowIcon(); fabAlignmentMode = defaultFabAlignmentMode = getFabAlignmentMode(); @@ -97,8 +96,7 @@ public void setDrawable(Drawable d) { @Override public boolean onMenuItemClick(MenuItem item) { for (int i = 0; i < children.size(); i++) { - if (children.get(i) instanceof BarButtonView) { - BarButtonView barButtonView = (BarButtonView) children.get(i); + if (children.get(i) instanceof BarButtonView barButtonView) { if (barButtonView.getMenuItem() != item) barButtonView.getMenuItem().collapseActionView(); else @@ -158,8 +156,7 @@ void setMenuItems() { getMenu().clear(); HashMap testIDs = new HashMap<>(); for (int i = 0; i < children.size(); i++) { - if (children.get(i) instanceof BarButtonView) { - BarButtonView barButton = (BarButtonView) children.get(i); + if (children.get(i) instanceof BarButtonView barButton) { MenuItem menuItem = getMenu().add(Menu.NONE, barButton.getId(), i, ""); barButton.setMenuItem(menuItem); testIDs.put(barButton.getId(), barButton.testID); @@ -191,11 +188,9 @@ public boolean onMenuItemActionExpand(@NonNull MenuItem item) { private void setMenuTintColor(HashMap testIDs) { for (int i = 0; i < getChildCount(); i++) { - if (getChildAt(i) instanceof ActionMenuView) { - ActionMenuView menu = (ActionMenuView) getChildAt(i); + if (getChildAt(i) instanceof ActionMenuView menu) { for (int j = 0; j < menu.getChildCount(); j++) { - if (menu.getChildAt(j) instanceof TextView) { - TextView menuItemView = (TextView) menu.getChildAt(j); + if (menu.getChildAt(j) instanceof TextView menuItemView) { if (defaultMenuTintColor == null) defaultMenuTintColor = menuItemView.getCurrentTextColor(); menuItemView.setTextColor(tintColor != null ? tintColor : defaultMenuTintColor); @@ -219,11 +214,9 @@ private void setTestID() { if (child instanceof AppCompatImageButton) { child.setTag(navigationTestID); } - if (child instanceof ActionMenuView) { - ActionMenuView menu = (ActionMenuView) child; + if (child instanceof ActionMenuView menu) { for (int j = 0; j < menu.getChildCount(); j++) { - if (menu.getChildAt(j) instanceof AppCompatImageView) { - AppCompatImageView overflowButton = (AppCompatImageView) menu.getChildAt(j); + if (menu.getChildAt(j) instanceof AppCompatImageView overflowButton) { overflowButton.setTag(overflowTestID); } } From 38401e0b09b702af3f116f236e51a2cbe9f8e4f4 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Sun, 21 Jul 2024 21:34:42 +0100 Subject: [PATCH 31/60] Added new line for consistency --- .../main/java/com/navigation/reactnative/BottomAppBarView.java | 1 + 1 file changed, 1 insertion(+) diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/BottomAppBarView.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/BottomAppBarView.java index a2aeafe0b..bab5b4e94 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/BottomAppBarView.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/BottomAppBarView.java @@ -255,6 +255,7 @@ private void registerDrawerToggleHandler() { } } } + private void onNavigationClick(View view) { ReactContext reactContext = (ReactContext) (getContext() instanceof ReactContext ? getContext() : ((ContextWrapper) getContext()).getBaseContext()); EventDispatcher eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, getId()); From 904f6d7e2688e13343aa3e57e2f1674a342d62ef Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Fri, 26 Jul 2024 22:51:08 +0100 Subject: [PATCH 32/60] Implemented auto navigation back on bottom bar Copied over changes from toolbar/bottombar to searchbar. This is just for the home/back - will do drawer toggle too though. Had to do weird thing because couldn't get the back arrow to show - setting the navigationicon to null just didn't work with searchbar - when the button was added back it didn't have the right layout. It got added back in wrong place and took up the whole of the navigationbar. But if don't set it to null then setDisplayHomeAsUpEnabled won't set it to arrow. So overrode getNavigationIcon and returned null just while setting the homeAsUp - that way the ToolbarWidgetWrapper thinks it's null and sets it to arrow - and the Toolbar doesn't remove the view because never setNavigationIcon to null --- .../reactnative/SearchToolbarManager.java | 13 ++++- .../reactnative/SearchToolbarView.java | 52 ++++++++++++++++--- 2 files changed, 58 insertions(+), 7 deletions(-) diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SearchToolbarManager.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SearchToolbarManager.java index 68e34f493..607a48023 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SearchToolbarManager.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SearchToolbarManager.java @@ -21,6 +21,16 @@ public String getName() { return "NVSearchToolbar"; } + @ReactProp(name = "crumb") + public void setCrumb(SearchToolbarView view, int crumb) { + view.crumb = crumb; + } + + @ReactProp(name = "autoNavigation") + public void setAutoNavigation(SearchToolbarView view, boolean autoNavigation) { + view.autoNavigation = autoNavigation; + } + @ReactProp(name = "placeholder") public void setPlaceholder(SearchToolbarView view, String placeholder) { view.setHint(placeholder); @@ -76,6 +86,7 @@ public void setTintColor(SearchToolbarView view, int tintColor) { @ReactProp(name = "navigationDecorative") public void setNavigationDecorative(SearchToolbarView view, boolean navigationDecorative) { + view.navigationDecorative = navigationDecorative; if (!navigationDecorative) view.addNavigationListener(); else @@ -128,7 +139,7 @@ protected SearchToolbarView createViewInstance(@Nonnull ThemedReactContext react @Override protected void onAfterUpdateTransaction(@NonNull SearchToolbarView view) { super.onAfterUpdateTransaction(view); - view.stylePlaceholder(); + view.onAfterUpdateTransaction(); } @Override diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SearchToolbarView.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SearchToolbarView.java index 5aed74d6d..13ee8901d 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SearchToolbarView.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SearchToolbarView.java @@ -13,6 +13,7 @@ import android.widget.TextView; import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.ActionMenuView; import androidx.appcompat.widget.AppCompatImageButton; import androidx.appcompat.widget.AppCompatImageView; @@ -34,6 +35,9 @@ import java.util.HashMap; public class SearchToolbarView extends SearchBar { + int crumb; + boolean autoNavigation; + boolean settingHomeAsUp = false; final Drawable defaultBackground; private Integer tintColor; private Integer defaultMenuTintColor; @@ -47,7 +51,9 @@ public class SearchToolbarView extends SearchBar { private boolean placeholderFontChanged = false; private final Typeface defaultTypeface; private final float defaultFontSize; + boolean navigationDecorative; final Drawable defaultNavigationIcon; + private Drawable navigationIcon; private final IconResolver.IconResolverListener navIconResolverListener; private final IconResolver.IconResolverListener overflowIconResolverListener; final ArrayList children = new ArrayList<>(); @@ -63,12 +69,12 @@ public SearchToolbarView(Context context) { defaultOverflowIcon = getOverflowIcon(); defaultNavigationIcon = getNavigationIcon(); navIconResolverListener = d -> { - if (d != null) - setNavigationIcon(d); - else - setNavigationIcon(defaultNavigationIcon); - setTintColor(getNavigationIcon()); - setTestID(); + navigationIcon = d != null ? d : defaultNavigationIcon; + if (!autoNavigation) { + setNavigationIcon(navigationIcon); + setTintColor(getNavigationIcon()); + setTestID(); + } }; overflowIconResolverListener = d -> { setOverflowIcon(d); @@ -172,6 +178,40 @@ void setMenuItems() { requestLayout(); } + void onAfterUpdateTransaction() { + ReactContext reactContext = (ReactContext) ((ContextWrapper) getContext()).getBaseContext(); + AppCompatActivity activity = (AppCompatActivity) reactContext.getCurrentActivity(); + assert activity != null; + if (autoNavigation) { + if (crumb > 0) { + settingHomeAsUp = true; + activity.setSupportActionBar(this); + assert activity.getSupportActionBar() != null; + activity.getSupportActionBar().setDisplayHomeAsUpEnabled(true); + settingHomeAsUp = false; + activity.setSupportActionBar(null); + addNavigationListener(); + } else { + setNavigationIcon(defaultNavigationIcon); + } + } else { + setNavigationIcon(navigationIcon); + if (!navigationDecorative) + addNavigationListener(); + else + setNavigationOnClickListener(null); + } + setTintColor(getNavigationIcon()); + setTestID(); + stylePlaceholder(); + } + + @Nullable + @Override + public Drawable getNavigationIcon() { + return !settingHomeAsUp ? super.getNavigationIcon() : null; + } + void addNavigationListener() { setNavigationOnClickListener(view -> { ReactContext reactContext = (ReactContext) ((ContextWrapper) getContext()).getBaseContext(); From 6d40447923fa8729efb6fbb9b818966eca962a3f Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Fri, 26 Jul 2024 22:52:08 +0100 Subject: [PATCH 33/60] Stopped calling onNavigationPress if auto Think it's clearer not to call onNavigationPress (and not render navigationicon) if autoNavigation is on. Feels more consistent. If it sometimes called it might be a bit odd? --- NavigationReactNative/src/NavigationBar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NavigationReactNative/src/NavigationBar.tsx b/NavigationReactNative/src/NavigationBar.tsx index 5218f1e95..ee99828c3 100644 --- a/NavigationReactNative/src/NavigationBar.tsx +++ b/NavigationReactNative/src/NavigationBar.tsx @@ -94,7 +94,7 @@ class NavigationBar extends React.Component { navigationDecorative={!onNavigationPress} onNavigationPress={() => { if (autoNavigation && crumb > 0) stateNavigator.navigateBack(1); - else onNavigationPress(); + if (!autoNavigation) onNavigationPress(); }} style={{height: !material3 || searchToolbar ? 56 : 64, margin: searchToolbar ? 16 : undefined}}> {[ From 193eac9c042567b37334b869efb87bede2ddb069 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Sat, 27 Jul 2024 10:48:17 +0100 Subject: [PATCH 34/60] Allowed custom navigation icon with auto Improved the idea. Want a user to always turn on autoNavigation without having to think about whether it's first screen or not. Allowing a custom navigation icon is crucial for that as well as firing onNavigationPress (if it's not been taken over by auto navigation). So always going to set the navigation icon if it's provided. If it's not provided then going to use the default one from auto navigation (or from search bar if auto isn't on). --- NavigationReactNative/src/NavigationBar.tsx | 4 ++-- .../reactnative/SearchToolbarView.java | 23 +++++++++---------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/NavigationReactNative/src/NavigationBar.tsx b/NavigationReactNative/src/NavigationBar.tsx index ee99828c3..e3afa15d4 100644 --- a/NavigationReactNative/src/NavigationBar.tsx +++ b/NavigationReactNative/src/NavigationBar.tsx @@ -91,10 +91,10 @@ class NavigationBar extends React.Component { fontSize={searchBar?.props.fontSize} titleCentered={!!titleCentered} barHeight={!material3 || searchToolbar ? 56 : 64} - navigationDecorative={!onNavigationPress} + navigationDecorative={!onNavigationPress || autoNavigation} onNavigationPress={() => { if (autoNavigation && crumb > 0) stateNavigator.navigateBack(1); - if (!autoNavigation) onNavigationPress(); + else onNavigationPress(); }} style={{height: !material3 || searchToolbar ? 56 : 64, margin: searchToolbar ? 16 : undefined}}> {[ diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SearchToolbarView.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SearchToolbarView.java index 13ee8901d..0ca08cda3 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SearchToolbarView.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SearchToolbarView.java @@ -52,7 +52,8 @@ public class SearchToolbarView extends SearchBar { private final Typeface defaultTypeface; private final float defaultFontSize; boolean navigationDecorative; - final Drawable defaultNavigationIcon; + final Drawable searchDefaultNavigationIcon; + Drawable defaultNavigationIcon; private Drawable navigationIcon; private final IconResolver.IconResolverListener navIconResolverListener; private final IconResolver.IconResolverListener overflowIconResolverListener; @@ -67,14 +68,12 @@ public SearchToolbarView(Context context) { defaultTypeface = getTextView().getTypeface(); defaultFontSize = PixelUtil.toDIPFromPixel(getTextView().getTextSize()); defaultOverflowIcon = getOverflowIcon(); - defaultNavigationIcon = getNavigationIcon(); + defaultNavigationIcon = searchDefaultNavigationIcon = getNavigationIcon(); navIconResolverListener = d -> { - navigationIcon = d != null ? d : defaultNavigationIcon; - if (!autoNavigation) { - setNavigationIcon(navigationIcon); - setTintColor(getNavigationIcon()); - setTestID(); - } + navigationIcon = d; + setNavigationIcon(d != null ? d : defaultNavigationIcon); + setTintColor(getNavigationIcon()); + setTestID(); }; overflowIconResolverListener = d -> { setOverflowIcon(d); @@ -189,13 +188,13 @@ void onAfterUpdateTransaction() { assert activity.getSupportActionBar() != null; activity.getSupportActionBar().setDisplayHomeAsUpEnabled(true); settingHomeAsUp = false; + defaultNavigationIcon = getNavigationIcon(); activity.setSupportActionBar(null); addNavigationListener(); - } else { - setNavigationIcon(defaultNavigationIcon); } } else { - setNavigationIcon(navigationIcon); + defaultNavigationIcon = searchDefaultNavigationIcon; + setNavigationIcon(navigationIcon != null ? navigationIcon : defaultNavigationIcon); if (!navigationDecorative) addNavigationListener(); else @@ -209,7 +208,7 @@ void onAfterUpdateTransaction() { @Nullable @Override public Drawable getNavigationIcon() { - return !settingHomeAsUp ? super.getNavigationIcon() : null; + return (!settingHomeAsUp || super.getNavigationIcon() != defaultNavigationIcon) ? super.getNavigationIcon() : null; } void addNavigationListener() { From c8256e36e9d1ed7573e8e2f41290237334fd7bad Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Sat, 27 Jul 2024 19:47:07 +0100 Subject: [PATCH 35/60] Enabled automatic navigation by default If there's on onNavigationPress then automatically handle navigation. Don't need an autoNavigation prop at all. There could be multiple toolbars - toolbar and bottombar - and don't want them both having hamburgers if there's a drawer. So only allowed one auto drawer handler in scene view. But same could happen for back arrow - in that case can add an onNavigationPress handler to the bottom bar and it won't have the back arrow. The one case think might need special prop is custom decorative nav icon on search bar on second scene and don't want back arrow (same for drawer). Can't add onNavigationPress because then not decorative anymore. Think this is super rare so can add prop if really needed later. Turning on by default is important because then users don't have to know about hamburger animation - they'll just get it if drawer and toolbar on same scene --- NavigationReactNative/src/NavigationBar.tsx | 10 +++++----- .../java/com/navigation/reactnative/SceneView.java | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/NavigationReactNative/src/NavigationBar.tsx b/NavigationReactNative/src/NavigationBar.tsx index e3afa15d4..a7add154e 100644 --- a/NavigationReactNative/src/NavigationBar.tsx +++ b/NavigationReactNative/src/NavigationBar.tsx @@ -32,7 +32,7 @@ class NavigationBar extends React.Component { } } render() { - var {navigationEvent, bottomBar, hidden, logo, autoNavigation, navigationImage, overflowImage, backTitle, backImage, titleCentered, shadowColor, children, onNavigationPress, style = {height: undefined}, ...otherProps} = this.props; + var {navigationEvent, bottomBar, hidden, logo, navigationImage, overflowImage, backTitle, backImage, titleCentered, shadowColor, children, onNavigationPress, style = {height: undefined}, ...otherProps} = this.props; const Material3 = global.__turboModuleProxy != null ? require("./NativeMaterial3Module").default : NativeModules.Material3; const { on: material3 } = Platform.OS === 'android' ? Material3.getConstants() : { on: false }; var scrollEdgeProps = this.getScrollEdgeProps() @@ -76,7 +76,7 @@ class NavigationBar extends React.Component { {collapsingBar && collapsingBar.props.children} { fontSize={searchBar?.props.fontSize} titleCentered={!!titleCentered} barHeight={!material3 || searchToolbar ? 56 : 64} - navigationDecorative={!onNavigationPress || autoNavigation} + navigationDecorative={!onNavigationPress} onNavigationPress={() => { - if (autoNavigation && crumb > 0) stateNavigator.navigateBack(1); - else onNavigationPress(); + if (onNavigationPress) onNavigationPress(); + else if (crumb > 0) stateNavigator.navigateBack(1); }} style={{height: !material3 || searchToolbar ? 56 : 64, margin: searchToolbar ? 16 : undefined}}> {[ diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SceneView.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SceneView.java index ee528b5a6..6c81d53d1 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SceneView.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SceneView.java @@ -53,9 +53,9 @@ protected void onAttachedToWindow() { } protected void registerDrawerToggleHandler(DrawerToggleHandler drawerToggleHandler) { - if (drawerToggleHandler instanceof Toolbar toolbarView) + if (drawerToggleHandler instanceof Toolbar toolbarView && toolbar == null) this.toolbar = new WeakReference<>(toolbarView); - if (drawerToggleHandler instanceof DrawerLayoutView drawerView) + if (drawerToggleHandler instanceof DrawerLayoutView drawerView && drawer == null) this.drawer = new WeakReference<>(drawerView); initDrawerToggle(); } From 27d6c1f94a4025c7bddd1cfd61a3fdf4189ba4d2 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Sat, 27 Jul 2024 20:01:30 +0100 Subject: [PATCH 36/60] Removed redundant navigationDecorative It's the same as autoNavigation - true if there's no onNavigationPress and false otherwise - so don't need it anymore --- NavigationReactNative/src/NavigationBar.tsx | 1 - .../reactnative/SearchToolbarManager.java | 9 --------- .../reactnative/SearchToolbarView.java | 20 +++++++------------ 3 files changed, 7 insertions(+), 23 deletions(-) diff --git a/NavigationReactNative/src/NavigationBar.tsx b/NavigationReactNative/src/NavigationBar.tsx index a7add154e..3a22034a9 100644 --- a/NavigationReactNative/src/NavigationBar.tsx +++ b/NavigationReactNative/src/NavigationBar.tsx @@ -91,7 +91,6 @@ class NavigationBar extends React.Component { fontSize={searchBar?.props.fontSize} titleCentered={!!titleCentered} barHeight={!material3 || searchToolbar ? 56 : 64} - navigationDecorative={!onNavigationPress} onNavigationPress={() => { if (onNavigationPress) onNavigationPress(); else if (crumb > 0) stateNavigator.navigateBack(1); diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SearchToolbarManager.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SearchToolbarManager.java index 607a48023..c42f9269b 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SearchToolbarManager.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SearchToolbarManager.java @@ -84,15 +84,6 @@ public void setTintColor(SearchToolbarView view, int tintColor) { view.setTintColor(tintColor != Integer.MAX_VALUE ? tintColor : null); } - @ReactProp(name = "navigationDecorative") - public void setNavigationDecorative(SearchToolbarView view, boolean navigationDecorative) { - view.navigationDecorative = navigationDecorative; - if (!navigationDecorative) - view.addNavigationListener(); - else - view.setNavigationOnClickListener(null); - } - @ReactProp(name = "navigationTestID") public void setNavigationTestID(SearchToolbarView view, String navigationTestID) { view.setNavigationTestID(navigationTestID); diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SearchToolbarView.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SearchToolbarView.java index 0ca08cda3..aadd06c4f 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SearchToolbarView.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SearchToolbarView.java @@ -51,7 +51,6 @@ public class SearchToolbarView extends SearchBar { private boolean placeholderFontChanged = false; private final Typeface defaultTypeface; private final float defaultFontSize; - boolean navigationDecorative; final Drawable searchDefaultNavigationIcon; Drawable defaultNavigationIcon; private Drawable navigationIcon; @@ -191,14 +190,13 @@ void onAfterUpdateTransaction() { defaultNavigationIcon = getNavigationIcon(); activity.setSupportActionBar(null); addNavigationListener(); + } else { + setNavigationOnClickListener(null); } } else { defaultNavigationIcon = searchDefaultNavigationIcon; setNavigationIcon(navigationIcon != null ? navigationIcon : defaultNavigationIcon); - if (!navigationDecorative) - addNavigationListener(); - else - setNavigationOnClickListener(null); + addNavigationListener(); } setTintColor(getNavigationIcon()); setTestID(); @@ -231,11 +229,9 @@ void setOverflowTestID(String overflowTestID) { private void setMenuTintColor(HashMap testIDs) { for (int i = 0; i < getChildCount(); i++) { - if (getChildAt(i) instanceof ActionMenuView) { - ActionMenuView menu = (ActionMenuView) getChildAt(i); + if (getChildAt(i) instanceof ActionMenuView menu) { for (int j = 0; j < menu.getChildCount(); j++) { - if (menu.getChildAt(j) instanceof TextView) { - TextView menuItemView = (TextView) menu.getChildAt(j); + if (menu.getChildAt(j) instanceof TextView menuItemView) { if (defaultMenuTintColor == null) defaultMenuTintColor = menuItemView.getCurrentTextColor(); menuItemView.setTextColor(tintColor != null ? tintColor : defaultMenuTintColor); @@ -257,11 +253,9 @@ private void setTestID() { if (child instanceof AppCompatImageButton) { child.setTag(navigationTestID); } - if (child instanceof ActionMenuView) { - ActionMenuView menu = (ActionMenuView) child; + if (child instanceof ActionMenuView menu) { for (int j = 0; j < menu.getChildCount(); j++) { - if (menu.getChildAt(j) instanceof AppCompatImageView) { - AppCompatImageView overflowButton = (AppCompatImageView) menu.getChildAt(j); + if (menu.getChildAt(j) instanceof AppCompatImageView overflowButton) { overflowButton.setTag(overflowTestID); } } From 19e086ff3feaa2c34543ecbe25a9124b1992e14f Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Sat, 27 Jul 2024 21:17:56 +0100 Subject: [PATCH 37/60] Copied auto nav default changes to bottom app bar Haven't tested them yet but needs to match the navigation bar --- NavigationReactNative/src/BottomAppBar.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/NavigationReactNative/src/BottomAppBar.tsx b/NavigationReactNative/src/BottomAppBar.tsx index f21e397ef..165d0b111 100644 --- a/NavigationReactNative/src/BottomAppBar.tsx +++ b/NavigationReactNative/src/BottomAppBar.tsx @@ -3,7 +3,7 @@ import { Platform, Image, requireNativeComponent, NativeModules } from 'react-na import { NavigationContext } from 'navigation-react'; import SearchBar from './SearchBar'; -const BottomAppBar = ({ autoNavigation, navigationImage, overflowImage, onNavigationPress, children, style, ...props }: any) => { +const BottomAppBar = ({ navigationImage, overflowImage, onNavigationPress, children, style, ...props }: any) => { const navigationEvent = useContext(NavigationContext); var {stateNavigator} = navigationEvent; var crumb = stateNavigator.stateContext.crumbs.length; @@ -17,15 +17,15 @@ const BottomAppBar = ({ autoNavigation, navigationImage, overflowImage, onNaviga <> { - if (autoNavigation && crumb > 0) stateNavigator.navigateBack(1); - else onNavigationPress?.(); + if (onNavigationPress) onNavigationPress(); + else if (crumb > 0) stateNavigator.navigateBack(1); }}> {childrenArray.filter(({type}) => type !== SearchBar)} From 564a64402a402e9cf7977e78391370badfef096a Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Sun, 28 Jul 2024 14:56:07 +0100 Subject: [PATCH 38/60] Removed redundant code Not relevant now always setting the navigation icon. Also this code wouldn't work when doing the drawer toggle because that happens later so don't know at this stage what to do with the listener --- .../main/java/com/navigation/reactnative/SearchToolbarView.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SearchToolbarView.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SearchToolbarView.java index aadd06c4f..6ce26442f 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SearchToolbarView.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SearchToolbarView.java @@ -190,8 +190,6 @@ void onAfterUpdateTransaction() { defaultNavigationIcon = getNavigationIcon(); activity.setSupportActionBar(null); addNavigationListener(); - } else { - setNavigationOnClickListener(null); } } else { defaultNavigationIcon = searchDefaultNavigationIcon; From b85c63a1e744429cd453763194544b8ef3133c77 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Sun, 28 Jul 2024 17:33:07 +0100 Subject: [PATCH 39/60] Implemented auto navigation toggle on search bar Copied over drawer toggle from bottombar to searchbar. Had to copy the DrawerArrowDrawable setup from material SearchView because there's no public property to set to add it - the useDrawerArrowDrawable is how to set it but that can only be set from resources styles. Was gonna try using resource but the code is only a few lines long so copied it in instead. If don't add the drawerarrordrawable to the searchresults toolbar then android falls over because it goes down the FadeThroughDrawable path and DrawerArrowDrawable (on the searchbar) doesn't have constant state?! --- .../reactnative/SearchResultsView.java | 10 ++--- .../reactnative/SearchToolbarView.java | 39 ++++++++++++++++++- 2 files changed, 43 insertions(+), 6 deletions(-) diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SearchResultsView.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SearchResultsView.java index 98b8cf273..9efa710bf 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SearchResultsView.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SearchResultsView.java @@ -9,7 +9,7 @@ import android.view.ViewGroup; import android.view.inputmethod.InputMethodManager; -import androidx.core.view.ViewCompat; +import androidx.appcompat.graphics.drawable.DrawerArrowDrawable; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.ReactContext; @@ -42,7 +42,7 @@ public class SearchResultsView extends SearchView { public SearchResultsView(Context context) { super(context); - ViewCompat.setLayoutDirection(this, !I18nUtil.getInstance().isRTL(context) ? ViewCompat.LAYOUT_DIRECTION_LTR : ViewCompat.LAYOUT_DIRECTION_RTL); + setLayoutDirection(!I18nUtil.getInstance().isRTL(context) ? LAYOUT_DIRECTION_LTR : LAYOUT_DIRECTION_RTL); defaultBackground = getToolbar().getBackground(); defaultTypeface = getEditText().getTypeface(); defaultFontSize = PixelUtil.toDIPFromPixel(getEditText().getTextSize()); @@ -130,10 +130,10 @@ protected void onAttachedToWindow() { if (isShowing()) post(focusAndKeyboard); ViewGroup view = (ViewGroup) getParent(); for(int i = 0; i < view.getChildCount(); i++) { - if (view.getChildAt(i) instanceof NavigationBarView) { - SearchToolbarView searchToolbarView = (SearchToolbarView) ((NavigationBarView) view.getChildAt(i)).getChildAt(0); + if (view.getChildAt(i) instanceof NavigationBarView navigationBarView) { + SearchToolbarView searchToolbarView = (SearchToolbarView) navigationBarView.getChildAt(0); setupWithSearchBar(searchToolbarView); - setAnimatedNavigationIcon(searchToolbarView.getNavigationIcon() == searchToolbarView.defaultNavigationIcon); + setAnimatedNavigationIcon(searchToolbarView.getNavigationIcon() == searchToolbarView.defaultNavigationIcon || searchToolbarView.getNavigationIcon() instanceof DrawerArrowDrawable); } } } diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SearchToolbarView.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SearchToolbarView.java index 6ce26442f..cece66e27 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SearchToolbarView.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SearchToolbarView.java @@ -10,10 +10,14 @@ import android.view.Menu; import android.view.MenuItem; import android.view.View; +import android.view.ViewGroup; +import android.view.ViewParent; import android.widget.TextView; import androidx.annotation.Nullable; +import androidx.appcompat.app.ActionBarDrawerToggle; import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.graphics.drawable.DrawerArrowDrawable; import androidx.appcompat.widget.ActionMenuView; import androidx.appcompat.widget.AppCompatImageButton; import androidx.appcompat.widget.AppCompatImageView; @@ -28,13 +32,15 @@ import com.facebook.react.uimanager.events.RCTEventEmitter; import com.facebook.react.views.text.ReactTypefaceUtils; import com.google.android.material.appbar.AppBarLayout; +import com.google.android.material.color.MaterialColors; import com.google.android.material.search.SearchBar; import com.google.android.material.shape.MaterialShapeDrawable; +import com.google.android.material.R; import java.util.ArrayList; import java.util.HashMap; -public class SearchToolbarView extends SearchBar { +public class SearchToolbarView extends SearchBar implements DrawerToggleHandler { int crumb; boolean autoNavigation; boolean settingHomeAsUp = false; @@ -121,6 +127,7 @@ void stylePlaceholder() { @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); + if (autoNavigation && crumb == 0) registerDrawerToggleHandler(); getParent().requestLayout(); } @@ -190,6 +197,8 @@ void onAfterUpdateTransaction() { defaultNavigationIcon = getNavigationIcon(); activity.setSupportActionBar(null); addNavigationListener(); + } else { + registerDrawerToggleHandler(); } } else { defaultNavigationIcon = searchDefaultNavigationIcon; @@ -207,6 +216,16 @@ public Drawable getNavigationIcon() { return (!settingHomeAsUp || super.getNavigationIcon() != defaultNavigationIcon) ? super.getNavigationIcon() : null; } + private void registerDrawerToggleHandler() { + ViewParent parent = this; + while(parent != null) { + parent = parent.getParent(); + if (parent instanceof SceneView sceneView) { + sceneView.registerDrawerToggleHandler(this); + parent = null; + } + } + } void addNavigationListener() { setNavigationOnClickListener(view -> { ReactContext reactContext = (ReactContext) ((ContextWrapper) getContext()).getBaseContext(); @@ -261,6 +280,24 @@ private void setTestID() { } } + @Override + public void initDrawerToggle(ActionBarDrawerToggle drawerToggle) { + setTintColor(getNavigationIcon()); + setTestID(); + ViewGroup view = getParent() != null ? (ViewGroup) getParent().getParent() : null; + if (view != null) { + for(int i = 0; i < view.getChildCount(); i++) { + if (view.getChildAt(i) instanceof SearchResultsView searchResultsView) { + DrawerArrowDrawable drawerArrowDrawable = new DrawerArrowDrawable(getContext()); + drawerArrowDrawable.setColor(MaterialColors.getColor(this, R.attr.colorOnSurface)); + searchResultsView.getToolbar().setNavigationIcon(drawerArrowDrawable); + searchResultsView.setAnimatedNavigationIcon(true); + searchResultsView.getToolbar().setNavigationOnClickListener(v -> searchResultsView.hide()); + } + } + } + } + static class NavigationPressEvent extends Event { public NavigationPressEvent(int viewId) { super(viewId); From de0755fc42adf2cd91390e6467133e0f2a065cfb Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Sun, 28 Jul 2024 17:41:00 +0100 Subject: [PATCH 40/60] Removed drawer string values Used navigationAccessibilityLabel prop instead - obviously better to use prop so user can control text. Tested can dynamically change prop when drawer open status changes --- .../src/main/java/com/navigation/reactnative/SceneView.java | 2 +- .../src/android/src/main/res/values/strings.xml | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SceneView.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SceneView.java index 6c81d53d1..dd0f8397f 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SceneView.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SceneView.java @@ -66,7 +66,7 @@ private void initDrawerToggle() { DrawerLayoutView drawerView = drawer.get(); if (toolbarView != null && drawerView != null) { Activity activity = ((ReactContext) getContext()).getCurrentActivity(); - ActionBarDrawerToggle drawerToggle = new ActionBarDrawerToggle(activity, drawerView, toolbarView, R.string.drawer_open, R.string.drawer_close); + ActionBarDrawerToggle drawerToggle = new ActionBarDrawerToggle(activity, drawerView, toolbarView, 0, 0); drawerToggle.setDrawerIndicatorEnabled(true); drawerToggle.syncState(); ((DrawerToggleHandler) toolbarView).initDrawerToggle(drawerToggle); diff --git a/NavigationReactNative/src/android/src/main/res/values/strings.xml b/NavigationReactNative/src/android/src/main/res/values/strings.xml index 4ec311592..045e125f3 100644 --- a/NavigationReactNative/src/android/src/main/res/values/strings.xml +++ b/NavigationReactNative/src/android/src/main/res/values/strings.xml @@ -1,5 +1,3 @@ - Open drawer - Close drawer From 7f5e4d6905f10160b10ac1b9fc24d869c4ea45a9 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Sun, 28 Jul 2024 18:57:44 +0100 Subject: [PATCH 41/60] Removed android studio warnings --- .../com/navigation/reactnative/SearchToolbarView.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SearchToolbarView.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SearchToolbarView.java index cece66e27..7cb486680 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SearchToolbarView.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SearchToolbarView.java @@ -3,7 +3,8 @@ import android.content.Context; import android.content.ContextWrapper; import android.content.res.ColorStateList; -import android.graphics.PorterDuff; +import android.graphics.BlendMode; +import android.graphics.BlendModeColorFilter; import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.graphics.drawable.RippleDrawable; @@ -31,11 +32,11 @@ import com.facebook.react.uimanager.events.EventDispatcher; import com.facebook.react.uimanager.events.RCTEventEmitter; import com.facebook.react.views.text.ReactTypefaceUtils; +import com.google.android.material.R; import com.google.android.material.appbar.AppBarLayout; import com.google.android.material.color.MaterialColors; import com.google.android.material.search.SearchBar; import com.google.android.material.shape.MaterialShapeDrawable; -import com.google.android.material.R; import java.util.ArrayList; import java.util.HashMap; @@ -152,7 +153,7 @@ void setTintColor(Integer tintColor) { private void setTintColor(Drawable icon) { if (icon != null) { if (tintColor != null) - icon.setColorFilter(tintColor, PorterDuff.Mode.SRC_IN); + icon.setColorFilter(new BlendModeColorFilter(tintColor, BlendMode.SRC_IN)); else icon.clearColorFilter(); } @@ -162,7 +163,7 @@ void setBarTintColor(int barTintColor) { Drawable drawable = defaultBackground; if (defaultBackground instanceof RippleDrawable) drawable = ((RippleDrawable) defaultBackground).getDrawable(0); - if (drawable instanceof MaterialShapeDrawable) { + if (drawable instanceof MaterialShapeDrawable && drawable.getConstantState() != null) { drawable = drawable.getConstantState().newDrawable(); ((MaterialShapeDrawable) drawable).setFillColor(ColorStateList.valueOf(barTintColor)); setBackground(drawable); From 25b3272219491a578dd0861501ea5ab5c15c0b47 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Sun, 28 Jul 2024 20:34:54 +0100 Subject: [PATCH 42/60] Copied auto nav back from searchbar to toolbar 1. Always set the navigation icon. Can have auto back navigation with custom navigation icon. 2. Only set drawer if the crumb is 0. If drawer on second scene then only back arrow shows --- .../com/navigation/reactnative/ToolbarView.java | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarView.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarView.java index 5a11abfc7..6f78417a0 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarView.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarView.java @@ -78,11 +78,9 @@ public ToolbarView(Context context) { }; navIconResolverListener = d -> { navigationIcon = d; - if (!autoNavigation) { - setNavigationIcon(d); - setTintColor(getNavigationIcon()); - setTestID(); - } + setNavigationIcon(d); + setTintColor(getNavigationIcon()); + setTestID(); }; overflowIconResolverListener = d -> { setOverflowIcon(d); @@ -270,17 +268,18 @@ void onAfterUpdateTransaction() { assert activity != null; if (autoNavigation) { if (crumb > 0) { - setNavigationIcon(null); activity.setSupportActionBar(this); assert activity.getSupportActionBar() != null; activity.getSupportActionBar().setDisplayHomeAsUpEnabled(true); activity.setSupportActionBar(null); + setNavigationOnClickListener(this::onNavigationClick); + } else { + registerDrawerToggleHandler(); } - registerDrawerToggleHandler(); } else { setNavigationIcon(navigationIcon); + setNavigationOnClickListener(this::onNavigationClick); } - setNavigationOnClickListener(this::onNavigationClick); setTintColor(getNavigationIcon()); setTestID(); if (titleChanged) { @@ -308,7 +307,7 @@ void onAfterUpdateTransaction() { @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); - if (autoNavigation) registerDrawerToggleHandler(); + if (autoNavigation && crumb == 0) registerDrawerToggleHandler(); } private void registerDrawerToggleHandler() { From 3b2df5cc8c60516fb495274d80e40e3ca3f456f8 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Sun, 28 Jul 2024 20:44:07 +0100 Subject: [PATCH 43/60] Defaulted tint color Otherwise the arrow doesn't show up by default. Starts off either white or transparent and can't see it --- .../src/main/java/com/navigation/reactnative/ToolbarView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarView.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarView.java index 6f78417a0..736984e3c 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarView.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarView.java @@ -70,7 +70,7 @@ public class ToolbarView extends MaterialToolbar implements ActionView, DrawerTo public ToolbarView(Context context) { super(context); setLayoutParams(new AppBarLayout.LayoutParams(AppBarLayout.LayoutParams.MATCH_PARENT, AppBarLayout.LayoutParams.MATCH_PARENT)); - defaultTitleTextColor = getDefaultTitleTextColor(); + defaultTitleTextColor = tintColor = getDefaultTitleTextColor(); defaultOverflowIcon = getOverflowIcon(); logoResolverListener = d -> { setLogo(d); From 632fd2e1ded2930fddb2db13f02953c45a96b0f2 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Sun, 28 Jul 2024 20:45:06 +0100 Subject: [PATCH 44/60] Removed redundant color setting Tried this color as the default tint color for the navigation bar and it was purple. Clearly wasn't being used because the hamburger was black. So removed for clarity --- .../main/java/com/navigation/reactnative/SearchToolbarView.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SearchToolbarView.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SearchToolbarView.java index 7cb486680..00d16cb14 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SearchToolbarView.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SearchToolbarView.java @@ -32,7 +32,6 @@ import com.facebook.react.uimanager.events.EventDispatcher; import com.facebook.react.uimanager.events.RCTEventEmitter; import com.facebook.react.views.text.ReactTypefaceUtils; -import com.google.android.material.R; import com.google.android.material.appbar.AppBarLayout; import com.google.android.material.color.MaterialColors; import com.google.android.material.search.SearchBar; @@ -290,7 +289,6 @@ public void initDrawerToggle(ActionBarDrawerToggle drawerToggle) { for(int i = 0; i < view.getChildCount(); i++) { if (view.getChildAt(i) instanceof SearchResultsView searchResultsView) { DrawerArrowDrawable drawerArrowDrawable = new DrawerArrowDrawable(getContext()); - drawerArrowDrawable.setColor(MaterialColors.getColor(this, R.attr.colorOnSurface)); searchResultsView.getToolbar().setNavigationIcon(drawerArrowDrawable); searchResultsView.setAnimatedNavigationIcon(true); searchResultsView.getToolbar().setNavigationOnClickListener(v -> searchResultsView.hide()); From 880f0c960f6a9900f98fb044d897d7d304315437 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Sun, 28 Jul 2024 20:52:01 +0100 Subject: [PATCH 45/60] Copied auto nav back from toolbar to bottombar 1. Always set the navigation icon. Can have auto back navigation with custom navigation icon. 2. Only set drawer if the crumb is 0. If drawer on second scene then only back arrow shows --- .../reactnative/BottomAppBarView.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/BottomAppBarView.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/BottomAppBarView.java index bab5b4e94..85711a5a1 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/BottomAppBarView.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/BottomAppBarView.java @@ -77,11 +77,9 @@ public BottomAppBarView(@NonNull Context context) { @Override public void setDrawable(Drawable d) { navigationIcon = d; - if (!autoNavigation) { - setNavigationIcon(d); - setTintColor(getNavigationIcon()); - setTestID(); - } + setNavigationIcon(d); + setTintColor(getNavigationIcon()); + setTestID(); } }; overflowIconResolverListener = new IconResolver.IconResolverListener() { @@ -111,7 +109,7 @@ public boolean onMenuItemClick(MenuItem item) { @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); - if (autoNavigation) registerDrawerToggleHandler(); + if (autoNavigation && crumb == 0) registerDrawerToggleHandler(); requestLayout(); } @@ -225,21 +223,23 @@ private void setTestID() { } void onAfterUpdateTransaction() { - AppCompatActivity activity = (AppCompatActivity) ((ReactContext) getContext()).getCurrentActivity(); + ReactContext reactContext = (ReactContext) (getContext() instanceof ReactContext ? getContext() : ((ContextWrapper) getContext()).getBaseContext()); + AppCompatActivity activity = (AppCompatActivity) reactContext.getCurrentActivity(); assert activity != null; if (autoNavigation) { if (crumb > 0) { - setNavigationIcon(null); activity.setSupportActionBar(this); assert activity.getSupportActionBar() != null; activity.getSupportActionBar().setDisplayHomeAsUpEnabled(true); activity.setSupportActionBar(null); + setNavigationOnClickListener(this::onNavigationClick); + } else { + registerDrawerToggleHandler(); } - registerDrawerToggleHandler(); } else { setNavigationIcon(navigationIcon); + setNavigationOnClickListener(this::onNavigationClick); } - setNavigationOnClickListener(this::onNavigationClick); setTintColor(getNavigationIcon()); setTestID(); setFabAlignmentMode(fabAlignmentMode); From 502dcdc615d4251409fe96ae8a9ea6baff484573 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Sun, 28 Jul 2024 20:52:24 +0100 Subject: [PATCH 46/60] Applied accessibility label to bottom bar nav --- .../java/com/navigation/reactnative/BottomAppBarManager.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/BottomAppBarManager.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/BottomAppBarManager.java index 5f37e90ba..27159e05e 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/BottomAppBarManager.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/BottomAppBarManager.java @@ -43,6 +43,11 @@ public void setNavIcon(BottomAppBarView view, ReadableMap navIcon) { view.setNavIconSource(navIcon); } + @ReactProp(name = "navigationAccessibilityLabel") + public void setNavigationContentDescription(BottomAppBarView view, String navigationContentDescription) { + view.setNavigationContentDescription(navigationContentDescription); + } + @ReactProp(name = "overflowImage") public void setOverflowIcon(BottomAppBarView view, ReadableMap overflowIcon) { view.setOverflowIconSource(overflowIcon); From 48f06139ef1976b4680f86964f99f3d4720028d3 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Sun, 28 Jul 2024 21:04:28 +0100 Subject: [PATCH 47/60] Reverted because need to think more The bottom bar has a default tint - maybe it's a theme thing. If gonna set one need to make sure it's restored too if the tintColor prop is removed --- .../src/main/java/com/navigation/reactnative/ToolbarView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarView.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarView.java index 736984e3c..6f78417a0 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarView.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarView.java @@ -70,7 +70,7 @@ public class ToolbarView extends MaterialToolbar implements ActionView, DrawerTo public ToolbarView(Context context) { super(context); setLayoutParams(new AppBarLayout.LayoutParams(AppBarLayout.LayoutParams.MATCH_PARENT, AppBarLayout.LayoutParams.MATCH_PARENT)); - defaultTitleTextColor = tintColor = getDefaultTitleTextColor(); + defaultTitleTextColor = getDefaultTitleTextColor(); defaultOverflowIcon = getOverflowIcon(); logoResolverListener = d -> { setLogo(d); From 91c3d83b7609f4ca32ebd8fa9c300fae15b66f3e Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Mon, 29 Jul 2024 16:39:10 +0100 Subject: [PATCH 48/60] Ensured navigation image tinted by default The default navigation icon tint is null - at least on the DayNight.NoActionBar material3 theme. This is confusing for new users because it looks like the image isn't there. So defaulted to black if null. It's not null on bottombar and searchbar so leaving them. Also switched to BlendModeColorFilter to avoid deprecation warning --- .../java/com/navigation/reactnative/ToolbarView.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarView.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarView.java index 6f78417a0..cfdf49cc7 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarView.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarView.java @@ -1,5 +1,7 @@ package com.navigation.reactnative; +import android.graphics.BlendMode; +import android.graphics.BlendModeColorFilter; import android.os.Build; import android.content.Context; import android.content.res.Resources; @@ -41,6 +43,7 @@ import java.util.ArrayList; import java.util.HashMap; +import java.util.Objects; public class ToolbarView extends MaterialToolbar implements ActionView, DrawerToggleHandler { private MenuItem searchMenuItem; @@ -168,12 +171,8 @@ void setTintColor(Integer tintColor) { } private void setTintColor(Drawable icon) { - if (icon != null) { - if (tintColor != null) - icon.setColorFilter(tintColor, PorterDuff.Mode.SRC_IN); - else - icon.clearColorFilter(); - } + if (icon != null) + icon.setColorFilter(new BlendModeColorFilter(Objects.requireNonNullElseGet(tintColor, () -> getNavigationIconTint() != null ? getNavigationIconTint() : Color.BLACK), BlendMode.SRC_IN)); } void setNavigationTestID(String navigationTestID) { From 0b77993e84a11be9a58eedc4492cd7a78d659731 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Mon, 29 Jul 2024 16:39:44 +0100 Subject: [PATCH 49/60] Opitimized imports --- .../java/com/navigation/reactnative/ToolbarView.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarView.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarView.java index cfdf49cc7..61e2839ea 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarView.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarView.java @@ -1,14 +1,13 @@ package com.navigation.reactnative; -import android.graphics.BlendMode; -import android.graphics.BlendModeColorFilter; -import android.os.Build; import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; +import android.graphics.BlendMode; +import android.graphics.BlendModeColorFilter; import android.graphics.Color; -import android.graphics.PorterDuff; import android.graphics.drawable.Drawable; +import android.os.Build; import android.text.SpannableString; import android.text.style.AbsoluteSizeSpan; import android.text.style.StyleSpan; @@ -35,11 +34,10 @@ import com.facebook.react.uimanager.events.Event; import com.facebook.react.uimanager.events.EventDispatcher; import com.facebook.react.uimanager.events.RCTEventEmitter; +import com.facebook.react.views.text.ReactFontManager; import com.facebook.react.views.text.ReactTypefaceUtils; import com.google.android.material.appbar.AppBarLayout; import com.google.android.material.appbar.MaterialToolbar; -import com.facebook.react.views.text.ReactFontManager; - import java.util.ArrayList; import java.util.HashMap; From ae8277c04266122c00d2668945eec1f65225b1c2 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Mon, 29 Jul 2024 16:41:34 +0100 Subject: [PATCH 50/60] Used pattern variable to remove android warnings Automatically refactored by android studio --- .../com/navigation/reactnative/ToolbarView.java | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarView.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarView.java index 61e2839ea..211751c63 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarView.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarView.java @@ -187,8 +187,7 @@ void setMenuItems() { getMenu().clear(); HashMap testIDs = new HashMap<>(); for (int i = 0; i < children.size(); i++) { - if (children.get(i) instanceof BarButtonView) { - BarButtonView barButton = (BarButtonView) children.get(i); + if (children.get(i) instanceof BarButtonView barButton) { MenuItem menuItem = getMenu().add(Menu.NONE, barButton.getId(), i, ""); barButton.setMenuItem(menuItem); testIDs.put(barButton.getId(), barButton.testID); @@ -220,11 +219,9 @@ public boolean onMenuItemActionExpand(@NonNull MenuItem item) { private void setMenuTintColor(HashMap testIDs) { for (int i = 0; i < getChildCount(); i++) { - if (getChildAt(i) instanceof ActionMenuView) { - ActionMenuView menu = (ActionMenuView) getChildAt(i); + if (getChildAt(i) instanceof ActionMenuView menu) { for (int j = 0; j < menu.getChildCount(); j++) { - if (menu.getChildAt(j) instanceof TextView) { - TextView menuItemView = (TextView) menu.getChildAt(j); + if (menu.getChildAt(j) instanceof TextView menuItemView) { if (defaultMenuTintColor == null) defaultMenuTintColor = menuItemView.getCurrentTextColor(); menuItemView.setTextColor(tintColor != null ? tintColor : defaultMenuTintColor); @@ -248,11 +245,9 @@ private void setTestID() { if (child instanceof AppCompatImageButton) { child.setTag(navigationTestID); } - if (child instanceof ActionMenuView) { - ActionMenuView menu = (ActionMenuView) child; + if (child instanceof ActionMenuView menu) { for (int j = 0; j < menu.getChildCount(); j++) { - if (menu.getChildAt(j) instanceof AppCompatImageView) { - AppCompatImageView overflowButton = (AppCompatImageView) menu.getChildAt(j); + if (menu.getChildAt(j) instanceof AppCompatImageView overflowButton) { overflowButton.setTag(overflowTestID); } } From e1bfe438ec6397ab72e15252b8205aae40b2822c Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Mon, 29 Jul 2024 23:23:10 +0100 Subject: [PATCH 51/60] Implemented sheet detent in unit test Allowed unit tests to change the detent and only rendered the children if not hidden --- NavigationReactNative/src/setup-jest.js | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/NavigationReactNative/src/setup-jest.js b/NavigationReactNative/src/setup-jest.js index 060df576c..5f521a5ee 100644 --- a/NavigationReactNative/src/setup-jest.js +++ b/NavigationReactNative/src/setup-jest.js @@ -222,9 +222,27 @@ jest.mock('navigation-react-native', () => { const CollapsingBar = ({children}) => children; - const BottomSheet = ({children}) => children; + const BottomSheet = (props) => - const Sheet = ({children}) => children; + const Sheet = ({detent, defaultDetent = 'collapsed', onChangeDetent, children}) => { + const [selectedDetent, setSelectedDetent] = React.useState(detent || defaultDetent); + if (detent != null && detent !== selectedDetent) setSelectedDetent(detent); + return ( + { + if (selectedDetent !== newDetent) { + if (detent == null) + setSelectedDetent(newDetent); + onChangeDetent?.(newDetent); + } + }}> + {selectedDetent !== 'hidden' ? children : null} + + ); + }; const FloatingActionButton = ({text, image, testID, onPress}) => ( Date: Mon, 29 Jul 2024 23:30:15 +0100 Subject: [PATCH 52/60] Implemented Drawer in unit test Modelled on Sheet so allows user to fire the changeOpen to open and close it --- NavigationReactNative/src/setup-jest.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/NavigationReactNative/src/setup-jest.js b/NavigationReactNative/src/setup-jest.js index 5f521a5ee..56b492a49 100644 --- a/NavigationReactNative/src/setup-jest.js +++ b/NavigationReactNative/src/setup-jest.js @@ -244,6 +244,26 @@ jest.mock('navigation-react-native', () => { ); }; + const Drawer = ({open, onChangeOpen, children}) => { + const [show, setShow] = React.useState(false); + if (open != null && show !== open) setShow(open); + return ( + { + if (show !== newOpen) { + if (open == null) + setShow(newOpen); + onChangeOpen?.(newOpen); + } + }}> + {show ? children : null} + + ); + }; + const FloatingActionButton = ({text, image, testID, onPress}) => ( { FloatingActionButton, BottomSheet, Sheet, + Drawer, }; }); From 84d89363dc45e8d15dc2f91f28c308f3c5cda503 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Wed, 31 Jul 2024 17:18:44 +0100 Subject: [PATCH 53/60] Rendered drawer as pass-through on ios There's no native drawer on ios so render children so it doesn't fall over --- NavigationReactNative/src/Drawer.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/NavigationReactNative/src/Drawer.tsx b/NavigationReactNative/src/Drawer.tsx index 64d1afd95..3d3441757 100644 --- a/NavigationReactNative/src/Drawer.tsx +++ b/NavigationReactNative/src/Drawer.tsx @@ -1,5 +1,5 @@ import React, { useState } from 'react'; -import { requireNativeComponent, StyleSheet } from 'react-native'; +import { requireNativeComponent, StyleSheet, Platform } from 'react-native'; import BackButton from './BackButton'; const Drawer = ({view, open, fromRight = false, onChangeOpen, onOpen, onClose, children}) => { @@ -18,6 +18,7 @@ const Drawer = ({view, open, fromRight = false, onChangeOpen, onOpen, onClose, c else onClose?.(); setMostRecentEventCount(mostRecentEventCount); } + if (Platform.OS === 'ios') return children; return ( <> { From 8d07e43ffa147d43013b5b40f34241fee5fc7dde Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Thu, 1 Aug 2024 18:01:44 +0100 Subject: [PATCH 54/60] Added drawer native managers and js specs --- NavigationReactNative/src/Drawer.tsx | 4 +- .../src/DrawerLayoutNativeComponent.js | 20 ++++++ .../src/DrawerNativeComponent.js | 14 ++++ .../reactnative/DrawerLayoutViewManager.java | 69 +++++++++++++++++++ .../reactnative/DrawerViewManager.java | 44 ++++++++++++ .../newarch/NavigationPackage.java | 2 + 6 files changed, 151 insertions(+), 2 deletions(-) create mode 100644 NavigationReactNative/src/DrawerLayoutNativeComponent.js create mode 100644 NavigationReactNative/src/DrawerNativeComponent.js create mode 100644 NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutViewManager.java create mode 100644 NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerViewManager.java diff --git a/NavigationReactNative/src/Drawer.tsx b/NavigationReactNative/src/Drawer.tsx index 3d3441757..1007e1204 100644 --- a/NavigationReactNative/src/Drawer.tsx +++ b/NavigationReactNative/src/Drawer.tsx @@ -46,8 +46,8 @@ const Drawer = ({view, open, fromRight = false, onChangeOpen, onOpen, onClose, c ); }; -const NVDrawerLayout = requireNativeComponent('NVDrawerLayout'); -const NVDrawer = requireNativeComponent('NVDrawer'); +const NVDrawerLayout = global.nativeFabricUIManager ? require('./DrawerLayoutNativeComponent').default : requireNativeComponent('NVDrawerLayout'); +const NVDrawer = global.nativeFabricUIManager ? require('./DrawerNativeComponent').default : requireNativeComponent('NVDrawer'); const styles = StyleSheet.create({ drawer: { diff --git a/NavigationReactNative/src/DrawerLayoutNativeComponent.js b/NavigationReactNative/src/DrawerLayoutNativeComponent.js new file mode 100644 index 000000000..a7b85bce0 --- /dev/null +++ b/NavigationReactNative/src/DrawerLayoutNativeComponent.js @@ -0,0 +1,20 @@ +// @flow strict-local + +import type {ViewProps} from 'react-native/Libraries/Components/View/ViewPropTypes'; +import type {HostComponent} from 'react-native'; +import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent'; + +type NativeProps = $ReadOnly<{| + ...ViewProps, + open: boolean, + fromRight: boolean, + mostRecentEventCount: Int32, + onChangeOpen: DirectEventHandler<$ReadOnly<{| + open: boolean, + eventCount: Int32, + |}>>, +|}>; + +export default (codegenNativeComponent( + 'NVDrawerLayout', +): HostComponent); diff --git a/NavigationReactNative/src/DrawerNativeComponent.js b/NavigationReactNative/src/DrawerNativeComponent.js new file mode 100644 index 000000000..77f373970 --- /dev/null +++ b/NavigationReactNative/src/DrawerNativeComponent.js @@ -0,0 +1,14 @@ +// @flow strict-local + +import type {ViewProps} from 'react-native/Libraries/Components/View/ViewPropTypes'; +import type {HostComponent} from 'react-native'; +import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent'; + +type NativeProps = $ReadOnly<{| + ...ViewProps, + fromRight: boolean, +|}>; + +export default (codegenNativeComponent( + 'NVDrawer', +): HostComponent); diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutViewManager.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutViewManager.java new file mode 100644 index 000000000..d58344e10 --- /dev/null +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerLayoutViewManager.java @@ -0,0 +1,69 @@ +package com.navigation.reactnative; + +import android.view.Gravity; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.facebook.react.common.MapBuilder; +import com.facebook.react.uimanager.ThemedReactContext; +import com.facebook.react.uimanager.ViewGroupManager; +import com.facebook.react.uimanager.ViewManagerDelegate; +import com.facebook.react.viewmanagers.NVDrawerLayoutManagerDelegate; +import com.facebook.react.viewmanagers.NVDrawerLayoutManagerInterface; + +import java.util.Map; + +public class DrawerLayoutViewManager extends ViewGroupManager implements NVDrawerLayoutManagerInterface { + private final ViewManagerDelegate delegate; + + public DrawerLayoutViewManager() { + delegate = new NVDrawerLayoutManagerDelegate<>(this); + } + + @Nullable + @Override + protected ViewManagerDelegate getDelegate() { + return delegate; + } + + @NonNull + @Override + public String getName() { + return "NVDrawerLayout"; + } + + @NonNull + @Override + protected DrawerLayoutView createViewInstance(@NonNull ThemedReactContext themedReactContext) { + return new DrawerLayoutView(themedReactContext); + } + + @Override + public void setOpen(DrawerLayoutView view, boolean open) { + view.pendingOpen = open; + } + + @Override + public void setFromRight(DrawerLayoutView view, boolean fromRight) { + view.gravity = !fromRight ? Gravity.LEFT : Gravity.RIGHT; + view.requestLayout(); + } + + @Override + public void setMostRecentEventCount(DrawerLayoutView view, int mostRecentEventCount) { + view.mostRecentEventCount = mostRecentEventCount; + } + + @Override + public boolean needsCustomLayoutForChildren() { + return true; + } + + @Override + public Map getExportedCustomDirectEventTypeConstants() { + return MapBuilder.builder() + .put("topChangeOpen", MapBuilder.of("registrationName", "onChangeOpen")) + .build(); + } +} diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerViewManager.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerViewManager.java new file mode 100644 index 000000000..301a8c3f1 --- /dev/null +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerViewManager.java @@ -0,0 +1,44 @@ +package com.navigation.reactnative; + +import android.view.Gravity; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.drawerlayout.widget.DrawerLayout; + +import com.facebook.react.uimanager.ThemedReactContext; +import com.facebook.react.uimanager.ViewGroupManager; +import com.facebook.react.uimanager.ViewManagerDelegate; +import com.facebook.react.viewmanagers.NVDrawerManagerDelegate; +import com.facebook.react.viewmanagers.NVDrawerManagerInterface; + +public class DrawerViewManager extends ViewGroupManager implements NVDrawerManagerInterface { + private final ViewManagerDelegate delegate; + + public DrawerViewManager() { + delegate = new NVDrawerManagerDelegate<>(this); + } + + @Nullable + @Override + protected ViewManagerDelegate getDelegate() { + return delegate; + } + + @NonNull + @Override + public String getName() { + return "NVDrawer"; + } + + @NonNull + @Override + protected DrawerView createViewInstance(@NonNull ThemedReactContext themedReactContext) { + return new DrawerView(themedReactContext); + } + + @Override + public void setFromRight(DrawerView view, boolean fromRight) { + ((DrawerLayout.LayoutParams) view.getLayoutParams()).gravity = !fromRight ? Gravity.LEFT : Gravity.RIGHT; + } +} diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/newarch/NavigationPackage.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/newarch/NavigationPackage.java index a4dc49480..9ff9047c1 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/newarch/NavigationPackage.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/newarch/NavigationPackage.java @@ -29,6 +29,8 @@ public List createViewManagers(ReactApplicationContext reactContext new CollapsingBarViewManager(), new CoordinatorLayoutViewManager(), new DialogViewManager(), + new DrawerLayoutViewManager(), + new DrawerViewManager(), new ExtendedFloatingActionButtonViewManager(), new FloatingActionButtonViewManager(), new NavigationBarViewManager(), From fe6682443a593f30d81a5bd0e1c69f659d703ffc Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Thu, 1 Aug 2024 19:41:58 +0100 Subject: [PATCH 55/60] Copied over other auto nav changes from old arch --- .../src/SearchToolbarNativeComponent.js | 1 - .../reactnative/BottomAppBarViewManager.java | 17 ++++++++++++++- .../reactnative/SearchToolbarViewManager.java | 21 ++++++++++--------- .../reactnative/ToolbarViewManager.java | 12 ++++++++++- 4 files changed, 38 insertions(+), 13 deletions(-) diff --git a/NavigationReactNative/src/SearchToolbarNativeComponent.js b/NavigationReactNative/src/SearchToolbarNativeComponent.js index a8d66c94f..997dddc69 100644 --- a/NavigationReactNative/src/SearchToolbarNativeComponent.js +++ b/NavigationReactNative/src/SearchToolbarNativeComponent.js @@ -15,7 +15,6 @@ type NativeProps = $ReadOnly<{| tintColor: ColorValue, navigationImage: ImageSource, navigationTestID: string, - navigationDecorative: boolean, navigationAccessibilityLabel: string, overflowImage: ImageSource, overflowTestID: string, diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/BottomAppBarViewManager.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/BottomAppBarViewManager.java index 6cf60490e..b4b2f8ec7 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/BottomAppBarViewManager.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/BottomAppBarViewManager.java @@ -38,11 +38,26 @@ protected BottomAppBarView createViewInstance(@NonNull ThemedReactContext reactC return new BottomAppBarView(reactContext); } + @ReactProp(name = "crumb") + public void setCrumb(BottomAppBarView view, int crumb) { + view.crumb = crumb; + } + + @ReactProp(name = "autoNavigation") + public void setAutoNavigation(BottomAppBarView view, boolean autoNavigation) { + view.autoNavigation = autoNavigation; + } + @ReactProp(name = "navigationImage") public void setNavigationImage(BottomAppBarView view, @Nullable ReadableMap navigationImage) { view.setNavIconSource(navigationImage); } + @ReactProp(name = "navigationAccessibilityLabel") + public void setNavigationAccessibilityLabel(BottomAppBarView view, @Nullable String navigationContentDescription) { + view.setNavigationContentDescription(navigationContentDescription); + } + @ReactProp(name = "overflowImage") public void setOverflowImage(BottomAppBarView view, @Nullable ReadableMap overflowImage) { view.setOverflowIconSource(overflowImage); @@ -132,7 +147,7 @@ public View getChildAt(BottomAppBarView parent, int index) { @Override protected void onAfterUpdateTransaction(@NonNull BottomAppBarView view) { super.onAfterUpdateTransaction(view); - view.setFabAlignmentMode(view.fabAlignmentMode); + view.onAfterUpdateTransaction(); } @Override diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SearchToolbarViewManager.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SearchToolbarViewManager.java index 2009e6a33..93dc2dfa4 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SearchToolbarViewManager.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/SearchToolbarViewManager.java @@ -43,6 +43,16 @@ protected SearchToolbarView createViewInstance(@NonNull ThemedReactContext theme return new SearchToolbarView(themedReactContext); } + @ReactProp(name = "crumb") + public void setCrumb(SearchToolbarView view, int crumb) { + view.crumb = crumb; + } + + @ReactProp(name = "autoNavigation") + public void setAutoNavigation(SearchToolbarView view, boolean autoNavigation) { + view.autoNavigation = autoNavigation; + } + @Override @ReactProp(name = "placeholder") public void setPlaceholder(SearchToolbarView view, @Nullable String placeholder) { @@ -96,15 +106,6 @@ public void setNavigationTestID(SearchToolbarView view, @Nullable String navigat view.setNavigationTestID(navigationTestID); } - @Override - @ReactProp(name = "navigationDecorative") - public void setNavigationDecorative(SearchToolbarView view, boolean navigationDecorative) { - if (!navigationDecorative) - view.addNavigationListener(); - else - view.setNavigationOnClickListener(null); - } - @Override @ReactProp(name = "navigationAccessibilityLabel") public void setNavigationAccessibilityLabel(SearchToolbarView view, @Nullable String navigationContentDescription) { @@ -148,7 +149,7 @@ public View getChildAt(SearchToolbarView parent, int index) { @Override protected void onAfterUpdateTransaction(@NonNull SearchToolbarView view) { super.onAfterUpdateTransaction(view); - view.stylePlaceholder(); + view.onAfterUpdateTransaction(); } @Override diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarViewManager.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarViewManager.java index 23e22d508..f2da89645 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarViewManager.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/ToolbarViewManager.java @@ -48,6 +48,16 @@ protected ToolbarView createViewInstance(@Nonnull ThemedReactContext reactContex return new ToolbarView(reactContext); } + @ReactProp(name = "crumb") + public void setCrumb(ToolbarView view, int crumb) { + view.crumb = crumb; + } + + @ReactProp(name = "autoNavigation") + public void setAutoNavigation(ToolbarView view, boolean autoNavigation) { + view.autoNavigation = autoNavigation; + } + @Override @ReactProp(name = "title") public void setTitle(ToolbarView view, String title) { @@ -191,7 +201,7 @@ public boolean needsCustomLayoutForChildren() { @Override protected void onAfterUpdateTransaction(@NonNull ToolbarView view) { super.onAfterUpdateTransaction(view); - view.styleTitle(); + view.onAfterUpdateTransaction(); } @Override From e533e41158418803772159a58338662550e2455d Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Thu, 1 Aug 2024 19:43:29 +0100 Subject: [PATCH 56/60] Ensured the drawer view renders Don't know why it's different on fabric but it rendered very wrong --- .../src/main/java/com/navigation/reactnative/DrawerView.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerView.java b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerView.java index 6f133168a..1f197066e 100644 --- a/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerView.java +++ b/NavigationReactNative/src/android/src/main/java/com/navigation/reactnative/DrawerView.java @@ -21,4 +21,8 @@ public DrawerView(@NonNull Context context) { @Override public void addView(View child) { } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + } } From 1621bf94dfa1b8c34750dbc280f67267257ed91b Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Thu, 1 Aug 2024 19:44:54 +0100 Subject: [PATCH 57/60] Reverted redundant change This happened when added the drawer open and close strings for the toggle. But since removed so don't want this showing up in the diff --- .../src/android/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NavigationReactNative/src/android/src/main/res/values/strings.xml b/NavigationReactNative/src/android/src/main/res/values/strings.xml index 045e125f3..55344e519 100644 --- a/NavigationReactNative/src/android/src/main/res/values/strings.xml +++ b/NavigationReactNative/src/android/src/main/res/values/strings.xml @@ -1,3 +1,3 @@ - + \ No newline at end of file From 67630ba6aa54ccc668380eb8fc71c40f8a286302 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Thu, 1 Aug 2024 19:50:14 +0100 Subject: [PATCH 58/60] Added new props to the native specs --- NavigationReactNative/src/BottomAppBarNativeComponent.js | 3 +++ NavigationReactNative/src/SearchToolbarNativeComponent.js | 2 ++ NavigationReactNative/src/ToolbarNativeComponent.js | 2 ++ 3 files changed, 7 insertions(+) diff --git a/NavigationReactNative/src/BottomAppBarNativeComponent.js b/NavigationReactNative/src/BottomAppBarNativeComponent.js index 87df192eb..9c483c26f 100644 --- a/NavigationReactNative/src/BottomAppBarNativeComponent.js +++ b/NavigationReactNative/src/BottomAppBarNativeComponent.js @@ -6,6 +6,8 @@ import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNati type NativeProps = $ReadOnly<{| ...ViewProps, + crumb: Int32, + autoNavigation: boolean, barTintColor: ColorValue, tintColor: ColorValue, fabAlignmentMode: string, @@ -16,6 +18,7 @@ type NativeProps = $ReadOnly<{| hideOnScroll: boolean, navigationImage: ImageSource, navigationTestID: string, + navigationAccessibilityLabel: string, overflowImage: ImageSource, overflowTestID: string, barHeight: Double, diff --git a/NavigationReactNative/src/SearchToolbarNativeComponent.js b/NavigationReactNative/src/SearchToolbarNativeComponent.js index 997dddc69..97d4d8844 100644 --- a/NavigationReactNative/src/SearchToolbarNativeComponent.js +++ b/NavigationReactNative/src/SearchToolbarNativeComponent.js @@ -6,6 +6,8 @@ import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNati type NativeProps = $ReadOnly<{| ...ViewProps, + crumb: Int32, + autoNavigation: boolean, placeholder: string, fontFamily: string, fontWeight: string, diff --git a/NavigationReactNative/src/ToolbarNativeComponent.js b/NavigationReactNative/src/ToolbarNativeComponent.js index 366b90432..1f9463b9d 100644 --- a/NavigationReactNative/src/ToolbarNativeComponent.js +++ b/NavigationReactNative/src/ToolbarNativeComponent.js @@ -6,6 +6,8 @@ import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNati type NativeProps = $ReadOnly<{| ...ViewProps, + crumb: Int32, + autoNavigation: boolean, title: string, titleFontFamily: string, titleFontWeight: string, From ba83c6b7e90e68e03d18da8a5669991a362aff6d Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Fri, 2 Aug 2024 15:08:40 +0100 Subject: [PATCH 59/60] Added empty Drawer native comps for ios fabric --- .../src/ios/NVDrawerComponentView.h | 13 +++++++ .../src/ios/NVDrawerComponentView.mm | 39 +++++++++++++++++++ .../src/ios/NVDrawerLayoutComponentView.h | 13 +++++++ .../src/ios/NVDrawerLayoutComponentView.mm | 39 +++++++++++++++++++ .../project.pbxproj | 8 ++++ 5 files changed, 112 insertions(+) create mode 100644 NavigationReactNative/src/ios/NVDrawerComponentView.h create mode 100644 NavigationReactNative/src/ios/NVDrawerComponentView.mm create mode 100644 NavigationReactNative/src/ios/NVDrawerLayoutComponentView.h create mode 100644 NavigationReactNative/src/ios/NVDrawerLayoutComponentView.mm diff --git a/NavigationReactNative/src/ios/NVDrawerComponentView.h b/NavigationReactNative/src/ios/NVDrawerComponentView.h new file mode 100644 index 000000000..c08ce9383 --- /dev/null +++ b/NavigationReactNative/src/ios/NVDrawerComponentView.h @@ -0,0 +1,13 @@ +#ifdef RCT_NEW_ARCH_ENABLED +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface NVDrawerComponentView : RCTViewComponentView + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/NavigationReactNative/src/ios/NVDrawerComponentView.mm b/NavigationReactNative/src/ios/NVDrawerComponentView.mm new file mode 100644 index 000000000..8b8e9bcf3 --- /dev/null +++ b/NavigationReactNative/src/ios/NVDrawerComponentView.mm @@ -0,0 +1,39 @@ +#ifdef RCT_NEW_ARCH_ENABLED +#import "NVDrawerComponentView.h" + +#import +#import +#import +#import + +#import "RCTFabricComponentsPlugins.h" + +using namespace facebook::react; + +@interface NVDrawerComponentView () +@end + +@implementation NVDrawerComponentView + +- (instancetype)initWithFrame:(CGRect)frame +{ + if (self = [super initWithFrame:frame]) { + } + return self; +} + +#pragma mark - RCTComponentViewProtocol + ++ (ComponentDescriptorProvider)componentDescriptorProvider +{ + return concreteComponentDescriptorProvider(); +} + +@end + +Class NVDrawerCls(void) +{ + return NVDrawerComponentView.class; +} + +#endif diff --git a/NavigationReactNative/src/ios/NVDrawerLayoutComponentView.h b/NavigationReactNative/src/ios/NVDrawerLayoutComponentView.h new file mode 100644 index 000000000..865c8cc55 --- /dev/null +++ b/NavigationReactNative/src/ios/NVDrawerLayoutComponentView.h @@ -0,0 +1,13 @@ +#ifdef RCT_NEW_ARCH_ENABLED +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface NVDrawerLayoutComponentView : RCTViewComponentView + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/NavigationReactNative/src/ios/NVDrawerLayoutComponentView.mm b/NavigationReactNative/src/ios/NVDrawerLayoutComponentView.mm new file mode 100644 index 000000000..9aac28cb5 --- /dev/null +++ b/NavigationReactNative/src/ios/NVDrawerLayoutComponentView.mm @@ -0,0 +1,39 @@ +#ifdef RCT_NEW_ARCH_ENABLED +#import "NVDrawerLayoutComponentView.h" + +#import +#import +#import +#import + +#import "RCTFabricComponentsPlugins.h" + +using namespace facebook::react; + +@interface NVDrawerLayoutComponentView () +@end + +@implementation NVDrawerLayoutComponentView + +- (instancetype)initWithFrame:(CGRect)frame +{ + if (self = [super initWithFrame:frame]) { + } + return self; +} + +#pragma mark - RCTComponentViewProtocol + ++ (ComponentDescriptorProvider)componentDescriptorProvider +{ + return concreteComponentDescriptorProvider(); +} + +@end + +Class NVDrawerLayoutCls(void) +{ + return NVDrawerLayoutComponentView.class; +} + +#endif diff --git a/NavigationReactNative/src/ios/NavigationReactNative.xcodeproj/project.pbxproj b/NavigationReactNative/src/ios/NavigationReactNative.xcodeproj/project.pbxproj index 0df47f6dc..4c76d530a 100644 --- a/NavigationReactNative/src/ios/NavigationReactNative.xcodeproj/project.pbxproj +++ b/NavigationReactNative/src/ios/NavigationReactNative.xcodeproj/project.pbxproj @@ -51,6 +51,10 @@ 7033352E2C2DFCA400160A73 /* NVDialogComponentView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NVDialogComponentView.h; sourceTree = ""; }; 7033352F2C2DFCBD00160A73 /* NVSheetComponentView.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = NVSheetComponentView.mm; sourceTree = ""; }; 703335302C2DFCC900160A73 /* NVSheetComponentView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NVSheetComponentView.h; sourceTree = ""; }; + 7048AA262C5D1E20004D3427 /* NVDrawerLayoutComponentView.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = NVDrawerLayoutComponentView.mm; sourceTree = ""; }; + 7048AA272C5D1E2C004D3427 /* NVDrawerLayoutComponentView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NVDrawerLayoutComponentView.h; sourceTree = ""; }; + 7048AA282C5D1EBA004D3427 /* NVDrawerComponentView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NVDrawerComponentView.h; sourceTree = ""; }; + 7048AA292C5D1EC4004D3427 /* NVDrawerComponentView.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = NVDrawerComponentView.mm; sourceTree = ""; }; 704B593B22A2AC14008C05C6 /* NVNavigationStackView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NVNavigationStackView.m; sourceTree = ""; }; 704B593D22A2AC20008C05C6 /* NVNavigationStackView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NVNavigationStackView.h; sourceTree = ""; }; 704B593F22A2AC3A008C05C6 /* NVNavigationStackManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NVNavigationStackManager.m; sourceTree = ""; }; @@ -156,6 +160,10 @@ 7020F6B520ECD07A00E7A74E = { isa = PBXGroup; children = ( + 7048AA292C5D1EC4004D3427 /* NVDrawerComponentView.mm */, + 7048AA282C5D1EBA004D3427 /* NVDrawerComponentView.h */, + 7048AA272C5D1E2C004D3427 /* NVDrawerLayoutComponentView.h */, + 7048AA262C5D1E20004D3427 /* NVDrawerLayoutComponentView.mm */, 701EDB672C3802FB00E79704 /* NVTabBarPagerItemComponentView.mm */, 701EDB662C3802EF00E79704 /* NVTabBarPagerItemComponentView.h */, 703335302C2DFCC900160A73 /* NVSheetComponentView.h */, From ba2338ee02352b76a81c729a2336ada05e6eb04d Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Fri, 2 Aug 2024 16:33:20 +0100 Subject: [PATCH 60/60] Typed the drawer component --- .../navigation.react.native.d.ts | 41 +++++++++++++++++++ types/navigation-react-native.d.ts | 41 +++++++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/build/npm/navigation-react-native/navigation.react.native.d.ts b/build/npm/navigation-react-native/navigation.react.native.d.ts index 670504e23..881abc736 100644 --- a/build/npm/navigation-react-native/navigation.react.native.d.ts +++ b/build/npm/navigation-react-native/navigation.react.native.d.ts @@ -929,6 +929,47 @@ export class Sheet extends Component {} */ export class BottomSheet extends Component> {} +/** + * Defines the Drawer Props contract + */ +export interface DrawerProps { + /** + * The drawer content + */ + view: ReactNode; + /** + * Indicates whether the drawer is open + */ + open?: boolean; + /** + * Indicates whether the drawer opens from the right + */ + fromRight?: boolean; + + /** + * The scene content + */ + children: ReactNode; + /** + * Handles drawer open change events + */ + onChangeOpen?: (open: boolean) => void; + /** + * Handles drawer open events + */ + onOpen?: () => void; + /** + * Handles drawer close events + */ + onClose?: () => void; +} + +/** + * Renders a drawer + * @platform android + */ +export class Drawer extends Component {} + /** * Defines the Floating Action Button Props contract */ diff --git a/types/navigation-react-native.d.ts b/types/navigation-react-native.d.ts index 670504e23..881abc736 100644 --- a/types/navigation-react-native.d.ts +++ b/types/navigation-react-native.d.ts @@ -929,6 +929,47 @@ export class Sheet extends Component {} */ export class BottomSheet extends Component> {} +/** + * Defines the Drawer Props contract + */ +export interface DrawerProps { + /** + * The drawer content + */ + view: ReactNode; + /** + * Indicates whether the drawer is open + */ + open?: boolean; + /** + * Indicates whether the drawer opens from the right + */ + fromRight?: boolean; + + /** + * The scene content + */ + children: ReactNode; + /** + * Handles drawer open change events + */ + onChangeOpen?: (open: boolean) => void; + /** + * Handles drawer open events + */ + onOpen?: () => void; + /** + * Handles drawer close events + */ + onClose?: () => void; +} + +/** + * Renders a drawer + * @platform android + */ +export class Drawer extends Component {} + /** * Defines the Floating Action Button Props contract */