Skip to content

Commit b6d5510

Browse files
author
Hristo Hristov
authored
Fix crash where some android Drawables doesn't implement getConstantState... (NativeScript#4742)
Changed all places where getConstantState was used.
1 parent d6689a0 commit b6d5510

File tree

4 files changed

+64
-22
lines changed

4 files changed

+64
-22
lines changed

tns-core-modules/ui/core/view/view.android.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,16 @@ export class View extends ViewCommon {
459459
[backgroundInternalProperty.getDefault](): android.graphics.drawable.Drawable {
460460
const nativeView = this.nativeViewProtected;
461461
const drawable = nativeView.getBackground();
462-
return drawable ? drawable.getConstantState().newDrawable(nativeView.getResources()) : null;
462+
if (drawable) {
463+
const constantState = drawable.getConstantState();
464+
if (constantState) {
465+
return constantState.newDrawable(nativeView.getResources());
466+
} else {
467+
return drawable;
468+
}
469+
}
470+
471+
return null;
463472
}
464473
[backgroundInternalProperty.setNative](value: android.graphics.drawable.Drawable | Background) {
465474
this._redrawNativeBackground(value);

tns-core-modules/ui/segmented-bar/segmented-bar.android.ts

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,15 @@ const R_ATTR_STATE_SELECTED = 0x010100a1;
1212
const TITLE_TEXT_VIEW_ID = 16908310; // http://developer.android.com/reference/android/R.id.html#title
1313

1414
interface TabChangeListener {
15-
new (owner: SegmentedBar): android.widget.TabHost.OnTabChangeListener;
15+
new(owner: SegmentedBar): android.widget.TabHost.OnTabChangeListener;
1616
}
1717

1818
interface TabContentFactory {
19-
new (owner: SegmentedBar): android.widget.TabHost.TabContentFactory;
19+
new(owner: SegmentedBar): android.widget.TabHost.TabContentFactory;
2020
}
2121

2222
interface TabHost {
23-
new (context: android.content.Context, attrs: android.util.AttributeSet): android.widget.TabHost;
23+
new(context: android.content.Context, attrs: android.util.AttributeSet): android.widget.TabHost;
2424
}
2525

2626
let apiLevel: number;
@@ -144,17 +144,18 @@ export class SegmentedBarItem extends SegmentedBarItemBase {
144144
this.nativeViewProtected.setTypeface(value instanceof Font ? value.getAndroidTypeface() : value);
145145
}
146146

147-
[selectedBackgroundColorProperty.getDefault](): android.graphics.drawable.Drawable.ConstantState {
147+
[selectedBackgroundColorProperty.getDefault](): android.graphics.drawable.Drawable {
148148
const viewGroup = <android.view.ViewGroup>this.nativeViewProtected.getParent();
149-
return viewGroup.getBackground().getConstantState();
149+
return viewGroup.getBackground();
150150
}
151-
[selectedBackgroundColorProperty.setNative](value: Color | android.graphics.drawable.Drawable.ConstantState) {
152-
const viewGroup = <android.view.ViewGroup>this.nativeViewProtected.getParent();
151+
[selectedBackgroundColorProperty.setNative](value: Color | android.graphics.drawable.Drawable) {
152+
const nativeView = this.nativeViewProtected;
153+
const viewGroup = <android.view.ViewGroup>nativeView.getParent();
153154
if (value instanceof Color) {
154155
const color = value.android;
155156
const backgroundDrawable = viewGroup.getBackground();
156-
if (apiLevel > 21 && backgroundDrawable && typeof backgroundDrawable.setColorFilter === "function") {
157-
const newDrawable = backgroundDrawable.getConstantState().newDrawable();
157+
if (apiLevel > 21 && backgroundDrawable) {
158+
const newDrawable = tryCloneDrawable(backgroundDrawable, nativeView.getResources());
158159
newDrawable.setColorFilter(color, android.graphics.PorterDuff.Mode.SRC_IN);
159160
org.nativescript.widgets.ViewHelper.setBackground(viewGroup, newDrawable);
160161
} else {
@@ -164,15 +165,26 @@ export class SegmentedBarItem extends SegmentedBarItemBase {
164165
arr[0] = R_ATTR_STATE_SELECTED;
165166
stateDrawable.addState(arr, colorDrawable);
166167
stateDrawable.setBounds(0, 15, viewGroup.getRight(), viewGroup.getBottom());
167-
168168
org.nativescript.widgets.ViewHelper.setBackground(viewGroup, stateDrawable);
169169
}
170170
} else {
171-
org.nativescript.widgets.ViewHelper.setBackground(viewGroup, value.newDrawable());
171+
const backgroundDrawable = tryCloneDrawable(value, nativeView.getResources());
172+
org.nativescript.widgets.ViewHelper.setBackground(viewGroup, backgroundDrawable);
172173
}
173174
}
174175
}
175176

177+
function tryCloneDrawable(value: android.graphics.drawable.Drawable, resources: android.content.res.Resources): android.graphics.drawable.Drawable {
178+
if (value) {
179+
const constantState = value.getConstantState();
180+
if (constantState) {
181+
return constantState.newDrawable(resources);
182+
}
183+
}
184+
185+
return value;
186+
}
187+
176188
export class SegmentedBar extends SegmentedBarBase {
177189
nativeViewProtected: android.widget.TabHost;
178190
private _tabContentFactory: android.widget.TabHost.TabContentFactory;

tns-core-modules/ui/styling/background.android.ts

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import * as application from "../../application";
66
export * from "./background-common"
77

88
interface AndroidView {
9-
_cachedDrawableConstState: android.graphics.drawable.Drawable.ConstantState;
9+
_cachedDrawable: android.graphics.drawable.Drawable.ConstantState | android.graphics.drawable.Drawable;
1010
}
1111

1212
// TODO: Change this implementation to use
@@ -41,8 +41,9 @@ export module ad {
4141
const drawable = nativeView.getBackground();
4242
const androidView = <any>view as AndroidView;
4343
// use undefined as not set. getBackground will never return undefined only Drawable or null;
44-
if (androidView._cachedDrawableConstState === undefined && drawable) {
45-
androidView._cachedDrawableConstState = drawable.getConstantState();
44+
if (androidView._cachedDrawable === undefined && drawable) {
45+
const constantState = drawable.getConstantState();
46+
androidView._cachedDrawable = constantState || drawable;
4647
}
4748

4849
if (isSetColorFilterOnlyWidget(nativeView)
@@ -77,10 +78,19 @@ export module ad {
7778
}
7879
}
7980
} else {
80-
// TODO: newDrawable for BitmapDrawable will fail if we don't specify resource. Use the other overload.
81-
const defaultDrawable = androidView._cachedDrawableConstState ? androidView._cachedDrawableConstState.newDrawable(nativeView.getResources()) : null;
81+
const cachedDrawable = androidView._cachedDrawable;
82+
let defaultDrawable: android.graphics.drawable.Drawable;
83+
if (cachedDrawable instanceof android.graphics.drawable.Drawable.ConstantState) {
84+
defaultDrawable = cachedDrawable.newDrawable(nativeView.getResources())
85+
} else if (cachedDrawable instanceof android.graphics.drawable.Drawable) {
86+
defaultDrawable = cachedDrawable;
87+
} else {
88+
defaultDrawable = null;
89+
}
90+
8291
org.nativescript.widgets.ViewHelper.setBackground(nativeView, defaultDrawable);
83-
androidView._cachedDrawableConstState = undefined;
92+
// TODO: Do we need to clear the drawable here? Can't we just reuse it again?
93+
androidView._cachedDrawable = undefined;
8494

8595
if (cache.layerType !== undefined) {
8696
cache.setLayerType(cache.layerType, null);

tns-core-modules/ui/tab-view/tab-view.android.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -412,14 +412,14 @@ export class TabView extends TabViewBase {
412412
selectedIndexProperty.coerce(this);
413413
}
414414

415-
[tabBackgroundColorProperty.getDefault](): android.graphics.drawable.Drawable.ConstantState {
416-
return this._tabLayout.getBackground().getConstantState();
415+
[tabBackgroundColorProperty.getDefault](): android.graphics.drawable.Drawable {
416+
return this._tabLayout.getBackground();
417417
}
418-
[tabBackgroundColorProperty.setNative](value: android.graphics.drawable.Drawable.ConstantState | Color) {
418+
[tabBackgroundColorProperty.setNative](value: android.graphics.drawable.Drawable | Color) {
419419
if (value instanceof Color) {
420420
this._tabLayout.setBackgroundColor(value.android);
421421
} else {
422-
this._tabLayout.setBackground(value ? value.newDrawable() : null);
422+
this._tabLayout.setBackground(tryCloneDrawable(value, this.nativeViewProtected.getResources));
423423
}
424424
}
425425

@@ -447,4 +447,15 @@ export class TabView extends TabViewBase {
447447
const color = value instanceof Color ? value.android : value;
448448
tabLayout.setSelectedIndicatorColors([color]);
449449
}
450+
}
451+
452+
function tryCloneDrawable(value: android.graphics.drawable.Drawable, resources: android.content.res.Resources): android.graphics.drawable.Drawable {
453+
if (value) {
454+
const constantState = value.getConstantState();
455+
if (constantState) {
456+
return constantState.newDrawable(resources);
457+
}
458+
}
459+
460+
return value;
450461
}

0 commit comments

Comments
 (0)