mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-11-05 13:26:48 +08:00
feat(android): edge-to-edge
Squashed from feat/edge-to-edge and resolved conflicts
This commit is contained in:
@@ -40,11 +40,12 @@ export namespace CoreTypes {
|
||||
unit: 'px',
|
||||
};
|
||||
|
||||
export type KeyboardInputType = 'datetime' | 'phone' | 'number' | 'url' | 'email' | 'integer';
|
||||
export type KeyboardInputType = 'datetime' | 'phone' | 'number' | 'decimal' | 'url' | 'email' | 'integer';
|
||||
export namespace KeyboardType {
|
||||
export const datetime = 'datetime';
|
||||
export const phone = 'phone';
|
||||
export const number = 'number';
|
||||
export const decimal = 'decimal';
|
||||
export const url = 'url';
|
||||
export const email = 'email';
|
||||
export const integer = 'integer';
|
||||
|
||||
Binary file not shown.
@@ -316,6 +316,120 @@ function getModalOptions(domId: number): DialogOptions {
|
||||
return modalMap.get(domId);
|
||||
}
|
||||
|
||||
const INSET_LEFT = 0;
|
||||
const INSET_TOP = 4;
|
||||
const INSET_RIGHT = 8;
|
||||
const INSET_BOTTOM = 12;
|
||||
const INSET_LEFT_CONSUMED = 16;
|
||||
const INSET_TOP_CONSUMED = 20;
|
||||
const INSET_RIGHT_CONSUMED = 24;
|
||||
const INSET_BOTTOM_CONSUMED = 28;
|
||||
|
||||
const OverflowEdgeNone: number = 0;
|
||||
const OverflowEdgeLeft: number = 1;
|
||||
const OverflowEdgeTop: number = 1 << 1;
|
||||
const OverflowEdgeRight: number = 1 << 2;
|
||||
const OverflowEdgeBottom: number = 1 << 3;
|
||||
const OverflowEdgeDontApply: number = 1 << 4;
|
||||
const OverflowEdgeLeftDontConsume: number = 1 << 5;
|
||||
const OverflowEdgeTopDontConsume: number = 1 << 6;
|
||||
const OverflowEdgeRightDontConsume: number = 1 << 7;
|
||||
const OverflowEdgeBottomDontConsume: number = 1 << 8;
|
||||
const OverflowEdgeAllButLeft: number = 1 << 9;
|
||||
const OverflowEdgeAllButTop: number = 1 << 10;
|
||||
const OverflowEdgeAllButRight: number = 1 << 11;
|
||||
const OverflowEdgeAllButBottom: number = 1 << 12;
|
||||
|
||||
class Inset {
|
||||
private view: DataView;
|
||||
private data: ArrayBuffer;
|
||||
constructor(data: java.nio.ByteBuffer) {
|
||||
this.data = (<any>ArrayBuffer).from(data);
|
||||
this.view = new DataView(this.data);
|
||||
}
|
||||
|
||||
public get left(): number {
|
||||
return this.view.getInt32(INSET_LEFT, true);
|
||||
}
|
||||
|
||||
public set left(value: number) {
|
||||
this.view.setInt32(INSET_LEFT, value, true);
|
||||
}
|
||||
|
||||
public get top(): number {
|
||||
return this.view.getInt32(INSET_TOP, true);
|
||||
}
|
||||
|
||||
public set top(value: number) {
|
||||
this.view.setInt32(INSET_TOP, value, true);
|
||||
}
|
||||
|
||||
public get right(): number {
|
||||
return this.view.getInt32(INSET_RIGHT, true);
|
||||
}
|
||||
|
||||
public set right(value: number) {
|
||||
this.view.setInt32(INSET_RIGHT, value, true);
|
||||
}
|
||||
|
||||
public get bottom(): number {
|
||||
return this.view.getInt32(INSET_BOTTOM, true);
|
||||
}
|
||||
|
||||
public set bottom(value: number) {
|
||||
this.view.setInt32(INSET_BOTTOM, value, true);
|
||||
}
|
||||
|
||||
public get leftConsumed(): boolean {
|
||||
return this.view.getInt32(INSET_LEFT_CONSUMED, true) > 0;
|
||||
}
|
||||
|
||||
public set leftConsumed(value: boolean) {
|
||||
this.view.setInt32(INSET_LEFT_CONSUMED, value ? 1 : 0, true);
|
||||
}
|
||||
|
||||
public get topConsumed(): boolean {
|
||||
return this.view.getInt32(INSET_TOP_CONSUMED, true) > 0;
|
||||
}
|
||||
|
||||
public set topConsumed(value: boolean) {
|
||||
this.view.setInt32(INSET_TOP_CONSUMED, value ? 1 : 0, true);
|
||||
}
|
||||
|
||||
public get rightConsumed(): boolean {
|
||||
return this.view.getInt32(INSET_RIGHT_CONSUMED, true) > 0;
|
||||
}
|
||||
|
||||
public set rightConsumed(value: boolean) {
|
||||
this.view.setInt32(INSET_RIGHT_CONSUMED, value ? 1 : 0, true);
|
||||
}
|
||||
|
||||
public get bottomConsumed(): boolean {
|
||||
return this.view.getInt32(INSET_BOTTOM_CONSUMED, true) > 0;
|
||||
}
|
||||
|
||||
public set bottomConsumed(value: boolean) {
|
||||
this.view.setInt32(INSET_BOTTOM_CONSUMED, value ? 1 : 0, true);
|
||||
}
|
||||
|
||||
toString() {
|
||||
return `Inset: left=${this.left}, top=${this.top}, right=${this.right}, bottom=${this.bottom}, ` + `leftConsumed=${this.leftConsumed}, topConsumed=${this.topConsumed}, ` + `rightConsumed=${this.rightConsumed}, bottomConsumed=${this.bottomConsumed}`;
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
return {
|
||||
left: this.left,
|
||||
top: this.top,
|
||||
right: this.right,
|
||||
bottom: this.bottom,
|
||||
leftConsumed: this.leftConsumed,
|
||||
topConsumed: this.topConsumed,
|
||||
rightConsumed: this.rightConsumed,
|
||||
bottomConsumed: this.bottomConsumed,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class View extends ViewCommon {
|
||||
public static androidBackPressedEvent = androidBackPressedEvent;
|
||||
|
||||
@@ -326,6 +440,8 @@ export class View extends ViewCommon {
|
||||
private layoutChangeListenerIsSet: boolean;
|
||||
private layoutChangeListener: android.view.View.OnLayoutChangeListener;
|
||||
private _rootManager: androidx.fragment.app.FragmentManager;
|
||||
private insetListenerIsSet: boolean;
|
||||
private needsInsetListener: boolean;
|
||||
|
||||
nativeViewProtected: android.view.View;
|
||||
|
||||
@@ -344,6 +460,12 @@ export class View extends ViewCommon {
|
||||
if (this.isLoaded && !this.layoutChangeListenerIsSet && isLayoutEvent) {
|
||||
this.setOnLayoutChangeListener();
|
||||
}
|
||||
|
||||
const isInsetEvent = typeof eventNames === 'string' ? eventNames.indexOf(ViewCommon.androidOverflowInsetEvent) !== -1 : false;
|
||||
// only avaiable on LayoutBase
|
||||
if (!this.insetListenerIsSet && isInsetEvent) {
|
||||
this.setInsetListener();
|
||||
}
|
||||
}
|
||||
|
||||
removeEventListener(eventNames: string, callback?: (data: EventData) => void, thisArg?: any) {
|
||||
@@ -355,6 +477,43 @@ export class View extends ViewCommon {
|
||||
this.nativeViewProtected.removeOnLayoutChangeListener(this.layoutChangeListener);
|
||||
this.layoutChangeListenerIsSet = false;
|
||||
}
|
||||
|
||||
const isInsetEvent = typeof eventNames === 'string' ? eventNames.indexOf(ViewCommon.androidOverflowInsetEvent) !== -1 : false;
|
||||
|
||||
if (this.insetListenerIsSet && isInsetEvent && this.nativeViewProtected && (this.nativeViewProtected as any).setInsetListener) {
|
||||
(this.nativeViewProtected as any).setInsetListener(null);
|
||||
this.insetListenerIsSet = false;
|
||||
}
|
||||
}
|
||||
|
||||
private setInsetListener() {
|
||||
if (this.nativeViewProtected) {
|
||||
if ((this.nativeViewProtected as any).setInsetListener) {
|
||||
const ref = new WeakRef(this);
|
||||
(this.nativeViewProtected as any).setInsetListener(
|
||||
new org.nativescript.widgets.LayoutBase.WindowInsetListener({
|
||||
onApplyWindowInsets(param0) {
|
||||
const owner = ref.get();
|
||||
if (!owner) {
|
||||
return;
|
||||
}
|
||||
|
||||
const inset = new Inset(param0);
|
||||
const args = {
|
||||
eventName: ViewCommon.androidOverflowInsetEvent,
|
||||
object: this,
|
||||
inset,
|
||||
};
|
||||
owner.notify(args);
|
||||
},
|
||||
}),
|
||||
);
|
||||
this.insetListenerIsSet = true;
|
||||
}
|
||||
this.needsInsetListener = false;
|
||||
} else {
|
||||
this.needsInsetListener = true;
|
||||
}
|
||||
}
|
||||
|
||||
public _getChildFragmentManager(): androidx.fragment.app.FragmentManager {
|
||||
@@ -415,6 +574,134 @@ export class View extends ViewCommon {
|
||||
return manager;
|
||||
}
|
||||
|
||||
protected _defaultOverflowEdge: number = OverflowEdgeNone;
|
||||
protected _defaultOverflowEdgeValue: string = 'none';
|
||||
// @ts-ignore
|
||||
public set androidOverflowEdge(value: string) {
|
||||
if (typeof value !== 'string') {
|
||||
return;
|
||||
}
|
||||
const nativeView = this.nativeViewProtected as any;
|
||||
if (nativeView && nativeView.setOverflowEdge) {
|
||||
if (value === 'none') {
|
||||
nativeView.setOverflowEdge(OverflowEdgeNone);
|
||||
} else {
|
||||
const newValue = parseEdges(value);
|
||||
if (newValue !== null) {
|
||||
nativeView.setOverflowEdge(newValue);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const edge = parseEdges(value);
|
||||
|
||||
if (edge === null) {
|
||||
return;
|
||||
}
|
||||
this._defaultOverflowEdgeValue = value;
|
||||
this._defaultOverflowEdge = edge;
|
||||
}
|
||||
}
|
||||
|
||||
public get androidOverflowEdge() {
|
||||
const nativeView = this.nativeViewProtected as any;
|
||||
if (nativeView && nativeView.getOverflowEdge) {
|
||||
const overflowEdge = nativeView.getOverflowEdge();
|
||||
switch (overflowEdge) {
|
||||
case OverflowEdgeNone:
|
||||
return 'none';
|
||||
case OverflowEdgeLeft:
|
||||
return 'left';
|
||||
case OverflowEdgeTop:
|
||||
return 'top';
|
||||
case OverflowEdgeRight:
|
||||
return 'right';
|
||||
case OverflowEdgeBottom:
|
||||
return 'bottom';
|
||||
case OverflowEdgeDontApply:
|
||||
return 'dont-apply';
|
||||
case OverflowEdgeLeftDontConsume:
|
||||
return 'left-dont-consume';
|
||||
case OverflowEdgeTopDontConsume:
|
||||
return 'top-dont-consume';
|
||||
case OverflowEdgeRightDontConsume:
|
||||
return 'right-dont-consume';
|
||||
case OverflowEdgeBottomDontConsume:
|
||||
return 'bottom-dont-consume';
|
||||
case OverflowEdgeAllButLeft:
|
||||
return 'all-but-left';
|
||||
case OverflowEdgeAllButTop:
|
||||
return 'all-but-top';
|
||||
case OverflowEdgeAllButRight:
|
||||
return 'all-but-right';
|
||||
case OverflowEdgeAllButBottom:
|
||||
return 'all-but-bottom';
|
||||
default:
|
||||
{
|
||||
let value = '';
|
||||
const overflowLeftConsume = (overflowEdge & OverflowEdgeLeft) == OverflowEdgeLeft;
|
||||
const overflowTopConsume = (overflowEdge & OverflowEdgeTop) == OverflowEdgeTop;
|
||||
const overflowRightConsume = (overflowEdge & OverflowEdgeRight) == OverflowEdgeRight;
|
||||
const overflowBottomConsume = (overflowEdge & OverflowEdgeBottom) == OverflowEdgeBottom;
|
||||
|
||||
const overflowLeft = (overflowEdge & OverflowEdgeLeftDontConsume) == OverflowEdgeLeftDontConsume;
|
||||
const overflowTop = (overflowEdge & OverflowEdgeTopDontConsume) == OverflowEdgeTopDontConsume;
|
||||
const overflowRight = (overflowEdge & OverflowEdgeRightDontConsume) == OverflowEdgeRightDontConsume;
|
||||
const overflowBottom = (overflowEdge & OverflowEdgeBottomDontConsume) == OverflowEdgeBottomDontConsume;
|
||||
|
||||
if (overflowLeftConsume) {
|
||||
value += 'left';
|
||||
}
|
||||
if (overflowTopConsume) {
|
||||
if (value.length > 0) {
|
||||
value += ',';
|
||||
}
|
||||
value += 'top';
|
||||
}
|
||||
if (overflowRightConsume) {
|
||||
if (value.length > 0) {
|
||||
value += ',';
|
||||
}
|
||||
value += 'right';
|
||||
}
|
||||
if (overflowBottomConsume) {
|
||||
if (value.length > 0) {
|
||||
value += ',';
|
||||
}
|
||||
value += 'bottom';
|
||||
}
|
||||
|
||||
if (overflowLeft) {
|
||||
value += 'left-dont-consume';
|
||||
}
|
||||
if (overflowTop) {
|
||||
if (value.length > 0) {
|
||||
value += ',';
|
||||
}
|
||||
value += 'top-dont-consume';
|
||||
}
|
||||
if (overflowRight) {
|
||||
if (value.length > 0) {
|
||||
value += ',';
|
||||
}
|
||||
value += 'right-dont-consume';
|
||||
}
|
||||
if (overflowBottom) {
|
||||
if (value.length > 0) {
|
||||
value += ',';
|
||||
}
|
||||
value += 'bottom-dont-consume';
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (this._defaultOverflowEdgeValue) {
|
||||
return this._defaultOverflowEdgeValue;
|
||||
}
|
||||
}
|
||||
return 'none';
|
||||
}
|
||||
|
||||
@profile
|
||||
public onLoaded() {
|
||||
this._manager = null;
|
||||
@@ -464,6 +751,17 @@ export class View extends ViewCommon {
|
||||
if (this.needsOnLayoutChangeListener()) {
|
||||
this.setOnLayoutChangeListener();
|
||||
}
|
||||
|
||||
if (!this.insetListenerIsSet && this.needsInsetListener) {
|
||||
this.setInsetListener();
|
||||
}
|
||||
|
||||
const nativeView = this.nativeViewProtected as any;
|
||||
if (typeof this._defaultOverflowEdge === 'number') {
|
||||
if (nativeView && nativeView.setOverflowEdge) {
|
||||
nativeView.setOverflowEdge(this._defaultOverflowEdge);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public needsOnLayoutChangeListener() {
|
||||
@@ -1269,6 +1567,111 @@ export class View extends ViewCommon {
|
||||
}
|
||||
}
|
||||
|
||||
function parseEdges(edges: string): number | null {
|
||||
const values = edges.trim().split(',');
|
||||
let newValue = -1;
|
||||
for (let value of values) {
|
||||
const trimmedValue = value.trim();
|
||||
switch (trimmedValue) {
|
||||
case 'none':
|
||||
if (newValue === -1) {
|
||||
newValue = OverflowEdgeNone;
|
||||
} else {
|
||||
newValue |= OverflowEdgeNone;
|
||||
}
|
||||
break;
|
||||
case 'left':
|
||||
if (newValue === -1) {
|
||||
newValue = OverflowEdgeLeft;
|
||||
} else {
|
||||
newValue |= OverflowEdgeLeft;
|
||||
}
|
||||
break;
|
||||
case 'top':
|
||||
if (newValue === -1) {
|
||||
newValue = OverflowEdgeTop;
|
||||
} else {
|
||||
newValue |= OverflowEdgeTop;
|
||||
}
|
||||
break;
|
||||
case 'right':
|
||||
if (newValue === -1) {
|
||||
newValue = OverflowEdgeRight;
|
||||
} else {
|
||||
newValue |= OverflowEdgeRight;
|
||||
}
|
||||
break;
|
||||
case 'bottom':
|
||||
if (newValue === -1) {
|
||||
newValue = OverflowEdgeBottom;
|
||||
} else {
|
||||
newValue |= OverflowEdgeBottom;
|
||||
}
|
||||
break;
|
||||
case 'dont-apply':
|
||||
newValue = OverflowEdgeDontApply;
|
||||
break;
|
||||
case 'left-dont-consume':
|
||||
if (newValue === -1) {
|
||||
newValue = OverflowEdgeLeftDontConsume;
|
||||
} else {
|
||||
newValue |= OverflowEdgeLeftDontConsume;
|
||||
}
|
||||
break;
|
||||
case 'top-dont-consume':
|
||||
if (newValue === -1) {
|
||||
newValue = OverflowEdgeTopDontConsume;
|
||||
} else {
|
||||
newValue |= OverflowEdgeTopDontConsume;
|
||||
}
|
||||
break;
|
||||
case 'right-dont-consume':
|
||||
if (newValue === -1) {
|
||||
newValue = OverflowEdgeRightDontConsume;
|
||||
} else {
|
||||
newValue |= OverflowEdgeRightDontConsume;
|
||||
}
|
||||
break;
|
||||
case 'bottom-dont-consume':
|
||||
if (newValue === -1) {
|
||||
newValue = OverflowEdgeBottomDontConsume;
|
||||
} else {
|
||||
newValue |= OverflowEdgeBottomDontConsume;
|
||||
}
|
||||
case 'all-but-left':
|
||||
if (newValue === -1) {
|
||||
newValue = OverflowEdgeAllButLeft;
|
||||
} else {
|
||||
newValue |= OverflowEdgeAllButLeft;
|
||||
}
|
||||
case 'all-but-top':
|
||||
if (newValue === -1) {
|
||||
newValue = OverflowEdgeAllButTop;
|
||||
} else {
|
||||
newValue |= OverflowEdgeAllButTop;
|
||||
}
|
||||
case 'all-but-right':
|
||||
if (newValue === -1) {
|
||||
newValue = OverflowEdgeAllButRight;
|
||||
} else {
|
||||
newValue |= OverflowEdgeAllButRight;
|
||||
}
|
||||
case 'all-but-bottom':
|
||||
if (newValue === -1) {
|
||||
newValue = OverflowEdgeAllButBottom;
|
||||
} else {
|
||||
newValue |= OverflowEdgeAllButBottom;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (newValue === -1) {
|
||||
return null;
|
||||
}
|
||||
return newValue;
|
||||
}
|
||||
|
||||
export class ContainerView extends View {
|
||||
public iosOverflowSafeArea: boolean;
|
||||
}
|
||||
|
||||
12
packages/core/ui/core/view/index.d.ts
vendored
12
packages/core/ui/core/view/index.d.ts
vendored
@@ -92,6 +92,13 @@ export abstract class View extends ViewCommon {
|
||||
*/
|
||||
public static accessibilityFocusChangedEvent: string;
|
||||
|
||||
/**
|
||||
* String value used when hooking to androidOverflowInset event.
|
||||
*
|
||||
* @nsEvent {EventDataValue} androidOverflowInset
|
||||
*/
|
||||
public static androidOverflowInsetEvent: string;
|
||||
|
||||
/**
|
||||
* Gets the android-specific native instance that lies behind this proxy. Will be available if running on an Android platform.
|
||||
*/
|
||||
@@ -786,6 +793,11 @@ export abstract class View extends ViewCommon {
|
||||
*/
|
||||
on(event: 'shownModally', callback: (args: ShownModallyData) => void, thisArg?: any);
|
||||
|
||||
/**
|
||||
* Raised after the view is shown as a modal dialog.
|
||||
*/
|
||||
on(event: 'androidOverflowInset', callback: (args: ShownModallyData) => void, thisArg?: any);
|
||||
|
||||
/**
|
||||
* Returns the current modal view that this page is showing (is parent of), if any.
|
||||
*/
|
||||
|
||||
@@ -79,6 +79,7 @@ export abstract class ViewCommon extends ViewBase {
|
||||
public static accessibilityFocusEvent = accessibilityFocusEvent;
|
||||
public static accessibilityFocusChangedEvent = accessibilityFocusChangedEvent;
|
||||
public static accessibilityPerformEscapeEvent = accessibilityPerformEscapeEvent;
|
||||
public static androidOverflowInsetEvent = 'androidOverflowInset';
|
||||
|
||||
public accessibilityIdentifier: string;
|
||||
public accessibilityLabel: string;
|
||||
@@ -979,6 +980,7 @@ export abstract class ViewCommon extends ViewBase {
|
||||
public iosOverflowSafeArea: boolean;
|
||||
public iosOverflowSafeAreaEnabled: boolean;
|
||||
public iosIgnoreSafeArea: boolean;
|
||||
public androidOverflowEdge: string;
|
||||
|
||||
get isLayoutValid(): boolean {
|
||||
return this._isLayoutValid;
|
||||
|
||||
@@ -59,3 +59,24 @@ export interface Size {
|
||||
*/
|
||||
height: number;
|
||||
}
|
||||
|
||||
export interface Inset {
|
||||
top: number;
|
||||
right: number;
|
||||
bottom: number;
|
||||
left: number;
|
||||
topConsumed: boolean;
|
||||
rightConsumed: boolean;
|
||||
bottomConsumed: boolean;
|
||||
leftConsumed: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the data for the androidOverflowInset event.
|
||||
*/
|
||||
export interface AndroidOverflowInsetData extends EventData {
|
||||
/**
|
||||
* The inset values passed to the view to consume or update.
|
||||
*/
|
||||
inset?: Inset;
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ export const placeholderColorProperty = new CssProperty<Style, Color>({
|
||||
});
|
||||
placeholderColorProperty.register(Style);
|
||||
|
||||
const keyboardTypeConverter = makeParser<CoreTypes.KeyboardInputType>(makeValidator<CoreTypes.KeyboardInputType>(CoreTypes.KeyboardType.datetime, CoreTypes.KeyboardType.phone, CoreTypes.KeyboardType.number, CoreTypes.KeyboardType.url, CoreTypes.KeyboardType.email, CoreTypes.KeyboardType.integer), true);
|
||||
const keyboardTypeConverter = makeParser<CoreTypes.KeyboardInputType>(makeValidator<CoreTypes.KeyboardInputType>(CoreTypes.KeyboardType.datetime, CoreTypes.KeyboardType.phone, CoreTypes.KeyboardType.number, CoreTypes.KeyboardType.decimal, CoreTypes.KeyboardType.url, CoreTypes.KeyboardType.email, CoreTypes.KeyboardType.integer), true);
|
||||
|
||||
export const autofillTypeProperty = new Property<EditableTextBase, CoreTypes.AutofillType>({ name: 'autofillType' });
|
||||
autofillTypeProperty.register(EditableTextBase);
|
||||
|
||||
@@ -204,7 +204,7 @@ export abstract class EditableTextBase extends EditableTextBaseCommon {
|
||||
[keyboardTypeProperty.getDefault](): number {
|
||||
return this.nativeTextViewProtected.getInputType();
|
||||
}
|
||||
[keyboardTypeProperty.setNative](value: 'datetime' | 'phone' | 'number' | 'url' | 'email' | 'integer' | number) {
|
||||
[keyboardTypeProperty.setNative](value: 'datetime' | 'phone' | 'number' | 'decimal' | 'url' | 'email' | 'integer' | number) {
|
||||
let newInputType;
|
||||
|
||||
switch (value) {
|
||||
@@ -220,6 +220,10 @@ export abstract class EditableTextBase extends EditableTextBaseCommon {
|
||||
newInputType = android.text.InputType.TYPE_CLASS_NUMBER | android.text.InputType.TYPE_NUMBER_VARIATION_NORMAL | android.text.InputType.TYPE_NUMBER_FLAG_SIGNED | android.text.InputType.TYPE_NUMBER_FLAG_DECIMAL;
|
||||
break;
|
||||
|
||||
case 'decimal':
|
||||
newInputType = android.text.InputType.TYPE_CLASS_NUMBER | android.text.InputType.TYPE_NUMBER_FLAG_DECIMAL | android.text.InputType.TYPE_NUMBER_FLAG_SIGNED;
|
||||
break;
|
||||
|
||||
case 'url':
|
||||
newInputType = android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_VARIATION_URI;
|
||||
break;
|
||||
|
||||
@@ -34,7 +34,7 @@ export abstract class EditableTextBase extends EditableTextBaseCommon {
|
||||
return keyboardType.toString();
|
||||
}
|
||||
}
|
||||
[keyboardTypeProperty.setNative](value: 'datetime' | 'phone' | 'number' | 'url' | 'email' | 'integer' | string) {
|
||||
[keyboardTypeProperty.setNative](value: 'datetime' | 'phone' | 'number' | 'decimal' | 'url' | 'email' | 'integer' | string) {
|
||||
let newKeyboardType: UIKeyboardType;
|
||||
switch (value) {
|
||||
case 'datetime':
|
||||
@@ -49,6 +49,10 @@ export abstract class EditableTextBase extends EditableTextBaseCommon {
|
||||
newKeyboardType = UIKeyboardType.NumbersAndPunctuation;
|
||||
break;
|
||||
|
||||
case 'decimal':
|
||||
newKeyboardType = UIKeyboardType.DecimalPad;
|
||||
break;
|
||||
|
||||
case 'url':
|
||||
newKeyboardType = UIKeyboardType.URL;
|
||||
break;
|
||||
|
||||
@@ -2,7 +2,7 @@ import '../../globals';
|
||||
import { setActivityCallbacks } from '.';
|
||||
import { Application } from '../../application';
|
||||
import { isEmbedded } from '../embedding';
|
||||
|
||||
import { enableEdgeToEdge } from '../../utils/android';
|
||||
const EMPTY_FN = () => {};
|
||||
declare const com: any;
|
||||
|
||||
@@ -22,6 +22,7 @@ if (!isEmbedded()) {
|
||||
// Set isNativeScriptActivity in onCreate.
|
||||
// The JS constructor might not be called because the activity is created from Android.
|
||||
this.isNativeScriptActivity = true;
|
||||
enableEdgeToEdge(this);
|
||||
if (!this._callbacks) {
|
||||
setActivityCallbacks(this);
|
||||
}
|
||||
@@ -78,6 +79,7 @@ if (!isEmbedded()) {
|
||||
// Set isNativeScriptActivity in onCreate.
|
||||
// The JS constructor might not be called because the activity is created from Android.
|
||||
activity.isNativeScriptActivity = true;
|
||||
enableEdgeToEdge(this);
|
||||
if (!activity._callbacks) {
|
||||
setActivityCallbacks(activity);
|
||||
}
|
||||
|
||||
@@ -74,6 +74,8 @@ export class Frame extends FrameBase {
|
||||
private _containerViewId = -1;
|
||||
private _tearDownPending = false;
|
||||
private _attachedToWindow = false;
|
||||
_defaultOverflowEdge: number;
|
||||
_defaultOverflowEdgeValue: string;
|
||||
/**
|
||||
* This property indicates that the view is to be reused as a root view or has been previously disposed.
|
||||
*/
|
||||
@@ -84,6 +86,8 @@ export class Frame extends FrameBase {
|
||||
constructor() {
|
||||
super();
|
||||
this._android = new AndroidFrame(this);
|
||||
this._defaultOverflowEdge = 1 << 4;
|
||||
this._defaultOverflowEdgeValue = 'dont-apply';
|
||||
}
|
||||
|
||||
public static reloadPage(context?: ModuleContext): void {
|
||||
|
||||
@@ -17,7 +17,7 @@ export { ViewBase, eachDescendant, getAncestor, getViewById, booleanConverter, q
|
||||
export type { ShowModalOptions } from './core/view-base';
|
||||
export { View, CSSType, ContainerView, ViewHelper, AndroidHelper, IOSHelper, isUserInteractionEnabledProperty, PseudoClassHandler, CustomLayoutView } from './core/view';
|
||||
export type { Template, KeyedTemplate, AddArrayFromBuilder, AddChildFromBuilder, GlassEffectConfig, GlassEffectType, GlassEffectVariant } from './core/view';
|
||||
export type { ShownModallyData, Size } from './core/view/view-interfaces';
|
||||
export type { ShownModallyData, Size, AndroidOverflowInsetData } from './core/view/view-interfaces';
|
||||
export { Property, CoercibleProperty, InheritedProperty, CssProperty, InheritedCssProperty, ShorthandProperty, CssAnimationProperty, makeParser, makeValidator } from './core/properties';
|
||||
export { unsetValue } from './core/properties/property-shared';
|
||||
export { addWeakEventListener, removeWeakEventListener } from './core/weak-event-listener';
|
||||
|
||||
@@ -78,6 +78,9 @@ export class TextField extends TextFieldBase {
|
||||
case 'number':
|
||||
inputType = android.text.InputType.TYPE_CLASS_NUMBER | android.text.InputType.TYPE_NUMBER_VARIATION_NORMAL | android.text.InputType.TYPE_NUMBER_FLAG_SIGNED | android.text.InputType.TYPE_NUMBER_FLAG_DECIMAL;
|
||||
break;
|
||||
case 'decimal':
|
||||
inputType = android.text.InputType.TYPE_CLASS_NUMBER | android.text.InputType.TYPE_NUMBER_FLAG_DECIMAL | android.text.InputType.TYPE_NUMBER_FLAG_SIGNED;
|
||||
break;
|
||||
case 'url':
|
||||
inputType = android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_VARIATION_URI;
|
||||
break;
|
||||
|
||||
@@ -3,6 +3,7 @@ import { getNativeApp } from '../application/helpers-common';
|
||||
import { androidGetCurrentActivity } from '../application/helpers';
|
||||
import { Trace } from '../trace';
|
||||
import { topmost } from '../ui/frame/frame-stack';
|
||||
import { Color } from '../color';
|
||||
|
||||
export function dataDeserialize(nativeData?: any) {
|
||||
if (nativeData === null || typeof nativeData !== 'object') {
|
||||
@@ -294,3 +295,117 @@ export function isRealDevice(): boolean {
|
||||
|
||||
return fingerprint != null && (fingerprint.indexOf('vbox') > -1 || fingerprint.indexOf('generic') > -1);
|
||||
}
|
||||
|
||||
const DefaultLightScrim = new Color(0xe6, 0xff, 0xff, 0xff);
|
||||
const DefaultDarkScrim = new Color(0x80, 0x1b, 0x1b, 0x1b);
|
||||
|
||||
let statusBarDarkColor: Color | null = null;
|
||||
let statusBarLightColor: Color | null = null;
|
||||
|
||||
export function setStatusBarColor(lightColor: Color | null = null, darkColor: Color | null = null): void {
|
||||
statusBarLightColor = lightColor;
|
||||
statusBarDarkColor = darkColor;
|
||||
const activity = getCurrentActivity();
|
||||
if (activity) {
|
||||
enableEdgeToEdge(activity, {
|
||||
statusBarLightColor: lightColor,
|
||||
statusBarDarkColor: darkColor,
|
||||
navigationBarLightColor,
|
||||
navigationBarDarkColor,
|
||||
handleDarkMode: darkModeHandler,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let navigationBarDarkColor: Color | null = null;
|
||||
let navigationBarLightColor: Color | null = null;
|
||||
|
||||
export function setNavigationBarColor(lightColor: Color | null = null, darkColor: Color | null = null): void {
|
||||
navigationBarLightColor = lightColor;
|
||||
navigationBarDarkColor = darkColor;
|
||||
const activity = getCurrentActivity();
|
||||
if (activity) {
|
||||
enableEdgeToEdge(activity, {
|
||||
statusBarLightColor,
|
||||
statusBarDarkColor,
|
||||
navigationBarLightColor: navigationBarLightColor,
|
||||
navigationBarDarkColor: navigationBarDarkColor,
|
||||
handleDarkMode: darkModeHandler,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let darkModeHandler: ((bar: 'status' | 'navigation', resources: android.content.res.Resources) => boolean) | null = null;
|
||||
|
||||
export function setDarkModeHandler(handler: (bar: 'status' | 'navigation', resources: android.content.res.Resources) => boolean): void {
|
||||
darkModeHandler = handler;
|
||||
const activity = getCurrentActivity();
|
||||
if (activity) {
|
||||
enableEdgeToEdge(activity, {
|
||||
statusBarLightColor,
|
||||
statusBarDarkColor,
|
||||
navigationBarLightColor,
|
||||
navigationBarDarkColor,
|
||||
handleDarkMode: handler,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function enableEdgeToEdge(
|
||||
activity: androidx.appcompat.app.AppCompatActivity,
|
||||
options?: {
|
||||
statusBarLightColor?: Color;
|
||||
statusBarDarkColor?: Color;
|
||||
navigationBarLightColor?: Color;
|
||||
navigationBarDarkColor?: Color;
|
||||
handleDarkMode?: (bar: 'status' | 'navigation', resources: android.content.res.Resources) => boolean;
|
||||
},
|
||||
): void {
|
||||
let handleDarkMode: org.nativescript.widgets.Utils.HandleDarkMode;
|
||||
let statusBarLight: number = statusBarLightColor?.android ?? 0;
|
||||
let statusBarDark: number = statusBarDarkColor?.android ?? 0;
|
||||
let navigationBarLight: number = navigationBarLightColor?.android ?? DefaultLightScrim.android;
|
||||
let navigationBarDark: number = navigationBarDarkColor?.android ?? DefaultDarkScrim.android;
|
||||
if (darkModeHandler) {
|
||||
handleDarkMode = new org.nativescript.widgets.Utils.HandleDarkMode({
|
||||
onHandle(bar, resources) {
|
||||
if (bar === 0) {
|
||||
return darkModeHandler('status', resources);
|
||||
} else {
|
||||
return darkModeHandler('navigation', resources);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
if (options) {
|
||||
if (typeof options.handleDarkMode === 'function') {
|
||||
handleDarkMode = new org.nativescript.widgets.Utils.HandleDarkMode({
|
||||
onHandle(bar, resources) {
|
||||
if (bar === 0) {
|
||||
return options.handleDarkMode('status', resources);
|
||||
} else {
|
||||
return options.handleDarkMode('navigation', resources);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
if (options.statusBarLightColor instanceof Color) {
|
||||
statusBarLight = options.statusBarLightColor.android;
|
||||
}
|
||||
if (options.statusBarDarkColor instanceof Color) {
|
||||
statusBarDark = options.statusBarDarkColor.android;
|
||||
}
|
||||
if (options.navigationBarLightColor instanceof Color) {
|
||||
navigationBarLight = options.navigationBarLightColor.android;
|
||||
}
|
||||
if (options.navigationBarDarkColor instanceof Color) {
|
||||
navigationBarDark = options.navigationBarDarkColor.android;
|
||||
}
|
||||
}
|
||||
|
||||
if (handleDarkMode) {
|
||||
org.nativescript.widgets.Utils.enableEdgeToEdge(activity, java.lang.Integer.valueOf(statusBarLight), java.lang.Integer.valueOf(statusBarDark), java.lang.Integer.valueOf(navigationBarLight), java.lang.Integer.valueOf(navigationBarDark), handleDarkMode);
|
||||
} else {
|
||||
org.nativescript.widgets.Utils.enableEdgeToEdge(activity, java.lang.Integer.valueOf(statusBarLight), java.lang.Integer.valueOf(statusBarDark), java.lang.Integer.valueOf(navigationBarLight), java.lang.Integer.valueOf(navigationBarDark));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { platformCheck } from './platform-check';
|
||||
|
||||
// importing this helper as a separate file avoids "android" symbol clash with the global android object
|
||||
import { resources, collections, getWindow, getApplication, getCurrentActivity, getApplicationContext, getResources, getPackageName, getInputMethodManager, showSoftInput, dismissSoftInput } from './native-helper-for-android';
|
||||
import { resources, collections, getWindow, getApplication, getCurrentActivity, getApplicationContext, getResources, getPackageName, getInputMethodManager, showSoftInput, dismissSoftInput, setStatusBarColor, setNavigationBarColor, setDarkModeHandler } from './native-helper-for-android';
|
||||
export { dataSerialize, dataDeserialize } from './native-helper-for-android';
|
||||
|
||||
export { getWindow } from './native-helper-for-android';
|
||||
@@ -18,6 +18,9 @@ export const android = {
|
||||
getInputMethodManager,
|
||||
showSoftInput,
|
||||
dismissSoftInput,
|
||||
setStatusBarColor,
|
||||
setNavigationBarColor,
|
||||
setDarkModeHandler,
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
3
packages/core/utils/native-helper.d.ts
vendored
3
packages/core/utils/native-helper.d.ts
vendored
@@ -107,6 +107,9 @@ export const android: {
|
||||
* Hides the soft input method, usually a soft keyboard.
|
||||
*/
|
||||
dismissSoftInput: (nativeView?: android.view.View) => void;
|
||||
setStatusBarColor: (lightColor: Color | null = null, darkColor: Color | null = null) => void;
|
||||
setNavigationBarColor: (lightColor: Color | null = null, darkColor: Color | null = null) => void;
|
||||
setDarkModeHandler: (handler: (bar: 'status' | 'navigation', resources: android.content.res.Resources) => boolean) => void;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user