From 2e1d2c175b6bcd15b70c4fd119b3cc303c3799f6 Mon Sep 17 00:00:00 2001 From: Nathan Walker Date: Thu, 17 Nov 2022 21:05:54 -0800 Subject: [PATCH] feat(utils): dismissKeyboard, copyToClipboard, setWindowBackgroundColor, getCurrentActivity and getResource (#10089) --- apps/toolbox/src/main-page.ts | 6 ++++- apps/toolbox/src/main.ts | 2 +- apps/toolbox/src/pages/box-shadow.ts | 2 +- apps/toolbox/src/pages/webview.xml | 17 +++++++----- packages/core/index.d.ts | 4 ++- packages/core/index.ts | 4 ++- packages/core/utils/index.android.ts | 24 +++++++++++++++++ packages/core/utils/index.d.ts | 10 +++++++ packages/core/utils/index.ios.ts | 12 +++++++++ packages/core/utils/native-helper.android.ts | 13 +++++++-- packages/core/utils/native-helper.d.ts | 25 +++++++++++++++++ packages/core/utils/native-helper.ios.ts | 28 +++++++++++++++++--- 12 files changed, 129 insertions(+), 18 deletions(-) diff --git a/apps/toolbox/src/main-page.ts b/apps/toolbox/src/main-page.ts index c2e8a7d5a..fec18a3ba 100644 --- a/apps/toolbox/src/main-page.ts +++ b/apps/toolbox/src/main-page.ts @@ -1,7 +1,11 @@ -import { EventData, Page } from '@nativescript/core'; +import { EventData, Page, Utils } from '@nativescript/core'; import { HelloWorldModel } from './main-view-model'; export function navigatingTo(args: EventData) { const page = args.object; page.bindingContext = new HelloWorldModel(); + + if (global.isIOS) { + Utils.ios.setWindowBackgroundColor('blue'); + } } diff --git a/apps/toolbox/src/main.ts b/apps/toolbox/src/main.ts index a4c5c529a..fba0a27ac 100644 --- a/apps/toolbox/src/main.ts +++ b/apps/toolbox/src/main.ts @@ -1,3 +1,3 @@ -import { Application } from '@nativescript/core'; +import { Application, Utils } from '@nativescript/core'; Application.run({ moduleName: 'app-root' }); diff --git a/apps/toolbox/src/pages/box-shadow.ts b/apps/toolbox/src/pages/box-shadow.ts index f269b3a2b..5de8dd7b1 100644 --- a/apps/toolbox/src/pages/box-shadow.ts +++ b/apps/toolbox/src/pages/box-shadow.ts @@ -10,7 +10,7 @@ export class BoxShadowModel extends Observable { private _selectedBackgroundType: string; private _selectedBorderType: string; private _selectedAnimation: string; - private _boxShadow: string = '0 10 15 -3 rgba(200, 0, 0, 0.4)'; + private _boxShadow: string = '0 0 2 2 rgba(200, 0, 0, 0.4)'; // private _boxShadow: string = '5 5 1 1 rgba(255, 0, 0, .9)'; // private _boxShadow: string = '5 5 5 10 rgba(255, 0, 0, .9)'; diff --git a/apps/toolbox/src/pages/webview.xml b/apps/toolbox/src/pages/webview.xml index 73f5b8f60..4ff23f3a6 100644 --- a/apps/toolbox/src/pages/webview.xml +++ b/apps/toolbox/src/pages/webview.xml @@ -5,13 +5,16 @@ - + diff --git a/packages/core/index.d.ts b/packages/core/index.d.ts index 3c75a31ad..c92f5b7be 100644 --- a/packages/core/index.d.ts +++ b/packages/core/index.d.ts @@ -105,7 +105,7 @@ export type { InstrumentationMode, TimerInfo } from './profiling'; export { encoding } from './text'; export * from './trace'; export * from './ui'; -import { GC, isFontIconURI, isDataURI, isFileOrResourcePath, executeOnMainThread, mainThreadify, isMainThread, dispatchToMainThread, executeOnUIThread, releaseNativeObject, getModuleName, openFile, openUrl, isRealDevice, layout, ad as androidUtils, iOSNativeHelper as iosUtils, Source, escapeRegexSymbols, convertString, dismissSoftInput, queueMacrotask, queueGC, throttle, debounce, dataSerialize, dataDeserialize } from './utils'; +import { GC, isFontIconURI, isDataURI, isFileOrResourcePath, executeOnMainThread, mainThreadify, isMainThread, dispatchToMainThread, executeOnUIThread, releaseNativeObject, getModuleName, openFile, openUrl, isRealDevice, layout, ad as androidUtils, iOSNativeHelper as iosUtils, Source, escapeRegexSymbols, convertString, dismissSoftInput, dismissKeyboard, queueMacrotask, queueGC, throttle, debounce, dataSerialize, dataDeserialize, copyToClipboard } from './utils'; import { ClassInfo, getClass, getBaseClasses, getClassInfo, isBoolean, isDefined, isFunction, isNullOrUndefined, isNumber, isObject, isString, isUndefined, toUIString, verifyCallback, numberHasDecimals, numberIs64Bit } from './utils/types'; export declare const Utils: { GC: typeof GC; @@ -158,5 +158,7 @@ export declare const Utils: { toUIString: typeof toUIString; verifyCallback: typeof verifyCallback; dismissSoftInput: typeof dismissSoftInput; + dismissKeyboard: typeof dismissKeyboard; + copyToClipboard: typeof copyToClipboard; }; export { XmlParser, ParserEventType, ParserEvent } from './xml'; diff --git a/packages/core/index.ts b/packages/core/index.ts index bc57df100..4d536133e 100644 --- a/packages/core/index.ts +++ b/packages/core/index.ts @@ -137,7 +137,7 @@ export * from './trace'; export * from './ui'; -import { GC, isFontIconURI, isDataURI, isFileOrResourcePath, executeOnMainThread, mainThreadify, isMainThread, dispatchToMainThread, executeOnUIThread, queueMacrotask, queueGC, debounce, throttle, releaseNativeObject, getModuleName, openFile, openUrl, isRealDevice, layout, ad as androidUtils, iOSNativeHelper as iosUtils, Source, RESOURCE_PREFIX, FILE_PREFIX, escapeRegexSymbols, convertString, dismissSoftInput, dataDeserialize, dataSerialize } from './utils'; +import { GC, isFontIconURI, isDataURI, isFileOrResourcePath, executeOnMainThread, mainThreadify, isMainThread, dispatchToMainThread, executeOnUIThread, queueMacrotask, queueGC, debounce, throttle, releaseNativeObject, getModuleName, openFile, openUrl, isRealDevice, layout, ad as androidUtils, iOSNativeHelper as iosUtils, Source, RESOURCE_PREFIX, FILE_PREFIX, escapeRegexSymbols, convertString, dismissSoftInput, dismissKeyboard, dataDeserialize, dataSerialize, copyToClipboard } from './utils'; import { ClassInfo, getClass, getBaseClasses, getClassInfo, isBoolean, isDefined, isFunction, isNullOrUndefined, isNumber, isObject, isString, isUndefined, toUIString, verifyCallback, numberHasDecimals, numberIs64Bit } from './utils/types'; export const Utils = { @@ -194,6 +194,8 @@ export const Utils = { toUIString, verifyCallback, dismissSoftInput, + dismissKeyboard, + copyToClipboard, }; export { XmlParser, ParserEventType, ParserEvent } from './xml'; diff --git a/packages/core/utils/index.android.ts b/packages/core/utils/index.android.ts index b4a28b6ed..919bcf30c 100644 --- a/packages/core/utils/index.android.ts +++ b/packages/core/utils/index.android.ts @@ -1,4 +1,5 @@ import { ad } from './native-helper'; +import { android as androidApp } from '../application'; import { SDK_VERSION } from '../utils'; import { FileSystemAccess } from '../file-system/file-system-access'; import { Trace } from '../trace'; @@ -170,3 +171,26 @@ export function isRealDevice(): boolean { export function dismissSoftInput(nativeView?: any): void { ad.dismissSoftInput(nativeView); } + +export function dismissKeyboard() { + dismissSoftInput(); + + const activity = ad.getCurrentActivity(); + if (activity) { + const focus = activity.getCurrentFocus(); + + if (focus) { + focus.clearFocus(); + } + } +} + +export function copyToClipboard(value: string) { + try { + const clipboard = ad.getApplicationContext().getSystemService(android.content.Context.CLIPBOARD_SERVICE); + const clip = android.content.ClipData.newPlainText('Clipboard value', value); + clipboard.setPrimaryClip(clip); + } catch (err) { + console.log(err); + } +} diff --git a/packages/core/utils/index.d.ts b/packages/core/utils/index.d.ts index 425f4fcb9..7b17bf2a4 100644 --- a/packages/core/utils/index.d.ts +++ b/packages/core/utils/index.d.ts @@ -321,3 +321,13 @@ export function isRealDevice(): boolean; * Hides the soft input method, usually a soft keyboard. */ export function dismissSoftInput(nativeView?: any): void; + +/** + * Dismiss any keyboard visible on the screen. + */ +export function dismissKeyboard(): void; + +/** + * Copy value to device clipboard. + */ +export function copyToClipboard(value: string): void; diff --git a/packages/core/utils/index.ios.ts b/packages/core/utils/index.ios.ts index 70c0f18bc..5044fbc1b 100644 --- a/packages/core/utils/index.ios.ts +++ b/packages/core/utils/index.ios.ts @@ -55,3 +55,15 @@ export function dismissSoftInput(nativeView?: UIView): void { } UIApplication.sharedApplication.sendActionToFromForEvent('resignFirstResponder', null, null, null); } + +export function dismissKeyboard() { + dismissSoftInput(); +} + +export function copyToClipboard(value: string) { + try { + UIPasteboard.generalPasteboard.setValueForPasteboardType(value, kUTTypePlainText); + } catch (err) { + console.log(err); + } +} diff --git a/packages/core/utils/native-helper.android.ts b/packages/core/utils/native-helper.android.ts index 978793ce9..48dadb2c3 100644 --- a/packages/core/utils/native-helper.android.ts +++ b/packages/core/utils/native-helper.android.ts @@ -152,6 +152,12 @@ export namespace ad { return applicationContext; } + export function getCurrentActivity() { + if (!androidApp) { + return null; + } + return androidApp.foregroundActivity || androidApp.startActivity; + } export function getApplication() { if (!application) { application = getNativeApplication(); @@ -199,8 +205,8 @@ export namespace ad { return; } windowToken = nativeView.getWindowToken(); - } else if (androidApp.foregroundActivity instanceof androidx.appcompat.app.AppCompatActivity) { - const decorView = androidApp.foregroundActivity.getWindow().getDecorView(); + } else if (getCurrentActivity() instanceof androidx.appcompat.app.AppCompatActivity) { + const decorView = getCurrentActivity().getWindow().getDecorView(); if (decorView) { windowToken = decorView.getWindowToken(); decorView.requestFocus(); @@ -259,6 +265,9 @@ export namespace ad { 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); } diff --git a/packages/core/utils/native-helper.d.ts b/packages/core/utils/native-helper.d.ts index e21f9f131..11ec7d9e4 100644 --- a/packages/core/utils/native-helper.d.ts +++ b/packages/core/utils/native-helper.d.ts @@ -18,6 +18,10 @@ export namespace ad { */ 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. */ @@ -82,6 +86,15 @@ export namespace ad { */ 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; + /** * [Obsolete - please use getPaletteColor] Gets a color from current theme. * @param name - Name of the color @@ -137,6 +150,18 @@ export namespace iOSNativeHelper { */ export function getRootViewController(): any; /* UIViewController */ + /** + * 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; + /** * Data serialize and deserialize helpers */ diff --git a/packages/core/utils/native-helper.ios.ts b/packages/core/utils/native-helper.ios.ts index b6d70fc87..2169f070f 100644 --- a/packages/core/utils/native-helper.ios.ts +++ b/packages/core/utils/native-helper.ios.ts @@ -1,3 +1,4 @@ +import { Color } from '../color'; import { Trace } from '../trace'; import { getClass, isNullOrUndefined, numberHasDecimals, numberIs64Bit } from './types'; @@ -108,7 +109,7 @@ export function dataSerialize(data: any, wrapPrimitives: boolean = false) { } export namespace iOSNativeHelper { - // TODO: remove for NativeScript 7.0 + // TODO: remove for NativeScript 9.0 export function getter(_this: any, property: T | { (): T }): T { console.log('utils.ios.getter() is deprecated; use the respective native property instead'); if (typeof property === 'function') { @@ -137,15 +138,34 @@ export namespace iOSNativeHelper { } export function getRootViewController(): UIViewController { - const app = UIApplication.sharedApplication; - const win = app.keyWindow || (app.windows && app.windows.count > 0 && app.windows.objectAtIndex(0)); - let vc = win.rootViewController; + 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');