2020-10-06 10:48:26 +02:00
|
|
|
// (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.
|
|
|
|
|
2020-12-01 18:35:07 +01:00
|
|
|
import { Injectable } from '@angular/core';
|
2020-10-06 10:48:26 +02:00
|
|
|
|
2020-10-07 10:53:19 +02:00
|
|
|
import { CoreDB } from '@services/db';
|
2020-10-22 12:48:23 +02:00
|
|
|
import { CoreEvents } from '@singletons/events';
|
2020-10-07 10:53:19 +02:00
|
|
|
import { SQLiteDB, SQLiteDBTableSchema } from '@classes/sqlitedb';
|
|
|
|
|
2022-06-20 18:19:18 +02:00
|
|
|
import { makeSingleton, Keyboard, StatusBar, Device } from '@singletons';
|
2020-10-06 10:48:26 +02:00
|
|
|
import { CoreLogger } from '@singletons/logger';
|
2020-11-24 09:31:11 +01:00
|
|
|
import { CoreColors } from '@singletons/colors';
|
2020-12-01 18:37:24 +01:00
|
|
|
import { DBNAME, SCHEMA_VERSIONS_TABLE_NAME, SCHEMA_VERSIONS_TABLE_SCHEMA, SchemaVersionsDBEntry } from '@services/database/app';
|
2021-02-02 18:41:58 +01:00
|
|
|
import { CoreObject } from '@singletons/object';
|
2022-01-28 15:19:01 +01:00
|
|
|
import { CoreRedirectPayload } from './navigator';
|
2022-02-08 14:46:51 +01:00
|
|
|
import { CoreDatabaseCachingStrategy, CoreDatabaseTableProxy } from '@classes/database/database-table-proxy';
|
|
|
|
import { asyncInstance } from '../utils/async-instance';
|
|
|
|
import { CoreDatabaseTable } from '@classes/database/database-table';
|
2022-05-30 18:13:43 +02:00
|
|
|
import { CorePromisedValue } from '@classes/promised-value';
|
|
|
|
import { Subscription } from 'rxjs';
|
2022-05-11 14:05:14 +02:00
|
|
|
import { CorePlatform } from '@services/platform';
|
2022-07-12 10:44:45 +02:00
|
|
|
import { CoreNetwork, CoreNetworkConnection } from '@services/network';
|
2020-10-14 08:29:01 +02:00
|
|
|
|
2020-10-06 10:48:26 +02:00
|
|
|
/**
|
2020-10-07 10:53:19 +02:00
|
|
|
* Factory to provide some global functionalities, like access to the global app database.
|
2020-10-07 10:11:20 +02:00
|
|
|
*
|
2020-10-07 10:53:19 +02:00
|
|
|
* @description
|
|
|
|
* Each service or component should be responsible of creating their own database tables. Example:
|
|
|
|
*
|
2020-10-07 10:11:20 +02:00
|
|
|
* ```ts
|
2020-10-07 10:53:19 +02:00
|
|
|
* constructor(appProvider: CoreAppProvider) {
|
|
|
|
* this.appDB = appProvider.getDB();
|
|
|
|
* this.appDB.createTableFromSchema(this.tableSchema);
|
|
|
|
* }
|
2020-10-07 10:11:20 +02:00
|
|
|
* ```
|
2020-10-06 10:48:26 +02:00
|
|
|
*/
|
2020-11-19 16:35:17 +01:00
|
|
|
@Injectable({ providedIn: 'root' })
|
2020-10-07 10:53:19 +02:00
|
|
|
export class CoreAppProvider {
|
2020-10-14 08:29:01 +02:00
|
|
|
|
2021-08-11 14:32:53 +02:00
|
|
|
protected db?: SQLiteDB;
|
2020-10-07 10:11:20 +02:00
|
|
|
protected logger: CoreLogger;
|
2022-05-30 18:13:43 +02:00
|
|
|
protected ssoAuthenticationDeferred?: CorePromisedValue<void>;
|
2020-10-07 10:53:19 +02:00
|
|
|
protected isKeyboardShown = false;
|
2020-10-14 08:29:01 +02:00
|
|
|
protected keyboardOpening = false;
|
|
|
|
protected keyboardClosing = false;
|
2021-02-02 18:41:58 +01:00
|
|
|
protected redirect?: CoreRedirectData;
|
2022-02-08 14:46:51 +01:00
|
|
|
protected schemaVersionsTable = asyncInstance<CoreDatabaseTable<SchemaVersionsDBEntry, 'name'>>();
|
2020-10-07 10:53:19 +02:00
|
|
|
|
2020-12-11 13:14:34 +01:00
|
|
|
constructor() {
|
2020-12-01 18:37:24 +01:00
|
|
|
this.logger = CoreLogger.getInstance('CoreAppProvider');
|
2020-10-07 10:53:19 +02:00
|
|
|
}
|
|
|
|
|
2020-10-06 10:48:26 +02:00
|
|
|
/**
|
2020-10-07 10:53:19 +02:00
|
|
|
* Returns whether the user agent is controlled by automation. I.e. Behat testing.
|
|
|
|
*
|
|
|
|
* @return True if the user agent is controlled by automation, false otherwise.
|
2020-10-06 10:48:26 +02:00
|
|
|
*/
|
2020-10-07 10:53:19 +02:00
|
|
|
static isAutomated(): boolean {
|
|
|
|
return !!navigator.webdriver;
|
|
|
|
}
|
2020-10-06 10:48:26 +02:00
|
|
|
|
2022-11-16 12:20:48 +01:00
|
|
|
/**
|
|
|
|
* Returns the forced timezone to use. Timezone is forced for automated tests.
|
|
|
|
*
|
|
|
|
* @return Timezone. Undefined to use the user's timezone.
|
|
|
|
*/
|
|
|
|
static getForcedTimezone(): string | undefined {
|
|
|
|
if (CoreAppProvider.isAutomated()) {
|
|
|
|
// Use the same timezone forced for LMS in tests.
|
|
|
|
return 'Australia/Perth';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-01 18:37:24 +01:00
|
|
|
/**
|
2020-12-09 13:39:18 +01:00
|
|
|
* Initialize database.
|
2020-12-01 18:37:24 +01:00
|
|
|
*/
|
2020-12-09 13:39:18 +01:00
|
|
|
async initializeDatabase(): Promise<void> {
|
2022-02-08 14:46:51 +01:00
|
|
|
const database = this.getDB();
|
2020-12-01 18:37:24 +01:00
|
|
|
|
2022-02-08 14:46:51 +01:00
|
|
|
await database.createTableFromSchema(SCHEMA_VERSIONS_TABLE_SCHEMA);
|
2020-12-01 18:37:24 +01:00
|
|
|
|
2022-02-08 14:46:51 +01:00
|
|
|
const schemaVersionsTable = new CoreDatabaseTableProxy<SchemaVersionsDBEntry, 'name'>(
|
|
|
|
{ cachingStrategy: CoreDatabaseCachingStrategy.Eager },
|
|
|
|
database,
|
|
|
|
SCHEMA_VERSIONS_TABLE_NAME,
|
|
|
|
['name'],
|
|
|
|
);
|
|
|
|
|
|
|
|
await schemaVersionsTable.initialize();
|
|
|
|
|
|
|
|
this.schemaVersionsTable.setInstance(schemaVersionsTable);
|
2020-12-01 18:37:24 +01:00
|
|
|
}
|
|
|
|
|
2020-10-06 10:48:26 +02:00
|
|
|
/**
|
2020-10-07 10:53:19 +02:00
|
|
|
* Check if the browser supports mediaDevices.getUserMedia.
|
|
|
|
*
|
|
|
|
* @return Whether the function is supported.
|
2020-10-06 10:48:26 +02:00
|
|
|
*/
|
2020-10-07 10:53:19 +02:00
|
|
|
canGetUserMedia(): boolean {
|
|
|
|
return !!(navigator && navigator.mediaDevices && navigator.mediaDevices.getUserMedia);
|
|
|
|
}
|
2020-10-06 10:48:26 +02:00
|
|
|
|
|
|
|
/**
|
2020-10-07 10:53:19 +02:00
|
|
|
* Check if the browser supports MediaRecorder.
|
|
|
|
*
|
|
|
|
* @return Whether the function is supported.
|
2020-10-06 10:48:26 +02:00
|
|
|
*/
|
2020-10-07 10:53:19 +02:00
|
|
|
canRecordMedia(): boolean {
|
2020-10-14 08:29:01 +02:00
|
|
|
return !!window.MediaRecorder;
|
2020-10-07 10:53:19 +02:00
|
|
|
}
|
2020-10-06 10:48:26 +02:00
|
|
|
|
|
|
|
/**
|
2020-10-07 10:53:19 +02:00
|
|
|
* Closes the keyboard.
|
2020-10-06 10:48:26 +02:00
|
|
|
*/
|
2020-10-07 10:53:19 +02:00
|
|
|
closeKeyboard(): void {
|
2022-05-11 14:05:14 +02:00
|
|
|
if (CorePlatform.isMobile()) {
|
2021-03-02 11:41:04 +01:00
|
|
|
Keyboard.hide();
|
2020-10-07 10:53:19 +02:00
|
|
|
}
|
|
|
|
}
|
2020-10-06 10:48:26 +02:00
|
|
|
|
2020-10-07 10:53:19 +02:00
|
|
|
/**
|
|
|
|
* Install and upgrade a certain schema.
|
|
|
|
*
|
|
|
|
* @param schema The schema to create.
|
|
|
|
* @return Promise resolved when done.
|
|
|
|
*/
|
2020-10-14 08:29:01 +02:00
|
|
|
async createTablesFromSchema(schema: CoreAppSchema): Promise<void> {
|
2020-10-07 10:53:19 +02:00
|
|
|
this.logger.debug(`Apply schema to app DB: ${schema.name}`);
|
2020-10-06 10:48:26 +02:00
|
|
|
|
2022-02-08 14:46:51 +01:00
|
|
|
const oldVersion = await this.getInstalledSchemaVersion(schema);
|
2020-10-07 10:53:19 +02:00
|
|
|
|
|
|
|
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) {
|
2021-08-11 14:32:53 +02:00
|
|
|
await this.getDB().createTablesFromSchema(schema.tables);
|
2020-10-07 10:53:19 +02:00
|
|
|
}
|
2022-09-08 09:27:11 +02:00
|
|
|
if (schema.install && oldVersion === 0) {
|
|
|
|
await schema.install(this.getDB());
|
|
|
|
}
|
2021-04-14 10:06:34 +02:00
|
|
|
if (schema.migrate && oldVersion > 0) {
|
2021-08-11 14:32:53 +02:00
|
|
|
await schema.migrate(this.getDB(), oldVersion);
|
2020-10-07 10:53:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Set installed version.
|
2022-02-08 14:46:51 +01:00
|
|
|
await this.schemaVersionsTable.insert({ name: schema.name, version: schema.version });
|
2020-10-07 10:53:19 +02:00
|
|
|
}
|
|
|
|
|
2022-02-08 14:47:39 +01:00
|
|
|
/**
|
|
|
|
* Delete table schema.
|
|
|
|
*
|
|
|
|
* @param name Schema name.
|
|
|
|
*/
|
|
|
|
async deleteTableSchema(name: string): Promise<void> {
|
|
|
|
await this.schemaVersionsTable.deleteByPrimaryKey({ name });
|
|
|
|
}
|
|
|
|
|
2020-10-07 10:53:19 +02:00
|
|
|
/**
|
|
|
|
* Get the application global database.
|
|
|
|
*
|
|
|
|
* @return App's DB.
|
|
|
|
*/
|
|
|
|
getDB(): SQLiteDB {
|
2021-08-11 14:32:53 +02:00
|
|
|
if (!this.db) {
|
|
|
|
this.db = CoreDB.getDB(DBNAME);
|
|
|
|
}
|
|
|
|
|
2020-10-07 10:53:19 +02:00
|
|
|
return this.db;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get an ID for a main menu.
|
|
|
|
*
|
|
|
|
* @return Main menu ID.
|
2021-01-14 12:58:51 +01:00
|
|
|
* @deprecated since 3.9.5. No longer supported.
|
2020-10-07 10:53:19 +02:00
|
|
|
*/
|
|
|
|
getMainMenuId(): number {
|
2021-01-14 12:58:51 +01:00
|
|
|
return 0;
|
2020-10-07 10:53:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get app store URL.
|
|
|
|
*
|
2021-05-13 14:12:42 +02:00
|
|
|
* @param storesConfig Config params to send the user to the right place.
|
2020-10-07 10:53:19 +02:00
|
|
|
* @return Store URL.
|
|
|
|
*/
|
2020-10-21 16:32:27 +02:00
|
|
|
getAppStoreUrl(storesConfig: CoreStoreConfig): string | undefined {
|
2020-10-07 10:53:19 +02:00
|
|
|
if (this.isIOS() && storesConfig.ios) {
|
|
|
|
return 'itms-apps://itunes.apple.com/app/' + storesConfig.ios;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.isAndroid() && storesConfig.android) {
|
|
|
|
return 'market://details?id=' + storesConfig.android;
|
|
|
|
}
|
|
|
|
|
2022-05-11 14:05:14 +02:00
|
|
|
if (CorePlatform.isMobile() && storesConfig.mobile) {
|
2020-10-07 10:53:19 +02:00
|
|
|
return storesConfig.mobile;
|
|
|
|
}
|
|
|
|
|
2020-10-21 16:32:27 +02:00
|
|
|
return storesConfig.default;
|
2020-10-07 10:53:19 +02:00
|
|
|
}
|
|
|
|
|
2020-11-06 15:32:00 +01:00
|
|
|
/**
|
|
|
|
* Get platform major version number.
|
|
|
|
*/
|
|
|
|
getPlatformMajorVersion(): number {
|
2022-05-11 14:05:14 +02:00
|
|
|
if (!CorePlatform.isMobile()) {
|
2020-11-06 15:32:00 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-03-02 11:41:04 +01:00
|
|
|
return Number(Device.version?.split('.')[0]);
|
2020-11-06 15:32:00 +01:00
|
|
|
}
|
|
|
|
|
2020-10-07 10:53:19 +02:00
|
|
|
/**
|
|
|
|
* Checks if the app is running in a 64 bits desktop environment (not browser).
|
|
|
|
*
|
2020-10-29 12:39:15 +01:00
|
|
|
* @return false.
|
2020-12-11 13:14:34 +01:00
|
|
|
* @deprecated since 3.9.5 Desktop support has been removed.
|
2020-10-07 10:53:19 +02:00
|
|
|
*/
|
|
|
|
is64Bits(): boolean {
|
2020-10-29 12:39:15 +01:00
|
|
|
return false;
|
2020-10-07 10:53:19 +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 {
|
2022-06-20 18:19:18 +02:00
|
|
|
return CorePlatform.isMobile() && CorePlatform.is('android');
|
2020-10-07 10:53:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Checks if the app is running in a desktop environment (not browser).
|
|
|
|
*
|
2020-10-29 12:39:15 +01:00
|
|
|
* @return false.
|
2020-12-11 13:14:34 +01:00
|
|
|
* @deprecated since 3.9.5 Desktop support has been removed.
|
2020-10-07 10:53:19 +02:00
|
|
|
*/
|
|
|
|
isDesktop(): boolean {
|
2020-10-14 08:29:01 +02:00
|
|
|
return false;
|
2020-10-07 10:53:19 +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 {
|
2022-06-20 18:19:18 +02:00
|
|
|
return CorePlatform.isMobile() && !CorePlatform.is('android');
|
2020-10-07 10:53:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if the keyboard is closing.
|
|
|
|
*
|
|
|
|
* @return Whether keyboard is closing (animating).
|
|
|
|
*/
|
|
|
|
isKeyboardClosing(): boolean {
|
2020-10-14 08:29:01 +02:00
|
|
|
return this.keyboardClosing;
|
2020-10-07 10:53:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if the keyboard is being opened.
|
|
|
|
*
|
|
|
|
* @return Whether keyboard is opening (animating).
|
|
|
|
*/
|
|
|
|
isKeyboardOpening(): boolean {
|
2020-10-14 08:29:01 +02:00
|
|
|
return this.keyboardOpening;
|
2020-10-07 10:53:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if the keyboard is visible.
|
|
|
|
*
|
|
|
|
* @return Whether keyboard is visible.
|
|
|
|
*/
|
|
|
|
isKeyboardVisible(): boolean {
|
|
|
|
return this.isKeyboardShown;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if the app is running in a Linux environment.
|
|
|
|
*
|
2020-10-29 12:39:15 +01:00
|
|
|
* @return false.
|
2020-12-11 13:14:34 +01:00
|
|
|
* @deprecated since 3.9.5 Desktop support has been removed.
|
2020-10-07 10:53:19 +02:00
|
|
|
*/
|
|
|
|
isLinux(): boolean {
|
2020-10-29 12:39:15 +01:00
|
|
|
return false;
|
2020-10-07 10:53:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if the app is running in a Mac OS environment.
|
|
|
|
*
|
2020-10-29 12:39:15 +01:00
|
|
|
* @return false.
|
2020-12-11 13:14:34 +01:00
|
|
|
* @deprecated since 3.9.5 Desktop support has been removed.
|
2020-10-07 10:53:19 +02:00
|
|
|
*/
|
|
|
|
isMac(): boolean {
|
2020-10-29 12:39:15 +01:00
|
|
|
return false;
|
2020-10-07 10:53:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if the main menu is open.
|
|
|
|
*
|
|
|
|
* @return Whether the main menu is open.
|
2021-01-14 12:58:51 +01:00
|
|
|
* @deprecated since 3.9.5. No longer supported.
|
2020-10-07 10:53:19 +02:00
|
|
|
*/
|
|
|
|
isMainMenuOpen(): boolean {
|
2021-01-14 12:58:51 +01:00
|
|
|
return false;
|
2020-10-07 10:53:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Checks if the app is running in a mobile or tablet device (Cordova).
|
|
|
|
*
|
|
|
|
* @return Whether the app is running in a mobile or tablet device.
|
2022-05-11 14:05:14 +02:00
|
|
|
* @deprecated since 4.1. use CorePlatform instead.
|
2020-10-07 10:53:19 +02:00
|
|
|
*/
|
|
|
|
isMobile(): boolean {
|
2022-05-11 14:05:14 +02:00
|
|
|
return CorePlatform.isMobile();
|
2020-10-07 10:53:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Checks if the current window is wider than a mobile.
|
|
|
|
*
|
|
|
|
* @return Whether the app the current window is wider than a mobile.
|
|
|
|
*/
|
|
|
|
isWide(): boolean {
|
2022-06-20 18:19:18 +02:00
|
|
|
return CorePlatform.width() > 768;
|
2020-10-07 10:53:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns whether we are online.
|
|
|
|
*
|
|
|
|
* @return Whether the app is online.
|
2022-05-11 14:06:42 +02:00
|
|
|
* @deprecated since 4.1.0. Use CoreNetwork instead.
|
2020-10-07 10:53:19 +02:00
|
|
|
*/
|
|
|
|
isOnline(): boolean {
|
2022-05-11 14:06:42 +02:00
|
|
|
return CoreNetwork.isOnline();
|
2020-10-07 10:53:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if device uses a limited connection.
|
|
|
|
*
|
|
|
|
* @return Whether the device uses a limited connection.
|
2022-05-11 14:06:42 +02:00
|
|
|
* @deprecated since 4.1.0. Use CoreNetwork instead.
|
2020-10-07 10:53:19 +02:00
|
|
|
*/
|
|
|
|
isNetworkAccessLimited(): boolean {
|
2022-05-11 14:06:42 +02:00
|
|
|
return CoreNetwork.isNetworkAccessLimited();
|
2020-10-07 10:53:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if device uses a wifi connection.
|
|
|
|
*
|
|
|
|
* @return Whether the device uses a wifi connection.
|
2022-05-11 14:06:42 +02:00
|
|
|
* @deprecated since 4.1.0. Use CoreNetwork instead.
|
2020-10-07 10:53:19 +02:00
|
|
|
*/
|
|
|
|
isWifi(): boolean {
|
2022-05-11 14:06:42 +02:00
|
|
|
return CoreNetwork.isWifi();
|
2020-10-07 10:53:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if the app is running in a Windows environment.
|
|
|
|
*
|
2020-10-29 12:39:15 +01:00
|
|
|
* @return false.
|
2020-12-11 13:14:34 +01:00
|
|
|
* @deprecated since 3.9.5 Desktop support has been removed.
|
2020-10-07 10:53:19 +02:00
|
|
|
*/
|
|
|
|
isWindows(): boolean {
|
2020-10-29 12:39:15 +01:00
|
|
|
return false;
|
2020-10-07 10:53:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Open the keyboard.
|
|
|
|
*/
|
|
|
|
openKeyboard(): void {
|
|
|
|
// Open keyboard is not supported in desktop and in iOS.
|
|
|
|
if (this.isAndroid()) {
|
2021-03-02 11:41:04 +01:00
|
|
|
Keyboard.show();
|
2020-10-07 10:53:19 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-01 18:35:07 +01:00
|
|
|
/**
|
|
|
|
* Notify that Keyboard has been shown.
|
|
|
|
*
|
|
|
|
* @param keyboardHeight Keyboard height.
|
|
|
|
*/
|
|
|
|
onKeyboardShow(keyboardHeight: number): void {
|
|
|
|
document.body.classList.add('keyboard-is-open');
|
|
|
|
this.setKeyboardShown(true);
|
|
|
|
// Error on iOS calculating size.
|
|
|
|
// More info: https://github.com/ionic-team/ionic-plugin-keyboard/issues/276 .
|
|
|
|
CoreEvents.trigger(CoreEvents.KEYBOARD_CHANGE, keyboardHeight);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Notify that Keyboard has been hidden.
|
|
|
|
*/
|
|
|
|
onKeyboardHide(): void {
|
|
|
|
document.body.classList.remove('keyboard-is-open');
|
|
|
|
this.setKeyboardShown(false);
|
|
|
|
CoreEvents.trigger(CoreEvents.KEYBOARD_CHANGE, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Notify that Keyboard is about to be shown.
|
|
|
|
*/
|
|
|
|
onKeyboardWillShow(): void {
|
|
|
|
this.keyboardOpening = true;
|
|
|
|
this.keyboardClosing = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Notify that Keyboard is about to be hidden.
|
|
|
|
*/
|
|
|
|
onKeyboardWillHide(): void {
|
|
|
|
this.keyboardOpening = false;
|
|
|
|
this.keyboardClosing = true;
|
|
|
|
}
|
|
|
|
|
2020-10-07 10:53:19 +02:00
|
|
|
/**
|
|
|
|
* Set keyboard shown or hidden.
|
|
|
|
*
|
2022-03-15 11:18:48 +01:00
|
|
|
* @param shown Whether the keyboard is shown or hidden.
|
2020-10-07 10:53:19 +02:00
|
|
|
*/
|
|
|
|
protected setKeyboardShown(shown: boolean): void {
|
|
|
|
this.isKeyboardShown = shown;
|
2020-10-14 08:29:01 +02:00
|
|
|
this.keyboardOpening = false;
|
|
|
|
this.keyboardClosing = false;
|
2020-10-07 10:53:19 +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.
|
|
|
|
*/
|
|
|
|
startSSOAuthentication(): void {
|
2022-05-30 18:13:43 +02:00
|
|
|
this.ssoAuthenticationDeferred = new CorePromisedValue();
|
2020-10-07 10:53:19 +02:00
|
|
|
|
2020-10-14 08:29:01 +02:00
|
|
|
// Resolve it automatically after 10 seconds (it should never take that long).
|
2020-10-15 12:11:07 +02:00
|
|
|
const cancelTimeout = setTimeout(() => this.finishSSOAuthentication(), 10000);
|
2020-10-07 10:53:19 +02:00
|
|
|
|
|
|
|
// If the promise is resolved because finishSSOAuthentication is called, stop the cancel promise.
|
2020-10-15 12:11:07 +02:00
|
|
|
// eslint-disable-next-line promise/catch-or-return
|
2022-05-30 18:13:43 +02:00
|
|
|
this.ssoAuthenticationDeferred.then(() => clearTimeout(cancelTimeout));
|
2020-10-07 10:53:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Finish an SSO authentication process.
|
|
|
|
*/
|
|
|
|
finishSSOAuthentication(): void {
|
2020-10-14 08:29:01 +02:00
|
|
|
if (this.ssoAuthenticationDeferred) {
|
|
|
|
this.ssoAuthenticationDeferred.resolve();
|
|
|
|
this.ssoAuthenticationDeferred = undefined;
|
2020-10-07 10:53:19 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if there's an ongoing SSO authentication process.
|
|
|
|
*
|
|
|
|
* @return Whether there's a SSO authentication ongoing.
|
|
|
|
*/
|
|
|
|
isSSOAuthenticationOngoing(): boolean {
|
2020-10-14 08:29:01 +02:00
|
|
|
return !!this.ssoAuthenticationDeferred;
|
2020-10-07 10:53:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a promise that will be resolved once SSO authentication finishes.
|
|
|
|
*
|
|
|
|
* @return Promise resolved once SSO authentication finishes.
|
|
|
|
*/
|
2020-10-14 08:29:01 +02:00
|
|
|
async waitForSSOAuthentication(): Promise<void> {
|
2022-05-30 18:13:43 +02:00
|
|
|
await this.ssoAuthenticationDeferred;
|
2020-10-07 10:53:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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> {
|
2022-05-30 18:13:43 +02:00
|
|
|
let deferred: CorePromisedValue<void> | null = new CorePromisedValue();
|
|
|
|
let resumeSubscription: Subscription | null = null;
|
|
|
|
let timeoutId: number | null = null;
|
2020-10-07 10:53:19 +02:00
|
|
|
|
2020-10-14 08:29:01 +02:00
|
|
|
const stopWaiting = () => {
|
|
|
|
if (!deferred) {
|
2020-10-07 10:53:19 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-10-14 08:29:01 +02:00
|
|
|
deferred.resolve();
|
2022-05-30 18:13:43 +02:00
|
|
|
resumeSubscription?.unsubscribe();
|
2020-10-07 10:53:19 +02:00
|
|
|
timeoutId && clearTimeout(timeoutId);
|
|
|
|
|
2020-10-14 08:29:01 +02:00
|
|
|
deferred = null;
|
2020-10-07 10:53:19 +02:00
|
|
|
};
|
|
|
|
|
2022-06-20 18:19:18 +02:00
|
|
|
resumeSubscription = CorePlatform.resume.subscribe(stopWaiting);
|
2022-05-30 18:13:43 +02:00
|
|
|
timeoutId = timeout ? window.setTimeout(stopWaiting, timeout) : null;
|
2020-10-07 10:53:19 +02:00
|
|
|
|
2022-05-30 18:13:43 +02:00
|
|
|
await deferred;
|
2020-10-06 10:48:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2021-02-02 18:41:58 +01:00
|
|
|
* Read redirect data from local storage and clear it if it existed.
|
2020-10-06 10:48:26 +02:00
|
|
|
*/
|
2021-02-02 18:41:58 +01:00
|
|
|
consumeStorageRedirect(): void {
|
|
|
|
if (!localStorage?.getItem) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
// Read data from storage.
|
|
|
|
const jsonData = localStorage.getItem('CoreRedirect');
|
|
|
|
|
|
|
|
if (!jsonData) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Clear storage.
|
|
|
|
localStorage.removeItem('CoreRedirect');
|
|
|
|
|
|
|
|
// Remember redirect data.
|
|
|
|
const data: CoreRedirectData = JSON.parse(jsonData);
|
2020-10-06 10:48:26 +02:00
|
|
|
|
2021-02-02 18:41:58 +01:00
|
|
|
if (!CoreObject.isEmpty(data)) {
|
|
|
|
this.redirect = data;
|
2020-10-06 10:48:26 +02:00
|
|
|
}
|
2021-02-02 18:41:58 +01:00
|
|
|
} catch (error) {
|
|
|
|
this.logger.error('Error loading redirect data:', error);
|
2020-10-06 10:48:26 +02:00
|
|
|
}
|
2021-02-02 18:41:58 +01:00
|
|
|
}
|
2020-10-06 10:48:26 +02:00
|
|
|
|
2021-04-27 10:55:43 +02:00
|
|
|
/**
|
|
|
|
* Retrieve and forget redirect data.
|
|
|
|
*
|
|
|
|
* @return Redirect data if any.
|
|
|
|
*/
|
2021-04-27 15:44:41 +02:00
|
|
|
consumeMemoryRedirect(): CoreRedirectData | null {
|
2021-04-27 10:55:43 +02:00
|
|
|
const redirect = this.getRedirect();
|
|
|
|
|
|
|
|
this.forgetRedirect();
|
|
|
|
|
|
|
|
return redirect;
|
|
|
|
}
|
|
|
|
|
2021-06-04 12:56:13 +02:00
|
|
|
/**
|
|
|
|
* Close the app.
|
|
|
|
*/
|
|
|
|
closeApp(): void {
|
|
|
|
const nav = <any> window.navigator; // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
|
|
nav.app?.exitApp();
|
|
|
|
}
|
|
|
|
|
2021-02-02 18:41:58 +01:00
|
|
|
/**
|
|
|
|
* Forget redirect data.
|
|
|
|
*/
|
|
|
|
forgetRedirect(): void {
|
|
|
|
delete this.redirect;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve redirect data.
|
|
|
|
*
|
2021-04-27 10:55:43 +02:00
|
|
|
* @return Redirect data if any.
|
2021-02-02 18:41:58 +01:00
|
|
|
*/
|
|
|
|
getRedirect(): CoreRedirectData | null {
|
|
|
|
return this.redirect || null;
|
2020-10-06 10:48:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Store redirect params.
|
|
|
|
*
|
|
|
|
* @param siteId Site ID.
|
2022-01-28 15:19:01 +01:00
|
|
|
* @param redirectData Redirect data.
|
2020-10-06 10:48:26 +02:00
|
|
|
*/
|
2022-01-28 15:19:01 +01:00
|
|
|
storeRedirect(siteId: string, redirectData: CoreRedirectPayload = {}): void {
|
|
|
|
if (!redirectData.redirectPath && !redirectData.urlToOpen) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-02-02 18:41:58 +01:00
|
|
|
try {
|
|
|
|
const redirect: CoreRedirectData = {
|
|
|
|
siteId,
|
|
|
|
timemodified: Date.now(),
|
2022-01-28 15:19:01 +01:00
|
|
|
...redirectData,
|
2021-02-02 18:41:58 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
localStorage.setItem('CoreRedirect', JSON.stringify(redirect));
|
2021-10-22 17:02:54 +02:00
|
|
|
} catch {
|
2021-02-02 18:41:58 +01:00
|
|
|
// Ignore errors.
|
2020-10-06 10:48:26 +02:00
|
|
|
}
|
|
|
|
}
|
2020-10-07 10:53:19 +02:00
|
|
|
|
|
|
|
/**
|
2021-02-25 12:16:23 +01:00
|
|
|
* Register a back button action.
|
|
|
|
* This function is deprecated and no longer works. You should now use Ionic events directly, please see:
|
|
|
|
* https://ionicframework.com/docs/developing/hardware-back-button
|
2020-10-07 10:53:19 +02:00
|
|
|
*
|
2021-02-25 12:16:23 +01:00
|
|
|
* @param callback Called when the back button is pressed.
|
|
|
|
* @param priority Priority.
|
2020-10-14 08:29:01 +02:00
|
|
|
* @return A function that, when called, will unregister the back button action.
|
2021-02-25 12:16:23 +01:00
|
|
|
* @deprecated since 3.9.5
|
2020-10-07 10:53:19 +02:00
|
|
|
*/
|
2021-02-25 12:16:23 +01:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
|
|
registerBackButtonAction(callback: () => boolean, priority = 0): () => boolean {
|
|
|
|
return () => false;
|
2020-10-07 10:53:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set StatusBar color depending on platform.
|
2020-11-23 12:30:30 +01:00
|
|
|
*
|
|
|
|
* @param color RGB color to use as status bar background. If not set the css variable will be read.
|
2020-10-07 10:53:19 +02:00
|
|
|
*/
|
2020-11-23 12:30:30 +01:00
|
|
|
setStatusBarColor(color?: string): void {
|
2022-05-11 14:05:14 +02:00
|
|
|
if (!CorePlatform.isMobile()) {
|
2021-06-11 12:16:18 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-11-23 12:30:30 +01:00
|
|
|
if (!color) {
|
2021-05-14 13:23:59 +02:00
|
|
|
// Get the default color to change it.
|
2021-11-22 11:22:00 +01:00
|
|
|
color = CoreColors.getToolbarBackgroundColor();
|
2020-11-23 12:30:30 +01:00
|
|
|
}
|
|
|
|
|
2021-05-14 13:23:59 +02:00
|
|
|
this.logger.debug(`Set status bar color ${color}`);
|
|
|
|
|
2020-11-23 12:30:30 +01:00
|
|
|
const useLightText = CoreColors.isWhiteContrastingBetter(color);
|
2021-06-10 11:01:21 +02:00
|
|
|
|
|
|
|
// styleDefault will use white text on iOS when darkmode is on. Force the background to black.
|
|
|
|
if (this.isIOS() && !useLightText && window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
2022-02-08 07:56:45 +01:00
|
|
|
StatusBar.backgroundColorByHexString('#000000');
|
|
|
|
StatusBar.styleLightContent();
|
2021-06-10 11:01:21 +02:00
|
|
|
} else {
|
2022-02-08 07:56:45 +01:00
|
|
|
StatusBar.backgroundColorByHexString(color);
|
|
|
|
useLightText ? StatusBar.styleLightContent() : StatusBar.styleDefault();
|
2021-06-10 11:01:21 +02:00
|
|
|
}
|
2020-10-07 10:53:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reset StatusBar color if any was set.
|
2020-11-23 12:30:30 +01:00
|
|
|
*
|
2021-09-06 08:03:01 +02:00
|
|
|
* @deprecated since 3.9.5. Use setStatusBarColor passing the color of the new statusbar color, or no color to reset.
|
2020-10-07 10:53:19 +02:00
|
|
|
*/
|
|
|
|
resetStatusBarColor(): void {
|
2020-11-23 12:30:30 +01:00
|
|
|
this.setStatusBarColor();
|
2020-10-07 10:53:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set value of forceOffline flag. If true, the app will think the device is offline.
|
|
|
|
*
|
|
|
|
* @param value Value to set.
|
2022-07-12 10:44:45 +02:00
|
|
|
* @deprecated since 4.1.0. Use CoreNetwork.setForceConnectionMode instead.
|
2020-10-07 10:53:19 +02:00
|
|
|
*/
|
|
|
|
setForceOffline(value: boolean): void {
|
2022-07-12 10:44:45 +02:00
|
|
|
CoreNetwork.setForceConnectionMode(value ? CoreNetworkConnection.NONE : CoreNetworkConnection.WIFI);
|
2020-10-07 10:53:19 +02:00
|
|
|
}
|
2020-10-14 08:29:01 +02:00
|
|
|
|
2022-02-08 14:46:51 +01:00
|
|
|
/**
|
|
|
|
* Get the installed version for the given schema.
|
|
|
|
*
|
|
|
|
* @param schema App schema.
|
|
|
|
* @returns Installed version number, or 0 if the schema is not installed.
|
|
|
|
*/
|
|
|
|
protected async getInstalledSchemaVersion(schema: CoreAppSchema): Promise<number> {
|
|
|
|
try {
|
|
|
|
// Fetch installed version of the schema.
|
|
|
|
const entry = await this.schemaVersionsTable.getOneByPrimaryKey({ name: schema.name });
|
|
|
|
|
|
|
|
return entry.version;
|
2022-09-08 09:27:11 +02:00
|
|
|
} catch {
|
2022-02-08 14:46:51 +01:00
|
|
|
// No installed version yet.
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-06 10:48:26 +02:00
|
|
|
}
|
|
|
|
|
2021-03-15 14:31:00 +01:00
|
|
|
export const CoreApp = makeSingleton(CoreAppProvider);
|
2020-10-07 10:53:19 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Data stored for a redirect to another page/site.
|
|
|
|
*/
|
2022-01-28 15:19:01 +01:00
|
|
|
export type CoreRedirectData = CoreRedirectPayload & {
|
|
|
|
siteId?: string; // ID of the site to load.
|
|
|
|
timemodified?: number; // Timestamp when this redirect was last modified.
|
2020-10-07 10:53:19 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Store config data.
|
|
|
|
*/
|
|
|
|
export type CoreStoreConfig = {
|
|
|
|
/**
|
|
|
|
* ID of the Apple store where the mobile iOS app is uploaded.
|
|
|
|
*/
|
|
|
|
ios?: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* ID of the Google play store where the android app is uploaded.
|
|
|
|
*/
|
|
|
|
android?: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fallback URL when the mobile options is not set.
|
|
|
|
*/
|
|
|
|
mobile?: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fallback URL when the other fallbacks options are not set.
|
|
|
|
*/
|
|
|
|
default?: string;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* App DB schema and migration function.
|
|
|
|
*/
|
|
|
|
export type 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.
|
|
|
|
*
|
2022-09-08 09:27:11 +02:00
|
|
|
* Called when upgrading the schema, after creating the defined tables.
|
2020-10-07 10:53:19 +02:00
|
|
|
*
|
|
|
|
* @param db The affected DB.
|
|
|
|
* @param oldVersion Old version of the schema or 0 if not installed.
|
|
|
|
* @return Promise resolved when done.
|
|
|
|
*/
|
2020-10-14 08:29:01 +02:00
|
|
|
migrate?(db: SQLiteDB, oldVersion: number): Promise<void>;
|
2022-09-08 09:27:11 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Make changes to install the schema.
|
|
|
|
*
|
|
|
|
* Called when installing the schema, after creating the defined tables.
|
|
|
|
*
|
|
|
|
* @param db Site database.
|
|
|
|
* @return Promise resolved when done.
|
|
|
|
*/
|
|
|
|
install?(db: SQLiteDB): Promise<void> | void;
|
2020-10-14 08:29:01 +02:00
|
|
|
};
|