forked from CIT/Vmeda.Online
		
	
						commit
						46df6f2989
					
				
							
								
								
									
										279
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										279
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -61,6 +61,7 @@ | ||||
|     "@ionic-native/clipboard": "^4.20.0", | ||||
|     "@ionic-native/core": "^4.20.0", | ||||
|     "@ionic-native/device": "^4.20.0", | ||||
|     "@ionic-native/diagnostic": "^4.2.0", | ||||
|     "@ionic-native/file": "^4.20.0", | ||||
|     "@ionic-native/file-opener": "^4.20.0", | ||||
|     "@ionic-native/file-transfer": "^4.20.0", | ||||
| @ -147,6 +148,7 @@ | ||||
|     "@types/node": "^8.10.59", | ||||
|     "@types/promise.prototype.finally": "^2.0.4", | ||||
|     "acorn": "^5.7.4", | ||||
|     "cordova.plugins.diagnostic": "^5.0.2", | ||||
|     "electron-builder-lib": "^20.23.1", | ||||
|     "electron-rebuild": "^1.10.0", | ||||
|     "gulp": "4.0.2", | ||||
| @ -223,7 +225,8 @@ | ||||
|       "cordova-plugin-wkuserscript": {}, | ||||
|       "cordova-plugin-media": { | ||||
|         "KEEP_AVAUDIOSESSION_ALWAYS_ACTIVE": "NO" | ||||
|       } | ||||
|       }, | ||||
|       "cordova.plugins.diagnostic": {} | ||||
|     } | ||||
|   }, | ||||
|   "main": "desktop/electron.js", | ||||
|  | ||||
| @ -14,10 +14,9 @@ | ||||
| import { Component } from '@angular/core'; | ||||
| import { FormBuilder } from '@angular/forms'; | ||||
| import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; | ||||
| import { Platform } from 'ionic-angular'; | ||||
| import { Geolocation, GeolocationOptions } 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'; | ||||
| 
 | ||||
| /** | ||||
| @ -33,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(); | ||||
| @ -116,33 +113,51 @@ export class AddonModDataFieldLatlongComponent extends AddonModDataFieldPluginCo | ||||
|      * | ||||
|      * @param $event The event. | ||||
|      */ | ||||
|     getLocation(event: Event): void { | ||||
|     async getLocation(event: Event): Promise<void> { | ||||
|         event.preventDefault(); | ||||
| 
 | ||||
|         const modal = this.domUtils.showModalLoading('addon.mod_data.gettinglocation', true); | ||||
| 
 | ||||
|         const options: GeolocationOptions = { | ||||
|             enableHighAccuracy: true, | ||||
|             timeout: 30000 | ||||
|         }; | ||||
|         try { | ||||
|             const coordinates = await CoreGeolocation.instance.getCoordinates(); | ||||
| 
 | ||||
|         this.geolocation.getCurrentPosition(options).then((result) => { | ||||
|             this.form.controls['f_' + this.field.id + '_0'].setValue(result.coords.latitude); | ||||
|             this.form.controls['f_' + this.field.id + '_1'].setValue(result.coords.longitude); | ||||
|         }).catch((error) => { | ||||
|             if (this.isPermissionDeniedError(error)) { | ||||
|                 this.domUtils.showErrorModal('addon.mod_data.locationpermissiondenied', true); | ||||
|             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.showLocationErrorModal(error); | ||||
|         } | ||||
| 
 | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             this.domUtils.showErrorModalDefault(error,  'Error getting location'); | ||||
|         }).finally(() => { | ||||
|             modal.dismiss(); | ||||
|         }); | ||||
|         modal.dismiss(); | ||||
|     } | ||||
| 
 | ||||
|     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 getting the location. | ||||
|      * | ||||
|      * @param error Location error. | ||||
|      */ | ||||
|     protected showLocationErrorModal(error: any): void { | ||||
|         if (error instanceof CoreGeolocationError) { | ||||
|             this.domUtils.showErrorModal(this.getGeolocationErrorMessage(error), true); | ||||
| 
 | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         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'; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -23,6 +23,7 @@ | ||||
|     "gettinglocation": "Getting location", | ||||
|     "latlongboth": "Both latitude and longitude are required.", | ||||
|     "locationpermissiondenied": "Permission to access your location has been denied.", | ||||
|     "locationnotenabled": "Location is not enabled", | ||||
|     "menuchoose": "Choose...", | ||||
|     "modulenameplural": "Databases", | ||||
|     "more": "More", | ||||
|  | ||||
| @ -25,6 +25,8 @@ import { MockLocationStrategy } from '@angular/common/testing'; | ||||
| 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'; | ||||
| @ -59,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'; | ||||
| @ -194,7 +197,8 @@ export const CORE_PROVIDERS: any[] = [ | ||||
|     CorePluginFileDelegate, | ||||
|     CoreSyncProvider, | ||||
|     CoreFileHelperProvider, | ||||
|     CoreCustomURLSchemesProvider | ||||
|     CoreCustomURLSchemesProvider, | ||||
|     CoreGeolocationProvider, | ||||
| ]; | ||||
| 
 | ||||
| export const WP_PROVIDER: any = null; | ||||
| @ -342,12 +346,15 @@ export const WP_PROVIDER: any = null; | ||||
|         CoreSyncProvider, | ||||
|         CoreFileHelperProvider, | ||||
|         CoreCustomURLSchemesProvider, | ||||
|         CoreGeolocationProvider, | ||||
|         CoreSiteInfoCronHandler, | ||||
|         { | ||||
|             provide: HTTP_INTERCEPTORS, | ||||
|             useClass: CoreInterceptor, | ||||
|             multi: true, | ||||
|         }, | ||||
|         Diagnostic, | ||||
|         Geolocation, | ||||
|         ScreenOrientation, | ||||
|         {provide: COMPILER_OPTIONS, useValue: {}, multi: true}, | ||||
|         {provide: JitCompilerFactory, useClass: JitCompilerFactory, deps: [COMPILER_OPTIONS]}, | ||||
|  | ||||
| @ -508,6 +508,7 @@ | ||||
|     "addon.mod_data.foundrecords": "Found records: {{$a.num}}/{{$a.max}} (<a href=\"{{$a.reseturl}}\">Reset filters</a>)", | ||||
|     "addon.mod_data.gettinglocation": "Getting location", | ||||
|     "addon.mod_data.latlongboth": "Both latitude and longitude are required.", | ||||
|     "addon.mod_data.locationnotenabled": "Location is not enabled", | ||||
|     "addon.mod_data.locationpermissiondenied": "Permission to access your location has been denied.", | ||||
|     "addon.mod_data.menuchoose": "Choose...", | ||||
|     "addon.mod_data.modulenameplural": "Databases", | ||||
|  | ||||
							
								
								
									
										33
									
								
								src/classes/error.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/classes/error.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,33 @@ | ||||
| // (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.
 | ||||
| 
 | ||||
| /** | ||||
|  * Base Error class. | ||||
|  * | ||||
|  * The native Error class cannot be extended in Typescript without restoring the prototype chain, extend this | ||||
|  * class instead. | ||||
|  * | ||||
|  * @see https://stackoverflow.com/questions/41102060/typescript-extending-error-class
 | ||||
|  */ | ||||
| export class CoreError extends Error { | ||||
| 
 | ||||
|     constructor(message?: string) { | ||||
|         super(message); | ||||
| 
 | ||||
|         // Fix prototype chain: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-2.html#support-for-newtarget
 | ||||
|         this.name = new.target.name; | ||||
|         Object.setPrototypeOf(this, new.target.prototype); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -69,6 +69,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'; | ||||
| @ -299,6 +300,8 @@ export class CoreCompileProvider { | ||||
|         instance['CoreSitePluginsQuizAccessRuleComponent'] = CoreSitePluginsQuizAccessRuleComponent; | ||||
|         instance['CoreSitePluginsAssignFeedbackComponent'] = CoreSitePluginsAssignFeedbackComponent; | ||||
|         instance['CoreSitePluginsAssignSubmissionComponent'] = CoreSitePluginsAssignSubmissionComponent; | ||||
|         instance['CoreGeolocationError'] = CoreGeolocationError; | ||||
|         instance['CoreGeolocationErrorReason'] = CoreGeolocationErrorReason; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|  | ||||
| @ -654,6 +654,35 @@ export class CoreAppProvider { | ||||
|         return this.ssoAuthenticationPromise || Promise.resolve(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Wait until the application is resumed. | ||||
|      * | ||||
|      * @param timeout Maximum time to wait, use null to wait forever. | ||||
|      */ | ||||
|     async waitForResume(timeout: number | null = null): Promise<void> { | ||||
|         let resolve: Function; | ||||
|         let resumeSubscription: any; | ||||
|         let timeoutId: NodeJS.Timer | false; | ||||
| 
 | ||||
|         const promise = new Promise((r): any => resolve = r); | ||||
|         const stopWaiting = (): any => { | ||||
|             if (!resolve) { | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             resolve(); | ||||
|             resumeSubscription.unsubscribe(); | ||||
|             timeoutId && clearTimeout(timeoutId); | ||||
| 
 | ||||
|             resolve = null; | ||||
|         }; | ||||
| 
 | ||||
|         resumeSubscription = this.platform.resume.subscribe(stopWaiting); | ||||
|         timeoutId = timeout ? setTimeout(stopWaiting, timeout) : false; | ||||
| 
 | ||||
|         await promise; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Retrieve redirect data. | ||||
|      * | ||||
|  | ||||
							
								
								
									
										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,8 @@ 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'; | ||||
| 
 | ||||
| @ -46,4 +48,8 @@ export class Alerts extends makeSingleton(AlertController) {} | ||||
| 
 | ||||
| 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