MOBILE-2261 core: Implement App provider

main
Dani Palou 2017-10-26 15:03:08 +02:00
parent 28da03ff4b
commit bdffb24c50
6 changed files with 442 additions and 20 deletions

85
package-lock.json generated
View File

@ -59,6 +59,16 @@
"resolved": "https://registry.npmjs.org/@ionic-native/core/-/core-4.3.0.tgz",
"integrity": "sha512-Pf0qCzqlVFmIpZpvo35Kl0e+1K8GUgPMcKBnN57gWh+5Ecj3dPcb+MbP4murJo/dnFsIYPYdXRZRf74hjo6gtw=="
},
"@ionic-native/keyboard": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/@ionic-native/keyboard/-/keyboard-4.3.2.tgz",
"integrity": "sha512-iTvFCONbE26+dXNdJAHmNxkfLbtN+CmiuJQUy56kvjzVvOTFI5gIZ0aE5nlbYpB1GY4DFQVw/+E+xc0Bpw5mKw=="
},
"@ionic-native/network": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/@ionic-native/network/-/network-4.3.2.tgz",
"integrity": "sha512-X8RXbOIwz3TGBsEmRjMRdo+y4VjBCkm69tHhWFPExtHWyl8fOGqXcNB2UX+R3dnEi1B95svr7TEi34dmQzEqYA=="
},
"@ionic-native/splash-screen": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/@ionic-native/splash-screen/-/splash-screen-4.3.0.tgz",
@ -85,11 +95,36 @@
"resolved": "https://registry.npmjs.org/@ionic/storage/-/storage-2.0.1.tgz",
"integrity": "sha1-uxqMJ2AH0AjXrN2kJrVgZbD9PAs="
},
"@ngx-translate/core": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/@ngx-translate/core/-/core-8.0.0.tgz",
"integrity": "sha1-dR/WtRLYDzp0jS3o38lt/vopr+A="
},
"@ngx-translate/http-loader": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@ngx-translate/http-loader/-/http-loader-2.0.0.tgz",
"integrity": "sha1-nBbQfNBwxnraJwoulAKB64JrP0M="
},
"@types/cordova": {
"version": "0.0.34",
"resolved": "https://registry.npmjs.org/@types/cordova/-/cordova-0.0.34.tgz",
"integrity": "sha1-6nrd907Ow9dimCegw54smt3HPQQ="
},
"@types/cordova-plugin-network-information": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/@types/cordova-plugin-network-information/-/cordova-plugin-network-information-0.0.3.tgz",
"integrity": "sha1-+iycaufkxX8Tt39pXaTtuzr6oBY="
},
"@types/localforage": {
"version": "0.0.30",
"resolved": "https://registry.npmjs.org/@types/localforage/-/localforage-0.0.30.tgz",
"integrity": "sha1-PWCmv23aOOP4pGlhFZg3nx9klQk="
},
"@types/promise.prototype.finally": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@types/promise.prototype.finally/-/promise.prototype.finally-2.0.2.tgz",
"integrity": "sha512-Fs99h+iFQZ4ZY2vO3+uJCrx+5KQnJ4FPerZ3oT/1L5aA7vnmK/d7Z/Ml1yHtNCh9UQcjFTR4Xo/Jss2f39Fgtw=="
},
"abbrev": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
@ -826,6 +861,11 @@
"integrity": "sha1-LO8fER4cV4cNi7uK8mUOWHzS9bQ=",
"dev": true
},
"define-properties": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz",
"integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ="
},
"delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
@ -949,6 +989,16 @@
"integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=",
"dev": true
},
"es-abstract": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.9.0.tgz",
"integrity": "sha512-kk3IJoKo7A3pWJc0OV8yZ/VEX2oSUytfekrJiqoxBlKJMFAJVJVpGdHClCCTdv+Fn2zHfpDHHIelMFhZVfef3Q=="
},
"es-to-primitive": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz",
"integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0="
},
"es3ify": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/es3ify/-/es3ify-0.1.4.tgz",
@ -1881,6 +1931,11 @@
"integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=",
"dev": true
},
"function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
},
"fwd-stream": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/fwd-stream/-/fwd-stream-1.0.4.tgz",
@ -1971,6 +2026,11 @@
"integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=",
"dev": true
},
"has": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz",
"integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg="
},
"has-ansi": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
@ -2158,6 +2218,16 @@
"integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=",
"dev": true
},
"is-callable": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.3.tgz",
"integrity": "sha1-hut1OSgF3cM69xySoO7fdO52BLI="
},
"is-date-object": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz",
"integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY="
},
"is-dotfile": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz",
@ -2230,12 +2300,22 @@
"integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=",
"dev": true
},
"is-regex": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz",
"integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE="
},
"is-stream": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
"integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=",
"dev": true
},
"is-symbol": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz",
"integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI="
},
"is-typedarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
@ -3136,6 +3216,11 @@
"integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=",
"dev": true
},
"promise.prototype.finally": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/promise.prototype.finally/-/promise.prototype.finally-3.0.1.tgz",
"integrity": "sha512-5o2ue0smn+Wn6tdkqN4wxB27lCu84UrladpMRQ3fm1tEkewOrhAGSOHwj15SRU+oE/ParFTou6XG3Jhgyb9sjw=="
},
"proxy-addr": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-1.1.5.tgz",

View File

@ -34,15 +34,23 @@
"@angular/platform-browser": "4.4.3",
"@angular/platform-browser-dynamic": "4.4.3",
"@ionic-native/core": "4.3.0",
"@ionic-native/keyboard": "^4.3.2",
"@ionic-native/network": "^4.3.2",
"@ionic-native/splash-screen": "4.3.0",
"@ionic-native/sqlite": "^4.3.2",
"@ionic-native/status-bar": "4.3.0",
"@ionic/storage": "2.0.1",
"@ngx-translate/core": "^8.0.0",
"@ngx-translate/http-loader": "^2.0.0",
"@types/cordova": "0.0.34",
"@types/cordova-plugin-network-information": "0.0.3",
"@types/promise.prototype.finally": "^2.0.2",
"electron-builder-squirrel-windows": "^19.3.0",
"electron-windows-notifications": "^1.1.13",
"ionic-angular": "3.7.1",
"ionicons": "3.0.0",
"moment": "^2.19.1",
"promise.prototype.finally": "^3.0.1",
"rxjs": "5.4.3",
"sw-toolbox": "3.6.0",
"zone.js": "0.8.18"

View File

@ -1,33 +1,55 @@
import { BrowserModule } from '@angular/platform-browser';
import { ErrorHandler, NgModule } from '@angular/core';
import { IonicApp, IonicErrorHandler, IonicModule } from 'ionic-angular';
import { HttpClient } from '@angular/common/http';
import { SplashScreen } from '@ionic-native/splash-screen';
import { StatusBar } from '@ionic-native/status-bar';
import { SQLite } from '@ionic-native/sqlite';
import { Keyboard } from '@ionic-native/keyboard';
import { Network } from '@ionic-native/network';
import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
import { MyApp } from './app.component';
import { CoreLoggerProvider } from '../providers/logger';
import { CoreDbProvider } from '../providers/db';
import { CoreAppProvider } from '../providers/app';
// For translate loader. AoT requires an exported function for factories.
export function createTranslateLoader(http: HttpClient) {
return new TranslateHttpLoader(http, './build/lang/', '.json');
}
@NgModule({
declarations: [
MyApp
],
imports: [
BrowserModule,
IonicModule.forRoot(MyApp)
],
bootstrap: [IonicApp],
entryComponents: [
MyApp
],
providers: [
StatusBar,
SplashScreen,
SQLite,
{provide: ErrorHandler, useClass: IonicErrorHandler},
CoreLoggerProvider,
CoreDbProvider
]
declarations: [
MyApp
],
imports: [
BrowserModule,
IonicModule.forRoot(MyApp),
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: (createTranslateLoader),
deps: [HttpClient]
}
})
],
bootstrap: [IonicApp],
entryComponents: [
MyApp
],
providers: [
StatusBar,
SplashScreen,
SQLite,
Keyboard,
Network,
{provide: ErrorHandler, useClass: IonicErrorHandler},
CoreLoggerProvider,
CoreDbProvider,
CoreAppProvider,
]
})
export class AppModule {}

View File

@ -2,4 +2,8 @@ import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app.module';
import { shim } from 'promise.prototype.finally';
shim(); // Support promise.finally.
platformBrowserDynamic().bootstrapModule(AppModule);

View File

@ -0,0 +1,303 @@
// (C) Copyright 2015 Martin Dougiamas
//
// 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 { Platform } from 'ionic-angular';
import { Keyboard } from '@ionic-native/keyboard';
import { Network } from '@ionic-native/network';
import { CoreDbProvider } from './db';
import { CoreLoggerProvider } from './logger';
/**
* 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 {
DBNAME = 'MoodleMobile';
db;
logger;
ssoAuthenticationPromise : Promise<any>;
isKeyboardShown: boolean = false;
constructor(private dbProvider: CoreDbProvider, private platform: Platform, private keyboard: Keyboard,
private network: Network, logger: CoreLoggerProvider) {
this.logger = logger.getInstance('CoreAppProvider');
this.keyboard.onKeyboardShow().subscribe((data) => {
this.isKeyboardShown = true;
});
this.keyboard.onKeyboardHide().subscribe((data) => {
this.isKeyboardShown = false;
});
}
/**
* Check if the browser supports mediaDevices.getUserMedia.
*
* @return {boolean} Whether the function is supported.
*/
canGetUserMedia() : boolean {
return !!(navigator && navigator.mediaDevices && navigator.mediaDevices.getUserMedia);
}
/**
* Check if the browser supports MediaRecorder.
*
* @return {boolean} Whether the function is supported.
*/
canRecordMedia() : boolean {
return !!(<any>window).MediaRecorder;
};
/**
* Closes the keyboard.
*/
closeKeyboard() : void {
if (this.isMobile()) {
this.keyboard.close();
}
};
/**
* Get the application global database.
*
* @return {any} App's DB.
*/
getDB() : any {
if (typeof this.db == 'undefined') {
this.db = this.dbProvider.getDB(this.DBNAME);
}
return this.db;
};
/**
* Core init process for the app.
*
* @description
* This should be the first init process of all, no other process should run until we are certain that the cordova plugins
* are loaded, which is what platform.ready tells us.
*
* Reserved for core use, do not call directly.
*
* @protected
* @return Promise resolved when ready.
*/
initProcess() : Promise<any> {
return this.platform.ready();
};
/**
* Checks if the app is running in a desktop environment (not browser).
*
* @return {boolean} Whether the app is running in a desktop environment (not browser).
*/
isDesktop() : boolean {
let process = (<any>window).process;
return !!(process && process.versions && typeof process.versions.electron != 'undefined');
};
/**
* Check if the keyboard is visible.
*
* @return {boolean} Whether keyboard is visible.
*/
isKeyboardVisible() : boolean {
return this.isKeyboardShown;
};
/**
* Checks if the app is running in a mobile or tablet device (Cordova).
*
* @return {boolean} Whether the app is running in a mobile or tablet device.
*/
isMobile() : boolean {
return this.platform.is('cordova');
};
/**
* Returns whether we are online.
*
* @return {boolean} Whether the app is online.
*/
isOnline() : boolean {
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;
}
return online;
};
/*
* Check if device uses a limited connection.
*
* @return {boolean} Whether the device uses a limited connection.
*/
isNetworkAccessLimited() : boolean {
let type = this.network.type;
if (type === null) {
// Plugin not defined, probably in browser.
return false;
}
let limited = [Connection.CELL_2G, Connection.CELL_3G, Connection.CELL_4G, Connection.CELL];
return limited.indexOf(type) > -1;
};
/**
* Instantly returns if the app is ready.
*
* To be notified when the app is ready, refer to {@link $mmApp#ready}.
*
* @return {Boolean} True when it is, false when not.
* @todo
*/
isReady() {
// var promise = $injector.get('$mmInitDelegate').ready();
// return promise.$$state.status === 1;
};
/**
* Open the keyboard.
*/
openKeyboard() : void {
// Open keyboard is not supported in desktop and in iOS.
if (this.isMobile() && !this.platform.is('ios')) {
this.keyboard.show();
}
};
/**
* Resolves when the app is ready.
*
* Usage:
*
* $mmApp.ready().then(function() {
* // What you want to do.
* });
*
* @return {Promise} Resolved when the app is initialised. Never rejected.
* @todo
*/
ready() {
// Injects to prevent circular dependencies.
// return $injector.get('$mmInitDelegate').ready();
};
/**
* 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 {
this.ssoAuthenticationPromise = new Promise((resolve, reject) => {
// Store the resolve function in the promise itself.
(<any>this.ssoAuthenticationPromise).resolve = resolve;
// Resolve it automatically after 10 seconds (it should never take that long).
let cancel = setTimeout(() => {
this.finishSSOAuthentication();
}, 10000);
// If the promise is resolved because finishSSOAuthentication is called, stop the cancel promise.
this.ssoAuthenticationPromise.then(() => {
clearTimeout(cancel);
});
});
};
/**
* Finish an SSO authentication process.
*/
finishSSOAuthentication() : void {
if (this.ssoAuthenticationPromise) {
(<any>this.ssoAuthenticationPromise).resolve && (<any>this.ssoAuthenticationPromise).resolve();
this.ssoAuthenticationPromise = undefined;
}
};
/**
* Check if there's an ongoing SSO authentication process.
*
* @return {boolean} Whether there's a SSO authentication ongoing.
*/
isSSOAuthenticationOngoing() : boolean {
return !!this.ssoAuthenticationPromise;
};
/**
* Returns a promise that will be resolved once SSO authentication finishes.
*
* @return {Promise<any>} Promise resolved once SSO authentication finishes.
*/
waitForSSOAuthentication() : Promise<any> {
return this.ssoAuthenticationPromise || Promise.resolve();
};
/**
* Retrieve redirect data.
*
* @return {object} Object with siteid, state, params and timemodified.
*/
getRedirect() : object {
if (localStorage && localStorage.getItem) {
try {
let data = {
siteid: localStorage.getItem('mmCoreRedirectSiteId'),
state: localStorage.getItem('mmCoreRedirectState'),
params: localStorage.getItem('mmCoreRedirectParams'),
timemodified: localStorage.getItem('mmCoreRedirectTime')
};
if (data.params) {
data.params = JSON.parse(data.params);
}
return data;
} catch(ex) {
this.logger.error('Error loading redirect data:', ex);
}
}
return {};
};
/**
* Store redirect params.
*
* @param {string} siteId Site ID.
* @param {string} page Page to go.
* @param {object} params Page params.
*/
storeRedirect(siteId: string, page: string, params: object) : void {
if (localStorage && localStorage.setItem) {
try {
localStorage.setItem('mmCoreRedirectSiteId', siteId);
localStorage.setItem('mmCoreRedirectState', page);
localStorage.setItem('mmCoreRedirectParams', JSON.stringify(params));
localStorage.setItem('mmCoreRedirectTime', String(Date.now()));
} catch(ex) {}
}
};
}

View File

@ -36,7 +36,7 @@ export class CoreDbProvider {
* @param {boolean} forceNew True if it should always create a new instance.
* @return {SQLiteDB} DB.
*/
getDB(name: string, forceNew: boolean) : SQLiteDB {
getDB(name: string, forceNew?: boolean) : SQLiteDB {
if (typeof this.dbInstances[name] === 'undefined' || forceNew) {
this.dbInstances[name] = new SQLiteDB(name, this.sqlite, this.platform);
}