diff --git a/LifeLog/LifeLog/HomeViewController.m b/LifeLog/LifeLog/HomeViewController.m index b98cfe7..4b7ddd1 100644 --- a/LifeLog/LifeLog/HomeViewController.m +++ b/LifeLog/LifeLog/HomeViewController.m @@ -235,20 +235,23 @@ static NSInteger numberTotal = 10000; // NSLog(@"%@", activityExtra.activity.startDate); [weakSelf.pedometer queryPedometerDataFromDate:activityExtra.activity.startDate toDate:activityExtra.endDate withHandler:^(CMPedometerData * _Nullable pedometerData, NSError * _Nullable error) { NSInteger numberStep = [pedometerData.numberOfSteps integerValue]; - + int mode = 1; if (activityExtra.activity.cycling) { // self.bike weakSelf.bike += numberStep; //NSLog(@"Step cycling"); + mode = 3; } else if (activityExtra.activity.walking) { // self.walking weakSelf.walking += numberStep; //NSLog(@"Step walking"); + mode = 1; } else if (activityExtra.activity.running) { weakSelf.running += numberStep; //NSLog(@"Step running"); + mode = 2; } else { // unknown @@ -256,6 +259,19 @@ static NSInteger numberTotal = 10000; } // NSLog(@"Number of step--> %ld", numberStep); weakSelf.countComplete += 1; + + // save step to server + if (numberStep > 0) { + NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; + [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"]; + NSString *dateBegin = [dateFormatter stringFromDate:activityExtra.activity.startDate]; + NSString *dateEnd = [dateFormatter stringFromDate:activityExtra.endDate]; + [[ServerAPI server] requestCreateLog:mode withStep:(int)numberStep startDate:dateBegin endDate:dateEnd CompletionHandler:^(NSError *error) { + if (error) { + NSLog(@"Error: %@", error); + } + }]; + } dispatch_async(dispatch_get_main_queue(), ^{ [weakSelf updateStepUI]; }); diff --git a/LifeLog/LifeLog/Info.plist b/LifeLog/LifeLog/Info.plist index f27f8b7..0b2ac8e 100644 --- a/LifeLog/LifeLog/Info.plist +++ b/LifeLog/LifeLog/Info.plist @@ -60,5 +60,9 @@ UIViewControllerBasedStatusBarAppearance + NSLocationWhenInUseUsageDescription + This application requires location services to work + NSLocationAlwaysUsageDescription + This application requires location services to work diff --git a/LifeLog/LifeLog/MapViewController.m b/LifeLog/LifeLog/MapViewController.m index ac3c418..d261ec5 100644 --- a/LifeLog/LifeLog/MapViewController.m +++ b/LifeLog/LifeLog/MapViewController.m @@ -7,9 +7,33 @@ // #import "MapViewController.h" +#import +#import +#import +#import "NSDate+helper.h" +#import "Utilities.h" -@interface MapViewController () +@interface MapViewController () +{ + CLGeocoder *geocoder; + CLPlacemark *placemark; + CLLocation *previousLocation; +} +@property (weak, nonatomic) IBOutlet MKMapView *mapView; +@property (weak, nonatomic) IBOutlet UILabel *lblTime; +@property (weak, nonatomic) IBOutlet UISlider *slider; + +@property (nonatomic, weak) IBOutlet UILabel *lblValueStep; +@property (nonatomic, weak) IBOutlet UILabel *lblUnitStep; +@property (strong, nonatomic) CLLocationManager *locationManager; + +@property (nonatomic, strong) CMPedometer *pedometer; +@property (nonatomic, strong) CMMotionActivityManager *motionActivityManager; +@property (nonatomic, strong) NSOperationQueue *operationQueue; +@property (nonatomic, strong) NSTimer *timer; +@property (nonatomic) int countTime; +@property (nonatomic, assign) BOOL isRequesting; @end @implementation MapViewController @@ -18,6 +42,21 @@ [super viewDidLoad]; // Do any additional setup after loading the view from its nib. self.title = NSLocalizedString(@"lifelog.tapbar.map", nil); + [self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.topLayoutGuide attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.mapView attribute:NSLayoutAttributeTop multiplier:1 constant:0]]; + geocoder = [[CLGeocoder alloc] init]; + [self setupLocation]; + [self setupMapView]; + previousLocation = nil; + + if ([CMPedometer isStepCountingAvailable]) { + _pedometer = [[CMPedometer alloc] init]; + } + if ([CMMotionActivityManager isActivityAvailable]) { + _motionActivityManager = [[CMMotionActivityManager alloc] init]; + } + self.isRequesting = NO; + _countTime = 0; + self.lblUnitStep.text = NSLocalizedString(@"lifelog.home.unit.step", nil); } - (void)didReceiveMemoryWarning { @@ -25,4 +64,131 @@ // Dispose of any resources that can be recreated. } +- (void)viewWillAppear:(BOOL)animated { + [super viewWillAppear:animated]; + _timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:@selector(countStep) userInfo:nil repeats:YES]; + [_timer fire]; +} + +- (void)viewWillDisappear:(BOOL)animated { + [super viewWillDisappear:animated]; + [_timer invalidate]; +} + +#pragma mark - CLLocationManagerDelegate +-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations { + + CLLocation *newLocation = [locations lastObject]; + if (previousLocation == nil) { + [self addAnnotationsOnMap:newLocation]; + } + else { + CLLocationCoordinate2D area[2]; + area[0] = previousLocation.coordinate; + area[1] = newLocation.coordinate; + MKPolyline *polyLine = [MKPolyline polylineWithCoordinates:area count:2]; + [_mapView addOverlay:polyLine]; + } + + previousLocation = newLocation; +} + +- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error +{ + NSLog(@"Cannot find the location."); +} + +#pragma mark - MKMapViewDelegate +- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id )overlay +{ + if ([overlay isKindOfClass:[MKPolyline class]]) { + MKPolylineRenderer *pr = [[MKPolylineRenderer alloc] initWithOverlay:overlay]; + pr.strokeColor = [UIColor redColor]; + pr.lineWidth = 5; + return pr; + } + return nil; +} + +#pragma mark - Functions Private + +- (void)addAnnotationsOnMap:(CLLocation *)locationToPoint { + MKPointAnnotation *annotation = [[MKPointAnnotation alloc] init]; + annotation.coordinate = locationToPoint.coordinate; + [geocoder reverseGeocodeLocation:locationToPoint completionHandler:^(NSArray *placemarks, NSError *error) { + if (error == nil && [placemarks count] >0) { + placemark = [placemarks firstObject]; + NSDictionary *addressDict = placemark.addressDictionary; + annotation.title = [NSString stringWithFormat:@"%@", addressDict[@"Name"]]; + [_mapView addAnnotation:annotation]; + } + }]; +} + +- (void)setupLocation { + _locationManager = [[CLLocationManager alloc] init]; + _locationManager.desiredAccuracy = kCLLocationAccuracyBest; + _locationManager.delegate = self; + CLAuthorizationStatus status = [CLLocationManager authorizationStatus]; + if (status == kCLAuthorizationStatusNotDetermined || status == kCLAuthorizationStatusDenied || status == kCLAuthorizationStatusAuthorizedWhenInUse) { + [_locationManager requestWhenInUseAuthorization]; + [_locationManager requestAlwaysAuthorization]; + } + [_locationManager startUpdatingLocation]; + [_locationManager startUpdatingHeading]; +} + +- (void)setupMapView { + _mapView.delegate = self; + _mapView.showsUserLocation = YES; + _mapView.mapType = MKMapTypeStandard; + _mapView.userTrackingMode = MKUserTrackingModeFollowWithHeading; +} + +#pragma mark - Functions Private +- (void)countStep +{ + _countTime++; + _lblTime.text = [self convertTime:_countTime]; + _slider.value = _countTime; + if (self.isRequesting == YES) { + return; + } + if ([CMMotionActivityManager isActivityAvailable]) { + self.isRequesting = YES; + NSDate *endDate = [NSDate date]; + NSDate *startDate = [endDate beginningAtMidnightOfDay]; + MapViewController __weak *weakSelf = self; + dispatch_queue_t myQueue = dispatch_queue_create("mobileworld.jp.lifelog", NULL); + dispatch_async(myQueue, ^{ + if (weakSelf == nil) { + return ; + } + [weakSelf.pedometer queryPedometerDataFromDate:startDate toDate:endDate withHandler:^(CMPedometerData * _Nullable pedometerData, NSError * _Nullable error) { + NSInteger numberStep = [pedometerData.numberOfSteps integerValue]; + dispatch_async(dispatch_get_main_queue(), ^{ + [weakSelf updateStepUI:numberStep]; + }); + }]; + }); + } +} + +- (NSString *)convertTime:(int)time +{ + NSString *result = @""; + int hour = time/3600; + int minute = time/60; + int second = time % 60; + result = [NSString stringWithFormat:@"%02d:%02d:%02d", hour, minute, second]; + return result; +} + +- (void)updateStepUI:(NSInteger)numberStep +{ + // NSLog(@"Number of step: %ld", numberStep); + self.isRequesting = NO; + self.lblValueStep.text = [Utilities addCommaFromNumber:numberStep]; +} + @end diff --git a/LifeLog/LifeLog/MapViewController.xib b/LifeLog/LifeLog/MapViewController.xib index 1fcd6c9..417d257 100644 --- a/LifeLog/LifeLog/MapViewController.xib +++ b/LifeLog/LifeLog/MapViewController.xib @@ -1,16 +1,20 @@ - + - + + + + + @@ -18,7 +22,89 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/LifeLog/LifeLog/ServerAPI.h b/LifeLog/LifeLog/ServerAPI.h index adcb0ac..7f86f0b 100644 --- a/LifeLog/LifeLog/ServerAPI.h +++ b/LifeLog/LifeLog/ServerAPI.h @@ -27,6 +27,7 @@ extern NSString *const kNotificationToken; #pragma mark - Home Screen Function - (void)requestTopWithMode:(int)mode andDate:(NSString *)date CompletionHandler:(void (^)(TopObject *, NSError *)) completion; +- (void)requestCreateLog:(int)mode withStep:(int)numStep startDate:(NSString *)startDate endDate:(NSString *)endDate CompletionHandler:(void (^)(NSError *))completion; #pragma mark - History Screen Function - (void) requestHistory:(NSString *)token atDate:(NSDate *)date withType:(int)type andMode:(int) mode CompletionHandler:(void (^)(HistoryObject *, NSError *)) completion; @@ -40,4 +41,6 @@ extern NSString *const kNotificationToken; */ - (void) requestTweetsList:(NSString *)token groupID: (int) groupID withPage:(int)page CompletionHandler:(void (^)(NSArray *, NSError *)) completion; - (void) searchGroup:(NSString *)token withKey:(NSString *)key andPage:(int)page CompletionHandler:(void (^)(NSArray *, NSError *)) completion; +#pragma mark - Common API +- (void)refreshToken: (NSString *)userID CompletionHandler:(void (^)(NSString *, NSError *))completion; @end diff --git a/LifeLog/LifeLog/ServerAPI.m b/LifeLog/LifeLog/ServerAPI.m index 7c6446e..ba65cf9 100644 --- a/LifeLog/LifeLog/ServerAPI.m +++ b/LifeLog/LifeLog/ServerAPI.m @@ -262,7 +262,7 @@ NSURLSessionDataTask * searchTask; { completion(nil, error); } - }]; + }]; } -(NSString *) convertIntToString : (int) type { @@ -289,7 +289,7 @@ NSURLSessionDataTask * searchTask; - (void)requestTopWithMode:(int)mode andDate:(NSString *)date CompletionHandler:(void (^)(TopObject *, NSError *)) completion { NSString * token = [[NSUserDefaults standardUserDefaults] stringForKey:kToken]; - NSString *url = [kServerAddress stringByAppendingFormat:@"/api/top/%d/%@", mode, date]; + NSString *url = [kServerAddress stringByAppendingFormat:@"api/top/%d/%@", mode, date]; [self _request:url method:@"GET" token:token paras:nil completion:^(NSData *data, NSError *error) { if (completion == NULL) { @@ -347,9 +347,15 @@ NSURLSessionDataTask * searchTask; if (message == nil) { message = @"Unknown error"; } - NSError *errorObject = [NSError errorWithDomain:@"LifeLog_Domain" code:-1 userInfo:@{@"message":message}]; - completion(nil, errorObject); - [self checkToken:message]; + + if ([message isEqualToString:@"Token is invalid"]) { + [self performSelectorOnMainThread:@selector(checkToken) withObject:nil waitUntilDone:YES]; + [self requestTopWithMode:mode andDate:date CompletionHandler:completion]; + } + else { + NSError *errorObject = [NSError errorWithDomain:@"LifeLog_Domain" code:-1 userInfo:@{@"message":message}]; + completion(nil, errorObject); + } } } else @@ -359,6 +365,46 @@ NSURLSessionDataTask * searchTask; }]; } +- (void)requestCreateLog:(int)mode withStep:(int)numStep startDate:(NSString *)startDate endDate:(NSString *)endDate CompletionHandler:(void (^)(NSError *))completion { + NSString * token = [[NSUserDefaults standardUserDefaults] stringForKey:kToken]; + NSString *url = [kServerAddress stringByAppendingFormat:@"api/createLog"]; + NSDictionary *dict = @{@"mode": [NSNumber numberWithInt:mode], @"numStep": [NSNumber numberWithInt:numStep], @"startTime": startDate, @"endTime": endDate}; + [self _request:url method:@"POST" token:token paras:dict completion:^(NSData *data, NSError *error) { + + if (completion == NULL) { + return ; + } + + if (error == nil) + { + NSDictionary *dataResult = [NSJSONSerialization JSONObjectWithData:data options: NSJSONReadingAllowFragments error: &error]; + int status = [dataResult[@"status"] intValue]; + if (status == 1) { // status = 1 success + completion(nil); + } + else { + NSString *message = dataResult[@"message"]; + if (message == nil) { + message = @"Unknown error"; + } + + if ([message isEqualToString:@"Token is invalid"]) { + [self performSelectorOnMainThread:@selector(checkToken) withObject:nil waitUntilDone:YES]; + [self requestCreateLog:mode withStep:numStep startDate:startDate endDate:endDate CompletionHandler:completion]; + } + else { + NSError *errorObject = [NSError errorWithDomain:@"LifeLog_Domain" code:-1 userInfo:@{@"message":message}]; + completion(errorObject); + } + } + } + else + { + completion(error); + } + }]; +} + #pragma mark - History Screen Function - (void) requestHistory:(NSString *)token atDate:(NSDate *)date withType:(int)type andMode:(int) mode CompletionHandler:(void (^)(HistoryObject *, NSError *)) completion { NSString *url = [kServerAddress stringByAppendingFormat:@"/api/history/%@/%d", [self convertIntToString:type], mode]; @@ -383,9 +429,16 @@ NSURLSessionDataTask * searchTask; if (message == nil) { message = @"Unknown error"; } - NSError *errorObject = [NSError errorWithDomain:@"LifeLog_Domain" code:-1 userInfo:@{@"message":message}]; - completion(nil, errorObject); - [self checkToken:message]; + + if ([message isEqualToString:@"Token is invalid"]) { + [self performSelectorOnMainThread:@selector(checkToken) withObject:nil waitUntilDone:YES]; + NSString *tokenNew = [[NSUserDefaults standardUserDefaults] objectForKey:kToken]; + [self requestHistory:tokenNew atDate:date withType:type andMode:mode CompletionHandler:completion]; + } + else { + NSError *errorObject = [NSError errorWithDomain:@"LifeLog_Domain" code:-1 userInfo:@{@"message":message}]; + completion(nil, errorObject); + } } } else @@ -418,9 +471,16 @@ NSURLSessionDataTask * searchTask; if (message == nil) { message = @"Unknown error"; } - NSError *errorObject = [NSError errorWithDomain:@"LifeLog_Domain" code:-1 userInfo:@{@"message":message}]; - completion(nil, errorObject); - [self checkToken:message]; + + if ([message isEqualToString:@"Token is invalid"]) { + [self performSelectorOnMainThread:@selector(checkToken) withObject:nil waitUntilDone:YES]; + NSString *tokenNew = [[NSUserDefaults standardUserDefaults] objectForKey:kToken]; + [self requestHistoryGraph:tokenNew withType:type andMode:mode CompletionHandler:completion]; + } + else { + NSError *errorObject = [NSError errorWithDomain:@"LifeLog_Domain" code:-1 userInfo:@{@"message":message}]; + completion(nil, errorObject); + } } } else @@ -464,9 +524,16 @@ NSURLSessionDataTask * searchTask; if (message == nil) { message = @"Unknown error"; } - NSError *errorObject = [NSError errorWithDomain:@"LifeLog_Domain" code:-1 userInfo:@{@"message":message}]; - completion(nil, errorObject); - [self checkToken:message]; + + if ([message isEqualToString:@"Token is invalid"]) { + [self performSelectorOnMainThread:@selector(checkToken) withObject:nil waitUntilDone:YES]; + NSString *tokenNew = [[NSUserDefaults standardUserDefaults] objectForKey:kToken]; + [self requestHistoryList:tokenNew withType:type andMode:mode AtPage:page CompletionHandler:completion]; + } + else { + NSError *errorObject = [NSError errorWithDomain:@"LifeLog_Domain" code:-1 userInfo:@{@"message":message}]; + completion(nil, errorObject); + } } } else @@ -511,9 +578,19 @@ NSURLSessionDataTask * searchTask; } else { NSString *message = dataResult[@"message"]; - NSError *errorObject = [NSError errorWithDomain:@"LifeLog_Domain" code:-1 userInfo:@{@"message":message}]; - completion(nil, errorObject); - [self checkToken:message]; + if (message == nil) { + message = @"Unknown error"; + } + + if ([message isEqualToString:@"Token is invalid"]) { + [self performSelectorOnMainThread:@selector(checkToken) withObject:nil waitUntilDone:YES]; + NSString *tokenNew = [[NSUserDefaults standardUserDefaults] objectForKey:kToken]; + [self requestTweetsList:tokenNew groupID:groupID withPage:page CompletionHandler:completion]; + } + else { + NSError *errorObject = [NSError errorWithDomain:@"LifeLog_Domain" code:-1 userInfo:@{@"message":message}]; + completion(nil, errorObject); + } } } else @@ -569,14 +646,59 @@ NSURLSessionDataTask * searchTask; }]; } +#pragma mark - Common API +- (void)refreshToken: (NSString *)userID CompletionHandler:(void (^)(NSString *, NSError *))completion { + [self _request:[kServerAddress stringByAppendingFormat: @"refreshToken"] method:@"POST" token:@"" paras:@{@"userId":userID} completion:^(NSData *data, NSError *error) { + + if (completion == NULL) { + return ; + } + + if (error == nil) + { + NSDictionary *dataResult = [NSJSONSerialization JSONObjectWithData:data options: NSJSONReadingAllowFragments error: &error]; + + int status = [dataResult[@"status"] intValue]; + if (status == 1) { // status = 1 success + NSArray *arrayResult = dataResult[@"result"]; + if (arrayResult.count > 0) { + NSString *token = arrayResult[0]; + completion(token, nil); + } + else { + NSError *errorObject = [NSError errorWithDomain:@"LifeLog_Domain" code:-1 userInfo:@{@"message":@"Unknown Error"}]; + completion(nil, errorObject); + } + + } + else { // status = 0 error + NSString *message = dataResult[@"message"]; + if (message == nil) { + message = @"Unknown error"; + } + NSError *loginFaild = [NSError errorWithDomain:@"LifeLog_Domain" code:-1 userInfo:@{@"message":message}]; + completion(nil, loginFaild); + } + } + else + { + completion(nil, error); + } + }]; +} + #pragma mark - Private Function -- (BOOL) checkToken:(NSString *)message { - if ([message isEqualToString:@"Token is invalid"]) { - [[NSNotificationCenter defaultCenter] postNotificationName:kNotificationToken object:nil]; - return YES; - } - else { - return NO; +- (void) checkToken { + // [[NSNotificationCenter defaultCenter] postNotificationName:kNotificationToken object:nil]; + NSData *data = [[NSUserDefaults standardUserDefaults] objectForKey:kUser]; + User *user = (User *)[NSKeyedUnarchiver unarchiveObjectWithData:data]; + if (user != nil) { + [self refreshToken:user.user_id CompletionHandler:^(NSString *token, NSError *error) { + if (error == nil) { + [[NSUserDefaults standardUserDefaults] setObject:token forKey:kToken]; + [[NSUserDefaults standardUserDefaults] synchronize]; + } + }]; } } - (NSData *) _encodeDictionary: (NSDictionary *) dictionary @@ -632,4 +754,11 @@ NSURLSessionDataTask * searchTask; return task; } +-(void)waitUntilDone:(void(^)(void))waitBlock { + //use your statement or call method here + if(waitBlock){ + waitBlock(); + } +} + @end