From 431cc69aac424ee3042faf7295602cc05e61d427 Mon Sep 17 00:00:00 2001 From: Drew Higgins Date: Tue, 14 Apr 2020 20:37:07 -0700 Subject: [PATCH 1/9] Added initial implementation --- vnext/ReactUWP/Views/TouchEventHandler.cpp | 13 +++++++ vnext/ReactUWP/Views/TouchEventHandler.h | 1 + .../Utilities/BackHandler.windows.js | 35 +++++++++++++++++-- 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/vnext/ReactUWP/Views/TouchEventHandler.cpp b/vnext/ReactUWP/Views/TouchEventHandler.cpp index cdcd9de40a8..5b620796534 100644 --- a/vnext/ReactUWP/Views/TouchEventHandler.cpp +++ b/vnext/ReactUWP/Views/TouchEventHandler.cpp @@ -78,6 +78,14 @@ void TouchEventHandler::OnPointerPressed( if (!TagFromOriginalSource(args, &tag, &sourceElement)) return; + // If this was caused by the user pressing the "back" hardware button, fire that event instead + if (args.GetCurrentPoint(sourceElement).Properties().PointerUpdateKind() == + winrt::Windows::UI::Input::PointerUpdateKind::XButton1Pressed) { + DispatchBackEvent(); + args.Handled(true); + return; + } + if (m_xamlView.as().CapturePointer(args.Pointer())) { // Pointer pressing updates the enter/leave state UpdatePointersInViews(args, tag, sourceElement); @@ -341,6 +349,11 @@ void TouchEventHandler::DispatchTouchEvent(TouchEventType eventType, size_t poin instance->CallJsFunction("RCTEventEmitter", "receiveTouches", std::move(params)); } +void TouchEventHandler::DispatchBackEvent() { + auto instance = m_wkReactInstance.lock(); + instance->CallJsFunction("RCTDeviceEventEmitter", "emit", folly::dynamic::array("hardwareBackPress")); +} + const char *TouchEventHandler::GetPointerDeviceTypeName( winrt::Windows::Devices::Input::PointerDeviceType deviceType) noexcept { const char *deviceTypeName = "unknown"; diff --git a/vnext/ReactUWP/Views/TouchEventHandler.h b/vnext/ReactUWP/Views/TouchEventHandler.h index 62488bc7e4f..4167e5cdc62 100644 --- a/vnext/ReactUWP/Views/TouchEventHandler.h +++ b/vnext/ReactUWP/Views/TouchEventHandler.h @@ -78,6 +78,7 @@ class TouchEventHandler { enum class TouchEventType { Start = 0, End, Move, Cancel, PointerEntered, PointerExited, PointerMove }; void OnPointerConcluded(TouchEventType eventType, const winrt::PointerRoutedEventArgs &args); void DispatchTouchEvent(TouchEventType eventType, size_t pointerIndex); + void DispatchBackEvent(); const char *GetPointerDeviceTypeName(winrt::Windows::Devices::Input::PointerDeviceType deviceType) noexcept; const char *GetTouchEventTypeName(TouchEventType eventType) noexcept; diff --git a/vnext/src/Libraries/Utilities/BackHandler.windows.js b/vnext/src/Libraries/Utilities/BackHandler.windows.js index 8a57351da63..7431245595d 100644 --- a/vnext/src/Libraries/Utilities/BackHandler.windows.js +++ b/vnext/src/Libraries/Utilities/BackHandler.windows.js @@ -13,6 +13,8 @@ 'use strict'; +const RCTDeviceEventEmitter = require('../EventEmitter/RCTDeviceEventEmitter'); + // TODO Hx: Implement. /** @@ -48,6 +50,20 @@ type BackPressEventName = 'backPress' | 'hardwareBackPress'; +const DEVICE_BACK_EVENT = 'hardwareBackPress'; + +const _backListeners = []; + +RCTDeviceEventEmitter.addListener(DEVICE_BACK_EVENT, () => { + for (let i = _backListeners.length - 1; i >= 0; i--) { + if (_backListeners[i]()) { + return; + } + } + + BackHandler.exitApp(); +}); + type TBackHandler = {| +exitApp: () => void, +addEventListener: ( @@ -63,9 +79,24 @@ type TBackHandler = {| const BackHandler: TBackHandler = { exitApp: () => {}, addEventListener: (eventName: BackPressEventName, handler: Function) => { - return {remove: () => {}}; + debugger; + if (_backListeners.indexOf(handler) === -1) { + _backListeners.push(handler); + } + + return { + remove: () => { + BackHandler.removeEventListener(eventName, handler); + }, + }; + }, + removeEventListener: (eventName: BackPressEventName, handler: Function) => { + const handlerIndex = _backListeners.indexOf(handler); + + if (handlerIndex !== -1) { + _backListeners.splice(handlerIndex, 1); + } }, - removeEventListener: (eventName: BackPressEventName, handler: Function) => {}, }; module.exports = BackHandler; From da8ae9b98d0bfc24c9c6e54c953cc973b26457ff Mon Sep 17 00:00:00 2001 From: Drew Higgins Date: Wed, 15 Apr 2020 16:13:38 -0700 Subject: [PATCH 2/9] Add backrequested listener --- .../Microsoft.ReactNative/Views/ReactRootControl.cpp | 6 ++++++ vnext/ReactUWP/Views/TouchEventHandler.cpp | 12 ++++++++---- vnext/ReactUWP/Views/TouchEventHandler.h | 2 +- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/vnext/Microsoft.ReactNative/Views/ReactRootControl.cpp b/vnext/Microsoft.ReactNative/Views/ReactRootControl.cpp index 0574a0762d3..cd621b253c2 100644 --- a/vnext/Microsoft.ReactNative/Views/ReactRootControl.cpp +++ b/vnext/Microsoft.ReactNative/Views/ReactRootControl.cpp @@ -185,6 +185,12 @@ void ReactRootControl::InitRootView( UpdateRootViewInternal(); + winrt::Windows::UI::Core::SystemNavigationManager::GetForCurrentView().BackRequested( + [uwpReactInstance](winrt::IInspectable const & /*sender*/, winrt::BackRequestedEventArgs const &args) { + uwpReactInstance->CallJsFunction("RCTDeviceEventEmitter", "emit", folly::dynamic::array("hardwareBackPress")); + args.Handled(true); + }); + m_isInitialized = true; } diff --git a/vnext/ReactUWP/Views/TouchEventHandler.cpp b/vnext/ReactUWP/Views/TouchEventHandler.cpp index 5b620796534..d08300864ca 100644 --- a/vnext/ReactUWP/Views/TouchEventHandler.cpp +++ b/vnext/ReactUWP/Views/TouchEventHandler.cpp @@ -81,8 +81,7 @@ void TouchEventHandler::OnPointerPressed( // If this was caused by the user pressing the "back" hardware button, fire that event instead if (args.GetCurrentPoint(sourceElement).Properties().PointerUpdateKind() == winrt::Windows::UI::Input::PointerUpdateKind::XButton1Pressed) { - DispatchBackEvent(); - args.Handled(true); + args.Handled(DispatchBackEvent()); return; } @@ -349,9 +348,14 @@ void TouchEventHandler::DispatchTouchEvent(TouchEventType eventType, size_t poin instance->CallJsFunction("RCTEventEmitter", "receiveTouches", std::move(params)); } -void TouchEventHandler::DispatchBackEvent() { +bool TouchEventHandler::DispatchBackEvent() { auto instance = m_wkReactInstance.lock(); - instance->CallJsFunction("RCTDeviceEventEmitter", "emit", folly::dynamic::array("hardwareBackPress")); + if (instance != nullptr && !instance->IsInError()) { + instance->CallJsFunction("RCTDeviceEventEmitter", "emit", folly::dynamic::array("hardwareBackPress")); + return true; + } + + return false; } const char *TouchEventHandler::GetPointerDeviceTypeName( diff --git a/vnext/ReactUWP/Views/TouchEventHandler.h b/vnext/ReactUWP/Views/TouchEventHandler.h index 4167e5cdc62..df26eaf2c19 100644 --- a/vnext/ReactUWP/Views/TouchEventHandler.h +++ b/vnext/ReactUWP/Views/TouchEventHandler.h @@ -78,7 +78,7 @@ class TouchEventHandler { enum class TouchEventType { Start = 0, End, Move, Cancel, PointerEntered, PointerExited, PointerMove }; void OnPointerConcluded(TouchEventType eventType, const winrt::PointerRoutedEventArgs &args); void DispatchTouchEvent(TouchEventType eventType, size_t pointerIndex); - void DispatchBackEvent(); + bool DispatchBackEvent(); const char *GetPointerDeviceTypeName(winrt::Windows::Devices::Input::PointerDeviceType deviceType) noexcept; const char *GetTouchEventTypeName(TouchEventType eventType) noexcept; From 543ab74df2e8e8b874d855f6ee382445c5bc3956 Mon Sep 17 00:00:00 2001 From: Drew Higgins Date: Thu, 16 Apr 2020 10:58:44 -0700 Subject: [PATCH 3/9] Add handlers for other back actions Also remove debug code --- packages/playground/Samples/mouse.tsx | 9 +++ .../Views/ReactRootControl.cpp | 54 ++++++++++++++++-- .../Views/ReactRootControl.h | 3 + vnext/ReactUWP/RCa15532 | Bin 0 -> 4282 bytes .../Utilities/BackHandler.windows.js | 1 - 5 files changed, 60 insertions(+), 7 deletions(-) create mode 100644 vnext/ReactUWP/RCa15532 diff --git a/packages/playground/Samples/mouse.tsx b/packages/playground/Samples/mouse.tsx index f55251e60b3..455947f7ada 100644 --- a/packages/playground/Samples/mouse.tsx +++ b/packages/playground/Samples/mouse.tsx @@ -11,6 +11,7 @@ import { Text, GestureResponderEvent, TouchableHighlight, + BackHandler, } from 'react-native'; export default class Bootstrap extends React.Component< @@ -32,6 +33,10 @@ export default class Bootstrap extends React.Component< }; } + componentDidMount() { + BackHandler.addEventListener('hardwareBackPress', this.back); + } + render() { return ( { + console.log('hardwareBackPress'); + return true; + }; } const styles = StyleSheet.create({ diff --git a/vnext/Microsoft.ReactNative/Views/ReactRootControl.cpp b/vnext/Microsoft.ReactNative/Views/ReactRootControl.cpp index cd621b253c2..0d36b6265f2 100644 --- a/vnext/Microsoft.ReactNative/Views/ReactRootControl.cpp +++ b/vnext/Microsoft.ReactNative/Views/ReactRootControl.cpp @@ -184,12 +184,7 @@ void ReactRootControl::InitRootView( m_SIPEventHandler->AttachView(xamlRootView, /*fireKeyboradEvents:*/ true); UpdateRootViewInternal(); - - winrt::Windows::UI::Core::SystemNavigationManager::GetForCurrentView().BackRequested( - [uwpReactInstance](winrt::IInspectable const & /*sender*/, winrt::BackRequestedEventArgs const &args) { - uwpReactInstance->CallJsFunction("RCTDeviceEventEmitter", "emit", folly::dynamic::array("hardwareBackPress")); - args.Handled(true); - }); + AttachBackHandlers(xamlRootView); m_isInitialized = true; } @@ -613,6 +608,53 @@ void ReactRootControl::ReloadViewHost() noexcept { } } +void ReactRootControl::AttachBackHandlers(XamlView const &rootView) noexcept { + auto rootElement(rootView.as()); + if (rootElement == nullptr) { + assert(false); + return; + } + + winrt::Windows::UI::Core::SystemNavigationManager::GetForCurrentView().BackRequested( + [this](winrt::IInspectable const & /*sender*/, winrt::BackRequestedEventArgs const &args) { + args.Handled(OnBackRequested()); + }); + + // Handle keyboard "back" button press + winrt::KeyboardAccelerator goBack{}; + goBack.Key(winrt::VirtualKey::GoBack); + goBack.Invoked( + [this](winrt::KeyboardAccelerator const & /*sender*/, winrt::KeyboardAcceleratorInvokedEventArgs const &args) { + args.Handled(OnBackRequested()); + }); + rootElement.KeyboardAccelerators().Append(goBack); + + // Handle Alt+Left keyboard shortcut + winrt::KeyboardAccelerator altLeft{}; + altLeft.Key(winrt::VirtualKey::Left); + altLeft.Invoked( + [this](winrt::KeyboardAccelerator const & /*sender*/, winrt::KeyboardAcceleratorInvokedEventArgs const &args) { + args.Handled(OnBackRequested()); + }); + rootElement.KeyboardAccelerators().Append(altLeft); + altLeft.Modifiers(winrt::VirtualKeyModifiers::Menu); +} + +bool ReactRootControl::OnBackRequested() noexcept { + if (m_weakReactInstance.IsExpired()) { + return false; + } + + auto &legacyReactInstance = query_cast(*m_weakReactInstance.GetStrongPtr()); + auto uwpReactInstance = legacyReactInstance.UwpReactInstance(); + if (uwpReactInstance != nullptr) { + uwpReactInstance->CallJsFunction("RCTDeviceEventEmitter", "emit", folly::dynamic::array("hardwareBackPress")); + return true; + } + + return false; +} + Mso::React::IReactViewHost *ReactRootControl::ReactViewHost() noexcept { return m_reactViewHost.Get(); } diff --git a/vnext/Microsoft.ReactNative/Views/ReactRootControl.h b/vnext/Microsoft.ReactNative/Views/ReactRootControl.h index 3166195164b..e543b40b178 100644 --- a/vnext/Microsoft.ReactNative/Views/ReactRootControl.h +++ b/vnext/Microsoft.ReactNative/Views/ReactRootControl.h @@ -83,6 +83,9 @@ struct ReactRootControl final : std::enable_shared_from_this, void ReloadHost() noexcept; void ReloadViewHost() noexcept; + void AttachBackHandlers(XamlView const &rootView) noexcept; + bool OnBackRequested() noexcept; + private: int64_t m_rootTag{-1}; diff --git a/vnext/ReactUWP/RCa15532 b/vnext/ReactUWP/RCa15532 new file mode 100644 index 0000000000000000000000000000000000000000..d104d891d3107379e0c8ba563c714619cb454b55 GIT binary patch literal 4282 zcmc(iYi}Ay6o${|O8pNb^2Ls@lh{tGR8^%AOzQ?;WMgQnN(dW5ksOG)Bu;;Qo_A)K z*@ZRh%ZCbiJv(zQ@4V;CnX}LA#zr=_fjN6+6|39N_7A@)Gd;Vux$Usp&{D&Fgxn_Q0ee|6D zlK7PWDcb+quWP#d$&55UE_d{&U04f5M|NVr@%-BkZ3`URSiNf{V949KOV)vZwua?R zGBW~6u;ajR0vlcU_yCfwwLx+Yh9f&=rJJBB@*O0*)Z4f57BX56{oC z3`&`NZ2p(o?^?{UYH2Mv_Vr3RQZ5_Wt}df}ch>GNW%3zOr>@lOlFGD++bHEYljXmd zq2;_j)OMSSshZf}8FkY*-c?`rEu7k*y;@0iwi=vGw^C!%d08*6!jX&3)mj$Li0dhs zFF9eY6;HBLZXw0c;68Xq%r1O(U2wAm99!5ZVkgx3b#2M(Hp()Ymp#Q^++!TI*@--L zx!c52o%aqi`$-*7eYNZ__I}>_xIA+hWONdG(d7pU7RHM(kAVfcEHJb%ijS$4hl8e#G(l*?XrU3 ziG8xqbWUUY2BIEs1LmuK^%w-#;F)mWPQWlvEaq*bIO}N6y!;4@>crGVrM6W?`%V|!eF%0iT9ZTM(Sk=ux<$n*JSN9$}%UwEyuk2_Sj5qv> z5H_(i^mfGPXSh^%F{B3+XMxQvxKT&9&Z_fqn4&j9FLbe?19e18K3&G%%)g0ip;x!r z2U`)U(?m=BYL988^p=hD!}hAqX@buM`g!(w~LT>N60AvJ+7b(fCOn9(;zS6=rMGojBM z`B(;PQ*U7c$BNv@{!7-v*G2k`G9&V_&ONsme#ZgZ5z%qz%b({BanKk*wjI#wc-j}fw3M| zZalIfsCuwCz?*g;dD+)Y*po1ayiO~P!{P#|aEf%c#rBoorLcxLg_@dW%@N5gZ&}p; z|2U7W0T+tXz;0nfm0LH2A|meeMX7hkN?Ls!JMZ}_;dm;S7Dy-$Dx84Z {}, addEventListener: (eventName: BackPressEventName, handler: Function) => { - debugger; if (_backListeners.indexOf(handler) === -1) { _backListeners.push(handler); } From 6abf9c0e5acd7b4694f246bfdbf721e2cf63b412 Mon Sep 17 00:00:00 2001 From: Drew Higgins Date: Thu, 16 Apr 2020 11:10:45 -0700 Subject: [PATCH 4/9] Change files --- ...-windows-2020-04-16-11-09-41-jahiggin-backHandler.json | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 change/react-native-windows-2020-04-16-11-09-41-jahiggin-backHandler.json diff --git a/change/react-native-windows-2020-04-16-11-09-41-jahiggin-backHandler.json b/change/react-native-windows-2020-04-16-11-09-41-jahiggin-backHandler.json new file mode 100644 index 00000000000..b7b3e0c8d40 --- /dev/null +++ b/change/react-native-windows-2020-04-16-11-09-41-jahiggin-backHandler.json @@ -0,0 +1,8 @@ +{ + "type": "prerelease", + "comment": "Add support for React Native BackHandler API", + "packageName": "react-native-windows", + "email": "jahiggin@microsoft.com", + "dependentChangeType": "patch", + "date": "2020-04-16T18:09:41.547Z" +} \ No newline at end of file From cf8a17c6ab7a6413c1c75ee9fb5629ab9ccc642f Mon Sep 17 00:00:00 2001 From: Drew Higgins Date: Thu, 16 Apr 2020 11:13:45 -0700 Subject: [PATCH 5/9] Remove TODO --- vnext/src/Libraries/Utilities/BackHandler.windows.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/vnext/src/Libraries/Utilities/BackHandler.windows.js b/vnext/src/Libraries/Utilities/BackHandler.windows.js index 55bcf5fbdd7..fe5f5e793c4 100644 --- a/vnext/src/Libraries/Utilities/BackHandler.windows.js +++ b/vnext/src/Libraries/Utilities/BackHandler.windows.js @@ -15,8 +15,6 @@ const RCTDeviceEventEmitter = require('../EventEmitter/RCTDeviceEventEmitter'); -// TODO Hx: Implement. - /** * Detect hardware button presses for back navigation. * From f3f13169e5359aee5a290734489eb0fe64af4eec Mon Sep 17 00:00:00 2001 From: Drew Higgins Date: Thu, 16 Apr 2020 14:43:07 -0700 Subject: [PATCH 6/9] Responding to PR comments * Copied in Android BackHandler code from upstream * Changed the base file for the BackHandler override * Fixed weak ptr check * Removed unnecessary validation after casting XamlView to UIElement * Call JS with legacyPointer --- .../Views/ReactRootControl.cpp | 16 +--- .../Utilities/BackHandler.windows.js | 85 +++++++++++-------- vnext/src/overrides.json | 2 +- 3 files changed, 55 insertions(+), 48 deletions(-) diff --git a/vnext/Microsoft.ReactNative/Views/ReactRootControl.cpp b/vnext/Microsoft.ReactNative/Views/ReactRootControl.cpp index 0d36b6265f2..868117b4b23 100644 --- a/vnext/Microsoft.ReactNative/Views/ReactRootControl.cpp +++ b/vnext/Microsoft.ReactNative/Views/ReactRootControl.cpp @@ -610,11 +610,6 @@ void ReactRootControl::ReloadViewHost() noexcept { void ReactRootControl::AttachBackHandlers(XamlView const &rootView) noexcept { auto rootElement(rootView.as()); - if (rootElement == nullptr) { - assert(false); - return; - } - winrt::Windows::UI::Core::SystemNavigationManager::GetForCurrentView().BackRequested( [this](winrt::IInspectable const & /*sender*/, winrt::BackRequestedEventArgs const &args) { args.Handled(OnBackRequested()); @@ -641,14 +636,9 @@ void ReactRootControl::AttachBackHandlers(XamlView const &rootView) noexcept { } bool ReactRootControl::OnBackRequested() noexcept { - if (m_weakReactInstance.IsExpired()) { - return false; - } - - auto &legacyReactInstance = query_cast(*m_weakReactInstance.GetStrongPtr()); - auto uwpReactInstance = legacyReactInstance.UwpReactInstance(); - if (uwpReactInstance != nullptr) { - uwpReactInstance->CallJsFunction("RCTDeviceEventEmitter", "emit", folly::dynamic::array("hardwareBackPress")); + if (auto reactInstance = m_weakReactInstance.GetStrongPtr()) { + auto &legacyReactInstance = query_cast(*m_weakReactInstance.GetStrongPtr()); + legacyReactInstance.CallJsFunction("RCTDeviceEventEmitter", "emit", folly::dynamic::array("hardwareBackPress")); return true; } diff --git a/vnext/src/Libraries/Utilities/BackHandler.windows.js b/vnext/src/Libraries/Utilities/BackHandler.windows.js index fe5f5e793c4..e6e3d035295 100644 --- a/vnext/src/Libraries/Utilities/BackHandler.windows.js +++ b/vnext/src/Libraries/Utilities/BackHandler.windows.js @@ -4,16 +4,30 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format * @flow + * @format */ -// On Apple TV, this implements back navigation using the TV remote's menu button. -// On iOS, this just implements a stub. - 'use strict'; -const RCTDeviceEventEmitter = require('../EventEmitter/RCTDeviceEventEmitter'); +import NativeDeviceEventManager from '../../Libraries/NativeModules/specs/NativeDeviceEventManager'; +import RCTDeviceEventEmitter from '../EventEmitter/RCTDeviceEventEmitter'; + +const DEVICE_BACK_EVENT = 'hardwareBackPress'; + +type BackPressEventName = 'backPress' | 'hardwareBackPress'; + +const _backPressSubscriptions = []; + +RCTDeviceEventEmitter.addListener(DEVICE_BACK_EVENT, function() { + for (let i = _backPressSubscriptions.length - 1; i >= 0; i--) { + if (_backPressSubscriptions[i]()) { + return; + } + } + + BackHandler.exitApp(); +}); /** * Detect hardware button presses for back navigation. @@ -45,23 +59,6 @@ const RCTDeviceEventEmitter = require('../EventEmitter/RCTDeviceEventEmitter'); * }); * ``` */ - -type BackPressEventName = 'backPress' | 'hardwareBackPress'; - -const DEVICE_BACK_EVENT = 'hardwareBackPress'; - -const _backListeners = []; - -RCTDeviceEventEmitter.addListener(DEVICE_BACK_EVENT, () => { - for (let i = _backListeners.length - 1; i >= 0; i--) { - if (_backListeners[i]()) { - return; - } - } - - BackHandler.exitApp(); -}); - type TBackHandler = {| +exitApp: () => void, +addEventListener: ( @@ -73,25 +70,45 @@ type TBackHandler = {| handler: Function, ) => void, |}; - const BackHandler: TBackHandler = { - exitApp: () => {}, - addEventListener: (eventName: BackPressEventName, handler: Function) => { - if (_backListeners.indexOf(handler) === -1) { - _backListeners.push(handler); + exitApp: function(): void { + if (!NativeDeviceEventManager) { + return; } + NativeDeviceEventManager.invokeDefaultBackPressHandler(); + }, + + /** + * Adds an event handler. Supported events: + * + * - `hardwareBackPress`: Fires when the Android hardware back button is pressed or when the + * tvOS menu button is pressed. + */ + addEventListener: function( + eventName: BackPressEventName, + handler: Function, + ): {remove: () => void, ...} { + if (_backPressSubscriptions.indexOf(handler) === -1) { + _backPressSubscriptions.push(handler); + } return { - remove: () => { - BackHandler.removeEventListener(eventName, handler); - }, + remove: (): void => BackHandler.removeEventListener(eventName, handler), }; }, - removeEventListener: (eventName: BackPressEventName, handler: Function) => { - const handlerIndex = _backListeners.indexOf(handler); - if (handlerIndex !== -1) { - _backListeners.splice(handlerIndex, 1); + /** + * Removes the event handler. + */ + removeEventListener: function( + eventName: BackPressEventName, + handler: Function, + ): void { + if (_backPressSubscriptions.indexOf(handler) !== -1) { + _backPressSubscriptions.splice( + _backPressSubscriptions.indexOf(handler), + 1, + ); } }, }; diff --git a/vnext/src/overrides.json b/vnext/src/overrides.json index 5a9e051c70d..22b1d6ce2ff 100644 --- a/vnext/src/overrides.json +++ b/vnext/src/overrides.json @@ -679,7 +679,7 @@ { "type": "derived", "file": "Libraries\\Utilities\\BackHandler.windows.js", - "baseFile": "Libraries\\Utilities\\BackHandler.ios.js", + "baseFile": "Libraries\\Utilities\\BackHandler.android.js", "baseVersion": "0.62.0-rc.3", "baseHash": "aa2d482b80ae66104d632c75c24646fdcbe916af", "issue": "LEGACY_FIXME" From 05e4608f1669ac2774d5729b0a4ba508c4a378ee Mon Sep 17 00:00:00 2001 From: Drew Higgins Date: Thu, 16 Apr 2020 16:58:09 -0700 Subject: [PATCH 7/9] More PR feedback * Use weak pointers to this in event handlers * Fix safety issue in query_cast * Revoke handlers on Uninit * Remove added binary file --- .../Views/ReactRootControl.cpp | 44 ++++++++++++++---- .../Views/ReactRootControl.h | 3 ++ vnext/ReactUWP/RCa15532 | Bin 4282 -> 0 bytes 3 files changed, 39 insertions(+), 8 deletions(-) delete mode 100644 vnext/ReactUWP/RCa15532 diff --git a/vnext/Microsoft.ReactNative/Views/ReactRootControl.cpp b/vnext/Microsoft.ReactNative/Views/ReactRootControl.cpp index 868117b4b23..846ceaf954f 100644 --- a/vnext/Microsoft.ReactNative/Views/ReactRootControl.cpp +++ b/vnext/Microsoft.ReactNative/Views/ReactRootControl.cpp @@ -233,6 +233,8 @@ void ReactRootControl::UninitRootView() noexcept { m_previewKeyboardEventHandlerOnRoot->unhook(); } + RemoveBackHandlers(); + // If the redbox error UI is shown we need to remove it, otherwise let the // natural teardown process do this if (m_redBoxGrid) { @@ -609,18 +611,30 @@ void ReactRootControl::ReloadViewHost() noexcept { } void ReactRootControl::AttachBackHandlers(XamlView const &rootView) noexcept { - auto rootElement(rootView.as()); - winrt::Windows::UI::Core::SystemNavigationManager::GetForCurrentView().BackRequested( + m_backRequestedRevoker = winrt::Windows::UI::Core::SystemNavigationManager::GetForCurrentView().BackRequested( + winrt::auto_revoke, [this](winrt::IInspectable const & /*sender*/, winrt::BackRequestedEventArgs const &args) { args.Handled(OnBackRequested()); }); + // In addition to handling the BackRequested event, UWP suggests that we listen for other user inputs that should + // trigger back navigation that don't fire that event: + // https://docs.microsoft.com/en-us/windows/uwp/design/basics/navigation-history-and-backwards-navigation + auto rootElement(rootView.try_as()); + if (rootElement == nullptr) { + assert(false); + return; + } + // Handle keyboard "back" button press winrt::KeyboardAccelerator goBack{}; goBack.Key(winrt::VirtualKey::GoBack); + auto weakThis = weak_from_this(); goBack.Invoked( - [this](winrt::KeyboardAccelerator const & /*sender*/, winrt::KeyboardAcceleratorInvokedEventArgs const &args) { - args.Handled(OnBackRequested()); + [weakThis](winrt::KeyboardAccelerator const & /*sender*/, winrt::KeyboardAcceleratorInvokedEventArgs const &args) { + if (auto self = weakThis.lock()) { + args.Handled(self->OnBackRequested()); + } }); rootElement.KeyboardAccelerators().Append(goBack); @@ -628,17 +642,31 @@ void ReactRootControl::AttachBackHandlers(XamlView const &rootView) noexcept { winrt::KeyboardAccelerator altLeft{}; altLeft.Key(winrt::VirtualKey::Left); altLeft.Invoked( - [this](winrt::KeyboardAccelerator const & /*sender*/, winrt::KeyboardAcceleratorInvokedEventArgs const &args) { - args.Handled(OnBackRequested()); + [weakThis](winrt::KeyboardAccelerator const & /*sender*/, winrt::KeyboardAcceleratorInvokedEventArgs const &args) { + if (auto self = weakThis.lock()) { + args.Handled(self->OnBackRequested()); + } }); rootElement.KeyboardAccelerators().Append(altLeft); altLeft.Modifiers(winrt::VirtualKeyModifiers::Menu); + + // Hide keyboard accelerator tooltips + rootElement.KeyboardAcceleratorPlacementMode(winrt::KeyboardAcceleratorPlacementMode::Hidden); +} + +void ReactRootControl::RemoveBackHandlers() noexcept { + m_backRequestedRevoker.revoke(); + if (auto rootView = m_weakRootView.get()) { + if(auto element = rootView.try_as()) { + element.KeyboardAccelerators().Clear(); + } + } } bool ReactRootControl::OnBackRequested() noexcept { if (auto reactInstance = m_weakReactInstance.GetStrongPtr()) { - auto &legacyReactInstance = query_cast(*m_weakReactInstance.GetStrongPtr()); - legacyReactInstance.CallJsFunction("RCTDeviceEventEmitter", "emit", folly::dynamic::array("hardwareBackPress")); + query_cast(*reactInstance) + .CallJsFunction("RCTDeviceEventEmitter", "emit", folly::dynamic::array("hardwareBackPress")); return true; } diff --git a/vnext/Microsoft.ReactNative/Views/ReactRootControl.h b/vnext/Microsoft.ReactNative/Views/ReactRootControl.h index e543b40b178..26bb5948cc2 100644 --- a/vnext/Microsoft.ReactNative/Views/ReactRootControl.h +++ b/vnext/Microsoft.ReactNative/Views/ReactRootControl.h @@ -15,6 +15,7 @@ namespace winrt { using namespace Windows::UI; +using namespace Windows::UI::Core; using namespace Windows::UI::Xaml; using namespace Windows::UI::Xaml::Controls; using namespace Windows::UI::Xaml::Input; @@ -84,6 +85,7 @@ struct ReactRootControl final : std::enable_shared_from_this, void ReloadViewHost() noexcept; void AttachBackHandlers(XamlView const &rootView) noexcept; + void RemoveBackHandlers() noexcept; bool OnBackRequested() noexcept; private: @@ -133,6 +135,7 @@ struct ReactRootControl final : std::enable_shared_from_this, winrt::Button::Click_revoker m_directDebuggingRevoker{}; winrt::Button::Click_revoker m_breakOnNextLineRevoker{}; winrt::CoreDispatcher::AcceleratorKeyActivated_revoker m_coreDispatcherAKARevoker{}; + winrt::SystemNavigationManager::BackRequested_revoker m_backRequestedRevoker{}; }; //! This class ensures that we access ReactRootView from UI thread. diff --git a/vnext/ReactUWP/RCa15532 b/vnext/ReactUWP/RCa15532 deleted file mode 100644 index d104d891d3107379e0c8ba563c714619cb454b55..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4282 zcmc(iYi}Ay6o${|O8pNb^2Ls@lh{tGR8^%AOzQ?;WMgQnN(dW5ksOG)Bu;;Qo_A)K z*@ZRh%ZCbiJv(zQ@4V;CnX}LA#zr=_fjN6+6|39N_7A@)Gd;Vux$Usp&{D&Fgxn_Q0ee|6D zlK7PWDcb+quWP#d$&55UE_d{&U04f5M|NVr@%-BkZ3`URSiNf{V949KOV)vZwua?R zGBW~6u;ajR0vlcU_yCfwwLx+Yh9f&=rJJBB@*O0*)Z4f57BX56{oC z3`&`NZ2p(o?^?{UYH2Mv_Vr3RQZ5_Wt}df}ch>GNW%3zOr>@lOlFGD++bHEYljXmd zq2;_j)OMSSshZf}8FkY*-c?`rEu7k*y;@0iwi=vGw^C!%d08*6!jX&3)mj$Li0dhs zFF9eY6;HBLZXw0c;68Xq%r1O(U2wAm99!5ZVkgx3b#2M(Hp()Ymp#Q^++!TI*@--L zx!c52o%aqi`$-*7eYNZ__I}>_xIA+hWONdG(d7pU7RHM(kAVfcEHJb%ijS$4hl8e#G(l*?XrU3 ziG8xqbWUUY2BIEs1LmuK^%w-#;F)mWPQWlvEaq*bIO}N6y!;4@>crGVrM6W?`%V|!eF%0iT9ZTM(Sk=ux<$n*JSN9$}%UwEyuk2_Sj5qv> z5H_(i^mfGPXSh^%F{B3+XMxQvxKT&9&Z_fqn4&j9FLbe?19e18K3&G%%)g0ip;x!r z2U`)U(?m=BYL988^p=hD!}hAqX@buM`g!(w~LT>N60AvJ+7b(fCOn9(;zS6=rMGojBM z`B(;PQ*U7c$BNv@{!7-v*G2k`G9&V_&ONsme#ZgZ5z%qz%b({BanKk*wjI#wc-j}fw3M| zZalIfsCuwCz?*g;dD+)Y*po1ayiO~P!{P#|aEf%c#rBoorLcxLg_@dW%@N5gZ&}p; z|2U7W0T+tXz;0nfm0LH2A|meeMX7hkN?Ls!JMZ}_;dm;S7Dy-$Dx84Z Date: Thu, 16 Apr 2020 17:02:15 -0700 Subject: [PATCH 8/9] use weak ptr for BackRequested too --- .../Views/ReactRootControl.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/vnext/Microsoft.ReactNative/Views/ReactRootControl.cpp b/vnext/Microsoft.ReactNative/Views/ReactRootControl.cpp index 846ceaf954f..bd5a340f130 100644 --- a/vnext/Microsoft.ReactNative/Views/ReactRootControl.cpp +++ b/vnext/Microsoft.ReactNative/Views/ReactRootControl.cpp @@ -611,10 +611,13 @@ void ReactRootControl::ReloadViewHost() noexcept { } void ReactRootControl::AttachBackHandlers(XamlView const &rootView) noexcept { + auto weakThis = weak_from_this(); m_backRequestedRevoker = winrt::Windows::UI::Core::SystemNavigationManager::GetForCurrentView().BackRequested( winrt::auto_revoke, - [this](winrt::IInspectable const & /*sender*/, winrt::BackRequestedEventArgs const &args) { - args.Handled(OnBackRequested()); + [weakThis](winrt::IInspectable const & /*sender*/, winrt::BackRequestedEventArgs const &args) { + if (auto self = weakThis.lock()) { + args.Handled(self->OnBackRequested()); + } }); // In addition to handling the BackRequested event, UWP suggests that we listen for other user inputs that should @@ -629,9 +632,9 @@ void ReactRootControl::AttachBackHandlers(XamlView const &rootView) noexcept { // Handle keyboard "back" button press winrt::KeyboardAccelerator goBack{}; goBack.Key(winrt::VirtualKey::GoBack); - auto weakThis = weak_from_this(); goBack.Invoked( - [weakThis](winrt::KeyboardAccelerator const & /*sender*/, winrt::KeyboardAcceleratorInvokedEventArgs const &args) { + [weakThis]( + winrt::KeyboardAccelerator const & /*sender*/, winrt::KeyboardAcceleratorInvokedEventArgs const &args) { if (auto self = weakThis.lock()) { args.Handled(self->OnBackRequested()); } @@ -642,7 +645,8 @@ void ReactRootControl::AttachBackHandlers(XamlView const &rootView) noexcept { winrt::KeyboardAccelerator altLeft{}; altLeft.Key(winrt::VirtualKey::Left); altLeft.Invoked( - [weakThis](winrt::KeyboardAccelerator const & /*sender*/, winrt::KeyboardAcceleratorInvokedEventArgs const &args) { + [weakThis]( + winrt::KeyboardAccelerator const & /*sender*/, winrt::KeyboardAcceleratorInvokedEventArgs const &args) { if (auto self = weakThis.lock()) { args.Handled(self->OnBackRequested()); } @@ -657,7 +661,7 @@ void ReactRootControl::AttachBackHandlers(XamlView const &rootView) noexcept { void ReactRootControl::RemoveBackHandlers() noexcept { m_backRequestedRevoker.revoke(); if (auto rootView = m_weakRootView.get()) { - if(auto element = rootView.try_as()) { + if (auto element = rootView.try_as()) { element.KeyboardAccelerators().Clear(); } } From a4362cced4af364f028eddcd24d89a785b2a26cc Mon Sep 17 00:00:00 2001 From: Drew Higgins Date: Fri, 17 Apr 2020 13:32:15 -0700 Subject: [PATCH 9/9] Fix override hash verification --- packages/override-tools/src/Manifest.ts | 3 ++- vnext/src/overrides.json | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/override-tools/src/Manifest.ts b/packages/override-tools/src/Manifest.ts index 78e872cf3d3..3c362767642 100644 --- a/packages/override-tools/src/Manifest.ts +++ b/packages/override-tools/src/Manifest.ts @@ -242,7 +242,8 @@ export default class Manifest { */ static hashContent(str: string) { const hasher = crypto.createHash('sha1'); - hasher.update(str); + const normalizedStr = str.replace(/(?