Skip to content

Commit

Permalink
Initial implmentation of the status screen
Browse files Browse the repository at this point in the history
- End-to-end implementation of the "check" parts of the interface
- Code structure similar to the existing android code for simplicity
- checks defined in `TripDiarySensorControlChecks`
- interface with the plugin in `SensorControlForegroundDelegate`
- interface with the background code to generate error notifications in `SensorControlBackgroundChecker`

Testing done:
With the UI changes in:
e-mission/e-mission-phone@d69f3a5
the plugin -> foreground delegate -> checks pipeline works

e-mission/e-mission-phone#812 (comment)

TO DO:
- Background checks need to be tested
- Implement the "fix" methods in addition to the "check" methods
  • Loading branch information
shankari committed Feb 10, 2022
1 parent d6088e0 commit 2fc078b
Show file tree
Hide file tree
Showing 13 changed files with 307 additions and 61 deletions.
8 changes: 6 additions & 2 deletions plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,9 @@
<header-file src="src/ios/Location/TripDiaryActions.h" target-dir="Location"/>
<header-file src="src/ios/Location/TripDiaryDelegate.h" target-dir="Location"/>
<header-file src="src/ios/Location/TripDiaryStateMachine.h" target-dir="Location"/>
<header-file src="src/ios/Location/TripDiarySettingsCheck.h" target-dir="Location"/>
<header-file src="src/ios/Verification/TripDiarySensorControlChecks.h" target-dir="Verification/"/>
<header-file src="src/ios/Verification/SensorControlForegroundDelegate.h" target-dir="Verification/"/>
<header-file src="src/ios/Verification/SensorControlBackgroundChecker.h" target-dir="Verification/"/>
<header-file src="src/ios/Wrapper/Metadata.h" target-dir="Wrapper"/>
<header-file src="src/ios/Wrapper/MotionActivity.h" target-dir="Wrapper"/>
<header-file src="src/ios/Wrapper/SimpleLocation.h" target-dir="Wrapper"/>
Expand All @@ -241,7 +243,9 @@
<source-file src="src/ios/Location/TripDiaryActions.m" target-dir="Location"/>
<source-file src="src/ios/Location/TripDiaryDelegate.m" target-dir="Location"/>
<source-file src="src/ios/Location/TripDiaryStateMachine.m" target-dir="Location"/>
<source-file src="src/ios/Location/TripDiarySettingsCheck.m" target-dir="Location"/>
<header-file src="src/ios/Verification/TripDiarySensorControlChecks.m" target-dir="Verification/"/>
<header-file src="src/ios/Verification/SensorControlForegroundDelegate.m" target-dir="Verification/"/>
<header-file src="src/ios/Verification/SensorControlBackgroundChecker.m" target-dir="Verification/"/>
<source-file src="src/ios/Wrapper/Metadata.m" target-dir="Wrapper"/>
<source-file src="src/ios/Wrapper/MotionActivity.m" target-dir="Wrapper"/>
<source-file src="src/ios/Wrapper/SimpleLocation.m" target-dir="Wrapper"/>
Expand Down
9 changes: 9 additions & 0 deletions res/ios/en.lproj/DCLocalizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,12 @@
"fix-permission-action-button" = "Fix permission";
"precise-location-problem" = "The app does not have permission to read 'precise' location - background trip tracking will not work.";
"notifications_blocked" = "Notifications blocked, please enable";
"notifications_blocked_app_open" = "Notifications blocked, please fix in app settings";
"location_not_enabled" = "Location turned off, please turn on";
"location_permission_off" = "Insufficient location permissions, please fix";
"location_permission_off_app_open" = "Insufficient location permissions, please fix in app settings";
"location_permission_off_enable" = "Insufficient location permissions, please enable";
"activity_permission_off" = "Motion and Fitness permission off, please enable";
"activity_permission_off_app_open" = "Motion and Fitness permission off, please fix in app settings";
"fix_app_status_title" = "Incorrect app settings";
"fix_app_status_text" = "Click to view and fix app status";
43 changes: 14 additions & 29 deletions src/ios/BEMDataCollection.m
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
#import "DataUtils.h"
#import "StatsEvent.h"
#import "BEMBuiltinUserCache.h"
#import "TripDiarySettingsCheck.h"
#import "SensorControlForegroundDelegate.h"
#import <CoreLocation/CoreLocation.h>

@implementation BEMDataCollection
Expand Down Expand Up @@ -65,7 +65,9 @@ - (void)markConsented:(CDVInvokedUrlCommand*)command
// which is actually easier ("always allow")
// so in that case, we continue calling the init code in TripDiaryStateMachine
[self initWithConsent];
/*
[TripDiarySettingsCheck checkMotionSettingsAndPermission:FALSE];
*/
CDVPluginResult* result = [CDVPluginResult
resultWithStatus:CDVCommandStatus_OK];
[self.commandDelegate sendPluginResult:result callbackId:callbackId];
Expand All @@ -85,6 +87,8 @@ - (void)fixLocationSettings:(CDVInvokedUrlCommand*)command

- (void)isValidLocationSettings:(CDVInvokedUrlCommand*)command
{
[SensorControlForegroundDelegate checkLocationSettings:self.commandDelegate
forCommand:command];
}

- (void)fixLocationPermissions:(CDVInvokedUrlCommand*)command
Expand All @@ -93,6 +97,9 @@ - (void)fixLocationPermissions:(CDVInvokedUrlCommand*)command

- (void)isValidLocationPermissions:(CDVInvokedUrlCommand*)command
{
[SensorControlForegroundDelegate checkLocationPermissions:self.commandDelegate
forCommand:command];

}

- (void)fixFitnessPermissions:(CDVInvokedUrlCommand*)command
Expand All @@ -101,16 +108,14 @@ - (void)fixFitnessPermissions:(CDVInvokedUrlCommand*)command

- (void)isValidFitnessPermissions:(CDVInvokedUrlCommand*)command
{
}
[SensorControlForegroundDelegate checkMotionActivityPermissions:self.commandDelegate
forCommand:command];

- (UIUserNotificationSettings*) REQUESTED_NOTIFICATION_TYPES {
return [UIUserNotificationSettings
settingsForTypes:UIUserNotificationTypeAlert|UIUserNotificationTypeBadge
categories:nil];
}

- (void)fixShowNotifications:(CDVInvokedUrlCommand*)command
{
/*
NSString* callbackId = [command callbackId];
@try {
if ([UIApplication instancesRespondToSelector:@selector(registerUserNotificationSettings:)]) {
Expand Down Expand Up @@ -139,34 +144,14 @@ - (void)fixShowNotifications:(CDVInvokedUrlCommand*)command
messageAsString:msg];
[self.commandDelegate sendPluginResult:result callbackId:callbackId];
}
*/
}


- (void)isValidShowNotifications:(CDVInvokedUrlCommand*)command
{
NSString* callbackId = [command callbackId];
@try {
UIUserNotificationSettings* requestedSettings = [self REQUESTED_NOTIFICATION_TYPES];
UIUserNotificationSettings* currSettings = [[UIApplication sharedApplication] currentUserNotificationSettings];
if (requestedSettings.types == currSettings.types) {
CDVPluginResult* result = [CDVPluginResult
resultWithStatus:CDVCommandStatus_OK];
[self.commandDelegate sendPluginResult:result callbackId:callbackId];
} else {
NSString* msg = NSLocalizedStringFromTable(@"notifications_blocked", @"DCLocalizable", nil);
CDVPluginResult* result = [CDVPluginResult
resultWithStatus:CDVCommandStatus_ERROR
messageAsString:msg];
[self.commandDelegate sendPluginResult:result callbackId:callbackId];
}
}
@catch (NSException *exception) {
NSString* msg = [NSString stringWithFormat: @"While getting settings, error %@", exception];
CDVPluginResult* result = [CDVPluginResult
resultWithStatus:CDVCommandStatus_ERROR
messageAsString:msg];
[self.commandDelegate sendPluginResult:result callbackId:callbackId];
}
[SensorControlForegroundDelegate checkNotificationsEnabled:self.commandDelegate
forCommand:command];
}

- (void)isNotificationsUnpaused:(CDVInvokedUrlCommand*)command
Expand Down
1 change: 0 additions & 1 deletion src/ios/BEMRemotePushNotificationHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
@interface BEMRemotePushNotificationHandler : NSObject
+ (BEMRemotePushNotificationHandler*) instance;
+ (void) performPeriodicActivity;
+ (void) validateAndCleanupState;
- (void) handleNotifications:(NSNotification*)note;
@property NSMutableArray* silentPushHandlerList;

Expand Down
20 changes: 3 additions & 17 deletions src/ios/BEMRemotePushNotificationHandler.m
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#import "BEMRemotePushNotificationHandler.h"
#import "TripDiaryStateMachine.h"
#import "TripDiarySettingsCheck.h"
#import "SensorControlBackgroundChecker.h"
#import "LocalNotificationManager.h"

@implementation BEMRemotePushNotificationHandler
Expand Down Expand Up @@ -100,22 +100,8 @@ - (void)handleNotifications:(NSNotification*)note {

+ (void) performPeriodicActivity
{
[TripDiarySettingsCheck checkSettingsAndPermission];
[BEMRemotePushNotificationHandler validateAndCleanupState];
}

+ (void) validateAndCleanupState
{
NSUInteger currState = [TripDiaryStateMachine instance].currState;
if (currState == kStartState) {
[LocalNotificationManager addNotification:[NSString stringWithFormat:
@"Still in start state, sending initialize..."] showUI:TRUE];
[[NSNotificationCenter defaultCenter] postNotificationName:CFCTransitionNotificationName
object:CFCTransitionInitialize];
} else {
[LocalNotificationManager addNotification:[NSString stringWithFormat:
@"In valid state %@, nothing to do...", [TripDiaryStateMachine getStateName:currState]] showUI:FALSE];
}
[SensorControlBackgroundChecker checkAppState];
[SensorControlBackgroundChecker restartFSMIfStartState];
}

@end
8 changes: 5 additions & 3 deletions src/ios/Location/TripDiaryDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
#import "LocationTrackingConfig.h"
#import "ConfigManager.h"
#import "BEMAppDelegate.h"
#import "TripDiarySettingsCheck.h"
#import "SensorControlBackgroundChecker.h"

#define ACCURACY_THRESHOLD 200

Expand Down Expand Up @@ -228,9 +228,11 @@ - (void)locationManager:(CLLocationManager *)manager
// and the background call to checkLocationSettingsAndPermission from the remote push code
// doesn't have that reference. Can simplify this after we stop supporting iOS13.
if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusNotDetermined) {
[TripDiarySettingsCheck promptForPermission:manager];
// STATUS SCREEN: handle with checkAndFix
// [TripDiarySettingsCheck promptForPermission:manager];
} else {
[TripDiarySettingsCheck checkLocationSettingsAndPermission:FALSE];
// STATUS SCREEN: handle with checkAndFix
// [TripDiarySettingsCheck checkLocationSettingsAndPermission:FALSE];
}

if (_tdsm.currState == kStartState) {
Expand Down
26 changes: 20 additions & 6 deletions src/ios/Location/TripDiaryStateMachine.m
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
#import "TripDiaryStateMachine.h"
#import "TripDiaryActions.h"
#import "TripDiaryDelegate.h"
#import "TripDiarySettingsCheck.h"
#import "SensorControlBackgroundChecker.h"

#import "LocalNotificationManager.h"

Expand Down Expand Up @@ -99,25 +99,38 @@ - (id) init {
// would be good to test, though.
}


// STATUS SCREEN: Figure out how to fix recursion when [TripDiaryStateMachine instance] is called
// [SensorControlBackgroundChecker checkAppState];
/* The only times we should get here are:
* - if we re-install a previously installed app, and so it is already authorized for background location collection BUT is in the start state, or
* - another option might be a re-launch of the app when the user has manually stopped tracking.
* It would be bad to automatically restart the tracking if the user has manully stopped tracking.
* One way to deal with this would be to have separate states for "start" and for "tracking suspended".
* Another way would be to just remove this transition from here...
* TODO: Figure out how to deal with it.
*/
// STATUS SCREEN: Figure out how to fix recursion when [TripDiaryStateMachine instance] is called
// [SensorControlBackgroundChecker restartFSMIfStartState];
/*
if ([CLLocationManager authorizationStatus] != kCLAuthorizationStatusAuthorizedAlways) {
[TripDiarySettingsCheck promptForPermission:self.locMgr];
} else {
NSLog(@"Current location authorization = %d, always = %d",
[CLLocationManager authorizationStatus], kCLAuthorizationStatusAuthorizedAlways);
/* The only times we should get here are:
The only times we should get here are:
* - if we re-install a previously installed app, and so it is already authorized for background location collection BUT is in the start state, or
* - another option might be a re-launch of the app when the user has manually stopped tracking.
* It would be bad to automatically restart the tracking if the user has manully stopped tracking.
* One way to deal with this would be to have separate states for "start" and for "tracking suspended".
* Another way would be to just remove this transition from here...
* TODO: Figure out how to deal with it.
*/
if (self.currState == kStartState) {
[[NSNotificationCenter defaultCenter] postNotificationName:CFCTransitionNotificationName
object:CFCTransitionInitialize];
}
}
*/

if (![ConfigManager instance].is_duty_cycling && self.currState != kTrackingStoppedState) {
/* If we are not using geofencing, and the tracking is not manually turned off, then we don't need to listen
Expand Down Expand Up @@ -203,6 +216,7 @@ -(void)setState:(TripDiaryStates) newState {
[TripDiaryStateMachine getStateName:newState]]];

self.currState = newState;
[SensorControlBackgroundChecker checkAppState];
}

/*
Expand Down Expand Up @@ -248,7 +262,7 @@ -(void) handleStart:(NSString*) transition withUserInfo:(NSDictionary*) userInfo
transition,
[TripDiaryStateMachine getStateName:self.currState],
[TripDiaryStateMachine getStateName:self.currState]);
[TripDiarySettingsCheck checkSettingsAndPermission];
[SensorControlBackgroundChecker checkAppState];
} else if ([transition isEqualToString:CFCTransitionExitedGeofence]) {
[TripDiaryActions startTracking:transition withLocationMgr:self.locMgr];
[TripDiaryActions deleteGeofence:self.locMgr];
Expand Down Expand Up @@ -397,8 +411,8 @@ - (void) handleOngoingTrip:(NSString*) transition withUserInfo:(NSDictionary*) u
return nil;
}];
} else if ([transition isEqualToString:CFCTransitionGeofenceCreationError]) {
// setState will call SensorControlBackgroundChecker checkAppState by default
[self setState:kStartState];
[TripDiarySettingsCheck checkSettingsAndPermission];
} else if ([transition isEqualToString:CFCTransitionForceStopTracking]) {
[TripDiaryActions resetFSM:transition withLocationMgr:self.locMgr];
} else if ([transition isEqualToString:CFCTransitionTrackingStopped]) {
Expand Down
11 changes: 11 additions & 0 deletions src/ios/Verification/SensorControlBackgroundChecker.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#import <Foundation/Foundation.h>
#import <CoreLocation/CoreLocation.h>
#import <UIKit/UIKit.h>
#import <CoreMotion/CoreMotion.h>

@interface SensorControlBackgroundChecker: NSObject

+(void)restartFSMIfStartState;
+(void)checkAppState;

@end
90 changes: 90 additions & 0 deletions src/ios/Verification/SensorControlBackgroundChecker.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
#import "SensorControlBackgroundChecker.h"
#import "TripDiarySensorControlChecks.h"
#import "TripDiaryStateMachine.h"
#import "LocalNotificationManager.h"
#import "BEMAppDelegate.h"
#import "BEMActivitySync.h"

#import <CoreMotion/CoreMotion.h>
#define OPEN_APP_STATUS_PAGE_ID @362253744

@implementation SensorControlBackgroundChecker

+(NSDictionary*)OPEN_APP_STATUS_PAGE
{
NSDictionary* config = @{
@"id": OPEN_APP_STATUS_PAGE_ID,
@"title": NSLocalizedStringFromTable(@"fix_app_status_title", @"DCLocalizable", nil),
@"text": NSLocalizedStringFromTable(@"fix_app_status_text", @"DCLocalizable", nil),
@"data": @{
@"redirectTo": @"root.main.control",
@"redirectParams": @{
@"launchAppStatusModal": @true
}
}
};
return config;
}

+(void)restartFSMIfStartState
{
NSUInteger currState = [TripDiaryStateMachine instance].currState;
if (currState == kStartState) {
[LocalNotificationManager addNotification:[NSString stringWithFormat:@"Still in start state, sending initialize..."] showUI:TRUE];
[[NSNotificationCenter defaultCenter] postNotificationName:CFCTransitionNotificationName
object:CFCTransitionInitialize];
} else {
[LocalNotificationManager addNotification:[NSString stringWithFormat:@"In valid state %@, nothing to do...", [TripDiaryStateMachine getStateName:currState]] showUI:FALSE];
}
}

+(void)checkAppState
{
[LocalNotificationManager cancelNotification:OPEN_APP_STATUS_PAGE_ID];

NSArray* allChecks = @[
@([TripDiarySensorControlChecks checkLocationSettings]),
@([TripDiarySensorControlChecks checkLocationPermissions]),
@([TripDiarySensorControlChecks checkMotionActivitySettings]),
@([TripDiarySensorControlChecks checkMotionActivityPermissions]),
@([TripDiarySensorControlChecks checkNotificationsEnabled])
];
BOOL allChecksPass = true;
for (id check in allChecks) {
allChecksPass = allChecksPass && check;
}

BOOL locChecksPass = allChecks[0] && allChecks[1];

if (allChecksPass) {
[LocalNotificationManager addNotification:[NSString stringWithFormat:@"All settings valid, nothing to prompt"]];
[self restartFSMIfStartState];
}
else if (locChecksPass) {
/*
Log.i(ctxt, TAG, "all checks = "+allOtherChecksPass+" but location permission status "+allOtherChecks[0]+" should be true "+
" so one of the non-location checks must be false: loc permission, motion permission, notification, unused apps" + Arrays.toString(allOtherChecks));
Log.i(ctxt, TAG, "a non-local check failed, generating only user visible notification");
*/
[self generateOpenAppSettingsNotification];
}
else {
/*
Log.i(ctxt, TAG, "location settings are valid, but location permission is not, generating tracking error and visible notification");
Log.i(ctxt, TAG, "curr status check results = " +
" loc permission, motion permission, notification, unused apps "+ Arrays.toString(allOtherChecks));
*/
// Should replace with TRACKING_ERROR but looks like we
// don't have any
[[NSNotificationCenter defaultCenter]
postNotificationName:CFCTransitionNotificationName
object:CFCTransitionGeofenceCreationError];
[self generateOpenAppSettingsNotification];
}
}

+(void)generateOpenAppSettingsNotification
{
[LocalNotificationManager schedulePluginCompatibleNotification:[self OPEN_APP_STATUS_PAGE] withNewData:NULL];
}
@end
15 changes: 15 additions & 0 deletions src/ios/Verification/SensorControlForegroundDelegate.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#import <Cordova/CDV.h>
#import <Foundation/Foundation.h>
#import <CoreLocation/CoreLocation.h>
#import <UIKit/UIKit.h>
#import <CoreMotion/CoreMotion.h>

@interface SensorControlForegroundDelegate: NSObject

+(void)checkLocationSettings:(id<CDVCommandDelegate>)delegate forCommand:(CDVInvokedUrlCommand*)command;
+(void)checkLocationPermissions:(id<CDVCommandDelegate>)delegate forCommand:(CDVInvokedUrlCommand*)command;
+(void)checkMotionActivitySettings:(id<CDVCommandDelegate>)delegate
forCommand:(CDVInvokedUrlCommand*)command;
+(void)checkMotionActivityPermissions:(id<CDVCommandDelegate>)delegate forCommand:(CDVInvokedUrlCommand*)command;
+(void)checkNotificationsEnabled:(id<CDVCommandDelegate>)delegate forCommand:(CDVInvokedUrlCommand*)command;
@end
Loading

0 comments on commit 2fc078b

Please sign in to comment.