From b6d5510627bf0d7df71900dc03d7552c6f5ed72c Mon Sep 17 00:00:00 2001 From: Hristo Hristov Date: Thu, 24 Aug 2017 11:06:33 +0300 Subject: [PATCH] Fix crash where some android Drawables doesn't implement getConstantState... (#4742) Changed all places where getConstantState was used. --- tns-core-modules/ui/core/view/view.android.ts | 11 +++++- .../ui/segmented-bar/segmented-bar.android.ts | 34 +++++++++++++------ .../ui/styling/background.android.ts | 22 ++++++++---- .../ui/tab-view/tab-view.android.ts | 19 ++++++++--- 4 files changed, 64 insertions(+), 22 deletions(-) diff --git a/tns-core-modules/ui/core/view/view.android.ts b/tns-core-modules/ui/core/view/view.android.ts index 802289e08..7c852f8b3 100644 --- a/tns-core-modules/ui/core/view/view.android.ts +++ b/tns-core-modules/ui/core/view/view.android.ts @@ -459,7 +459,16 @@ export class View extends ViewCommon { [backgroundInternalProperty.getDefault](): android.graphics.drawable.Drawable { const nativeView = this.nativeViewProtected; const drawable = nativeView.getBackground(); - return drawable ? drawable.getConstantState().newDrawable(nativeView.getResources()) : null; + if (drawable) { + const constantState = drawable.getConstantState(); + if (constantState) { + return constantState.newDrawable(nativeView.getResources()); + } else { + return drawable; + } + } + + return null; } [backgroundInternalProperty.setNative](value: android.graphics.drawable.Drawable | Background) { this._redrawNativeBackground(value); diff --git a/tns-core-modules/ui/segmented-bar/segmented-bar.android.ts b/tns-core-modules/ui/segmented-bar/segmented-bar.android.ts index 5dd3a62fd..d326f355a 100644 --- a/tns-core-modules/ui/segmented-bar/segmented-bar.android.ts +++ b/tns-core-modules/ui/segmented-bar/segmented-bar.android.ts @@ -12,15 +12,15 @@ const R_ATTR_STATE_SELECTED = 0x010100a1; const TITLE_TEXT_VIEW_ID = 16908310; // http://developer.android.com/reference/android/R.id.html#title interface TabChangeListener { - new (owner: SegmentedBar): android.widget.TabHost.OnTabChangeListener; + new(owner: SegmentedBar): android.widget.TabHost.OnTabChangeListener; } interface TabContentFactory { - new (owner: SegmentedBar): android.widget.TabHost.TabContentFactory; + new(owner: SegmentedBar): android.widget.TabHost.TabContentFactory; } interface TabHost { - new (context: android.content.Context, attrs: android.util.AttributeSet): android.widget.TabHost; + new(context: android.content.Context, attrs: android.util.AttributeSet): android.widget.TabHost; } let apiLevel: number; @@ -144,17 +144,18 @@ export class SegmentedBarItem extends SegmentedBarItemBase { this.nativeViewProtected.setTypeface(value instanceof Font ? value.getAndroidTypeface() : value); } - [selectedBackgroundColorProperty.getDefault](): android.graphics.drawable.Drawable.ConstantState { + [selectedBackgroundColorProperty.getDefault](): android.graphics.drawable.Drawable { const viewGroup = this.nativeViewProtected.getParent(); - return viewGroup.getBackground().getConstantState(); + return viewGroup.getBackground(); } - [selectedBackgroundColorProperty.setNative](value: Color | android.graphics.drawable.Drawable.ConstantState) { - const viewGroup = this.nativeViewProtected.getParent(); + [selectedBackgroundColorProperty.setNative](value: Color | android.graphics.drawable.Drawable) { + const nativeView = this.nativeViewProtected; + const viewGroup = nativeView.getParent(); if (value instanceof Color) { const color = value.android; const backgroundDrawable = viewGroup.getBackground(); - if (apiLevel > 21 && backgroundDrawable && typeof backgroundDrawable.setColorFilter === "function") { - const newDrawable = backgroundDrawable.getConstantState().newDrawable(); + if (apiLevel > 21 && backgroundDrawable) { + const newDrawable = tryCloneDrawable(backgroundDrawable, nativeView.getResources()); newDrawable.setColorFilter(color, android.graphics.PorterDuff.Mode.SRC_IN); org.nativescript.widgets.ViewHelper.setBackground(viewGroup, newDrawable); } else { @@ -164,15 +165,26 @@ export class SegmentedBarItem extends SegmentedBarItemBase { arr[0] = R_ATTR_STATE_SELECTED; stateDrawable.addState(arr, colorDrawable); stateDrawable.setBounds(0, 15, viewGroup.getRight(), viewGroup.getBottom()); - org.nativescript.widgets.ViewHelper.setBackground(viewGroup, stateDrawable); } } else { - org.nativescript.widgets.ViewHelper.setBackground(viewGroup, value.newDrawable()); + const backgroundDrawable = tryCloneDrawable(value, nativeView.getResources()); + org.nativescript.widgets.ViewHelper.setBackground(viewGroup, backgroundDrawable); } } } +function tryCloneDrawable(value: android.graphics.drawable.Drawable, resources: android.content.res.Resources): android.graphics.drawable.Drawable { + if (value) { + const constantState = value.getConstantState(); + if (constantState) { + return constantState.newDrawable(resources); + } + } + + return value; +} + export class SegmentedBar extends SegmentedBarBase { nativeViewProtected: android.widget.TabHost; private _tabContentFactory: android.widget.TabHost.TabContentFactory; diff --git a/tns-core-modules/ui/styling/background.android.ts b/tns-core-modules/ui/styling/background.android.ts index 59ac8a070..b9ea5fe19 100644 --- a/tns-core-modules/ui/styling/background.android.ts +++ b/tns-core-modules/ui/styling/background.android.ts @@ -6,7 +6,7 @@ import * as application from "../../application"; export * from "./background-common" interface AndroidView { - _cachedDrawableConstState: android.graphics.drawable.Drawable.ConstantState; + _cachedDrawable: android.graphics.drawable.Drawable.ConstantState | android.graphics.drawable.Drawable; } // TODO: Change this implementation to use @@ -41,8 +41,9 @@ export module ad { const drawable = nativeView.getBackground(); const androidView = view as AndroidView; // use undefined as not set. getBackground will never return undefined only Drawable or null; - if (androidView._cachedDrawableConstState === undefined && drawable) { - androidView._cachedDrawableConstState = drawable.getConstantState(); + if (androidView._cachedDrawable === undefined && drawable) { + const constantState = drawable.getConstantState(); + androidView._cachedDrawable = constantState || drawable; } if (isSetColorFilterOnlyWidget(nativeView) @@ -77,10 +78,19 @@ export module ad { } } } else { - // TODO: newDrawable for BitmapDrawable will fail if we don't specify resource. Use the other overload. - const defaultDrawable = androidView._cachedDrawableConstState ? androidView._cachedDrawableConstState.newDrawable(nativeView.getResources()) : null; + const cachedDrawable = androidView._cachedDrawable; + let defaultDrawable: android.graphics.drawable.Drawable; + if (cachedDrawable instanceof android.graphics.drawable.Drawable.ConstantState) { + defaultDrawable = cachedDrawable.newDrawable(nativeView.getResources()) + } else if (cachedDrawable instanceof android.graphics.drawable.Drawable) { + defaultDrawable = cachedDrawable; + } else { + defaultDrawable = null; + } + org.nativescript.widgets.ViewHelper.setBackground(nativeView, defaultDrawable); - androidView._cachedDrawableConstState = undefined; + // TODO: Do we need to clear the drawable here? Can't we just reuse it again? + androidView._cachedDrawable = undefined; if (cache.layerType !== undefined) { cache.setLayerType(cache.layerType, null); diff --git a/tns-core-modules/ui/tab-view/tab-view.android.ts b/tns-core-modules/ui/tab-view/tab-view.android.ts index 39db149eb..ef9857962 100644 --- a/tns-core-modules/ui/tab-view/tab-view.android.ts +++ b/tns-core-modules/ui/tab-view/tab-view.android.ts @@ -412,14 +412,14 @@ export class TabView extends TabViewBase { selectedIndexProperty.coerce(this); } - [tabBackgroundColorProperty.getDefault](): android.graphics.drawable.Drawable.ConstantState { - return this._tabLayout.getBackground().getConstantState(); + [tabBackgroundColorProperty.getDefault](): android.graphics.drawable.Drawable { + return this._tabLayout.getBackground(); } - [tabBackgroundColorProperty.setNative](value: android.graphics.drawable.Drawable.ConstantState | Color) { + [tabBackgroundColorProperty.setNative](value: android.graphics.drawable.Drawable | Color) { if (value instanceof Color) { this._tabLayout.setBackgroundColor(value.android); } else { - this._tabLayout.setBackground(value ? value.newDrawable() : null); + this._tabLayout.setBackground(tryCloneDrawable(value, this.nativeViewProtected.getResources)); } } @@ -447,4 +447,15 @@ export class TabView extends TabViewBase { const color = value instanceof Color ? value.android : value; tabLayout.setSelectedIndicatorColors([color]); } +} + +function tryCloneDrawable(value: android.graphics.drawable.Drawable, resources: android.content.res.Resources): android.graphics.drawable.Drawable { + if (value) { + const constantState = value.getConstantState(); + if (constantState) { + return constantState.newDrawable(resources); + } + } + + return value; } \ No newline at end of file