diff --git a/lib/pkpass/models/pass.dart b/lib/pkpass/models/pass.dart
index 7c2fb38..686517e 100644
--- a/lib/pkpass/models/pass.dart
+++ b/lib/pkpass/models/pass.dart
@@ -1,7 +1,7 @@
import 'package:intl/locale.dart';
import 'package:pkpass/pkpass.dart';
-import 'package:pkpass/pkpass/utils/mabe_decode.dart';
+import 'package:pkpass/pkpass/utils/maybe_decode.dart';
/// Information that is required for all passes.
class PassMetadata {
diff --git a/lib/pkpass/models/pass_structure_dictionary.dart b/lib/pkpass/models/pass_structure_dictionary.dart
index 2ffe1e6..7b64f42 100644
--- a/lib/pkpass/models/pass_structure_dictionary.dart
+++ b/lib/pkpass/models/pass_structure_dictionary.dart
@@ -1,7 +1,8 @@
+import 'package:intl/intl.dart';
import 'package:intl/locale.dart';
import 'package:pkpass/pkpass.dart';
-import 'package:pkpass/pkpass/utils/mabe_decode.dart';
+import 'package:pkpass/pkpass/utils/maybe_decode.dart';
/// Keys that define the structure of the pass.
///
@@ -70,6 +71,15 @@ class DictionaryField {
/// Value of the field, for example, 42.
final DictionaryValue value;
+ /// Attributed value of the field.
+ ///
+ /// The value may contain HTML markup for links. Only the tag and its href attribute are supported. For example, the following is key-value pair specifies a link with the text “Edit my profile”:
+ ///
+ /// "attributedValue": "Edit my profile"
+ ///
+ /// This key’s value overrides the text specified by the value key.
+ final DictionaryValue? attributedValue;
+
/// Label text for the field.
final String? label;
@@ -81,35 +91,50 @@ class DictionaryField {
/// Alignment for the field’s contents.
final PassTextAlign? textAlignment;
- /// Attributed value of the field.
- ///
- /// The value may contain HTML markup for links. Only the tag and its href attribute are supported. For example, the following is key-value pair specifies a link with the text “Edit my profile”:
- ///
- /// "attributedValue": "Edit my profile"
- ///
- /// This key’s value overrides the text specified by the value key.
- final DictionaryValue? attributedValue;
+ /// Number Style for a numeric value.
+ final PassTextNumberStyle? numberStyle;
+
+ /// ISO 4217 Currency Code for a numeric value.
+ final String? currencyCode;
+
+ /// Date and Time Style for a Date/Time value.
+ final PassTextDateStyle? dateStyle;
+ final PassTextDateStyle? timeStyle;
+
+ /// Wether to display the Date/Time value in the device timezone or in the timezone of the value.
+ final bool ignoresTimeZone;
const DictionaryField({
required this.key,
required this.value,
+ this.attributedValue,
this.label,
this.changeMessage,
this.textAlignment,
- this.attributedValue,
+ this.numberStyle,
+ this.currencyCode,
+ this.dateStyle,
+ this.timeStyle,
+ this.ignoresTimeZone = false,
});
factory DictionaryField.fromJson(Map json) =>
DictionaryField(
key: json['key'] as String,
value: DictionaryValue.parse(json['value'] as String),
+ attributedValue: json['attributedValue'] == null
+ ? null
+ : DictionaryValue.parse(json['attributedValue'] as String),
label: json['label'] as String?,
changeMessage: json['changeMessage'] as String?,
textAlignment:
MaybeDecode.maybeTextAlign(json['textAlignment'] as String?),
- attributedValue: json['attributedValue'] == null
- ? null
- : DictionaryValue.parse(json['attributedValue'] as String),
+ numberStyle:
+ MaybeDecode.maybeNumberStyle(json['numberStyle'] as String?),
+ currencyCode: json['currencyCode'] as String?,
+ dateStyle: MaybeDecode.maybeDateStyle(json['dateStyle'] as String?),
+ timeStyle: MaybeDecode.maybeDateStyle(json['timeStyle'] as String?),
+ ignoresTimeZone: json['ignoresTimeZone'] == true ? true : false,
);
/// Localized version of [label] based on given [locale] and [pass].
@@ -118,6 +143,75 @@ class DictionaryField {
return localizations?[label] ?? label;
}
+ /// Localized version of [value] based on given [locale] and [pass].
+ String? getLocalizedValue(PassFile pass, Locale? locale) {
+ final language = locale?.toLanguageTag();
+
+ if (value is StringDictionaryValue) {
+ String string = (value as StringDictionaryValue).string;
+ final localizations = pass.getLocalizations(locale);
+ return localizations?[string] ?? string;
+ } else if (value is DateTimeDictionaryValue) {
+ DateTime dateTime = (value as DateTimeDictionaryValue).dateTime;
+ DateFormat format = DateFormat(null, language);
+
+ switch (dateStyle) {
+ case PassTextDateStyle.None:
+ break;
+ case PassTextDateStyle.Short:
+ format = DateFormat.yMd(language);
+ break;
+ case PassTextDateStyle.Medium:
+ format = DateFormat.yMMMd(language);
+ break;
+ case PassTextDateStyle.Long:
+ format = DateFormat.yMMMMd(language);
+ break;
+ case PassTextDateStyle.Full:
+ default:
+ format = DateFormat.yMMMMEEEEd(language);
+ break;
+ }
+
+ switch (timeStyle) {
+ case PassTextDateStyle.None:
+ break;
+ case PassTextDateStyle.Short:
+ format.add_jm();
+ break;
+ case PassTextDateStyle.Medium:
+ case PassTextDateStyle.Long:
+ case PassTextDateStyle.Full:
+ default:
+ format.add_jms();
+ break;
+ }
+
+ if (ignoresTimeZone) {
+ /// violating standard: return in UTC instead of original timezone
+ return format.format(dateTime);
+ } else {
+ return format.format(dateTime.toLocal());
+ }
+ } else if (value is NumberDictionaryValue) {
+ num number = (value as NumberDictionaryValue).number;
+ if (currencyCode != null) {
+ return NumberFormat.simpleCurrency(locale: language, name: currencyCode)
+ .format(number);
+ } else if (numberStyle == PassTextNumberStyle.Percent) {
+ return NumberFormat.percentPattern(language).format(number);
+ } else if (numberStyle == PassTextNumberStyle.Scientific) {
+ return NumberFormat.scientificPattern(language).format(number);
+ } else {
+ /// PassTextNumberStyle.SpellOut not implemented
+ /// default to decimal format
+ return NumberFormat.decimalPattern(language).format(number);
+ }
+ }
+
+ return null;
+ }
+
/// Localized version of [changeMessage] based on given [locale] and [pass].
String? getLocalizedChangeMessage(PassFile pass, Locale? locale) {
final localizations = pass.getLocalizations(locale);
@@ -131,7 +225,7 @@ abstract class DictionaryValue {
/// parses the correct [DictionaryValue] implementor based on a given [value].
factory DictionaryValue.parse(String value) {
- final number = int.tryParse(value);
+ final number = num.tryParse(value);
if (number != null) return NumberDictionaryValue(number);
final dateTime = DateTime.tryParse(value);
@@ -139,9 +233,6 @@ abstract class DictionaryValue {
return StringDictionaryValue(value);
}
-
- /// Localized value based on given [locale] and [pass].
- DictionaryValue getLocalizedValue(PassFile pass, Locale? locale);
}
/// [String] content of a [DictionaryField].
@@ -149,14 +240,6 @@ class StringDictionaryValue extends DictionaryValue {
final String string;
const StringDictionaryValue(this.string);
-
- @override
-
- /// Localized value based on given [locale] and [pass].
- DictionaryValue getLocalizedValue(PassFile pass, Locale? locale) {
- final localizations = pass.getLocalizations(locale);
- return StringDictionaryValue(localizations?[string] ?? string);
- }
}
/// [DateTime] content of a [DictionaryField].
@@ -164,23 +247,13 @@ class DateTimeDictionaryValue extends DictionaryValue {
final DateTime dateTime;
const DateTimeDictionaryValue(this.dateTime);
-
- @override
-
- /// Localized value based on given [locale] and [pass]. Same as [dateTime].
- DictionaryValue getLocalizedValue(PassFile pass, Locale? locale) => this;
}
-/// [int] content of a [DictionaryField].
+/// [num] content of a [DictionaryField].
class NumberDictionaryValue extends DictionaryValue {
- final int number;
+ final num number;
const NumberDictionaryValue(this.number);
-
- @override
-
- /// Localized value based on given [locale] and [pass]. Same as [number].
- DictionaryValue getLocalizedValue(PassFile pass, Locale? locale) => this;
}
/// Possible types of [PassStructureDictionary.transitType].
@@ -216,6 +289,39 @@ enum PassTextAlign {
natural,
}
+/// Possible types of [DictionaryField.dateStyle] and [DictionaryField.timeStyle].
+enum PassTextDateStyle {
+ /// PKDateStyleNone
+ None,
+
+ /// PKDateStyleShort
+ Short,
+
+ /// PKDateStyleMedium
+ Medium,
+
+ /// PKDateStyleLong
+ Long,
+
+ /// PKDateStyleFull
+ Full,
+}
+
+/// Possible types of [DictionaryField.numberStyle].
+enum PassTextNumberStyle {
+ /// PKNumberStyleDecimal
+ Decimal,
+
+ /// PKNumberStylePercent
+ Percent,
+
+ /// PKNumberStyleScientific
+ Scientific,
+
+ /// PKNumberStyleSpellOut
+ SpellOut,
+}
+
extension _TarnsitType on TransitType {
static TransitType? parse(String? type) {
if (type == null) return null;
diff --git a/lib/pkpass/utils/mabe_decode.dart b/lib/pkpass/utils/mabe_decode.dart
deleted file mode 100644
index 19a471b..0000000
--- a/lib/pkpass/utils/mabe_decode.dart
+++ /dev/null
@@ -1,29 +0,0 @@
-import '../models/pass_structure_dictionary.dart';
-import 'color_helper.dart';
-
-abstract class MaybeDecode {
- const MaybeDecode._();
-
- static int? maybeColor(String? colorCode) {
- if (colorCode == null) return null;
- return fromCssColor(colorCode);
- }
-
- static DateTime? maybeDateTime(String? timeStamp) {
- if (timeStamp == null) return null;
- return DateTime.tryParse(timeStamp);
- }
-
- static PassTextAlign? maybeTextAlign(String? align) {
- switch (align) {
- case 'PKTextAlignmentLeft':
- return PassTextAlign.left;
- case 'PKTextAlignmentCenter':
- return PassTextAlign.center;
- case 'PKTextAlignmentRight':
- return PassTextAlign.right;
- default:
- return PassTextAlign.natural;
- }
- }
-}
diff --git a/lib/pkpass/utils/maybe_decode.dart b/lib/pkpass/utils/maybe_decode.dart
new file mode 100644
index 0000000..b468a3a
--- /dev/null
+++ b/lib/pkpass/utils/maybe_decode.dart
@@ -0,0 +1,61 @@
+import '../models/pass_structure_dictionary.dart';
+import 'color_helper.dart';
+
+abstract class MaybeDecode {
+ const MaybeDecode._();
+
+ static int? maybeColor(String? colorCode) {
+ if (colorCode == null) return null;
+ return fromCssColor(colorCode);
+ }
+
+ static DateTime? maybeDateTime(String? timeStamp) {
+ if (timeStamp == null) return null;
+ return DateTime.tryParse(timeStamp);
+ }
+
+ static PassTextAlign? maybeTextAlign(String? align) {
+ switch (align) {
+ case 'PKTextAlignmentLeft':
+ return PassTextAlign.left;
+ case 'PKTextAlignmentCenter':
+ return PassTextAlign.center;
+ case 'PKTextAlignmentRight':
+ return PassTextAlign.right;
+ default:
+ return PassTextAlign.natural;
+ }
+ }
+
+ static PassTextDateStyle? maybeDateStyle(String? style) {
+ switch (style) {
+ case 'PKDateStyleNone':
+ return PassTextDateStyle.None;
+ case 'PKDateStyleShort':
+ return PassTextDateStyle.Short;
+ case 'PKDateStyleMedium':
+ return PassTextDateStyle.Medium;
+ case 'PKDateStyleLong':
+ return PassTextDateStyle.Long;
+ case 'PKDateStyleFull':
+ return PassTextDateStyle.Full;
+ default:
+ return null;
+ }
+ }
+
+ static PassTextNumberStyle? maybeNumberStyle(String? style) {
+ switch (style) {
+ case 'PKNumberStyleDecimal':
+ return PassTextNumberStyle.Decimal;
+ case 'PKNumberStylePercent':
+ return PassTextNumberStyle.Percent;
+ case 'PKNumberStyleScientific':
+ return PassTextNumberStyle.Scientific;
+ case 'PKNumberStyleSpellOut':
+ return PassTextNumberStyle.SpellOut;
+ default:
+ return null;
+ }
+ }
+}