2018-02-23 14:44:17 +01:00
|
|
|
// (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.
|
|
|
|
|
2018-06-21 15:59:06 +02:00
|
|
|
import { Injectable, NgZone } from '@angular/core';
|
2019-02-01 15:35:13 +01:00
|
|
|
import { Platform } from 'ionic-angular';
|
2018-02-27 08:39:45 +01:00
|
|
|
import { Badge } from '@ionic-native/badge';
|
|
|
|
import { Push, PushObject, PushOptions } from '@ionic-native/push';
|
|
|
|
import { Device } from '@ionic-native/device';
|
2019-02-01 15:35:13 +01:00
|
|
|
import { TranslateService } from '@ngx-translate/core';
|
2018-02-23 14:44:17 +01:00
|
|
|
import { CoreAppProvider } from '@providers/app';
|
2018-09-19 10:54:41 +02:00
|
|
|
import { CoreInitDelegate } from '@providers/init';
|
2018-02-23 14:44:17 +01:00
|
|
|
import { CoreLoggerProvider } from '@providers/logger';
|
2019-03-05 09:18:07 +01:00
|
|
|
import { CoreSitesProvider, CoreSiteSchema } from '@providers/sites';
|
2019-03-05 11:30:46 +01:00
|
|
|
import { CoreSitesFactoryProvider } from '@providers/sites-factory';
|
2018-02-23 14:44:17 +01:00
|
|
|
import { AddonPushNotificationsDelegate } from './delegate';
|
2018-02-27 08:39:45 +01:00
|
|
|
import { CoreLocalNotificationsProvider } from '@providers/local-notifications';
|
|
|
|
import { CoreUtilsProvider } from '@providers/utils/utils';
|
|
|
|
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
|
|
|
import { CoreConfigProvider } from '@providers/config';
|
2018-04-09 11:44:12 +02:00
|
|
|
import { CoreConstants } from '@core/constants';
|
2018-03-12 15:44:41 +01:00
|
|
|
import { CoreConfigConstants } from '../../../configconstants';
|
2019-01-23 13:08:45 +01:00
|
|
|
import { ILocalNotification } from '@ionic-native/local-notifications';
|
2019-03-05 11:30:46 +01:00
|
|
|
import { SQLiteDB, SQLiteDBTableSchema } from '@classes/sqlitedb';
|
2019-03-05 09:18:07 +01:00
|
|
|
import { CoreSite } from '@classes/site';
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Data needed to register a device in a Moodle site.
|
|
|
|
*/
|
|
|
|
export interface AddonPushNotificationsRegisterData {
|
|
|
|
/**
|
|
|
|
* App ID.
|
|
|
|
* @type {string}
|
|
|
|
*/
|
|
|
|
appid: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Device UUID.
|
|
|
|
* @type {string}
|
|
|
|
*/
|
|
|
|
uuid: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Device name.
|
|
|
|
* @type {string}
|
|
|
|
*/
|
|
|
|
name: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Device model.
|
|
|
|
* @type {string}
|
|
|
|
*/
|
|
|
|
model: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Device platform.
|
|
|
|
* @type {string}
|
|
|
|
*/
|
|
|
|
platform: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Device version.
|
|
|
|
* @type {string}
|
|
|
|
*/
|
|
|
|
version: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Push ID.
|
|
|
|
* @type {string}
|
|
|
|
*/
|
|
|
|
pushid: string;
|
|
|
|
}
|
2018-02-23 14:44:17 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Service to handle push notifications.
|
|
|
|
*/
|
|
|
|
@Injectable()
|
|
|
|
export class AddonPushNotificationsProvider {
|
|
|
|
protected logger;
|
|
|
|
protected pushID: string;
|
2019-03-05 11:30:46 +01:00
|
|
|
protected appDB: SQLiteDB;
|
2018-03-08 13:25:20 +01:00
|
|
|
static COMPONENT = 'AddonPushNotificationsProvider';
|
2018-02-23 14:44:17 +01:00
|
|
|
|
|
|
|
// Variables for database.
|
2018-05-30 12:13:39 +02:00
|
|
|
static BADGE_TABLE = 'addon_pushnotifications_badge';
|
2019-03-05 11:30:46 +01:00
|
|
|
static PENDING_UNREGISTER_TABLE = 'addon_pushnotifications_pending_unregister';
|
2019-03-05 09:18:07 +01:00
|
|
|
static REGISTERED_DEVICES_TABLE = 'addon_pushnotifications_registered_devices';
|
|
|
|
protected appTablesSchema: SQLiteDBTableSchema[] = [
|
2018-02-23 14:44:17 +01:00
|
|
|
{
|
2018-05-30 12:13:39 +02:00
|
|
|
name: AddonPushNotificationsProvider.BADGE_TABLE,
|
2018-02-23 14:44:17 +01:00
|
|
|
columns: [
|
|
|
|
{
|
|
|
|
name: 'siteid',
|
2018-03-08 13:25:20 +01:00
|
|
|
type: 'TEXT'
|
2018-02-23 14:44:17 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'addon',
|
|
|
|
type: 'TEXT'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'number',
|
|
|
|
type: 'INTEGER'
|
|
|
|
}
|
2018-02-27 08:39:45 +01:00
|
|
|
],
|
|
|
|
primaryKeys: ['siteid', 'addon']
|
2019-03-05 11:30:46 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
name: AddonPushNotificationsProvider.PENDING_UNREGISTER_TABLE,
|
|
|
|
columns: [
|
|
|
|
{
|
|
|
|
name: 'siteid',
|
|
|
|
type: 'TEXT',
|
|
|
|
primaryKey: true
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'siteurl',
|
|
|
|
type: 'TEXT'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'token',
|
|
|
|
type: 'TEXT'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'info',
|
|
|
|
type: 'TEXT'
|
|
|
|
}
|
|
|
|
]
|
2018-02-23 14:44:17 +01:00
|
|
|
}
|
|
|
|
];
|
2019-03-05 09:18:07 +01:00
|
|
|
protected siteSchema: CoreSiteSchema = {
|
|
|
|
name: 'AddonPushNotificationsProvider',
|
|
|
|
version: 1,
|
|
|
|
tables: [
|
|
|
|
{
|
|
|
|
name: AddonPushNotificationsProvider.REGISTERED_DEVICES_TABLE,
|
|
|
|
columns: [
|
|
|
|
{
|
|
|
|
name: 'appid',
|
|
|
|
type: 'TEXT',
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'uuid',
|
|
|
|
type: 'TEXT'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'name',
|
|
|
|
type: 'TEXT'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'model',
|
|
|
|
type: 'TEXT'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'platform',
|
|
|
|
type: 'TEXT'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'version',
|
|
|
|
type: 'TEXT'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'pushid',
|
|
|
|
type: 'TEXT'
|
|
|
|
},
|
|
|
|
],
|
|
|
|
primaryKeys: ['appid', 'uuid']
|
|
|
|
}
|
|
|
|
],
|
|
|
|
};
|
2018-02-23 14:44:17 +01:00
|
|
|
|
2018-09-19 10:54:41 +02:00
|
|
|
constructor(logger: CoreLoggerProvider, protected appProvider: CoreAppProvider, private initDelegate: CoreInitDelegate,
|
2018-02-27 08:39:45 +01:00
|
|
|
protected pushNotificationsDelegate: AddonPushNotificationsDelegate, protected sitesProvider: CoreSitesProvider,
|
|
|
|
private badge: Badge, private localNotificationsProvider: CoreLocalNotificationsProvider,
|
|
|
|
private utils: CoreUtilsProvider, private textUtils: CoreTextUtilsProvider, private push: Push,
|
2019-02-01 15:35:13 +01:00
|
|
|
private configProvider: CoreConfigProvider, private device: Device, private zone: NgZone,
|
2019-03-05 11:30:46 +01:00
|
|
|
private translate: TranslateService, private platform: Platform, private sitesFactory: CoreSitesFactoryProvider) {
|
2018-02-23 14:44:17 +01:00
|
|
|
this.logger = logger.getInstance('AddonPushNotificationsProvider');
|
|
|
|
this.appDB = appProvider.getDB();
|
2019-03-05 09:18:07 +01:00
|
|
|
this.appDB.createTablesFromSchema(this.appTablesSchema);
|
|
|
|
this.sitesProvider.registerSiteSchema(this.siteSchema);
|
2019-02-01 15:35:13 +01:00
|
|
|
|
|
|
|
platform.ready().then(() => {
|
|
|
|
// Create the default channel.
|
|
|
|
this.createDefaultChannel();
|
|
|
|
|
|
|
|
translate.onLangChange.subscribe((event: any) => {
|
|
|
|
// Update the channel name.
|
|
|
|
this.createDefaultChannel();
|
|
|
|
});
|
|
|
|
});
|
2018-02-23 14:44:17 +01:00
|
|
|
}
|
|
|
|
|
2018-02-27 08:39:45 +01:00
|
|
|
/**
|
|
|
|
* Delete all badge records for a given site.
|
|
|
|
*
|
|
|
|
* @param {string} siteId Site ID.
|
|
|
|
* @return {Promise<any>} Resolved when done.
|
|
|
|
*/
|
|
|
|
cleanSiteCounters(siteId: string): Promise<any> {
|
2018-05-30 12:13:39 +02:00
|
|
|
return this.appDB.deleteRecords(AddonPushNotificationsProvider.BADGE_TABLE, {siteid: siteId} ).finally(() => {
|
2018-02-27 08:39:45 +01:00
|
|
|
this.updateAppCounter();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-02-01 15:35:13 +01:00
|
|
|
/**
|
|
|
|
* Create the default push channel. It is used to change the name.
|
|
|
|
*
|
|
|
|
* @return {Promise<any>} Promise resolved when done.
|
|
|
|
*/
|
|
|
|
protected createDefaultChannel(): Promise<any> {
|
|
|
|
if (!this.platform.is('android')) {
|
|
|
|
return Promise.resolve();
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.push.createChannel({
|
|
|
|
id: 'PushPluginChannel',
|
|
|
|
description: this.translate.instant('core.misc'),
|
|
|
|
importance: 4
|
|
|
|
}).catch((error) => {
|
|
|
|
this.logger.error('Error changing push channel name', error);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-02-27 08:39:45 +01:00
|
|
|
/**
|
2018-03-08 13:25:20 +01:00
|
|
|
* Returns options for push notifications based on device.
|
|
|
|
*
|
|
|
|
* @return {Promise<PushOptions>} Promise with the push options resolved when done.
|
2018-02-27 08:39:45 +01:00
|
|
|
*/
|
|
|
|
protected getOptions(): Promise<PushOptions> {
|
2018-04-09 11:44:12 +02:00
|
|
|
return this.configProvider.get(CoreConstants.SETTINGS_NOTIFICATION_SOUND, true).then((soundEnabled) => {
|
2018-02-27 08:39:45 +01:00
|
|
|
return {
|
2018-03-08 13:25:20 +01:00
|
|
|
android: {
|
2019-01-31 09:05:35 +01:00
|
|
|
sound: !!soundEnabled,
|
|
|
|
icon: 'smallicon'
|
2018-03-08 13:25:20 +01:00
|
|
|
},
|
|
|
|
ios: {
|
|
|
|
alert: 'true',
|
|
|
|
badge: true,
|
|
|
|
sound: !!soundEnabled
|
|
|
|
},
|
|
|
|
windows: {
|
|
|
|
sound: !!soundEnabled
|
|
|
|
}
|
2018-02-27 08:39:45 +01:00
|
|
|
};
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-02-23 14:44:17 +01:00
|
|
|
/**
|
|
|
|
* Get the pushID for this device.
|
|
|
|
*
|
|
|
|
* @return {string} Push ID.
|
|
|
|
*/
|
|
|
|
getPushId(): string {
|
|
|
|
return this.pushID;
|
|
|
|
}
|
|
|
|
|
2019-03-05 09:18:07 +01:00
|
|
|
/**
|
|
|
|
* Get data to register the device in Moodle.
|
|
|
|
*
|
|
|
|
* @return {AddonPushNotificationsRegisterData} Data.
|
|
|
|
*/
|
|
|
|
protected getRegisterData(): AddonPushNotificationsRegisterData {
|
|
|
|
return {
|
|
|
|
appid: CoreConfigConstants.app_id,
|
|
|
|
name: this.device.manufacturer || '',
|
|
|
|
model: this.device.model,
|
|
|
|
platform: this.device.platform + '-fcm',
|
|
|
|
version: this.device.version,
|
|
|
|
pushid: this.pushID,
|
|
|
|
uuid: this.device.uuid
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2018-02-27 08:39:45 +01:00
|
|
|
/**
|
|
|
|
* Get Sitebadge counter from the database.
|
|
|
|
*
|
|
|
|
* @param {string} siteId Site ID.
|
|
|
|
* @return {Promise<any>} Promise resolved with the stored badge counter for the site.
|
|
|
|
*/
|
|
|
|
getSiteCounter(siteId: string): Promise<any> {
|
|
|
|
return this.getAddonBadge(siteId);
|
|
|
|
}
|
|
|
|
|
2018-02-23 14:44:17 +01:00
|
|
|
/**
|
|
|
|
* Function called when a push notification is clicked. Redirect the user to the right state.
|
|
|
|
*
|
|
|
|
* @param {any} notification Notification.
|
|
|
|
*/
|
|
|
|
notificationClicked(notification: any): void {
|
2018-09-19 10:54:41 +02:00
|
|
|
this.initDelegate.ready().then(() => {
|
2018-02-23 14:44:17 +01:00
|
|
|
this.pushNotificationsDelegate.clicked(notification);
|
|
|
|
});
|
|
|
|
}
|
2018-02-27 08:39:45 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* This function is called when we receive a Notification from APNS or a message notification from GCM.
|
|
|
|
* The app can be in foreground or background,
|
|
|
|
* if we are in background this code is executed when we open the app clicking in the notification bar.
|
|
|
|
*
|
|
|
|
* @param {any} notification Notification received.
|
|
|
|
*/
|
|
|
|
onMessageReceived(notification: any): void {
|
|
|
|
const data = notification ? notification.additionalData : {};
|
|
|
|
|
|
|
|
this.sitesProvider.getSite(data.site).then(() => {
|
|
|
|
if (this.utils.isTrueOrOne(data.foreground)) {
|
|
|
|
// If the app is in foreground when the notification is received, it's not shown. Let's show it ourselves.
|
|
|
|
if (this.localNotificationsProvider.isAvailable()) {
|
2019-01-23 13:08:45 +01:00
|
|
|
const localNotif: ILocalNotification = {
|
2018-02-27 08:39:45 +01:00
|
|
|
id: 1,
|
2019-02-01 15:35:13 +01:00
|
|
|
data: data,
|
2018-02-27 08:39:45 +01:00
|
|
|
title: '',
|
|
|
|
text: ''
|
|
|
|
},
|
|
|
|
promises = [];
|
|
|
|
|
|
|
|
// Apply formatText to title and message.
|
|
|
|
promises.push(this.textUtils.formatText(notification.title, true, true).then((formattedTitle) => {
|
|
|
|
localNotif.title = formattedTitle;
|
|
|
|
}).catch(() => {
|
|
|
|
localNotif.title = notification.title;
|
|
|
|
}));
|
|
|
|
|
|
|
|
promises.push(this.textUtils.formatText(notification.message, true, true).then((formattedMessage) => {
|
|
|
|
localNotif.text = formattedMessage;
|
|
|
|
}).catch(() => {
|
|
|
|
localNotif.text = notification.message;
|
|
|
|
}));
|
|
|
|
|
|
|
|
Promise.all(promises).then(() => {
|
|
|
|
this.localNotificationsProvider.schedule(localNotif, AddonPushNotificationsProvider.COMPONENT, data.site);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// Trigger a notification received event.
|
2018-09-19 10:54:41 +02:00
|
|
|
this.initDelegate.ready().then(() => {
|
2018-02-27 08:39:45 +01:00
|
|
|
data.title = notification.title;
|
|
|
|
data.message = notification.message;
|
|
|
|
this.pushNotificationsDelegate.received(data);
|
|
|
|
});
|
|
|
|
} else {
|
2018-03-01 16:55:49 +01:00
|
|
|
// The notification was clicked.
|
2018-02-27 08:39:45 +01:00
|
|
|
this.notificationClicked(data);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Unregisters a device from a certain Moodle site.
|
|
|
|
*
|
2019-03-05 09:18:07 +01:00
|
|
|
* @param {CoreSite} site Site to unregister from.
|
|
|
|
* @return {Promise<any>} Promise resolved when device is unregistered.
|
2018-02-27 08:39:45 +01:00
|
|
|
*/
|
2019-03-05 09:18:07 +01:00
|
|
|
unregisterDeviceOnMoodle(site: CoreSite): Promise<any> {
|
2018-02-27 08:39:45 +01:00
|
|
|
if (!site || !this.appProvider.isMobile()) {
|
|
|
|
return Promise.reject(null);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.logger.debug(`Unregister device on Moodle: '${site.id}'`);
|
|
|
|
|
|
|
|
const data = {
|
|
|
|
appid: CoreConfigConstants.app_id,
|
|
|
|
uuid: this.device.uuid
|
|
|
|
};
|
|
|
|
|
|
|
|
return site.write('core_user_remove_user_device', data).then((response) => {
|
|
|
|
if (!response || !response.removed) {
|
|
|
|
return Promise.reject(null);
|
|
|
|
}
|
2019-03-05 09:18:07 +01:00
|
|
|
|
2019-03-05 11:30:46 +01:00
|
|
|
const promises = [];
|
|
|
|
|
2019-03-05 09:18:07 +01:00
|
|
|
// Remove the device from the local DB.
|
2019-03-05 11:30:46 +01:00
|
|
|
promises.push(site.getDb().deleteRecords(AddonPushNotificationsProvider.REGISTERED_DEVICES_TABLE,
|
|
|
|
this.getRegisterData()));
|
|
|
|
|
|
|
|
// Remove pending unregisters for this site.
|
|
|
|
promises.push(this.appDB.deleteRecords(AddonPushNotificationsProvider.PENDING_UNREGISTER_TABLE, {siteid: site.id}));
|
|
|
|
|
|
|
|
return Promise.all(promises).catch(() => {
|
2019-03-05 09:18:07 +01:00
|
|
|
// Ignore errors.
|
|
|
|
});
|
2019-03-05 11:30:46 +01:00
|
|
|
}).catch((error) => {
|
|
|
|
if (this.utils.isWebServiceError(error)) {
|
|
|
|
// It's a WebService error, can't unregister.
|
|
|
|
return Promise.reject(error);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Store the pending unregister so it's retried again later.
|
|
|
|
return this.appDB.insertRecord(AddonPushNotificationsProvider.PENDING_UNREGISTER_TABLE, {
|
|
|
|
siteid: site.id,
|
|
|
|
siteurl: site.getURL(),
|
|
|
|
token: site.getToken(),
|
|
|
|
info: JSON.stringify(site.getInfo())
|
|
|
|
}).then(() => {
|
|
|
|
return Promise.reject(error);
|
|
|
|
});
|
2018-02-27 08:39:45 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Update Counter for an addon. It will update the refered siteId counter and the total badge.
|
|
|
|
* It will return the updated addon counter.
|
|
|
|
*
|
|
|
|
* @param {string} addon Registered addon name to set the badge number.
|
|
|
|
* @param {number} value The number to be stored.
|
|
|
|
* @param {string} [siteId] Site ID. If not defined, use current site.
|
|
|
|
* @return {Promise<any>} Promise resolved with the stored badge counter for the addon on the site.
|
|
|
|
*/
|
|
|
|
updateAddonCounter(addon: string, value: number, siteId?: string): Promise<any> {
|
|
|
|
if (this.pushNotificationsDelegate.isCounterHandlerRegistered(addon)) {
|
|
|
|
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
|
|
|
|
|
|
|
return this.saveAddonBadge(value, siteId, addon).then(() => {
|
|
|
|
return this.updateSiteCounter(siteId).then(() => {
|
|
|
|
return value;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
return Promise.resolve(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Update total badge counter of the app.
|
|
|
|
*
|
|
|
|
* @return {Promise<any>} Promise resolved with the stored badge counter for the site.
|
|
|
|
*/
|
|
|
|
updateAppCounter(): Promise<any> {
|
|
|
|
return this.sitesProvider.getSitesIds().then((sites) => {
|
|
|
|
const promises = [];
|
|
|
|
sites.forEach((siteId) => {
|
|
|
|
promises.push(this.getAddonBadge(siteId));
|
|
|
|
});
|
|
|
|
|
|
|
|
return Promise.all(promises).then((counters) => {
|
|
|
|
const total = counters.reduce((previous, counter) => {
|
2018-03-08 13:25:20 +01:00
|
|
|
// The app badge counter does not support strings, so parse to int before.
|
|
|
|
return previous + parseInt(counter, 10);
|
|
|
|
}, 0);
|
2018-02-27 08:39:45 +01:00
|
|
|
|
2018-05-07 10:08:24 +02:00
|
|
|
if (!this.appProvider.isDesktop() && !this.appProvider.isMobile()) {
|
|
|
|
// Browser doesn't have an app badge, stop.
|
|
|
|
return total;
|
|
|
|
}
|
|
|
|
|
2018-02-27 08:39:45 +01:00
|
|
|
// Set the app badge.
|
|
|
|
return this.badge.set(total).then(() => {
|
|
|
|
return total;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Update counter for a site using the stored addon data. It will update the total badge application number.
|
|
|
|
* It will return the updated site counter.
|
|
|
|
*
|
|
|
|
* @param {string} siteId Site ID.
|
|
|
|
* @return {Promise<any>} Promise resolved with the stored badge counter for the site.
|
|
|
|
*/
|
|
|
|
updateSiteCounter(siteId: string): Promise<any> {
|
|
|
|
const addons = this.pushNotificationsDelegate.getCounterHandlers(),
|
|
|
|
promises = [];
|
|
|
|
|
2018-03-01 16:55:49 +01:00
|
|
|
for (const x in addons) {
|
|
|
|
promises.push(this.getAddonBadge(siteId, addons[x]));
|
|
|
|
}
|
2018-02-27 08:39:45 +01:00
|
|
|
|
|
|
|
return Promise.all(promises).then((counters) => {
|
|
|
|
let plus = false,
|
|
|
|
total = counters.reduce((previous, counter) => {
|
|
|
|
// Check if there is a plus sign at the end of the counter.
|
|
|
|
if (counter != parseInt(counter, 10)) {
|
|
|
|
plus = true;
|
|
|
|
counter = parseInt(counter, 10);
|
|
|
|
}
|
|
|
|
|
|
|
|
return previous + counter;
|
|
|
|
}, 0);
|
|
|
|
|
|
|
|
total = plus && total > 0 ? total + '+' : total;
|
|
|
|
|
|
|
|
// Save the counter on site.
|
|
|
|
return this.saveAddonBadge(total, siteId);
|
|
|
|
}).then((siteTotal) => {
|
|
|
|
return this.updateAppCounter().then(() => {
|
|
|
|
return siteTotal;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Register a device in Apple APNS or Google GCM.
|
|
|
|
*
|
|
|
|
* @return {Promise<any>} Promise resolved when the device is registered.
|
|
|
|
*/
|
|
|
|
registerDevice(): Promise<any> {
|
|
|
|
try {
|
|
|
|
// Check if sound is enabled for notifications.
|
|
|
|
return this.getOptions().then((options) => {
|
|
|
|
const pushObject: PushObject = this.push.init(options);
|
|
|
|
|
|
|
|
pushObject.on('notification').subscribe((notification: any) => {
|
2018-06-21 15:59:06 +02:00
|
|
|
// Execute the callback in the Angular zone, so change detection doesn't stop working.
|
|
|
|
this.zone.run(() => {
|
|
|
|
this.logger.log('Received a notification', notification);
|
|
|
|
this.onMessageReceived(notification);
|
|
|
|
});
|
2018-02-27 08:39:45 +01:00
|
|
|
});
|
|
|
|
|
2018-03-20 14:53:25 +01:00
|
|
|
pushObject.on('registration').subscribe((data: any) => {
|
2018-06-21 15:59:06 +02:00
|
|
|
// Execute the callback in the Angular zone, so change detection doesn't stop working.
|
|
|
|
this.zone.run(() => {
|
|
|
|
this.pushID = data.registrationId;
|
2018-09-10 11:37:39 +02:00
|
|
|
if (this.sitesProvider.isLoggedIn()) {
|
|
|
|
this.registerDeviceOnMoodle().catch((error) => {
|
|
|
|
this.logger.warn('Can\'t register device', error);
|
|
|
|
});
|
|
|
|
}
|
2018-03-01 16:55:49 +01:00
|
|
|
});
|
2018-02-27 08:39:45 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
pushObject.on('error').subscribe((error: any) => {
|
2018-06-21 15:59:06 +02:00
|
|
|
// Execute the callback in the Angular zone, so change detection doesn't stop working.
|
|
|
|
this.zone.run(() => {
|
|
|
|
this.logger.warn('Error with Push plugin', error);
|
|
|
|
});
|
2018-02-27 08:39:45 +01:00
|
|
|
});
|
|
|
|
});
|
|
|
|
} catch (ex) {
|
|
|
|
// Ignore errors.
|
|
|
|
this.logger.warn(ex);
|
|
|
|
}
|
|
|
|
|
|
|
|
return Promise.reject(null);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-03-05 09:18:07 +01:00
|
|
|
* Registers a device on a Moodle site if needed.
|
2018-02-27 08:39:45 +01:00
|
|
|
*
|
2019-03-05 09:18:07 +01:00
|
|
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
|
|
|
* @param {boolean} [forceUnregister] Whether to force unregister and register.
|
|
|
|
* @return {Promise<any>} Promise resolved when device is registered.
|
2018-02-27 08:39:45 +01:00
|
|
|
*/
|
2019-03-05 09:18:07 +01:00
|
|
|
registerDeviceOnMoodle(siteId?: string, forceUnregister?: boolean): Promise<any> {
|
2018-02-27 08:39:45 +01:00
|
|
|
this.logger.debug('Register device on Moodle.');
|
|
|
|
|
2019-03-05 09:18:07 +01:00
|
|
|
if (!this.pushID || !this.appProvider.isMobile()) {
|
2018-02-27 08:39:45 +01:00
|
|
|
return Promise.reject(null);
|
|
|
|
}
|
|
|
|
|
2019-03-05 09:18:07 +01:00
|
|
|
const data = this.getRegisterData();
|
|
|
|
let result,
|
|
|
|
site: CoreSite;
|
|
|
|
|
|
|
|
return this.sitesProvider.getSite(siteId).then((s) => {
|
|
|
|
site = s;
|
|
|
|
|
|
|
|
if (forceUnregister) {
|
|
|
|
return {unregister: true, register: true};
|
|
|
|
} else {
|
|
|
|
// Check if the device is already registered.
|
|
|
|
return this.shouldRegister(data, site);
|
|
|
|
}
|
|
|
|
}).then((res) => {
|
|
|
|
result = res;
|
|
|
|
|
|
|
|
if (result.unregister) {
|
|
|
|
// Unregister the device first.
|
|
|
|
return this.unregisterDeviceOnMoodle(site).catch(() => {
|
|
|
|
// Ignore errors.
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}).then(() => {
|
|
|
|
if (result.register) {
|
|
|
|
// Now register the device.
|
|
|
|
return site.write('core_user_add_user_device', this.utils.clone(data)).then((response) => {
|
|
|
|
// Insert the device in the local DB.
|
|
|
|
return site.getDb().insertRecord(AddonPushNotificationsProvider.REGISTERED_DEVICES_TABLE, data)
|
|
|
|
.catch((error) => {
|
|
|
|
// Ignore errors.
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
2019-03-05 11:30:46 +01:00
|
|
|
}).finally(() => {
|
|
|
|
// Remove pending unregisters for this site.
|
|
|
|
this.appDB.deleteRecords(AddonPushNotificationsProvider.PENDING_UNREGISTER_TABLE, {siteid: site.id}).catch(() => {
|
|
|
|
// Ignore errors.
|
|
|
|
});
|
2019-03-05 09:18:07 +01:00
|
|
|
});
|
2018-02-27 08:39:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the addon/site badge counter from the database.
|
|
|
|
*
|
|
|
|
* @param {string} siteId Site ID.
|
|
|
|
* @param {string} [addon='site'] Registered addon name. If not defined it will store the site total.
|
|
|
|
* @return {Promise<any>} Promise resolved with the stored badge counter for the addon or site or 0 if none.
|
|
|
|
*/
|
|
|
|
protected getAddonBadge(siteId?: string, addon: string = 'site'): Promise<any> {
|
2018-05-30 12:13:39 +02:00
|
|
|
return this.appDB.getRecord(AddonPushNotificationsProvider.BADGE_TABLE, {siteid: siteId, addon: addon}).then((entry) => {
|
2018-02-27 08:39:45 +01:00
|
|
|
return (entry && entry.number) || 0;
|
|
|
|
}).catch(() => {
|
|
|
|
return 0;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-03-05 11:30:46 +01:00
|
|
|
/**
|
|
|
|
* Retry pending unregisters.
|
|
|
|
*
|
|
|
|
* @param {string} [siteId] If defined, retry only for that site if needed. Otherwise, retry all pending unregisters.
|
|
|
|
* @return {Promise<any>} Promise resolved when done.
|
|
|
|
*/
|
|
|
|
retryUnregisters(siteId?: string): Promise<any> {
|
|
|
|
let promise;
|
|
|
|
|
|
|
|
if (siteId) {
|
|
|
|
// Check if the site has a pending unregister.
|
|
|
|
promise = this.appDB.getRecords(AddonPushNotificationsProvider.REGISTERED_DEVICES_TABLE, {siteid: siteId});
|
|
|
|
} else {
|
|
|
|
// Get all pending unregisters.
|
|
|
|
promise = this.appDB.getAllRecords(AddonPushNotificationsProvider.PENDING_UNREGISTER_TABLE);
|
|
|
|
}
|
|
|
|
|
|
|
|
return promise.then((results) => {
|
|
|
|
const promises = [];
|
|
|
|
|
|
|
|
results.forEach((result) => {
|
|
|
|
// Create a temporary site to unregister.
|
|
|
|
const tmpSite = this.sitesFactory.makeSite(result.siteid, result.siteurl, result.token,
|
|
|
|
this.textUtils.parseJSON(result.info, {}));
|
|
|
|
|
|
|
|
promises.push(this.unregisterDeviceOnMoodle(tmpSite));
|
|
|
|
});
|
|
|
|
|
|
|
|
return Promise.all(promises);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-02-27 08:39:45 +01:00
|
|
|
/**
|
|
|
|
* Save the addon/site badgecounter on the database.
|
|
|
|
*
|
|
|
|
* @param {number} value The number to be stored.
|
|
|
|
* @param {string} [siteId] Site ID. If not defined, use current site.
|
|
|
|
* @param {string} [addon='site'] Registered addon name. If not defined it will store the site total.
|
|
|
|
* @return {Promise<any>} Promise resolved with the stored badge counter for the addon or site.
|
|
|
|
*/
|
|
|
|
protected saveAddonBadge(value: number, siteId?: string, addon: string = 'site'): Promise<any> {
|
|
|
|
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
|
|
|
|
|
|
|
const entry = {
|
|
|
|
siteid: siteId,
|
|
|
|
addon: addon,
|
|
|
|
number: value
|
|
|
|
};
|
|
|
|
|
2018-05-30 12:13:39 +02:00
|
|
|
return this.appDB.insertRecord(AddonPushNotificationsProvider.BADGE_TABLE, entry).then(() => {
|
2018-02-27 08:39:45 +01:00
|
|
|
return value;
|
|
|
|
});
|
|
|
|
}
|
2019-03-05 09:18:07 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if device should be registered (and unregistered first).
|
|
|
|
*
|
|
|
|
* @param {AddonPushNotificationsRegisterData} data Data of the device.
|
|
|
|
* @param {CoreSite} site Site to use.
|
|
|
|
* @return {Promise<{register: boolean, unregister: boolean}>} Promise resolved with booleans: whether to register/unregister.
|
|
|
|
*/
|
|
|
|
protected shouldRegister(data: AddonPushNotificationsRegisterData, site: CoreSite)
|
|
|
|
: Promise<{register: boolean, unregister: boolean}> {
|
|
|
|
|
|
|
|
// Check if the device is already registered.
|
|
|
|
return site.getDb().getRecords(AddonPushNotificationsProvider.REGISTERED_DEVICES_TABLE, {
|
|
|
|
appid: data.appid,
|
|
|
|
uuid: data.uuid
|
|
|
|
}).catch(() => {
|
|
|
|
// Ignore errors.
|
|
|
|
return [];
|
|
|
|
}).then((records: AddonPushNotificationsRegisterData[]) => {
|
|
|
|
let isStored = false,
|
|
|
|
versionOrPushChanged = false;
|
|
|
|
|
|
|
|
records.forEach((record) => {
|
|
|
|
if (record.name == data.name && record.model == data.model && record.platform == data.platform) {
|
|
|
|
if (record.version == data.version && record.pushid == data.pushid) {
|
|
|
|
// The device is already stored.
|
|
|
|
isStored = true;
|
|
|
|
} else {
|
|
|
|
// The version or pushid has changed.
|
|
|
|
versionOrPushChanged = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
if (isStored) {
|
|
|
|
// The device has already been registered, no need to register it again.
|
|
|
|
return {
|
|
|
|
register: false,
|
|
|
|
unregister: false
|
|
|
|
};
|
|
|
|
} else if (versionOrPushChanged) {
|
|
|
|
// This data can be updated by calling register WS, no need to call unregister.
|
|
|
|
return {
|
|
|
|
register: true,
|
|
|
|
unregister: false
|
|
|
|
};
|
|
|
|
} else {
|
|
|
|
return {
|
|
|
|
register: true,
|
|
|
|
unregister: true
|
|
|
|
};
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2018-02-23 14:44:17 +01:00
|
|
|
}
|