forked from EVOgeek/Vmeda.Online
		
	MOBILE-3489 location: Expose geolocation service to plugins
This commit is contained in:
		
							parent
							
								
									2c477ac93a
								
							
						
					
					
						commit
						af5f010239
					
				| @ -14,15 +14,10 @@ | ||||
| import { Component } from '@angular/core'; | ||||
| import { FormBuilder } from '@angular/forms'; | ||||
| import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; | ||||
| import { Platform } from 'ionic-angular'; | ||||
| import { Geolocation } from '@ionic-native/geolocation'; | ||||
| import { AddonModDataFieldPluginComponent } from '../../../classes/field-plugin-component'; | ||||
| import { CoreApp, CoreAppProvider } from '@providers/app'; | ||||
| import { CoreGeolocation, CoreGeolocationError, CoreGeolocationErrorReason } from '@providers/geolocation'; | ||||
| import { CoreDomUtilsProvider } from '@providers/utils/dom'; | ||||
| import { Diagnostic } from '@singletons/core.singletons'; | ||||
| import { CoreError } from '@classes/error'; | ||||
| 
 | ||||
| class AccessLocationError extends CoreError {} | ||||
| 
 | ||||
| /** | ||||
|  * Component to render data latlong field. | ||||
| @ -37,13 +32,11 @@ export class AddonModDataFieldLatlongComponent extends AddonModDataFieldPluginCo | ||||
|     east: number; | ||||
|     showGeolocation: boolean; | ||||
| 
 | ||||
|     constructor(protected fb: FormBuilder, | ||||
|             protected platform: Platform, | ||||
|             protected geolocation: Geolocation, | ||||
|     constructor( | ||||
|             protected fb: FormBuilder, | ||||
|             protected domUtils: CoreDomUtilsProvider, | ||||
|             protected sanitizer: DomSanitizer, | ||||
|             appProvider: CoreAppProvider | ||||
|             ) { | ||||
|             appProvider: CoreAppProvider) { | ||||
|         super(fb); | ||||
| 
 | ||||
|         this.showGeolocation = !appProvider.isDesktop(); | ||||
| @ -126,107 +119,25 @@ export class AddonModDataFieldLatlongComponent extends AddonModDataFieldPluginCo | ||||
|         const modal = this.domUtils.showModalLoading('addon.mod_data.gettinglocation', true); | ||||
| 
 | ||||
|         try { | ||||
|             await this.updateLocation(); | ||||
|             const coordinates = await CoreGeolocation.instance.getCoordinates(); | ||||
| 
 | ||||
|             this.form.controls['f_' + this.field.id + '_0'].setValue(coordinates.latitude); | ||||
|             this.form.controls['f_' + this.field.id + '_1'].setValue(coordinates.longitude); | ||||
|         } catch (error) { | ||||
|             this.showErrorModal(error); | ||||
|             this.showLocationErrorModal(error); | ||||
|         } | ||||
| 
 | ||||
|         modal.dismiss(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Update component location. | ||||
|      */ | ||||
|     protected async updateLocation(): Promise<void> { | ||||
|         await this.authorizeLocation(); | ||||
|         await this.enableLocation(); | ||||
| 
 | ||||
|         const result = await this.geolocation.getCurrentPosition({ | ||||
|             enableHighAccuracy: true, | ||||
|             timeout: 30000, | ||||
|         }); | ||||
| 
 | ||||
|         this.form.controls['f_' + this.field.id + '_0'].setValue(result.coords.latitude); | ||||
|         this.form.controls['f_' + this.field.id + '_1'].setValue(result.coords.longitude); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Make sure that using device location has been authorize and ask for permission if it hasn't. | ||||
|      * Show the appropriate error modal for the given error getting the location. | ||||
|      * | ||||
|      * @param failOnDeniedOnce Throw an exception if the permission has been denied once. | ||||
|      * @param error Location error. | ||||
|      */ | ||||
|     protected async authorizeLocation(failOnDeniedOnce: boolean = false): Promise<void> { | ||||
|         const authorizationStatus = await Diagnostic.instance.getLocationAuthorizationStatus(); | ||||
| 
 | ||||
|         switch (authorizationStatus) { | ||||
|             // This constant is hard-coded because it is not declared in @ionic-native/diagnostic v4.
 | ||||
|             case 'DENIED_ONCE': | ||||
|                 if (failOnDeniedOnce) { | ||||
|                     throw new AccessLocationError('addon.mod_data.locationpermissiondenied'); | ||||
|                 } | ||||
|             // Fall through.
 | ||||
|             case Diagnostic.instance.permissionStatus.NOT_REQUESTED: | ||||
|                 await Diagnostic.instance.requestLocationAuthorization(); | ||||
|                 await CoreApp.instance.waitForResume(500); | ||||
|                 await this.authorizeLocation(true); | ||||
| 
 | ||||
|                 return; | ||||
|             case Diagnostic.instance.permissionStatus.GRANTED: | ||||
|             case Diagnostic.instance.permissionStatus.GRANTED_WHEN_IN_USE: | ||||
|                 // Location is authorized.
 | ||||
|                 return; | ||||
|             case Diagnostic.instance.permissionStatus.DENIED: | ||||
|             default: | ||||
|                 throw new AccessLocationError('addon.mod_data.locationpermissiondenied'); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Make sure that location is enabled and switch to settings if it hasn't. | ||||
|      */ | ||||
|     protected async enableLocation(): Promise<void> { | ||||
|         let locationEnabled = await Diagnostic.instance.isLocationEnabled(); | ||||
| 
 | ||||
|         if (locationEnabled) { | ||||
|             // Location is enabled.
 | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         if (!CoreApp.instance.isIOS()) { | ||||
|             await Diagnostic.instance.switchToLocationSettings(); | ||||
|             await CoreApp.instance.waitForResume(30000); | ||||
| 
 | ||||
|             locationEnabled = await Diagnostic.instance.isLocationEnabled(); | ||||
|         } | ||||
| 
 | ||||
|         if (!locationEnabled) { | ||||
|             throw new AccessLocationError('addon.mod_data.locationnotenabled'); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Check whether an error was caused by a PERMISSION_DENIED. | ||||
|      * | ||||
|      * @param error Error. | ||||
|      */ | ||||
|     protected isPermissionDeniedError(error?: any): boolean { | ||||
|         return error && 'code' in error && 'PERMISSION_DENIED' in error && error.code === error.PERMISSION_DENIED; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Show the appropriate error modal for the given error. | ||||
|      * | ||||
|      * @param error Error. | ||||
|      */ | ||||
|     protected showErrorModal(error: any): void { | ||||
|         if (error instanceof AccessLocationError) { | ||||
|             this.domUtils.showErrorModal(error.message, true); | ||||
| 
 | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         if (this.isPermissionDeniedError(error)) { | ||||
|             this.domUtils.showErrorModal('addon.mod_data.locationpermissiondenied', true); | ||||
|     protected showLocationErrorModal(error: any): void { | ||||
|         if (error instanceof CoreGeolocationError) { | ||||
|             this.domUtils.showErrorModal(this.getGeolocationErrorMessage(error), true); | ||||
| 
 | ||||
|             return; | ||||
|         } | ||||
| @ -234,4 +145,19 @@ export class AddonModDataFieldLatlongComponent extends AddonModDataFieldPluginCo | ||||
|         this.domUtils.showErrorModalDefault(error,  'Error getting location'); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get error message from a geolocation error. | ||||
|      * | ||||
|      * @param error Geolocation error. | ||||
|      */ | ||||
|     protected getGeolocationErrorMessage(error: CoreGeolocationError): string { | ||||
|         // tslint:disable-next-line: switch-default
 | ||||
|         switch (error.reason) { | ||||
|             case CoreGeolocationErrorReason.PermissionDenied: | ||||
|                 return 'addon.mod_data.locationpermissiondenied'; | ||||
|             case CoreGeolocationErrorReason.LocationNotEnabled: | ||||
|                 return 'addon.mod_data.locationnotenabled'; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -26,6 +26,7 @@ import { TranslateModule, TranslateLoader } from '@ngx-translate/core'; | ||||
| import { TranslateHttpLoader } from '@ngx-translate/http-loader'; | ||||
| 
 | ||||
| import { Diagnostic } from '@ionic-native/diagnostic'; | ||||
| import { Geolocation } from '@ionic-native/geolocation'; | ||||
| import { ScreenOrientation } from '@ionic-native/screen-orientation'; | ||||
| 
 | ||||
| import { MoodleMobileApp } from './app.component'; | ||||
| @ -60,6 +61,7 @@ import { CorePluginFileDelegate } from '@providers/plugin-file-delegate'; | ||||
| import { CoreSyncProvider } from '@providers/sync'; | ||||
| import { CoreFileHelperProvider } from '@providers/file-helper'; | ||||
| import { CoreCustomURLSchemesProvider } from '@providers/urlschemes'; | ||||
| import { CoreGeolocationProvider } from '@providers/geolocation'; | ||||
| 
 | ||||
| // Handlers.
 | ||||
| import { CoreSiteInfoCronHandler } from '@providers/handlers/site-info-cron-handler'; | ||||
| @ -195,7 +197,8 @@ export const CORE_PROVIDERS: any[] = [ | ||||
|     CorePluginFileDelegate, | ||||
|     CoreSyncProvider, | ||||
|     CoreFileHelperProvider, | ||||
|     CoreCustomURLSchemesProvider | ||||
|     CoreCustomURLSchemesProvider, | ||||
|     CoreGeolocationProvider, | ||||
| ]; | ||||
| 
 | ||||
| export const WP_PROVIDER: any = null; | ||||
| @ -343,6 +346,7 @@ export const WP_PROVIDER: any = null; | ||||
|         CoreSyncProvider, | ||||
|         CoreFileHelperProvider, | ||||
|         CoreCustomURLSchemesProvider, | ||||
|         CoreGeolocationProvider, | ||||
|         CoreSiteInfoCronHandler, | ||||
|         { | ||||
|             provide: HTTP_INTERCEPTORS, | ||||
| @ -350,6 +354,7 @@ export const WP_PROVIDER: any = null; | ||||
|             multi: true, | ||||
|         }, | ||||
|         Diagnostic, | ||||
|         Geolocation, | ||||
|         ScreenOrientation, | ||||
|         {provide: COMPILER_OPTIONS, useValue: {}, multi: true}, | ||||
|         {provide: JitCompilerFactory, useClass: JitCompilerFactory, deps: [COMPILER_OPTIONS]}, | ||||
|  | ||||
| @ -67,6 +67,7 @@ import { CoreContentLinksModuleGradeHandler } from '@core/contentlinks/classes/m | ||||
| import { CoreContentLinksModuleIndexHandler } from '@core/contentlinks/classes/module-index-handler'; | ||||
| import { CoreCourseActivityPrefetchHandlerBase } from '@core/course/classes/activity-prefetch-handler'; | ||||
| import { CoreCourseResourcePrefetchHandlerBase } from '@core/course/classes/resource-prefetch-handler'; | ||||
| import { CoreGeolocationError, CoreGeolocationErrorReason } from '@providers/geolocation'; | ||||
| 
 | ||||
| // Import all core modules that define components, directives and pipes.
 | ||||
| import { CoreComponentsModule } from '@components/components.module'; | ||||
| @ -294,6 +295,8 @@ export class CoreCompileProvider { | ||||
|         instance['CoreSitePluginsQuizAccessRuleComponent'] = CoreSitePluginsQuizAccessRuleComponent; | ||||
|         instance['CoreSitePluginsAssignFeedbackComponent'] = CoreSitePluginsAssignFeedbackComponent; | ||||
|         instance['CoreSitePluginsAssignSubmissionComponent'] = CoreSitePluginsAssignSubmissionComponent; | ||||
|         instance['CoreGeolocationError'] = CoreGeolocationError; | ||||
|         instance['CoreGeolocationErrorReason'] = CoreGeolocationErrorReason; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|  | ||||
							
								
								
									
										143
									
								
								src/providers/geolocation.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								src/providers/geolocation.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,143 @@ | ||||
| // (C) Copyright 2015 Moodle Pty Ltd.
 | ||||
| //
 | ||||
| // Licensed under the Apache License, Version 2.0 (the "License");
 | ||||
| // you may not use this file except in compliance with the License.
 | ||||
| // You may obtain a copy of the License at
 | ||||
| //
 | ||||
| //     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| //
 | ||||
| // Unless required by applicable law or agreed to in writing, software
 | ||||
| // distributed under the License is distributed on an "AS IS" BASIS,
 | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | ||||
| // See the License for the specific language governing permissions and
 | ||||
| // limitations under the License.
 | ||||
| 
 | ||||
| import { Injectable } from '@angular/core'; | ||||
| import { Coordinates } from '@ionic-native/geolocation'; | ||||
| import { CoreApp } from '@providers/app'; | ||||
| import { Geolocation, Diagnostic, makeSingleton } from '@singletons/core.singletons'; | ||||
| import { CoreError } from '@classes/error'; | ||||
| 
 | ||||
| export enum CoreGeolocationErrorReason { | ||||
|     PermissionDenied = 'permission-denied', | ||||
|     LocationNotEnabled = 'location-not-enabled', | ||||
| } | ||||
| 
 | ||||
| export class CoreGeolocationError extends CoreError { | ||||
| 
 | ||||
|     readonly reason: CoreGeolocationErrorReason; | ||||
| 
 | ||||
|     constructor(reason: CoreGeolocationErrorReason) { | ||||
|         super(`GeolocationError: ${reason}`); | ||||
| 
 | ||||
|         this.reason = reason; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| @Injectable() | ||||
| export class CoreGeolocationProvider { | ||||
| 
 | ||||
|     /** | ||||
|      * Get current user coordinates. | ||||
|      * | ||||
|      * @throws {CoreGeolocationError} | ||||
|      */ | ||||
|     async getCoordinates(): Promise<Coordinates> { | ||||
|         try { | ||||
|             await this.authorizeLocation(); | ||||
|             await this.enableLocation(); | ||||
| 
 | ||||
|             const result = await Geolocation.instance.getCurrentPosition({ | ||||
|                 enableHighAccuracy: true, | ||||
|                 timeout: 30000, | ||||
|             }); | ||||
| 
 | ||||
|             return result.coords; | ||||
|         } catch (error) { | ||||
|             if (this.isCordovaPermissionDeniedError(error)) { | ||||
|                 throw new CoreGeolocationError(CoreGeolocationErrorReason.PermissionDenied); | ||||
|             } | ||||
| 
 | ||||
|             throw error; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Make sure that using device location has been authorized and ask for permission if it hasn't. | ||||
|      * | ||||
|      * @throws {CoreGeolocationError} | ||||
|      */ | ||||
|     async authorizeLocation(): Promise<void> { | ||||
|         await this.doAuthorizeLocation(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Make sure that location is enabled and open settings to enable it if necessary. | ||||
|      * | ||||
|      * @throws {CoreGeolocationError} | ||||
|      */ | ||||
|     async enableLocation(): Promise<void> { | ||||
|         let locationEnabled = await Diagnostic.instance.isLocationEnabled(); | ||||
| 
 | ||||
|         if (locationEnabled) { | ||||
|             // Location is enabled.
 | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         if (!CoreApp.instance.isIOS()) { | ||||
|             await Diagnostic.instance.switchToLocationSettings(); | ||||
|             await CoreApp.instance.waitForResume(30000); | ||||
| 
 | ||||
|             locationEnabled = await Diagnostic.instance.isLocationEnabled(); | ||||
|         } | ||||
| 
 | ||||
|         if (!locationEnabled) { | ||||
|             throw new CoreGeolocationError(CoreGeolocationErrorReason.LocationNotEnabled); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Recursive implementation of authorizeLocation method, protected to avoid exposing the failOnDeniedOnce parameter. | ||||
|      * | ||||
|      * @param failOnDeniedOnce Throw an exception if the permission has been denied once. | ||||
|      * @throws {CoreGeolocationError} | ||||
|      */ | ||||
|     protected async doAuthorizeLocation(failOnDeniedOnce: boolean = false): Promise<void> { | ||||
|         const authorizationStatus = await Diagnostic.instance.getLocationAuthorizationStatus(); | ||||
| 
 | ||||
|         switch (authorizationStatus) { | ||||
|             // This constant is hard-coded because it is not declared in @ionic-native/diagnostic v4.
 | ||||
|             case 'DENIED_ONCE': | ||||
|                 if (failOnDeniedOnce) { | ||||
|                     throw new CoreGeolocationError(CoreGeolocationErrorReason.PermissionDenied); | ||||
|                 } | ||||
|             // Fall through.
 | ||||
|             case Diagnostic.instance.permissionStatus.NOT_REQUESTED: | ||||
|                 await Diagnostic.instance.requestLocationAuthorization(); | ||||
|                 await CoreApp.instance.waitForResume(500); | ||||
|                 await this.doAuthorizeLocation(true); | ||||
| 
 | ||||
|                 return; | ||||
|             case Diagnostic.instance.permissionStatus.GRANTED: | ||||
|             case Diagnostic.instance.permissionStatus.GRANTED_WHEN_IN_USE: | ||||
|                 // Location is authorized.
 | ||||
|                 return; | ||||
|             case Diagnostic.instance.permissionStatus.DENIED: | ||||
|             default: | ||||
|                 throw new CoreGeolocationError(CoreGeolocationErrorReason.PermissionDenied); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Check whether an error was caused by a PERMISSION_DENIED from the cordova plugin. | ||||
|      * | ||||
|      * @param error Error. | ||||
|      */ | ||||
|     protected isCordovaPermissionDeniedError(error?: any): boolean { | ||||
|         return error && 'code' in error && 'PERMISSION_DENIED' in error && error.code === error.PERMISSION_DENIED; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| export class CoreGeolocation extends makeSingleton(CoreGeolocationProvider) {} | ||||
| @ -16,6 +16,7 @@ import { AlertController, App } from 'ionic-angular'; | ||||
| import { Injector } from '@angular/core'; | ||||
| import { TranslateService } from '@ngx-translate/core'; | ||||
| import { HttpClient } from '@angular/common/http'; | ||||
| import { Geolocation as GeolocationService } from '@ionic-native/geolocation'; | ||||
| import { Diagnostic as DiagnosticService } from '@ionic-native/diagnostic'; | ||||
| 
 | ||||
| import { CoreSingletonsFactory, CoreInjectionToken, CoreSingletonClass } from '@classes/singletons-factory'; | ||||
| @ -49,4 +50,6 @@ export class Ionic extends makeSingleton(App) {} | ||||
| 
 | ||||
| export class Diagnostic extends makeSingleton(DiagnosticService) {} | ||||
| 
 | ||||
| export class Geolocation extends makeSingleton(GeolocationService) {} | ||||
| 
 | ||||
| export class Http extends makeSingleton(HttpClient) {} | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user