Skip to content

Commit bc8ec97

Browse files
committed
Refactor: unifying command handling and improving layer separation #286
1 parent 8fa5ce5 commit bc8ec97

13 files changed

Lines changed: 177 additions & 276 deletions

File tree

source/main/data/communications/web-requests/command/BaseCommandRequest.mc

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,13 @@ class BaseCommandRequest extends BaseRequest {
8989
// If not on Wifi connection or if in sync mode, the command will be sent immediately
9090
// If on Wifi connection, the command will be handed over to the sync delegate and
9191
// sync mode will be started.
92-
public function sendCommand( cmd as String ) as Void {
92+
public function sendCommand( cmd as Item.ItemState ) as Void {
93+
94+
// Non-string commands are converted to a string
95+
if( ! ( cmd instanceof String ) ) {
96+
cmd = cmd.toString();
97+
}
98+
9399
// Note that isWifiConnected()=true indicates only that Wi-Fi is available,
94100
// but the app still needs to switch into sync mode to connect to it. If _syncMode
95101
// is true, then we are already in sync mode and can send the command.

source/main/user-interface/features/sitemap/control/picker/numeric/NumericPickerDelegate.mc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ class NumericPickerDelegate extends BaseDynamicPickerDelegate {
4444
throw new NonFatalUserInterfaceException( NonFatalUserInterfaceException.EX_INVALID_STATE_TYPE );
4545
}
4646
// Logger.debug "NumericPickerDelegate.onAccept: new state=" + newState.toString() );
47-
_menuItem.updateState( newState );
47+
_menuItem.sendCommand( newState );
4848
}
4949
ViewStack.popView( WatchUi.SLIDE_RIGHT );
5050
return true;
@@ -58,7 +58,7 @@ class NumericPickerDelegate extends BaseDynamicPickerDelegate {
5858
if( ! _menuItem.getSitemapNumeric().isReleaseOnly() ) {
5959
if( _menuItem.getSitemapNumeric().getNumericItem().getNumericState() != _previousState ) {
6060
// Logger.debug "NumericPickerDelegate.onCancel: reverting to state=" + _previousState.toString() );
61-
_menuItem.updateState( _previousState );
61+
_menuItem.sendCommand( _previousState );
6262
} else {
6363
// Logger.debug "NumericPickerDelegate.onCancel: state did not change" );
6464
}
@@ -85,7 +85,7 @@ class NumericPickerDelegate extends BaseDynamicPickerDelegate {
8585
}
8686
// Update only if releaseOnly is false
8787
if( ! _menuItem.getSitemapNumeric().isReleaseOnly() ) {
88-
_menuItem.updateState( state );
88+
_menuItem.sendCommand( state );
8989
}
9090
}
9191
}

source/main/user-interface/features/sitemap/menu-items/base/BaseCommandMenuItem.mc

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@ import Toybox.Time;
66
class BaseCommandMenuItem extends BaseWidgetMenuItem {
77

88
private var _commandRequest as BaseCommandRequest?;
9+
10+
// The last time an internal update was applied. Internal updates are applied
11+
// by some menu items directly after a command is sent to immediately reflect
12+
// the new state. Storing this timestamp is required to apply the post-command
13+
// hold time configured in the app settings.
14+
private var _lastLocalStateUpdate as Moment?;
15+
916
private var _pendingCommand as Item.ItemState?;
1017

1118
// Constructor
@@ -44,19 +51,29 @@ class BaseCommandMenuItem extends BaseWidgetMenuItem {
4451
return _pendingCommand != null;
4552
}
4653

54+
// Returns true if the item is still within the configured post-command hold time
55+
// since the last internal state update.
56+
public function isInHoldTime() as Boolean {
57+
var postCommandHoldTime = AppSettings.getPostCommandHoldTime();
58+
if( _lastLocalStateUpdate != null && postCommandHoldTime.value() > 0 ) {
59+
return Time.now().lessThan( _lastLocalStateUpdate.add( postCommandHoldTime ) );
60+
} else {
61+
return false;
62+
}
63+
}
64+
4765
public function onCommandComplete() as Void {
4866
if( _pendingCommand != null ) {
4967
// Perform an internal update of the state in the SitemapSwitch
5068
getSitemapWidget().updateState( _pendingCommand as Item.ItemState );
5169
_pendingCommand = null;
5270

53-
// Notify the base class that an internal state update was performed.
54-
// This triggers the post-command hold time for state updates,
71+
// This is used to apply the post-command hold time for state updates,
5572
// if configured in the app settings.
56-
notifyStateUpdatedLocally();
73+
_lastLocalStateUpdate = Time.now();
5774

5875
// Update state displayed by the menu item
59-
onStateUpdatedLocally();
76+
onStateChangedLocally();
6077

6178
WatchUi.requestUpdate();
6279
}
@@ -73,22 +90,23 @@ class BaseCommandMenuItem extends BaseWidgetMenuItem {
7390
_pendingCommand = null;
7491
}
7592

76-
public function onStateUpdatedLocally() as Void {
93+
public function onStateChangedLocally() as Void {
7794
setIcon( getSitemapWidget().getIcon() );
78-
onStateUpdated();
95+
onStateChanged();
7996
}
8097

81-
public function onStateUpdated() as Void {
98+
public function onStateChanged() as Void {
8299
}
83100

84-
// Send the command via the command request
101+
// Sends the command via the CommandRequest.
102+
// This method allows a command to be sent even if another command
103+
// is currently pending. In that case, the pending command is replaced
104+
// and the underlying request cancels the previous one.
105+
// Subclasses that want to prevent sending a new command while one
106+
// is pending should call hasPendingCommand() before invoking
107+
// sendCommand().
85108
public function sendCommand( command as Item.ItemState ) as Void {
86-
87-
if( ! ( command instanceof String ) ) {
88-
throw new GeneralException( "BaseCommandMenuItem.sendCommand supports only String arguments." );
89-
}
90-
91-
if( ! hasPendingCommand() && _commandRequest != null ) {
109+
if( _commandRequest != null ) {
92110
_pendingCommand = command;
93111
_commandRequest.sendCommand( command );
94112
}

source/main/user-interface/features/sitemap/menu-items/base/BaseWidgetMenuItem.mc

Lines changed: 27 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -30,31 +30,24 @@ typedef BaseWidgetMenuItemOptions as {
3030

3131
class BaseWidgetMenuItem extends BaseSitemapMenuItem {
3232

33+
// Store whether the subclass indicated that an action icon should be shown
34+
// This information is passed into the constructor as option, but needed
35+
// also during updates
36+
private var _isActionable as Boolean;
37+
3338
// The submenu representing the nested elements, if there are any
3439
private var _page as PageMenu?;
35-
40+
41+
// The current sitemap widget associated with this menu item
42+
private var _sitemapWidget as SitemapWidget;
43+
3644
// Reference to the parent menu is required so that submenus
3745
// can navigate back. Submenus may be created not only in the
3846
// constructor, but also dynamically in updateWidget(), so we
3947
// store the parent menu as a member variable and use a weak
4048
// reference to avoid memory leaks due to circular references.
4149
private var _weakParent as WeakReference;
4250

43-
// Store whether the subclass indicated that an action icon should be shown
44-
// This information is passed into the constructor as option, but needed
45-
// also during updates
46-
private var _isActionable as Boolean;
47-
48-
// The last time an internal update was applied. Internal updates are applied
49-
// by some menu items directly after a command is sent to immediately reflect
50-
// the new state. Storing this timestamp is required to apply the post-command
51-
// hold time configured in the app settings.
52-
private var lastLocalStateUpdate as Moment?;
53-
54-
// The sitemap widget is stored, so in updateWidget() we can check if the
55-
// state has changed, and if yes call onStateUpdated()
56-
private var _sitemapWidget as SitemapWidget;
57-
5851
// Constructor
5952
protected function initialize(
6053
options as BaseWidgetMenuItemOptions
@@ -81,50 +74,35 @@ class BaseWidgetMenuItem extends BaseSitemapMenuItem {
8174
);
8275
}
8376

77+
8478
// Returns the current sitemap widget
8579
protected function getSitemapWidget() as SitemapWidget {
8680
return _sitemapWidget;
8781
}
8882

83+
8984
// Returns true if the widget is linked to a page (sub menu)
9085
public function hasPage() as Boolean {
9186
return _page != null;
9287
}
9388

94-
// Returns true if the item is still within the configured post-command hold time
95-
// since the last internal state update.
96-
public function isInHoldTime() as Boolean {
97-
var postCommandHoldTime = AppSettings.getPostCommandHoldTime();
98-
if( lastLocalStateUpdate != null && postCommandHoldTime.value() > 0 ) {
99-
return Time.now().lessThan( lastLocalStateUpdate.add( postCommandHoldTime ) );
100-
} else {
101-
return false;
102-
}
103-
}
10489

10590
// Subclasses must override this method to determine whether the given
10691
// `SitemapWidget` instance is compatible with this menu item type.
10792
public static function isMyType( sitemapWidget as SitemapWidget ) as Boolean {
10893
throw new AbstractMethodException( "BaseSitemapMenuItem.getItemType" );
10994
}
11095

111-
// Must be called by subclasses when they perform an internal state update.
112-
// Some menu items use internal updates to immediately reflect the new state
113-
// after a command is sent. This notification stores the time of the update,
114-
// which is required to apply the configured post-command hold time.
115-
protected function notifyStateUpdatedLocally() as Void {
116-
lastLocalStateUpdate = Time.now();
117-
}
11896

11997
/*
120-
* Subclasses need to override this method to process state updates.
121-
* Note: updateWidget() can be overriden if other widget properties have an
122-
* effect on the
98+
* Subclasses can to override this method to process state changes.
99+
* Note: this is triggered only when the state has changed. If updates
100+
* to other widget data elements should be processed independent of a
101+
* state change, then updateWidget() should be overriden to process those.
123102
*/
124-
public function onStateUpdated() as Void {
125-
//throw new AbstractMethodException( "BaseWidgetMenuItem.onStateUpdated" );
126-
}
127-
103+
public function onStateChanged() as Void {}
104+
105+
128106
// Handles selection of the menu item.
129107
//
130108
// If a submenu is present, it is opened on selection. This typically takes precedence
@@ -195,10 +173,10 @@ class BaseWidgetMenuItem extends BaseSitemapMenuItem {
195173
* parts of the update, but they must also call the base class’s
196174
* updateWidget() to ensure core functionality is preserved.
197175
*
198-
* To process state updates subclasses SHOULD NOT use this function, but
199-
* instead override onStateUpdated(). This method is only called
176+
* To process state changes subclasses SHOULD NOT use this function, but
177+
* instead override onStateChanged(). This method is only called
200178
* if the state has changed, subclasses may use that implementation also
201-
* to process local updates.
179+
* to process local changes.
202180
*/
203181
public function updateWidget( sitemapWidget as SitemapWidget ) as Void {
204182
// Store the new widget instance
@@ -209,17 +187,19 @@ class BaseWidgetMenuItem extends BaseSitemapMenuItem {
209187
// during an update, we always use the async task queue
210188
processWidget( sitemapWidget, BasePageMenu.PROCESSING_ASYNC );
211189

212-
// Determine if the state as changed and if
213-
// yes, call onStateUpdated()
190+
// Determine if the display state or state as changed and if
191+
// yes, call onStateChanged()
214192
var previousItem = previousSitemapWidget.getItem();
215193
var newItem = sitemapWidget.getItem();
216194
var hasStateChanged =
217-
( previousItem == null ) != ( newItem == null )
195+
! previousSitemapWidget.getDisplayState().equals( sitemapWidget.getDisplayState() )
196+
|| ( previousItem == null ) != ( newItem == null )
218197
|| ( previousItem != null
219198
&& newItem != null
220199
&& ! previousItem.getState().equals( newItem.getState() ) );
200+
221201
if( hasStateChanged ) {
222-
onStateUpdated();
202+
onStateChanged();
223203
}
224204

225205
WatchUi.requestUpdate();

source/main/user-interface/features/sitemap/menu-items/container/ContainerMenuItem.mc

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,9 @@ class ContainerMenuItem extends BaseWidgetMenuItem {
3030
} );
3131
}
3232

33-
// Updates the menu item
34-
public function updateWidget( sitemapWidget as SitemapWidget ) as Void {
35-
BaseWidgetMenuItem.updateWidget( sitemapWidget );
36-
if( ! ( sitemapWidget instanceof SitemapFrame || sitemapWidget instanceof SitemapGroup ) ) {
37-
throw new GeneralException( "Sitemap element '" + sitemapWidget.getLabel() + "' was passed into ContainerMenuItem but is of a different type" );
38-
}
39-
setStateTextResponsive( sitemapWidget.getDisplayStateOrNull() );
33+
// When the state changes, change the displayed state text
34+
public function onStateChanged() as Void {
35+
setStateTextResponsive( getSitemapWidget().getDisplayStateOrNull() );
4036
}
37+
4138
}

0 commit comments

Comments
 (0)