diff --git a/packages/core/references.d.ts b/packages/core/references.d.ts index db2b50e87e..4300b64aee 100644 --- a/packages/core/references.d.ts +++ b/packages/core/references.d.ts @@ -4,7 +4,7 @@ /// /// /// -/// +/// /// /// /// diff --git a/packages/core/ui/core/view/index.android.ts b/packages/core/ui/core/view/index.android.ts index 74fc92bde6..aa83cfaf64 100644 --- a/packages/core/ui/core/view/index.android.ts +++ b/packages/core/ui/core/view/index.android.ts @@ -72,6 +72,51 @@ interface DialogOptions { dismissCallback: () => void; } +let OnBackPressedCallback; + +if (SDK_VERSION >= 33) { + OnBackPressedCallback = (androidx.activity.OnBackPressedCallback as any).extend({ + handleOnBackPressed() { + console.log('OnBackPressedCallback handleOnBackPressed called'); + const dialog = this['_dialog']?.get(); + + if (!dialog) { + // disable the callback and call super to avoid infinite loop + + this.setEnabled(false); + + return; + } + + const view = dialog.fragment.owner; + + const args: AndroidActivityBackPressedEventData = { + eventName: 'activityBackPressed', + object: view, + activity: view._context, + cancel: false, + }; + + // Fist fire application.android global event + getNativeScriptGlobals().events.notify(args); + + if (args.cancel) { + return; + } + + view.notify(args); + + if (!args.cancel) { + this.setEnabled(false); + + dialog.getOnBackPressedDispatcher().onBackPressed(); + + this.setEnabled(true); + } + }, + }); +} + interface TouchListener { new (owner: View): android.view.View.OnTouchListener; } @@ -121,7 +166,7 @@ function initializeDialogFragment() { } @NativeClass - class DialogImpl extends android.app.Dialog { + class DialogImpl extends androidx.appcompat.app.AppCompatDialog { constructor( public fragment: DialogFragmentImpl, context: android.content.Context, @@ -129,6 +174,16 @@ function initializeDialogFragment() { ) { super(context, themeResId); + if (SDK_VERSION >= 33 && OnBackPressedCallback) { + const callback = new OnBackPressedCallback(true); + + callback['_dialog'] = new WeakRef(this); + + // @ts-ignore + + this.getOnBackPressedDispatcher().addCallback(this, callback); + } + return global.__native(this); } @@ -138,6 +193,10 @@ function initializeDialogFragment() { } public onBackPressed(): void { + if (SDK_VERSION >= 33) { + super.onBackPressed(); + return; + } const view = this.fragment.owner; const args = { eventName: 'activityBackPressed', diff --git a/packages/core/ui/frame/index.android.ts b/packages/core/ui/frame/index.android.ts index a8838b7e8d..168c81ac52 100644 --- a/packages/core/ui/frame/index.android.ts +++ b/packages/core/ui/frame/index.android.ts @@ -17,6 +17,7 @@ import { AndroidActivityBackPressedEventData, AndroidActivityNewIntentEventData, import { Application } from '../../application/application'; import { isEmbedded, setEmbeddedView } from '../embedding'; import { CALLBACKS, FRAMEID, framesCache, setFragmentCallbacks } from './frame-helper-for-android'; +import { Device } from '../../platform'; export * from './frame-common'; export { setFragmentClass } from './fragment'; @@ -762,6 +763,78 @@ function startActivity(activity: androidx.appcompat.app.AppCompatActivity, frame activity.startActivity(intent); } +let OnBackPressedCallback; + +if (parseInt(Device.sdkVersion) >= 33) { + OnBackPressedCallback = (androidx.activity.OnBackPressedCallback).extend('com.tns.OnBackPressedCallback', { + handleOnBackPressed() { + if (Trace.isEnabled()) { + Trace.write('NativeScriptActivity.onBackPressed;', Trace.categories.NativeLifecycle); + } + + const activity = this['_activity']?.get(); + + if (!activity) { + if (Trace.isEnabled()) { + Trace.write('NativeScriptActivity.onBackPressed; Activity is null, calling super', Trace.categories.NativeLifecycle); + } + + this.setEnabled(false); + + return; + } + + const args = { + eventName: 'activityBackPressed', + + object: Application, + + android: Application.android, + + activity: activity, + + cancel: false, + }; + + Application.android.notify(args); + + if (args.cancel) { + return; + } + + const view = activity._rootView; + + let callSuper = false; + + const viewArgs = { + eventName: 'activityBackPressed', + + object: view, + + activity: activity, + + cancel: false, + }; + + view?.notify(viewArgs); + + // In the case of Frame, use this callback only if it was overridden, since the original will cause navigation issues + + if (!viewArgs.cancel && (view?.onBackPressed === Frame.prototype.onBackPressed || !view?.onBackPressed())) { + callSuper = view instanceof Frame ? !Frame.goBack() : true; + } + + if (callSuper) { + this.setEnabled(false); + + activity.getOnBackPressedDispatcher().onBackPressed(); + + this.setEnabled(true); + } + }, + }); +} + const activityRootViewsMap = new Map>(); const ROOT_VIEW_ID_EXTRA = 'com.tns.activity.rootViewId'; @@ -1055,4 +1128,13 @@ export class ActivityCallbacksImplementation implements AndroidActivityCallbacks export function setActivityCallbacks(activity: androidx.appcompat.app.AppCompatActivity): void { activity[CALLBACKS] = new ActivityCallbacksImplementation(); + if (OnBackPressedCallback && !activity['_onBackPressed']) { + const callback = new OnBackPressedCallback(true); + + callback['_activity'] = new WeakRef(activity); + + activity['_onBackPressed'] = true; + + (activity as androidx.activity.ComponentActivity).getOnBackPressedDispatcher().addCallback(activity, callback); + } }