summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorharisheb <ebuser@EBUsers-MacBook-Air.local>2017-09-27 13:51:11 -0400
committerharisheb <ebuser@EBUsers-MacBook-Air.local>2017-09-27 13:51:11 -0400
commit3c211b33f6b4c8e31b601006bed92acedf3bf9c3 (patch)
treec4385df78d4a0e5fa33f1e06155a77c06129d8d7
parentc0284225c825bbbe3a140bae46f65b5cd52cddf9 (diff)
downloadsdl_ios-3c211b33f6b4c8e31b601006bed92acedf3bf9c3.tar.gz
Fixed review comments. Modified implementation to handle UIButtons. Added more test cases.
-rw-r--r--SmartDeviceLink/SDLHapticInterface.h6
-rw-r--r--SmartDeviceLink/SDLHapticManager.m25
-rw-r--r--SmartDeviceLink/SDLNotificationConstants.h2
-rw-r--r--SmartDeviceLink/SDLNotificationConstants.m2
-rw-r--r--SmartDeviceLinkTests/ProxySpecs/SDLHapticManagerSpec.m162
5 files changed, 173 insertions, 24 deletions
diff --git a/SmartDeviceLink/SDLHapticInterface.h b/SmartDeviceLink/SDLHapticInterface.h
index ec1453c3d..987f10e7c 100644
--- a/SmartDeviceLink/SDLHapticInterface.h
+++ b/SmartDeviceLink/SDLHapticInterface.h
@@ -15,15 +15,15 @@ NS_ASSUME_NONNULL_BEGIN
@protocol SDLHapticInterface <NSObject>
/**
- Initializes haptic interface. After initializing the application must call updateInterfaceLayout to process the UIWindow. Application must update later view changes in the window by sending SDLDidProjectionViewUpdate notification.
+ Initializes haptic interface. After initializing the application must call updateInterfaceLayout to process the UIWindow. Application must update later view changes in the window by sending SDLDidUpdateProjectionView notification.
- @param window UIWindow to initialize
+ @param window UIWindow to be stored in haptic interface
@param connectionManager Object of a class that implements ConnectionManagerType. This is used for RPC communication.
*/
- (instancetype)initWithWindow:(UIWindow *)window connectionManager:(id<SDLConnectionManagerType>)connectionManager;
/**
- updateInterfaceLayout crawls through the view hierarchy, updates and keep tracks of views to be reported through Haptic RPC. This function is automatically called when SDLDidProjectionViewUpdate notification is sent by the application.
+ updateInterfaceLayout crawls through the view hierarchy, updates and keep tracks of views to be reported through Haptic RPC. This function is automatically called when SDLDidUpdateProjectionView notification is sent by the application.
*/
- (void)updateInterfaceLayout;
diff --git a/SmartDeviceLink/SDLHapticManager.m b/SmartDeviceLink/SDLHapticManager.m
index f5a57a148..95c69454e 100644
--- a/SmartDeviceLink/SDLHapticManager.m
+++ b/SmartDeviceLink/SDLHapticManager.m
@@ -21,7 +21,7 @@ NS_ASSUME_NONNULL_BEGIN
/**
The projection window associated with the Haptic Manager
*/
-@property (nonatomic, strong) UIWindow *projectionWindow;
+@property (nonatomic, weak) UIWindow *projectionWindow;
/**
Array of focusable view objects extracted from the projection window
@@ -31,7 +31,7 @@ NS_ASSUME_NONNULL_BEGIN
/**
reference to SDLConnectionManager
*/
-@property (nonatomic, strong) id<SDLConnectionManagerType> connectionManager;
+@property (nonatomic, weak) id<SDLConnectionManagerType> connectionManager;
@end
@@ -45,7 +45,7 @@ NS_ASSUME_NONNULL_BEGIN
_projectionWindow = window;
_connectionManager = connectionManager;
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sdl_projectionViewUpdated:) name:SDLDidProjectionViewUpdate object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sdl_projectionViewUpdated:) name:SDLDidUpdateProjectionView object:nil];
return self;
}
@@ -75,11 +75,12 @@ NS_ASSUME_NONNULL_BEGIN
}
NSArray *focusableSubviews = [currentView.subviews filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(UIView * _Nullable evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) {
- return evaluatedObject.canBecomeFocused;
+ return (evaluatedObject.canBecomeFocused || [evaluatedObject isKindOfClass:[UIButton class]]);
}]];
+ BOOL isButton = [currentView isKindOfClass:[UIButton class]];
//if current view is focusable and it doesn't have any focusable sub views then add the cuurent view and return
- if (currentView.canBecomeFocused && focusableSubviews.count == 0) {
+ if ((currentView.canBecomeFocused || isButton) && focusableSubviews.count == 0) {
[self.focusableViews addObject:currentView];
return;
}
@@ -104,7 +105,7 @@ NS_ASSUME_NONNULL_BEGIN
NSMutableArray<SDLHapticRect *> *hapticRects = [[NSMutableArray alloc] init];
for (UIView *view in self.focusableViews) {
- CGPoint originOnScreen = [view.superview convertPoint:view.frame.origin toView:nil];
+ CGPoint originOnScreen = [self.projectionWindow convertPoint:view.frame.origin toView:nil];
CGRect convertedRect = {originOnScreen, view.bounds.size};
SDLRectangle* rect = [[SDLRectangle alloc] initWithCGRect:(convertedRect)];
// using the view index as the id field in SendHapticData request (should be guaranteed unique)
@@ -114,8 +115,7 @@ NS_ASSUME_NONNULL_BEGIN
}
SDLSendHapticData* hapticRPC = [[SDLSendHapticData alloc] initWithHapticRectData:hapticRects];
- [self.connectionManager sendManagerRequest:hapticRPC withResponseHandler:^(__kindof SDLRPCRequest * _Nullable request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error) {
- }];
+ [self.connectionManager sendManagerRequest:hapticRPC withResponseHandler:nil];
}
#pragma mark SDLHapticHitTester functions
@@ -136,14 +136,7 @@ NS_ASSUME_NONNULL_BEGIN
}
}
- if(selectedView != nil) {
- NSUInteger selectedViewIndex = [self.focusableViews indexOfObject:selectedView];
-
- if (selectedViewIndex != NSNotFound) {
- return self.focusableViews[selectedViewIndex];
- }
- }
- return nil;
+ return selectedView;
}
#pragma mark notifications
diff --git a/SmartDeviceLink/SDLNotificationConstants.h b/SmartDeviceLink/SDLNotificationConstants.h
index b6a6468f0..be7c0b2aa 100644
--- a/SmartDeviceLink/SDLNotificationConstants.h
+++ b/SmartDeviceLink/SDLNotificationConstants.h
@@ -82,7 +82,7 @@ extern SDLNotificationName const SDLTransportDidConnect;
extern SDLNotificationName const SDLDidReceiveError;
extern SDLNotificationName const SDLDidReceiveLockScreenIcon;
extern SDLNotificationName const SDLDidBecomeReady;
-extern SDLNotificationName const SDLDidProjectionViewUpdate;
+extern SDLNotificationName const SDLDidUpdateProjectionView;
/**
* NSNotification names associated with specific RPC responses.
diff --git a/SmartDeviceLink/SDLNotificationConstants.m b/SmartDeviceLink/SDLNotificationConstants.m
index 1cf3776da..813a07ca6 100644
--- a/SmartDeviceLink/SDLNotificationConstants.m
+++ b/SmartDeviceLink/SDLNotificationConstants.m
@@ -18,7 +18,7 @@ SDLNotificationName const SDLDidReceiveError = @"com.sdl.general.error";
SDLNotificationName const SDLDidReceiveLockScreenIcon = @"com.sdl.general.lockscreenIconReceived";
SDLNotificationName const SDLDidBecomeReady = @"com.sdl.notification.managerReady";
SDLNotificationName const SDLDidReceiveVehicleIconNotification = @"com.sdl.notification.vehicleIcon";
-SDLNotificationName const SDLDidProjectionViewUpdate = @"com.sdl.notification.projectionViewUpdate";
+SDLNotificationName const SDLDidUpdateProjectionView = @"com.sdl.notification.projectionViewUpdate";
#pragma mark - RPC Responses
SDLNotificationName const SDLDidReceiveAddCommandResponse = @"com.sdl.response.addCommand";
diff --git a/SmartDeviceLinkTests/ProxySpecs/SDLHapticManagerSpec.m b/SmartDeviceLinkTests/ProxySpecs/SDLHapticManagerSpec.m
index 387427b81..527fe8e44 100644
--- a/SmartDeviceLinkTests/ProxySpecs/SDLHapticManagerSpec.m
+++ b/SmartDeviceLinkTests/ProxySpecs/SDLHapticManagerSpec.m
@@ -33,13 +33,13 @@ SDLTouch* generateTouchEvent(int xCoord, int yCoord)
return firstTouch;
}
-bool compareRectangle(SDLRectangle *sdlRectangle, CGRect cgRect)
+BOOL compareRectangle(SDLRectangle *sdlRectangle, CGRect cgRect)
{
expect(sdlRectangle.x).to(equal(cgRect.origin.x));
expect(sdlRectangle.y).to(equal(cgRect.origin.y));
expect(sdlRectangle.width).to(equal(cgRect.size.width));
expect(sdlRectangle.height).to(equal(cgRect.size.height));
- return true;
+ return YES;
}
QuickSpecBegin(SDLHapticManagerSpec)
@@ -111,7 +111,33 @@ describe(@"the haptic manager", ^{
}
});
});
+
+ context(@"when initialized with single button view", ^{
+ beforeEach(^{
+ viewRect1 = CGRectMake(101, 101, 50, 50);
+ UIButton *button = [[UIButton alloc] initWithFrame:viewRect1];
+ [uiWindow addSubview:button];
+
+ hapticManager = [[SDLHapticManager alloc] initWithWindow:uiWindow connectionManager:sdlLifecycleManager];
+ [hapticManager updateInterfaceLayout];
+ });
+ it(@"should have one view", ^{
+ OCMVerify(sdlLifecycleManager);
+
+ int expectedCount = 1;
+ expect(sentHapticRequest.hapticRectData.count).to(equal(expectedCount));
+
+ if(sentHapticRequest.hapticRectData.count == expectedCount) {
+ NSArray<SDLHapticRect *> *hapticRectData = sentHapticRequest.hapticRectData;
+ SDLHapticRect *sdlhapticRect = hapticRectData[0];
+ SDLRectangle *sdlRect = sdlhapticRect.rect;
+
+ compareRectangle(sdlRect, viewRect1);
+ }
+ });
+ });
+
context(@"when initialized with no views and then updated with two additional views", ^{
beforeEach(^{
hapticManager = [[SDLHapticManager alloc] initWithWindow:uiWindow connectionManager:sdlLifecycleManager];
@@ -146,7 +172,81 @@ describe(@"the haptic manager", ^{
compareRectangle(sdlRect2, viewRect2);
}
});
- });
+ });
+
+ context(@"when initialized with nested views", ^{
+ beforeEach(^{
+ UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(101, 101, 50, 50)];
+ [uiViewController.view addSubview:textField];
+
+ viewRect1 = CGRectMake(110, 110, 10, 10);
+ UITextField *textField1 = [[UITextField alloc] initWithFrame:viewRect1];
+ [textField addSubview:textField1];
+
+ viewRect2 = CGRectMake(130, 130, 10, 10);
+ UITextField *textField2 = [[UITextField alloc] initWithFrame:viewRect2];
+ [textField addSubview:textField2];
+
+ hapticManager = [[SDLHapticManager alloc] initWithWindow:uiWindow connectionManager:sdlLifecycleManager];
+ [hapticManager updateInterfaceLayout];
+ });
+
+ it(@"should have only leaf views added", ^{
+ OCMVerify(sdlLifecycleManager);
+
+ int expectedCount = 2;
+ expect(sentHapticRequest.hapticRectData.count).to(equal(expectedCount));
+
+ if(sentHapticRequest.hapticRectData.count == expectedCount) {
+ NSArray<SDLHapticRect *> *hapticRectData = sentHapticRequest.hapticRectData;
+ SDLHapticRect *sdlhapticRect1 = hapticRectData[0];
+ SDLRectangle *sdlRect1 = sdlhapticRect1.rect;
+
+ SDLHapticRect *sdlhapticRect2 = hapticRectData[1];
+ SDLRectangle *sdlRect2 = sdlhapticRect2.rect;
+
+ compareRectangle(sdlRect1, viewRect1);
+ compareRectangle(sdlRect2, viewRect2);
+ }
+ });
+ });
+
+ context(@"when initialized with nested button views", ^{
+ beforeEach(^{
+ UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(101, 101, 50, 50)];
+ [uiViewController.view addSubview:button];
+
+ viewRect1 = CGRectMake(110, 110, 10, 10);
+ UIButton *button1 = [[UIButton alloc] initWithFrame:viewRect1];
+ [button addSubview:button1];
+
+ viewRect2 = CGRectMake(130, 130, 10, 10);
+ UITextField *textField2 = [[UITextField alloc] initWithFrame:viewRect2];
+ [button addSubview:textField2];
+
+ hapticManager = [[SDLHapticManager alloc] initWithWindow:uiWindow connectionManager:sdlLifecycleManager];
+ [hapticManager updateInterfaceLayout];
+ });
+
+ it(@"should have only leaf views added", ^{
+ OCMVerify(sdlLifecycleManager);
+
+ int expectedCount = 2;
+ expect(sentHapticRequest.hapticRectData.count).to(equal(expectedCount));
+
+ if(sentHapticRequest.hapticRectData.count == expectedCount) {
+ NSArray<SDLHapticRect *> *hapticRectData = sentHapticRequest.hapticRectData;
+ SDLHapticRect *sdlhapticRect1 = hapticRectData[0];
+ SDLRectangle *sdlRect1 = sdlhapticRect1.rect;
+
+ SDLHapticRect *sdlhapticRect2 = hapticRectData[1];
+ SDLRectangle *sdlRect2 = sdlhapticRect2.rect;
+
+ compareRectangle(sdlRect1, viewRect1);
+ compareRectangle(sdlRect2, viewRect2);
+ }
+ });
+ });
context(@"when initialized with two views and then updated with one view removed", ^{
beforeEach(^{
@@ -181,6 +281,42 @@ describe(@"the haptic manager", ^{
}
});
});
+
+ context(@"when initialized with one view and notified after adding one more view", ^{
+ beforeEach(^{
+ viewRect1 = CGRectMake(101, 101, 50, 50);
+ UITextField *textField1 = [[UITextField alloc] initWithFrame:viewRect1];
+ [uiViewController.view addSubview:textField1];
+
+ hapticManager = [[SDLHapticManager alloc] initWithWindow:uiWindow connectionManager:sdlLifecycleManager];
+ [hapticManager updateInterfaceLayout];
+
+ viewRect2 = CGRectMake(201, 201, 50, 50);
+ UITextField *textField2 = [[UITextField alloc] initWithFrame:viewRect2];
+ [uiViewController.view addSubview:textField2];
+
+ [[NSNotificationCenter defaultCenter] postNotificationName:SDLDidUpdateProjectionView object:nil];
+ });
+
+ it(@"should have two views", ^{
+ OCMVerify(sdlLifecycleManager);
+
+ int expectedCount = 2;
+ expect(sentHapticRequest.hapticRectData.count).to(equal(expectedCount));
+
+ if(sentHapticRequest.hapticRectData.count == expectedCount) {
+ NSArray<SDLHapticRect *> *hapticRectData = sentHapticRequest.hapticRectData;
+ SDLHapticRect *sdlhapticRect1 = hapticRectData[0];
+ SDLRectangle *sdlRect1 = sdlhapticRect1.rect;
+
+ SDLHapticRect *sdlhapticRect2 = hapticRectData[1];
+ SDLRectangle *sdlRect2 = sdlhapticRect2.rect;
+
+ compareRectangle(sdlRect1, viewRect1);
+ compareRectangle(sdlRect2, viewRect2);
+ }
+ });
+ });
context(@"when touched inside a view", ^{
beforeEach(^{
@@ -207,6 +343,26 @@ describe(@"the haptic manager", ^{
});
});
+ context(@"when touched in overlapping views' area", ^{
+ beforeEach(^{
+ UITextField *textField1 = [[UITextField alloc] initWithFrame:CGRectMake(101, 101, 50, 50)];
+ [uiViewController.view addSubview:textField1];
+
+ UITextField *textField2 = [[UITextField alloc] initWithFrame:CGRectMake(126, 126, 50, 50)];
+ [uiViewController.view addSubview:textField2];
+
+ hapticManager = [[SDLHapticManager alloc] initWithWindow:uiWindow connectionManager:sdlLifecycleManager];
+ [hapticManager updateInterfaceLayout];
+ });
+
+ it(@"should return no view object", ^{
+ SDLTouch* collisionTouch = generateTouchEvent(130, 130);
+ UIView* view = [hapticManager viewForSDLTouch:collisionTouch];
+
+ expect(view).to(beNil());
+ });
+ });
+
context(@"when touched outside view boundary", ^{
beforeEach(^{
UITextField *textField1 = [[UITextField alloc] initWithFrame:CGRectMake(101, 101, 50, 50)];