From b5096c3c49caeee4f9ddb66578fd22bb420f4ca0 Mon Sep 17 00:00:00 2001 From: Igor Randjelovic Date: Tue, 9 May 2023 23:54:28 +0200 Subject: [PATCH] refactor: clean up Utils --- packages/core/index.d.ts | 63 +--------------- packages/core/index.ts | 72 +------------------ packages/core/utils/index.android.ts | 25 ++++--- packages/core/utils/index.d.ts | 16 ++--- packages/core/utils/index.ios.ts | 21 +++--- .../core/utils/layout-helper/index.android.ts | 6 +- packages/core/utils/native-helper.android.ts | 51 +++++++------ packages/core/utils/native-helper.d.ts | 19 ++++- packages/core/utils/native-helper.ios.ts | 14 +++- packages/core/utils/platform-check.ts | 26 +++++++ 10 files changed, 130 insertions(+), 183 deletions(-) create mode 100644 packages/core/utils/platform-check.ts diff --git a/packages/core/index.d.ts b/packages/core/index.d.ts index c25e9d2d7..5873998cb 100644 --- a/packages/core/index.d.ts +++ b/packages/core/index.d.ts @@ -107,65 +107,6 @@ export type { InstrumentationMode, TimerInfo } from './profiling'; export { encoding } from './text'; export * from './trace'; export * from './ui'; -import { GC, isFontIconURI, isDataURI, isFileOrResourcePath, executeOnMainThread, mainThreadify, isMainThread, dispatchToMainThread, executeOnUIThread, releaseNativeObject, getModuleName, openFile, openUrl, isRealDevice, layout, ad as androidUtils, iOSNativeHelper as iosUtils, Source, escapeRegexSymbols, convertString, dismissSoftInput, dismissKeyboard, queueMacrotask, queueGC, throttle, debounce, dataSerialize, dataDeserialize, copyToClipboard, getFileExtension, isEmoji, getDurationWithDampingFromSpring } from './utils'; -import { SDK_VERSION } from './utils/constants'; -import { ClassInfo, getClass, getBaseClasses, getClassInfo, isBoolean, isDefined, isFunction, isNullOrUndefined, isNumber, isObject, isString, isUndefined, toUIString, verifyCallback, numberHasDecimals, numberIs64Bit } from './utils/types'; -export declare const Utils: { - GC: typeof GC; - SDK_VERSION: typeof SDK_VERSION; - RESOURCE_PREFIX: string; - FILE_PREFIX: string; - queueMacrotask: typeof queueMacrotask; - queueGC: typeof queueGC; - debounce: typeof debounce; - throttle: typeof throttle; - isFontIconURI: typeof isFontIconURI; - isDataURI: typeof isDataURI; - isFileOrResourcePath: typeof isFileOrResourcePath; - getFileExtension: typeof getFileExtension; - executeOnMainThread: typeof executeOnMainThread; - executeOnUIThread: typeof executeOnUIThread; - mainThreadify: typeof mainThreadify; - isMainThread: typeof isMainThread; - dispatchToMainThread: typeof dispatchToMainThread; - releaseNativeObject: typeof releaseNativeObject; - escapeRegexSymbols: typeof escapeRegexSymbols; - convertString: typeof convertString; - getModuleName: typeof getModuleName; - openFile: typeof openFile; - openUrl: typeof openUrl; - isRealDevice: typeof isRealDevice; - layout: typeof layout; - android: typeof androidUtils; - ad: typeof androidUtils; - ios: typeof iosUtils; - dataSerialize: typeof dataSerialize; - dataDeserialize: typeof dataDeserialize; - numberHasDecimals: typeof numberHasDecimals; - numberIs64Bit: typeof numberIs64Bit; - setTimeout: typeof setTimeout; - setInterval: typeof setInterval; - clearInterval: typeof clearInterval; - clearTimeout: typeof clearTimeout; - Source: typeof Source; - ClassInfo: typeof ClassInfo; - getClass: typeof getClass; - getBaseClasses: typeof getBaseClasses; - getClassInfo: typeof getClassInfo; - isBoolean: typeof isBoolean; - isDefined: typeof isDefined; - isFunction: typeof isFunction; - isNullOrUndefined: typeof isNullOrUndefined; - isNumber: typeof isNumber; - isObject: typeof isObject; - isString: typeof isString; - isUndefined: typeof isUndefined; - toUIString: typeof toUIString; - verifyCallback: typeof verifyCallback; - dismissSoftInput: typeof dismissSoftInput; - dismissKeyboard: typeof dismissKeyboard; - copyToClipboard: typeof copyToClipboard; - isEmoji: typeof isEmoji; - getDurationWithDampingFromSpring: typeof getDurationWithDampingFromSpring; -}; +export * as Utils from './utils'; + export { XmlParser, ParserEventType, ParserEvent } from './xml'; diff --git a/packages/core/index.ts b/packages/core/index.ts index 7a078eb27..28e38c020 100644 --- a/packages/core/index.ts +++ b/packages/core/index.ts @@ -1,3 +1,4 @@ +// eslint-disable-next-line @typescript-eslint/triple-slash-reference /// // Init globals first (use import to ensure it's always at the top) import './globals'; @@ -131,78 +132,9 @@ 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'; -// for developers to be explicit if they desire around globals (allows access via Utils below) -import { setTimeout, setInterval, clearInterval, clearTimeout } from './timer'; export * from './trace'; - export * from './ui'; - -import { GC, isFontIconURI, isDataURI, isFileOrResourcePath, executeOnMainThread, mainThreadify, isMainThread, dispatchToMainThread, executeOnUIThread, queueMacrotask, queueGC, debounce, throttle, releaseNativeObject, getModuleName, openFile, openUrl, isRealDevice, layout, ad as androidUtils, iOSNativeHelper as iosUtils, Source, RESOURCE_PREFIX, FILE_PREFIX, escapeRegexSymbols, convertString, dismissSoftInput, dismissKeyboard, dataDeserialize, dataSerialize, copyToClipboard, getFileExtension, isEmoji, getDurationWithDampingFromSpring } from './utils'; -import { SDK_VERSION } from './utils/constants'; -import { ClassInfo, getClass, getBaseClasses, getClassInfo, isBoolean, isDefined, isFunction, isNullOrUndefined, isNumber, isObject, isString, isUndefined, toUIString, verifyCallback, numberHasDecimals, numberIs64Bit } from './utils/types'; - -export const Utils = { - GC, - SDK_VERSION, - RESOURCE_PREFIX, - FILE_PREFIX, - isFontIconURI, - isDataURI, - isFileOrResourcePath, - getFileExtension, - executeOnMainThread, - executeOnUIThread, - mainThreadify, - isMainThread, - dispatchToMainThread, - queueMacrotask, - queueGC, - debounce, - throttle, - releaseNativeObject, - convertString, - escapeRegexSymbols, - - getModuleName, - openFile, - openUrl, - isRealDevice, - - layout, - android: androidUtils, - // legacy (a lot of plugins use the shorthand "ad" Utils.ad instead of Utils.android) - ad: androidUtils, - ios: iosUtils, - dataSerialize, - dataDeserialize, - numberHasDecimals, - numberIs64Bit, - setTimeout, - setInterval, - clearInterval, - clearTimeout, - Source, - ClassInfo, - getClass, - getBaseClasses, - getClassInfo, - isBoolean, - isDefined, - isFunction, - isNullOrUndefined, - isNumber, - isObject, - isString, - isUndefined, - toUIString, - verifyCallback, - dismissSoftInput, - dismissKeyboard, - copyToClipboard, - isEmoji, - getDurationWithDampingFromSpring, -}; +export * as Utils from './utils'; export { XmlParser, ParserEventType, ParserEvent } from './xml'; diff --git a/packages/core/utils/index.android.ts b/packages/core/utils/index.android.ts index 5677c5451..429ea7eed 100644 --- a/packages/core/utils/index.android.ts +++ b/packages/core/utils/index.android.ts @@ -1,12 +1,17 @@ -import { ad } from './native-helper'; import { Trace } from '../trace'; import { getFileExtension } from './common'; import { SDK_VERSION } from './constants'; +import { android as AndroidUtils } from './native-helper'; -export { ad, dataDeserialize, dataSerialize, iOSNativeHelper } from './native-helper'; -export * from './layout-helper'; +export { clearInterval, clearTimeout, setInterval, setTimeout } from '../timer'; export * from './common'; -export { Source } from './debug'; +export * from './constants'; +export * from './debug'; +export * from './layout-helper'; +export * from './macrotask-scheduler'; +export * from './mainthread-helper'; +export * from './native-helper'; +export * from './types'; const MIN_URI_SHARE_RESTRICTED_APK_VERSION = 24; @@ -19,7 +24,7 @@ export function releaseNativeObject(object: java.lang.Object) { } export function openUrl(location: string): boolean { - const context = ad.getApplicationContext(); + const context = AndroidUtils.getApplicationContext(); try { const intent = new android.content.Intent(android.content.Intent.ACTION_VIEW, android.net.Uri.parse(location.trim())); intent.addFlags(android.content.Intent.FLAG_ACTIVITY_NEW_TASK); @@ -82,7 +87,7 @@ function getMimeTypeNameFromExtension(filePath: string): string { * @returns {boolean} whether opening the file succeeded or not */ export function openFile(filePath: string, title: string = 'Open File...'): boolean { - const context = ad.getApplicationContext(); + const context = AndroidUtils.getApplicationContext(); try { // Ensure external storage is available if (!isExternalStorageAvailable()) { @@ -164,17 +169,17 @@ Please ensure you have your manifest correctly configured with the FileProvider. } export function isRealDevice(): boolean { - return ad.isRealDevice(); + return AndroidUtils.isRealDevice(); } export function dismissSoftInput(nativeView?: any): void { - ad.dismissSoftInput(nativeView); + AndroidUtils.dismissSoftInput(nativeView); } export function dismissKeyboard() { dismissSoftInput(); - const activity = ad.getCurrentActivity(); + const activity = AndroidUtils.getCurrentActivity(); if (activity) { const focus = activity.getCurrentFocus(); @@ -186,7 +191,7 @@ export function dismissKeyboard() { export function copyToClipboard(value: string) { try { - const clipboard = ad.getApplicationContext().getSystemService(android.content.Context.CLIPBOARD_SERVICE); + const clipboard = AndroidUtils.getApplicationContext().getSystemService(android.content.Context.CLIPBOARD_SERVICE); const clip = android.content.ClipData.newPlainText('Clipboard value', value); clipboard.setPrimaryClip(clip); } catch (err) { diff --git a/packages/core/utils/index.d.ts b/packages/core/utils/index.d.ts index 63c785c71..90beec410 100644 --- a/packages/core/utils/index.d.ts +++ b/packages/core/utils/index.d.ts @@ -1,12 +1,12 @@ -import { CoreTypes } from '../core-types'; - -export * from './mainthread-helper'; -export * from './macrotask-scheduler'; -export { Source } from './debug'; - -export * from './layout-helper'; -export * from './native-helper'; +export { clearInterval, clearTimeout, setInterval, setTimeout } from '../timer'; export * from './common'; +export * from './constants'; +export * from './debug'; +export * from './layout-helper'; +export * from './macrotask-scheduler'; +export * from './mainthread-helper'; +export * from './native-helper'; +export * from './types'; export const RESOURCE_PREFIX: string; export const FILE_PREFIX: string; diff --git a/packages/core/utils/index.ios.ts b/packages/core/utils/index.ios.ts index 815218894..5ea39931c 100644 --- a/packages/core/utils/index.ios.ts +++ b/packages/core/utils/index.ios.ts @@ -1,18 +1,23 @@ -import { iOSNativeHelper } from './native-helper'; import { Trace } from '../trace'; +import { ios as iOSUtils } from './native-helper'; -export { dataDeserialize, dataSerialize, iOSNativeHelper } from './native-helper'; -export * from './layout-helper'; +export { clearInterval, clearTimeout, setInterval, setTimeout } from '../timer'; export * from './common'; -export { Source } from './debug'; +export * from './constants'; +export * from './debug'; +export * from './layout-helper'; +export * from './macrotask-scheduler'; +export * from './mainthread-helper'; +export * from './native-helper'; +export * from './types'; export function openFile(filePath: string): boolean { try { - const appPath = iOSNativeHelper.getCurrentAppPath(); - const path = iOSNativeHelper.isRealDevice() ? filePath.replace('~', appPath) : filePath; + const appPath = iOSUtils.getCurrentAppPath(); + const path = iOSUtils.isRealDevice() ? filePath.replace('~', appPath) : filePath; const controller = UIDocumentInteractionController.interactionControllerWithURL(NSURL.fileURLWithPath(path)); - controller.delegate = iOSNativeHelper.createUIDocumentInteractionControllerDelegate(); + controller.delegate = iOSUtils.createUIDocumentInteractionControllerDelegate(); return controller.presentPreviewAnimated(true); } catch (e) { @@ -45,7 +50,7 @@ export function openUrl(location: string): boolean { } export function isRealDevice(): boolean { - return iOSNativeHelper.isRealDevice(); + return iOSUtils.isRealDevice(); } export const ad = 0; diff --git a/packages/core/utils/layout-helper/index.android.ts b/packages/core/utils/layout-helper/index.android.ts index 920b7a123..825858729 100644 --- a/packages/core/utils/layout-helper/index.android.ts +++ b/packages/core/utils/layout-helper/index.android.ts @@ -1,5 +1,5 @@ import * as layoutCommon from './layout-helper-common'; -import { ad } from '../native-helper'; +import { android as AndroidUtils } from '../native-helper'; // export * from './layout-helper-common'; @@ -38,7 +38,7 @@ export namespace layout { export function makeMeasureSpec(size: number, mode: number): number { if (sdkVersion === undefined) { // check whether the old layout is needed - sdkVersion = ad.getApplicationContext().getApplicationInfo().targetSdkVersion; + sdkVersion = AndroidUtils.getApplicationContext().getApplicationInfo().targetSdkVersion; useOldMeasureSpec = sdkVersion <= 17; } @@ -51,7 +51,7 @@ export namespace layout { export function getDisplayDensity(): number { if (density === undefined) { - density = ad.getResources().getDisplayMetrics().density; + density = AndroidUtils.getResources().getDisplayMetrics().density; } return density; diff --git a/packages/core/utils/native-helper.android.ts b/packages/core/utils/native-helper.android.ts index 3d8339107..db4b0ae64 100644 --- a/packages/core/utils/native-helper.android.ts +++ b/packages/core/utils/native-helper.android.ts @@ -1,7 +1,10 @@ +import { platformCheck } from './platform-check'; import { getNativeApplication, android as androidApp } from '../application'; import { Trace } from '../trace'; import { numberHasDecimals, numberIs64Bit } from './types'; +const globalThis = global; + export function dataDeserialize(nativeData?: any) { if (nativeData === null || typeof nativeData !== 'object') { return nativeData; @@ -140,11 +143,10 @@ export function dataSerialize(data?: any, wrapPrimitives?: boolean) { } } -// We are using "ad" here to avoid namespace collision with the global android object -export namespace ad { - let application: android.app.Application; - let applicationContext: android.content.Context; - let contextResources: android.content.res.Resources; +namespace AndroidUtils { + let application: globalThis.android.app.Application; + let applicationContext: globalThis.android.content.Context; + let contextResources: globalThis.android.content.res.Resources; let packageName: string; export function getApplicationContext() { if (!applicationContext) { @@ -161,7 +163,7 @@ export namespace ad { } export function getApplication() { if (!application) { - application = getNativeApplication(); + application = getNativeApplication(); } return application; @@ -181,27 +183,27 @@ export namespace ad { return packageName; } - let inputMethodManager: android.view.inputmethod.InputMethodManager; - export function getInputMethodManager(): android.view.inputmethod.InputMethodManager { + let inputMethodManager: globalThis.android.view.inputmethod.InputMethodManager; + export function getInputMethodManager(): globalThis.android.view.inputmethod.InputMethodManager { if (!inputMethodManager) { - inputMethodManager = getApplicationContext().getSystemService(android.content.Context.INPUT_METHOD_SERVICE); + inputMethodManager = getApplicationContext().getSystemService(globalThis.android.content.Context.INPUT_METHOD_SERVICE); } return inputMethodManager; } - export function showSoftInput(nativeView: android.view.View): void { + export function showSoftInput(nativeView: globalThis.android.view.View): void { const inputManager = getInputMethodManager(); - if (inputManager && nativeView instanceof android.view.View) { - inputManager.showSoftInput(nativeView, android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT); + if (inputManager && nativeView instanceof globalThis.android.view.View) { + inputManager.showSoftInput(nativeView, globalThis.android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT); } } - export function dismissSoftInput(nativeView?: android.view.View): void { + export function dismissSoftInput(nativeView?: globalThis.android.view.View): void { const inputManager = getInputMethodManager(); - let windowToken: android.os.IBinder; + let windowToken: globalThis.android.os.IBinder; - if (nativeView instanceof android.view.View) { + if (nativeView instanceof globalThis.android.view.View) { if (!nativeView.hasFocus()) { return; } @@ -269,10 +271,10 @@ export namespace ad { export function getResource(name: string, type?: string): number { return getResources().getIdentifier(name, type, getPackageName()); } - export function getPalleteColor(name: string, context: android.content.Context): number { + export function getPalleteColor(name: string, context: globalThis.android.content.Context): number { return getPaletteColor(name, context); } - export function getPaletteColor(name: string, context: android.content.Context): number { + export function getPaletteColor(name: string, context: globalThis.android.content.Context): number { if (attrCache.has(name)) { return attrCache.get(name); } @@ -290,7 +292,7 @@ export namespace ad { } if (colorID) { - const typedValue = new android.util.TypedValue(); + const typedValue = new globalThis.android.util.TypedValue(); context.getTheme().resolveAttribute(colorID, typedValue, true); result = typedValue.data; } @@ -305,10 +307,19 @@ export namespace ad { } export function isRealDevice(): boolean { - const fingerprint = android.os.Build.FINGERPRINT; + const fingerprint = globalThis.android.os.Build.FINGERPRINT; return fingerprint != null && (fingerprint.indexOf('vbox') > -1 || fingerprint.indexOf('generic') > -1); } } -export const iOSNativeHelper = 0; +/** + * @deprecated Use `Utils.android` instead. + */ +export import ad = AndroidUtils; + +export import android = AndroidUtils; + +// these don't exist on Android.Stub them to empty functions. +export const iOSNativeHelper = platformCheck('Utils.iOSNativeHelper'); +export const ios = platformCheck('Utils.ios'); diff --git a/packages/core/utils/native-helper.d.ts b/packages/core/utils/native-helper.d.ts index 65fadf36f..ceac0f4bc 100644 --- a/packages/core/utils/native-helper.d.ts +++ b/packages/core/utils/native-helper.d.ts @@ -12,7 +12,7 @@ export function dataDeserialize(nativeData?: any): any; /** * Module with android specific utilities. */ -export namespace ad { +declare namespace AndroidUtils { /** * Gets the native Android application instance. */ @@ -113,10 +113,11 @@ export namespace ad { */ export function isRealDevice(): boolean; } + /** * Module with ios specific utilities. */ -export namespace iOSNativeHelper { +declare namespace iOSUtils { // Common properties between UILabel, UITextView and UITextField export interface TextUIView { font: any; @@ -268,3 +269,17 @@ export namespace iOSNativeHelper { */ export function animateWithSpring(options?: { tension?: number; friction?: number; mass?: number; delay?: number; velocity?: number; animateOptions?: UIViewAnimationOptions; animations?: () => void; completion?: (finished?: boolean) => void }); } + +/** + * @deprecated use Utils.android instead. + */ +export import ad = AndroidUtils; + +export import android = AndroidUtils; + +/** + * @deprecated use Utils.ios instead. + */ +export import iOSNativeHelper = iOSUtils; + +export import ios = iOSUtils; diff --git a/packages/core/utils/native-helper.ios.ts b/packages/core/utils/native-helper.ios.ts index 5b8371d09..f3ec0b7f6 100644 --- a/packages/core/utils/native-helper.ios.ts +++ b/packages/core/utils/native-helper.ios.ts @@ -1,3 +1,4 @@ +import { platformCheck } from './platform-check'; import { Color } from '../color'; import { Trace } from '../trace'; import { CORE_ANIMATION_DEFAULTS, getDurationWithDampingFromSpring } from './common'; @@ -112,7 +113,7 @@ export function dataSerialize(data: any, wrapPrimitives: boolean = false) { } } -export namespace iOSNativeHelper { +namespace iOSUtils { // TODO: remove for NativeScript 9.0 export function getter(_this: any, property: T | { (): T }): T { console.log('utils.ios.getter() is deprecated; use the respective native property instead'); @@ -416,3 +417,14 @@ export namespace iOSNativeHelper { UIView.animateWithDurationDelayUsingSpringWithDampingInitialSpringVelocityOptionsAnimationsCompletion(duration, opt.delay, damping, opt.velocity, opt.animateOptions, opt.animations, opt.completion); } } + +// these don't exist on iOS. Stub them to empty functions. +export const ad = platformCheck('Utils.ad'); +export const android = platformCheck('Utils.android'); + +/** + * @deprecated Use `Utils.ios` instead. + */ +export import iOSNativeHelper = iOSUtils; + +export import ios = iOSUtils; diff --git a/packages/core/utils/platform-check.ts b/packages/core/utils/platform-check.ts new file mode 100644 index 000000000..68f8385d9 --- /dev/null +++ b/packages/core/utils/platform-check.ts @@ -0,0 +1,26 @@ +/** + * @internal Util used for exporting opposing platform utils and warning the user if they are trying to access them. + */ +export function platformCheck(parent?: string) { + if (__DEV__) { + return new Proxy( + {}, + { + get(_, prop) { + const propPretty = [parent, prop.toString()].join('.'); + const hintPlatformCheck = global.isAndroid ? 'global.isIOS' : 'global.isAndroid'; + + // prettier-ignore + const errorMsg = [ + `Trying to access "${propPretty}" without checking platform first.`, + `Hint: Use "${hintPlatformCheck}" to check platform before accessing "${propPretty}".` + ].join('\n'); + + throw new Error(errorMsg); + }, + } + ); + } + + return undefined; +}