diff --git a/apps/tests/ui/time-picker/time-picker-tests.ts b/apps/tests/ui/time-picker/time-picker-tests.ts index f421756fd..4af8757f9 100644 --- a/apps/tests/ui/time-picker/time-picker-tests.ts +++ b/apps/tests/ui/time-picker/time-picker-tests.ts @@ -29,7 +29,7 @@ function _createTimePicker(hour?: number, minute?: number): timePickerModule.Tim if (minute) { timePicker.minute = minute; } - + return timePicker; } @@ -58,38 +58,38 @@ if (platform.device.os === platform.platformNames.ios) { } } -export function test_WhenCreated_HourIsUndefined() { +export function test_WhenCreated_HourIsCurrentHour() { helper.buildUIAndRunTest(_createTimePicker(), function (views: Array) { var timePicker = views[0]; var actualValue = timePicker.hour; - var expectedValue = undefined; + var expectedValue = timePickerTestsNative.getNativeHour(timePicker); TKUnit.assert(actualValue === expectedValue, "Actual: " + actualValue + "; Expected: " + expectedValue); }); } -export function test_WhenCreated_MinHourIs1() { +export function test_WhenCreated_MinHourIs0() { helper.buildUIAndRunTest(_createTimePicker(), function (views: Array) { var timePicker = views[0]; var actualValue = timePicker.minHour; - var expectedValue = 1; + var expectedValue = 0; TKUnit.assert(actualValue === expectedValue, "Actual: " + actualValue + "; Expected: " + expectedValue); }); } -export function test_WhenCreated_MaxHourIs24() { +export function test_WhenCreated_MaxHourIs23() { helper.buildUIAndRunTest(_createTimePicker(), function (views: Array) { var timePicker = views[0]; var actualValue = timePicker.maxHour; - var expectedValue = 24; + var expectedValue = 23; TKUnit.assert(actualValue === expectedValue, "Actual: " + actualValue + "; Expected: " + expectedValue); }); } -export function test_WhenCreated_MinuteIsUndefined() { +export function test_WhenCreated_MinuteIsCurrentMinute() { helper.buildUIAndRunTest(_createTimePicker(), function (views: Array) { var timePicker = views[0]; var actualValue = timePicker.minute; - var expectedValue = undefined; + var expectedValue = timePickerTestsNative.getNativeMinute(timePicker); TKUnit.assert(actualValue === expectedValue, "Actual: " + actualValue + "; Expected: " + expectedValue); }); } @@ -115,84 +115,96 @@ export function test_WhenCreated_MaxMinuteIs59() { export function testHourThrowExceptionWhenLessThanMinHour() { helper.buildUIAndRunTest(_createTimePicker(), function (views: Array) { var timePicker = views[0]; - timePicker.minHour = 13; + timePicker.hour = 14; + timePicker.minHour = timePicker.hour - 1; TKUnit.assertThrows(function () { timePicker.hour = timePicker.minHour - 1; }, "Setting hour property to a value less than minHour property value should throw."); }); } +export function testMinHourThrowExceptionWhenHourLessThanMinHour() { + helper.buildUIAndRunTest(_createTimePicker(), function (views: Array) { + var timePicker = views[0]; + timePicker.hour = 14; + TKUnit.assertThrows(function () { + timePicker.minHour = timePicker.hour + 1; + }, "Setting minHour property to a greater than hour property value should throw."); + }); +} + export function testHourThrowExceptionWhenGreaterThanMaxHour() { helper.buildUIAndRunTest(_createTimePicker(), function (views: Array) { var timePicker = views[0]; - timePicker.maxHour = 13; + timePicker.hour = 14; + timePicker.maxHour = timePicker.hour + 1; TKUnit.assertThrows(function () { timePicker.hour = timePicker.maxHour + 1;; }, "Setting hour property to a value greater than maxHour property value should throw."); }); } +export function testMaxHourThrowExceptionWhenHourGreaterThanMaxHour() { + helper.buildUIAndRunTest(_createTimePicker(), function (views: Array) { + var timePicker = views[0]; + timePicker.hour = 14; + TKUnit.assertThrows(function () { + timePicker.maxHour = timePicker.hour - 1; + }, "Setting maxHour property to a value less than hour property value should throw."); + }); +} + export function testMinuteThrowExceptionWhenLessThanMinMinute() { helper.buildUIAndRunTest(_createTimePicker(), function (views: Array) { var timePicker = views[0]; - timePicker.minMinute = 13; + timePicker.hour = 14; + timePicker.minute = 13; + + timePicker.minHour = timePicker.hour; + timePicker.minMinute = timePicker.minute; TKUnit.assertThrows(function () { timePicker.minute = timePicker.minMinute - 1; - }, "Setting hour property to a value less than minHour property value should throw."); + }, "Setting minute property to a value less than minMinute property value should throw."); + }); +} + +export function testMinMinuteThrowExceptionWhenMinuteLessThanMinMinute() { + helper.buildUIAndRunTest(_createTimePicker(), function (views: Array) { + var timePicker = views[0]; + timePicker.hour = 14; + timePicker.minute = 13; + + timePicker.minHour = timePicker.hour; + TKUnit.assertThrows(function () { + timePicker.minMinute = timePicker.minute + 1; + }, "Setting minMinute property to a value greater than minute property value should throw."); }); } export function testMinuteThrowExceptionWhenGreaterThanMaxMinute() { helper.buildUIAndRunTest(_createTimePicker(), function (views: Array) { var timePicker = views[0]; - timePicker.maxMinute = 13; + timePicker.hour = 14; + timePicker.minute = 13; + + timePicker.maxHour = timePicker.hour; + timePicker.maxMinute = timePicker.minute; TKUnit.assertThrows(function () { - timePicker.minute = timePicker.maxMinute + 1;; - }, "Setting hour property to a value greater than maxHour property value should throw."); + timePicker.minute = timePicker.maxMinute + 1; + }, "Setting minute property to a value greater than maxMinute property value should throw."); }); } -export function testHourFromNativeEqualToMinHourWhenLessThanMinHour() { +export function testMaxMinuteThrowExceptionWhenMinuteGreaterThanMaxMinute() { helper.buildUIAndRunTest(_createTimePicker(), function (views: Array) { var timePicker = views[0]; - var expectedValue = 13; - timePicker.minHour = expectedValue; - timePickerTestsNative.setNativeHour(timePicker, expectedValue - 1); - var actualValue = timePickerTestsNative.getNativeHour(timePicker); - TKUnit.assert(actualValue === expectedValue, "Actual: " + actualValue + "; Expected: " + expectedValue); - }); -} + timePicker.hour = 14; + timePicker.minute = 13; -export function testHourFromNativeEqualToMaxHourWhenGreaterThanMaxHour() { - helper.buildUIAndRunTest(_createTimePicker(), function (views: Array) { - var timePicker = views[0]; - var expectedValue = 13; - timePicker.maxHour = expectedValue; - timePickerTestsNative.setNativeHour(timePicker, expectedValue + 1); - var actualValue = timePickerTestsNative.getNativeHour(timePicker); - TKUnit.assert(actualValue === expectedValue, "Actual: " + actualValue + "; Expected: " + expectedValue); - }); -} - -export function testMinuteFromNativeEqualToMinMinuteWhenLessThanMinMinute() { - helper.buildUIAndRunTest(_createTimePicker(), function (views: Array) { - var timePicker = views[0]; - var expectedValue = 13; - timePicker.minMinute = expectedValue; - timePickerTestsNative.setNativeMinute(timePicker, expectedValue - 1); - var actualValue = timePickerTestsNative.getNativeMinute(timePicker); - TKUnit.assert(actualValue === expectedValue, "Actual: " + actualValue + "; Expected: " + expectedValue); - }); -} - -export function testMinuteFromNativeEqualToMaxMinuteWhenGreaterThanMaxMinute() { - helper.buildUIAndRunTest(_createTimePicker(), function (views: Array) { - var timePicker = views[0]; - var expectedValue = 13; - timePicker.maxMinute = expectedValue; - timePickerTestsNative.setNativeMinute(timePicker, expectedValue + 1); - var actualValue = timePickerTestsNative.getNativeMinute(timePicker); - TKUnit.assert(actualValue === expectedValue, "Actual: " + actualValue + "; Expected: " + expectedValue); + timePicker.maxHour = timePicker.hour; + TKUnit.assertThrows(function () { + timePicker.maxMinute = timePicker.minute - 1; + }, "Setting maxMinute property to a value less than minute property value should throw."); }); } diff --git a/ui/time-picker/time-picker-common.ts b/ui/time-picker/time-picker-common.ts index 0c75174cd..51c213125 100644 --- a/ui/time-picker/time-picker-common.ts +++ b/ui/time-picker/time-picker-common.ts @@ -5,59 +5,138 @@ import view = require("ui/core/view"); import types = require("utils/types"); function isHourValid(value: number): boolean { - return types.isNumber(value) && value >= 1 && value <= 24; + return types.isNumber(value) && value >= 0 && value <= 23; } function isMinuteValid(value: number): boolean { return types.isNumber(value) && value >= 0 && value <= 59; } -export function getValidHour(hour: number, minHour: number, maxHour: number): number { - let hourValue = hour; - - if (minHour && hour < minHour) { - hourValue = minHour - } - - if (maxHour && hour > maxHour) { - hourValue = maxHour - } - - return hourValue; +export interface Time { + hour: number; + minute: number; } -export function getValidMinute(minute: number, minMinute: number, maxMinute: number): number { - let minuteValue = minute; +function getMinutes(hour: number): number { + return hour * 60; +} - if (minMinute && minute < minMinute) { - minuteValue = minMinute +export function isGreaterThanMinTime(picker: definition.TimePicker, hour?: number, minute?: number): boolean { + return getMinutes(types.isDefined(hour) ? hour : picker.hour) + (types.isDefined(minute) ? minute : picker.minute) >= getMinutes(picker.minHour) + picker.minMinute; +} + +export function isLessThanMaxTime(picker: definition.TimePicker, hour?: number, minute?: number): boolean { + return getMinutes(types.isDefined(hour) ? hour : picker.hour) + (types.isDefined(minute) ? minute : picker.minute) <= getMinutes(picker.maxHour) + picker.maxMinute; +} + +export function isValidTime(picker: definition.TimePicker): boolean { + return isGreaterThanMinTime(picker) && isLessThanMaxTime(picker); +} + +export function getValidTime(picker: definition.TimePicker, hour?: number, minute?: number): Time { + let time = { hour: hour, minute: minute }; + + if (!isGreaterThanMinTime(picker, hour, minute)) { + time = { hour: picker.minHour, minute: picker.minMinute }; } - if (maxMinute && minute > maxMinute) { - minuteValue = maxMinute + if (!isLessThanMaxTime(picker, hour, minute)) { + time = { hour: picker.maxHour, minute: picker.maxMinute }; } - return minuteValue; + return time; +} + +function toString(value: number): string { + return value < 10 ? `0${value}` : `${value}`; +} + +function getMinMaxTimeErrorMessage(picker: definition.TimePicker): string { + return `Min time: (${toString(picker.minHour) }:${toString(picker.minMinute) }), max time: (${toString(picker.maxHour) }:${toString(picker.maxMinute) })`; +} + +function getErrorMessage(picker: definition.TimePicker, propertyName: string, newValue: number): string { + return `${propertyName} property value (${toString(newValue) }:${toString(picker.minute) }) is not valid. ${getMinMaxTimeErrorMessage(picker) }.`; +} + +function onHourPropertyChanged(data: dependencyObservable.PropertyChangeData) { + var picker = data.object; + + if (isValidTime(picker)) { + picker._setNativeTime(); + } else { + throw new Error(getErrorMessage(picker, "Hour", data.newValue)); + } +} + +function onMinutePropertyChanged(data: dependencyObservable.PropertyChangeData) { + var picker = data.object; + + if (isValidTime(picker)) { + picker._setNativeTime(); + } else { + throw new Error(getErrorMessage(picker, "Minute", data.newValue)); + } +} + +function onMinMinutePropertyChanged(data: dependencyObservable.PropertyChangeData) { + var picker = data.object; + + if (isValidTime(picker)) { + picker._setNativeMinTime(); + } else { + throw new Error(getErrorMessage(picker, "Minute", data.newValue)); + } +} + +function onMaxMinutePropertyChanged(data: dependencyObservable.PropertyChangeData) { + var picker = data.object; + + if (isValidTime(picker)) { + picker._setNativeMaxTime(); + } else { + throw new Error(getErrorMessage(picker, "Minute", data.newValue)); + } +} + +function onMinHourPropertyChanged(data: dependencyObservable.PropertyChangeData) { + var picker = data.object; + + if (isValidTime(picker)) { + picker._setNativeMinTime(); + } else { + throw new Error(getErrorMessage(picker, "Hour", data.newValue)); + } +} + +function onMaxHourPropertyChanged(data: dependencyObservable.PropertyChangeData) { + var picker = data.object; + + if (isValidTime(picker)) { + picker._setNativeMaxTime(); + } else { + throw new Error(getErrorMessage(picker, "Hour", data.newValue)); + } } export class TimePicker extends view.View implements definition.TimePicker { public static hourProperty = new dependencyObservable.Property("hour", "TimePicker", - new proxy.PropertyMetadata(undefined, dependencyObservable.PropertyMetadataSettings.None, undefined, isHourValid)); + new proxy.PropertyMetadata(0, dependencyObservable.PropertyMetadataSettings.None, onHourPropertyChanged, isHourValid)); public static minHourProperty = new dependencyObservable.Property("minHour", "TimePicker", - new proxy.PropertyMetadata(1, dependencyObservable.PropertyMetadataSettings.None, undefined, isHourValid)); + new proxy.PropertyMetadata(0, dependencyObservable.PropertyMetadataSettings.None, onMinHourPropertyChanged, isHourValid)); public static maxHourProperty = new dependencyObservable.Property("maxHour", "TimePicker", - new proxy.PropertyMetadata(24, dependencyObservable.PropertyMetadataSettings.None, undefined, isHourValid)); + new proxy.PropertyMetadata(23, dependencyObservable.PropertyMetadataSettings.None, onMaxHourPropertyChanged, isHourValid)); public static minuteProperty = new dependencyObservable.Property("minute", "TimePicker", - new proxy.PropertyMetadata(undefined, dependencyObservable.PropertyMetadataSettings.None, undefined, isMinuteValid)); + new proxy.PropertyMetadata(0, dependencyObservable.PropertyMetadataSettings.None, onMinutePropertyChanged, isMinuteValid)); public static minMinuteProperty = new dependencyObservable.Property("minMinute", "TimePicker", - new proxy.PropertyMetadata(0, dependencyObservable.PropertyMetadataSettings.None, undefined, isMinuteValid)); + new proxy.PropertyMetadata(0, dependencyObservable.PropertyMetadataSettings.None, onMinMinutePropertyChanged, isMinuteValid)); public static maxMinuteProperty = new dependencyObservable.Property("maxMinute", "TimePicker", - new proxy.PropertyMetadata(59, dependencyObservable.PropertyMetadataSettings.None, undefined, isMinuteValid)); + new proxy.PropertyMetadata(59, dependencyObservable.PropertyMetadataSettings.None, onMaxMinutePropertyChanged, isMinuteValid)); constructor() { super(); @@ -104,4 +183,16 @@ export class TimePicker extends view.View implements definition.TimePicker { set minMinute(value: number) { this._setValue(TimePicker.minMinuteProperty, value); } + + public _setNativeTime() { + // + } + + public _setNativeMinTime() { + // + } + + public _setNativeMaxTime() { + // + } } \ No newline at end of file diff --git a/ui/time-picker/time-picker.android.ts b/ui/time-picker/time-picker.android.ts index 758c443b5..5f4d9a65d 100644 --- a/ui/time-picker/time-picker.android.ts +++ b/ui/time-picker/time-picker.android.ts @@ -4,32 +4,6 @@ import proxy = require("ui/core/proxy"); import utils = require("utils/utils") import types = require("utils/types") -function onHourPropertyChanged(data: dependencyObservable.PropertyChangeData) { - var picker = data.object; - - var validValue = common.getValidHour(data.newValue, picker.minHour, picker.maxHour); - if (validValue === data.newValue) { - picker._setNativeValueSilently(data.newValue, picker.minute); - } else { - throw new Error(`Hour property value (${data.newValue}) is not valid. Min value: (${picker.minHour} ), max value: (${picker.maxHour} ).`); - } -} - -(common.TimePicker.hourProperty.metadata).onSetNativeValue = onHourPropertyChanged; - -function onMinutePropertyChanged(data: dependencyObservable.PropertyChangeData) { - var picker = data.object; - - var validValue = common.getValidMinute(data.newValue, picker.minMinute, picker.maxMinute); - if (validValue === data.newValue) { - picker._setNativeValueSilently(picker.hour, data.newValue); - } else { - throw new Error(`Minute property value (${data.newValue}) is not valid. Min value: (${picker.minMinute} ), max value: (${picker.maxMinute} ).`); - } -} - -(common.TimePicker.minuteProperty.metadata).onSetNativeValue = onMinutePropertyChanged; - global.moduleMerge(common, exports); var SDK = android.os.Build.VERSION.SDK_INT; @@ -55,45 +29,53 @@ export class TimePicker extends common.TimePicker { onTimeChanged: function (picker: android.widget.TimePicker, hour: number, minute: number) { if (this.owner) { - - this.owner._setNativeValueSilently(hour, minute); - - if (hour !== this.owner.hour) { - this.owner._onPropertyChangedFromNative(common.TimePicker.hourProperty, hour); - } - - if (minute !== this.owner.minute) { - this.owner._onPropertyChangedFromNative(common.TimePicker.minuteProperty, minute); - } + var validTime = common.getValidTime(this.owner, hour, minute); + this.owner._setNativeValueSilently(validTime.hour, validTime.minute); } } }); this._android.setOnTimeChangedListener(this._listener); + + var c = java.util.Calendar.getInstance(); + + if (this.hour === common.TimePicker.hourProperty.metadata.defaultValue) { + this.hour = c.get(java.util.Calendar.HOUR_OF_DAY); + } + + if (this.minute === common.TimePicker.minuteProperty.metadata.defaultValue) { + this.minute = c.get(java.util.Calendar.MINUTE); + } + + var validTime = common.getValidTime(this, this.hour, this.minute); + this._setNativeValueSilently(validTime.hour, validTime.minute); } - public _setNativeValueSilently(hour: number, minute: number) { + private _setNativeValueSilently(hour: number, minute: number) { if (this.android) { this.android.setOnTimeChangedListener(null); - if (types.isNumber(hour)) { - var h = new java.lang.Integer(common.getValidHour(hour, this.minHour, this.maxHour)); - if (SDK >= 23) { - (this.android).setHour(h); - } else { - this.android.setCurrentHour(h); - } + var h = new java.lang.Integer(hour); + if (SDK >= 23) { + (this.android).setHour(h); + } else { + this.android.setCurrentHour(h); } - if (types.isNumber(minute)) { - var m = new java.lang.Integer(common.getValidMinute(minute, this.minMinute, this.maxMinute)); - if (SDK >= 23) { - (this.android).setMinute(m); - } else { - this.android.setCurrentMinute(m); - } + var m = new java.lang.Integer(minute); + if (SDK >= 23) { + (this.android).setMinute(m); + } else { + this.android.setCurrentMinute(m); } + this.minute = minute; + this.hour = hour; + this.android.setOnTimeChangedListener(this._listener); } } + + public _setNativeTime() { + this._setNativeValueSilently(this.hour, this.minute); + } } diff --git a/ui/time-picker/time-picker.d.ts b/ui/time-picker/time-picker.d.ts index d12c55bd2..1cfbcad56 100644 --- a/ui/time-picker/time-picker.d.ts +++ b/ui/time-picker/time-picker.d.ts @@ -53,5 +53,9 @@ declare module "ui/time-picker" { * Gets or sets the min time minute. */ minMinute: number; + + _setNativeTime(): void; + _setNativeMinTime(): void; + _setNativeMaxTime(): void; } } diff --git a/ui/time-picker/time-picker.ios.ts b/ui/time-picker/time-picker.ios.ts index 13e6eaf13..ee925928f 100644 --- a/ui/time-picker/time-picker.ios.ts +++ b/ui/time-picker/time-picker.ios.ts @@ -3,41 +3,16 @@ import dependencyObservable = require("ui/core/dependency-observable"); import proxy = require("ui/core/proxy"); import types = require("utils/types"); -function getDate(date: NSDate, hour?: number, minute?: number): NSDate { - var comps = NSCalendar.currentCalendar().componentsFromDate(NSCalendarUnit.NSCalendarUnitHour | NSCalendarUnit.NSCalendarUnitMinute, date); - - if (hour) { - comps.hour = hour; - } - - if (minute) { - comps.minute = minute; - } - +function getDate(hour: number, minute: number): NSDate { + var comps = NSDateComponents.alloc().init(); + comps.hour = hour; + comps.minute = minute; return NSCalendar.currentCalendar().dateFromComponents(comps); } -function onHourPropertyChanged(data: dependencyObservable.PropertyChangeData) { - var picker = data.object; - var validValue = common.getValidHour(data.newValue, picker.minHour, picker.maxHour); - if (validValue === data.newValue) { - picker.ios.setDateAnimated(getDate(picker.ios.date, data.newValue, picker.minute), false); - } else { - throw new Error(`Hour property value (${data.newValue}) is not valid. Min value: (${picker.minHour} ), max value: (${picker.maxHour} ).`); - } +function getComponents(date: NSDate): NSDateComponents { + return NSCalendar.currentCalendar().componentsFromDate(NSCalendarUnit.NSCalendarUnitHour | NSCalendarUnit.NSCalendarUnitMinute, date); } -(common.TimePicker.hourProperty.metadata).onSetNativeValue = onHourPropertyChanged; - -function onMinutePropertyChanged(data: dependencyObservable.PropertyChangeData) { - var picker = data.object; - var validValue = common.getValidMinute(data.newValue, picker.minMinute, picker.maxMinute); - if (validValue === data.newValue) { - picker.ios.setDateAnimated(getDate(picker.ios.date, picker.hour, data.newValue), false); - } else { - throw new Error(`Minute property value (${data.newValue}) is not valid. Min value: (${picker.minMinute} ), max value: (${picker.maxMinute} ).`); - } -} -(common.TimePicker.minuteProperty.metadata).onSetNativeValue = onMinutePropertyChanged; global.moduleMerge(common, exports); @@ -53,23 +28,31 @@ export class TimePicker extends common.TimePicker { this._changeHandler = UITimePickerChangeHandlerImpl.initWithOwner(new WeakRef(this)); this._ios.addTargetActionForControlEvents(this._changeHandler, "valueChanged", UIControlEvents.UIControlEventValueChanged); + + var comps = getComponents(NSDate.date()); + this.hour = comps.hour; + this.minute = comps.minute; } get ios(): UIDatePicker { return this._ios; } - public _setNativeValueSilently(hour: number, minute: number) { + public _setNativeTime() { if (this.ios) { - this.ios.removeTargetActionForControlEvents(this._changeHandler, "valueChanged", UIControlEvents.UIControlEventValueChanged) + this.ios.date = getDate(this.hour, this.minute); + } + } - if (types.isNumber(hour) && types.isNumber(minute)) { - this.ios.setDateAnimated(getDate(this.ios.date, - common.getValidHour(hour, this.minHour, this.maxHour), - common.getValidMinute(minute, this.minMinute, this.maxMinute)), false); - } + public _setNativeMinTime() { + if (this.ios) { + this.ios.minimumDate = getDate(this.minHour, this.minMinute); + } + } - this.ios.addTargetActionForControlEvents(this._changeHandler, "valueChanged", UIControlEvents.UIControlEventValueChanged); + public _setNativeMaxTime() { + if (this.ios) { + this.ios.maximumDate = getDate(this.maxHour, this.maxMinute); } } } @@ -90,9 +73,7 @@ class UITimePickerChangeHandlerImpl extends NSObject { return; } - var comps = NSCalendar.currentCalendar().componentsFromDate(NSCalendarUnit.NSCalendarUnitHour | NSCalendarUnit.NSCalendarUnitMinute, sender.date); - owner._setNativeValueSilently(comps.hour, comps.minute); - comps = NSCalendar.currentCalendar().componentsFromDate(NSCalendarUnit.NSCalendarUnitHour | NSCalendarUnit.NSCalendarUnitMinute, sender.date); + var comps = getComponents(sender.date); if (comps.hour !== owner.hour) { owner._onPropertyChangedFromNative(common.TimePicker.hourProperty, comps.hour);