forked from CIT/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