2019-09-26 12:43:29 +02:00
|
|
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
2017-10-26 15:03:08 +02:00
|
|
|
//
|
|
|
|
// 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.
|
|
|
|
|
2018-06-21 15:59:06 +02:00
|
|
|
import { Injectable, NgZone } from '@angular/core';
|
2018-11-21 15:33:09 +01:00
|
|
|
import { Platform, App, NavController, MenuController } from 'ionic-angular';
|
2017-10-26 15:03:08 +02:00
|
|
|
import { Keyboard } from '@ionic-native/keyboard';
|
|
|
|
import { Network } from '@ionic-native/network';
|
2019-05-03 11:53:34 +02:00
|
|
|
import { StatusBar } from '@ionic-native/status-bar';
|
2017-10-26 15:03:08 +02:00
|
|
|
|
|
|
|
import { CoreDbProvider } from './db';
|
|
|
|
import { CoreLoggerProvider } from './logger';
|
2018-06-21 12:10:32 +02:00
|
|
|
import { CoreEventsProvider } from './events';
|
2020-01-17 15:03:33 +01:00
|
|
|
import { SQLiteDB, SQLiteDBTableSchema } from '@classes/sqlitedb';
|
2019-05-03 11:53:34 +02:00
|
|
|
import { CoreConfigConstants } from '../configconstants';
|
2020-03-24 12:18:32 +01:00
|
|
|
import { makeSingleton } from '@singletons/core.singletons';
|
2017-10-26 15:03:08 +02:00
|
|
|
|
2018-01-15 08:22:00 +01:00
|
|
|
/**
|
|
|
|
* Data stored for a redirect to another page/site.
|
|
|
|
*/
|
2017-11-21 16:35:41 +01:00
|
|
|
export interface CoreRedirectData {
|
2018-01-15 08:22:00 +01:00
|
|
|
/**
|
|
|
|
* ID of the site to load.
|
|
|
|
*/
|
2017-11-21 16:35:41 +01:00
|
|
|
siteId?: string;
|
2018-01-15 08:22:00 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Name of the page to redirect to.
|
|
|
|
*/
|
|
|
|
page?: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Params to pass to the page.
|
|
|
|
*/
|
|
|
|
params?: any;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Timestamp when this redirect was last modified.
|
|
|
|
*/
|
2017-11-21 16:35:41 +01:00
|
|
|
timemodified?: number;
|
2018-01-29 10:05:20 +01:00
|
|
|
}
|
2017-11-21 16:35:41 +01:00
|
|
|
|
2020-01-17 15:03:33 +01:00
|
|
|
/**
|
|
|
|
* App DB schema and migration function.
|
|
|
|
*/
|
|
|
|
export interface CoreAppSchema {
|
|
|
|
/**
|
|
|
|
* Name of the schema.
|
|
|
|
*/
|
|
|
|
name: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Latest version of the schema (integer greater than 0).
|
|
|
|
*/
|
|
|
|
version: number;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Tables to create when installing or upgrading the schema.
|
|
|
|
*/
|
|
|
|
tables?: SQLiteDBTableSchema[];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Migrates the schema to the latest version.
|
|
|
|
*
|
|
|
|
* Called when installing and upgrading the schema, after creating the defined tables.
|
|
|
|
*
|
|
|
|
* @param db The affected DB.
|
|
|
|
* @param oldVersion Old version of the schema or 0 if not installed.
|
|
|
|
* @return Promise resolved when done.
|
|
|
|
*/
|
|
|
|
migrate?(db: SQLiteDB, oldVersion: number): Promise<any>;
|
|
|
|
}
|
|
|
|
|
2017-10-26 15:03:08 +02:00
|
|
|
/**
|
|
|
|
* Factory to provide some global functionalities, like access to the global app database.
|
|
|
|
* @description
|
|
|
|
* Each service or component should be responsible of creating their own database tables. Example:
|
|
|
|
*
|
|
|
|
* constructor(appProvider: CoreAppProvider) {
|
|
|
|
* this.appDB = appProvider.getDB();
|
|
|
|
* this.appDB.createTableFromSchema(this.tableSchema);
|
|
|
|
* }
|
|
|
|
*/
|
|
|
|
@Injectable()
|
|
|
|
export class CoreAppProvider {
|
2018-01-12 14:28:46 +01:00
|
|
|
protected DBNAME = 'MoodleMobile';
|
|
|
|
protected db: SQLiteDB;
|
|
|
|
protected logger;
|
2018-01-29 10:05:20 +01:00
|
|
|
protected ssoAuthenticationPromise: Promise<any>;
|
|
|
|
protected isKeyboardShown = false;
|
2018-11-21 15:33:09 +01:00
|
|
|
protected backActions = [];
|
2019-05-23 16:18:32 +02:00
|
|
|
protected mainMenuId = 0;
|
|
|
|
protected mainMenuOpen: number;
|
2019-05-30 12:28:13 +02:00
|
|
|
protected forceOffline = false;
|
2017-10-26 15:03:08 +02:00
|
|
|
|
2020-01-17 15:03:33 +01:00
|
|
|
// Variables for DB.
|
|
|
|
protected createVersionsTableReady: Promise<any>;
|
|
|
|
protected SCHEMA_VERSIONS_TABLE = 'schema_versions';
|
|
|
|
protected versionsTableSchema: SQLiteDBTableSchema = {
|
|
|
|
name: this.SCHEMA_VERSIONS_TABLE,
|
|
|
|
columns: [
|
|
|
|
{
|
|
|
|
name: 'name',
|
|
|
|
type: 'TEXT',
|
|
|
|
primaryKey: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'version',
|
2020-01-20 11:06:08 +01:00
|
|
|
type: 'INTEGER',
|
|
|
|
},
|
|
|
|
],
|
2020-01-17 15:03:33 +01:00
|
|
|
};
|
|
|
|
|
2018-01-08 14:42:08 +01:00
|
|
|
constructor(dbProvider: CoreDbProvider, private platform: Platform, private keyboard: Keyboard, private appCtrl: App,
|
2019-05-23 16:18:32 +02:00
|
|
|
private network: Network, logger: CoreLoggerProvider, private events: CoreEventsProvider, zone: NgZone,
|
2019-05-03 11:53:34 +02:00
|
|
|
private menuCtrl: MenuController, private statusBar: StatusBar) {
|
2017-10-26 15:03:08 +02:00
|
|
|
this.logger = logger.getInstance('CoreAppProvider');
|
2017-12-01 08:51:07 +01:00
|
|
|
this.db = dbProvider.getDB(this.DBNAME);
|
2017-10-26 15:03:08 +02:00
|
|
|
|
2020-01-17 15:03:33 +01:00
|
|
|
// Create the schema versions table.
|
|
|
|
this.createVersionsTableReady = this.db.createTableFromSchema(this.versionsTableSchema);
|
|
|
|
|
2017-10-26 15:03:08 +02:00
|
|
|
this.keyboard.onKeyboardShow().subscribe((data) => {
|
2018-06-21 15:59:06 +02:00
|
|
|
// Execute the callback in the Angular zone, so change detection doesn't stop working.
|
|
|
|
zone.run(() => {
|
2018-06-22 10:36:09 +02:00
|
|
|
document.body.classList.add('keyboard-is-open');
|
2018-06-21 15:59:06 +02:00
|
|
|
this.isKeyboardShown = true;
|
2018-08-17 17:28:24 +02:00
|
|
|
// Error on iOS calculating size.
|
|
|
|
// More info: https://github.com/ionic-team/ionic-plugin-keyboard/issues/276 .
|
|
|
|
events.trigger(CoreEventsProvider.KEYBOARD_CHANGE, data.keyboardHeight);
|
2018-06-21 15:59:06 +02:00
|
|
|
});
|
2017-10-26 15:03:08 +02:00
|
|
|
});
|
|
|
|
this.keyboard.onKeyboardHide().subscribe((data) => {
|
2018-06-21 15:59:06 +02:00
|
|
|
// Execute the callback in the Angular zone, so change detection doesn't stop working.
|
|
|
|
zone.run(() => {
|
2018-06-22 10:36:09 +02:00
|
|
|
document.body.classList.remove('keyboard-is-open');
|
2018-06-21 15:59:06 +02:00
|
|
|
this.isKeyboardShown = false;
|
2018-08-17 17:28:24 +02:00
|
|
|
events.trigger(CoreEventsProvider.KEYBOARD_CHANGE, 0);
|
2018-06-21 15:59:06 +02:00
|
|
|
});
|
2017-10-26 15:03:08 +02:00
|
|
|
});
|
2018-11-21 15:33:09 +01:00
|
|
|
|
|
|
|
this.platform.registerBackButtonAction(() => {
|
|
|
|
this.backButtonAction();
|
|
|
|
}, 100);
|
2019-05-30 12:28:13 +02:00
|
|
|
|
|
|
|
// Export the app provider so Behat tests can change the forceOffline flag.
|
2019-12-04 12:36:43 +01:00
|
|
|
if (CoreAppProvider.isAutomated()) {
|
|
|
|
(<any> window).appProvider = this;
|
|
|
|
}
|
2017-10-26 15:03:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if the browser supports mediaDevices.getUserMedia.
|
|
|
|
*
|
2019-09-10 16:48:56 +02:00
|
|
|
* @return Whether the function is supported.
|
2017-10-26 15:03:08 +02:00
|
|
|
*/
|
2018-01-29 10:05:20 +01:00
|
|
|
canGetUserMedia(): boolean {
|
2017-10-26 15:03:08 +02:00
|
|
|
return !!(navigator && navigator.mediaDevices && navigator.mediaDevices.getUserMedia);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if the browser supports MediaRecorder.
|
|
|
|
*
|
2019-09-10 16:48:56 +02:00
|
|
|
* @return Whether the function is supported.
|
2017-10-26 15:03:08 +02:00
|
|
|
*/
|
2018-01-29 10:05:20 +01:00
|
|
|
canRecordMedia(): boolean {
|
|
|
|
return !!(<any> window).MediaRecorder;
|
2018-01-08 14:42:08 +01:00
|
|
|
}
|
2017-10-26 15:03:08 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Closes the keyboard.
|
|
|
|
*/
|
2018-01-29 10:05:20 +01:00
|
|
|
closeKeyboard(): void {
|
2017-10-26 15:03:08 +02:00
|
|
|
if (this.isMobile()) {
|
2018-12-11 12:20:26 +01:00
|
|
|
this.keyboard.hide();
|
2017-10-26 15:03:08 +02:00
|
|
|
}
|
2018-01-08 14:42:08 +01:00
|
|
|
}
|
2017-10-26 15:03:08 +02:00
|
|
|
|
2020-01-17 15:03:33 +01:00
|
|
|
/**
|
|
|
|
* Install and upgrade a certain schema.
|
|
|
|
*
|
|
|
|
* @param schema The schema to create.
|
|
|
|
* @return Promise resolved when done.
|
|
|
|
*/
|
|
|
|
async createTablesFromSchema(schema: CoreAppSchema): Promise<any> {
|
|
|
|
this.logger.debug(`Apply schema to app DB: ${schema.name}`);
|
|
|
|
|
|
|
|
let oldVersion;
|
|
|
|
|
|
|
|
try {
|
|
|
|
// Wait for the schema versions table to be created.
|
|
|
|
await this.createVersionsTableReady;
|
|
|
|
|
|
|
|
// Fetch installed version of the schema.
|
|
|
|
const entry = await this.db.getRecord(this.SCHEMA_VERSIONS_TABLE, {name: schema.name});
|
|
|
|
oldVersion = entry.version;
|
|
|
|
} catch (error) {
|
|
|
|
// No installed version yet.
|
|
|
|
oldVersion = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (oldVersion >= schema.version) {
|
|
|
|
// Version already installed, nothing else to do.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.logger.debug(`Migrating schema '${schema.name}' of app DB from version ${oldVersion} to ${schema.version}`);
|
|
|
|
|
|
|
|
if (schema.tables) {
|
|
|
|
await this.db.createTablesFromSchema(schema.tables);
|
|
|
|
}
|
|
|
|
if (schema.migrate) {
|
|
|
|
await schema.migrate(this.db, oldVersion);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set installed version.
|
|
|
|
await this.db.insertRecord(this.SCHEMA_VERSIONS_TABLE, {name: schema.name, version: schema.version});
|
|
|
|
}
|
|
|
|
|
2017-10-26 15:03:08 +02:00
|
|
|
/**
|
|
|
|
* Get the application global database.
|
|
|
|
*
|
2019-09-10 16:48:56 +02:00
|
|
|
* @return App's DB.
|
2017-10-26 15:03:08 +02:00
|
|
|
*/
|
2018-01-29 10:05:20 +01:00
|
|
|
getDB(): SQLiteDB {
|
2017-10-26 15:03:08 +02:00
|
|
|
return this.db;
|
2018-01-08 14:42:08 +01:00
|
|
|
}
|
|
|
|
|
2019-05-23 16:18:32 +02:00
|
|
|
/**
|
|
|
|
* Get an ID for a main menu.
|
|
|
|
*
|
2019-09-10 16:48:56 +02:00
|
|
|
* @return Main menu ID.
|
2019-05-23 16:18:32 +02:00
|
|
|
*/
|
|
|
|
getMainMenuId(): number {
|
|
|
|
return this.mainMenuId++;
|
|
|
|
}
|
|
|
|
|
2018-01-08 14:42:08 +01:00
|
|
|
/**
|
|
|
|
* Get the app's root NavController.
|
|
|
|
*
|
2019-09-10 16:48:56 +02:00
|
|
|
* @return Root NavController.
|
2018-01-08 14:42:08 +01:00
|
|
|
*/
|
2018-01-29 10:05:20 +01:00
|
|
|
getRootNavController(): NavController {
|
|
|
|
// Function getRootNav is deprecated. Get the first root nav, there should always be one.
|
2018-01-08 14:42:08 +01:00
|
|
|
return this.appCtrl.getRootNavs()[0];
|
|
|
|
}
|
2017-10-26 15:03:08 +02:00
|
|
|
|
2019-12-04 12:36:43 +01:00
|
|
|
/**
|
|
|
|
* Returns whether the user agent is controlled by automation. I.e. Behat testing.
|
|
|
|
*
|
2019-12-11 08:21:20 +01:00
|
|
|
* @return True if the user agent is controlled by automation, false otherwise.
|
2019-12-04 12:36:43 +01:00
|
|
|
*/
|
|
|
|
static isAutomated(): boolean {
|
|
|
|
return !!navigator.webdriver;
|
|
|
|
}
|
|
|
|
|
2018-12-11 16:25:27 +01:00
|
|
|
/**
|
|
|
|
* Checks if the app is running in a 64 bits desktop environment (not browser).
|
|
|
|
*
|
2019-09-10 16:48:56 +02:00
|
|
|
* @return Whether the app is running in a 64 bits desktop environment (not browser).
|
2018-12-11 16:25:27 +01:00
|
|
|
*/
|
|
|
|
is64Bits(): boolean {
|
|
|
|
const process = (<any> window).process;
|
|
|
|
|
|
|
|
return this.isDesktop() && process.arch == 'x64';
|
|
|
|
}
|
|
|
|
|
2019-10-02 17:44:45 +02:00
|
|
|
/**
|
|
|
|
* Checks if the app is running in an Android mobile or tablet device.
|
|
|
|
*
|
|
|
|
* @return Whether the app is running in an Android mobile or tablet device.
|
|
|
|
*/
|
|
|
|
isAndroid(): boolean {
|
|
|
|
return this.platform.is('android');
|
|
|
|
}
|
|
|
|
|
2017-10-26 15:03:08 +02:00
|
|
|
/**
|
|
|
|
* Checks if the app is running in a desktop environment (not browser).
|
|
|
|
*
|
2019-09-10 16:48:56 +02:00
|
|
|
* @return Whether the app is running in a desktop environment (not browser).
|
2017-10-26 15:03:08 +02:00
|
|
|
*/
|
2018-01-29 10:05:20 +01:00
|
|
|
isDesktop(): boolean {
|
|
|
|
const process = (<any> window).process;
|
|
|
|
|
2017-10-26 15:03:08 +02:00
|
|
|
return !!(process && process.versions && typeof process.versions.electron != 'undefined');
|
2018-01-08 14:42:08 +01:00
|
|
|
}
|
2017-10-26 15:03:08 +02:00
|
|
|
|
2019-10-02 17:44:45 +02:00
|
|
|
/**
|
|
|
|
* Checks if the app is running in an iOS mobile or tablet device.
|
|
|
|
*
|
|
|
|
* @return Whether the app is running in an iOS mobile or tablet device.
|
|
|
|
*/
|
|
|
|
isIOS(): boolean {
|
|
|
|
return this.platform.is('ios');
|
|
|
|
}
|
|
|
|
|
2017-10-26 15:03:08 +02:00
|
|
|
/**
|
|
|
|
* Check if the keyboard is visible.
|
|
|
|
*
|
2019-09-10 16:48:56 +02:00
|
|
|
* @return Whether keyboard is visible.
|
2017-10-26 15:03:08 +02:00
|
|
|
*/
|
2018-01-29 10:05:20 +01:00
|
|
|
isKeyboardVisible(): boolean {
|
2017-10-26 15:03:08 +02:00
|
|
|
return this.isKeyboardShown;
|
2018-01-08 14:42:08 +01:00
|
|
|
}
|
2017-10-26 15:03:08 +02:00
|
|
|
|
2017-12-08 13:45:30 +01:00
|
|
|
/**
|
|
|
|
* Check if the app is running in a Linux environment.
|
|
|
|
*
|
2019-09-10 16:48:56 +02:00
|
|
|
* @return Whether it's running in a Linux environment.
|
2017-12-08 13:45:30 +01:00
|
|
|
*/
|
2018-01-29 10:05:20 +01:00
|
|
|
isLinux(): boolean {
|
2017-12-08 13:45:30 +01:00
|
|
|
if (!this.isDesktop()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
2018-01-29 10:05:20 +01:00
|
|
|
return require('os').platform().indexOf('linux') === 0;
|
|
|
|
} catch (ex) {
|
2017-12-08 13:45:30 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if the app is running in a Mac OS environment.
|
|
|
|
*
|
2019-09-10 16:48:56 +02:00
|
|
|
* @return Whether it's running in a Mac OS environment.
|
2017-12-08 13:45:30 +01:00
|
|
|
*/
|
2018-01-29 10:05:20 +01:00
|
|
|
isMac(): boolean {
|
2017-12-08 13:45:30 +01:00
|
|
|
if (!this.isDesktop()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
2018-01-29 10:05:20 +01:00
|
|
|
return require('os').platform().indexOf('darwin') === 0;
|
|
|
|
} catch (ex) {
|
2017-12-08 13:45:30 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-23 16:18:32 +02:00
|
|
|
/**
|
|
|
|
* Check if the main menu is open.
|
|
|
|
*
|
2019-09-10 16:48:56 +02:00
|
|
|
* @return Whether the main menu is open.
|
2019-05-23 16:18:32 +02:00
|
|
|
*/
|
|
|
|
isMainMenuOpen(): boolean {
|
|
|
|
return typeof this.mainMenuOpen != 'undefined';
|
|
|
|
}
|
|
|
|
|
2017-10-26 15:03:08 +02:00
|
|
|
/**
|
|
|
|
* Checks if the app is running in a mobile or tablet device (Cordova).
|
|
|
|
*
|
2019-09-10 16:48:56 +02:00
|
|
|
* @return Whether the app is running in a mobile or tablet device.
|
2017-10-26 15:03:08 +02:00
|
|
|
*/
|
2018-01-29 10:05:20 +01:00
|
|
|
isMobile(): boolean {
|
2017-10-26 15:03:08 +02:00
|
|
|
return this.platform.is('cordova');
|
2018-01-08 14:42:08 +01:00
|
|
|
}
|
2017-10-26 15:03:08 +02:00
|
|
|
|
2018-02-07 14:15:53 +01:00
|
|
|
/**
|
|
|
|
* Checks if the current window is wider than a mobile.
|
|
|
|
*
|
2019-09-10 16:48:56 +02:00
|
|
|
* @return Whether the app the current window is wider than a mobile.
|
2018-02-07 14:15:53 +01:00
|
|
|
*/
|
|
|
|
isWide(): boolean {
|
|
|
|
return this.platform.width() > 768;
|
|
|
|
}
|
|
|
|
|
2017-10-26 15:03:08 +02:00
|
|
|
/**
|
|
|
|
* Returns whether we are online.
|
|
|
|
*
|
2019-09-10 16:48:56 +02:00
|
|
|
* @return Whether the app is online.
|
2017-10-26 15:03:08 +02:00
|
|
|
*/
|
2018-01-29 10:05:20 +01:00
|
|
|
isOnline(): boolean {
|
2019-05-30 12:28:13 +02:00
|
|
|
if (this.forceOffline) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-10-26 15:03:08 +02:00
|
|
|
let online = this.network.type !== null && this.network.type != Connection.NONE && this.network.type != Connection.UNKNOWN;
|
|
|
|
// Double check we are not online because we cannot rely 100% in Cordova APIs. Also, check it in browser.
|
|
|
|
if (!online && navigator.onLine) {
|
|
|
|
online = true;
|
|
|
|
}
|
2018-01-29 10:05:20 +01:00
|
|
|
|
2017-10-26 15:03:08 +02:00
|
|
|
return online;
|
2018-01-08 14:42:08 +01:00
|
|
|
}
|
2017-10-26 15:03:08 +02:00
|
|
|
|
2017-12-28 16:10:26 +01:00
|
|
|
/**
|
2017-10-26 15:03:08 +02:00
|
|
|
* Check if device uses a limited connection.
|
|
|
|
*
|
2019-09-10 16:48:56 +02:00
|
|
|
* @return Whether the device uses a limited connection.
|
2017-10-26 15:03:08 +02:00
|
|
|
*/
|
2018-01-29 10:05:20 +01:00
|
|
|
isNetworkAccessLimited(): boolean {
|
|
|
|
const type = this.network.type;
|
2017-10-26 15:03:08 +02:00
|
|
|
if (type === null) {
|
|
|
|
// Plugin not defined, probably in browser.
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-01-29 10:05:20 +01:00
|
|
|
const limited = [Connection.CELL_2G, Connection.CELL_3G, Connection.CELL_4G, Connection.CELL];
|
|
|
|
|
2017-10-26 15:03:08 +02:00
|
|
|
return limited.indexOf(type) > -1;
|
2018-01-08 14:42:08 +01:00
|
|
|
}
|
2017-10-26 15:03:08 +02:00
|
|
|
|
2018-10-03 16:11:09 +02:00
|
|
|
/**
|
|
|
|
* Check if device uses a wifi connection.
|
|
|
|
*
|
2019-09-10 16:48:56 +02:00
|
|
|
* @return Whether the device uses a wifi connection.
|
2018-10-03 16:11:09 +02:00
|
|
|
*/
|
|
|
|
isWifi(): boolean {
|
|
|
|
return this.isOnline() && !this.isNetworkAccessLimited();
|
|
|
|
}
|
|
|
|
|
2017-12-08 13:45:30 +01:00
|
|
|
/**
|
|
|
|
* Check if the app is running in a Windows environment.
|
|
|
|
*
|
2019-09-10 16:48:56 +02:00
|
|
|
* @return Whether it's running in a Windows environment.
|
2017-12-08 13:45:30 +01:00
|
|
|
*/
|
2018-01-29 10:05:20 +01:00
|
|
|
isWindows(): boolean {
|
2017-12-08 13:45:30 +01:00
|
|
|
if (!this.isDesktop()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
2018-01-29 10:05:20 +01:00
|
|
|
return require('os').platform().indexOf('win') === 0;
|
|
|
|
} catch (ex) {
|
2017-12-08 13:45:30 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-26 15:03:08 +02:00
|
|
|
/**
|
|
|
|
* Open the keyboard.
|
|
|
|
*/
|
2018-01-29 10:05:20 +01:00
|
|
|
openKeyboard(): void {
|
2017-10-26 15:03:08 +02:00
|
|
|
// Open keyboard is not supported in desktop and in iOS.
|
|
|
|
if (this.isMobile() && !this.platform.is('ios')) {
|
|
|
|
this.keyboard.show();
|
|
|
|
}
|
2018-01-08 14:42:08 +01:00
|
|
|
}
|
2017-10-26 15:03:08 +02:00
|
|
|
|
2019-05-23 16:18:32 +02:00
|
|
|
/**
|
|
|
|
* Set a main menu as open or not.
|
|
|
|
*
|
2019-09-10 16:48:56 +02:00
|
|
|
* @param id Main menu ID.
|
|
|
|
* @param open Whether it's open or not.
|
2019-05-23 16:18:32 +02:00
|
|
|
*/
|
|
|
|
setMainMenuOpen(id: number, open: boolean): void {
|
|
|
|
if (open) {
|
|
|
|
this.mainMenuOpen = id;
|
|
|
|
this.events.trigger(CoreEventsProvider.MAIN_MENU_OPEN);
|
|
|
|
} else if (this.mainMenuOpen == id) {
|
|
|
|
delete this.mainMenuOpen;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-26 15:03:08 +02:00
|
|
|
/**
|
|
|
|
* Start an SSO authentication process.
|
|
|
|
* Please notice that this function should be called when the app receives the new token from the browser,
|
|
|
|
* NOT when the browser is opened.
|
|
|
|
*/
|
2018-01-29 10:05:20 +01:00
|
|
|
startSSOAuthentication(): void {
|
2017-12-08 15:15:27 +01:00
|
|
|
let cancelTimeout,
|
|
|
|
resolvePromise;
|
|
|
|
|
2018-01-29 10:05:20 +01:00
|
|
|
this.ssoAuthenticationPromise = new Promise((resolve, reject): void => {
|
2017-12-08 15:15:27 +01:00
|
|
|
resolvePromise = resolve;
|
2017-10-26 15:03:08 +02:00
|
|
|
|
|
|
|
// Resolve it automatically after 10 seconds (it should never take that long).
|
2017-12-08 15:15:27 +01:00
|
|
|
cancelTimeout = setTimeout(() => {
|
2017-10-26 15:03:08 +02:00
|
|
|
this.finishSSOAuthentication();
|
|
|
|
}, 10000);
|
2017-12-08 15:15:27 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
// Store the resolve function in the promise itself.
|
2018-01-29 10:05:20 +01:00
|
|
|
(<any> this.ssoAuthenticationPromise).resolve = resolvePromise;
|
2017-10-26 15:03:08 +02:00
|
|
|
|
2017-12-08 15:15:27 +01:00
|
|
|
// If the promise is resolved because finishSSOAuthentication is called, stop the cancel promise.
|
|
|
|
this.ssoAuthenticationPromise.then(() => {
|
|
|
|
clearTimeout(cancelTimeout);
|
2017-10-26 15:03:08 +02:00
|
|
|
});
|
2018-01-08 14:42:08 +01:00
|
|
|
}
|
2017-10-26 15:03:08 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Finish an SSO authentication process.
|
|
|
|
*/
|
2018-01-29 10:05:20 +01:00
|
|
|
finishSSOAuthentication(): void {
|
2017-10-26 15:03:08 +02:00
|
|
|
if (this.ssoAuthenticationPromise) {
|
2018-01-29 10:05:20 +01:00
|
|
|
(<any> this.ssoAuthenticationPromise).resolve && (<any> this.ssoAuthenticationPromise).resolve();
|
2017-10-26 15:03:08 +02:00
|
|
|
this.ssoAuthenticationPromise = undefined;
|
|
|
|
}
|
2018-01-08 14:42:08 +01:00
|
|
|
}
|
2017-10-26 15:03:08 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if there's an ongoing SSO authentication process.
|
|
|
|
*
|
2019-09-10 16:48:56 +02:00
|
|
|
* @return Whether there's a SSO authentication ongoing.
|
2017-10-26 15:03:08 +02:00
|
|
|
*/
|
2018-01-29 10:05:20 +01:00
|
|
|
isSSOAuthenticationOngoing(): boolean {
|
2017-10-26 15:03:08 +02:00
|
|
|
return !!this.ssoAuthenticationPromise;
|
2018-01-08 14:42:08 +01:00
|
|
|
}
|
2017-10-26 15:03:08 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a promise that will be resolved once SSO authentication finishes.
|
|
|
|
*
|
2019-09-10 16:48:56 +02:00
|
|
|
* @return Promise resolved once SSO authentication finishes.
|
2017-10-26 15:03:08 +02:00
|
|
|
*/
|
2018-01-29 10:05:20 +01:00
|
|
|
waitForSSOAuthentication(): Promise<any> {
|
2017-10-26 15:03:08 +02:00
|
|
|
return this.ssoAuthenticationPromise || Promise.resolve();
|
2018-01-08 14:42:08 +01:00
|
|
|
}
|
2017-10-26 15:03:08 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve redirect data.
|
|
|
|
*
|
2019-09-10 16:48:56 +02:00
|
|
|
* @return Object with siteid, state, params and timemodified.
|
2017-10-26 15:03:08 +02:00
|
|
|
*/
|
2018-01-29 10:05:20 +01:00
|
|
|
getRedirect(): CoreRedirectData {
|
2017-10-26 15:03:08 +02:00
|
|
|
if (localStorage && localStorage.getItem) {
|
|
|
|
try {
|
2018-01-29 10:05:20 +01:00
|
|
|
const data: CoreRedirectData = {
|
2018-03-23 11:57:13 +01:00
|
|
|
siteId: localStorage.getItem('CoreRedirectSiteId'),
|
|
|
|
page: localStorage.getItem('CoreRedirectState'),
|
|
|
|
params: localStorage.getItem('CoreRedirectParams'),
|
|
|
|
timemodified: parseInt(localStorage.getItem('CoreRedirectTime'), 10)
|
2017-10-26 15:03:08 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
if (data.params) {
|
|
|
|
data.params = JSON.parse(data.params);
|
|
|
|
}
|
|
|
|
|
|
|
|
return data;
|
2018-01-29 10:05:20 +01:00
|
|
|
} catch (ex) {
|
2017-10-26 15:03:08 +02:00
|
|
|
this.logger.error('Error loading redirect data:', ex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return {};
|
2018-01-08 14:42:08 +01:00
|
|
|
}
|
2017-10-26 15:03:08 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Store redirect params.
|
|
|
|
*
|
2019-09-10 16:48:56 +02:00
|
|
|
* @param siteId Site ID.
|
|
|
|
* @param page Page to go.
|
|
|
|
* @param params Page params.
|
2017-10-26 15:03:08 +02:00
|
|
|
*/
|
2018-01-29 10:05:20 +01:00
|
|
|
storeRedirect(siteId: string, page: string, params: any): void {
|
2017-10-26 15:03:08 +02:00
|
|
|
if (localStorage && localStorage.setItem) {
|
|
|
|
try {
|
2018-03-23 11:57:13 +01:00
|
|
|
localStorage.setItem('CoreRedirectSiteId', siteId);
|
|
|
|
localStorage.setItem('CoreRedirectState', page);
|
|
|
|
localStorage.setItem('CoreRedirectParams', JSON.stringify(params));
|
|
|
|
localStorage.setItem('CoreRedirectTime', String(Date.now()));
|
2018-01-29 10:05:20 +01:00
|
|
|
} catch (ex) {
|
|
|
|
// Ignore errors.
|
|
|
|
}
|
2017-10-26 15:03:08 +02:00
|
|
|
}
|
2018-01-08 14:42:08 +01:00
|
|
|
}
|
2018-11-21 15:33:09 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Implement the backbutton actions pile.
|
|
|
|
*/
|
|
|
|
backButtonAction(): void {
|
|
|
|
let x = 0;
|
|
|
|
for (; x < this.backActions.length; x++) {
|
|
|
|
if (this.backActions[x].priority < 1000) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// Stop in the first action taken.
|
|
|
|
if (this.backActions[x].fn()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Close open modals if any.
|
|
|
|
if (this.menuCtrl && this.menuCtrl.isOpen()) {
|
|
|
|
this.menuCtrl.close();
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remaining actions will have priority less than 1000.
|
|
|
|
for (; x < this.backActions.length; x++) {
|
|
|
|
if (this.backActions[x].priority < 500) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// Stop in the first action taken.
|
|
|
|
if (this.backActions[x].fn()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Nothing found, go back.
|
|
|
|
const navPromise = this.appCtrl.navPop();
|
|
|
|
if (navPromise) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// No views to go back to.
|
|
|
|
|
|
|
|
// Remaining actions will have priority less than 500.
|
|
|
|
for (; x < this.backActions.length; x++) {
|
|
|
|
// Stop in the first action taken.
|
|
|
|
if (this.backActions[x].fn()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ionic will decide (exit the app).
|
|
|
|
this.appCtrl.goBack();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The back button event is triggered when the user presses the native
|
|
|
|
* platform's back button, also referred to as the "hardware" back button.
|
|
|
|
* This event is only used within Cordova apps running on Android and
|
|
|
|
* Windows platforms. This event is not fired on iOS since iOS doesn't come
|
|
|
|
* with a hardware back button in the same sense an Android or Windows device
|
|
|
|
* does.
|
|
|
|
*
|
|
|
|
* Registering a hardware back button action and setting a priority allows
|
|
|
|
* apps to control which action should be called when the hardware back
|
|
|
|
* button is pressed. This method decides which of the registered back button
|
|
|
|
* actions has the highest priority and should be called.
|
|
|
|
*
|
2019-09-10 16:48:56 +02:00
|
|
|
* @param fn Called when the back button is pressed,
|
|
|
|
* if this registered action has the highest priority.
|
|
|
|
* @param priority Set the priority for this action. All actions sorted by priority will be executed since one of
|
|
|
|
* them returns true.
|
|
|
|
* * Priorities higher or equal than 1000 will go before closing modals
|
|
|
|
* * Priorities lower than 500 will only be executed if you are in the first state of the app (before exit).
|
|
|
|
* @return A function that, when called, will unregister
|
|
|
|
* the back button action.
|
2018-11-21 15:33:09 +01:00
|
|
|
*/
|
|
|
|
registerBackButtonAction(fn: Function, priority: number = 0): Function {
|
|
|
|
const action = { fn: fn, priority: priority };
|
|
|
|
|
|
|
|
this.backActions.push(action);
|
|
|
|
|
|
|
|
this.backActions.sort((a, b) => {
|
|
|
|
return b.priority - a.priority;
|
|
|
|
});
|
|
|
|
|
|
|
|
return (): boolean => {
|
|
|
|
const index = this.backActions.indexOf(action);
|
|
|
|
|
|
|
|
return index >= 0 && !!this.backActions.splice(index, 1);
|
|
|
|
};
|
|
|
|
}
|
2019-05-03 11:53:34 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Set StatusBar color depending on platform.
|
|
|
|
*/
|
|
|
|
setStatusBarColor(): void {
|
|
|
|
if (typeof CoreConfigConstants.statusbarbgios == 'string' && this.platform.is('ios')) {
|
|
|
|
// IOS Status bar properties.
|
|
|
|
this.statusBar.overlaysWebView(false);
|
|
|
|
this.statusBar.backgroundColorByHexString(CoreConfigConstants.statusbarbgios);
|
|
|
|
CoreConfigConstants.statusbarlighttextios ? this.statusBar.styleLightContent() : this.statusBar.styleDefault();
|
|
|
|
} else if (typeof CoreConfigConstants.statusbarbgandroid == 'string' && this.platform.is('android')) {
|
|
|
|
// Android Status bar properties.
|
|
|
|
this.statusBar.backgroundColorByHexString(CoreConfigConstants.statusbarbgandroid);
|
|
|
|
CoreConfigConstants.statusbarlighttextandroid ? this.statusBar.styleLightContent() : this.statusBar.styleDefault();
|
|
|
|
} else if (typeof CoreConfigConstants.statusbarbg == 'string') {
|
|
|
|
// Generic Status bar properties.
|
|
|
|
this.platform.is('ios') && this.statusBar.overlaysWebView(false);
|
|
|
|
this.statusBar.backgroundColorByHexString(CoreConfigConstants.statusbarbg);
|
|
|
|
CoreConfigConstants.statusbarlighttext ? this.statusBar.styleLightContent() : this.statusBar.styleDefault();
|
|
|
|
} else {
|
|
|
|
// Default Status bar properties.
|
|
|
|
this.platform.is('android') ? this.statusBar.styleLightContent() : this.statusBar.styleDefault();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reset StatusBar color if any was set.
|
|
|
|
*/
|
|
|
|
resetStatusBarColor(): void {
|
|
|
|
if (typeof CoreConfigConstants.statusbarbgremotetheme == 'string' &&
|
|
|
|
((typeof CoreConfigConstants.statusbarbgios == 'string' && this.platform.is('ios')) ||
|
|
|
|
(typeof CoreConfigConstants.statusbarbgandroid == 'string' && this.platform.is('android')) ||
|
|
|
|
typeof CoreConfigConstants.statusbarbg == 'string')) {
|
|
|
|
// If the status bar has been overriden and there's a fallback color for remote themes, use it now.
|
|
|
|
this.platform.is('ios') && this.statusBar.overlaysWebView(false);
|
|
|
|
this.statusBar.backgroundColorByHexString(CoreConfigConstants.statusbarbgremotetheme);
|
|
|
|
CoreConfigConstants.statusbarlighttextremotetheme ?
|
|
|
|
this.statusBar.styleLightContent() : this.statusBar.styleDefault();
|
|
|
|
}
|
|
|
|
}
|
2019-05-30 12:28:13 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Set value of forceOffline flag. If true, the app will think the device is offline.
|
|
|
|
*
|
2019-09-10 16:48:56 +02:00
|
|
|
* @param value Value to set.
|
2019-05-30 12:28:13 +02:00
|
|
|
*/
|
|
|
|
setForceOffline(value: boolean): void {
|
|
|
|
this.forceOffline = !!value;
|
|
|
|
}
|
2017-10-26 15:03:08 +02:00
|
|
|
}
|
2020-03-24 12:18:32 +01:00
|
|
|
|
|
|
|
export class CoreApp extends makeSingleton(CoreAppProvider) {}
|