mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-11-05 13:26:48 +08:00
feat: activityBackPressed handling with OnBackPressedCallback
This commit is contained in:
@@ -24,7 +24,8 @@ import * as Utils from '../../../utils';
|
|||||||
import { SDK_VERSION } from '../../../utils/constants';
|
import { SDK_VERSION } from '../../../utils/constants';
|
||||||
import { BoxShadow } from '../../styling/box-shadow';
|
import { BoxShadow } from '../../styling/box-shadow';
|
||||||
import { NativeScriptAndroidView } from '../../utils';
|
import { NativeScriptAndroidView } from '../../utils';
|
||||||
|
import { Device } from '../../../platform';
|
||||||
|
import { Frame } from '../../frame';
|
||||||
export * from './view-common';
|
export * from './view-common';
|
||||||
// helpers (these are okay re-exported here)
|
// helpers (these are okay re-exported here)
|
||||||
export * from './view-helper';
|
export * from './view-helper';
|
||||||
@@ -52,6 +53,42 @@ const GRAVITY_FILL_VERTICAL = 112; // android.view.Gravity.FILL_VERTICAL
|
|||||||
|
|
||||||
const modalMap = new Map<number, DialogOptions>();
|
const modalMap = new Map<number, DialogOptions>();
|
||||||
|
|
||||||
|
let OnBackPressedCallback;
|
||||||
|
if (parseInt(Device.sdkVersion) >= 33) {
|
||||||
|
OnBackPressedCallback = (<any>androidx.activity.OnBackPressedCallback).extend({
|
||||||
|
handleOnBackPressed() {
|
||||||
|
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
|
||||||
|
Application.android.notify(args);
|
||||||
|
if (args.cancel) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
view.notify(args);
|
||||||
|
|
||||||
|
if (!args.cancel) {
|
||||||
|
this.setEnabled(false);
|
||||||
|
dialog.getOnBackPressedDispatcher().onBackPressed();
|
||||||
|
this.setEnabled(true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
let TouchListener: TouchListener;
|
let TouchListener: TouchListener;
|
||||||
let DialogFragment: DialogFragment;
|
let DialogFragment: DialogFragment;
|
||||||
|
|
||||||
@@ -115,13 +152,21 @@ function initializeDialogFragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NativeClass
|
@NativeClass
|
||||||
class DialogImpl extends android.app.Dialog {
|
class DialogImpl extends androidx.appcompat.app.AppCompatDialog {
|
||||||
constructor(
|
constructor(
|
||||||
public fragment: DialogFragmentImpl,
|
public fragment: DialogFragmentImpl,
|
||||||
context: android.content.Context,
|
context: android.content.Context,
|
||||||
themeResId: number,
|
themeResId: number,
|
||||||
) {
|
) {
|
||||||
super(context, themeResId);
|
super(context, themeResId);
|
||||||
|
// @ts-ignore
|
||||||
|
|
||||||
|
if (parseInt(Device.sdkVersion) >= 33 && OnBackPressedCallback) {
|
||||||
|
const callback = new OnBackPressedCallback(true);
|
||||||
|
callback['_dialog'] = new WeakRef(this);
|
||||||
|
// @ts-ignore
|
||||||
|
this.getOnBackPressedDispatcher().addCallback(this, callback);
|
||||||
|
}
|
||||||
|
|
||||||
return global.__native(this);
|
return global.__native(this);
|
||||||
}
|
}
|
||||||
@@ -132,6 +177,10 @@ function initializeDialogFragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public onBackPressed(): void {
|
public onBackPressed(): void {
|
||||||
|
if (parseInt(Device.sdkVersion) >= 33) {
|
||||||
|
super.onBackPressed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
const view = this.fragment.owner;
|
const view = this.fragment.owner;
|
||||||
const args = <AndroidActivityBackPressedEventData>{
|
const args = <AndroidActivityBackPressedEventData>{
|
||||||
eventName: 'activityBackPressed',
|
eventName: 'activityBackPressed',
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { Page } from '../page';
|
|||||||
import { TransitionState } from './frame-common';
|
import { TransitionState } from './frame-common';
|
||||||
|
|
||||||
// Types.
|
// Types.
|
||||||
import { Application } from '../../application';
|
import { Application, AndroidActivityBackPressedEventData } from '../../application';
|
||||||
|
|
||||||
import { Observable } from '../../data/observable';
|
import { Observable } from '../../data/observable';
|
||||||
import { Trace } from '../../trace';
|
import { Trace } from '../../trace';
|
||||||
@@ -19,6 +19,7 @@ import type { ExpandedEntry } from './fragment.transitions.android';
|
|||||||
import { ensureFragmentClass, fragmentClass } from './fragment';
|
import { ensureFragmentClass, fragmentClass } from './fragment';
|
||||||
import { FragmentCallbacksImplementation } from './callbacks/fragment-callbacks';
|
import { FragmentCallbacksImplementation } from './callbacks/fragment-callbacks';
|
||||||
import { ActivityCallbacksImplementation } from './callbacks/activity-callbacks';
|
import { ActivityCallbacksImplementation } from './callbacks/activity-callbacks';
|
||||||
|
import { Device } from '../../platform';
|
||||||
|
|
||||||
export * from './frame-common';
|
export * from './frame-common';
|
||||||
export { setFragmentClass } from './fragment';
|
export { setFragmentClass } from './fragment';
|
||||||
@@ -782,8 +783,69 @@ export function getFrameByNumberId(frameId: number): Frame {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let OnBackPressedCallback;
|
||||||
|
if (parseInt(Device.sdkVersion) >= 33) {
|
||||||
|
OnBackPressedCallback = (<any>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 = <AndroidActivityBackPressedEventData>{
|
||||||
|
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 = <AndroidActivityBackPressedEventData>{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function setActivityCallbacks(activity: androidx.appcompat.app.AppCompatActivity): void {
|
export function setActivityCallbacks(activity: androidx.appcompat.app.AppCompatActivity): void {
|
||||||
activity[CALLBACKS] = new ActivityCallbacksImplementation();
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setFragmentCallbacks(fragment: androidx.fragment.app.Fragment): void {
|
export function setFragmentCallbacks(fragment: androidx.fragment.app.Fragment): void {
|
||||||
|
|||||||
@@ -205,7 +205,7 @@ declare module androidx {
|
|||||||
|
|
||||||
declare module androidx {
|
declare module androidx {
|
||||||
export module activity {
|
export module activity {
|
||||||
export class ComponentDialog implements androidx.activity.OnBackPressedDispatcherOwner {
|
export class ComponentDialog extends android.app.Dialog implements androidx.activity.OnBackPressedDispatcherOwner {
|
||||||
public static class: java.lang.Class<androidx.activity.ComponentDialog>;
|
public static class: java.lang.Class<androidx.activity.ComponentDialog>;
|
||||||
public onStart(): void;
|
public onStart(): void;
|
||||||
public constructor(context: globalAndroid.content.Context);
|
public constructor(context: globalAndroid.content.Context);
|
||||||
|
|||||||
Reference in New Issue
Block a user