refactor: circular deps part 6

This commit is contained in:
Nathan Walker
2025-07-08 11:55:45 -07:00
parent b70aa2c45f
commit 18e75b66ec
21 changed files with 603 additions and 575 deletions

View File

@@ -1,7 +1,7 @@
import { Application, ApplicationEventData } from '../application';
import { Trace } from '../trace';
import { SDK_VERSION } from '../utils/constants';
import { resources } from '../utils/android';
import { android as androidUtils } from '../utils';
import type { View } from '../ui/core/view';
import { GestureTypes } from '../ui/gestures';
import { notifyAccessibilityFocusState } from './accessibility-common';
@@ -168,7 +168,7 @@ function ensureNativeClasses() {
}
// Set resource id that can be used with test frameworks without polluting the content description.
const id = host.getTag(resources.getId(`:id/nativescript_accessibility_id`));
const id = host.getTag(androidUtils.resources.getId(`:id/nativescript_accessibility_id`));
if (id != null) {
info.setViewIdResourceName(id);
}

View File

@@ -1,5 +1,5 @@
import { encoding as textEncoding } from '../text';
import { iOSNativeHelper } from '../utils';
import { ios as iosUtils } from '../utils';
// TODO: Implement all the APIs receiving callback using async blocks
// TODO: Check whether we need try/catch blocks for the iOS implementation
@@ -257,7 +257,7 @@ export class FileSystemAccess {
}
public getCurrentAppPath(): string {
return iOSNativeHelper.getCurrentAppPath();
return iosUtils.getCurrentAppPath();
}
public copy = this.copySync.bind(this);
@@ -700,7 +700,7 @@ export class FileSystemAccess {
}
public joinPaths(paths: string[]): string {
return iOSNativeHelper.joinPaths(...paths);
return iosUtils.joinPaths(...paths);
}
}

View File

@@ -4,7 +4,7 @@ import * as imageSourceModule from '../../image-source';
import * as fsModule from '../../file-system';
import { SDK_VERSION } from '../../utils/constants';
import { isRealDevice } from '../../utils/ios';
import { isRealDevice } from '../../utils';
import * as types from '../../utils/types';
import * as domainDebugger from '../../debugger';
import { getFilenameFromUrl } from './http-request-common';

View File

@@ -7,13 +7,12 @@ import { LinearGradient } from '../styling/linear-gradient';
import { colorProperty, backgroundInternalProperty, backgroundColorProperty, backgroundImageProperty } from '../styling/style-properties';
import { ios as iosViewUtils } from '../utils';
import { ImageSource } from '../../image-source';
import { layout, iOSNativeHelper, isFontIconURI } from '../../utils';
import { layout, isFontIconURI } from '../../utils';
import { SDK_VERSION } from '../../utils/constants';
import { accessibilityHintProperty, accessibilityLabelProperty, accessibilityLanguageProperty, accessibilityValueProperty } from '../../accessibility/accessibility-properties';
export * from './action-bar-common';
const majorVersion = iOSNativeHelper.MajorVersion;
const UNSPECIFIED = layout.makeMeasureSpec(0, layout.UNSPECIFIED);
interface NSUINavigationBar extends UINavigationBar {
@@ -271,7 +270,7 @@ export class ActionBar extends ActionBarBase {
// show the one from the old page but the new page will still be visible (because we canceled EdgeBackSwipe gesutre)
// Consider moving this to new method and call it from - navigationControllerDidShowViewControllerAnimated.
const image = img ? img.imageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal) : null;
if (majorVersion >= 15) {
if (SDK_VERSION >= 15) {
const appearance = this._getAppearance(navigationBar);
appearance.setBackIndicatorImageTransitionMaskImage(image, image);
this._updateAppearance(navigationBar, appearance);
@@ -378,7 +377,7 @@ export class ActionBar extends ActionBarBase {
}
if (color) {
const titleTextColor = NSDictionary.dictionaryWithObjectForKey(color.ios, NSForegroundColorAttributeName);
if (majorVersion >= 15) {
if (SDK_VERSION >= 15) {
const appearance = this._getAppearance(navBar);
appearance.titleTextAttributes = titleTextColor;
}
@@ -398,7 +397,7 @@ export class ActionBar extends ActionBarBase {
}
const nativeColor = color instanceof Color ? color.ios : color;
if (__VISIONOS__ || majorVersion >= 15) {
if (__VISIONOS__ || SDK_VERSION >= 15) {
const appearance = this._getAppearance(navBar);
// appearance.configureWithOpaqueBackground();
appearance.backgroundColor = nativeColor;
@@ -416,7 +415,7 @@ export class ActionBar extends ActionBarBase {
let color: UIColor;
if (__VISIONOS__ || majorVersion >= 15) {
if (__VISIONOS__ || SDK_VERSION >= 15) {
const appearance = this._getAppearance(navBar);
color = appearance.backgroundColor;
} else {
@@ -432,7 +431,7 @@ export class ActionBar extends ActionBarBase {
return;
}
if (__VISIONOS__ || majorVersion >= 15) {
if (__VISIONOS__ || SDK_VERSION >= 15) {
const appearance = this._getAppearance(navBar);
// appearance.configureWithOpaqueBackground();
appearance.backgroundImage = image;
@@ -456,7 +455,7 @@ export class ActionBar extends ActionBarBase {
let image: UIImage;
if (__VISIONOS__ || majorVersion >= 15) {
if (__VISIONOS__ || SDK_VERSION >= 15) {
const appearance = this._getAppearance(navBar);
image = appearance.backgroundImage;
} else {
@@ -517,7 +516,7 @@ export class ActionBar extends ActionBarBase {
private updateFlatness(navBar: UINavigationBar) {
if (this.flat) {
if (majorVersion >= 15) {
if (SDK_VERSION >= 15) {
const appearance = this._getAppearance(navBar);
appearance.shadowColor = UIColor.clearColor;
this._updateAppearance(navBar, appearance);
@@ -530,7 +529,7 @@ export class ActionBar extends ActionBarBase {
navBar.translucent = false;
}
} else {
if (majorVersion >= 15) {
if (SDK_VERSION >= 15) {
if (navBar.standardAppearance) {
// Not flat and never been set do nothing.
const appearance = navBar.standardAppearance;
@@ -581,7 +580,7 @@ export class ActionBar extends ActionBarBase {
public onLayout(left: number, top: number, right: number, bottom: number) {
const titleView = this.titleView;
if (titleView) {
if (majorVersion > 10) {
if (SDK_VERSION > 10) {
// On iOS 11 titleView is wrapped in another view that is centered with constraints.
View.layoutChild(this, titleView, 0, 0, titleView.getMeasuredWidth(), titleView.getMeasuredHeight());
} else {

View File

@@ -1,13 +1,11 @@
import { ActivityIndicatorBase, busyProperty, iosIndicatorViewStyleProperty } from './activity-indicator-common';
import { colorProperty } from '../styling/style-properties';
import { Color } from '../../color';
import { iOSNativeHelper } from '../../utils';
import { SDK_VERSION } from '../../utils/constants';
import { IOSIndicatorViewStyle } from '.';
export * from './activity-indicator-common';
const majorVersion = iOSNativeHelper.MajorVersion;
export class ActivityIndicator extends ActivityIndicatorBase {
nativeViewProtected: UIActivityIndicatorView;
@@ -29,10 +27,10 @@ export class ActivityIndicator extends ActivityIndicatorBase {
switch (value) {
case 'large':
viewStyle = majorVersion > 12 ? UIActivityIndicatorViewStyle.Large : UIActivityIndicatorViewStyle.WhiteLarge;
viewStyle = SDK_VERSION > 12 ? UIActivityIndicatorViewStyle.Large : UIActivityIndicatorViewStyle.WhiteLarge;
break;
default:
viewStyle = majorVersion > 12 ? UIActivityIndicatorViewStyle.Medium : UIActivityIndicatorViewStyle.Gray;
viewStyle = SDK_VERSION > 12 ? UIActivityIndicatorViewStyle.Medium : UIActivityIndicatorViewStyle.Gray;
break;
}

View File

@@ -41,7 +41,7 @@ class UILayoutViewController extends UIViewController {
super.viewDidLayoutSubviews();
const owner = this.owner?.deref();
if (owner) {
if (iOSUtils.MajorVersion >= 11) {
if (SDK_VERSION >= 11) {
// Handle nested UILayoutViewController safe area application.
// Currently, UILayoutViewController can be nested only in a TabView.
// The TabView itself is handled by the OS, so we check the TabView's parent (usually a Page, but can be a Layout).
@@ -116,7 +116,7 @@ class UILayoutViewController extends UIViewController {
public traitCollectionDidChange(previousTraitCollection: UITraitCollection): void {
super.traitCollectionDidChange(previousTraitCollection);
if (iOSUtils.MajorVersion >= 13) {
if (SDK_VERSION >= 13) {
const owner = this.owner?.deref();
if (owner && this.traitCollection.hasDifferentColorAppearanceComparedToTraitCollection && this.traitCollection.hasDifferentColorAppearanceComparedToTraitCollection(previousTraitCollection)) {
owner.notify({

View File

@@ -137,7 +137,7 @@ export class Frame extends FrameBase {
// _onAttachedToWindow called from OS again after it was detach
// still happens with androidx.fragment:1.3.2
const activity = androidUtils.getCurrentActivity();
const lifecycleState = activity?.getLifecycle?.()?.getCurrentState() || androidx.lifecycle.Lifecycle.State.CREATED;
const lifecycleState = (activity as androidx.fragment.app.FragmentActivity)?.getLifecycle?.()?.getCurrentState() || androidx.lifecycle.Lifecycle.State.CREATED;
if ((this._manager && this._manager.isDestroyed()) || !lifecycleState.isAtLeast(androidx.lifecycle.Lifecycle.State.CREATED)) {
return;
}

View File

@@ -5,7 +5,8 @@ import { Page } from '../page';
import { View } from '../core/view';
import { IOSHelper } from '../core/view/view-helper';
import { profile } from '../../profiling';
import { CORE_ANIMATION_DEFAULTS, ios as iOSUtils, layout } from '../../utils';
import { CORE_ANIMATION_DEFAULTS, layout } from '../../utils';
import { SDK_VERSION } from '../../utils/constants';
import { Trace } from '../../trace';
import type { PageTransition } from '../transition/page-transition';
import { SlideTransition } from '../transition/slide-transition';
@@ -14,8 +15,6 @@ import { SharedTransition } from '../transition/shared-transition';
export * from './frame-common';
const majorVersion = iOSUtils.MajorVersion;
const ENTRY = '_entry';
const DELEGATE = '_delegate';
const NAV_DEPTH = '_navDepth';
@@ -144,7 +143,7 @@ export class Frame extends FrameBase {
backstackEntry[NAV_DEPTH] = navDepth;
viewController[ENTRY] = backstackEntry;
if (!animated && majorVersion > 10) {
if (!animated && SDK_VERSION > 10) {
// Reset back button title before pushing view controller to prevent
// displaying default 'back' title (when NavigaitonButton custom title is set).
const barButtonItem = UIBarButtonItem.alloc().initWithTitleStyleTargetAction('', UIBarButtonItemStyle.Plain, null, null);
@@ -633,7 +632,7 @@ class UINavigationControllerImpl extends UINavigationController {
public traitCollectionDidChange(previousTraitCollection: UITraitCollection): void {
super.traitCollectionDidChange(previousTraitCollection);
if (majorVersion >= 13) {
if (SDK_VERSION >= 13) {
const owner = this._owner?.deref?.();
if (owner && this.traitCollection.hasDifferentColorAppearanceComparedToTraitCollection && this.traitCollection.hasDifferentColorAppearanceComparedToTraitCollection(previousTraitCollection)) {
owner.notify({

View File

@@ -3,12 +3,11 @@ import { Font } from '../styling/font';
import { colorProperty, fontInternalProperty } from '../styling/style-properties';
import { HtmlViewBase, htmlProperty, selectableProperty, linkColorProperty } from './html-view-common';
import { View } from '../core/view';
import { iOSNativeHelper, layout } from '../../utils';
import { layout } from '../../utils';
import { SDK_VERSION } from '../../utils/constants';
export * from './html-view-common';
const majorVersion = iOSNativeHelper.MajorVersion;
export class HtmlView extends HtmlViewBase {
nativeViewProtected: UITextView;
@@ -89,7 +88,7 @@ export class HtmlView extends HtmlViewBase {
this.nativeViewProtected.attributedText = NSAttributedString.alloc().initWithDataOptionsDocumentAttributesError(nsData, attributes, null);
if (!this.style.color && majorVersion >= 13 && UIColor.labelColor) {
if (!this.style.color && SDK_VERSION >= 13 && UIColor.labelColor) {
this.nativeViewProtected.textColor = UIColor.labelColor;
}
}

View File

@@ -1,12 +1,11 @@
import { ScrollEventData } from '.';
import { ScrollViewBase, scrollBarIndicatorVisibleProperty, isScrollEnabledProperty } from './scroll-view-common';
import { iOSNativeHelper, layout } from '../../utils';
import { layout } from '../../utils';
import { SDK_VERSION } from '../../utils/constants';
import { View } from '../core/view';
export * from './scroll-view-common';
const majorVersion = iOSNativeHelper.MajorVersion;
@NativeClass
class UIScrollViewDelegateImpl extends NSObject implements UIScrollViewDelegate {
private _owner: WeakRef<ScrollView>;
@@ -174,7 +173,7 @@ export class ScrollView extends ScrollViewBase {
let width = right - left - insets.right - insets.left;
let height = bottom - top - insets.bottom - insets.top;
if (majorVersion > 10) {
if (SDK_VERSION > 10) {
// Disable automatic adjustment of scroll view insets
// Consider exposing this as property with all 4 modes
// https://developer.apple.com/documentation/uikit/uiscrollview/contentinsetadjustmentbehavior

View File

@@ -1,12 +1,11 @@
import { SwitchBase, checkedProperty, offBackgroundColorProperty } from './switch-common';
import { colorProperty, backgroundColorProperty, backgroundInternalProperty } from '../styling/style-properties';
import { Color } from '../../color';
import { iOSNativeHelper, layout } from '../../utils';
import { layout } from '../../utils';
import { SDK_VERSION } from '../../utils/constants';
export * from './switch-common';
const majorVersion = iOSNativeHelper.MajorVersion;
@NativeClass
class SwitchChangeHandlerImpl extends NSObject {
private _owner: WeakRef<Switch>;
@@ -75,7 +74,7 @@ export class Switch extends SwitchBase {
// only add :checked pseudo handling on supported iOS versions
// ios <13 works but causes glitchy animations when toggling
// so we decided to keep the old behavior on older versions.
if (majorVersion >= 13) {
if (SDK_VERSION >= 13) {
super._onCheckedPropertyChanged(newValue);
if (this.offBackgroundColor) {
@@ -130,7 +129,7 @@ export class Switch extends SwitchBase {
return this.nativeViewProtected.onTintColor;
}
[backgroundColorProperty.setNative](value: UIColor | Color) {
if (majorVersion >= 13) {
if (SDK_VERSION >= 13) {
if (!this.offBackgroundColor || this.checked) {
this.setNativeBackgroundColor(value);
}
@@ -151,7 +150,7 @@ export class Switch extends SwitchBase {
return this.nativeViewProtected.backgroundColor;
}
[offBackgroundColorProperty.setNative](value: Color | UIColor) {
if (majorVersion >= 13) {
if (SDK_VERSION >= 13) {
if (!this.checked) {
this.setNativeBackgroundColor(value);
}

View File

@@ -8,7 +8,7 @@ import { ImageSource } from '../../image-source';
import { Trace } from '../../trace';
import { Color } from '../../color';
import { fontSizeProperty, fontInternalProperty } from '../styling/style-properties';
import { RESOURCE_PREFIX, ad, layout } from '../../utils';
import { RESOURCE_PREFIX, android as androidUtils, layout } from '../../utils';
import { Frame } from '../frame';
import { Application } from '../../application';
import { AndroidHelper } from '../core/view';
@@ -293,7 +293,7 @@ function createTabItemSpec(item: TabViewItem): org.nativescript.widgets.TabItemS
if (item.iconSource) {
if (item.iconSource.indexOf(RESOURCE_PREFIX) === 0) {
result.iconId = ad.resources.getDrawableId(item.iconSource.substr(RESOURCE_PREFIX.length));
result.iconId = androidUtils.resources.getDrawableId(item.iconSource.substr(RESOURCE_PREFIX.length));
if (result.iconId === 0) {
traceMissingIcon(item.iconSource);
}
@@ -315,7 +315,7 @@ let defaultAccentColor: number = undefined;
function getDefaultAccentColor(context: android.content.Context): number {
if (defaultAccentColor === undefined) {
//Fallback color: https://developer.android.com/samples/SlidingTabsColors/src/com.example.android.common/view/SlidingTabStrip.html
defaultAccentColor = ad.resources.getPaletteColor(ACCENT_COLOR, context) || 0xff33b5e5;
defaultAccentColor = androidUtils.resources.getPaletteColor(ACCENT_COLOR, context) || 0xff33b5e5;
}
return defaultAccentColor;
@@ -484,7 +484,7 @@ export class TabView extends TabViewBase {
const viewPager = new org.nativescript.widgets.TabViewPager(context);
const tabLayout = new org.nativescript.widgets.TabLayout(context);
const lp = new org.nativescript.widgets.CommonLayoutParams();
const primaryColor = ad.resources.getPaletteColor(PRIMARY_COLOR, context);
const primaryColor = androidUtils.resources.getPaletteColor(PRIMARY_COLOR, context);
let accentColor = getDefaultAccentColor(context);
lp.row = 1;
@@ -494,7 +494,7 @@ export class TabView extends TabViewBase {
JSON.stringify([
{ value: 1, type: 0 /* org.nativescript.widgets.GridUnitType.auto */ },
{ value: 1, type: 2 /* org.nativescript.widgets.GridUnitType.star */ },
])
]),
);
viewPager.setLayoutParams(lp);
@@ -506,7 +506,7 @@ export class TabView extends TabViewBase {
JSON.stringify([
{ value: 1, type: 2 /* org.nativescript.widgets.GridUnitType.star */ },
{ value: 1, type: 0 /* org.nativescript.widgets.GridUnitType.auto */ },
])
]),
);
tabLayout.setLayoutParams(lp);
viewPager.setSwipePageEnabled(false);

View File

@@ -12,14 +12,12 @@ import { Span } from './span';
import { colorProperty, fontInternalProperty, fontScaleInternalProperty, Length } from '../styling/style-properties';
import { StrokeCSSValues } from '../styling/css-stroke';
import { isString, isNullOrUndefined } from '../../utils/types';
import { iOSNativeHelper, layout } from '../../utils';
import { Trace } from '../../trace';
import { layout } from '../../utils';
import { SDK_VERSION } from '../../utils/constants';
import { CoreTypes } from '../../core-types';
export * from './text-base-common';
const majorVersion = iOSNativeHelper.MajorVersion;
@NativeClass
class UILabelClickHandlerImpl extends NSObject {
private _owner: WeakRef<TextBase>;
@@ -350,7 +348,7 @@ export class TextBase extends TextBaseCommon {
const text = getTransformedText(isNullOrUndefined(this.text) ? '' : `${this.text}`, this.textTransform);
this.nativeTextViewProtected.nativeScriptSetTextDecorationAndTransformTextDecorationLetterSpacingLineHeight(text, this.style.textDecoration || '', letterSpacing, lineHeight);
if (!this.style?.color && majorVersion >= 13 && UIColor.labelColor) {
if (!this.style?.color && SDK_VERSION >= 13 && UIColor.labelColor) {
this._setColor(UIColor.labelColor);
}
}

View File

@@ -1,174 +0,0 @@
import { Application } from '../../application';
import { Trace } from '../../trace';
import { topmost } from '../../ui/frame/frame-stack';
let application: android.app.Application;
let applicationContext: android.content.Context;
let contextResources: android.content.res.Resources;
let packageName: string;
export function getApplicationContext() {
if (!applicationContext) {
applicationContext = getApplication().getApplicationContext();
}
return applicationContext;
}
export function getCurrentActivity() {
if (!Application) {
return null;
}
return Application.android.foregroundActivity || Application.android.startActivity;
}
export function getApplication() {
if (!application) {
application = Application.android.getNativeApplication();
}
return application;
}
export function getResources() {
if (!contextResources) {
contextResources = getApplication().getResources();
}
return contextResources;
}
export function getPackageName() {
if (!packageName) {
packageName = getApplicationContext().getPackageName();
}
return packageName;
}
let inputMethodManager: android.view.inputmethod.InputMethodManager;
export function getInputMethodManager(): android.view.inputmethod.InputMethodManager {
if (!inputMethodManager) {
inputMethodManager = <android.view.inputmethod.InputMethodManager>getApplicationContext().getSystemService(android.content.Context.INPUT_METHOD_SERVICE);
}
return inputMethodManager;
}
export function showSoftInput(nativeView: android.view.View): void {
const inputManager = getInputMethodManager();
if (inputManager && nativeView instanceof android.view.View) {
inputManager.showSoftInput(nativeView, android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT);
}
}
export function dismissSoftInput(nativeView?: android.view.View): void {
const inputManager = getInputMethodManager();
let windowToken: android.os.IBinder;
if (nativeView instanceof android.view.View) {
if (!nativeView.hasFocus()) {
return;
}
windowToken = nativeView.getWindowToken();
} else if (getCurrentActivity() instanceof androidx.appcompat.app.AppCompatActivity) {
const modalDialog = (topmost()?._modalParent ?? (topmost()?.modal as any))?._dialogFragment?.getDialog();
const window = (modalDialog ?? getCurrentActivity()).getWindow();
const decorView = window.getDecorView();
if (decorView) {
windowToken = decorView.getWindowToken();
decorView.requestFocus();
} else {
windowToken = null;
}
}
if (inputManager && windowToken) {
inputManager.hideSoftInputFromWindow(windowToken, 0);
}
}
export namespace collections {
export function stringArrayToStringSet(str: string[]): java.util.HashSet<string> {
const hashSet = new java.util.HashSet<string>();
if (str !== undefined) {
for (const element in str) {
hashSet.add('' + str[element]);
}
}
return hashSet;
}
export function stringSetToStringArray(stringSet: any): string[] {
const arr = [];
if (stringSet !== undefined) {
const it = stringSet.iterator();
while (it.hasNext()) {
const element = '' + it.next();
arr.push(element);
}
}
return arr;
}
}
export namespace resources {
let attr;
const attrCache = new Map<string, number>();
export function getDrawableId(name) {
return getId(':drawable/' + name);
}
export function getStringId(name) {
return getId(':string/' + name);
}
export function getId(name: string): number {
const resources = getResources();
const packageName = getPackageName();
const uri = packageName + name;
return resources.getIdentifier(uri, null, null);
}
export function getResource(name: string, type?: string): number {
return getResources().getIdentifier(name, type, getPackageName());
}
export function getPalleteColor(name: string, context: android.content.Context): number {
return getPaletteColor(name, context);
}
export function getPaletteColor(name: string, context: android.content.Context): number {
if (attrCache.has(name)) {
return attrCache.get(name);
}
let result = 0;
try {
if (!attr) {
attr = java.lang.Class.forName('androidx.appcompat.R$attr');
}
let colorID = 0;
const field = attr.getField(name);
if (field) {
colorID = field.getInt(null);
}
if (colorID) {
const typedValue = new android.util.TypedValue();
context.getTheme().resolveAttribute(colorID, typedValue, true);
result = typedValue.data;
}
} catch (ex) {
Trace.write('Cannot get pallete color: ' + name, Trace.categories.Error, Trace.messageType.error);
}
attrCache.set(name, result);
return result;
}
}
export function isRealDevice(): boolean {
const fingerprint = android.os.Build.FINGERPRINT;
return fingerprint != null && (fingerprint.indexOf('vbox') > -1 || fingerprint.indexOf('generic') > -1);
}

View File

@@ -185,10 +185,6 @@ Please ensure you have your manifest correctly configured with the FileProvider.
}
}
export function isRealDevice(): boolean {
return AndroidUtils.isRealDevice();
}
export function dismissSoftInput(nativeView?: any): void {
AndroidUtils.dismissSoftInput(nativeView);
}

View File

@@ -1,5 +1,5 @@
import { Trace } from '../trace';
import { ios as iOSUtils } from './native-helper';
import { ios as iOSUtils, isRealDevice } from './native-helper';
export { clearInterval, clearTimeout, setInterval, setTimeout } from '../timer';
export * from './common';
@@ -10,11 +10,12 @@ export * from './macrotask-scheduler';
export * from './mainthread-helper';
export * from './native-helper';
export * from './types';
export * from './native-helper';
export function openFile(filePath: string): boolean {
try {
const appPath = iOSUtils.getCurrentAppPath();
const path = iOSUtils.isRealDevice() ? filePath.replace('~', appPath) : filePath;
const path = isRealDevice() ? filePath.replace('~', appPath) : filePath;
const controller = UIDocumentInteractionController.interactionControllerWithURL(NSURL.fileURLWithPath(path));
controller.delegate = iOSUtils.createUIDocumentInteractionControllerDelegate();
@@ -69,10 +70,6 @@ export function openUrlAsync(location: string): Promise<boolean> {
});
}
export function isRealDevice(): boolean {
return iOSUtils.isRealDevice();
}
export const ad = 0;
export function dismissSoftInput(nativeView?: UIView): void {

View File

@@ -1,264 +0,0 @@
import { Color } from '../../color';
import { Trace } from '../../trace';
import { CORE_ANIMATION_DEFAULTS, getDurationWithDampingFromSpring } from '../common';
import { SDK_VERSION } from '../constants';
declare let UIImagePickerControllerSourceType: any;
const radToDeg = Math.PI / 180;
function isOrientationLandscape(orientation: number) {
return orientation === UIDeviceOrientation.LandscapeLeft /* 3 */ || orientation === UIDeviceOrientation.LandscapeRight /* 4 */;
}
function openFileAtRootModule(filePath: string): boolean {
try {
const appPath = getCurrentAppPath();
const path = isRealDevice() ? filePath.replace('~', appPath) : filePath;
const controller = UIDocumentInteractionController.interactionControllerWithURL(NSURL.fileURLWithPath(path));
controller.delegate = createUIDocumentInteractionControllerDelegate();
return controller.presentPreviewAnimated(true);
} catch (e) {
Trace.write('Error in openFile', Trace.categories.Error, Trace.messageType.error);
}
return false;
}
// TODO: remove for NativeScript 9.0
export function getter<T>(_this: any, property: T | { (): T }): T {
console.log('utils.ios.getter() is deprecated; use the respective native property instead');
if (typeof property === 'function') {
return (<{ (): T }>property).call(_this);
} else {
return <T>property;
}
}
export namespace collections {
export function jsArrayToNSArray<T>(str: T[]): NSArray<T> {
return NSArray.arrayWithArray(str);
}
export function nsArrayToJSArray<T>(a: NSArray<T>): Array<T> {
const arr = [];
if (a !== undefined) {
const count = a.count;
for (let i = 0; i < count; i++) {
arr.push(a.objectAtIndex(i));
}
}
return arr;
}
}
export function getRootViewController(): UIViewController {
const win = getWindow();
let vc = win && win.rootViewController;
while (vc && vc.presentedViewController) {
vc = vc.presentedViewController;
}
return vc;
}
export function getWindow(): UIWindow {
let window: UIWindow;
if (SDK_VERSION >= 15 && typeof NativeScriptViewFactory !== 'undefined') {
// UIWindowScene.keyWindow is only available 15+
window = NativeScriptViewFactory.getKeyWindow();
}
if (window) {
return window;
}
const app = UIApplication.sharedApplication;
if (!app) {
return;
}
return app.keyWindow || (app.windows && app.windows.count > 0 && app.windows.objectAtIndex(0));
}
export function getMainScreen(): UIScreen {
const window = getWindow();
return window ? window.screen : UIScreen.mainScreen;
}
export function setWindowBackgroundColor(value: string) {
const win = getWindow();
if (win) {
const bgColor = new Color(value);
win.backgroundColor = bgColor.ios;
const rootVc = getRootViewController();
if (rootVc?.view) {
rootVc.view.backgroundColor = bgColor.ios;
}
}
}
export function isLandscape(): boolean {
console.log('utils.ios.isLandscape() is deprecated; use application.orientation instead');
const deviceOrientation = UIDevice.currentDevice.orientation;
const statusBarOrientation = UIApplication.sharedApplication.statusBarOrientation;
const isDeviceOrientationLandscape = isOrientationLandscape(deviceOrientation);
const isStatusBarOrientationLandscape = isOrientationLandscape(statusBarOrientation);
return isDeviceOrientationLandscape || isStatusBarOrientationLandscape;
}
/**
* @deprecated use Utils.SDK_VERSION instead which is a float of the {major}.{minor} verison
*/
export const MajorVersion = NSString.stringWithString(UIDevice.currentDevice.systemVersion).intValue;
export function openFile(filePath: string): boolean {
console.log('utils.ios.openFile() is deprecated; use utils.openFile() instead');
return openFileAtRootModule(filePath);
}
export function getCurrentAppPath(): string {
const currentDir = __dirname;
const tnsModulesIndex = currentDir.indexOf('/tns_modules');
// Module not hosted in ~/tns_modules when bundled. Use current dir.
let appPath = currentDir;
if (tnsModulesIndex !== -1) {
// Strip part after tns_modules to obtain app root
appPath = currentDir.substring(0, tnsModulesIndex);
}
return appPath;
}
export function joinPaths(...paths: string[]): string {
if (!paths || paths.length === 0) {
return '';
}
return NSString.stringWithString(NSString.pathWithComponents(<any>paths)).stringByStandardizingPath;
}
export function getVisibleViewController(rootViewController: UIViewController): UIViewController {
let viewController = rootViewController;
while (viewController && viewController.presentedViewController) {
viewController = viewController.presentedViewController;
}
return viewController;
}
export function applyRotateTransform(transform: CATransform3D, x: number, y: number, z: number): CATransform3D {
if (x) {
transform = CATransform3DRotate(transform, x * radToDeg, 1, 0, 0);
}
if (y) {
transform = CATransform3DRotate(transform, y * radToDeg, 0, 1, 0);
}
if (z) {
transform = CATransform3DRotate(transform, z * radToDeg, 0, 0, 1);
}
return transform;
}
export function createUIDocumentInteractionControllerDelegate(): NSObject {
@NativeClass
class UIDocumentInteractionControllerDelegateImpl extends NSObject implements UIDocumentInteractionControllerDelegate {
public static ObjCProtocols = [UIDocumentInteractionControllerDelegate];
public getViewController(): UIViewController {
return getWindow().rootViewController;
}
public documentInteractionControllerViewControllerForPreview(controller: UIDocumentInteractionController) {
return this.getViewController();
}
public documentInteractionControllerViewForPreview(controller: UIDocumentInteractionController) {
return this.getViewController().view;
}
public documentInteractionControllerRectForPreview(controller: UIDocumentInteractionController): CGRect {
return this.getViewController().view.frame;
}
}
return new UIDocumentInteractionControllerDelegateImpl();
}
export function isRealDevice() {
try {
if (NSProcessInfo.processInfo.environment.valueForKey('SIMULATOR_DEVICE_NAME')) {
return false;
}
return true;
} catch (e) {
return true;
}
}
export function printCGRect(rect: CGRect) {
if (rect) {
return `CGRect(${rect.origin.x} ${rect.origin.y} ${rect.size.width} ${rect.size.height})`;
}
}
export function snapshotView(view: UIView, scale: number): UIImage {
if (view instanceof UIImageView) {
return view.image;
}
// console.log('snapshotView view.frame:', printRect(view.frame));
const originalOpacity = view.layer.opacity;
view.layer.opacity = originalOpacity > 0 ? originalOpacity : 1;
UIGraphicsBeginImageContextWithOptions(CGSizeMake(view.frame.size.width, view.frame.size.height), false, scale);
view.layer.renderInContext(UIGraphicsGetCurrentContext());
const image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
setTimeout(() => {
// ensure set back properly on next tick
view.layer.opacity = originalOpacity;
});
return image;
}
export function copyLayerProperties(view: UIView, toView: UIView, customProperties?: { view?: Array<keyof UIView>; layer?: Array<keyof CALayer> }) {
const viewPropertiesToMatch: Array<keyof UIView> = customProperties?.view || ['backgroundColor'];
const layerPropertiesToMatch: Array<keyof CALayer> = customProperties?.layer || ['cornerRadius', 'borderWidth', 'borderColor'];
viewPropertiesToMatch.forEach((property) => {
if (view[property] !== toView[property]) {
// console.log('| -- matching view property:', property);
view[property as any] = toView[property];
}
});
layerPropertiesToMatch.forEach((property) => {
if (view.layer[property] !== toView.layer[property]) {
// console.log('| -- matching layer property:', property);
view.layer[property as any] = toView.layer[property];
}
});
}
export function animateWithSpring(options?: { tension?: number; friction?: number; mass?: number; delay?: number; velocity?: number; animateOptions?: UIViewAnimationOptions; animations?: () => void; completion?: (finished?: boolean) => void }) {
// for convenience, default spring settings are provided
const opt = {
...CORE_ANIMATION_DEFAULTS.spring,
delay: 0,
animateOptions: null,
animations: null,
completion: null,
...(options || {}),
};
const { duration, damping } = getDurationWithDampingFromSpring(opt);
if (duration === 0) {
UIView.animateWithDurationAnimationsCompletion(0, opt.animations, opt.completion);
return;
}
UIView.animateWithDurationDelayUsingSpringWithDampingInitialSpringVelocityOptionsAnimationsCompletion(duration, opt.delay, damping, opt.velocity, opt.animateOptions, opt.animations, opt.completion);
}

View File

@@ -1,5 +1,5 @@
import * as layoutCommon from './layout-helper-common';
import { getMainScreen } from '../ios';
import { ios as iosUtils } from '../native-helper';
export namespace layout {
// cache the MeasureSpec constants here, to prevent extensive marshaling calls to and from Objective C
@@ -33,15 +33,15 @@ export namespace layout {
}
export function getDisplayDensity(): number {
return getMainScreen().scale;
return iosUtils.getMainScreen().scale;
}
export function toDevicePixels(value: number): number {
return value * getMainScreen().scale;
return value * iosUtils.getMainScreen().scale;
}
export function toDeviceIndependentPixels(value: number): number {
return value / getMainScreen().scale;
return value / iosUtils.getMainScreen().scale;
}
export function round(value: number) {

View File

@@ -1,6 +1,8 @@
import * as AndroidUtils from './android';
import { platformCheck } from './platform-check';
import { numberHasDecimals, numberIs64Bit } from './types';
import { Application } from '../application';
import { Trace } from '../trace';
import { topmost } from '../ui/frame/frame-stack';
export function dataDeserialize(nativeData?: any) {
if (nativeData === null || typeof nativeData !== 'object') {
@@ -140,12 +142,190 @@ export function dataSerialize(data?: any, wrapPrimitives?: boolean) {
}
}
export import android = AndroidUtils;
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;
}
function getCurrentActivity() {
if (!Application) {
return null;
}
return Application.android.foregroundActivity || Application.android.startActivity;
}
function getApplication() {
if (!application) {
application = Application.android.getNativeApplication();
}
return application;
}
function getResources() {
if (!contextResources) {
contextResources = getApplication().getResources();
}
return contextResources;
}
function getPackageName() {
if (!packageName) {
packageName = getApplicationContext().getPackageName();
}
return packageName;
}
let inputMethodManager: android.view.inputmethod.InputMethodManager;
function getInputMethodManager(): android.view.inputmethod.InputMethodManager {
if (!inputMethodManager) {
inputMethodManager = <android.view.inputmethod.InputMethodManager>getApplicationContext().getSystemService(android.content.Context.INPUT_METHOD_SERVICE);
}
return inputMethodManager;
}
function showSoftInput(nativeView: android.view.View): void {
const inputManager = getInputMethodManager();
if (inputManager && nativeView instanceof android.view.View) {
inputManager.showSoftInput(nativeView, android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT);
}
}
function dismissSoftInput(nativeView?: android.view.View): void {
const inputManager = getInputMethodManager();
let windowToken: android.os.IBinder;
if (nativeView instanceof android.view.View) {
if (!nativeView.hasFocus()) {
return;
}
windowToken = nativeView.getWindowToken();
} else if (getCurrentActivity() instanceof androidx.appcompat.app.AppCompatActivity) {
const modalDialog = (topmost()?._modalParent ?? (topmost()?.modal as any))?._dialogFragment?.getDialog();
const window = (modalDialog ?? getCurrentActivity()).getWindow();
const decorView = window.getDecorView();
if (decorView) {
windowToken = decorView.getWindowToken();
decorView.requestFocus();
} else {
windowToken = null;
}
}
if (inputManager && windowToken) {
inputManager.hideSoftInputFromWindow(windowToken, 0);
}
}
namespace collections {
export function stringArrayToStringSet(str: string[]): java.util.HashSet<string> {
const hashSet = new java.util.HashSet<string>();
if (str !== undefined) {
for (const element in str) {
hashSet.add('' + str[element]);
}
}
return hashSet;
}
export function stringSetToStringArray(stringSet: any): string[] {
const arr = [];
if (stringSet !== undefined) {
const it = stringSet.iterator();
while (it.hasNext()) {
const element = '' + it.next();
arr.push(element);
}
}
return arr;
}
}
namespace resources {
let attr;
const attrCache = new Map<string, number>();
export function getDrawableId(name) {
return getId(':drawable/' + name);
}
export function getStringId(name) {
return getId(':string/' + name);
}
export function getId(name: string): number {
const resources = getResources();
const packageName = getPackageName();
const uri = packageName + name;
return resources.getIdentifier(uri, null, null);
}
export function getResource(name: string, type?: string): number {
return getResources().getIdentifier(name, type, getPackageName());
}
export function getPaletteColor(name: string, context: android.content.Context): number {
if (attrCache.has(name)) {
return attrCache.get(name);
}
let result = 0;
try {
if (!attr) {
attr = java.lang.Class.forName('androidx.appcompat.R$attr');
}
let colorID = 0;
const field = attr.getField(name);
if (field) {
colorID = field.getInt(null);
}
if (colorID) {
const typedValue = new android.util.TypedValue();
context.getTheme().resolveAttribute(colorID, typedValue, true);
result = typedValue.data;
}
} catch (ex) {
Trace.write('Cannot get pallete color: ' + name, Trace.categories.Error, Trace.messageType.error);
}
attrCache.set(name, result);
return result;
}
}
export function isRealDevice(): boolean {
const fingerprint = android.os.Build.FINGERPRINT;
return fingerprint != null && (fingerprint.indexOf('vbox') > -1 || fingerprint.indexOf('generic') > -1);
}
export const androidUtils = {
resources,
getApplication,
getCurrentActivity,
getApplicationContext,
getResources,
getPackageName,
getInputMethodManager,
showSoftInput,
dismissSoftInput,
};
/**
* @deprecated Use `Utils.android` instead.
*/
export import ad = AndroidUtils;
export const ad = androidUtils;
// these don't exist on Android.Stub them to empty functions.
export const iOSNativeHelper = platformCheck('Utils.iOSNativeHelper');

View File

@@ -9,6 +9,11 @@ export function dataSerialize(data?: any, wrapPrimitives?: boolean): any;
*/
export function dataDeserialize(nativeData?: any): any;
/**
* Checks whether the application is running on real device and not on emulator.
*/
export function isRealDevice(): boolean;
// /**
// * Module with android specific utilities.
// */
@@ -94,24 +99,8 @@ export function dataDeserialize(nativeData?: any): any;
// * @param type - (Optional) type
// */
// export function getResource(name: string, type?: string): number;
// /**
// * [Obsolete - please use getPaletteColor] Gets a color from current theme.
// * @param name - Name of the color
// */
// export function getPalleteColor();
// /**
// * Gets a color from the current theme.
// * @param name - Name of the color resource.
// */
// export function getPaletteColor(name: string, context: any /* android.content.Context */): number;
// }
// /**
// * Checks whether the application is running on real device and not on emulator.
// */
// export function isRealDevice(): boolean;
// }
// /**
@@ -187,19 +176,6 @@ export function dataDeserialize(nativeData?: any): any;
// */
// export function openFile(filePath: string): boolean;
// /**
// * Joins an array of file paths.
// * @param paths An array of paths.
// * Returns the joined path.
// */
// export function joinPaths(...paths: string[]): string;
// /**
// * Gets the root folder for the current application. This Folder is private for the application and not accessible from Users/External apps.
// * iOS - this folder is read-only and contains the app and all its resources.
// */
// export function getCurrentAppPath(): string;
// /**
// * Gets the currently visible(topmost) UIViewController.
// * @param rootViewController The root UIViewController instance to start searching from (normally window.rootViewController).
@@ -207,20 +183,6 @@ export function dataDeserialize(nativeData?: any): any;
// */
// export function getVisibleViewController(rootViewController: any /* UIViewController*/): any; /* UIViewController*/
// /**
// *
// * @param transform Applies a rotation transform over X,Y and Z axis
// * @param x Rotation over X axis in degrees
// * @param y Rotation over Y axis in degrees
// * @param z Rotation over Z axis in degrees
// */
// export function applyRotateTransform(transform: any /* CATransform3D*/, x: number, y: number, z: number): any; /* CATransform3D*/
// /**
// * Create a UIDocumentInteractionControllerDelegate implementation for use with UIDocumentInteractionController
// */
// export function createUIDocumentInteractionControllerDelegate(): any;
// /**
// * Checks whether the application is running on real device and not on simulator.
// */
@@ -248,33 +210,93 @@ export function dataDeserialize(nativeData?: any): any;
// */
// export function copyLayerProperties(view: UIView, toView: UIView, customProperties?: { view?: Array<string> /* Array<keyof UIView> */; layer?: Array<string> /* Array<keyof CALayer> */ }): void;
// /**
// * Animate views with a configurable spring effect
// * @param options various animation settings for the spring
// * - tension: number
// * - friction: number
// * - mass: number
// * - delay: number
// * - velocity: number
// * - animateOptions: UIViewAnimationOptions
// * - animations: () => void, Callback containing the property changes you want animated
// * - completion: (finished: boolean) => void, Callback when animation is finished
// */
// export function animateWithSpring(options?: { tension?: number; friction?: number; mass?: number; delay?: number; velocity?: number; animateOptions?: UIViewAnimationOptions; animations?: () => void; completion?: (finished?: boolean) => void });
// }
import * as AndroidUtils from './android';
export import android = AndroidUtils;
export const android: {
resources: {
getDrawableId: (name) => number;
getStringId: (name) => number;
getId: (name: string) => number;
getResource: (name: string, type?: string) => number;
/**
* Gets a color from the current theme.
* @param name - Name of the color resource.
* @param context - Context to resolve the color.
*/
getPaletteColor: (name: string, context: android.content.Context) => number;
};
getApplication: () => android.app.Application;
getCurrentActivity: () => androidx.appcompat.app.AppCompatActivity | android.app.Activity | null;
getApplicationContext: () => android.content.Context;
getResources: () => android.content.res.Resources;
getPackageName: () => string;
getInputMethodManager: () => android.view.inputmethod.InputMethodManager;
showSoftInput: (nativeView: android.view.View) => void;
dismissSoftInput: (nativeView?: android.view.View) => void;
};
/**
* @deprecated use Utils.android instead.
*/
export import ad = AndroidUtils;
export const ad = android;
import * as iOSUtils from './ios';
export import ios = iOSUtils;
export const ios: {
collections: {
jsArrayToNSArray<T>(str: T[]): NSArray<T>;
nsArrayToJSArray<T>(a: NSArray<T>): Array<T>;
};
/**
* Create a UIDocumentInteractionControllerDelegate implementation for use with UIDocumentInteractionController
*/
createUIDocumentInteractionControllerDelegate: () => NSObject;
/**
* Gets the root folder for the current application. This Folder is private for the application and not accessible from Users/External apps.
* iOS - this folder is read-only and contains the app and all its resources.
*/
getCurrentAppPath: () => string;
getRootViewController: () => UIViewController;
getVisibleViewController: (rootViewController: UIViewController) => UIViewController;
getWindow: () => UIWindow;
getMainScreen: () => UIScreen;
setWindowBackgroundColor: (value: string) => void;
isLandscape: () => boolean;
snapshotView: (view: UIView, scale: number) => UIImage;
/**
* Applies a rotation transform over X,Y and Z axis
* @param transform Applies a rotation transform over X,Y and Z axis
* @param x Rotation over X axis in degrees
* @param y Rotation over Y axis in degrees
* @param z Rotation over Z axis in degrees
*/
applyRotateTransform: (transform: CATransform3D, x: number, y: number, z: number) => CATransform3D;
printCGRect: (rect: CGRect) => void;
copyLayerProperties: (view: UIView, toView: UIView, customProperties?: { view?: Array<keyof UIView>; layer?: Array<keyof CALayer> }) => void;
/**
* Animate views with a configurable spring effect
* @param options various animation settings for the spring
* - tension: number
* - friction: number
* - mass: number
* - delay: number
* - velocity: number
* - animateOptions: UIViewAnimationOptions
* - animations: () => void, Callback containing the property changes you want animated
* - completion: (finished: boolean) => void, Callback when animation is finished
*/
animateWithSpring: (options?: { tension?: number; friction?: number; mass?: number; delay?: number; velocity?: number; animateOptions?: UIViewAnimationOptions; animations?: () => void; completion?: (finished?: boolean) => void }) => void;
/**
* Joins an array of file paths.
* @param paths An array of paths.
* Returns the joined path.
*/
joinPaths: (...paths: string[]) => string;
/**
* @deprecated use Utils.SDK_VERSION instead which is a float of the {major}.{minor} verison
*/
MajorVersion: number;
};
/**
* @deprecated use Utils.ios instead.
*/
export import iOSNativeHelper = iOSUtils;
export const iOSNativeHelper = ios;

View File

@@ -1,6 +1,9 @@
import * as iOSUtils from './ios';
import { platformCheck } from './platform-check';
import { getClass, isNullOrUndefined, numberHasDecimals, numberIs64Bit } from './types';
import { Color } from '../color';
import { Trace } from '../trace';
import { CORE_ANIMATION_DEFAULTS, getDurationWithDampingFromSpring } from './common';
import { SDK_VERSION } from './constants';
export function dataDeserialize(nativeData?: any) {
if (isNullOrUndefined(nativeData)) {
@@ -88,13 +91,290 @@ export function dataSerialize(data: any, wrapPrimitives: boolean = false) {
}
}
function getCurrentAppPath(): string {
const currentDir = __dirname;
const tnsModulesIndex = currentDir.indexOf('/tns_modules');
// Module not hosted in ~/tns_modules when bundled. Use current dir.
let appPath = currentDir;
if (tnsModulesIndex !== -1) {
// Strip part after tns_modules to obtain app root
appPath = currentDir.substring(0, tnsModulesIndex);
}
return appPath;
}
function joinPaths(...paths: string[]): string {
if (!paths || paths.length === 0) {
return '';
}
return NSString.stringWithString(NSString.pathWithComponents(<any>paths)).stringByStandardizingPath;
}
declare let UIImagePickerControllerSourceType: any;
const radToDeg = Math.PI / 180;
function isOrientationLandscape(orientation: number) {
return orientation === UIDeviceOrientation.LandscapeLeft /* 3 */ || orientation === UIDeviceOrientation.LandscapeRight /* 4 */;
}
function openFileAtRootModule(filePath: string): boolean {
try {
const appPath = getCurrentAppPath();
const path = isRealDevice() ? filePath.replace('~', appPath) : filePath;
const controller = UIDocumentInteractionController.interactionControllerWithURL(NSURL.fileURLWithPath(path));
controller.delegate = createUIDocumentInteractionControllerDelegate();
return controller.presentPreviewAnimated(true);
} catch (e) {
Trace.write('Error in openFile', Trace.categories.Error, Trace.messageType.error);
}
return false;
}
// TODO: remove for NativeScript 9.0
export function getter<T>(_this: any, property: T | { (): T }): T {
console.log('utils.ios.getter() is deprecated; use the respective native property instead');
if (typeof property === 'function') {
return (<{ (): T }>property).call(_this);
} else {
return <T>property;
}
}
namespace collections {
export function jsArrayToNSArray<T>(str: T[]): NSArray<T> {
return NSArray.arrayWithArray(str);
}
export function nsArrayToJSArray<T>(a: NSArray<T>): Array<T> {
const arr = [];
if (a !== undefined) {
const count = a.count;
for (let i = 0; i < count; i++) {
arr.push(a.objectAtIndex(i));
}
}
return arr;
}
}
function getRootViewController(): UIViewController {
const win = getWindow();
let vc = win && win.rootViewController;
while (vc && vc.presentedViewController) {
vc = vc.presentedViewController;
}
return vc;
}
function getWindow(): UIWindow {
let window: UIWindow;
if (SDK_VERSION >= 15 && typeof NativeScriptViewFactory !== 'undefined') {
// UIWindowScene.keyWindow is only available 15+
window = NativeScriptViewFactory.getKeyWindow();
}
if (window) {
return window;
}
const app = UIApplication.sharedApplication;
if (!app) {
return;
}
return app.keyWindow || (app.windows && app.windows.count > 0 && app.windows.objectAtIndex(0));
}
function getMainScreen(): UIScreen {
const window = getWindow();
return window ? window.screen : UIScreen.mainScreen;
}
function setWindowBackgroundColor(value: string) {
const win = getWindow();
if (win) {
const bgColor = new Color(value);
win.backgroundColor = bgColor.ios;
const rootVc = getRootViewController();
if (rootVc?.view) {
rootVc.view.backgroundColor = bgColor.ios;
}
}
}
function isLandscape(): boolean {
console.log('utils.ios.isLandscape() is deprecated; use application.orientation instead');
const deviceOrientation = UIDevice.currentDevice.orientation;
const statusBarOrientation = UIApplication.sharedApplication.statusBarOrientation;
const isDeviceOrientationLandscape = isOrientationLandscape(deviceOrientation);
const isStatusBarOrientationLandscape = isOrientationLandscape(statusBarOrientation);
return isDeviceOrientationLandscape || isStatusBarOrientationLandscape;
}
/**
* @deprecated use Utils.SDK_VERSION instead which is a float of the {major}.{minor} verison
*/
const MajorVersion = NSString.stringWithString(UIDevice.currentDevice.systemVersion).intValue;
export function openFile(filePath: string): boolean {
console.log('utils.ios.openFile() is deprecated; use utils.openFile() instead');
return openFileAtRootModule(filePath);
}
function getVisibleViewController(rootViewController: UIViewController): UIViewController {
let viewController = rootViewController;
while (viewController && viewController.presentedViewController) {
viewController = viewController.presentedViewController;
}
return viewController;
}
function applyRotateTransform(transform: CATransform3D, x: number, y: number, z: number): CATransform3D {
if (x) {
transform = CATransform3DRotate(transform, x * radToDeg, 1, 0, 0);
}
if (y) {
transform = CATransform3DRotate(transform, y * radToDeg, 0, 1, 0);
}
if (z) {
transform = CATransform3DRotate(transform, z * radToDeg, 0, 0, 1);
}
return transform;
}
function createUIDocumentInteractionControllerDelegate(): NSObject {
@NativeClass
class UIDocumentInteractionControllerDelegateImpl extends NSObject implements UIDocumentInteractionControllerDelegate {
public static ObjCProtocols = [UIDocumentInteractionControllerDelegate];
public getViewController(): UIViewController {
return getWindow().rootViewController;
}
public documentInteractionControllerViewControllerForPreview(controller: UIDocumentInteractionController) {
return this.getViewController();
}
public documentInteractionControllerViewForPreview(controller: UIDocumentInteractionController) {
return this.getViewController().view;
}
public documentInteractionControllerRectForPreview(controller: UIDocumentInteractionController): CGRect {
return this.getViewController().view.frame;
}
}
return new UIDocumentInteractionControllerDelegateImpl();
}
export function isRealDevice() {
try {
if (NSProcessInfo.processInfo.environment.valueForKey('SIMULATOR_DEVICE_NAME')) {
return false;
}
return true;
} catch (e) {
return true;
}
}
function printCGRect(rect: CGRect) {
if (rect) {
return `CGRect(${rect.origin.x} ${rect.origin.y} ${rect.size.width} ${rect.size.height})`;
}
}
function snapshotView(view: UIView, scale: number): UIImage {
if (view instanceof UIImageView) {
return view.image;
}
// console.log('snapshotView view.frame:', printRect(view.frame));
const originalOpacity = view.layer.opacity;
view.layer.opacity = originalOpacity > 0 ? originalOpacity : 1;
UIGraphicsBeginImageContextWithOptions(CGSizeMake(view.frame.size.width, view.frame.size.height), false, scale);
view.layer.renderInContext(UIGraphicsGetCurrentContext());
const image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
setTimeout(() => {
// ensure set back properly on next tick
view.layer.opacity = originalOpacity;
});
return image;
}
function copyLayerProperties(view: UIView, toView: UIView, customProperties?: { view?: Array<keyof UIView>; layer?: Array<keyof CALayer> }) {
const viewPropertiesToMatch: Array<keyof UIView> = customProperties?.view || ['backgroundColor'];
const layerPropertiesToMatch: Array<keyof CALayer> = customProperties?.layer || ['cornerRadius', 'borderWidth', 'borderColor'];
viewPropertiesToMatch.forEach((property) => {
if (view[property] !== toView[property]) {
// console.log('| -- matching view property:', property);
view[property as any] = toView[property];
}
});
layerPropertiesToMatch.forEach((property) => {
if (view.layer[property] !== toView.layer[property]) {
// console.log('| -- matching layer property:', property);
view.layer[property as any] = toView.layer[property];
}
});
}
function animateWithSpring(options?: { tension?: number; friction?: number; mass?: number; delay?: number; velocity?: number; animateOptions?: UIViewAnimationOptions; animations?: () => void; completion?: (finished?: boolean) => void }) {
// for convenience, default spring settings are provided
const opt = {
...CORE_ANIMATION_DEFAULTS.spring,
delay: 0,
animateOptions: null,
animations: null,
completion: null,
...(options || {}),
};
const { duration, damping } = getDurationWithDampingFromSpring(opt);
if (duration === 0) {
UIView.animateWithDurationAnimationsCompletion(0, opt.animations, opt.completion);
return;
}
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');
export import ios = iOSUtils;
export const ios = {
collections,
createUIDocumentInteractionControllerDelegate,
getCurrentAppPath,
getRootViewController,
getVisibleViewController,
getWindow,
getMainScreen,
setWindowBackgroundColor,
isLandscape,
applyRotateTransform,
snapshotView,
joinPaths,
printCGRect,
copyLayerProperties,
animateWithSpring,
MajorVersion,
};
/**
* @deprecated Use `Utils.ios` instead.
*/
export import iOSNativeHelper = iOSUtils;
export const iOSNativeHelper = ios;