refactor: move ios/android utils into separate modules

This commit is contained in:
Igor Randjelovic
2023-05-10 00:37:10 +02:00
parent b5096c3c49
commit 407ab567e8
6 changed files with 754 additions and 756 deletions

View File

@@ -3,7 +3,7 @@ import { getPageStartDefaultsForType, getRectFromProps, getSpringFromProps, Shar
import { isNumber } from '../../utils/types';
import { Screen } from '../../platform';
import { CORE_ANIMATION_DEFAULTS } from '../../utils/common';
import { iOSNativeHelper } from '../../utils/native-helper';
import { ios as iOSUtils } from '../../utils/native-helper';
interface PlatformTransitionInteractiveState extends TransitionInteractiveState {
transitionContext?: UIViewControllerContextTransitioning;
@@ -81,7 +81,7 @@ export class SharedTransitionHelper {
// in case the image is loaded async, we need to update the snapshot when it changes
// todo: remove listener on transition end
presentedView.on('imageSourceChange', () => {
snapshot.image = iOSNativeHelper.snapshotView(presentedSharedElement, Screen.mainScreen.scale);
snapshot.image = iOSUtils.snapshotView(presentedSharedElement, Screen.mainScreen.scale);
snapshot.tintColor = presentedSharedElement.tintColor;
});
@@ -89,7 +89,7 @@ export class SharedTransitionHelper {
snapshot.contentMode = presentedSharedElement.contentMode;
}
iOSNativeHelper.copyLayerProperties(snapshot, presentingSharedElement, pageEndProps?.propertiesToMatch);
iOSUtils.copyLayerProperties(snapshot, presentingSharedElement, pageEndProps?.propertiesToMatch as any);
snapshot.clipsToBounds = true;
// console.log('---> snapshot: ', snapshot);
@@ -97,7 +97,7 @@ export class SharedTransitionHelper {
const endFrame = presentedSharedElement.convertRectToView(presentedSharedElement.bounds, transitionContext.containerView);
snapshot.frame = startFrame;
if (SharedTransition.DEBUG) {
console.log('---> ', presentingView.sharedTransitionTag, ' frame:', iOSNativeHelper.printCGRect(snapshot.frame));
console.log('---> ', presentingView.sharedTransitionTag, ' frame:', iOSUtils.printCGRect(snapshot.frame));
}
transition.sharedElements.presenting.push({
@@ -151,11 +151,11 @@ export class SharedTransitionHelper {
await pageEndProps?.callback(independentView, 'present');
}
let snapshot: UIImageView;
// let snapshot: UIImageView;
// if (isPresented) {
// snapshot = UIImageView.alloc().init();
// } else {
snapshot = UIImageView.alloc().initWithImage(iOSNativeHelper.snapshotView(independentSharedElement, Screen.mainScreen.scale));
const snapshot = UIImageView.alloc().initWithImage(iOSUtils.snapshotView(independentSharedElement, Screen.mainScreen.scale));
// }
if (independentSharedElement instanceof UIImageView) {
@@ -184,7 +184,7 @@ export class SharedTransitionHelper {
snapshot.frame = startFrame; //startFrameAdjusted;
// }
if (SharedTransition.DEBUG) {
console.log('---> ', independentView.sharedTransitionTag, ' frame:', iOSNativeHelper.printCGRect(snapshot.frame));
console.log('---> ', independentView.sharedTransitionTag, ' frame:', iOSUtils.printCGRect(snapshot.frame));
}
const endFrameRect = getRectFromProps(pageEndProps);
@@ -289,18 +289,18 @@ export class SharedTransitionHelper {
presentingMatch.snapshot.frame = correctedEndFrame;
// apply view and layer properties to the snapshot view to match the source/presented view
iOSNativeHelper.copyLayerProperties(presentingMatch.snapshot, presented.view.ios, presented.propertiesToMatch);
iOSUtils.copyLayerProperties(presentingMatch.snapshot, presented.view.ios, presented.propertiesToMatch as any);
// create a snapshot of the presented view
presentingMatch.snapshot.image = iOSNativeHelper.snapshotView(presented.view.ios, Screen.mainScreen.scale);
presentingMatch.snapshot.image = iOSUtils.snapshotView(presented.view.ios, Screen.mainScreen.scale);
// apply correct alpha
presentingMatch.snapshot.alpha = presentingMatch.endOpacity;
if (SharedTransition.DEBUG) {
console.log(`---> ${presentingMatch.view.sharedTransitionTag} animate to: `, iOSNativeHelper.printCGRect(correctedEndFrame));
console.log(`---> ${presentingMatch.view.sharedTransitionTag} animate to: `, iOSUtils.printCGRect(correctedEndFrame));
}
}
for (const independent of transition.sharedElements.independent) {
let endFrame: CGRect = independent.endFrame;
const endFrame: CGRect = independent.endFrame;
// if (independent.isPresented) {
// const updatedEndFrame = independent.view.ios.convertRectToView(independent.view.ios.bounds, transitionContext.containerView);
// endFrame = CGRectMake(updatedEndFrame.origin.x, updatedEndFrame.origin.y, independent.endFrame.size.width, independent.endFrame.size.height);
@@ -313,7 +313,7 @@ export class SharedTransitionHelper {
independent.snapshot.alpha = independent.endOpacity;
if (SharedTransition.DEBUG) {
console.log(`---> ${independent.view.sharedTransitionTag} animate to: `, iOSNativeHelper.printCGRect(independent.endFrame));
console.log(`---> ${independent.view.sharedTransitionTag} animate to: `, iOSUtils.printCGRect(independent.endFrame));
}
}
};
@@ -332,7 +332,7 @@ export class SharedTransitionHelper {
}
);
} else {
iOSNativeHelper.animateWithSpring({
iOSUtils.animateWithSpring({
...getSpringFromProps(pageEnd?.spring),
animations: () => {
animateProperties();
@@ -409,7 +409,7 @@ export class SharedTransitionHelper {
}
// take a new snapshot
data.snapshot.image = iOSNativeHelper.snapshotView(view, Screen.mainScreen.scale);
data.snapshot.image = iOSUtils.snapshotView(view, Screen.mainScreen.scale);
// find the currently visible view with the same sharedTransitionTag
const fromView = transition.sharedElements.presented.find((p) => p.view.sharedTransitionTag === data.view.sharedTransitionTag)?.view;
@@ -458,12 +458,12 @@ export class SharedTransitionHelper {
transition.presented.view.frame = CGRectMake(endFrame.x, endFrame.y, endFrame.width, endFrame.height);
for (const presenting of transition.sharedElements.presenting) {
iOSNativeHelper.copyLayerProperties(presenting.snapshot, presenting.view.ios, presenting.propertiesToMatch);
iOSUtils.copyLayerProperties(presenting.snapshot, presenting.view.ios, presenting.propertiesToMatch as any);
presenting.snapshot.frame = presenting.startFrame;
presenting.snapshot.alpha = presenting.startOpacity;
if (SharedTransition.DEBUG) {
console.log(`---> ${presenting.view.sharedTransitionTag} animate to: `, iOSNativeHelper.printCGRect(presenting.snapshot.frame));
console.log(`---> ${presenting.view.sharedTransitionTag} animate to: `, iOSUtils.printCGRect(presenting.snapshot.frame));
}
}
@@ -476,7 +476,7 @@ export class SharedTransitionHelper {
}
if (SharedTransition.DEBUG) {
console.log(`---> ${independent.view.sharedTransitionTag} animate to: `, iOSNativeHelper.printCGRect(independent.snapshot.frame));
console.log(`---> ${independent.view.sharedTransitionTag} animate to: `, iOSUtils.printCGRect(independent.snapshot.frame));
}
}
};
@@ -495,7 +495,7 @@ export class SharedTransitionHelper {
}
);
} else {
iOSNativeHelper.animateWithSpring({
iOSUtils.animateWithSpring({
...getSpringFromProps(pageReturn?.spring),
animations: () => {
animateProperties();
@@ -542,7 +542,7 @@ export class SharedTransitionHelper {
interactiveState.propertyAnimator = UIViewPropertyAnimator.alloc().initWithDurationDampingRatioAnimations(1, 1, () => {
for (const p of state.instance.sharedElements.presenting) {
p.snapshot.frame = p.startFrame;
iOSNativeHelper.copyLayerProperties(p.snapshot, p.view.ios, p.propertiesToMatch);
iOSUtils.copyLayerProperties(p.snapshot, p.view.ios, p.propertiesToMatch as any);
p.snapshot.alpha = 1;
}

View File

@@ -0,0 +1,171 @@
import { getNativeApplication, android as androidApp } from '../../application';
import { Trace } from '../../trace';
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 (!androidApp) {
return null;
}
return androidApp.foregroundActivity || androidApp.startActivity;
}
export function getApplication() {
if (!application) {
application = <android.app.Application>getNativeApplication();
}
return application;
}
export function getResources() {
if (!contextResources) {
contextResources = getApplication().getResources();
}
return contextResources;
}
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 decorView = getCurrentActivity().getWindow().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

@@ -0,0 +1,329 @@
import { Color } from '../../color';
import { Trace } from '../../trace';
import { CORE_ANIMATION_DEFAULTS, getDurationWithDampingFromSpring } from '../common';
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 {
const app = UIApplication.sharedApplication;
if (!app) {
return;
}
return app.keyWindow || (app.windows && app.windows.count > 0 && app.windows.objectAtIndex(0));
}
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;
}
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 getShadowLayer(nativeView: UIView, name: string = 'ns-shadow-layer', create: boolean = true): CALayer {
return nativeView.layer;
console.log(`--- ${create ? 'CREATE' : 'READ'}`);
/**
* UIView
* -> Shadow
*
*
* UIView
* -> UIView
* -> Shadow
*/
if (!nativeView) {
return null;
}
if (!nativeView.layer) {
// should never hit this?
console.log('- no layer! -');
return null;
}
// if the nativeView's layer is the shadow layer?
if (nativeView.layer.name === name) {
console.log('- found shadow layer - reusing.');
return nativeView.layer;
}
console.log('>> layer :', nativeView.layer);
if (nativeView.layer.sublayers?.count) {
const count = nativeView.layer.sublayers.count;
for (let i = 0; i < count; i++) {
const subLayer = nativeView.layer.sublayers.objectAtIndex(i);
console.log(`>> subLayer ${i + 1}/${count} :`, subLayer);
console.log(`>> subLayer ${i + 1}/${count} name :`, subLayer.name);
if (subLayer.name === name) {
console.log('- found shadow sublayer - reusing.');
return subLayer;
}
}
// if (nativeView instanceof UITextView) {
// return nativeView.layer.sublayers.objectAtIndex(1);
// } else {
// return nativeView.layer.sublayers.objectAtIndex(nativeView.layer.sublayers.count - 1);
// }
}
// else {
// layer = nativeView.layer;
// }
// we're not interested in creating a new layer
if (!create) {
return null;
}
console.log(`- adding a new layer for - ${name}`);
const viewLayer = nativeView.layer;
const newLayer = CALayer.layer();
newLayer.name = name;
newLayer.zPosition = 0.0;
// nativeView.layer.insertSublayerBelow(newLayer, nativeView.layer)
// newLayer.insertSublayerAtIndex(nativeView.layer, 0)
// nativeView.layer.zPosition = 1.0;
// nativeView.layer.addSublayer(newLayer);
// nativeView.layer = CALayer.layer()
nativeView.layer.insertSublayerAtIndex(newLayer, 0);
// nativeView.layer.insertSublayerAtIndex(viewLayer, 1)
// nativeView.layer.replaceSublayerWith(newLayer, nativeView.layer);
return newLayer;
}
export function createUIDocumentInteractionControllerDelegate(): NSObject {
@NativeClass
class UIDocumentInteractionControllerDelegateImpl extends NSObject implements UIDocumentInteractionControllerDelegate {
public static ObjCProtocols = [UIDocumentInteractionControllerDelegate];
public getViewController(): UIViewController {
const app = UIApplication.sharedApplication;
return app.keyWindow.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 {
// https://stackoverflow.com/a/5093092/4936697
const sourceType = UIImagePickerControllerSourceType.UIImagePickerControllerSourceTypeCamera;
const mediaTypes = UIImagePickerController.availableMediaTypesForSourceType(sourceType);
return !!mediaTypes;
} 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();
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,10 +1,7 @@
import * as AndroidUtils from './android';
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;
@@ -143,183 +140,13 @@ export function dataSerialize(data?: any, wrapPrimitives?: boolean) {
}
}
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) {
applicationContext = getApplication().getApplicationContext();
}
return applicationContext;
}
export function getCurrentActivity() {
if (!androidApp) {
return null;
}
return androidApp.foregroundActivity || androidApp.startActivity;
}
export function getApplication() {
if (!application) {
application = <globalThis.android.app.Application>getNativeApplication();
}
return application;
}
export function getResources() {
if (!contextResources) {
contextResources = getApplication().getResources();
}
return contextResources;
}
function getPackageName() {
if (!packageName) {
packageName = getApplicationContext().getPackageName();
}
return packageName;
}
let inputMethodManager: globalThis.android.view.inputmethod.InputMethodManager;
export function getInputMethodManager(): globalThis.android.view.inputmethod.InputMethodManager {
if (!inputMethodManager) {
inputMethodManager = <globalThis.android.view.inputmethod.InputMethodManager>getApplicationContext().getSystemService(globalThis.android.content.Context.INPUT_METHOD_SERVICE);
}
return inputMethodManager;
}
export function showSoftInput(nativeView: globalThis.android.view.View): void {
const inputManager = getInputMethodManager();
if (inputManager && nativeView instanceof globalThis.android.view.View) {
inputManager.showSoftInput(nativeView, globalThis.android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT);
}
}
export function dismissSoftInput(nativeView?: globalThis.android.view.View): void {
const inputManager = getInputMethodManager();
let windowToken: globalThis.android.os.IBinder;
if (nativeView instanceof globalThis.android.view.View) {
if (!nativeView.hasFocus()) {
return;
}
windowToken = nativeView.getWindowToken();
} else if (getCurrentActivity() instanceof androidx.appcompat.app.AppCompatActivity) {
const decorView = getCurrentActivity().getWindow().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: globalThis.android.content.Context): number {
return getPaletteColor(name, context);
}
export function getPaletteColor(name: string, context: globalThis.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 globalThis.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 = globalThis.android.os.Build.FINGERPRINT;
return fingerprint != null && (fingerprint.indexOf('vbox') > -1 || fingerprint.indexOf('generic') > -1);
}
}
export import android = AndroidUtils;
/**
* @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');

View File

@@ -9,277 +9,279 @@ export function dataSerialize(data?: any, wrapPrimitives?: boolean): any;
*/
export function dataDeserialize(nativeData?: any): any;
/**
* Module with android specific utilities.
*/
declare namespace AndroidUtils {
/**
* Gets the native Android application instance.
*/
export function getApplication(): any; /* android.app.Application */
// /**
// * Module with android specific utilities.
// */
// declare namespace AndroidUtils {
// /**
// * Gets the native Android application instance.
// */
// export function getApplication(): any; /* android.app.Application */
/**
* Get the current native Android activity.
*/
export function getCurrentActivity(): any; /* android.app.Activity */
/**
* Gets the native Android application resources.
*/
export function getResources(): any; /* android.content.res.Resources */
// /**
// * Get the current native Android activity.
// */
// export function getCurrentActivity(): any; /* android.app.Activity */
// /**
// * Gets the native Android application resources.
// */
// export function getResources(): any; /* android.content.res.Resources */
/**
* Gets the Android application context.
*/
export function getApplicationContext(): any; /* android.content.Context */
// /**
// * Gets the Android application context.
// */
// export function getApplicationContext(): any; /* android.content.Context */
/**
* Gets the native Android input method manager.
*/
export function getInputMethodManager(): any; /* android.view.inputmethod.InputMethodManager */
// /**
// * Gets the native Android input method manager.
// */
// export function getInputMethodManager(): any; /* android.view.inputmethod.InputMethodManager */
/**
* Hides the soft input method, usually a soft keyboard.
*/
export function dismissSoftInput(nativeView?: any /* android.view.View */): void;
// /**
// * Hides the soft input method, usually a soft keyboard.
// */
// export function dismissSoftInput(nativeView?: any /* android.view.View */): void;
/**
* Shows the soft input method, usually a soft keyboard.
*/
export function showSoftInput(nativeView: any /* android.view.View */): void;
// /**
// * Shows the soft input method, usually a soft keyboard.
// */
// export function showSoftInput(nativeView: any /* android.view.View */): void;
/**
* Utility module dealing with some android collections.
*/
namespace collections {
/**
* Converts string array into a String [hash set](http://developer.android.com/reference/java/util/HashSet.html).
* @param str - An array of strings to convert.
*/
export function stringArrayToStringSet(str: string[]): any;
// /**
// * Utility module dealing with some android collections.
// */
// namespace collections {
// /**
// * Converts string array into a String [hash set](http://developer.android.com/reference/java/util/HashSet.html).
// * @param str - An array of strings to convert.
// */
// export function stringArrayToStringSet(str: string[]): any;
/**
* Converts string hash set into array of strings.
* @param stringSet - A string hash set to convert.
*/
export function stringSetToStringArray(stringSet: any): string[];
}
// /**
// * Converts string hash set into array of strings.
// * @param stringSet - A string hash set to convert.
// */
// export function stringSetToStringArray(stringSet: any): string[];
// }
/**
* Utility module related to android resources.
*/
export namespace resources {
/**
* Gets the drawable id from a given name.
* @param name - Name of the resource.
*/
export function getDrawableId(name);
// /**
// * Utility module related to android resources.
// */
// export namespace resources {
// /**
// * Gets the drawable id from a given name.
// * @param name - Name of the resource.
// */
// export function getDrawableId(name);
/**
* Gets the string id from a given name.
* @param name - Name of the resource.
*/
export function getStringId(name);
// /**
// * Gets the string id from a given name.
// * @param name - Name of the resource.
// */
// export function getStringId(name);
/**
* Gets the id from a given name.
* @param name - Name of the resource.
*/
export function getId(name: string): number;
// /**
// * Gets the id from a given name.
// * @param name - Name of the resource.
// */
// export function getId(name: string): number;
/**
* Gets the id from a given name with optional type.
* This sets an explicit package name.
* https://developer.android.com/reference/android/content/res/Resources#getIdentifier(java.lang.String,%20java.lang.String,%20java.lang.String)
* @param name - Name of the resource.
* @param type - (Optional) type
*/
export function getResource(name: string, type?: string): number;
// /**
// * Gets the id from a given name with optional type.
// * This sets an explicit package name.
// * https://developer.android.com/reference/android/content/res/Resources#getIdentifier(java.lang.String,%20java.lang.String,%20java.lang.String)
// * @param name - Name of the resource.
// * @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();
// /**
// * [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;
}
// /**
// * 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;
}
// /**
// * Checks whether the application is running on real device and not on emulator.
// */
// export function isRealDevice(): boolean;
// }
/**
* Module with ios specific utilities.
*/
declare namespace iOSUtils {
// Common properties between UILabel, UITextView and UITextField
export interface TextUIView {
font: any;
textAlignment: number;
textColor: any;
text: string;
attributedText: any;
lineBreakMode: number;
numberOfLines: number;
}
// /**
// * Module with ios specific utilities.
// */
// declare namespace iOSUtils {
// // Common properties between UILabel, UITextView and UITextField
// export interface TextUIView {
// font: any;
// textAlignment: number;
// textColor: any;
// text: string;
// attributedText: any;
// lineBreakMode: number;
// numberOfLines: number;
// }
/**
* Utility module dealing with some iOS collections.
*/
export namespace collections {
/**
* Converts JavaScript array to [NSArray](https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSArray_Class/).
* @param str - JavaScript string array to convert.
*/
export function jsArrayToNSArray<T>(str: T[]): NSArray<T>;
// /**
// * Utility module dealing with some iOS collections.
// */
// export namespace collections {
// /**
// * Converts JavaScript array to [NSArray](https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSArray_Class/).
// * @param str - JavaScript string array to convert.
// */
// export function jsArrayToNSArray<T>(str: T[]): NSArray<T>;
/**
* Converts NSArray to JavaScript array.
* @param a - NSArray to convert.
*/
export function nsArrayToJSArray<T>(a: NSArray<T>): T[];
}
// /**
// * Converts NSArray to JavaScript array.
// * @param a - NSArray to convert.
// */
// export function nsArrayToJSArray<T>(a: NSArray<T>): T[];
// }
/**
* Get the root UIViewController of the app
*/
export function getRootViewController(): any; /* UIViewController */
// /**
// * Get the root UIViewController of the app
// */
// export function getRootViewController(): any; /* UIViewController */
/**
* Get the UIWindow of the app
*/
export function getWindow(): any; /* UIWindow */
// /**
// * Get the UIWindow of the app
// */
// export function getWindow(): any; /* UIWindow */
/**
* Set the window background color of base view of the app.
* Often this is shown when opening a modal as the view underneath scales down revealing the window color.
* @param value color (hex, rgb, rgba, etc.)
*/
export function setWindowBackgroundColor(value: string): void;
// /**
// * Set the window background color of base view of the app.
// * Often this is shown when opening a modal as the view underneath scales down revealing the window color.
// * @param value color (hex, rgb, rgba, etc.)
// */
// export function setWindowBackgroundColor(value: string): void;
/**
* Data serialize and deserialize helpers
*/
export function dataSerialize(data?: any): any;
export function dataDeserialize(nativeData?: any): any;
// /**
// * Data serialize and deserialize helpers
// */
// export function dataSerialize(data?: any): any;
// export function dataDeserialize(nativeData?: any): any;
/**
* @deprecated use application.orientation instead
*
* Gets an information about if current mode is Landscape.
*/
export function isLandscape(): boolean;
// /**
// * @deprecated use application.orientation instead
// *
// * Gets an information about if current mode is Landscape.
// */
// export function isLandscape(): boolean;
/**
* Gets the iOS device major version (for 8.1 will return 8).
*/
export const MajorVersion: number;
// /**
// * Gets the iOS device major version (for 8.1 will return 8).
// */
// export const MajorVersion: number;
/**
* Opens file with associated application.
* @param filePath The file path.
*/
export function openFile(filePath: string): boolean;
// /**
// * Opens file with associated application.
// * @param filePath The file path.
// */
// 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;
// /**
// * 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 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).
* Returns the visible UIViewController.
*/
export function getVisibleViewController(rootViewController: any /* UIViewController*/): any; /* UIViewController*/
// /**
// * Gets the currently visible(topmost) UIViewController.
// * @param rootViewController The root UIViewController instance to start searching from (normally window.rootViewController).
// * Returns the visible UIViewController.
// */
// 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*/
// /**
// *
// * @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*/
/**
* @param nativeView UIView to find shadow layer with
* @param name Name of the shadow layer if looking for specifically named layer
* @param create should we create a new layer if not found
*/
export function getShadowLayer(nativeView: any /* UIView */, name?: string, create?: boolean): any; /* CALayer */
// /**
// * @param nativeView UIView to find shadow layer with
// * @param name Name of the shadow layer if looking for specifically named layer
// * @param create should we create a new layer if not found
// */
// export function getShadowLayer(nativeView: any /* UIView */, name?: string, create?: boolean): any; /* CALayer */
/**
* Create a UIDocumentInteractionControllerDelegate implementation for use with UIDocumentInteractionController
*/
export function createUIDocumentInteractionControllerDelegate(): any;
// /**
// * 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.
*/
export function isRealDevice(): boolean;
// /**
// * Checks whether the application is running on real device and not on simulator.
// */
// export function isRealDevice(): boolean;
/**
* Debug utility to insert CGRect values into logging output.
* Note: when printing a CGRect directly it will print blank so this helps show the values.
* @param rect CGRect
*/
export function printCGRect(rect: CGRect): void;
// /**
// * Debug utility to insert CGRect values into logging output.
// * Note: when printing a CGRect directly it will print blank so this helps show the values.
// * @param rect CGRect
// */
// export function printCGRect(rect: CGRect): void;
/**
* Take a snapshot of a View on screen.
* @param view view to snapshot
* @param scale screen scale
*/
export function snapshotView(view: UIView, scale: number): UIImage;
// /**
// * Take a snapshot of a View on screen.
// * @param view view to snapshot
// * @param scale screen scale
// */
// export function snapshotView(view: UIView, scale: number): UIImage;
/**
* Copy layer properties from one view to another.
* @param view a view to copy layer properties to
* @param toView a view to copy later properties from
* @param (optional) custom properties to copy between both views
*/
export function copyLayerProperties(view: UIView, toView: UIView, customProperties?: { view?: Array<string> /* Array<keyof UIView> */; layer?: Array<string> /* Array<keyof CALayer> */ }): void;
// /**
// * Copy layer properties from one view to another.
// * @param view a view to copy layer properties to
// * @param toView a view to copy later properties from
// * @param (optional) custom properties to copy between both views
// */
// 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 });
}
// /**
// * 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;
/**
* @deprecated use Utils.android instead.
*/
export import ad = AndroidUtils;
export import android = AndroidUtils;
import * as iOSUtils from './ios';
export import ios = iOSUtils;
/**
* @deprecated use Utils.ios instead.
*/
export import iOSNativeHelper = iOSUtils;
export import ios = iOSUtils;

View File

@@ -1,33 +1,7 @@
import * as iOSUtils from './ios';
import { platformCheck } from './platform-check';
import { Color } from '../color';
import { Trace } from '../trace';
import { CORE_ANIMATION_DEFAULTS, getDurationWithDampingFromSpring } from './common';
import { getClass, isNullOrUndefined, numberHasDecimals, numberIs64Bit } from './types';
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 = iOSNativeHelper.getCurrentAppPath();
const path = iOSNativeHelper.isRealDevice() ? filePath.replace('~', appPath) : filePath;
const controller = UIDocumentInteractionController.interactionControllerWithURL(NSURL.fileURLWithPath(path));
controller.delegate = iOSNativeHelper.createUIDocumentInteractionControllerDelegate();
return controller.presentPreviewAnimated(true);
} catch (e) {
Trace.write('Error in openFile', Trace.categories.Error, Trace.messageType.error);
}
return false;
}
export function dataDeserialize(nativeData?: any) {
if (isNullOrUndefined(nativeData)) {
// some native values will already be js null values
@@ -113,318 +87,13 @@ export function dataSerialize(data: any, wrapPrimitives: boolean = false) {
}
}
namespace iOSUtils {
// 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 {
const app = UIApplication.sharedApplication;
if (!app) {
return;
}
return app.keyWindow || (app.windows && app.windows.count > 0 && app.windows.objectAtIndex(0));
}
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;
}
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 getShadowLayer(nativeView: UIView, name: string = 'ns-shadow-layer', create: boolean = true): CALayer {
return nativeView.layer;
console.log(`--- ${create ? 'CREATE' : 'READ'}`);
/**
* UIView
* -> Shadow
*
*
* UIView
* -> UIView
* -> Shadow
*/
if (!nativeView) {
return null;
}
if (!nativeView.layer) {
// should never hit this?
console.log('- no layer! -');
return null;
}
// if the nativeView's layer is the shadow layer?
if (nativeView.layer.name === name) {
console.log('- found shadow layer - reusing.');
return nativeView.layer;
}
console.log('>> layer :', nativeView.layer);
if (nativeView.layer.sublayers?.count) {
const count = nativeView.layer.sublayers.count;
for (let i = 0; i < count; i++) {
const subLayer = nativeView.layer.sublayers.objectAtIndex(i);
console.log(`>> subLayer ${i + 1}/${count} :`, subLayer);
console.log(`>> subLayer ${i + 1}/${count} name :`, subLayer.name);
if (subLayer.name === name) {
console.log('- found shadow sublayer - reusing.');
return subLayer;
}
}
// if (nativeView instanceof UITextView) {
// return nativeView.layer.sublayers.objectAtIndex(1);
// } else {
// return nativeView.layer.sublayers.objectAtIndex(nativeView.layer.sublayers.count - 1);
// }
}
// else {
// layer = nativeView.layer;
// }
// we're not interested in creating a new layer
if (!create) {
return null;
}
console.log(`- adding a new layer for - ${name}`);
const viewLayer = nativeView.layer;
const newLayer = CALayer.layer();
newLayer.name = name;
newLayer.zPosition = 0.0;
// nativeView.layer.insertSublayerBelow(newLayer, nativeView.layer)
// newLayer.insertSublayerAtIndex(nativeView.layer, 0)
// nativeView.layer.zPosition = 1.0;
// nativeView.layer.addSublayer(newLayer);
// nativeView.layer = CALayer.layer()
nativeView.layer.insertSublayerAtIndex(newLayer, 0);
// nativeView.layer.insertSublayerAtIndex(viewLayer, 1)
// nativeView.layer.replaceSublayerWith(newLayer, nativeView.layer);
return newLayer;
}
export function createUIDocumentInteractionControllerDelegate(): NSObject {
@NativeClass
class UIDocumentInteractionControllerDelegateImpl extends NSObject implements UIDocumentInteractionControllerDelegate {
public static ObjCProtocols = [UIDocumentInteractionControllerDelegate];
public getViewController(): UIViewController {
const app = UIApplication.sharedApplication;
return app.keyWindow.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 {
// https://stackoverflow.com/a/5093092/4936697
const sourceType = UIImagePickerControllerSourceType.UIImagePickerControllerSourceTypeCamera;
const mediaTypes = UIImagePickerController.availableMediaTypesForSourceType(sourceType);
return mediaTypes;
} 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();
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);
}
}
// 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;
/**
* @deprecated Use `Utils.ios` instead.
*/
export import iOSNativeHelper = iOSUtils;
export import ios = iOSUtils;