feat(utils): dismissKeyboard, copyToClipboard, setWindowBackgroundColor, getCurrentActivity and getResource (#10089)

This commit is contained in:
Nathan Walker
2022-11-17 21:05:54 -08:00
committed by GitHub
parent 0226f47f8d
commit 2e1d2c175b
12 changed files with 129 additions and 18 deletions

View File

@ -1,7 +1,11 @@
import { EventData, Page } from '@nativescript/core'; import { EventData, Page, Utils } from '@nativescript/core';
import { HelloWorldModel } from './main-view-model'; import { HelloWorldModel } from './main-view-model';
export function navigatingTo(args: EventData) { export function navigatingTo(args: EventData) {
const page = <Page>args.object; const page = <Page>args.object;
page.bindingContext = new HelloWorldModel(); page.bindingContext = new HelloWorldModel();
if (global.isIOS) {
Utils.ios.setWindowBackgroundColor('blue');
}
} }

View File

@ -1,3 +1,3 @@
import { Application } from '@nativescript/core'; import { Application, Utils } from '@nativescript/core';
Application.run({ moduleName: 'app-root' }); Application.run({ moduleName: 'app-root' });

View File

@ -10,7 +10,7 @@ export class BoxShadowModel extends Observable {
private _selectedBackgroundType: string; private _selectedBackgroundType: string;
private _selectedBorderType: string; private _selectedBorderType: string;
private _selectedAnimation: 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 1 1 rgba(255, 0, 0, .9)';
// private _boxShadow: string = '5 5 5 10 rgba(255, 0, 0, .9)'; // private _boxShadow: string = '5 5 5 10 rgba(255, 0, 0, .9)';

View File

@ -8,10 +8,13 @@
<Label text="iPhone by default plays media content in a webview in full screen, meaning it takes over your whole UI. You can toggle this on and off with a cool new property: iosAllowInlineMediaPlayback" textWrap="true" style="color: gray;" class="m-b-10 v-center text-center" /> <Label text="iPhone by default plays media content in a webview in full screen, meaning it takes over your whole UI. You can toggle this on and off with a cool new property: iosAllowInlineMediaPlayback" textWrap="true" style="color: gray;" class="m-b-10 v-center text-center" />
<Label style="color: gray;" textWrap="true" class="m-b-5 text-center" text="This webview plays this youtube content inline." /> <Label style="color: gray;" textWrap="true" class="m-b-5 text-center" text="This webview plays this youtube content inline." />
<WebView iosAllowInlineMediaPlayback="true" src="https://sphere.presonus.com/video/youtube/QnGX0xrv6Dw" height="200" /> <GridLayout rows="200" columns="">
<WebView iosAllowInlineMediaPlayback="true" src="https://www.youtube.com/embed/Mzy1jWxrSiw" height="200" />
</GridLayout>
<Label style="color: gray;" textWrap="true" class="m-b-5 m-t-20 text-center" text="This webview forces media content into fullscreen on iPhone." /> <Label style="color: gray;" textWrap="true" class="m-b-5 m-t-20 text-center" text="This webview forces media content into fullscreen on iPhone." />
<WebView src="https://sphere.presonus.com/video/youtube/QnGX0xrv6Dw" height="200" /> <GridLayout rows="200" columns="">
<WebView src="https://www.youtube.com/embed/Mzy1jWxrSiw" height="200" />
</GridLayout>
</StackLayout> </StackLayout>
</Page> </Page>

View File

@ -105,7 +105,7 @@ export type { InstrumentationMode, TimerInfo } from './profiling';
export { encoding } from './text'; export { encoding } from './text';
export * from './trace'; export * from './trace';
export * from './ui'; 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'; import { ClassInfo, getClass, getBaseClasses, getClassInfo, isBoolean, isDefined, isFunction, isNullOrUndefined, isNumber, isObject, isString, isUndefined, toUIString, verifyCallback, numberHasDecimals, numberIs64Bit } from './utils/types';
export declare const Utils: { export declare const Utils: {
GC: typeof GC; GC: typeof GC;
@ -158,5 +158,7 @@ export declare const Utils: {
toUIString: typeof toUIString; toUIString: typeof toUIString;
verifyCallback: typeof verifyCallback; verifyCallback: typeof verifyCallback;
dismissSoftInput: typeof dismissSoftInput; dismissSoftInput: typeof dismissSoftInput;
dismissKeyboard: typeof dismissKeyboard;
copyToClipboard: typeof copyToClipboard;
}; };
export { XmlParser, ParserEventType, ParserEvent } from './xml'; export { XmlParser, ParserEventType, ParserEvent } from './xml';

View File

@ -137,7 +137,7 @@ export * from './trace';
export * from './ui'; 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'; import { ClassInfo, getClass, getBaseClasses, getClassInfo, isBoolean, isDefined, isFunction, isNullOrUndefined, isNumber, isObject, isString, isUndefined, toUIString, verifyCallback, numberHasDecimals, numberIs64Bit } from './utils/types';
export const Utils = { export const Utils = {
@ -194,6 +194,8 @@ export const Utils = {
toUIString, toUIString,
verifyCallback, verifyCallback,
dismissSoftInput, dismissSoftInput,
dismissKeyboard,
copyToClipboard,
}; };
export { XmlParser, ParserEventType, ParserEvent } from './xml'; export { XmlParser, ParserEventType, ParserEvent } from './xml';

View File

@ -1,4 +1,5 @@
import { ad } from './native-helper'; import { ad } from './native-helper';
import { android as androidApp } from '../application';
import { SDK_VERSION } from '../utils'; import { SDK_VERSION } from '../utils';
import { FileSystemAccess } from '../file-system/file-system-access'; import { FileSystemAccess } from '../file-system/file-system-access';
import { Trace } from '../trace'; import { Trace } from '../trace';
@ -170,3 +171,26 @@ export function isRealDevice(): boolean {
export function dismissSoftInput(nativeView?: any): void { export function dismissSoftInput(nativeView?: any): void {
ad.dismissSoftInput(nativeView); 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);
}
}

View File

@ -321,3 +321,13 @@ export function isRealDevice(): boolean;
* Hides the soft input method, usually a soft keyboard. * Hides the soft input method, usually a soft keyboard.
*/ */
export function dismissSoftInput(nativeView?: any): void; 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;

View File

@ -55,3 +55,15 @@ export function dismissSoftInput(nativeView?: UIView): void {
} }
UIApplication.sharedApplication.sendActionToFromForEvent('resignFirstResponder', null, null, null); 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);
}
}

View File

@ -152,6 +152,12 @@ export namespace ad {
return applicationContext; return applicationContext;
} }
export function getCurrentActivity() {
if (!androidApp) {
return null;
}
return androidApp.foregroundActivity || androidApp.startActivity;
}
export function getApplication() { export function getApplication() {
if (!application) { if (!application) {
application = <android.app.Application>getNativeApplication(); application = <android.app.Application>getNativeApplication();
@ -199,8 +205,8 @@ export namespace ad {
return; return;
} }
windowToken = nativeView.getWindowToken(); windowToken = nativeView.getWindowToken();
} else if (androidApp.foregroundActivity instanceof androidx.appcompat.app.AppCompatActivity) { } else if (getCurrentActivity() instanceof androidx.appcompat.app.AppCompatActivity) {
const decorView = androidApp.foregroundActivity.getWindow().getDecorView(); const decorView = getCurrentActivity().getWindow().getDecorView();
if (decorView) { if (decorView) {
windowToken = decorView.getWindowToken(); windowToken = decorView.getWindowToken();
decorView.requestFocus(); decorView.requestFocus();
@ -259,6 +265,9 @@ export namespace ad {
return resources.getIdentifier(uri, null, null); 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 { export function getPalleteColor(name: string, context: android.content.Context): number {
return getPaletteColor(name, context); return getPaletteColor(name, context);
} }

View File

@ -18,6 +18,10 @@ export namespace ad {
*/ */
export function getApplication(): any; /* android.app.Application */ 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. * Gets the native Android application resources.
*/ */
@ -82,6 +86,15 @@ export namespace ad {
*/ */
export function getId(name: string): number; 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. * [Obsolete - please use getPaletteColor] Gets a color from current theme.
* @param name - Name of the color * @param name - Name of the color
@ -137,6 +150,18 @@ export namespace iOSNativeHelper {
*/ */
export function getRootViewController(): any; /* UIViewController */ 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 * Data serialize and deserialize helpers
*/ */

View File

@ -1,3 +1,4 @@
import { Color } from '../color';
import { Trace } from '../trace'; import { Trace } from '../trace';
import { getClass, isNullOrUndefined, numberHasDecimals, numberIs64Bit } from './types'; import { getClass, isNullOrUndefined, numberHasDecimals, numberIs64Bit } from './types';
@ -108,7 +109,7 @@ export function dataSerialize(data: any, wrapPrimitives: boolean = false) {
} }
export namespace iOSNativeHelper { export namespace iOSNativeHelper {
// TODO: remove for NativeScript 7.0 // TODO: remove for NativeScript 9.0
export function getter<T>(_this: any, property: T | { (): T }): T { export function getter<T>(_this: any, property: T | { (): T }): T {
console.log('utils.ios.getter() is deprecated; use the respective native property instead'); console.log('utils.ios.getter() is deprecated; use the respective native property instead');
if (typeof property === 'function') { if (typeof property === 'function') {
@ -137,15 +138,34 @@ export namespace iOSNativeHelper {
} }
export function getRootViewController(): UIViewController { export function getRootViewController(): UIViewController {
const app = UIApplication.sharedApplication; const win = getWindow();
const win = app.keyWindow || (app.windows && app.windows.count > 0 && app.windows.objectAtIndex(0)); let vc = win && win.rootViewController;
let vc = win.rootViewController;
while (vc && vc.presentedViewController) { while (vc && vc.presentedViewController) {
vc = vc.presentedViewController; vc = vc.presentedViewController;
} }
return vc; 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 { export function isLandscape(): boolean {
console.log('utils.ios.isLandscape() is deprecated; use application.orientation instead'); console.log('utils.ios.isLandscape() is deprecated; use application.orientation instead');