From 5fc7d1a4d4b784c755026cde6105b73cd6a65730 Mon Sep 17 00:00:00 2001 From: Martin Guillon Date: Wed, 17 Feb 2021 16:05:56 +0100 Subject: [PATCH 1/7] fix: refactor background handling. The idea is for views to handle sepecial case themselves. I realised that android.widget.Button was materialized even if you were not using it because there was a test for instanceof in the background handling. Now the special background handling is done in Button and ActionBar --- packages/core/ui/action-bar/index.android.ts | 26 +++++- packages/core/ui/button/index.android.ts | 21 +++++ packages/core/ui/core/view/index.android.ts | 66 +++++++++++--- packages/core/ui/core/view/index.d.ts | 6 ++ packages/core/ui/core/view/view-common.ts | 3 + .../core/ui/styling/background.android.ts | 87 +------------------ 6 files changed, 112 insertions(+), 97 deletions(-) diff --git a/packages/core/ui/action-bar/index.android.ts b/packages/core/ui/action-bar/index.android.ts index 3fe45f0030..e8faf9c0c6 100644 --- a/packages/core/ui/action-bar/index.android.ts +++ b/packages/core/ui/action-bar/index.android.ts @@ -6,6 +6,9 @@ import { layout, RESOURCE_PREFIX, isFontIconURI } from '../../utils'; import { colorProperty } from '../styling/style-properties'; import { ImageSource } from '../../image-source'; import * as application from '../../application'; +import type { Background } from 'ui/styling/background'; +import { Device } from '../../platform'; +import lazy from '../../utils/lazy'; export * from './action-bar-common'; @@ -13,6 +16,9 @@ const R_ID_HOME = 0x0102002c; const ACTION_ITEM_ID_OFFSET = 10000; const DEFAULT_ELEVATION = 4; +const sdkVersion = lazy(() => parseInt(Device.sdkVersion)); + + let AppCompatTextView; let actionItemIdGenerator = ACTION_ITEM_ID_OFFSET; function generateItemId(): number { @@ -58,7 +64,7 @@ function initializeMenuItemClickListener(): void { return; } - apiLevel = android.os.Build.VERSION.SDK_INT; + apiLevel = sdkVersion(); AppCompatTextView = androidx.appcompat.widget.AppCompatTextView; @@ -215,6 +221,24 @@ export class ActionBar extends ActionBarBase { this._updateNavigationButton(); } + public _applyBackground(background: Background, isBorderDrawable, onlyColor: boolean, backgroundDrawable: any) { + const nativeView = this.nativeViewProtected; + if (backgroundDrawable && onlyColor && sdkVersion() >= 21) { + if (isBorderDrawable && (nativeView)._cachedDrawable) { + backgroundDrawable = (nativeView)._cachedDrawable.newDrawable(nativeView.getResources()); + nativeView.setBackground(backgroundDrawable); + } + + const backgroundColor = ((backgroundDrawable).backgroundColor = background.color.android); + backgroundDrawable.mutate(); + backgroundDrawable.setColorFilter(backgroundColor, android.graphics.PorterDuff.Mode.SRC_IN); + backgroundDrawable.invalidateSelf(); // Make sure the drawable is invalidated. Android forgets to invalidate it in some cases: toolbar + (backgroundDrawable).backgroundColor = backgroundColor; + } else { + super._applyBackground(background, isBorderDrawable, onlyColor, backgroundDrawable); + } + } + public _onAndroidItemSelected(itemId: number): boolean { // Handle home button if (this.navigationButton && itemId === R_ID_HOME) { diff --git a/packages/core/ui/button/index.android.ts b/packages/core/ui/button/index.android.ts index 29cd44bba8..036737b3db 100644 --- a/packages/core/ui/button/index.android.ts +++ b/packages/core/ui/button/index.android.ts @@ -7,6 +7,7 @@ import { profile } from '../../profiling'; import { TouchGestureEventData, GestureTypes, TouchAction } from '../gestures'; import { Device } from '../../platform'; import lazy from '../../utils/lazy'; +import type { Background } from 'ui/styling/background'; export * from './button-common'; @@ -58,6 +59,26 @@ export class Button extends ButtonBase { private _stateListAnimator: any; private _highlightedHandler: (args: TouchGestureEventData) => void; + + public _applyBackground(background: Background, isBorderDrawable, onlyColor: boolean, backgroundDrawable: any) { + const nativeView = this.nativeViewProtected; + console.log('_applyBackground', nativeView, backgroundDrawable, onlyColor, isBorderDrawable); + if (backgroundDrawable && onlyColor) { + if (isBorderDrawable && (nativeView)._cachedDrawable) { + backgroundDrawable = (nativeView)._cachedDrawable.newDrawable(nativeView.getResources()); + nativeView.setBackground(backgroundDrawable); + } + + const backgroundColor = ((backgroundDrawable).backgroundColor = background.color.android); + backgroundDrawable.mutate(); + backgroundDrawable.setColorFilter(backgroundColor, android.graphics.PorterDuff.Mode.SRC_IN); + backgroundDrawable.invalidateSelf(); // Make sure the drawable is invalidated. Android forgets to invalidate it in some cases: toolbar + (backgroundDrawable).backgroundColor = backgroundColor; + } else { + super._applyBackground(background, isBorderDrawable, onlyColor, backgroundDrawable); + } + } + @profile public createNativeView() { if (!AndroidButton) { diff --git a/packages/core/ui/core/view/index.android.ts b/packages/core/ui/core/view/index.android.ts index fe7b0b7d41..e9052d1d91 100644 --- a/packages/core/ui/core/view/index.android.ts +++ b/packages/core/ui/core/view/index.android.ts @@ -42,6 +42,7 @@ import { } from '../../styling/style-properties'; import { Background, ad as androidBackground } from '../../styling/background'; +import { refreshBorderDrawable } from '../../styling/background.android'; import { profile } from '../../../profiling'; import { topmost } from '../../frame/frame-stack'; import { Screen } from '../../../platform'; @@ -989,21 +990,19 @@ export class View extends ViewCommon { [backgroundInternalProperty.getDefault](): android.graphics.drawable.Drawable { const nativeView = this.nativeViewProtected; - const drawable = nativeView.getBackground(); + let drawable = nativeView.getBackground(); if (drawable) { const constantState = drawable.getConstantState(); if (constantState) { try { - return constantState.newDrawable(nativeView.getResources()); - } catch (e) { - return drawable; - } - } else { - return drawable; + drawable = constantState.newDrawable(nativeView.getResources()); + // eslint-disable-next-line no-empty + } catch {} } } + (nativeView)._cachedDrawable = drawable; - return null; + return drawable; } [backgroundInternalProperty.setNative](value: android.graphics.drawable.Drawable | Background) { this._redrawNativeBackground(value); @@ -1025,9 +1024,56 @@ export class View extends ViewCommon { } } + public _applyBackground(background: Background, isBorderDrawable: boolean, onlyColor: boolean, backgroundDrawable: any) { + const nativeView = this.nativeViewProtected; + if (!isBorderDrawable && onlyColor) { + if (backgroundDrawable && backgroundDrawable.setColor) { + backgroundDrawable.setColor(background.color.android); + backgroundDrawable.invalidateSelf(); + } else { + nativeView.setBackgroundColor(background.color.android); + } + } else if (!background.isEmpty()) { + if (!isBorderDrawable) { + backgroundDrawable = new org.nativescript.widgets.BorderDrawable(layout.getDisplayDensity(), this.toString()); + refreshBorderDrawable(this, backgroundDrawable); + nativeView.setBackground(backgroundDrawable); + } else { + refreshBorderDrawable(this, backgroundDrawable); + } + } else { + //empty background let s reset + const cachedDrawable = (nativeView)._cachedDrawable; + nativeView.setBackground(cachedDrawable); + } + } + protected onBackgroundOrBorderPropertyChanged() { + const nativeView = this.nativeViewProtected; + if (!nativeView) { + return; + } + + const background = this.style.backgroundInternal; + const drawable = nativeView.getBackground(); + const isBorderDrawable = drawable instanceof org.nativescript.widgets.BorderDrawable; + const onlyColor = !background.hasBorderWidth() && !background.hasBorderRadius() && !background.clipPath && !background.image && !!background.color; + this._applyBackground(background, isBorderDrawable, onlyColor, drawable); + + // TODO: Can we move BorderWidths as separate native setter? + // This way we could skip setPadding if borderWidth is not changed. + const leftPadding = Math.ceil(this.effectiveBorderLeftWidth + this.effectivePaddingLeft); + const topPadding = Math.ceil(this.effectiveBorderTopWidth + this.effectivePaddingTop); + const rightPadding = Math.ceil(this.effectiveBorderRightWidth + this.effectivePaddingRight); + const bottomPadding = Math.ceil(this.effectiveBorderBottomWidth + this.effectivePaddingBottom); + if (this._isPaddingRelative) { + nativeView.setPaddingRelative(leftPadding, topPadding, rightPadding, bottomPadding); + } else { + nativeView.setPadding(leftPadding, topPadding, rightPadding, bottomPadding); + } + } _redrawNativeBackground(value: android.graphics.drawable.Drawable | Background): void { if (value instanceof Background) { - androidBackground.onBackgroundOrBorderPropertyChanged(this); + this.onBackgroundOrBorderPropertyChanged(); } else { const nativeView = this.nativeViewProtected; nativeView.setBackground(value); @@ -1043,8 +1089,6 @@ export class View extends ViewCommon { } else { nativeView.setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom); } - - (nativeView).background = undefined; } } } diff --git a/packages/core/ui/core/view/index.d.ts b/packages/core/ui/core/view/index.d.ts index 4ddd4c1ca4..bd23413477 100644 --- a/packages/core/ui/core/view/index.d.ts +++ b/packages/core/ui/core/view/index.d.ts @@ -742,6 +742,12 @@ export abstract class View extends ViewBase { * @private */ _redrawNativeBackground(value: any): void; + /** + * @private + * method called on Android to apply the background. This allows custom handling + */ + _applyBackground(background: Background, isBorderDrawable: boolean, onlyColor: boolean, backgroundDrawable: any); + /** * @private */ diff --git a/packages/core/ui/core/view/view-common.ts b/packages/core/ui/core/view/view-common.ts index f5178100b2..f5960cf9d7 100644 --- a/packages/core/ui/core/view/view-common.ts +++ b/packages/core/ui/core/view/view-common.ts @@ -985,6 +985,9 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition { public _redrawNativeBackground(value: any): void { // } + public _applyBackground(background, isBorderDrawable: boolean, onlyColor: boolean, backgroundDrawable: any) { + // + } _onAttachedToWindow(): void { // diff --git a/packages/core/ui/styling/background.android.ts b/packages/core/ui/styling/background.android.ts index 65459fdb9d..647ac96719 100644 --- a/packages/core/ui/styling/background.android.ts +++ b/packages/core/ui/styling/background.android.ts @@ -15,90 +15,7 @@ interface AndroidView { // TODO: Change this implementation to use // We are using "ad" here to avoid namespace collision with the global android object export namespace ad { - let SDK: number; - function getSDK() { - if (!SDK) { - SDK = android.os.Build.VERSION.SDK_INT; - } - - return SDK; - } - - function isSetColorFilterOnlyWidget(nativeView: android.view.View): boolean { - return ( - nativeView instanceof android.widget.Button || (nativeView instanceof androidx.appcompat.widget.Toolbar && getSDK() >= 21) // There is an issue with the DrawableContainer which was fixed for API version 21 and above: https://code.google.com/p/android/issues/detail?id=60183 - ); - } - - export function onBackgroundOrBorderPropertyChanged(view: View) { - const nativeView = view.nativeViewProtected; - if (!nativeView) { - return; - } - - const background = view.style.backgroundInternal; - let drawable = nativeView.getBackground(); - const androidView = (view) as AndroidView; - // use undefined as not set. getBackground will never return undefined only Drawable or null; - if (androidView._cachedDrawable === undefined && drawable) { - const constantState = drawable.getConstantState(); - androidView._cachedDrawable = constantState || drawable; - } - const isBorderDrawable = drawable instanceof org.nativescript.widgets.BorderDrawable; - const onlyColor = !background.hasBorderWidth() && !background.hasBorderRadius() && !background.clipPath && !background.image && !!background.color; - if (!isBorderDrawable && drawable instanceof android.graphics.drawable.ColorDrawable && onlyColor) { - drawable.setColor(background.color.android); - drawable.invalidateSelf(); - } else if (isSetColorFilterOnlyWidget(nativeView) && drawable && onlyColor) { - if (isBorderDrawable && androidView._cachedDrawable) { - if (!(androidView._cachedDrawable instanceof android.graphics.drawable.Drawable.ConstantState)) { - return; - } - - drawable = androidView._cachedDrawable.newDrawable(nativeView.getResources()); - nativeView.setBackground(drawable); - } - - const backgroundColor = ((drawable).backgroundColor = background.color.android); - drawable.mutate(); - drawable.setColorFilter(backgroundColor, android.graphics.PorterDuff.Mode.SRC_IN); - drawable.invalidateSelf(); // Make sure the drawable is invalidated. Android forgets to invalidate it in some cases: toolbar - (drawable).backgroundColor = backgroundColor; - } else if (!isBorderDrawable && onlyColor) { - // this is the fastest way to change only background color - nativeView.setBackgroundColor(background.color.android); - } else if (!background.isEmpty()) { - let backgroundDrawable = drawable as org.nativescript.widgets.BorderDrawable; - if (!isBorderDrawable) { - backgroundDrawable = new org.nativescript.widgets.BorderDrawable(layout.getDisplayDensity(), view.toString()); - refreshBorderDrawable(view, backgroundDrawable); - nativeView.setBackground(backgroundDrawable); - } else { - refreshBorderDrawable(view, backgroundDrawable); - } - } else { - const cachedDrawable = androidView._cachedDrawable; - let defaultDrawable: android.graphics.drawable.Drawable = null; - if (cachedDrawable) { - if (cachedDrawable instanceof android.graphics.drawable.Drawable.ConstantState) { - defaultDrawable = cachedDrawable.newDrawable(nativeView.getResources()); - } else if (cachedDrawable instanceof android.graphics.drawable.Drawable) { - defaultDrawable = cachedDrawable; - } - } - - nativeView.setBackground(defaultDrawable); - } - - // TODO: Can we move BorderWidths as separate native setter? - // This way we could skip setPadding if borderWidth is not changed. - const leftPadding = Math.ceil(view.effectiveBorderLeftWidth + view.effectivePaddingLeft); - const topPadding = Math.ceil(view.effectiveBorderTopWidth + view.effectivePaddingTop); - const rightPadding = Math.ceil(view.effectiveBorderRightWidth + view.effectivePaddingRight); - const bottomPadding = Math.ceil(view.effectiveBorderBottomWidth + view.effectivePaddingBottom); - - nativeView.setPadding(leftPadding, topPadding, rightPadding, bottomPadding); - } + } function fromBase64(source: string): android.graphics.Bitmap { @@ -129,7 +46,7 @@ function fromGradient(gradient: LinearGradient): org.nativescript.widgets.Linear } const pattern = /url\(('|")(.*?)\1\)/; -function refreshBorderDrawable(this: void, view: View, borderDrawable: org.nativescript.widgets.BorderDrawable) { +export function refreshBorderDrawable(this: void, view: View, borderDrawable: org.nativescript.widgets.BorderDrawable) { const nativeView = view.nativeViewProtected; const context = nativeView.getContext(); From 016313bef1e139228a8d336c59f7d1d868b302de Mon Sep 17 00:00:00 2001 From: Martin Guillon Date: Wed, 17 Feb 2021 17:07:38 +0100 Subject: [PATCH 2/7] fix: automated tests are now passing --- packages/core/ui/action-bar/index.android.ts | 11 ++++++++++- packages/core/ui/button/index.android.ts | 11 +++++++++-- packages/core/ui/core/view/index.android.ts | 2 +- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/packages/core/ui/action-bar/index.android.ts b/packages/core/ui/action-bar/index.android.ts index e8faf9c0c6..0fdbdf3c2d 100644 --- a/packages/core/ui/action-bar/index.android.ts +++ b/packages/core/ui/action-bar/index.android.ts @@ -225,7 +225,16 @@ export class ActionBar extends ActionBarBase { const nativeView = this.nativeViewProtected; if (backgroundDrawable && onlyColor && sdkVersion() >= 21) { if (isBorderDrawable && (nativeView)._cachedDrawable) { - backgroundDrawable = (nativeView)._cachedDrawable.newDrawable(nativeView.getResources()); + backgroundDrawable = (nativeView)._cachedDrawable; + // we need to duplicate the drawable or we lose the "default" cached drawable + const constantState = backgroundDrawable.getConstantState(); + if (constantState) { + try { + backgroundDrawable = constantState.newDrawable(nativeView.getResources()); + // eslint-disable-next-line no-empty + } catch {} + } + nativeView.setBackground(backgroundDrawable); nativeView.setBackground(backgroundDrawable); } diff --git a/packages/core/ui/button/index.android.ts b/packages/core/ui/button/index.android.ts index 036737b3db..9b9092a093 100644 --- a/packages/core/ui/button/index.android.ts +++ b/packages/core/ui/button/index.android.ts @@ -62,10 +62,17 @@ export class Button extends ButtonBase { public _applyBackground(background: Background, isBorderDrawable, onlyColor: boolean, backgroundDrawable: any) { const nativeView = this.nativeViewProtected; - console.log('_applyBackground', nativeView, backgroundDrawable, onlyColor, isBorderDrawable); if (backgroundDrawable && onlyColor) { if (isBorderDrawable && (nativeView)._cachedDrawable) { - backgroundDrawable = (nativeView)._cachedDrawable.newDrawable(nativeView.getResources()); + backgroundDrawable = (nativeView)._cachedDrawable; + // we need to duplicate the drawable or we lose the "default" cached drawable + const constantState = backgroundDrawable.getConstantState(); + if (constantState) { + try { + backgroundDrawable = constantState.newDrawable(nativeView.getResources()); + // eslint-disable-next-line no-empty + } catch {} + } nativeView.setBackground(backgroundDrawable); } diff --git a/packages/core/ui/core/view/index.android.ts b/packages/core/ui/core/view/index.android.ts index e9052d1d91..84883dc4d7 100644 --- a/packages/core/ui/core/view/index.android.ts +++ b/packages/core/ui/core/view/index.android.ts @@ -996,7 +996,7 @@ export class View extends ViewCommon { if (constantState) { try { drawable = constantState.newDrawable(nativeView.getResources()); - // eslint-disable-next-line no-empty + // eslint-disable-next-line no-empty } catch {} } } From f7e9c6b7577cc2d9707ed5e218771273215827c3 Mon Sep 17 00:00:00 2001 From: Martin Guillon Date: Wed, 17 Feb 2021 17:09:05 +0100 Subject: [PATCH 3/7] fix: removed duplicated line --- packages/core/ui/action-bar/index.android.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/core/ui/action-bar/index.android.ts b/packages/core/ui/action-bar/index.android.ts index 0fdbdf3c2d..96c0e7df4c 100644 --- a/packages/core/ui/action-bar/index.android.ts +++ b/packages/core/ui/action-bar/index.android.ts @@ -235,7 +235,6 @@ export class ActionBar extends ActionBarBase { } catch {} } nativeView.setBackground(backgroundDrawable); - nativeView.setBackground(backgroundDrawable); } const backgroundColor = ((backgroundDrawable).backgroundColor = background.color.android); From 5baf6c74b3b0bdc6e8c44c10c128cc64d42b733c Mon Sep 17 00:00:00 2001 From: Martin Guillon Date: Fri, 9 Apr 2021 09:03:06 +0200 Subject: [PATCH 4/7] fix: port boxshadow to background improvements --- packages/core/ui/core/view/index.android.ts | 51 ++++++- .../core/ui/styling/background.android.ts | 139 +----------------- 2 files changed, 49 insertions(+), 141 deletions(-) diff --git a/packages/core/ui/core/view/index.android.ts b/packages/core/ui/core/view/index.android.ts index 4022f52adb..c011005a4f 100644 --- a/packages/core/ui/core/view/index.android.ts +++ b/packages/core/ui/core/view/index.android.ts @@ -4,7 +4,7 @@ import type { GestureTypes, GestureEventData } from '../../gestures'; // Types. import { ViewCommon, isEnabledProperty, originXProperty, originYProperty, isUserInteractionEnabledProperty } from './view-common'; -import { paddingLeftProperty, paddingTopProperty, paddingRightProperty, paddingBottomProperty } from '../../styling/style-properties'; +import { paddingLeftProperty, paddingTopProperty, paddingRightProperty, paddingBottomProperty, Length } from '../../styling/style-properties'; import { layout } from '../../../utils'; import { Trace } from '../../../trace'; import { ShowModalOptions, hiddenProperty } from '../view-base'; @@ -14,7 +14,7 @@ import { perspectiveProperty, visibilityProperty, opacityProperty, horizontalAli import { CoreTypes } from '../../../core-types'; import { Background, ad as androidBackground } from '../../styling/background'; -import { refreshBorderDrawable } from '../../styling/background.android'; +import { BackgroundClearFlags, refreshBorderDrawable } from '../../styling/background.android'; import { profile } from '../../../profiling'; import { topmost } from '../../frame/frame-stack'; import { Screen } from '../../../platform'; @@ -24,6 +24,7 @@ import lazy from '../../../utils/lazy'; import { accessibilityEnabledProperty, accessibilityHiddenProperty, accessibilityHintProperty, accessibilityIdentifierProperty, accessibilityLabelProperty, accessibilityLanguageProperty, accessibilityLiveRegionProperty, accessibilityMediaSessionProperty, accessibilityRoleProperty, accessibilityStateProperty, accessibilityValueProperty } from '../../../accessibility/accessibility-properties'; import { AccessibilityLiveRegion, AccessibilityRole, AndroidAccessibilityEvent, setupAccessibleView, isAccessibilityServiceEnabled, sendAccessibilityEvent, updateAccessibilityProperties, updateContentDescription, AccessibilityState } from '../../../accessibility'; import * as Utils from '../../../utils'; +import { CSSShadow } from 'ui/styling/css-shadow'; export * from './view-common'; // helpers (these are okay re-exported here) @@ -46,6 +47,10 @@ const modalMap = new Map(); let TouchListener: TouchListener; let DialogFragment: DialogFragment; +interface AndroidView { + _cachedDrawable: android.graphics.drawable.Drawable.ConstantState | android.graphics.drawable.Drawable; +} + interface DialogOptions { owner: View; fullscreen: boolean; @@ -1101,6 +1106,19 @@ export class View extends ViewCommon { nativeView.setBackground(cachedDrawable); } } + + protected _drawBoxShadow(boxShadow: CSSShadow) { + const nativeView = this.nativeViewProtected; + const config = { + shadowColor: boxShadow.color.android, + cornerRadius: Length.toDevicePixels(this.borderRadius as CoreTypes.LengthType, 0.0), + spreadRadius: Length.toDevicePixels(boxShadow.spreadRadius, 0.0), + blurRadius: Length.toDevicePixels(boxShadow.blurRadius, 0.0), + offsetX: Length.toDevicePixels(boxShadow.offsetX, 0.0), + offsetY: Length.toDevicePixels(boxShadow.offsetY, 0.0), + }; + org.nativescript.widgets.Utils.drawBoxShadow(nativeView, JSON.stringify(config)); + } protected onBackgroundOrBorderPropertyChanged() { const nativeView = this.nativeViewProtected; if (!nativeView) { @@ -1108,11 +1126,36 @@ export class View extends ViewCommon { } const background = this.style.backgroundInternal; + + if (background.clearFlags & BackgroundClearFlags.CLEAR_BOX_SHADOW || background.clearFlags & BackgroundClearFlags.CLEAR_BACKGROUND_COLOR) { + // clear background if we're clearing the box shadow + // or the background has been removed + nativeView.setBackground(null); + } + const drawable = nativeView.getBackground(); + const androidView = (this) as AndroidView; + // use undefined as not set. getBackground will never return undefined only Drawable or null; + if (androidView._cachedDrawable === undefined && drawable) { + const constantState = drawable.getConstantState(); + androidView._cachedDrawable = constantState || drawable; + } const isBorderDrawable = drawable instanceof org.nativescript.widgets.BorderDrawable; - const onlyColor = !background.hasBorderWidth() && !background.hasBorderRadius() && !background.clipPath && !background.image && !!background.color; + + // prettier-ignore + const onlyColor = !background.hasBorderWidth() + && !background.hasBorderRadius() + && !background.hasBoxShadow() + && !background.clipPath + && !background.image + && !!background.color; + this._applyBackground(background, isBorderDrawable, onlyColor, drawable); + if (background.hasBoxShadow()) { + this._drawBoxShadow(background.getBoxShadow()); + } + // TODO: Can we move BorderWidths as separate native setter? // This way we could skip setPadding if borderWidth is not changed. const leftPadding = Math.ceil(this.effectiveBorderLeftWidth + this.effectivePaddingLeft); @@ -1124,6 +1167,8 @@ export class View extends ViewCommon { } else { nativeView.setPadding(leftPadding, topPadding, rightPadding, bottomPadding); } + // reset clear flags + background.clearFlags = BackgroundClearFlags.NONE; } _redrawNativeBackground(value: android.graphics.drawable.Drawable | Background): void { if (value instanceof Background) { diff --git a/packages/core/ui/styling/background.android.ts b/packages/core/ui/styling/background.android.ts index bee7ded345..bd6f9d106d 100644 --- a/packages/core/ui/styling/background.android.ts +++ b/packages/core/ui/styling/background.android.ts @@ -1,139 +1,14 @@ import { View } from '../core/view'; import { LinearGradient } from './linear-gradient'; -import { CoreTypes } from '../../core-types'; -import { isDataURI, isFileOrResourcePath, layout, RESOURCE_PREFIX, FILE_PREFIX } from '../../utils'; +import { isDataURI, isFileOrResourcePath, RESOURCE_PREFIX, FILE_PREFIX } from '../../utils'; import { parse } from '../../css-value'; import { path, knownFolders } from '../../file-system'; import * as application from '../../application'; -import { profile } from '../../profiling'; -import { CSSShadow } from './css-shadow'; -import { Length } from './style-properties'; -import { BackgroundClearFlags } from './background-common'; export * from './background-common'; -interface AndroidView { - _cachedDrawable: android.graphics.drawable.Drawable.ConstantState | android.graphics.drawable.Drawable; -} - // TODO: Change this implementation to use // We are using "ad" here to avoid namespace collision with the global android object export namespace ad { - let SDK: number; - function getSDK() { - if (!SDK) { - SDK = android.os.Build.VERSION.SDK_INT; - } - - return SDK; - } - - function isSetColorFilterOnlyWidget(nativeView: android.view.View): boolean { - // prettier-ignore - return ( - nativeView instanceof android.widget.Button - || (nativeView instanceof androidx.appcompat.widget.Toolbar && getSDK() >= 21) - // There is an issue with the DrawableContainer which was fixed - // for API version 21 and above: https://code.google.com/p/android/issues/detail?id=60183 - ); - } - - export function onBackgroundOrBorderPropertyChanged(view: View) { - const nativeView = view.nativeViewProtected; - if (!nativeView) { - return; - } - - const background = view.style.backgroundInternal; - - if (background.clearFlags & BackgroundClearFlags.CLEAR_BOX_SHADOW || background.clearFlags & BackgroundClearFlags.CLEAR_BACKGROUND_COLOR) { - // clear background if we're clearing the box shadow - // or the background has been removed - nativeView.setBackground(null); - } - - let drawable = nativeView.getBackground(); - const androidView = (view) as AndroidView; - // use undefined as not set. getBackground will never return undefined only Drawable or null; - if (androidView._cachedDrawable === undefined && drawable) { - const constantState = drawable.getConstantState(); - androidView._cachedDrawable = constantState || drawable; - } - const isBorderDrawable = drawable instanceof org.nativescript.widgets.BorderDrawable; - - // prettier-ignore - const onlyColor = !background.hasBorderWidth() - && !background.hasBorderRadius() - && !background.hasBoxShadow() - && !background.clipPath - && !background.image - && !!background.color; - - if (!isBorderDrawable && drawable instanceof android.graphics.drawable.ColorDrawable && onlyColor) { - drawable.setColor(background.color.android); - drawable.invalidateSelf(); - } else if (isSetColorFilterOnlyWidget(nativeView) && drawable && onlyColor) { - if (isBorderDrawable && androidView._cachedDrawable) { - if (!(androidView._cachedDrawable instanceof android.graphics.drawable.Drawable.ConstantState)) { - return; - } - - drawable = androidView._cachedDrawable.newDrawable(nativeView.getResources()); - nativeView.setBackground(drawable); - } - - const backgroundColor = ((drawable).backgroundColor = background.color.android); - drawable.mutate(); - drawable.setColorFilter(backgroundColor, android.graphics.PorterDuff.Mode.SRC_IN); - drawable.invalidateSelf(); // Make sure the drawable is invalidated. Android forgets to invalidate it in some cases: toolbar - (drawable).backgroundColor = backgroundColor; - } else if (!isBorderDrawable && onlyColor) { - // this is the fastest way to change only background color - nativeView.setBackgroundColor(background.color.android); - } else if (!background.isEmpty()) { - let backgroundDrawable = drawable; - - if (drawable instanceof org.nativescript.widgets.BoxShadowDrawable) { - // if we have BoxShadow's we have to get the underlying drawable - backgroundDrawable = drawable.getWrappedDrawable(); - } - - if (backgroundDrawable instanceof org.nativescript.widgets.BorderDrawable) { - refreshBorderDrawable(view, backgroundDrawable); - } else { - backgroundDrawable = new org.nativescript.widgets.BorderDrawable(layout.getDisplayDensity(), view.toString()); - refreshBorderDrawable(view, backgroundDrawable); - nativeView.setBackground(backgroundDrawable); - } - } else { - const cachedDrawable = androidView._cachedDrawable; - let defaultDrawable: android.graphics.drawable.Drawable = null; - if (cachedDrawable) { - if (cachedDrawable instanceof android.graphics.drawable.Drawable.ConstantState) { - defaultDrawable = cachedDrawable.newDrawable(nativeView.getResources()); - } else if (cachedDrawable instanceof android.graphics.drawable.Drawable) { - defaultDrawable = cachedDrawable; - } - } - - nativeView.setBackground(defaultDrawable); - } - - if (background.hasBoxShadow()) { - drawBoxShadow(nativeView, view, background.getBoxShadow()); - } - - // TODO: Can we move BorderWidths as separate native setter? - // This way we could skip setPadding if borderWidth is not changed. - const leftPadding = Math.ceil(view.effectiveBorderLeftWidth + view.effectivePaddingLeft); - const topPadding = Math.ceil(view.effectiveBorderTopWidth + view.effectivePaddingTop); - const rightPadding = Math.ceil(view.effectiveBorderRightWidth + view.effectivePaddingRight); - const bottomPadding = Math.ceil(view.effectiveBorderBottomWidth + view.effectivePaddingBottom); - - nativeView.setPadding(leftPadding, topPadding, rightPadding, bottomPadding); - - // reset clear flags - background.clearFlags = BackgroundClearFlags.NONE; - } } function fromBase64(source: string): android.graphics.Bitmap { @@ -253,18 +128,6 @@ function createNativeCSSValueArray(css: string): androidNative.Array Date: Fri, 9 Apr 2021 10:37:28 +0200 Subject: [PATCH 5/7] Update packages/core/ui/core/view/index.android.ts Co-authored-by: Igor Randjelovic --- packages/core/ui/core/view/index.android.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/ui/core/view/index.android.ts b/packages/core/ui/core/view/index.android.ts index c011005a4f..8e56435e2d 100644 --- a/packages/core/ui/core/view/index.android.ts +++ b/packages/core/ui/core/view/index.android.ts @@ -24,7 +24,7 @@ import lazy from '../../../utils/lazy'; import { accessibilityEnabledProperty, accessibilityHiddenProperty, accessibilityHintProperty, accessibilityIdentifierProperty, accessibilityLabelProperty, accessibilityLanguageProperty, accessibilityLiveRegionProperty, accessibilityMediaSessionProperty, accessibilityRoleProperty, accessibilityStateProperty, accessibilityValueProperty } from '../../../accessibility/accessibility-properties'; import { AccessibilityLiveRegion, AccessibilityRole, AndroidAccessibilityEvent, setupAccessibleView, isAccessibilityServiceEnabled, sendAccessibilityEvent, updateAccessibilityProperties, updateContentDescription, AccessibilityState } from '../../../accessibility'; import * as Utils from '../../../utils'; -import { CSSShadow } from 'ui/styling/css-shadow'; +import { CSSShadow } from '../../styling/css-shadow'; export * from './view-common'; // helpers (these are okay re-exported here) From ffe150c1933dc3ff3becfe277b14d0104a2971aa Mon Sep 17 00:00:00 2001 From: Martin Guillon Date: Fri, 9 Apr 2021 10:38:09 +0200 Subject: [PATCH 6/7] Update packages/core/ui/action-bar/index.android.ts Co-authored-by: Igor Randjelovic --- packages/core/ui/action-bar/index.android.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/ui/action-bar/index.android.ts b/packages/core/ui/action-bar/index.android.ts index 0062ff9694..0a1bcdd15f 100644 --- a/packages/core/ui/action-bar/index.android.ts +++ b/packages/core/ui/action-bar/index.android.ts @@ -7,7 +7,7 @@ import { colorProperty } from '../styling/style-properties'; import { ImageSource } from '../../image-source'; import * as application from '../../application'; import { isAccessibilityServiceEnabled, updateContentDescription } from '../../accessibility'; -import type { Background } from 'ui/styling/background'; +import type { Background } from '../styling/background'; import { Device } from '../../platform'; import lazy from '../../utils/lazy'; From 0e416d87b383ea03ba31cc1732225a8ddab721f0 Mon Sep 17 00:00:00 2001 From: Martin Guillon Date: Fri, 9 Apr 2021 10:39:54 +0200 Subject: [PATCH 7/7] chore: remove empty namespace --- packages/core/ui/styling/background.android.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/packages/core/ui/styling/background.android.ts b/packages/core/ui/styling/background.android.ts index bd6f9d106d..a00d0f8ba2 100644 --- a/packages/core/ui/styling/background.android.ts +++ b/packages/core/ui/styling/background.android.ts @@ -6,11 +6,6 @@ import { path, knownFolders } from '../../file-system'; import * as application from '../../application'; export * from './background-common'; -// TODO: Change this implementation to use -// We are using "ad" here to avoid namespace collision with the global android object -export namespace ad { -} - function fromBase64(source: string): android.graphics.Bitmap { const bytes = android.util.Base64.decode(source, android.util.Base64.DEFAULT);