From 50cfd6fb25e457f5b8573b9fc62c4828d7617a97 Mon Sep 17 00:00:00 2001 From: Nathan Walker Date: Tue, 8 Jul 2025 22:55:03 -0700 Subject: [PATCH] refactor: circular deps part 8 --- packages/core/application/application.ios.ts | 5 +- .../file-system/file-system-access.android.ts | 72 ++++++++---------- packages/core/file-system/index.ts | 17 +---- packages/core/platform/screen/index.ios.ts | 4 +- packages/core/ui/core/view-base/index.ts | 2 +- packages/core/utils/debug-source.ts | 73 +++++++++++++++++++ packages/core/utils/debug.ts | 73 +------------------ packages/core/utils/index.ios.ts | 2 +- packages/core/utils/ios-helper.ts | 1 + packages/core/utils/native-helper.android.ts | 5 ++ packages/core/utils/native-helper.d.ts | 3 + packages/core/utils/native-helper.ios.ts | 2 +- 12 files changed, 126 insertions(+), 133 deletions(-) create mode 100644 packages/core/utils/debug-source.ts create mode 100644 packages/core/utils/ios-helper.ts diff --git a/packages/core/application/application.ios.ts b/packages/core/application/application.ios.ts index 1675dcd0d..ecaa53a8b 100644 --- a/packages/core/application/application.ios.ts +++ b/packages/core/application/application.ios.ts @@ -3,6 +3,7 @@ import type { View } from '../ui/core/view'; import { isEmbedded } from '../ui/embedding'; import { IOSHelper } from '../ui/core/view/view-helper'; import { NavigationEntry } from '../ui/frame/frame-interfaces'; +import { getWindow } from '../utils/ios-helper'; import * as Utils from '../utils'; import { ApplicationCommon } from './application-common'; import { ApplicationEventData } from './application-interfaces'; @@ -167,7 +168,7 @@ export class iOSApplication extends ApplicationCommon { } this._rootView = rootView; // Attach to the existing iOS app - const window = Utils.ios.getWindow(); + const window = getWindow() as UIWindow; if (!window) { return; @@ -282,7 +283,7 @@ export class iOSApplication extends ApplicationCommon { // particularly with SwiftUI app lifecycle based apps if (!this._window) { // Note: NativeScriptViewFactory.getKeyWindow will always be used in SwiftUI app lifecycle based apps - this._window = Utils.ios.getWindow(); + this._window = getWindow() as UIWindow; } return this._window; diff --git a/packages/core/file-system/file-system-access.android.ts b/packages/core/file-system/file-system-access.android.ts index bc2cb82f5..70514e38b 100644 --- a/packages/core/file-system/file-system-access.android.ts +++ b/packages/core/file-system/file-system-access.android.ts @@ -1,21 +1,11 @@ import * as textModule from '../text'; -import { Application } from '../application'; -import { getFileExtension } from '../utils'; +import { getFileExtension, android as androidUtils } from '../utils'; import { SDK_VERSION } from '../utils/constants'; import type { IFileSystemAccess } from './file-system-access'; -let applicationContext: android.content.Context; -function getApplicationContext() { - if (!applicationContext) { - applicationContext = Application.android.getNativeApplication().getApplicationContext(); - } - - return applicationContext; -} - function getOrSetHelper(path: string): org.nativescript.widgets.FileHelper { - return org.nativescript.widgets.FileHelper.fromString(getApplicationContext(), path); + return org.nativescript.widgets.FileHelper.fromString(androidUtils.getApplicationContext(), path); } function isContentUri(path: string): boolean { @@ -222,30 +212,30 @@ export class FileSystemAccess implements IFileSystemAccess { } public getDocumentsFolderPath(): string { - const dir = getApplicationContext().getFilesDir(); + const dir = androidUtils.getApplicationContext().getFilesDir(); return dir.getAbsolutePath(); } public getExternalDocumentsFolderPath(): string { - const dirs = getApplicationContext().getExternalFilesDirs(null); + const dirs = androidUtils.getApplicationContext().getExternalFilesDirs(null); let dir; if (dirs && dirs.length > 1) { dir = dirs[dirs.length - 1]; } if (!dir) { - dir = getApplicationContext().getExternalFilesDir(null); + dir = androidUtils.getApplicationContext().getExternalFilesDir(null); } return dir.getAbsolutePath(); } public getLogicalRootPath(): string { - const dir = getApplicationContext().getFilesDir(); + const dir = androidUtils.getApplicationContext().getFilesDir(); return dir.getCanonicalPath(); } public getTempFolderPath(): string { - const dir = getApplicationContext().getCacheDir(); + const dir = androidUtils.getApplicationContext().getCacheDir(); return dir.getAbsolutePath(); } @@ -258,7 +248,7 @@ export class FileSystemAccess implements IFileSystemAccess { public copySync(src: string, dest: string, onError?: (error: any) => any) { try { - return org.nativescript.widgets.Async.File.copySync(src, dest, getApplicationContext()); + return org.nativescript.widgets.Async.File.copySync(src, dest, androidUtils.getApplicationContext()); } catch (error) { if (onError) { onError(error); @@ -282,7 +272,7 @@ export class FileSystemAccess implements IFileSystemAccess { reject(err); }, }), - getApplicationContext(), + androidUtils.getApplicationContext(), ); } catch (ex) { reject(ex); @@ -926,7 +916,7 @@ export class FileSystemAccess29 extends FileSystemAccess { } fileExists(path: string): boolean { if (isContentUri(path)) { - return org.nativescript.widgets.FileHelper.exists(applicationContext, path); + return org.nativescript.widgets.FileHelper.exists(androidUtils.getApplicationContext(), path); } return super.fileExists(path); } @@ -939,7 +929,7 @@ export class FileSystemAccess29 extends FileSystemAccess { deleteFile(path: string, onError?: (error: any) => any) { if (isContentUri(path)) { try { - getOrSetHelper(path).delete(applicationContext); + getOrSetHelper(path).delete(androidUtils.getApplicationContext()); } catch (e) { onError?.(e); } @@ -968,7 +958,7 @@ export class FileSystemAccess29 extends FileSystemAccess { }, }); } - getOrSetHelper(path).renameSync(applicationContext, newPath, callback); + getOrSetHelper(path).renameSync(androidUtils.getApplicationContext(), newPath, callback); } else { super.rename(path, newPath, onError); } @@ -977,7 +967,7 @@ export class FileSystemAccess29 extends FileSystemAccess { public renameAsync(path: string, newPath: string): Promise { return new Promise((resolve, reject) => { getOrSetHelper(path).renameSync( - applicationContext, + androidUtils.getApplicationContext(), newPath, new org.nativescript.widgets.FileHelper.Callback({ onSuccess(result) { @@ -1010,7 +1000,7 @@ export class FileSystemAccess29 extends FileSystemAccess { if (isContentUri(path)) { return new Promise((resolve, reject) => { getOrSetHelper(path).appendBuffer( - applicationContext, + androidUtils.getApplicationContext(), FileSystemAccess.getBuffer(content), new org.nativescript.widgets.FileHelper.Callback({ onSuccess(result) { @@ -1037,7 +1027,7 @@ export class FileSystemAccess29 extends FileSystemAccess { }, }); } - getOrSetHelper(path).appendSync(applicationContext, FileSystemAccess.getBuffer(content), callback); + getOrSetHelper(path).appendSync(androidUtils.getApplicationContext(), FileSystemAccess.getBuffer(content), callback); } else { super.appendSync(path, content, onError); } @@ -1049,7 +1039,7 @@ export class FileSystemAccess29 extends FileSystemAccess { if (isContentUri(path)) { return new Promise((resolve, reject) => { getOrSetHelper(path).append( - applicationContext, + androidUtils.getApplicationContext(), content, new org.nativescript.widgets.FileHelper.Callback({ onSuccess(result) { @@ -1076,7 +1066,7 @@ export class FileSystemAccess29 extends FileSystemAccess { }, }); } - getOrSetHelper(path).appendSync(applicationContext, content, callback); + getOrSetHelper(path).appendSync(androidUtils.getApplicationContext(), content, callback); } else { super.appendSync(path, content, onError); } @@ -1088,7 +1078,7 @@ export class FileSystemAccess29 extends FileSystemAccess { if (isContentUri(path)) { return new Promise((resolve, reject) => { getOrSetHelper(path).appendText( - applicationContext, + androidUtils.getApplicationContext(), content, encoding ?? null, new org.nativescript.widgets.FileHelper.Callback({ @@ -1116,7 +1106,7 @@ export class FileSystemAccess29 extends FileSystemAccess { }, }); } - getOrSetHelper(path).appendTextSync(applicationContext, content, encoding ?? null, callback); + getOrSetHelper(path).appendTextSync(androidUtils.getApplicationContext(), content, encoding ?? null, callback); } else { super.appendTextSync(path, content, onError); } @@ -1128,7 +1118,7 @@ export class FileSystemAccess29 extends FileSystemAccess { if (isContentUri(path)) { return new Promise((resolve, reject) => { getOrSetHelper(path).readText( - applicationContext, + androidUtils.getApplicationContext(), encoding ?? null, new org.nativescript.widgets.FileHelper.Callback({ onSuccess(result) { @@ -1154,7 +1144,7 @@ export class FileSystemAccess29 extends FileSystemAccess { }, }); } - return getOrSetHelper(path).readTextSync(applicationContext, encoding ?? null, callback); + return getOrSetHelper(path).readTextSync(androidUtils.getApplicationContext(), encoding ?? null, callback); } else { return super.readTextSync(path, onError, encoding); } @@ -1166,7 +1156,7 @@ export class FileSystemAccess29 extends FileSystemAccess { if (isContentUri(path)) { return new Promise((resolve, reject) => { getOrSetHelper(path).readBuffer( - applicationContext, + androidUtils.getApplicationContext(), new org.nativescript.widgets.FileHelper.Callback({ onSuccess(result) { resolve(result); @@ -1192,7 +1182,7 @@ export class FileSystemAccess29 extends FileSystemAccess { }, }); } - const ret = getOrSetHelper(path).readBufferSync(applicationContext, callback); + const ret = getOrSetHelper(path).readBufferSync(androidUtils.getApplicationContext(), callback); if (ret) { return null; } @@ -1207,7 +1197,7 @@ export class FileSystemAccess29 extends FileSystemAccess { if (isContentUri(path)) { return new Promise((resolve, reject) => { getOrSetHelper(path).read( - applicationContext, + androidUtils.getApplicationContext(), new org.nativescript.widgets.FileHelper.Callback({ onSuccess(result) { resolve(result); @@ -1233,7 +1223,7 @@ export class FileSystemAccess29 extends FileSystemAccess { }, }); } - return getOrSetHelper(path).readSync(applicationContext, callback); + return getOrSetHelper(path).readSync(androidUtils.getApplicationContext(), callback); } return super.readSync(path, onError); } @@ -1244,7 +1234,7 @@ export class FileSystemAccess29 extends FileSystemAccess { if (isContentUri(path)) { return new Promise((resolve, reject) => { getOrSetHelper(path).writeText( - applicationContext, + androidUtils.getApplicationContext(), content, encoding ?? null, new org.nativescript.widgets.FileHelper.Callback({ @@ -1272,7 +1262,7 @@ export class FileSystemAccess29 extends FileSystemAccess { }, }); } - getOrSetHelper(path).writeTextSync(applicationContext, content, encoding ?? null, callback); + getOrSetHelper(path).writeTextSync(androidUtils.getApplicationContext(), content, encoding ?? null, callback); } else { super.writeTextSync(path, content, onError); } @@ -1284,7 +1274,7 @@ export class FileSystemAccess29 extends FileSystemAccess { if (isContentUri(path)) { return new Promise((resolve, reject) => { getOrSetHelper(path).writeBuffer( - applicationContext, + androidUtils.getApplicationContext(), FileSystemAccess.getBuffer(content), new org.nativescript.widgets.FileHelper.Callback({ onSuccess(result) { @@ -1311,7 +1301,7 @@ export class FileSystemAccess29 extends FileSystemAccess { }, }); } - getOrSetHelper(path).writeSync(applicationContext, FileSystemAccess.getBuffer(content), callback); + getOrSetHelper(path).writeSync(androidUtils.getApplicationContext(), FileSystemAccess.getBuffer(content), callback); } else { super.writeSync(path, content, onError); } @@ -1323,7 +1313,7 @@ export class FileSystemAccess29 extends FileSystemAccess { if (isContentUri(path)) { return new Promise((resolve, reject) => { getOrSetHelper(path).write( - applicationContext, + androidUtils.getApplicationContext(), content, new org.nativescript.widgets.FileHelper.Callback({ onSuccess(result) { @@ -1350,7 +1340,7 @@ export class FileSystemAccess29 extends FileSystemAccess { }, }); } - getOrSetHelper(path).writeSync(applicationContext, content, callback); + getOrSetHelper(path).writeSync(androidUtils.getApplicationContext(), content, callback); } else { super.writeSync(path, content, onError); } diff --git a/packages/core/file-system/index.ts b/packages/core/file-system/index.ts index 1ff959eed..42055416c 100644 --- a/packages/core/file-system/index.ts +++ b/packages/core/file-system/index.ts @@ -1,6 +1,6 @@ import { IFileSystemAccess, FileSystemAccess, FileSystemAccess29 } from './file-system-access'; -import { SDK_VERSION } from '../utils'; -import { Application } from '../application'; +import { SDK_VERSION } from '../utils/constants'; +import { android as androidUtils } from '../utils'; // The FileSystemAccess implementation, used through all the APIs. let fileAccess: IFileSystemAccess; @@ -182,15 +182,6 @@ export class FileSystemEntity { } } -let applicationContext; -function getApplicationContext() { - if (!applicationContext) { - applicationContext = Application.android.getNativeApplication().getApplicationContext(); - } - - return applicationContext; -} - export enum AndroidDirectory { ALARMS = 'alarms', AUDIOBOOKS = 'audiobooks', @@ -279,7 +270,7 @@ class Android { throw new Error(`createFile is available on Android only!`); } - const context = getApplicationContext() as android.content.Context; + const context = androidUtils.getApplicationContext() as android.content.Context; const meta = new android.content.ContentValues(); meta.put(android.provider.MediaStore.MediaColumns.DISPLAY_NAME, options.name); @@ -332,7 +323,7 @@ export class File extends FileSystemEntity { // falls back to creating a temp file without a known extension. if (!fileInfo) { const tempFile = `${knownFolders.temp().path}/${java.util.UUID.randomUUID().toString()}`; - org.nativescript.widgets.Async.File.copySync(path, tempFile, getApplicationContext()); + org.nativescript.widgets.Async.File.copySync(path, tempFile, androidUtils.getApplicationContext()); path = tempFile; } else { const ext = fileInfo.extension; diff --git a/packages/core/platform/screen/index.ios.ts b/packages/core/platform/screen/index.ios.ts index 6a036ff18..acfed170d 100644 --- a/packages/core/platform/screen/index.ios.ts +++ b/packages/core/platform/screen/index.ios.ts @@ -1,4 +1,4 @@ -import { ios } from '../../utils'; +import { getWindow } from '../../utils/ios-helper'; class MainScreen { private _screen: UIScreen; @@ -6,7 +6,7 @@ class MainScreen { private get screen(): UIScreen { if (!this._screen) { // NOTE: may not want to cache this value with SwiftUI app lifecycle based apps (using NativeScriptViewFactory) given the potential of multiple scenes - const window = ios.getWindow(); + const window = getWindow() as UIWindow; this._screen = window ? window.screen : UIScreen.mainScreen; } diff --git a/packages/core/ui/core/view-base/index.ts b/packages/core/ui/core/view-base/index.ts index 8beebac20..9d5034b45 100644 --- a/packages/core/ui/core/view-base/index.ts +++ b/packages/core/ui/core/view-base/index.ts @@ -3,7 +3,7 @@ import { Page } from '../../page'; import { CoreTypes } from '../../../core-types'; import { Property, CssProperty, CssAnimationProperty, InheritedProperty, clearInheritedProperties, propagateInheritableProperties, propagateInheritableCssProperties, initNativeView } from '../properties'; import { CSSUtils } from '../../../css/system-classes'; -import { Source } from '../../../utils/debug'; +import { Source } from '../../../utils/debug-source'; import { Binding } from '../bindable'; import { BindingOptions } from '../bindable/bindable-types'; import { Trace } from '../../../trace'; diff --git a/packages/core/utils/debug-source.ts b/packages/core/utils/debug-source.ts new file mode 100644 index 000000000..7f3f3747c --- /dev/null +++ b/packages/core/utils/debug-source.ts @@ -0,0 +1,73 @@ +// This file exists to break cycles between debug.ts and file-system. +// Only put logic here that depends on file-system (e.g., knownFolders). +import { knownFolders } from '../file-system'; + +let applicationRootPath: string; +function ensureAppRootPath() { + if (!applicationRootPath) { + applicationRootPath = knownFolders.currentApp().path; + applicationRootPath = applicationRootPath.substring(0, applicationRootPath.length - 'app/'.length); + } +} + +export class Source { + private _uri: string; + private _line: number; + private _column: number; + + private static _source = Symbol('source'); + + constructor(uri: string, line: number, column: number) { + ensureAppRootPath(); + + if (uri.length > applicationRootPath.length && uri.substring(0, applicationRootPath.length) === applicationRootPath) { + this._uri = 'file://' + uri.substring(applicationRootPath.length); + } else { + this._uri = uri; + } + this._line = line; + this._column = column; + } + + get uri(): string { + return this._uri; + } + get line(): number { + return this._line; + } + get column(): number { + return this._column; + } + + public toString() { + return this._uri + ':' + this._line + ':' + this._column; + } + + public static get(object: any): Source { + return object[Source._source]; + } + + public static set(object: any, src: Source) { + object[Source._source] = src; + } +} + +export class ScopeError extends Error { + constructor(inner: Error, message?: string) { + let formattedMessage; + if (message && inner.message) { + formattedMessage = message + '\n > ' + inner.message.replace('\n', '\n '); + } else { + formattedMessage = message || inner.message || undefined; + } + super(formattedMessage); + this.stack = __ANDROID__ ? 'Error: ' + this.message + '\n' + inner.stack.substr(inner.stack.indexOf('\n') + 1) : inner.stack; + this.message = formattedMessage; + } +} + +export class SourceError extends ScopeError { + constructor(child: Error, source: Source, message?: string) { + super(child, message ? message + ' @' + source + '' : source + ''); + } +} diff --git a/packages/core/utils/debug.ts b/packages/core/utils/debug.ts index c830a0a67..43030b472 100644 --- a/packages/core/utils/debug.ts +++ b/packages/core/utils/debug.ts @@ -1,73 +1,2 @@ -import { knownFolders } from '../file-system'; - export const debug = true; - -let applicationRootPath: string; -function ensureAppRootPath() { - if (!applicationRootPath) { - applicationRootPath = knownFolders.currentApp().path; - applicationRootPath = applicationRootPath.substring(0, applicationRootPath.length - 'app/'.length); - } -} - -export class Source { - private _uri: string; - private _line: number; - private _column: number; - - private static _source = Symbol('source'); - - constructor(uri: string, line: number, column: number) { - ensureAppRootPath(); - - if (uri.length > applicationRootPath.length && uri.substring(0, applicationRootPath.length) === applicationRootPath) { - this._uri = 'file://' + uri.substring(applicationRootPath.length); - } else { - this._uri = uri; - } - this._line = line; - this._column = column; - } - - get uri(): string { - return this._uri; - } - get line(): number { - return this._line; - } - get column(): number { - return this._column; - } - - public toString() { - return this._uri + ':' + this._line + ':' + this._column; - } - - public static get(object: any): Source { - return object[Source._source]; - } - - public static set(object: any, src: Source) { - object[Source._source] = src; - } -} - -export class ScopeError extends Error { - constructor(inner: Error, message?: string) { - let formattedMessage; - if (message && inner.message) { - formattedMessage = message + '\n > ' + inner.message.replace('\n', '\n '); - } else { - formattedMessage = message || inner.message || undefined; - } - super(formattedMessage); - this.stack = __ANDROID__ ? 'Error: ' + this.message + '\n' + inner.stack.substr(inner.stack.indexOf('\n') + 1) : inner.stack; - this.message = formattedMessage; - } -} - -export class SourceError extends ScopeError { - constructor(child: Error, source: Source, message?: string) { - super(child, message ? message + ' @' + source + '' : source + ''); - } -} +export { Source, ScopeError, SourceError } from './debug-source'; diff --git a/packages/core/utils/index.ios.ts b/packages/core/utils/index.ios.ts index 7314e22c8..390626536 100644 --- a/packages/core/utils/index.ios.ts +++ b/packages/core/utils/index.ios.ts @@ -8,7 +8,7 @@ export * from './debug'; export * from './layout-helper'; export * from './macrotask-scheduler'; export * from './mainthread-helper'; -export * from './native-helper'; +export * from './native-helper'; // do not re-export getWindow here, use ios-helper.ts for that export * from './types'; export * from './native-helper'; diff --git a/packages/core/utils/ios-helper.ts b/packages/core/utils/ios-helper.ts new file mode 100644 index 000000000..2073d41c7 --- /dev/null +++ b/packages/core/utils/ios-helper.ts @@ -0,0 +1 @@ +export { getWindow } from './native-helper'; diff --git a/packages/core/utils/native-helper.android.ts b/packages/core/utils/native-helper.android.ts index f6e9c8b59..e82b33991 100644 --- a/packages/core/utils/native-helper.android.ts +++ b/packages/core/utils/native-helper.android.ts @@ -191,6 +191,10 @@ function getInputMethodManager(): android.view.inputmethod.InputMethodManager { return inputMethodManager; } +export function getWindow() { + return getCurrentActivity()?.getWindow(); +} + function showSoftInput(nativeView: android.view.View): void { const inputManager = getInputMethodManager(); if (inputManager && nativeView instanceof android.view.View) { @@ -315,6 +319,7 @@ export const androidUtils = { getApplication, getCurrentActivity, getApplicationContext, + getWindow, getResources, getPackageName, getInputMethodManager, diff --git a/packages/core/utils/native-helper.d.ts b/packages/core/utils/native-helper.d.ts index 3b6311fb5..52433fb18 100644 --- a/packages/core/utils/native-helper.d.ts +++ b/packages/core/utils/native-helper.d.ts @@ -14,6 +14,8 @@ export function dataDeserialize(nativeData?: any): any; */ export function isRealDevice(): boolean; +export function getWindow(): UIWindow | android.view.Window; + // /** // * Module with android specific utilities. // */ @@ -228,6 +230,7 @@ export const android: { getApplication: () => android.app.Application; getCurrentActivity: () => androidx.appcompat.app.AppCompatActivity | android.app.Activity | null; getApplicationContext: () => android.content.Context; + getWindow: () => android.view.Window; getResources: () => android.content.res.Resources; getPackageName: () => string; getInputMethodManager: () => android.view.inputmethod.InputMethodManager; diff --git a/packages/core/utils/native-helper.ios.ts b/packages/core/utils/native-helper.ios.ts index bf2ba049a..ee396a4cd 100644 --- a/packages/core/utils/native-helper.ios.ts +++ b/packages/core/utils/native-helper.ios.ts @@ -173,7 +173,7 @@ function getRootViewController(): UIViewController { return vc; } -function getWindow(): UIWindow { +export function getWindow(): UIWindow { let window: UIWindow; if (SDK_VERSION >= 15 && typeof NativeScriptViewFactory !== 'undefined') { // UIWindowScene.keyWindow is only available 15+