fix: Don't crash on missing resources in tab-view and action-bar (#6388)

This commit is contained in:
Alexander Vakrilov
2018-10-11 21:31:19 +03:00
committed by GitHub
parent 5b77017d37
commit 56a1b120af
6 changed files with 68 additions and 25 deletions

View File

@@ -9,7 +9,13 @@ import { profile } from "../../profiling";
export * from "../core/view";
import { View, ViewBase, Property, unsetValue, booleanConverter, horizontalAlignmentProperty, verticalAlignmentProperty, CSSType } from "../core/view";
import {
View, ViewBase, Property,
unsetValue, booleanConverter,
horizontalAlignmentProperty,
verticalAlignmentProperty, CSSType,
traceWrite, traceCategories, traceMessageType
} from "../core/view";
export module knownCollections {
export var actionItems = "actionItems";
@@ -334,6 +340,12 @@ function onVisibilityChanged(item: ActionItemBase, oldValue: string, newValue: s
item._onVisibilityChanged(newValue);
}
export function traceMissingIcon(icon: string) {
traceWrite("Could not load action bar icon: " + icon,
traceCategories.Error,
traceMessageType.error);
}
export const textProperty = new Property<ActionItemBase, string>({ name: "text", defaultValue: "", valueChanged: onItemChanged });
textProperty.register(ActionItemBase);

View File

@@ -1,5 +1,9 @@
import { AndroidActionBarSettings as AndroidActionBarSettingsDefinition, AndroidActionItemSettings } from ".";
import { ActionItemBase, ActionBarBase, isVisible, View, layout, colorProperty, flatProperty, Color } from "./action-bar-common";
import {
ActionItemBase, ActionBarBase, isVisible,
View, layout, colorProperty, flatProperty,
Color, traceMissingIcon
} from "./action-bar-common";
import { RESOURCE_PREFIX } from "../../utils/utils";
import { fromFileOrResource } from "../../image-source";
import * as application from "../../application";
@@ -18,7 +22,7 @@ function generateItemId(): number {
}
interface MenuItemClickListener {
new (owner: ActionBar): android.support.v7.widget.Toolbar.OnMenuItemClickListener;
new(owner: ActionBar): android.support.v7.widget.Toolbar.OnMenuItemClickListener;
}
let appResources: android.content.res.Resources;
@@ -219,7 +223,9 @@ export class ActionBar extends ActionBarBase {
}
else if (navButton.icon) {
let drawableOrId = getDrawableOrResourceId(navButton.icon, appResources);
this.nativeViewProtected.setNavigationIcon(drawableOrId);
if (drawableOrId) {
this.nativeViewProtected.setNavigationIcon(drawableOrId);
}
}
// Set navigation content descripion, used by screen readers for the vision-impaired users
@@ -304,9 +310,6 @@ export class ActionBar extends ActionBarBase {
if (drawableOrId) {
menuItem.setIcon(drawableOrId);
}
else {
throw new Error("Error loading icon from " + item.icon);
}
}
let showAsAction = getShowAsAction(item);
@@ -422,10 +425,11 @@ function getDrawableOrResourceId(icon: string, resources: android.content.res.Re
return undefined;
}
let result = undefined;
if (icon.indexOf(RESOURCE_PREFIX) === 0) {
let resourceId: number = resources.getIdentifier(icon.substr(RESOURCE_PREFIX.length), "drawable", application.android.packageName);
if (resourceId > 0) {
return resourceId;
result = resourceId;
}
}
else {
@@ -436,10 +440,14 @@ function getDrawableOrResourceId(icon: string, resources: android.content.res.Re
drawable = new android.graphics.drawable.BitmapDrawable(is.android);
}
return drawable;
result = drawable;
}
return undefined;
if (!result) {
traceMissingIcon(icon);
}
return result;
}
function getShowAsAction(menuItem: ActionItem): number {

View File

@@ -1,13 +1,28 @@
import { IOSActionItemSettings, ActionItem as ActionItemDefinition } from ".";
import { ActionItemBase, ActionBarBase, isVisible, View, colorProperty, backgroundColorProperty, backgroundInternalProperty, flatProperty, layout, Color } from "./action-bar-common";
import { ImageSource, fromFileOrResource } from "../../image-source";
import {
ActionItemBase, ActionBarBase, isVisible, View,
colorProperty, backgroundColorProperty,
backgroundInternalProperty, flatProperty,
layout, Color, traceMissingIcon } from "./action-bar-common";
import { fromFileOrResource } from "../../image-source";
import { ios as iosUtils } from "../../utils/utils";
import { write as traceWrite, categories, messageType } from "../../trace";
export * from "./action-bar-common";
const majorVersion = iosUtils.MajorVersion;
const UNSPECIFIED = layout.makeMeasureSpec(0, layout.UNSPECIFIED);
function loadActionIconFromFileOrResource(icon: string): UIImage {
const img = fromFileOrResource(icon);
if (img && img.ios) {
return img.ios;
} else {
traceMissingIcon(icon);
return null;
}
}
class TapBarItemHandlerImpl extends NSObject {
private _owner: WeakRef<ActionItemDefinition>;
@@ -151,16 +166,16 @@ export class ActionBar extends ActionBarBase {
}
// Set back button image
let img: ImageSource;
let img: UIImage;
if (this.navigationButton && isVisible(this.navigationButton) && this.navigationButton.icon) {
img = fromFileOrResource(this.navigationButton.icon);
img = loadActionIconFromFileOrResource(this.navigationButton.icon);
}
// TODO: This could cause issue when canceling BackEdge gesture - we will change the backIndicator to
// show the one from the old page but the new page will still be visible (because we canceled EdgeBackSwipe gesutre)
// Consider moving this to new method and call it from - navigationControllerDidShowViewControllerAnimated.
if (img && img.ios) {
let image = img.ios.imageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal);
if (img) {
let image = img.imageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal);
navigationBar.backIndicatorImage = image;
navigationBar.backIndicatorTransitionMaskImage = image;
} else {
@@ -226,12 +241,8 @@ export class ActionBar extends ActionBarBase {
barButtonItem = UIBarButtonItem.alloc().initWithBarButtonSystemItemTargetAction(id, tapHandler, "tap");
} else if (item.icon) {
const img = fromFileOrResource(item.icon);
if (img && img.ios) {
barButtonItem = UIBarButtonItem.alloc().initWithImageStyleTargetAction(img.ios, UIBarButtonItemStyle.Plain, tapHandler, "tap");
} else {
throw new Error("Error loading icon from " + item.icon);
}
const img = loadActionIconFromFileOrResource(item.icon);
barButtonItem = UIBarButtonItem.alloc().initWithImageStyleTargetAction(img, UIBarButtonItemStyle.Plain, tapHandler, "tap");
} else {
barButtonItem = UIBarButtonItem.alloc().initWithTitleStyleTargetAction(item.text + "", UIBarButtonItemStyle.Plain, tapHandler, "tap");
}

View File

@@ -1,7 +1,8 @@
import { TabView as TabViewDefinition, TabViewItem as TabViewItemDefinition, SelectedIndexChangedEventData } from ".";
import {
View, ViewBase, Style, Property, CssProperty, CoercibleProperty,
Color, isIOS, AddArrayFromBuilder, AddChildFromBuilder, EventData, CSSType
Color, isIOS, AddArrayFromBuilder, AddChildFromBuilder, EventData, CSSType,
traceWrite, traceCategories, traceMessageType
} from "../core/view";
export * from "../core/view";
@@ -203,6 +204,10 @@ export interface TabViewBase {
on(event: "selectedIndexChanged", callback: (args: SelectedIndexChangedEventData) => void, thisArg?: any);
}
export function traceMissingIcon(icon: string) {
traceWrite("Could not load tab bar icon: " + icon, traceCategories.Error, traceMessageType.error);
}
export const selectedIndexProperty = new CoercibleProperty<TabViewBase, number>({
name: "selectedIndex", defaultValue: -1, affectsLayout: isIOS,
valueChanged: (target, oldValue, newValue) => {

View File

@@ -6,7 +6,7 @@ import {
tabTextColorProperty, tabBackgroundColorProperty, tabTextFontSizeProperty, selectedTabTextColorProperty,
androidSelectedTabHighlightColorProperty, androidOffscreenTabLimitProperty,
fontSizeProperty, fontInternalProperty, layout, traceCategory, traceEnabled,
traceWrite, Color
traceWrite, Color, traceMissingIcon
} from "./tab-view-common"
import { textTransformProperty, TextTransform, getTransformedText } from "../text-base";
import { fromFileOrResource } from "../../image-source";
@@ -233,11 +233,16 @@ function createTabItemSpec(item: TabViewItem): org.nativescript.widgets.TabItemS
if (item.iconSource) {
if (item.iconSource.indexOf(RESOURCE_PREFIX) === 0) {
result.iconId = ad.resources.getDrawableId(item.iconSource.substr(RESOURCE_PREFIX.length));
if (result.iconId === 0) {
traceMissingIcon(item.iconSource);
}
} else {
const is = fromFileOrResource(item.iconSource);
if (is) {
// TODO: Make this native call that accepts string so that we don't load Bitmap in JS.
result.iconDrawable = new android.graphics.drawable.BitmapDrawable(is.android);
} else {
traceMissingIcon(item.iconSource);
}
}
}

View File

@@ -5,7 +5,7 @@ import { ios as iosView, ViewBase } from "../core/view";
import {
TabViewBase, TabViewItemBase, itemsProperty, selectedIndexProperty,
tabTextColorProperty, tabTextFontSizeProperty, tabBackgroundColorProperty, selectedTabTextColorProperty, iosIconRenderingModeProperty,
View, fontInternalProperty, layout, traceEnabled, traceWrite, traceCategories, Color
View, fontInternalProperty, layout, traceEnabled, traceWrite, traceCategories, Color, traceMissingIcon
} from "./tab-view-common"
import { textTransformProperty, TextTransform, getTransformedText } from "../text-base";
import { fromFileOrResource } from "../../image-source";
@@ -451,6 +451,8 @@ export class TabView extends TabViewBase {
const originalRenderedImage = is.ios.imageWithRenderingMode(this._getIconRenderingMode());
this._iconsCache[iconSource] = originalRenderedImage;
image = originalRenderedImage;
} else {
traceMissingIcon(iconSource);
}
}