Skip to content

Commit

Permalink
Initial release
Browse files Browse the repository at this point in the history
  • Loading branch information
Jeroen van Dijk committed Oct 4, 2017
1 parent e9b65ed commit e8a6a63
Show file tree
Hide file tree
Showing 22 changed files with 2,006 additions and 0 deletions.
1 change: 1 addition & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
TODO: place your license here and we'll include it in the module distribution
54 changes: 54 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Titanium Location Permission
This module gives you the possibility to shortcircuit the current iOS location permission from Titanium to properly support iOS 11 its new way of location permissions.

## Follow Guide

### Setup

1. Integrate the module into the `modules` folder and define them into the `tiapp.xml` file:

```xml
<modules>
<module platform="iphone" version="0.1.0">ti.locationpermission</module>
</modules>
```

### Usage
1. Check if you have a location permission

```js
var locationPermission = (OS_IOS) ? require('ti.locationpermission') : Ti.Geolocation;

if (locationPermission.hasLocationPermissions(Ti.Geolocation.AUTHORIZATION_WHEN_IN_USE)) {
alert('true');
}
```

1. Request or upgrade location permission

```js
var locationPermission = (OS_IOS) ? require('ti.locationpermission') : Ti.Geolocation;

locationPermission.requestLocationPermissions(Ti.Geolocation.AUTHORIZATION_WHEN_IN_USE, function(e) {
alert(e.success);
});

locationPermission.requestLocationPermissions(Ti.Geolocation.AUTHORIZATION_ALWAYS, function(e) {
if (OS_IOS && e.authorizationStatus !== Ti.Geolocation.AUTHORIZATION_ALWAYS) {
alert('Permission wasn\'t upgraded');
}
});
```

Cheers!

## Build yourself

### iOS

If you already have Titanium installed, skip the first 2 steps, if not let's install Titanium locally.

1. `brew install yarn --without-node` to install yarn without relying on a specific Node version
1. In the ios directory execute `yarn install`
1. Alter the `titanium.xcconfig` to build with the preferred SDK
1. To build the module execute `rm -rf build && ./node_modules/.bin/ti build -p ios --build-only`
9 changes: 9 additions & 0 deletions assets/README
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Place your assets like PNG files in this directory and they will be packaged
with your module.

All JavaScript files in the assets directory are IGNORED except if you create a
file named "ti.locationpermission.js" in this directory in which case it will be
wrapped by native code, compiled, and used as your module. This allows you to
run pure JavaScript modules that are pre-compiled.

Note: Mobile Web does not support this assets directory.
39 changes: 39 additions & 0 deletions documentation/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# ti.locationpermission Module

## Description

TODO: Enter your module description here

## Accessing the ti.locationpermission Module

To access this module from JavaScript, you would do the following:

var ti_locationpermission = require("ti.locationpermission");

The ti_locationpermission variable is a reference to the Module object.

## Reference

TODO: If your module has an API, you should document
the reference here.

### ti_locationpermission.function

TODO: This is an example of a module function.

### ti_locationpermission.property

TODO: This is an example of a module property.

## Usage

TODO: Enter your usage example here

## Author

TODO: Enter your author name, email and other contact
details you want to share here.

## License

TODO: Enter your license/legal information here.
39 changes: 39 additions & 0 deletions example/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// This is a test harness for your module
// You should do something interesting in this harness
// to test out the module and to provide instructions
// to users on how to use it by example.


// open a single window
var win = Ti.UI.createWindow({
backgroundColor:'white'
});
var label = Ti.UI.createLabel();
win.add(label);
win.open();

// TODO: write your module tests here
var ti_locationpermission = require('ti.locationpermission');
Ti.API.info("module is => " + ti_locationpermission);

label.text = ti_locationpermission.example();

Ti.API.info("module exampleProp is => " + ti_locationpermission.exampleProp);
ti_locationpermission.exampleProp = "This is a test value";

if (Ti.Platform.name == "android") {
var proxy = ti_locationpermission.createExample({
message: "Creating an example Proxy",
backgroundColor: "red",
width: 100,
height: 100,
top: 100,
left: 150
});

proxy.printMessage("Hello world!");
proxy.message = "Hi world!. It's me again.";
proxy.printMessage("Hello world!");
win.add(proxy);
}

22 changes: 22 additions & 0 deletions ios/Classes/TiLocationpermissionModule.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* ti.locationpermission
*
* Created by Your Name
* Copyright (c) 2017 Your Company. All rights reserved.
*/

#import "TiModule.h"
#import <CoreLocation/CoreLocation.h>

NSString *const kTiGeolocationUsageDescriptionWhenInUse = @"NSLocationWhenInUseUsageDescription";
NSString *const kTiGeolocationUsageDescriptionAlways = @"NSLocationAlwaysUsageDescription";
NSString *const kTiGeolocationUsageDescriptionAlwaysAndWhenInUse = @"NSLocationAlwaysAndWhenInUseUsageDescription";

@interface TiLocationpermissionModule : TiModule <CLLocationManagerDelegate>
{
CLLocationManager *locationPermissionManager;
CLAuthorizationStatus oldStatus;
KrollCallback *authorizationCallback;
}

@end
214 changes: 214 additions & 0 deletions ios/Classes/TiLocationpermissionModule.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
/**
* ti.locationpermission
*
* Created by Your Name
* Copyright (c) 2017 Your Company. All rights reserved.
*/

#import "TiLocationpermissionModule.h"
#import "TiBase.h"
#import "TiHost.h"
#import "TiUtils.h"

@implementation TiLocationpermissionModule

#pragma mark Internal

// this is generated for your module, please do not change it
-(id)moduleGUID
{
return @"1fc6c195-8d20-4733-af97-5d1755a9387a";
}

// this is generated for your module, please do not change it
-(NSString*)moduleId
{
return @"ti.locationpermission";
}

#pragma mark Lifecycle

-(void)startup
{
// this method is called when the module is first loaded
// you *must* call the superclass
[super startup];

NSLog(@"[INFO] %@ loaded",self);
}

-(void)shutdown:(id)sender
{
// this method is called when the module is being unloaded
// typically this is during shutdown. make sure you don't do too
// much processing here or the app will be quit forceably

// you *must* call the superclass
[super shutdown:sender];
}

- (CLLocationManager *)locationPermissionManager
{
// if we don't have an instance, create it
if (locationPermissionManager == nil) {
locationPermissionManager = [[CLLocationManager alloc] init];
locationPermissionManager.delegate = self;
}
return locationPermissionManager;
}

#pragma mark Cleanup

-(void)dealloc
{
RELEASE_TO_NIL(locationPermissionManager);
// release any resources that have been retained by the module
[super dealloc];
}

#pragma mark Internal Memory Management

-(void)didReceiveMemoryWarning:(NSNotification*)notification
{
// optionally release any resources that can be dynamically
// reloaded once memory is available - such as caches
[super didReceiveMemoryWarning:notification];
}

- (void)executeAndReleaseCallbackWithCode:(NSInteger)code andMessage:(NSString *)message
{
if (authorizationCallback == nil) {
return;
}

NSMutableDictionary *propertiesDict = [TiUtils dictionaryWithCode:code message:message];
NSArray *invocationArray = [[NSArray alloc] initWithObjects:&propertiesDict count:1];
[authorizationCallback call:invocationArray thisObject:self];

[invocationArray release];
RELEASE_TO_NIL(message);
RELEASE_TO_NIL(authorizationCallback);
RELEASE_TO_NIL(locationPermissionManager);
}

#pragma Public APIs

- (NSNumber *)hasLocationPermissions:(id)args
{
BOOL locationServicesEnabled = [CLLocationManager locationServicesEnabled];
CLAuthorizationStatus currentPermissionLevel = [CLLocationManager authorizationStatus];
oldStatus = currentPermissionLevel;
id value = [args objectAtIndex:0];
ENSURE_TYPE(value, NSNumber);
CLAuthorizationStatus requestedPermissionLevel = [TiUtils intValue:value];
return NUMBOOL(locationServicesEnabled && currentPermissionLevel == requestedPermissionLevel);
}

- (void)requestLocationPermissions:(id)args
{
id value = [args objectAtIndex:0];
ENSURE_TYPE(value, NSNumber);

// Store the authorization callback for later usage
if ([args count] == 2) {
RELEASE_TO_NIL(authorizationCallback);
ENSURE_TYPE([args objectAtIndex:1], KrollCallback);
authorizationCallback = [[args objectAtIndex:1] retain];
}

CLAuthorizationStatus requested = [TiUtils intValue:value];
CLAuthorizationStatus currentPermissionLevel = [CLLocationManager authorizationStatus];
oldStatus = currentPermissionLevel;

if (currentPermissionLevel == kCLAuthorizationStatusAuthorizedWhenInUse && requested == kCLAuthorizationStatusAuthorizedWhenInUse) {
[self executeAndReleaseCallbackWithCode:0 andMessage:nil];
return;
} else if (currentPermissionLevel == kCLAuthorizationStatusAuthorizedAlways && requested == kCLAuthorizationStatusAuthorizedAlways) {
[self executeAndReleaseCallbackWithCode:0 andMessage:nil];
return;
} else if (currentPermissionLevel == kCLAuthorizationStatusDenied) {
NSString *message = @"The user denied access to use location services.";
[self executeAndReleaseCallbackWithCode:1 andMessage:message];
return;
}

NSString *errorMessage = nil;

if (requested == kCLAuthorizationStatusAuthorizedWhenInUse) {
NSLog(@"[DEBUG] %@", @"Requesting when in use authorize");
if ([[NSBundle mainBundle] objectForInfoDictionaryKey:kTiGeolocationUsageDescriptionWhenInUse]) {
if (currentPermissionLevel == kCLAuthorizationStatusAuthorizedAlways) {
errorMessage = @"Cannot change already granted permission from AUTHORIZATION_ALWAYS to AUTHORIZATION_WHEN_IN_USE";
} else {
NSLog(@"[DEBUG] %@", @"Requesting when in use authorize with key set");
TiThreadPerformOnMainThread(^{
[[self locationPermissionManager] requestWhenInUseAuthorization];
},
NO);
}
} else {
errorMessage = [NSString stringWithFormat:@"The %@ key must be defined in your tiapp.xml in order to request this permission", kTiGeolocationUsageDescriptionWhenInUse];
}
return;
}
if (requested == kCLAuthorizationStatusAuthorizedAlways) {
NSLog(@"[DEBUG] %@", @"Requesting always authorize");
if ([[NSBundle mainBundle] objectForInfoDictionaryKey:kTiGeolocationUsageDescriptionAlways] || [[NSBundle mainBundle] objectForInfoDictionaryKey:kTiGeolocationUsageDescriptionAlwaysAndWhenInUse]) {
NSLog(@"[DEBUG] %@", @"Requesting always authorize with key set");
TiThreadPerformOnMainThread(^{
[[self locationPermissionManager] requestAlwaysAuthorization];
},
NO);
} else {
errorMessage = [NSString stringWithFormat:@"The %@ or %@ key must be defined in your tiapp.xml in order to request this permission.",
kTiGeolocationUsageDescriptionAlways, kTiGeolocationUsageDescriptionAlwaysAndWhenInUse];
}
return;
}

if (errorMessage != nil) {
NSLog(@"[ERROR] %@", errorMessage);
[self executeAndReleaseCallbackWithCode:(errorMessage == nil) ? 0 : 1 andMessage:errorMessage];
RELEASE_TO_NIL(errorMessage);
}
}

#pragma mark Delegates

- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status
{
NSLog(@"[DEBUG] %@ : %d", @"Auth changed to", status);

// The new callback for android parity used inside Ti.Geolocation.requestLocationPermissions()
if (authorizationCallback != nil && status != kCLAuthorizationStatusNotDetermined && status != oldStatus) {

int code = 0;
NSString *errorStr = nil;

switch (status) {
case kCLAuthorizationStatusAuthorizedAlways:
case kCLAuthorizationStatusAuthorizedWhenInUse:
break;
default:
code = 1;
errorStr = @"The user denied access to use location services.";
}

TiThreadPerformOnMainThread(^{
NSMutableDictionary *propertiesDict = [TiUtils dictionaryWithCode:code message:errorStr];
[propertiesDict setObject:NUMINT([CLLocationManager authorizationStatus]) forKey:@"authorizationStatus"];
KrollEvent *invocationEvent = [[KrollEvent alloc] initWithCallback:authorizationCallback eventObject:propertiesDict thisObject:self];
[[authorizationCallback context] enqueue:invocationEvent];
RELEASE_TO_NIL(invocationEvent);
},
YES);

RELEASE_TO_NIL(authorizationCallback);
RELEASE_TO_NIL(errorStr);
RELEASE_TO_NIL(locationPermissionManager);

oldStatus = status;
}
}

@end
Loading

0 comments on commit e8a6a63

Please sign in to comment.