MOBILE-2831 push: Unregister before register if needed

main
Dani Palou 2019-03-05 09:18:07 +01:00
parent 75a2732141
commit a9e4e0c528
3 changed files with 293 additions and 22 deletions

View File

@ -21,7 +21,7 @@ import { TranslateService } from '@ngx-translate/core';
import { CoreAppProvider } from '@providers/app';
import { CoreInitDelegate } from '@providers/init';
import { CoreLoggerProvider } from '@providers/logger';
import { CoreSitesProvider } from '@providers/sites';
import { CoreSitesProvider, CoreSiteSchema } from '@providers/sites';
import { AddonPushNotificationsDelegate } from './delegate';
import { CoreLocalNotificationsProvider } from '@providers/local-notifications';
import { CoreUtilsProvider } from '@providers/utils/utils';
@ -31,6 +31,54 @@ import { CoreConstants } from '@core/constants';
import { CoreConfigConstants } from '../../../configconstants';
import { ILocalNotification } from '@ionic-native/local-notifications';
import { SQLiteDBTableSchema } from '@classes/sqlitedb';
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;
}
/**
* Service to handle push notifications.
@ -44,7 +92,8 @@ export class AddonPushNotificationsProvider {
// Variables for database.
static BADGE_TABLE = 'addon_pushnotifications_badge';
protected tablesSchema: SQLiteDBTableSchema[] = [
static REGISTERED_DEVICES_TABLE = 'addon_pushnotifications_registered_devices';
protected appTablesSchema: SQLiteDBTableSchema[] = [
{
name: AddonPushNotificationsProvider.BADGE_TABLE,
columns: [
@ -64,6 +113,46 @@ export class AddonPushNotificationsProvider {
primaryKeys: ['siteid', 'addon']
}
];
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']
}
],
};
constructor(logger: CoreLoggerProvider, protected appProvider: CoreAppProvider, private initDelegate: CoreInitDelegate,
protected pushNotificationsDelegate: AddonPushNotificationsDelegate, protected sitesProvider: CoreSitesProvider,
@ -73,7 +162,8 @@ export class AddonPushNotificationsProvider {
private translate: TranslateService, private platform: Platform) {
this.logger = logger.getInstance('AddonPushNotificationsProvider');
this.appDB = appProvider.getDB();
this.appDB.createTablesFromSchema(this.tablesSchema);
this.appDB.createTablesFromSchema(this.appTablesSchema);
this.sitesProvider.registerSiteSchema(this.siteSchema);
platform.ready().then(() => {
// Create the default channel.
@ -150,6 +240,23 @@ export class AddonPushNotificationsProvider {
return this.pushID;
}
/**
* 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
};
}
/**
* Get Sitebadge counter from the database.
*
@ -227,10 +334,10 @@ export class AddonPushNotificationsProvider {
/**
* Unregisters a device from a certain Moodle site.
*
* @param {any} site Site to unregister from.
* @param {CoreSite} site Site to unregister from.
* @return {Promise<any>} Promise resolved when device is unregistered.
*/
unregisterDeviceOnMoodle(site: any): Promise<any> {
unregisterDeviceOnMoodle(site: CoreSite): Promise<any> {
if (!site || !this.appProvider.isMobile()) {
return Promise.reject(null);
}
@ -246,6 +353,12 @@ export class AddonPushNotificationsProvider {
if (!response || !response.removed) {
return Promise.reject(null);
}
// Remove the device from the local DB.
return site.getDb().deleteRecords(AddonPushNotificationsProvider.REGISTERED_DEVICES_TABLE, this.getRegisterData())
.catch(() => {
// Ignore errors.
});
});
}
@ -388,28 +501,54 @@ export class AddonPushNotificationsProvider {
}
/**
* Registers a device on current Moodle site.
* Registers a device on a Moodle site if needed.
*
* @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.
*/
registerDeviceOnMoodle(): Promise<any> {
registerDeviceOnMoodle(siteId?: string, forceUnregister?: boolean): Promise<any> {
this.logger.debug('Register device on Moodle.');
if (!this.sitesProvider.isLoggedIn() || !this.pushID || !this.appProvider.isMobile()) {
if (!this.pushID || !this.appProvider.isMobile()) {
return Promise.reject(null);
}
const data = {
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
};
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.
});
});
}
});
return this.sitesProvider.getCurrentSite().write('core_user_add_user_device', data);
}
/**
@ -448,4 +587,58 @@ export class AddonPushNotificationsProvider {
return value;
});
}
/**
* 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
};
}
});
}
}

View File

@ -0,0 +1,71 @@
// (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 { CoreCronHandler } from '@providers/cron';
import { AddonPushNotificationsProvider } from './pushnotifications';
/**
* Cron handler to force a register on a Moodle site when a site is manually synchronized.
*/
@Injectable()
export class AddonPushNotificationsRegisterCronHandler implements CoreCronHandler {
name = 'AddonPushNotificationsRegisterCronHandler';
constructor(private pushNotificationsProvider: AddonPushNotificationsProvider) {}
/**
* Check whether the sync can be executed manually. Call isSync if not defined.
*
* @return {boolean} Whether the sync can be executed manually.
*/
canManualSync(): boolean {
return true; // Execute the handler when the site is manually synchronized.
}
/**
* Execute the process.
* Receives the ID of the site affected, undefined for all sites.
*
* @param {string} [siteId] ID of the site affected, undefined for all sites.
* @return {Promise<any>} Promise resolved when done, rejected if failure.
*/
execute(siteId?: string): Promise<any> {
if (!siteId) {
// It's not a specific site, don't do anything.
return Promise.resolve();
}
// Register the device again.
return this.pushNotificationsProvider.registerDeviceOnMoodle(siteId, true);
}
/**
* Get the time between consecutive executions.
*
* @return {number} Time between consecutive executions (in ms).
*/
getInterval(): number {
return 86400000; // 1 day. We won't do anything with automatic execution, so use a big number.
}
/**
* Check whether it's a synchronization process or not. True if not defined.
*
* @return {boolean} Whether it's a synchronization process or not.
*/
isSync(): boolean {
return false;
}
}

View File

@ -16,6 +16,8 @@ import { NgModule } from '@angular/core';
import { Platform } from 'ionic-angular';
import { AddonPushNotificationsProvider } from './providers/pushnotifications';
import { AddonPushNotificationsDelegate } from './providers/delegate';
import { AddonPushNotificationsRegisterCronHandler } from './providers/register-cron-handler';
import { CoreCronDelegate } from '@providers/cron';
import { CoreEventsProvider } from '@providers/events';
import { CoreLoggerProvider } from '@providers/logger';
import { CoreLocalNotificationsProvider } from '@providers/local-notifications';
@ -34,16 +36,21 @@ export const ADDON_PUSHNOTIFICATIONS_PROVIDERS: any[] = [
],
providers: [
AddonPushNotificationsProvider,
AddonPushNotificationsDelegate
AddonPushNotificationsDelegate,
AddonPushNotificationsRegisterCronHandler
]
})
export class AddonPushNotificationsModule {
constructor(platform: Platform, pushNotificationsProvider: AddonPushNotificationsProvider, eventsProvider: CoreEventsProvider,
localNotificationsProvider: CoreLocalNotificationsProvider, loggerProvider: CoreLoggerProvider,
updateManager: CoreUpdateManagerProvider) {
updateManager: CoreUpdateManagerProvider, cronDelegate: CoreCronDelegate,
registerCronHandler: AddonPushNotificationsRegisterCronHandler) {
const logger = loggerProvider.getInstance('AddonPushNotificationsModule');
// Register the handlers.
cronDelegate.register(registerCronHandler);
// Register device on GCM or APNS server.
platform.ready().then(() => {
pushNotificationsProvider.registerDevice();