diff --git a/Classes/AssistantView.m b/Classes/AssistantView.m index 41c3f548d..b63ba79d9 100644 --- a/Classes/AssistantView.m +++ b/Classes/AssistantView.m @@ -726,29 +726,4 @@ void assistant_validation_tested(LinphoneAccountCreator *creator, LinphoneAccoun [PhoneMainView.instance changeCurrentView:DialerView.compositeViewDescription]; } -// TODO: remove that! -#pragma mark - TPMultiLayoutViewController Functions - -- (NSDictionary *)attributesForView:(UIView *)view { - NSMutableDictionary *attributes = [NSMutableDictionary dictionary]; - [attributes setObject:[NSValue valueWithCGRect:view.frame] forKey:@"frame"]; - [attributes setObject:[NSValue valueWithCGRect:view.bounds] forKey:@"bounds"]; - if ([view isKindOfClass:[UIButton class]]) { - UIButton *button = (UIButton *)view; - [LinphoneUtils buttonMultiViewAddAttributes:attributes button:button]; - } - [attributes setObject:[NSNumber numberWithInteger:view.autoresizingMask] forKey:@"autoresizingMask"]; - return attributes; -} - -- (void)applyAttributes:(NSDictionary *)attributes toView:(UIView *)view { - view.frame = [[attributes objectForKey:@"frame"] CGRectValue]; - view.bounds = [[attributes objectForKey:@"bounds"] CGRectValue]; - if ([view isKindOfClass:[UIButton class]]) { - UIButton *button = (UIButton *)view; - [LinphoneUtils buttonMultiViewApplyAttributes:attributes button:button]; - } - view.autoresizingMask = [[attributes objectForKey:@"autoresizingMask"] integerValue]; -} - @end diff --git a/Classes/Base.lproj/CallView.xib b/Classes/Base.lproj/CallView.xib index fd1c1707f..268b67b09 100644 --- a/Classes/Base.lproj/CallView.xib +++ b/Classes/Base.lproj/CallView.xib @@ -11,6 +11,8 @@ + + @@ -688,6 +690,30 @@ + + + + + + + + + + + + + + @@ -1320,6 +1346,30 @@ + + + + + + + + + + + + + + @@ -1357,6 +1407,7 @@ + diff --git a/Classes/CallIncomingView.m b/Classes/CallIncomingView.m index 58b24c2b9..4b2572b71 100644 --- a/Classes/CallIncomingView.m +++ b/Classes/CallIncomingView.m @@ -130,30 +130,4 @@ static UICompositeViewDescription *compositeDescription = nil; [_delegate incomingCallAccepted:_call evenWithVideo:NO]; } -#pragma mark - TPMultiLayoutViewController Functions - -- (NSDictionary *)attributesForView:(UIView *)view { - NSMutableDictionary *attributes = [NSMutableDictionary dictionary]; - - [attributes setObject:[NSValue valueWithCGRect:view.frame] forKey:@"frame"]; - [attributes setObject:[NSValue valueWithCGRect:view.bounds] forKey:@"bounds"]; - if ([view isKindOfClass:[UIButton class]]) { - UIButton *button = (UIButton *)view; - [LinphoneUtils buttonMultiViewAddAttributes:attributes button:button]; - } - [attributes setObject:[NSNumber numberWithInteger:view.autoresizingMask] forKey:@"autoresizingMask"]; - - return attributes; -} - -- (void)applyAttributes:(NSDictionary *)attributes toView:(UIView *)view { - view.frame = [[attributes objectForKey:@"frame"] CGRectValue]; - view.bounds = [[attributes objectForKey:@"bounds"] CGRectValue]; - if ([view isKindOfClass:[UIButton class]]) { - UIButton *button = (UIButton *)view; - [LinphoneUtils buttonMultiViewApplyAttributes:attributes button:button]; - } - view.autoresizingMask = [[attributes objectForKey:@"autoresizingMask"] integerValue]; -} - @end diff --git a/Classes/CallView.h b/Classes/CallView.h index 864144edd..9647eee25 100644 --- a/Classes/CallView.h +++ b/Classes/CallView.h @@ -32,6 +32,7 @@ #import "UIHangUpButton.h" #import "UIDigitButton.h" #import "UIRoundedImageView.h" +#import "UIBouncingView.h" @class VideoView; @@ -71,6 +72,8 @@ @property(nonatomic, strong) IBOutlet UIButton *optionsTransferButton; @property(nonatomic, strong) IBOutlet UIToggleButton *numpadButton; @property(weak, nonatomic) IBOutlet UIPauseButton *conferencePauseButton; +@property(weak, nonatomic) IBOutlet UIBouncingView *chatNotificationView; +@property(weak, nonatomic) IBOutlet UILabel *chatNotificationLabel; @property(weak, nonatomic) IBOutlet UIView *bottomBar; @property(nonatomic, strong) IBOutlet UIDigitButton *oneButton; diff --git a/Classes/CallView.m b/Classes/CallView.m index 0a3af1846..346050f8a 100644 --- a/Classes/CallView.m +++ b/Classes/CallView.m @@ -112,6 +112,12 @@ static UICompositeViewDescription *compositeDescription = nil; - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(messageReceived:) + name:kLinphoneMessageReceived + object:nil]; + [self updateUnreadMessage:FALSE]; + // Update on show LinphoneCall *call = linphone_core_get_current_call([LinphoneManager getLc]); LinphoneCallState state = (call != NULL) ? linphone_call_get_state(call) : 0; @@ -217,6 +223,7 @@ static UICompositeViewDescription *compositeDescription = nil; - (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation { [super didRotateFromInterfaceOrientation:fromInterfaceOrientation]; + [self updateUnreadMessage:NO]; [self previewTouchLift]; [self showStatusBar:!videoShown || (_nameLabel.alpha > 0.f)]; } @@ -876,4 +883,17 @@ static void hideSpinner(LinphoneCall *call, void *user_data) { }]; } +#pragma mark - Bounce +- (void)messageReceived:(NSNotification *)notif { + [self updateUnreadMessage:TRUE]; +} +- (void)updateUnreadMessage:(BOOL)appear { + int unreadMessage = [LinphoneManager unreadMessageCount]; + if (unreadMessage > 0) { + _chatNotificationLabel.text = [NSString stringWithFormat:@"%i", unreadMessage]; + [_chatNotificationView startAnimating:appear]; + } else { + [_chatNotificationView stopAnimating:appear]; + } +} @end diff --git a/Classes/LinphoneUI/Base.lproj/TabBarView.xib b/Classes/LinphoneUI/Base.lproj/TabBarView.xib index c7a620ad7..c8f03344a 100644 --- a/Classes/LinphoneUI/Base.lproj/TabBarView.xib +++ b/Classes/LinphoneUI/Base.lproj/TabBarView.xib @@ -89,7 +89,7 @@ - + @@ -113,7 +113,7 @@ - + @@ -198,7 +198,7 @@ - + @@ -222,7 +222,7 @@ - + diff --git a/Classes/LinphoneUI/Base.lproj/UIChatCell.xib b/Classes/LinphoneUI/Base.lproj/UIChatCell.xib index a751d05ee..a3782140b 100644 --- a/Classes/LinphoneUI/Base.lproj/UIChatCell.xib +++ b/Classes/LinphoneUI/Base.lproj/UIChatCell.xib @@ -11,7 +11,8 @@ - + + @@ -51,17 +52,30 @@ - + + @@ -71,6 +85,6 @@ - + diff --git a/Classes/LinphoneUI/StatusBarView.m b/Classes/LinphoneUI/StatusBarView.m index 076ec8207..4d5f759ee 100644 --- a/Classes/LinphoneUI/StatusBarView.m +++ b/Classes/LinphoneUI/StatusBarView.m @@ -345,22 +345,4 @@ linphone_core_refresh_registers([LinphoneManager getLc]); } -#pragma mark - TPMultiLayoutViewController Functions - -- (NSDictionary *)attributesForView:(UIView *)view { - NSMutableDictionary *attributes = [NSMutableDictionary dictionary]; - - [attributes setObject:[NSValue valueWithCGRect:view.frame] forKey:@"frame"]; - [attributes setObject:[NSValue valueWithCGRect:view.bounds] forKey:@"bounds"]; - [attributes setObject:[NSNumber numberWithInteger:view.autoresizingMask] forKey:@"autoresizingMask"]; - - return attributes; -} - -- (void)applyAttributes:(NSDictionary *)attributes toView:(UIView *)view { - view.frame = [[attributes objectForKey:@"frame"] CGRectValue]; - view.bounds = [[attributes objectForKey:@"bounds"] CGRectValue]; - view.autoresizingMask = [[attributes objectForKey:@"autoresizingMask"] integerValue]; -} - @end diff --git a/Classes/LinphoneUI/TabBarView.h b/Classes/LinphoneUI/TabBarView.h index 193fef6a6..9f249378b 100644 --- a/Classes/LinphoneUI/TabBarView.h +++ b/Classes/LinphoneUI/TabBarView.h @@ -19,6 +19,7 @@ #import #import "TPMultiLayoutViewController.h" +#import "UIBouncingView.h" @interface TabBarView : TPMultiLayoutViewController { } @@ -27,10 +28,10 @@ @property(nonatomic, strong) IBOutlet UIButton *contactsButton; @property(nonatomic, strong) IBOutlet UIButton *dialerButton; @property(nonatomic, strong) IBOutlet UIButton *chatButton; -@property(nonatomic, strong) IBOutlet UIView *historyNotificationView; -@property(nonatomic, strong) IBOutlet UILabel *historyNotificationLabel; -@property(nonatomic, strong) IBOutlet UIView *chatNotificationView; +@property(nonatomic, strong) IBOutlet UIBouncingView *historyNotificationView; +@property(nonatomic, strong) IBOutlet UIBouncingView *chatNotificationView; @property(nonatomic, strong) IBOutlet UILabel *chatNotificationLabel; +@property(nonatomic, strong) IBOutlet UILabel *historyNotificationLabel; @property(weak, nonatomic) IBOutlet UIImageView *selectedButtonImage; - (IBAction)onHistoryClick:(id)event; diff --git a/Classes/LinphoneUI/TabBarView.m b/Classes/LinphoneUI/TabBarView.m index 6ffe371a4..f061214d2 100644 --- a/Classes/LinphoneUI/TabBarView.m +++ b/Classes/LinphoneUI/TabBarView.m @@ -19,23 +19,9 @@ #import "TabBarView.h" #import "PhoneMainView.h" -#import "CAAnimation+Blocks.h" @implementation TabBarView -static NSString *const kBounceAnimation = @"bounce"; -static NSString *const kAppearAnimation = @"appear"; -static NSString *const kDisappearAnimation = @"disappear"; - -@synthesize historyButton; -@synthesize contactsButton; -@synthesize dialerButton; -@synthesize chatButton; -@synthesize historyNotificationView; -@synthesize historyNotificationLabel; -@synthesize chatNotificationView; -@synthesize chatNotificationLabel; - #pragma mark - ViewController Functions - (void)viewWillAppear:(BOOL)animated { @@ -53,10 +39,6 @@ static NSString *const kDisappearAnimation = @"disappear"; selector:@selector(messageReceived:) name:kLinphoneMessageReceived object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(settingsUpdate:) - name:kLinphoneSettingsUpdate - object:nil]; [self update:FALSE]; } @@ -65,40 +47,12 @@ static NSString *const kDisappearAnimation = @"disappear"; [[NSNotificationCenter defaultCenter] removeObserver:self]; } -- (void)viewDidLoad { - [super viewDidLoad]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(applicationWillEnterForeground:) - name:UIApplicationWillEnterForegroundNotification - object:nil]; -} - -- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation - duration:(NSTimeInterval)duration { - // Force the animations - [[self.view layer] removeAllAnimations]; - [historyNotificationView.layer setTransform:CATransform3DIdentity]; - [chatNotificationView.layer setTransform:CATransform3DIdentity]; -} - - (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation { - [chatNotificationView setHidden:TRUE]; - [historyNotificationView setHidden:TRUE]; [self update:FALSE]; } #pragma mark - Event Functions -- (void)applicationWillEnterForeground:(NSNotification *)notif { - // Force the animations - [[self.view layer] removeAllAnimations]; - [historyNotificationView.layer setTransform:CATransform3DIdentity]; - [chatNotificationView.layer setTransform:CATransform3DIdentity]; - [chatNotificationView setHidden:TRUE]; - [historyNotificationView setHidden:TRUE]; - [self update:FALSE]; -} - - (void)callUpdate:(NSNotification *)notif { // LinphoneCall *call = [[notif.userInfo objectForKey: @"call"] pointerValue]; // LinphoneCallState state = [[notif.userInfo objectForKey: @"state"] intValue]; @@ -112,23 +66,6 @@ static NSString *const kDisappearAnimation = @"disappear"; } } -- (void)settingsUpdate:(NSNotification *)notif { - if ([[LinphoneManager instance] lpConfigBoolForKey:@"animations_preference"] == false) { - [self stopBounceAnimation:kBounceAnimation target:chatNotificationView]; - chatNotificationView.layer.transform = CATransform3DIdentity; - [self stopBounceAnimation:kBounceAnimation target:historyNotificationView]; - historyNotificationView.layer.transform = CATransform3DIdentity; - } else { - if (![chatNotificationView isHidden] && [chatNotificationView.layer animationForKey:kBounceAnimation] == nil) { - [self startBounceAnimation:kBounceAnimation target:chatNotificationView]; - } - if (![historyNotificationView isHidden] && - [historyNotificationView.layer animationForKey:kBounceAnimation] == nil) { - [self startBounceAnimation:kBounceAnimation target:historyNotificationView]; - } - } -} - - (void)messageReceived:(NSNotification *)notif { [self updateUnreadMessage:TRUE]; } @@ -144,112 +81,60 @@ static NSString *const kDisappearAnimation = @"disappear"; - (void)updateUnreadMessage:(BOOL)appear { int unreadMessage = [LinphoneManager unreadMessageCount]; if (unreadMessage > 0) { - if ([chatNotificationView isHidden]) { - [chatNotificationView setHidden:FALSE]; - if ([[LinphoneManager instance] lpConfigBoolForKey:@"animations_preference"] == true) { - if (appear) { - [self appearAnimation:kAppearAnimation - target:chatNotificationView - completion:^(BOOL finished) { - [self startBounceAnimation:kBounceAnimation target:chatNotificationView]; - [chatNotificationView.layer removeAnimationForKey:kAppearAnimation]; - }]; - } else { - [self startBounceAnimation:kBounceAnimation target:chatNotificationView]; - } - } - } - [chatNotificationLabel setText:[NSString stringWithFormat:@"%i", unreadMessage]]; + _chatNotificationLabel.text = [NSString stringWithFormat:@"%i", unreadMessage]; + [_chatNotificationView startAnimating:appear]; } else { - if (![chatNotificationView isHidden]) { - [self stopBounceAnimation:kBounceAnimation target:chatNotificationView]; - if (appear) { - [self disappearAnimation:kDisappearAnimation - target:chatNotificationView - completion:^(BOOL finished) { - [chatNotificationView setHidden:TRUE]; - [chatNotificationView.layer removeAnimationForKey:kDisappearAnimation]; - }]; - } else { - [chatNotificationView setHidden:TRUE]; - } - } + [_chatNotificationView stopAnimating:appear]; } } - (void)updateMissedCall:(int)missedCall appear:(BOOL)appear { if (missedCall > 0) { - if ([historyNotificationView isHidden]) { - [historyNotificationView setHidden:FALSE]; - if ([[LinphoneManager instance] lpConfigBoolForKey:@"animations_preference"] == true) { - if (appear) { - [self appearAnimation:kAppearAnimation - target:historyNotificationView - completion:^(BOOL finished) { - [self startBounceAnimation:kBounceAnimation target:historyNotificationView]; - [historyNotificationView.layer removeAnimationForKey:kAppearAnimation]; - }]; - } else { - [self startBounceAnimation:kBounceAnimation target:historyNotificationView]; - } - } - } - [historyNotificationLabel setText:[NSString stringWithFormat:@"%i", missedCall]]; + _historyNotificationLabel.text = [NSString stringWithFormat:@"%i", missedCall]; + [_historyNotificationView startAnimating:appear]; } else { - if (![historyNotificationView isHidden]) { - [self stopBounceAnimation:kBounceAnimation target:historyNotificationView]; - if (appear) { - [self disappearAnimation:kDisappearAnimation - target:historyNotificationView - completion:^(BOOL finished) { - [historyNotificationView setHidden:TRUE]; - [historyNotificationView.layer removeAnimationForKey:kDisappearAnimation]; - }]; - } else { - [historyNotificationView setHidden:TRUE]; - } - } + [_historyNotificationView stopAnimating:appear]; } } - (void)updateSelectedButton:(UICompositeViewDescription *)view { - historyButton.selected = [view equal:HistoryListView.compositeViewDescription] || - [view equal:HistoryDetailsView.compositeViewDescription]; - contactsButton.selected = [view equal:ContactsListView.compositeViewDescription] || - [view equal:ContactDetailsView.compositeViewDescription]; - dialerButton.selected = [view equal:DialerView.compositeViewDescription]; - chatButton.selected = [view equal:ChatsListView.compositeViewDescription] || - [view equal:ChatConversationCreateView.compositeViewDescription] || - [view equal:ChatConversationView.compositeViewDescription]; + _historyButton.selected = [view equal:HistoryListView.compositeViewDescription] || + [view equal:HistoryDetailsView.compositeViewDescription]; + _contactsButton.selected = [view equal:ContactsListView.compositeViewDescription] || + [view equal:ContactDetailsView.compositeViewDescription]; + _dialerButton.selected = [view equal:DialerView.compositeViewDescription]; + _chatButton.selected = [view equal:ChatsListView.compositeViewDescription] || + [view equal:ChatConversationCreateView.compositeViewDescription] || + [view equal:ChatConversationView.compositeViewDescription]; CGRect selectedNewFrame = _selectedButtonImage.frame; if ([self viewIsCurrentlyPortrait]) { selectedNewFrame.origin.x = - (historyButton.selected - ? historyButton.frame.origin.x - : (contactsButton.selected - ? contactsButton.frame.origin.x - : (dialerButton.selected - ? dialerButton.frame.origin.x - : (chatButton.selected - ? chatButton.frame.origin.x + (_historyButton.selected + ? _historyButton.frame.origin.x + : (_contactsButton.selected + ? _contactsButton.frame.origin.x + : (_dialerButton.selected + ? _dialerButton.frame.origin.x + : (_chatButton.selected + ? _chatButton.frame.origin.x : -selectedNewFrame.size.width /*hide it if none is selected*/)))); } else { selectedNewFrame.origin.y = - (historyButton.selected - ? historyButton.frame.origin.y - : (contactsButton.selected - ? contactsButton.frame.origin.y - : (dialerButton.selected - ? dialerButton.frame.origin.y - : (chatButton.selected - ? chatButton.frame.origin.y + (_historyButton.selected + ? _historyButton.frame.origin.y + : (_contactsButton.selected + ? _contactsButton.frame.origin.y + : (_dialerButton.selected + ? _dialerButton.frame.origin.y + : (_chatButton.selected + ? _chatButton.frame.origin.y : -selectedNewFrame.size.height /*hide it if none is selected*/)))); } - + CGFloat delay = [[LinphoneManager instance] lpConfigBoolForKey:@"animations_preference"] ? 0.3 : 0; [UIView animateWithDuration:delay animations:^{ _selectedButtonImage.frame = selectedNewFrame; - + }]; } @@ -279,73 +164,4 @@ static NSString *const kDisappearAnimation = @"disappear"; [PhoneMainView.instance changeCurrentView:ChatsListView.compositeViewDescription]; } -#pragma mark - Animation - -- (void)appearAnimation:(NSString *)animationID target:(UIView *)target completion:(void (^)(BOOL finished))completion { - CABasicAnimation *appear = [CABasicAnimation animationWithKeyPath:@"transform.scale"]; - appear.duration = 0.4; - appear.fromValue = [NSNumber numberWithDouble:0.0f]; - appear.toValue = [NSNumber numberWithDouble:1.0f]; - appear.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]; - appear.fillMode = kCAFillModeForwards; - appear.removedOnCompletion = NO; - [appear setCompletion:completion]; - [target.layer addAnimation:appear forKey:animationID]; -} - -- (void)disappearAnimation:(NSString *)animationID - target:(UIView *)target - completion:(void (^)(BOOL finished))completion { - CABasicAnimation *disappear = [CABasicAnimation animationWithKeyPath:@"transform.scale"]; - disappear.duration = 0.4; - disappear.fromValue = [NSNumber numberWithDouble:1.0f]; - disappear.toValue = [NSNumber numberWithDouble:0.0f]; - disappear.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]; - disappear.fillMode = kCAFillModeForwards; - disappear.removedOnCompletion = NO; - [disappear setCompletion:completion]; - [target.layer addAnimation:disappear forKey:animationID]; -} - -- (void)startBounceAnimation:(NSString *)animationID target:(UIView *)target { - CABasicAnimation *bounce = [CABasicAnimation animationWithKeyPath:@"transform.translation.y"]; - bounce.duration = 0.3; - bounce.fromValue = [NSNumber numberWithDouble:0.0f]; - bounce.toValue = [NSNumber numberWithDouble:8.0f]; - bounce.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]; - bounce.autoreverses = TRUE; - bounce.repeatCount = HUGE_VALF; - [target.layer addAnimation:bounce forKey:animationID]; -} - -- (void)stopBounceAnimation:(NSString *)animationID target:(UIView *)target { - [target.layer removeAnimationForKey:animationID]; -} - -#pragma mark - TPMultiLayoutViewController Functions - -- (NSDictionary *)attributesForView:(UIView *)view { - NSMutableDictionary *attributes = [NSMutableDictionary dictionary]; - - [attributes setObject:[NSValue valueWithCGRect:view.frame] forKey:@"frame"]; - [attributes setObject:[NSValue valueWithCGRect:view.bounds] forKey:@"bounds"]; - if ([view isKindOfClass:[UIButton class]]) { - UIButton *button = (UIButton *)view; - [LinphoneUtils buttonMultiViewAddAttributes:attributes button:button]; - } - [attributes setObject:[NSNumber numberWithInteger:view.autoresizingMask] forKey:@"autoresizingMask"]; - - return attributes; -} - -- (void)applyAttributes:(NSDictionary *)attributes toView:(UIView *)view { - view.frame = [[attributes objectForKey:@"frame"] CGRectValue]; - view.bounds = [[attributes objectForKey:@"bounds"] CGRectValue]; - if ([view isKindOfClass:[UIButton class]]) { - UIButton *button = (UIButton *)view; - [LinphoneUtils buttonMultiViewApplyAttributes:attributes button:button]; - } - view.autoresizingMask = [[attributes objectForKey:@"autoresizingMask"] integerValue]; -} - @end diff --git a/Classes/LinphoneUI/UIBouncingView.h b/Classes/LinphoneUI/UIBouncingView.h new file mode 100644 index 000000000..34d77f963 --- /dev/null +++ b/Classes/LinphoneUI/UIBouncingView.h @@ -0,0 +1,16 @@ +// +// UIBouncingView.h +// linphone +// +// Created by Gautier Pelloux-Prayer on 11/12/15. +// +// + +#import + +@interface UIBouncingView : UIView + +- (void)startAnimating:(BOOL)animated; +- (void)stopAnimating:(BOOL)animated; + +@end diff --git a/Classes/LinphoneUI/UIBouncingView.m b/Classes/LinphoneUI/UIBouncingView.m new file mode 100644 index 000000000..60e4ce007 --- /dev/null +++ b/Classes/LinphoneUI/UIBouncingView.m @@ -0,0 +1,123 @@ +// +// UIBouncingView.m +// linphone +// +// Created by Gautier Pelloux-Prayer on 11/12/15. +// +// + +#import "UIBouncingView.h" + +#import "CAAnimation+Blocks.h" +#import "Utils.h" + +static NSString *const kBounceAnimation = @"bounce"; +static NSString *const kAppearAnimation = @"appear"; +static NSString *const kDisappearAnimation = @"disappear"; + +@implementation UIBouncingView + +INIT_WITH_COMMON { + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(settingsUpdate:) + name:kLinphoneSettingsUpdate + object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(applicationWillEnterForeground:) + name:UIApplicationWillEnterForegroundNotification + object:nil]; + return self; +} + +- (void)settingsUpdate:(NSNotification *)notif { + if ([[LinphoneManager instance] lpConfigBoolForKey:@"animations_preference"] == false) { + [self stopBounceAnimation:kBounceAnimation target:self]; + } else { + if (![self isHidden] && [self.layer animationForKey:kBounceAnimation] == nil) { + [self startBounceAnimation:kBounceAnimation target:self]; + } + } +} + +- (void)applicationWillEnterForeground:(NSNotification *)notif { + // Force the animations + if (!self.isHidden) { + [self startAnimating:NO]; + } else { + [self stopAnimating:NO]; + } +} + +#pragma mark - Animation + +- (void)appearAnimation:(NSString *)animationID target:(UIView *)target completion:(void (^)(BOOL finished))completion { + CABasicAnimation *appear = [CABasicAnimation animationWithKeyPath:@"transform.scale"]; + appear.duration = 0.4; + appear.fromValue = [NSNumber numberWithDouble:0.0f]; + appear.toValue = [NSNumber numberWithDouble:1.0f]; + appear.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]; + appear.fillMode = kCAFillModeForwards; + appear.removedOnCompletion = NO; + [appear setCompletion:completion]; + [target.layer addAnimation:appear forKey:animationID]; +} + +- (void)disappearAnimation:(NSString *)animationID + target:(UIView *)target + completion:(void (^)(BOOL finished))completion { + CABasicAnimation *disappear = [CABasicAnimation animationWithKeyPath:@"transform.scale"]; + disappear.duration = 0.4; + disappear.fromValue = [NSNumber numberWithDouble:1.0f]; + disappear.toValue = [NSNumber numberWithDouble:0.0f]; + disappear.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]; + disappear.fillMode = kCAFillModeForwards; + disappear.removedOnCompletion = NO; + [disappear setCompletion:completion]; + [target.layer addAnimation:disappear forKey:animationID]; +} + +- (void)startBounceAnimation:(NSString *)animationID target:(UIView *)target { + CABasicAnimation *bounce = [CABasicAnimation animationWithKeyPath:@"transform.translation.y"]; + bounce.duration = 0.3; + bounce.fromValue = [NSNumber numberWithDouble:0.0f]; + bounce.toValue = [NSNumber numberWithDouble:8.0f]; + bounce.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]; + bounce.autoreverses = TRUE; + bounce.repeatCount = HUGE_VALF; + [target.layer addAnimation:bounce forKey:animationID]; +} + +- (void)stopBounceAnimation:(NSString *)animationID target:(UIView *)target { + [target.layer removeAnimationForKey:animationID]; +} + +- (void)startAnimating:(BOOL)animated { + [self setHidden:FALSE]; + if ([[LinphoneManager instance] lpConfigBoolForKey:@"animations_preference"] == true) { + if (animated) { + [self appearAnimation:kAppearAnimation + target:self + completion:^(BOOL finished) { + [self startBounceAnimation:kBounceAnimation target:self]; + [self.layer removeAnimationForKey:kAppearAnimation]; + }]; + } else { + [self startBounceAnimation:kBounceAnimation target:self]; + } + } +} + +- (void)stopAnimating:(BOOL)animated { + [self stopBounceAnimation:kBounceAnimation target:self]; + if (animated) { + [self disappearAnimation:kDisappearAnimation + target:self + completion:^(BOOL finished) { + [self setHidden:TRUE]; + [self.layer removeAnimationForKey:kDisappearAnimation]; + }]; + } else { + [self setHidden:TRUE]; + } +} +@end diff --git a/Classes/LinphoneUI/UIChatCell.h b/Classes/LinphoneUI/UIChatCell.h index e2d9257a7..6c8415b17 100644 --- a/Classes/LinphoneUI/UIChatCell.h +++ b/Classes/LinphoneUI/UIChatCell.h @@ -21,6 +21,7 @@ #import "UIRoundedImageView.h" #import "UIIconButton.h" +#import "UIBouncingView.h" #include "linphone/linphonecore.h" @@ -33,6 +34,8 @@ @property (nonatomic, strong) IBOutlet UILabel* chatContentLabel; @property(weak, nonatomic) IBOutlet UILabel *chatLatestTimeLabel; @property(weak, nonatomic) IBOutlet UIIconButton *unreadCountButton; +@property(weak, nonatomic) IBOutlet UIBouncingView *unreadCountView; +@property(weak, nonatomic) IBOutlet UILabel *unreadCountLabel; - (id)initWithIdentifier:(NSString*)identifier; diff --git a/Classes/LinphoneUI/UIChatCell.m b/Classes/LinphoneUI/UIChatCell.m index 4f1c3d6d9..ccfb9b78b 100644 --- a/Classes/LinphoneUI/UIChatCell.m +++ b/Classes/LinphoneUI/UIChatCell.m @@ -95,8 +95,12 @@ } int count = linphone_chat_room_get_unread_messages_count(chatRoom); - [_unreadCountButton setTitle:[NSString stringWithFormat:@"%i", count] forState:UIControlStateNormal]; - _unreadCountButton.hidden = (count <= 0); + _unreadCountLabel.text = [NSString stringWithFormat:@"%i", count]; + if (count > 0) { + [_unreadCountView startAnimating:YES]; + } else { + [_unreadCountView stopAnimating:YES]; + } UIFont *addressFont = (count <= 0) ? [UIFont systemFontOfSize:25] : [UIFont boldSystemFontOfSize:25]; _addressLabel.font = addressFont; diff --git a/linphone.xcodeproj/project.pbxproj b/linphone.xcodeproj/project.pbxproj index 205b02aaf..7d862e665 100755 --- a/linphone.xcodeproj/project.pbxproj +++ b/linphone.xcodeproj/project.pbxproj @@ -469,6 +469,7 @@ 63730FB91C07570C00AD7A74 /* waiting_time.png in Resources */ = {isa = PBXBuildFile; fileRef = 63730E511C07570C00AD7A74 /* waiting_time.png */; }; 63730FBA1C07570C00AD7A74 /* waiting_time@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 63730E521C07570C00AD7A74 /* waiting_time@2x.png */; }; 6377AC801BDE4069007F7625 /* UIBackToCallButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 6377AC7F1BDE4069007F7625 /* UIBackToCallButton.m */; }; + 6381DA7D1C1AD5EA00DF3BBD /* UIBouncingView.m in Sources */ = {isa = PBXBuildFile; fileRef = 6381DA7C1C1AD5EA00DF3BBD /* UIBouncingView.m */; }; 639CEAFD1A1DF4D9004DE38F /* StatusBarView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 639CEAFF1A1DF4D9004DE38F /* StatusBarView.xib */; }; 639CEB001A1DF4E4004DE38F /* UIHistoryCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 639CEB021A1DF4E4004DE38F /* UIHistoryCell.xib */; }; 639CEB031A1DF4EB004DE38F /* UICompositeView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 639CEB051A1DF4EB004DE38F /* UICompositeView.xib */; }; @@ -1286,6 +1287,8 @@ 63730E521C07570C00AD7A74 /* waiting_time@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "waiting_time@2x.png"; sourceTree = ""; }; 6377AC7E1BDE4068007F7625 /* UIBackToCallButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UIBackToCallButton.h; sourceTree = ""; }; 6377AC7F1BDE4069007F7625 /* UIBackToCallButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIBackToCallButton.m; sourceTree = ""; }; + 6381DA7B1C1AD5EA00DF3BBD /* UIBouncingView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UIBouncingView.h; sourceTree = ""; }; + 6381DA7C1C1AD5EA00DF3BBD /* UIBouncingView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIBouncingView.m; sourceTree = ""; }; 639CEAFE1A1DF4D9004DE38F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/StatusBarView.xib; sourceTree = ""; }; 639CEB011A1DF4E4004DE38F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/UIHistoryCell.xib; sourceTree = ""; }; 639CEB041A1DF4EB004DE38F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/UICompositeView.xib; sourceTree = ""; }; @@ -1912,6 +1915,8 @@ D3196D3D15A32BD8007FEEBA /* UITransferButton.m */, 340751E5150F38FC00B89C47 /* UIVideoButton.h */, 340751E6150F38FD00B89C47 /* UIVideoButton.m */, + 6381DA7B1C1AD5EA00DF3BBD /* UIBouncingView.h */, + 6381DA7C1C1AD5EA00DF3BBD /* UIBouncingView.m */, ); path = LinphoneUI; sourceTree = ""; @@ -3469,6 +3474,7 @@ 63BC49E21BA2CDFC004EC273 /* UICallPausedCell.m in Sources */, D37EE162160377D7003608A6 /* DTActionSheet.m in Sources */, D306459E1611EC2A00BB571E /* UILoadingImageView.m in Sources */, + 6381DA7D1C1AD5EA00DF3BBD /* UIBouncingView.m in Sources */, D37E3ECD1619C27A0087659A /* CAAnimation+Blocks.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0;