refactor: circular deps part 13

This commit is contained in:
Nathan Walker
2025-07-09 20:07:56 -07:00
parent ee03774ec0
commit 579a25d583
31 changed files with 552 additions and 387 deletions

View File

@ -240,7 +240,7 @@ export class LabelTest extends testModule.UITest<Label> {
testLabel.textWrap = true;
this.waitUntilTestElementLayoutIsValid();
if (isAndroid) {
if (__ANDROID__) {
TKUnit.assertNull(testLabel.android.getEllipsize());
TKUnit.assertTrue(testLabel.android.getLineCount() > 1, 'LinesNumber');
const actualHorizontalScrolling = testLabel.android.canScrollHorizontally(-1) || testLabel.android.canScrollHorizontally(1);
@ -262,7 +262,7 @@ export class LabelTest extends testModule.UITest<Label> {
testLabel.textWrap = false;
this.waitUntilTestElementLayoutIsValid();
if (isAndroid) {
if (__ANDROID__) {
TKUnit.assertEqual(testLabel.android.getEllipsize(), android.text.TextUtils.TruncateAt.END, 'Ellipsize');
const actualHorizontalScrolling = testLabel.android.canScrollHorizontally(-1) || testLabel.android.canScrollHorizontally(1);
TKUnit.assertEqual(actualHorizontalScrolling, false, 'HorizontalScrolling');

View File

@ -1,10 +1,10 @@
import * as common from './application-settings-common';
import { Application } from '../application';
import { getNativeApp } from '../application/helpers-common';
let sharedPreferences: android.content.SharedPreferences;
function ensureSharedPreferences() {
if (!sharedPreferences) {
sharedPreferences = Application.android.getNativeApplication().getApplicationContext().getSharedPreferences('prefs.db', 0);
sharedPreferences = (getNativeApp() as android.app.Application).getApplicationContext().getSharedPreferences('prefs.db', 0);
}
}

View File

@ -13,6 +13,7 @@ import type { StyleScope } from '../ui/styling/style-scope';
import type { AndroidApplication as AndroidApplicationType, iOSApplication as iOSApplicationType } from '.';
import type { ApplicationEventData, CssChangedEventData, DiscardedErrorEventData, FontScaleChangedEventData, InitRootViewEventData, LaunchEventData, LoadAppCSSEventData, NativeScriptError, OrientationChangedEventData, SystemAppearanceChangedEventData, UnhandledErrorEventData } from './application-interfaces';
import { readyInitAccessibilityCssHelper, readyInitFontScale } from '../accessibility/accessibility-common';
import { isAppInBackground, setAppInBackground } from './helpers-common';
// prettier-ignore
const ORIENTATION_CSS_CLASSES = [
@ -575,14 +576,12 @@ export class ApplicationCommon {
rootView._onCssStateChange();
}
private _inBackground: boolean = false;
get inBackground() {
return this._inBackground;
return isAppInBackground();
}
setInBackground(value: boolean, additonalData?: any) {
this._inBackground = value;
setAppInBackground(value);
this.notify(<ApplicationEventData>{
eventName: value ? this.backgroundEvent : this.foregroundEvent,

View File

@ -1,4 +1,3 @@
import type { ApplicationCommon } from './application-common';
import type { EventData, Observable } from '../data/observable';
import type { View } from '../ui/core/view';
@ -42,7 +41,7 @@ export interface ApplicationEventData {
/**
* The instance that has raised the event.
*/
object: ApplicationCommon | Observable;
object: any; // Application;
}
/**

View File

@ -43,6 +43,8 @@ import {
isA11yEnabled,
setA11yEnabled,
} from '../accessibility/accessibility-common';
import { androidGetForegroundActivity, androidGetStartActivity, androidPendingReceiverRegistrations, androidRegisterBroadcastReceiver, androidRegisteredReceivers, androidSetForegroundActivity, androidSetStartActivity, androidUnregisterBroadcastReceiver, applyContentDescription } from './helpers';
import { getRootView, setA11yUpdatePropertiesCallback, setApplicationPropertiesCallback, setNativeApp, setRootView, setToggleApplicationEventListenersCallback } from './helpers-common';
declare namespace com {
namespace tns {
@ -58,38 +60,6 @@ declare namespace com {
}
}
declare class BroadcastReceiver extends android.content.BroadcastReceiver {
constructor(onReceiveCallback: (context: android.content.Context, intent: android.content.Intent) => void);
}
let BroadcastReceiver_: typeof BroadcastReceiver;
function initBroadcastReceiver() {
if (BroadcastReceiver_) {
return BroadcastReceiver_;
}
@NativeClass
class BroadcastReceiverImpl extends android.content.BroadcastReceiver {
private _onReceiveCallback: (context: android.content.Context, intent: android.content.Intent) => void;
constructor(onReceiveCallback: (context: android.content.Context, intent: android.content.Intent) => void) {
super();
this._onReceiveCallback = onReceiveCallback;
return global.__native(this);
}
public onReceive(context: android.content.Context, intent: android.content.Intent) {
if (this._onReceiveCallback) {
this._onReceiveCallback(context, intent);
}
}
}
BroadcastReceiver_ = BroadcastReceiverImpl;
return BroadcastReceiver_;
}
declare class NativeScriptLifecycleCallbacks extends android.app.Application.ActivityLifecycleCallbacks {}
let NativeScriptLifecycleCallbacks_: typeof NativeScriptLifecycleCallbacks;
@ -365,6 +335,7 @@ export class AndroidApplication extends ApplicationCommon {
}
this._nativeApp = nativeApp;
setNativeApp(nativeApp);
this._context = nativeApp.getApplicationContext();
this._packageName = nativeApp.getPackageName();
@ -378,11 +349,9 @@ export class AndroidApplication extends ApplicationCommon {
this._registerPendingReceivers();
}
private _registeredReceivers = {};
private _pendingReceiverRegistrations = new Array<(context: android.content.Context) => void>();
private _registerPendingReceivers() {
this._pendingReceiverRegistrations.forEach((func) => func(this.context));
this._pendingReceiverRegistrations.length = 0;
androidPendingReceiverRegistrations.forEach((func) => func(this.context));
androidPendingReceiverRegistrations.length = 0;
}
onConfigurationChanged(configuration: android.content.res.Configuration): void {
@ -445,23 +414,20 @@ export class AndroidApplication extends ApplicationCommon {
}
}
private _startActivity: androidx.appcompat.app.AppCompatActivity;
private _foregroundActivity: androidx.appcompat.app.AppCompatActivity;
get startActivity() {
return this._startActivity;
return androidGetStartActivity();
}
get foregroundActivity() {
return this._foregroundActivity;
return androidGetForegroundActivity();
}
setStartActivity(value: androidx.appcompat.app.AppCompatActivity) {
this._startActivity = value;
androidSetStartActivity(value);
}
setForegroundActivity(value: androidx.appcompat.app.AppCompatActivity) {
this._foregroundActivity = value;
androidSetForegroundActivity(value);
}
get paused(): boolean {
@ -485,34 +451,15 @@ export class AndroidApplication extends ApplicationCommon {
// RECEIVER_NOT_EXPORTED (4)
// RECEIVER_VISIBLE_TO_INSTANT_APPS (1)
public registerBroadcastReceiver(intentFilter: string, onReceiveCallback: (context: android.content.Context, intent: android.content.Intent) => void, flags = 2): void {
const registerFunc = (context: android.content.Context) => {
const receiver: android.content.BroadcastReceiver = new (initBroadcastReceiver())(onReceiveCallback);
if (SDK_VERSION >= 26) {
context.registerReceiver(receiver, new android.content.IntentFilter(intentFilter), flags);
} else {
context.registerReceiver(receiver, new android.content.IntentFilter(intentFilter));
}
this._registeredReceivers[intentFilter] = receiver;
};
if (this.context) {
registerFunc(this.context);
} else {
this._pendingReceiverRegistrations.push(registerFunc);
}
androidRegisterBroadcastReceiver(intentFilter, onReceiveCallback, flags);
}
public unregisterBroadcastReceiver(intentFilter: string): void {
const receiver = this._registeredReceivers[intentFilter];
if (receiver) {
this.context.unregisterReceiver(receiver);
this._registeredReceivers[intentFilter] = undefined;
delete this._registeredReceivers[intentFilter];
}
androidUnregisterBroadcastReceiver(intentFilter);
}
public getRegisteredBroadcastReceiver(intentFilter: string): android.content.BroadcastReceiver | undefined {
return this._registeredReceivers[intentFilter];
return androidRegisteredReceivers[intentFilter];
}
getRootView(): View {
@ -522,7 +469,8 @@ export class AndroidApplication extends ApplicationCommon {
}
const callbacks: AndroidActivityCallbacks = activity['_callbacks'];
return callbacks ? callbacks.getRootView() : undefined;
setRootView(callbacks ? callbacks.getRootView() : undefined);
return getRootView();
}
resetRootView(entry?: NavigationEntry | string): void {
@ -1298,10 +1246,6 @@ export function isAccessibilityServiceEnabled(): boolean {
return accessibilityServiceEnabled;
}
export function setupAccessibleView(view: View): void {
updateAccessibilityProperties(view);
}
let updateAccessibilityPropertiesMicroTask;
let pendingViews = new Set<View>();
export function updateAccessibilityProperties(view: View) {
@ -1325,6 +1269,7 @@ export function updateAccessibilityProperties(view: View) {
_pendingViews = [];
});
}
setA11yUpdatePropertiesCallback(updateAccessibilityProperties);
export function sendAccessibilityEvent(view: View, eventType: AndroidAccessibilityEvent, text?: string): void {
if (!isAccessibilityServiceEnabled()) {
@ -1405,14 +1350,6 @@ export function sendAccessibilityEvent(view: View, eventType: AndroidAccessibili
accessibilityManager.sendAccessibilityEvent(accessibilityEvent);
}
export function updateContentDescription(view: View, forceUpdate?: boolean): string | null {
if (!view.nativeViewProtected) {
return;
}
return applyContentDescription(view, forceUpdate);
}
function setAccessibilityDelegate(view: View): void {
if (!view.nativeViewProtected) {
return;
@ -1439,105 +1376,21 @@ function setAccessibilityDelegate(view: View): void {
androidView.setAccessibilityDelegate(TNSAccessibilityDelegate);
}
function applyContentDescription(view: View, forceUpdate?: boolean) {
let androidView = view.nativeViewProtected as android.view.View;
if (!androidView || (androidView instanceof android.widget.TextView && !view._androidContentDescriptionUpdated)) {
return null;
}
if (androidView instanceof androidx.appcompat.widget.Toolbar) {
const numChildren = androidView.getChildCount();
for (let i = 0; i < numChildren; i += 1) {
const childAndroidView = androidView.getChildAt(i);
if (childAndroidView instanceof androidx.appcompat.widget.AppCompatTextView) {
androidView = childAndroidView;
break;
}
}
}
const cls = `applyContentDescription(${view})`;
const titleValue = view['title'] as string;
const textValue = view['text'] as string;
if (!forceUpdate && view._androidContentDescriptionUpdated === false && textValue === view['_lastText'] && titleValue === view['_lastTitle']) {
// prevent updating this too much
return androidView.getContentDescription();
}
const contentDescriptionBuilder = new Array<string>();
// Workaround: TalkBack won't read the checked state for fake Switch.
if (view.accessibilityRole === AccessibilityRole.Switch) {
const androidSwitch = new android.widget.Switch(Application.android.context);
if (view.accessibilityState === AccessibilityState.Checked) {
contentDescriptionBuilder.push(androidSwitch.getTextOn());
const applicationEvents: string[] = [Application.orientationChangedEvent, Application.systemAppearanceChangedEvent];
function toggleApplicationEventListeners(toAdd: boolean, callback: (args: ApplicationEventData) => void) {
for (const eventName of applicationEvents) {
if (toAdd) {
Application.on(eventName, callback);
} else {
contentDescriptionBuilder.push(androidSwitch.getTextOff());
Application.off(eventName, callback);
}
}
if (view.accessibilityLabel) {
if (Trace.isEnabled()) {
Trace.write(`${cls} - have accessibilityLabel`, Trace.categories.Accessibility);
}
contentDescriptionBuilder.push(`${view.accessibilityLabel}`);
}
if (view.accessibilityValue) {
if (Trace.isEnabled()) {
Trace.write(`${cls} - have accessibilityValue`, Trace.categories.Accessibility);
}
contentDescriptionBuilder.push(`${view.accessibilityValue}`);
} else if (textValue) {
if (textValue !== view.accessibilityLabel) {
if (Trace.isEnabled()) {
Trace.write(`${cls} - don't have accessibilityValue - use 'text' value`, Trace.categories.Accessibility);
}
contentDescriptionBuilder.push(`${textValue}`);
}
} else if (titleValue) {
if (titleValue !== view.accessibilityLabel) {
if (Trace.isEnabled()) {
Trace.write(`${cls} - don't have accessibilityValue - use 'title' value`, Trace.categories.Accessibility);
}
contentDescriptionBuilder.push(`${titleValue}`);
}
}
if (view.accessibilityHint) {
if (Trace.isEnabled()) {
Trace.write(`${cls} - have accessibilityHint`, Trace.categories.Accessibility);
}
contentDescriptionBuilder.push(`${view.accessibilityHint}`);
}
const contentDescription = contentDescriptionBuilder.join('. ').trim().replace(/^\.$/, '');
if (contentDescription) {
if (Trace.isEnabled()) {
Trace.write(`${cls} - set to "${contentDescription}"`, Trace.categories.Accessibility);
}
androidView.setContentDescription(contentDescription);
} else {
if (Trace.isEnabled()) {
Trace.write(`${cls} - remove value`, Trace.categories.Accessibility);
}
androidView.setContentDescription(null);
}
view['_lastTitle'] = titleValue;
view['_lastText'] = textValue;
view._androidContentDescriptionUpdated = false;
return contentDescription;
}
setToggleApplicationEventListenersCallback(toggleApplicationEventListeners);
setApplicationPropertiesCallback(() => {
return {
orientation: Application.orientation(),
systemAppearance: Application.systemAppearance(),
};
});

View File

@ -198,11 +198,6 @@ export const VALID_FONT_SCALES: number[];
export function getCurrentFontScale(): number;
export function getAndroidAccessibilityManager(): android.view.accessibility.AccessibilityManager | null;
/**
* Initialize accessibility for View. This should be called on loaded-event.
*/
export function setupAccessibleView(view: View): void;
/**
* Update accessibility properties on nativeView
*/
@ -213,11 +208,6 @@ export function updateAccessibilityProperties(view: View): void;
*/
export function sendAccessibilityEvent(View: View, eventName: AndroidAccessibilityEvent, text?: string): void;
/**
* Android: Update the content description for views
*/
export function updateContentDescription(View: View, forceUpdate?: boolean): string | null;
/**
* Is Android TalkBack or iOS VoiceOver enabled?
*/

View File

@ -45,6 +45,8 @@ import {
setA11yEnabled,
enforceArray,
} from '../accessibility/accessibility-common';
import { iosAddNotificationObserver, iosRemoveNotificationObserver } from './helpers';
import { getiOSWindow, setA11yUpdatePropertiesCallback, setApplicationPropertiesCallback, setiOSWindow, setRootView, setToggleApplicationEventListenersCallback } from './helpers-common';
@NativeClass
class CADisplayLinkTarget extends NSObject {
@ -78,26 +80,6 @@ class CADisplayLinkTarget extends NSObject {
};
}
@NativeClass
class NotificationObserver extends NSObject {
private _onReceiveCallback: (notification: NSNotification) => void;
public static initWithCallback(onReceiveCallback: (notification: NSNotification) => void): NotificationObserver {
const observer = <NotificationObserver>super.new();
observer._onReceiveCallback = onReceiveCallback;
return observer;
}
public onReceive(notification: NSNotification): void {
this._onReceiveCallback(notification);
}
public static ObjCExposedMethods = {
onReceive: { returns: interop.types.void, params: [NSNotification] },
};
}
@NativeClass
class Responder extends UIResponder implements UIApplicationDelegate {
get window(): UIWindow {
@ -114,8 +96,6 @@ class Responder extends UIResponder implements UIApplicationDelegate {
export class iOSApplication extends ApplicationCommon {
private _delegate: UIApplicationDelegate;
private _delegateHandlers = new Map<string, Array<Function>>();
private _window: UIWindow;
private _notificationObservers: NotificationObserver[] = [];
private _rootView: View;
displayedOnce = false;
@ -167,6 +147,7 @@ export class iOSApplication extends ApplicationCommon {
return;
}
this._rootView = rootView;
setRootView(rootView);
// Attach to the existing iOS app
const window = getWindow() as UIWindow;
@ -281,12 +262,12 @@ export class iOSApplication extends ApplicationCommon {
// TODO: consideration
// may not want to cache this value given the potential of multiple scenes
// particularly with SwiftUI app lifecycle based apps
if (!this._window) {
if (!getiOSWindow()) {
// Note: NativeScriptViewFactory.getKeyWindow will always be used in SwiftUI app lifecycle based apps
this._window = getWindow() as UIWindow;
setiOSWindow(getWindow() as UIWindow);
}
return this._window;
return getiOSWindow();
}
get delegate(): UIApplicationDelegate & { prototype: UIApplicationDelegate } {
@ -342,19 +323,11 @@ export class iOSApplication extends ApplicationCommon {
}
addNotificationObserver(notificationName: string, onReceiveCallback: (notification: NSNotification) => void) {
const observer = NotificationObserver.initWithCallback(onReceiveCallback);
NSNotificationCenter.defaultCenter.addObserverSelectorNameObject(observer, 'onReceive', notificationName, null);
this._notificationObservers.push(observer);
return observer;
return iosAddNotificationObserver(notificationName, onReceiveCallback);
}
removeNotificationObserver(observer: any, notificationName: string) {
const index = this._notificationObservers.indexOf(observer);
if (index >= 0) {
this._notificationObservers.splice(index, 1);
NSNotificationCenter.defaultCenter.removeObserverNameObject(observer, notificationName, null);
}
removeNotificationObserver(observer: any /* NotificationObserver */, notificationName: string) {
iosRemoveNotificationObserver(observer, notificationName);
}
protected getSystemAppearance(): 'light' | 'dark' {
@ -405,12 +378,12 @@ export class iOSApplication extends ApplicationCommon {
ios: notification?.userInfo?.objectForKey('UIApplicationLaunchOptionsLocalNotificationKey') ?? null,
});
if (this._window) {
if (getiOSWindow()) {
if (root !== null && !isEmbedded()) {
this.setWindowContent(root);
}
} else {
this._window = this.window; // UIApplication.sharedApplication.keyWindow;
setiOSWindow(this.window);
}
}
@ -438,6 +411,7 @@ export class iOSApplication extends ApplicationCommon {
const controller = this.getViewController(rootView);
this._rootView = rootView;
setRootView(rootView);
// setup view as styleScopeHost
rootView._setupAsRootView({});
@ -468,11 +442,11 @@ export class iOSApplication extends ApplicationCommon {
private didFinishLaunchingWithOptions(notification: NSNotification) {
this.setMaxRefreshRate();
// ensures window is assigned to proper window scene
this._window = this.window;
setiOSWindow(this.window);
if (!this._window) {
if (!getiOSWindow()) {
// if still no window, create one
this._window = UIWindow.alloc().initWithFrame(UIScreen.mainScreen.bounds);
setiOSWindow(UIWindow.alloc().initWithFrame(UIScreen.mainScreen.bounds));
}
if (!__VISIONOS__) {
@ -746,21 +720,6 @@ function ensureNativeClasses() {
});
}
export function setupAccessibleView(view: View): void {
const uiView = view.nativeViewProtected as UIView;
if (!uiView) {
return;
}
/**
* We need to map back from the UIView to the NativeScript View.
*
* We do that by setting the uiView's tag to the View's domId.
* This way we can do reverse lookup.
*/
uiView.tag = view._domId;
}
export function updateAccessibilityProperties(view: View): void {
const uiView = view.nativeViewProtected as UIView;
if (!uiView) {
@ -847,9 +806,9 @@ export function updateAccessibilityProperties(view: View): void {
uiView.accessibilityTraits = a11yTraits;
}
setA11yUpdatePropertiesCallback(updateAccessibilityProperties);
export const sendAccessibilityEvent = (): void => {};
export const updateContentDescription = (): string | null => null;
export function isAccessibilityServiceEnabled(): boolean {
const accessibilityServiceEnabled = isA11yEnabled();
@ -1065,3 +1024,22 @@ export function initAccessibilityCssHelper(): void {
accessibilityServiceObservable.on(AccessibilityServiceEnabledObservable.propertyChangeEvent, () => updateCurrentHelperClasses(applyRootCssClass));
}
setInitAccessibilityCssHelper(initAccessibilityCssHelper);
const applicationEvents: string[] = [Application.orientationChangedEvent, Application.systemAppearanceChangedEvent];
function toggleApplicationEventListeners(toAdd: boolean, callback: (args: ApplicationEventData) => void) {
for (const eventName of applicationEvents) {
if (toAdd) {
Application.on(eventName, callback);
} else {
Application.off(eventName, callback);
}
}
}
setToggleApplicationEventListenersCallback(toggleApplicationEventListeners);
setApplicationPropertiesCallback(() => {
return {
orientation: Application.orientation(),
systemAppearance: Application.systemAppearance(),
};
});

View File

@ -0,0 +1,86 @@
/**
* Keep this helper file slim to avoid circular dependencies.
* Used to define helper functions and variables that are shared between Android and iOS
* without introducing platform-specific code directly.
* It should not import platform-specific modules directly.
*/
let nativeApp: UIApplication | android.app.Application;
/**
* Get the current application instance.
* @returns current application instance, UIApplication on iOS or android.app.Application on Android.
*/
export function getNativeApp() {
return nativeApp;
}
/**
* This is called internally to set the native application instance.
* You typically do not need to call this directly.
* However, it's exposed for special case purposes, such as custom application initialization.
* @param app The native application instance to set.
* This should be called once during application startup to set the native app instance.
*/
export function setNativeApp(app: UIApplication | android.app.Application) {
nativeApp = app;
}
let rootView: any;
export function getRootView() {
return rootView;
}
export function setRootView(view: any) {
rootView = view;
}
let _appInBackground: boolean = false;
export function isAppInBackground() {
return _appInBackground;
}
export function setAppInBackground(value: boolean) {
_appInBackground = value;
}
let _iosWindow: UIWindow;
export function getiOSWindow(): UIWindow {
return _iosWindow;
}
export function setiOSWindow(value: UIWindow) {
_iosWindow = value;
}
// Aids avoiding circular dependencies by allowing the application event listeners to be toggled
let _toggleApplicationEventListenersHandler: (toAdd: boolean, callback: (args: any) => void) => void;
export function toggleApplicationEventListeners(toAdd: boolean, callback: (args: any) => void) {
if (_toggleApplicationEventListenersHandler) {
_toggleApplicationEventListenersHandler(toAdd, callback);
}
}
export function setToggleApplicationEventListenersCallback(callback: (toAdd: boolean, callback: (args: any) => void) => void) {
_toggleApplicationEventListenersHandler = callback;
}
// Aids avoiding circular dependencies by allowing the application properties to be retrieved
type ApplicationPropertyValues = { orientation: 'portrait' | 'landscape' | 'unknown'; systemAppearance: 'dark' | 'light' | null };
let _applicationPropertiesCallback: () => ApplicationPropertyValues;
export function getApplicationProperties(): ApplicationPropertyValues {
if (_applicationPropertiesCallback) {
return _applicationPropertiesCallback();
}
return { orientation: 'unknown', systemAppearance: null };
}
export function setApplicationPropertiesCallback(callback: () => ApplicationPropertyValues) {
_applicationPropertiesCallback = callback;
}
let _a11yUpdatePropertiesCallback: (view: any /* View */) => void;
export function setA11yUpdatePropertiesCallback(callback: (view: any /* View */) => void) {
_a11yUpdatePropertiesCallback = callback;
}
export function updateA11yPropertiesCallback(view: any /* View */) {
if (_a11yUpdatePropertiesCallback) {
_a11yUpdatePropertiesCallback(view);
}
}

View File

@ -0,0 +1,204 @@
import { SDK_VERSION } from '../utils/constants';
import { getNativeApp, updateA11yPropertiesCallback } from './helpers-common';
import { AccessibilityRole, AccessibilityState } from '../accessibility/accessibility-common';
import { Trace } from '../trace';
let _startActivity: androidx.appcompat.app.AppCompatActivity;
let _foregroundActivity: androidx.appcompat.app.AppCompatActivity;
export function androidGetCurrentActivity(): androidx.appcompat.app.AppCompatActivity {
return _foregroundActivity || _startActivity;
}
export function androidGetForegroundActivity(): androidx.appcompat.app.AppCompatActivity {
return _foregroundActivity;
}
export function androidSetForegroundActivity(activity: androidx.appcompat.app.AppCompatActivity): void {
_foregroundActivity = activity;
}
export function androidGetStartActivity(): androidx.appcompat.app.AppCompatActivity {
return _startActivity;
}
export function androidSetStartActivity(activity: androidx.appcompat.app.AppCompatActivity): void {
_startActivity = activity;
}
function getApplicationContext(): android.content.Context {
return (getNativeApp() as android.app.Application).getApplicationContext();
}
export const androidRegisteredReceivers: { [key: string]: android.content.BroadcastReceiver } = {};
export const androidPendingReceiverRegistrations = new Array<(context: android.content.Context) => void>();
declare class BroadcastReceiver extends android.content.BroadcastReceiver {
constructor(onReceiveCallback: (context: android.content.Context, intent: android.content.Intent) => void);
}
let BroadcastReceiver_: typeof BroadcastReceiver;
function initBroadcastReceiver() {
if (BroadcastReceiver_) {
return BroadcastReceiver_;
}
@NativeClass
class BroadcastReceiverImpl extends android.content.BroadcastReceiver {
private _onReceiveCallback: (context: android.content.Context, intent: android.content.Intent) => void;
constructor(onReceiveCallback: (context: android.content.Context, intent: android.content.Intent) => void) {
super();
this._onReceiveCallback = onReceiveCallback;
return global.__native(this);
}
public onReceive(context: android.content.Context, intent: android.content.Intent) {
if (this._onReceiveCallback) {
this._onReceiveCallback(context, intent);
}
}
}
BroadcastReceiver_ = BroadcastReceiverImpl;
return BroadcastReceiver_;
}
export function androidRegisterBroadcastReceiver(intentFilter: string, onReceiveCallback: (context: android.content.Context, intent: android.content.Intent) => void, flags = 2): void {
const registerFunc = (context: android.content.Context) => {
const receiver: android.content.BroadcastReceiver = new (initBroadcastReceiver())(onReceiveCallback);
if (SDK_VERSION >= 26) {
context.registerReceiver(receiver, new android.content.IntentFilter(intentFilter), flags);
} else {
context.registerReceiver(receiver, new android.content.IntentFilter(intentFilter));
}
androidRegisteredReceivers[intentFilter] = receiver;
};
if (getApplicationContext()) {
registerFunc(getApplicationContext());
} else {
androidPendingReceiverRegistrations.push(registerFunc);
}
}
export function androidUnregisterBroadcastReceiver(intentFilter: string): void {
const receiver = androidRegisteredReceivers[intentFilter];
if (receiver) {
getApplicationContext().unregisterReceiver(receiver);
androidRegisteredReceivers[intentFilter] = undefined;
delete androidRegisteredReceivers[intentFilter];
}
}
export function updateContentDescription(view: any /* View */, forceUpdate?: boolean): string | null {
if (!view.nativeViewProtected) {
return;
}
return applyContentDescription(view, forceUpdate);
}
export function applyContentDescription(view: any /* View */, forceUpdate?: boolean) {
let androidView = view.nativeViewProtected as android.view.View;
if (!androidView || (androidView instanceof android.widget.TextView && !view._androidContentDescriptionUpdated)) {
return null;
}
if (androidView instanceof androidx.appcompat.widget.Toolbar) {
const numChildren = androidView.getChildCount();
for (let i = 0; i < numChildren; i += 1) {
const childAndroidView = androidView.getChildAt(i);
if (childAndroidView instanceof androidx.appcompat.widget.AppCompatTextView) {
androidView = childAndroidView;
break;
}
}
}
const cls = `applyContentDescription(${view})`;
const titleValue = view['title'] as string;
const textValue = view['text'] as string;
if (!forceUpdate && view._androidContentDescriptionUpdated === false && textValue === view['_lastText'] && titleValue === view['_lastTitle']) {
// prevent updating this too much
return androidView.getContentDescription();
}
const contentDescriptionBuilder = new Array<string>();
// Workaround: TalkBack won't read the checked state for fake Switch.
if (view.accessibilityRole === AccessibilityRole.Switch) {
const androidSwitch = new android.widget.Switch(getApplicationContext());
if (view.accessibilityState === AccessibilityState.Checked) {
contentDescriptionBuilder.push(androidSwitch.getTextOn());
} else {
contentDescriptionBuilder.push(androidSwitch.getTextOff());
}
}
if (view.accessibilityLabel) {
if (Trace.isEnabled()) {
Trace.write(`${cls} - have accessibilityLabel`, Trace.categories.Accessibility);
}
contentDescriptionBuilder.push(`${view.accessibilityLabel}`);
}
if (view.accessibilityValue) {
if (Trace.isEnabled()) {
Trace.write(`${cls} - have accessibilityValue`, Trace.categories.Accessibility);
}
contentDescriptionBuilder.push(`${view.accessibilityValue}`);
} else if (textValue) {
if (textValue !== view.accessibilityLabel) {
if (Trace.isEnabled()) {
Trace.write(`${cls} - don't have accessibilityValue - use 'text' value`, Trace.categories.Accessibility);
}
contentDescriptionBuilder.push(`${textValue}`);
}
} else if (titleValue) {
if (titleValue !== view.accessibilityLabel) {
if (Trace.isEnabled()) {
Trace.write(`${cls} - don't have accessibilityValue - use 'title' value`, Trace.categories.Accessibility);
}
contentDescriptionBuilder.push(`${titleValue}`);
}
}
if (view.accessibilityHint) {
if (Trace.isEnabled()) {
Trace.write(`${cls} - have accessibilityHint`, Trace.categories.Accessibility);
}
contentDescriptionBuilder.push(`${view.accessibilityHint}`);
}
const contentDescription = contentDescriptionBuilder.join('. ').trim().replace(/^\.$/, '');
if (contentDescription) {
if (Trace.isEnabled()) {
Trace.write(`${cls} - set to "${contentDescription}"`, Trace.categories.Accessibility);
}
androidView.setContentDescription(contentDescription);
} else {
if (Trace.isEnabled()) {
Trace.write(`${cls} - remove value`, Trace.categories.Accessibility);
}
androidView.setContentDescription(null);
}
view['_lastTitle'] = titleValue;
view['_lastText'] = textValue;
view._androidContentDescriptionUpdated = false;
return contentDescription;
}
export function setupAccessibleView(view: any /* any */): void {
updateA11yPropertiesCallback(view);
}

25
packages/core/application/helpers.d.ts vendored Normal file
View File

@ -0,0 +1,25 @@
/**
* Initialize accessibility for View. This should be called on loaded-event.
*/
export function setupAccessibleView(view: View): void;
/**
* Android: Update the content description for views
*/
export const updateContentDescription: (view: any /* View */, forceUpdate?: boolean) => string | null;
export function applyContentDescription(view: any /* View */, forceUpdate?: boolean);
/* Android app-wide helpers */
export const androidRegisteredReceivers: { [key: string]: android.content.BroadcastReceiver };
export const androidPendingReceiverRegistrations: Array<(context: android.content.Context) => void>;
export function androidRegisterBroadcastReceiver(intentFilter: string, onReceiveCallback: (context: android.content.Context, intent: android.content.Intent) => void, flags = 2): void;
export function androidUnregisterBroadcastReceiver(intentFilter: string): void;
export function androidGetCurrentActivity(): androidx.appcompat.app.AppCompatActivity;
export function androidGetForegroundActivity(): androidx.appcompat.app.AppCompatActivity;
export function androidSetForegroundActivity(activity: androidx.appcompat.app.AppCompatActivity): void;
export function androidGetStartActivity(): androidx.appcompat.app.AppCompatActivity;
export function androidSetStartActivity(activity: androidx.appcompat.app.AppCompatActivity): void;
/* iOS app-wide helpers */
export const iosNotificationObservers: NotificationObserver[];
class NotificationObserver extends NSObject {}
export function iosAddNotificationObserver(notificationName: string, onReceiveCallback: (notification: NSNotification) => void): NotificationObserver;
export function iosRemoveNotificationObserver(observer: NotificationObserver, notificationName: string): void;

View File

@ -0,0 +1,68 @@
// stubs to avoid bundler warnings
export const updateContentDescription = (view: any /* View */, forceUpdate?: boolean): string | null => null;
export function applyContentDescription(view: any /* View */, forceUpdate?: boolean) {
return null;
}
export const androidRegisteredReceivers = undefined;
export const androidPendingReceiverRegistrations = undefined;
export function androidRegisterBroadcastReceiver(intentFilter: string, onReceiveCallback: (context: android.content.Context, intent: android.content.Intent) => void, flags = 2): void {}
export function androidUnregisterBroadcastReceiver(intentFilter: string): void {}
export function androidGetCurrentActivity() {}
export function androidGetForegroundActivity() {}
export function androidSetForegroundActivity(activity: androidx.appcompat.app.AppCompatActivity): void {}
export function androidGetStartActivity() {}
export function androidSetStartActivity(activity: androidx.appcompat.app.AppCompatActivity): void {}
@NativeClass
class NotificationObserver extends NSObject {
private _onReceiveCallback: (notification: NSNotification) => void;
public static initWithCallback(onReceiveCallback: (notification: NSNotification) => void): NotificationObserver {
const observer = <NotificationObserver>super.new();
observer._onReceiveCallback = onReceiveCallback;
return observer;
}
public onReceive(notification: NSNotification): void {
this._onReceiveCallback(notification);
}
public static ObjCExposedMethods = {
onReceive: { returns: interop.types.void, params: [NSNotification] },
};
}
export const iosNotificationObservers: NotificationObserver[] = [];
export function iosAddNotificationObserver(notificationName: string, onReceiveCallback: (notification: NSNotification) => void) {
const observer = NotificationObserver.initWithCallback(onReceiveCallback);
NSNotificationCenter.defaultCenter.addObserverSelectorNameObject(observer, 'onReceive', notificationName, null);
iosNotificationObservers.push(observer);
return observer;
}
export function iosRemoveNotificationObserver(observer: NotificationObserver, notificationName: string) {
// TODO: test if this finds the right observer instance match everytime
// after circular dependencies are resolved
const index = iosNotificationObservers.indexOf(observer);
if (index >= 0) {
iosNotificationObservers.splice(index, 1);
NSNotificationCenter.defaultCenter.removeObserverNameObject(observer, notificationName, null);
}
}
export function setupAccessibleView(view: any /* any */): void {
const uiView = view.nativeViewProtected as UIView;
if (!uiView) {
return;
}
/**
* We need to map back from the UIView to the NativeScript View.
*
* We do that by setting the uiView's tag to the View's domId.
* This way we can do reverse lookup.
*/
uiView.tag = view._domId;
}

View File

@ -1,4 +1,5 @@
import { Application } from '../application';
import { getNativeApp } from '../application/helpers-common';
import { androidRegisterBroadcastReceiver, androidUnregisterBroadcastReceiver } from '../application/helpers';
import { SDK_VERSION } from '../utils/constants';
export enum connectionType {
@ -18,7 +19,7 @@ const vpn = 'vpn';
// Get Connection Type
function getConnectivityManager(): android.net.ConnectivityManager {
return Application.android.getNativeApplication().getApplicationContext().getSystemService(android.content.Context.CONNECTIVITY_SERVICE);
return (getNativeApp() as android.app.Application).getApplicationContext().getSystemService(android.content.Context.CONNECTIVITY_SERVICE);
}
function getActiveNetworkInfo(): android.net.NetworkInfo {
@ -109,7 +110,7 @@ function startMonitoringLegacy(connectionTypeChangedCallback) {
connectionTypeChangedCallback(newConnectionType);
};
const zoneCallback = zonedCallback(onReceiveCallback);
Application.android.registerBroadcastReceiver(android.net.ConnectivityManager.CONNECTIVITY_ACTION, zoneCallback);
androidRegisterBroadcastReceiver(android.net.ConnectivityManager.CONNECTIVITY_ACTION, zoneCallback);
}
let callback;
@ -170,6 +171,6 @@ export function stopMonitoring(): void {
callback = null;
}
} else {
Application.android.unregisterBroadcastReceiver(android.net.ConnectivityManager.CONNECTIVITY_ACTION);
androidUnregisterBroadcastReceiver(android.net.ConnectivityManager.CONNECTIVITY_ACTION);
}
}

View File

@ -1,19 +1,24 @@
// Types
import { ViewBase } from '../ui/core/view-base';
//Requires
import { androidGetCurrentActivity } from '../application/helpers';
import { getRootView } from '../application/helpers-common';
import type { ViewBase } from '../ui/core/view-base';
import { unsetValue } from '../ui/core/properties/property-shared';
import { getNodeById } from './dom-types';
import { mainThreadify } from '../utils';
// Use lazy requires for core modules
const getAppRootView = () => require('../application').getRootView();
let unsetValue;
function unsetViewValue(view, name) {
if (!unsetValue) {
unsetValue = require('../ui/core/properties').unsetValue;
function getAppRootView() {
if (__ANDROID__) {
const activity = androidGetCurrentActivity();
if (!activity) {
return undefined;
}
const callbacks = activity['_callbacks'];
return callbacks ? callbacks.getRootView() : undefined;
} else {
return getRootView();
}
}
function unsetViewValue(view, name) {
view[name] = unsetValue;
}

View File

@ -4,7 +4,6 @@ import { trace as profilingTrace, time, uptime, level as profilingLevel } from '
import * as timer from '../timer';
import * as animationFrame from '../animation-frame';
import * as mediaQueryList from '../media-query-list';
import * as uiDialogs from '../ui/dialogs';
import * as text from '../text';
import * as xhrImpl from '../xhr';
import '../fetch';
@ -340,9 +339,6 @@ export function initGlobal() {
global.registerModule('media-query-list', () => mediaQueryList);
installPolyfills('media-query-list', ['matchMedia', 'MediaQueryList']);
global.registerModule('ui-dialogs', () => uiDialogs);
installPolyfills('ui-dialogs', ['alert', 'confirm', 'prompt', 'login', 'action']);
global.registerModule('text', () => text);
installPolyfills('text', ['TextDecoder', 'TextEncoder']);
@ -436,7 +432,9 @@ if (!global.NativeScriptHasInitGlobal && !isTestingEnv()) {
initGlobal();
}
if (!isTestingEnv()) {
// ensure the Application instance is initialized before any other module imports it.
require('@nativescript/core/application');
}
// if (!isTestingEnv()) {
// // ensure the Application instance is initialized before any other module imports it.
// require('@nativescript/core/application');
// }
// ensure the Application instance is initialized before any other module imports it.
import '@nativescript/core/application';

View File

@ -1,12 +1,9 @@
// Definitions.
import { ImageSource as ImageSourceDefinition, iosSymbolScaleType } from '.';
import { ImageAsset } from '../image-asset';
import * as http from '../http';
// Types.
import { path as fsPath, knownFolders } from '../file-system';
import { isFileOrResourcePath, RESOURCE_PREFIX, layout } from '../utils';
import { Application } from '../application';
import { getNativeApp } from '../application/helpers-common';
import { Font } from '../ui/styling/font';
import { Color } from '../color';
@ -14,23 +11,8 @@ import { getScaledDimensions } from './image-source-common';
export { isFileOrResourcePath };
let application: android.app.Application;
let resources: android.content.res.Resources;
function getApplication() {
if (!application) {
application = Application.android.getNativeApplication();
}
return application;
}
function getResources() {
if (!resources) {
resources = getApplication().getResources();
}
return resources;
return getNativeApp() as android.app.Application;
}
export class ImageSource implements ImageSourceDefinition {
@ -85,7 +67,7 @@ export class ImageSource implements ImageSourceDefinition {
}
static fromResourceSync(name: string): ImageSource {
const res = getResources();
const res = getApplication().getResources();
if (res) {
const identifier: number = res.getIdentifier(name, 'drawable', getApplication().getPackageName());
if (0 < identifier) {

View File

@ -8,6 +8,8 @@
export type { NativeScriptConfig } from './config';
export * from './application';
export { androidRegisterBroadcastReceiver, androidUnregisterBroadcastReceiver, androidRegisteredReceivers, iosAddNotificationObserver, iosRemoveNotificationObserver, iosNotificationObservers } from './application/helpers';
export { getNativeApp, setNativeApp } from './application/helpers-common';
export * as ApplicationSettings from './application-settings';
import * as Accessibility from './accessibility';
export namespace AccessibilityEvents {

View File

@ -3,6 +3,8 @@
// Init globals first (use import to ensure it's always at the top)
import './globals';
export * from './application';
export { androidRegisterBroadcastReceiver, androidUnregisterBroadcastReceiver, androidRegisteredReceivers, iosAddNotificationObserver, iosRemoveNotificationObserver, iosNotificationObservers } from './application/helpers';
export { getNativeApp, setNativeApp } from './application/helpers-common';
export * as ApplicationSettings from './application-settings';
import * as Accessibility from './accessibility';
export namespace AccessibilityEvents {
@ -24,8 +26,6 @@ export type { PropertyChangeData, EventData } from './data/observable';
export { VirtualArray } from './data/virtual-array';
export type { ItemsLoading } from './data/virtual-array';
export { File, FileSystemEntity, Folder, knownFolders, path, getFileAccess, AndroidDirectory } from './file-system';
// Export all interfaces from "http" module
export type { HttpRequestOptions, HttpResponse, Headers, HttpResponseEncoding, HttpContent } from './http';
export * as Http from './http';
export { ImageAsset } from './image-asset';
@ -37,12 +37,10 @@ export type { PlatformContext } from './module-name-resolver';
export type { ModuleListProvider } from './module-name-resolver/helpers';
export { isAndroid, isIOS, isVisionOS, isApple, Screen, Device, platformNames } from './platform';
export type { IDevice } from './platform';
// Profiling
export { profile, enable as profilingEnable, disable as profilingDisable, time as profilingTime, uptime as profilingUptime, start as profilingStart, stop as profilingStop, isRunning as profilingIsRunning, dumpProfiles as profilingDumpProfiles, resetProfiles as profilingResetProfiles, startCPUProfile as profilingStartCPU, stopCPUProfile as profilingStopCPU } from './profiling';
export type { InstrumentationMode, TimerInfo } from './profiling';
export { encoding } from './text';
export * from './trace';
export * from './ui';
export * as Utils from './utils';
export { XmlParser, ParserEventType, ParserEvent } from './xml';
export * from './ui';

View File

@ -1,25 +1,15 @@
import { EventData, Observable } from '../data/observable';
import { Screen } from '../platform';
import { Application, ApplicationEventData } from '../application';
import { getApplicationProperties, toggleApplicationEventListeners } from '../application/helpers-common';
import type { ApplicationEventData } from '../application/application-interfaces';
import { matchQuery, MediaQueryType } from '../css-mediaquery';
import { Trace } from '../trace';
const mediaQueryLists: MediaQueryListImpl[] = [];
const applicationEvents: string[] = [Application.orientationChangedEvent, Application.systemAppearanceChangedEvent];
// In browser, developers cannot create MediaQueryList instances without calling matchMedia
let isMediaInitializationEnabled: boolean = false;
function toggleApplicationEventListeners(toAdd: boolean) {
for (const eventName of applicationEvents) {
if (toAdd) {
Application.on(eventName, onDeviceChange);
} else {
Application.off(eventName, onDeviceChange);
}
}
}
function onDeviceChange(args: ApplicationEventData) {
for (const mql of mediaQueryLists) {
const matches = checkIfMediaQueryMatches(mql.media);
@ -42,14 +32,15 @@ function checkIfMediaQueryMatches(mediaQueryString: string): boolean {
let matches: boolean;
try {
const appProperties = getApplicationProperties();
matches = matchQuery(mediaQueryString, {
type: MediaQueryType.screen,
width: widthPixels,
height: heightPixels,
'device-width': widthPixels,
'device-height': heightPixels,
orientation: Application.orientation(),
'prefers-color-scheme': Application.systemAppearance(),
orientation: appProperties.orientation,
'prefers-color-scheme': appProperties.systemAppearance,
});
} catch (err) {
matches = false;
@ -130,7 +121,7 @@ class MediaQueryListImpl extends Observable implements MediaQueryList {
mediaQueryLists.push(this);
if (mediaQueryLists.length === 1) {
toggleApplicationEventListeners(true);
toggleApplicationEventListeners(true, onDeviceChange);
}
}
}
@ -151,7 +142,7 @@ class MediaQueryListImpl extends Observable implements MediaQueryList {
mediaQueryLists.splice(index, 1);
if (!mediaQueryLists.length) {
toggleApplicationEventListeners(false);
toggleApplicationEventListeners(false, onDeviceChange);
}
}
}

View File

@ -1,4 +1,3 @@
import { Application } from '../application';
import * as fs from '../file-system';
import { Trace } from '../trace';
@ -136,7 +135,7 @@ export function registerModulesFromFileSystem(moduleName: string) {
function initialize() {
if (!initialized) {
Application.on('livesync', (args) => cache.clear());
// Application.on('livesync', (args) => cache.clear());
initialized = true;
}
}

View File

@ -1,4 +1,4 @@
import { Application } from '../../application';
import { getNativeApp } from '../../application/helpers-common';
import { SDK_VERSION } from '../../utils/constants';
import { platformNames } from '../common';
import { Screen } from '../screen';
@ -65,7 +65,7 @@ class DeviceRef {
get uuid(): string {
if (!this._uuid) {
const nativeApp = Application.android.getNativeApplication();
const nativeApp = getNativeApp() as android.app.Application;
this._uuid = android.provider.Settings.Secure.getString(nativeApp.getContentResolver(), android.provider.Settings.Secure.ANDROID_ID);
}

View File

@ -1,10 +1,10 @@
import { Application } from '../../application';
import { getNativeApp } from '../../application/helpers-common';
class MainScreen {
private _metrics: android.util.DisplayMetrics;
private initMetrics(): void {
const nativeApp = Application.android.getNativeApplication();
const nativeApp = getNativeApp() as android.app.Application;
nativeApp.getSystemService(android.content.Context.WINDOW_SERVICE).getDefaultDisplay().getRealMetrics(this._metrics);
}

View File

@ -1,15 +1,14 @@
import { AndroidActionItemSettings, AndroidActionBarSettings as AndroidActionBarSettingsDefinition, ActionItem as ActionItemDefinition } from '.';
import { isAccessibilityServiceEnabled, updateContentDescription } from '../../application';
import { isAccessibilityServiceEnabled } from '../../application';
import { updateContentDescription } from '../../application/helpers';
import { ActionItemBase, ActionBarBase, isVisible, flatProperty, traceMissingIcon, androidContentInsetLeftProperty, androidContentInsetRightProperty } from './action-bar-common';
import { AndroidHelper, View } from '../core/view';
import { Color } from '../../color';
import { layout, RESOURCE_PREFIX, isFontIconURI } from '../../utils';
import { colorProperty } from '../styling/style-properties';
import { ImageSource } from '../../image-source';
import { Application } from '../../application';
import type { Background } from '../styling/background';
import { getNativeApp } from '../../application/helpers-common';
import { SDK_VERSION } from '../../utils/constants';
import { NativeScriptAndroidView } from '../utils';
export * from './action-bar-common';
@ -81,7 +80,7 @@ function initializeMenuItemClickListener(): void {
}
MenuItemClickListener = MenuItemClickListenerImpl;
appResources = Application.android.context.getResources();
appResources = (getNativeApp() as android.app.Application).getApplicationContext().getResources();
}
export class ActionItem extends ActionItemBase {
@ -296,7 +295,7 @@ export class ActionBar extends ActionBarBase {
traceMissingIcon(icon);
}
} else {
const defaultIcon = Application.android.nativeApp.getApplicationInfo().icon;
const defaultIcon = (getNativeApp() as android.app.Application).getApplicationInfo().icon;
this.nativeViewProtected.setLogo(defaultIcon);
}
} else {
@ -311,7 +310,7 @@ export class ActionBar extends ActionBarBase {
if (title !== undefined) {
this.nativeViewProtected.setTitle(title);
} else {
const appContext = Application.android.context;
const appContext = (getNativeApp() as android.app.Application).getApplicationContext();
const appInfo = appContext.getApplicationInfo();
const appLabel = appContext.getPackageManager().getApplicationLabel(appInfo);
if (appLabel) {
@ -548,7 +547,7 @@ function getDrawableOrResourceId(icon: string, resources: android.content.res.Re
let result = null;
if (icon.indexOf(RESOURCE_PREFIX) === 0) {
const resourceId: number = resources.getIdentifier(icon.substr(RESOURCE_PREFIX.length), 'drawable', Application.android.packageName);
const resourceId: number = resources.getIdentifier(icon.substr(RESOURCE_PREFIX.length), 'drawable', (getNativeApp() as android.app.Application).getApplicationContext().getPackageName());
if (resourceId > 0) {
result = resourceId;
}

View File

@ -17,7 +17,9 @@ import { Background, BackgroundClearFlags, refreshBorderDrawable } from '../../s
import { profile } from '../../../profiling';
import { topmost } from '../../frame/frame-stack';
import { Screen } from '../../../platform';
import { AndroidActivityBackPressedEventData, Application, updateAccessibilityProperties, updateContentDescription } from '../../../application';
import { AndroidActivityBackPressedEventData } from '../../../application/application-interfaces';
import { isAppInBackground, updateA11yPropertiesCallback } from '../../../application/helpers-common';
import { updateContentDescription } from '../../../application/helpers';
import { accessibilityEnabledProperty, accessibilityHiddenProperty, accessibilityHintProperty, accessibilityIdentifierProperty, accessibilityLabelProperty, accessibilityLiveRegionProperty, accessibilityMediaSessionProperty, accessibilityRoleProperty, accessibilityStateProperty, accessibilityValueProperty } from '../../../accessibility/accessibility-properties';
import { AccessibilityLiveRegion, AccessibilityRole, AndroidAccessibilityEvent, AccessibilityState } from '../../../accessibility';
import * as Utils from '../../../utils';
@ -141,7 +143,7 @@ function initializeDialogFragment() {
};
// Fist fire application.android global event
Application.android.notify(args);
global.NativeScriptGlobals.events.notify(args);
if (args.cancel) {
return;
}
@ -681,7 +683,7 @@ export class View extends ViewCommon {
// if the app is in background while triggering _showNativeModalView
// then DialogFragment.show will trigger IllegalStateException: Can not perform this action after onSaveInstanceState
// so if in background we create an event to call _showNativeModalView when loaded (going back in foreground)
if (Application.inBackground && !parent.isLoaded) {
if (isAppInBackground() && !parent.isLoaded) {
const onLoaded = () => {
parent.off('loaded', onLoaded);
this._showNativeModalView(parent, options);
@ -826,7 +828,7 @@ export class View extends ViewCommon {
[accessibilityEnabledProperty.setNative](value: boolean): void {
this.nativeViewProtected.setFocusable(!!value);
updateAccessibilityProperties(this);
updateA11yPropertiesCallback(this);
}
[accessibilityIdentifierProperty.setNative](value: string): void {
@ -836,7 +838,7 @@ export class View extends ViewCommon {
// @ts-expect-error
[accessibilityRoleProperty.setNative](value: AccessibilityRole): void {
this.accessibilityRole = value;
updateAccessibilityProperties(this);
updateA11yPropertiesCallback(this);
if (SDK_VERSION >= 28) {
this.nativeViewProtected?.setAccessibilityHeading(value === AccessibilityRole.Header);
@ -887,11 +889,11 @@ export class View extends ViewCommon {
// @ts-expect-error
[accessibilityStateProperty.setNative](value: AccessibilityState): void {
this.accessibilityState = value;
updateAccessibilityProperties(this);
updateA11yPropertiesCallback(this);
}
[accessibilityMediaSessionProperty.setNative](): void {
updateAccessibilityProperties(this);
updateA11yPropertiesCallback(this);
}
[androidElevationProperty.getDefault](): number {

View File

@ -1,6 +1,7 @@
import type { Point, Position } from './view-interfaces';
import { ViewCommon, isEnabledProperty, originXProperty, originYProperty, isUserInteractionEnabledProperty, testIDProperty } from './view-common';
import { isAccessibilityServiceEnabled, updateAccessibilityProperties } from '../../../application';
import { isAccessibilityServiceEnabled } from '../../../application';
import { updateA11yPropertiesCallback } from '../../../application/helpers-common';
import { ShowModalOptions, hiddenProperty } from '../view-base';
import { Trace } from '../../../trace';
import { layout, ios as iosUtils, SDK_VERSION } from '../../../utils';
@ -690,7 +691,7 @@ export class View extends ViewCommon {
[accessibilityEnabledProperty.setNative](value: boolean): void {
this.nativeViewProtected.isAccessibilityElement = !!value;
updateAccessibilityProperties(this);
updateA11yPropertiesCallback(this);
}
[accessibilityIdentifierProperty.getDefault](): string {
@ -703,7 +704,7 @@ export class View extends ViewCommon {
[accessibilityRoleProperty.setNative](value: AccessibilityRole): void {
this.accessibilityRole = value;
updateAccessibilityProperties(this);
updateA11yPropertiesCallback(this);
}
[accessibilityValueProperty.setNative](value: string): void {
@ -738,20 +739,20 @@ export class View extends ViewCommon {
[accessibilityHiddenProperty.setNative](value: boolean): void {
this.nativeViewProtected.accessibilityElementsHidden = !!value;
updateAccessibilityProperties(this);
updateA11yPropertiesCallback(this);
}
[accessibilityLiveRegionProperty.setNative](): void {
updateAccessibilityProperties(this);
updateA11yPropertiesCallback(this);
}
[accessibilityStateProperty.setNative](value: AccessibilityState): void {
this.accessibilityState = value;
updateAccessibilityProperties(this);
updateA11yPropertiesCallback(this);
}
[accessibilityMediaSessionProperty.setNative](): void {
updateAccessibilityProperties(this);
updateA11yPropertiesCallback(this);
}
[isUserInteractionEnabledProperty.getDefault](): boolean {

View File

@ -9,7 +9,7 @@ import { Color } from '../../../color';
import { Property, InheritedProperty } from '../properties';
import { EventData } from '../../../data/observable';
import { ViewHelper } from './view-helper';
import { setupAccessibleView } from '../../../application';
import { setupAccessibleView } from '../../../application/helpers';
import { PercentLength } from '../../styling/length-shared';

View File

@ -4,7 +4,7 @@
import { Trace } from '../../trace';
import { ConfirmOptions, PromptOptions, PromptResult, LoginOptions, LoginResult, ActionOptions, getCurrentPage, getLabelColor, getButtonColors, getTextFieldColor, isDialogOptions, inputType, capitalizationType, DialogStrings, parseLoginOptions } from './dialogs-common';
import { isString, isDefined, isFunction } from '../../utils/types';
import { Application } from '../../application';
import { getiOSWindow } from '../../application/helpers-common';
export * from './dialogs-common';
@ -17,7 +17,7 @@ function addButtonsToAlertController(alertController: UIAlertController, options
alertController.addAction(
UIAlertAction.actionWithTitleStyleHandler(options.cancelButtonText, UIAlertActionStyle.Default, () => {
raiseCallback(callback, false);
})
}),
);
}
@ -25,7 +25,7 @@ function addButtonsToAlertController(alertController: UIAlertController, options
alertController.addAction(
UIAlertAction.actionWithTitleStyleHandler(options.neutralButtonText, UIAlertActionStyle.Default, () => {
raiseCallback(callback, undefined);
})
}),
);
}
@ -33,7 +33,7 @@ function addButtonsToAlertController(alertController: UIAlertController, options
alertController.addAction(
UIAlertAction.actionWithTitleStyleHandler(options.okButtonText, UIAlertActionStyle.Default, () => {
raiseCallback(callback, true);
})
}),
);
}
}
@ -45,7 +45,7 @@ function raiseCallback(callback, result) {
}
function showUIAlertController(alertController: UIAlertController) {
let viewController = Application.ios.rootController;
let viewController = getiOSWindow()?.rootViewController;
while (viewController && viewController.presentedViewController && !viewController.presentedViewController.beingDismissed) {
viewController = viewController.presentedViewController;
@ -91,7 +91,7 @@ export function alert(arg: any): Promise<void> {
title: DialogStrings.ALERT,
okButtonText: DialogStrings.OK,
message: arg + '',
}
}
: arg;
const alertController = UIAlertController.alertControllerWithTitleMessagePreferredStyle(options.title, options.message, UIAlertControllerStyle.Alert);
@ -115,7 +115,7 @@ export function confirm(arg: any): Promise<boolean> {
okButtonText: DialogStrings.OK,
cancelButtonText: DialogStrings.CANCEL,
message: arg + '',
}
}
: arg;
const alertController = UIAlertController.alertControllerWithTitleMessagePreferredStyle(options.title, options.message, UIAlertControllerStyle.Alert);
@ -301,7 +301,7 @@ export function action(...args): Promise<string> {
alertController.addAction(
UIAlertAction.actionWithTitleStyleHandler(action, dialogType, (arg: UIAlertAction) => {
resolve(arg.title);
})
}),
);
}
}
@ -311,7 +311,7 @@ export function action(...args): Promise<string> {
alertController.addAction(
UIAlertAction.actionWithTitleStyleHandler(options.cancelButtonText, UIAlertActionStyle.Cancel, (arg: UIAlertAction) => {
resolve(arg.title);
})
}),
);
}

View File

@ -1,7 +1,7 @@
import { profile } from '../../../profiling';
import { AndroidFragmentCallbacks, BackstackEntry, Frame, getFrameByNumberId } from '..';
import { Trace } from '../../../trace';
import { Application } from '../../../application';
import { getNativeApp } from '../../../application/helpers-common';
import { Color } from '../../../color';
import { _updateTransitions } from '../fragment.transitions';
import { Page } from '../../page';
@ -159,7 +159,7 @@ export class FragmentCallbacksImplementation implements AndroidFragmentCallbacks
if (hasRemovingParent) {
const nativeFrameView = this.frame.nativeViewProtected;
if (nativeFrameView) {
const bitmapDrawable = new android.graphics.drawable.BitmapDrawable(Application.android.context.getResources(), this.backgroundBitmap);
const bitmapDrawable = new android.graphics.drawable.BitmapDrawable((getNativeApp() as android.app.Application).getApplicationContext().getResources(), this.backgroundBitmap);
this.frame._originalBackground = this.frame.backgroundColor || new Color('White');
nativeFrameView.setBackgroundDrawable(bitmapDrawable);
this.backgroundBitmap = null;

View File

@ -23,7 +23,11 @@ export { unsetValue } from './core/properties/property-shared';
export { addWeakEventListener, removeWeakEventListener } from './core/weak-event-listener';
export { DatePicker } from './date-picker';
// No need go export dialogs, they are already export exported globally
import { installPolyfills } from '../globals';
// No need to export dialogs, they are already export exported globally
import * as uiDialogs from '../ui/dialogs';
global.registerModule('ui-dialogs', () => uiDialogs);
installPolyfills('ui-dialogs', ['alert', 'confirm', 'prompt', 'login', 'action']);
export { DialogStrings, action, alert, confirm, login, prompt, getCurrentPage, Dialogs, inputType, capitalizationType } from './dialogs';
export type { DialogOptions, CancelableOptions, AlertOptions, PromptResult, PromptOptions, ActionOptions, ConfirmOptions, LoginResult, LoginOptions } from './dialogs';

View File

@ -10,7 +10,7 @@ import { Color } from '../../color';
import { fontSizeProperty, fontInternalProperty } from '../styling/style-properties';
import { RESOURCE_PREFIX, android as androidUtils, layout } from '../../utils';
import { Frame } from '../frame';
import { Application } from '../../application';
import { getNativeApp } from '../../application/helpers-common';
import { AndroidHelper } from '../core/view';
export * from './tab-view-common';
@ -284,7 +284,7 @@ function initializeNativeClasses() {
}
PagerAdapter = FragmentPagerAdapter;
appResources = Application.android.context.getResources();
appResources = (getNativeApp() as android.app.Application).getApplicationContext().getResources();
}
function createTabItemSpec(item: TabViewItem): org.nativescript.widgets.TabItemSpec {

View File

@ -1,6 +1,7 @@
import { platformCheck } from './platform-check';
import { numberHasDecimals, numberIs64Bit } from './types';
import { Application } from '../application';
import { getNativeApp } from '../application/helpers-common';
import { androidGetCurrentActivity } from '../application/helpers';
import { Trace } from '../trace';
import { topmost } from '../ui/frame/frame-stack';
@ -142,38 +143,19 @@ export function dataSerialize(data?: any, wrapPrimitives?: boolean) {
}
}
let application: android.app.Application;
let applicationContext: android.content.Context;
let contextResources: android.content.res.Resources;
let packageName: string;
function getApplicationContext() {
if (!applicationContext) {
applicationContext = getApplication().getApplicationContext();
}
return applicationContext;
return getApplication().getApplicationContext();
}
function getCurrentActivity() {
if (!Application) {
return null;
}
return Application.android.foregroundActivity || Application.android.startActivity;
return androidGetCurrentActivity();
}
function getApplication() {
if (!application) {
application = Application.android.getNativeApplication();
}
return application;
return getNativeApp() as android.app.Application;
}
function getResources() {
if (!contextResources) {
contextResources = getApplication().getResources();
}
return contextResources;
return getApplication().getResources();
}
let packageName: string;
function getPackageName() {
if (!packageName) {
packageName = getApplicationContext().getPackageName();

View File

@ -4,7 +4,6 @@ import { Color } from '../color';
import { Trace } from '../trace';
import { CORE_ANIMATION_DEFAULTS, getDurationWithDampingFromSpring } from './common';
import { SDK_VERSION } from './constants';
import { getFileExtension } from './utils-shared';
export function dataDeserialize(nativeData?: any) {
if (isNullOrUndefined(nativeData)) {