MOBILE-4313 notifications: Show warnings if permissions missing
parent
09a2bf3d96
commit
edd6214e71
|
@ -43,11 +43,10 @@
|
|||
"@moodlehq/cordova-plugin-advanced-http": "3.3.1-moodle.1",
|
||||
"@moodlehq/cordova-plugin-file-opener": "4.0.0-moodle.1",
|
||||
"@moodlehq/cordova-plugin-file-transfer": "2.0.0-moodle.2",
|
||||
"@moodlehq/cordova-plugin-camera": "6.0.0-moodle.2",
|
||||
"@moodlehq/cordova-plugin-inappbrowser": "5.0.0-moodle.3",
|
||||
"@moodlehq/cordova-plugin-intent": "2.2.0-moodle.3",
|
||||
"@moodlehq/cordova-plugin-ionic-webview": "5.0.0-moodle.3",
|
||||
"@moodlehq/cordova-plugin-local-notification": "0.9.0-moodle.11",
|
||||
"@moodlehq/cordova-plugin-local-notification": "0.9.0-moodle.12",
|
||||
"@moodlehq/cordova-plugin-qrscanner": "3.0.1-moodle.5",
|
||||
"@moodlehq/cordova-plugin-statusbar": "4.0.0-moodle.3",
|
||||
"@moodlehq/cordova-plugin-zip": "3.1.0-moodle.1",
|
||||
|
@ -7797,7 +7796,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@moodlehq/cordova-plugin-local-notification": {
|
||||
"version": "0.9.0-moodle.11",
|
||||
"version": "0.9.0-moodle.12",
|
||||
"resolved": "https://registry.npmjs.org/@moodlehq/cordova-plugin-local-notification/-/cordova-plugin-local-notification-0.9.0-moodle.12.tgz",
|
||||
"integrity": "sha512-gt6BhqsltCnNmk/CRUIDxTha/c1/UGTsh2d15zoUVeGaKYqE6olmIyN/HCAn+ofy7CA6DNHOdm1v0uqC3YbPZg==",
|
||||
"engines": [
|
||||
{
|
||||
"name": "cordova",
|
||||
|
@ -7819,8 +7820,7 @@
|
|||
"name": "apple-ios",
|
||||
"version": ">=10.0.0"
|
||||
}
|
||||
],
|
||||
"license": "Apache 2.0"
|
||||
]
|
||||
},
|
||||
"node_modules/@moodlehq/cordova-plugin-qrscanner": {
|
||||
"version": "3.0.1-moodle.5",
|
||||
|
|
|
@ -81,7 +81,7 @@
|
|||
"@moodlehq/cordova-plugin-inappbrowser": "5.0.0-moodle.3",
|
||||
"@moodlehq/cordova-plugin-intent": "2.2.0-moodle.3",
|
||||
"@moodlehq/cordova-plugin-ionic-webview": "5.0.0-moodle.3",
|
||||
"@moodlehq/cordova-plugin-local-notification": "0.9.0-moodle.11",
|
||||
"@moodlehq/cordova-plugin-local-notification": "0.9.0-moodle.12",
|
||||
"@moodlehq/cordova-plugin-qrscanner": "3.0.1-moodle.5",
|
||||
"@moodlehq/cordova-plugin-statusbar": "4.0.0-moodle.3",
|
||||
"@moodlehq/cordova-plugin-zip": "3.1.0-moodle.1",
|
||||
|
|
|
@ -1752,6 +1752,8 @@
|
|||
"core.errorsyncblocked": "local_moodlemobileapp",
|
||||
"core.errorurlschemeinvalidscheme": "local_moodlemobileapp",
|
||||
"core.errorurlschemeinvalidsite": "local_moodlemobileapp",
|
||||
"core.exactalarmsturnedoff": "local_moodlemobileapp",
|
||||
"core.exactalarmsturnedoffmessage": "local_moodlemobileapp",
|
||||
"core.expand": "moodle",
|
||||
"core.explanationdigitalminor": "moodle",
|
||||
"core.favourites": "moodle",
|
||||
|
@ -2225,6 +2227,7 @@
|
|||
"core.notenrolledprofile": "moodle",
|
||||
"core.notice": "moodle",
|
||||
"core.notingroup": "moodle",
|
||||
"core.notnow": "local_moodlemobileapp",
|
||||
"core.notsent": "local_moodlemobileapp",
|
||||
"core.now": "moodle",
|
||||
"core.nummore": "local_moodlemobileapp",
|
||||
|
@ -2499,6 +2502,10 @@
|
|||
"core.today": "moodle",
|
||||
"core.toggledelete": "local_moodlemobileapp",
|
||||
"core.tryagain": "local_moodlemobileapp",
|
||||
"core.turnon": "local_moodlemobileapp",
|
||||
"core.turnonexactalarms": "local_moodlemobileapp",
|
||||
"core.turnonnotifications": "local_moodlemobileapp",
|
||||
"core.turnonnotificationsmessage": "local_moodlemobileapp",
|
||||
"core.twoparagraphs": "local_moodlemobileapp",
|
||||
"core.type": "repository",
|
||||
"core.uhoh": "local_moodlemobileapp",
|
||||
|
|
|
@ -120,6 +120,23 @@
|
|||
</ion-item>
|
||||
</ion-list>
|
||||
|
||||
<ion-card class="core-warning-card core-card-with-buttons"
|
||||
*ngIf="remindersEnabled && event && !canScheduleExactAlarms && !scheduleExactWarningHidden">
|
||||
<ion-item class="ion-text-wrap">
|
||||
<ion-icon name="fas-circle-info" slot="start" aria-hidden="true" />
|
||||
<ion-label>
|
||||
<p><strong>{{ 'core.exactalarmsturnedoff' | translate }}</strong></p>
|
||||
<p>{{ 'core.exactalarmsturnedoffmessage' | translate }}</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<div class="core-card-buttons">
|
||||
<ion-button fill="clear" (click)="hideAlarmWarning()">
|
||||
{{ 'core.dontshowagain' | translate | coreNoPeriod }}
|
||||
</ion-button>
|
||||
<ion-button fill="outline" (click)="openAlarmSettings()">{{ 'core.turnon' | translate }}</ion-button>
|
||||
</div>
|
||||
</ion-card>
|
||||
|
||||
<ion-card *ngIf="remindersEnabled && event">
|
||||
<ion-item>
|
||||
<ion-label>
|
||||
|
|
|
@ -40,6 +40,9 @@ import { AddonCalendarEventsSource } from '@addons/calendar/classes/events-sourc
|
|||
import { CoreSwipeNavigationItemsManager } from '@classes/items-management/swipe-navigation-items-manager';
|
||||
import { CoreReminders, CoreRemindersService } from '@features/reminders/services/reminders';
|
||||
import { CoreRemindersSetReminderMenuComponent } from '@features/reminders/components/set-reminder-menu/set-reminder-menu';
|
||||
import { CoreLocalNotifications } from '@services/local-notifications';
|
||||
import { CorePlatform } from '@services/platform';
|
||||
import { CoreConfig } from '@services/config';
|
||||
|
||||
/**
|
||||
* Page that displays a single calendar event.
|
||||
|
@ -61,6 +64,7 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy {
|
|||
protected defaultTimeChangedObserver: CoreEventObserver;
|
||||
protected currentSiteId: string;
|
||||
protected updateCurrentTime?: number;
|
||||
protected appResumeSubscription: Subscription;
|
||||
|
||||
eventLoaded = false;
|
||||
event?: AddonCalendarEventToDisplay;
|
||||
|
@ -78,6 +82,8 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy {
|
|||
hasOffline = false;
|
||||
isOnline = false;
|
||||
syncIcon = CoreConstants.ICON_LOADING; // Sync icon.
|
||||
canScheduleExactAlarms = true;
|
||||
scheduleExactWarningHidden = false;
|
||||
|
||||
constructor(
|
||||
protected route: ActivatedRoute,
|
||||
|
@ -138,6 +144,11 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy {
|
|||
this.updateCurrentTime = window.setInterval(() => {
|
||||
this.currentTime = CoreTimeUtils.timestamp();
|
||||
}, 5000);
|
||||
|
||||
this.checkExactAlarms();
|
||||
this.appResumeSubscription = CorePlatform.resume.subscribe(() => {
|
||||
this.checkExactAlarms();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -153,6 +164,14 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy {
|
|||
this.reminders = await AddonCalendarHelper.getEventReminders(this.eventId, this.event.timestart, this.currentSiteId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the app can schedule exact alarms.
|
||||
*/
|
||||
protected async checkExactAlarms(): Promise<void> {
|
||||
this.scheduleExactWarningHidden = !!(await CoreConfig.get(CoreConstants.DONT_SHOW_EXACT_ALARMS_WARNING, 0));
|
||||
this.canScheduleExactAlarms = await CoreLocalNotifications.canScheduleExactAlarms();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
|
@ -616,6 +635,21 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Open alarm settings.
|
||||
*/
|
||||
openAlarmSettings(): void {
|
||||
CoreLocalNotifications.openAlarmSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide alarm warning.
|
||||
*/
|
||||
hideAlarmWarning(): void {
|
||||
CoreConfig.set(CoreConstants.DONT_SHOW_EXACT_ALARMS_WARNING, 1);
|
||||
this.scheduleExactWarningHidden = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
|
@ -626,6 +660,7 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy {
|
|||
this.onlineObserver.unsubscribe();
|
||||
this.newEventObserver.off();
|
||||
this.events?.destroy();
|
||||
this.appResumeSubscription.unsubscribe();
|
||||
clearInterval(this.updateCurrentTime);
|
||||
}
|
||||
|
||||
|
|
|
@ -17,8 +17,23 @@
|
|||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}" />
|
||||
</ion-refresher>
|
||||
<core-loading [hideUntil]="notifications.loaded">
|
||||
<ion-card class="core-warning-card core-card-with-buttons" *ngIf="!hasNotificationsPermission && !permissionWarningHidden">
|
||||
<ion-item class="ion-text-wrap">
|
||||
<ion-icon name="fas-circle-info" slot="start" aria-hidden="true" />
|
||||
<ion-label>
|
||||
<p><strong>{{ 'core.turnonnotifications' | translate }}</strong></p>
|
||||
<p>{{ 'core.turnonnotificationsmessage' | translate }}</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<div class="core-card-buttons">
|
||||
<ion-button fill="clear" (click)="hidePermissionWarning()">
|
||||
{{ 'core.dontshowagain' | translate | coreNoPeriod }}
|
||||
</ion-button>
|
||||
<ion-button fill="outline" (click)="openSettings()">{{ 'core.turnon' | translate }}</ion-button>
|
||||
</div>
|
||||
</ion-card>
|
||||
|
||||
<ion-item *ngFor="let notification of notifications.items" class="ion-text-wrap"
|
||||
<ion-item *ngFor="let notification of notifications.items" class="ion-text-wrap addon-notification-item"
|
||||
[attr.aria-current]="notifications.getItemAriaCurrent(notification)" (click)="notifications.select(notification)" button
|
||||
[detail]="false" lines="full">
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
@use "theme/globals" as *;
|
||||
|
||||
ion-item {
|
||||
ion-item.addon-notification-item {
|
||||
ion-label {
|
||||
margin-top: 8px;
|
||||
margin-bottom: 8px;
|
||||
|
|
|
@ -31,6 +31,10 @@ import { CoreTimeUtils } from '@services/utils/time';
|
|||
import { AddonNotificationsNotificationsSource } from '@addons/notifications/classes/notifications-source';
|
||||
import { CoreListItemsManager } from '@classes/items-management/list-items-manager';
|
||||
import { AddonLegacyNotificationsNotificationsSource } from '@addons/notifications/classes/legacy-notifications-source';
|
||||
import { CoreLocalNotifications } from '@services/local-notifications';
|
||||
import { CoreConfig } from '@services/config';
|
||||
import { CoreConstants } from '@/core/constants';
|
||||
import { CorePlatform } from '@services/platform';
|
||||
|
||||
/**
|
||||
* Page that displays the list of notifications.
|
||||
|
@ -47,12 +51,15 @@ export class AddonNotificationsListPage implements AfterViewInit, OnDestroy {
|
|||
fetchMoreNotificationsFailed = false;
|
||||
canMarkAllNotificationsAsRead = false;
|
||||
loadingMarkAllNotificationsAsRead = false;
|
||||
hasNotificationsPermission = true;
|
||||
permissionWarningHidden = false;
|
||||
|
||||
protected isCurrentView?: boolean;
|
||||
protected cronObserver?: CoreEventObserver;
|
||||
protected readObserver?: CoreEventObserver;
|
||||
protected pushObserver?: Subscription;
|
||||
protected pendingRefresh = false;
|
||||
protected appResumeSubscription?: Subscription;
|
||||
|
||||
constructor() {
|
||||
try {
|
||||
|
@ -67,7 +74,14 @@ export class AddonNotificationsListPage implements AfterViewInit, OnDestroy {
|
|||
} catch(error) {
|
||||
CoreDomUtils.showErrorModal(error);
|
||||
CoreNavigator.back();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.checkPermission();
|
||||
this.appResumeSubscription = CorePlatform.resume.subscribe(() => {
|
||||
this.checkPermission();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -120,6 +134,14 @@ export class AddonNotificationsListPage implements AfterViewInit, OnDestroy {
|
|||
deepLinkManager.treatLink();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the app has permission to display notifications.
|
||||
*/
|
||||
protected async checkPermission(): Promise<void> {
|
||||
this.permissionWarningHidden = !!(await CoreConfig.get(CoreConstants.DONT_SHOW_NOTIFICATIONS_PERMISSION_WARNING, 0));
|
||||
this.hasNotificationsPermission = await CoreLocalNotifications.hasNotificationsPermission();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function to get notifications. Gets unread notifications first.
|
||||
*
|
||||
|
@ -211,6 +233,21 @@ export class AddonNotificationsListPage implements AfterViewInit, OnDestroy {
|
|||
refresher?.complete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Open notification settings.
|
||||
*/
|
||||
openSettings(): void {
|
||||
CoreLocalNotifications.openNotificationSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide permission warning.
|
||||
*/
|
||||
hidePermissionWarning(): void {
|
||||
CoreConfig.set(CoreConstants.DONT_SHOW_NOTIFICATIONS_PERMISSION_WARNING, 1);
|
||||
this.permissionWarningHidden = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* User entered the page.
|
||||
*/
|
||||
|
@ -241,6 +278,7 @@ export class AddonNotificationsListPage implements AfterViewInit, OnDestroy {
|
|||
this.readObserver?.off();
|
||||
this.pushObserver?.unsubscribe();
|
||||
this.notifications?.destroy();
|
||||
this.appResumeSubscription?.unsubscribe();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -155,6 +155,9 @@ export class CoreConstants {
|
|||
|
||||
// Other constants.
|
||||
static readonly CALENDAR_DEFAULT_STARTING_WEEKDAY = 1;
|
||||
static readonly DONT_SHOW_NOTIFICATIONS_PERMISSION_WARNING = 'CoreDontShowNotificationsPermissionWarning';
|
||||
static readonly DONT_SHOW_EXACT_ALARMS_WARNING = 'CoreDontShowScheduleExactWarning';
|
||||
static readonly EXACT_ALARMS_WARNING_DISPLAYED = 'CoreScheduleExactWarningModalDisplayed';
|
||||
|
||||
// Config & environment constants.
|
||||
static readonly CONFIG = { ...envJson.config } as unknown as EnvironmentConfig; // Data parsed from config.json files.
|
||||
|
|
|
@ -124,6 +124,8 @@
|
|||
"errorsyncblocked": "This {{$a}} cannot be synchronised right now because of an ongoing process. Please try again later. If the problem persists, try restarting the app.",
|
||||
"errorurlschemeinvalidscheme": "This URL is meant to be used in another app: {{$a}}.",
|
||||
"errorurlschemeinvalidsite": "This site URL cannot be opened in this app.",
|
||||
"exactalarmsturnedoff": "Real-time notifications are turned off",
|
||||
"exactalarmsturnedoffmessage": "To make sure you don't miss any important alerts, turn on 'Alarms and reminders' in your device's settings.",
|
||||
"expand": "Expand",
|
||||
"explanationdigitalminor": "This information is required to determine if your age is over the digital age of consent. This is the age when an individual can consent to terms and conditions and their data being legally stored and processed.",
|
||||
"favourites": "Starred",
|
||||
|
@ -225,6 +227,7 @@
|
|||
"notenrolledprofile": "This profile is not available because this user is not enrolled in this course.",
|
||||
"notice": "Notice",
|
||||
"notingroup": "Sorry, but you need to be part of a group to see this page.",
|
||||
"notnow": "Not now",
|
||||
"notsent": "Not sent",
|
||||
"now": "now",
|
||||
"nummore": "{{$a}} more",
|
||||
|
@ -333,6 +336,10 @@
|
|||
"today": "Today",
|
||||
"toggledelete": "Toggle delete buttons",
|
||||
"tryagain": "Try again",
|
||||
"turnon": "Turn on",
|
||||
"turnonexactalarms": "Turn on real-time alerts",
|
||||
"turnonnotifications": "Turn on notifications",
|
||||
"turnonnotificationsmessage": "Would you like to receive notifications about activities and assignments?",
|
||||
"twoparagraphs": "{{p1}}<br><br>{{p2}}",
|
||||
"type": "Type",
|
||||
"uhoh": "Uh oh!",
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
// (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.
|
||||
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
|
||||
/**
|
||||
* Pipe to remove period at the end of a text if present.
|
||||
*/
|
||||
@Pipe({
|
||||
name: 'coreNoPeriod',
|
||||
})
|
||||
export class CoreNoPeriodPipe implements PipeTransform {
|
||||
|
||||
/**
|
||||
* Takes a text and removes ending period.
|
||||
*
|
||||
* @param text The text to treat.
|
||||
* @returns Treated text.
|
||||
*/
|
||||
transform(text: string): string {
|
||||
if (!text) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return text.trim().replace(/\.$/, '');
|
||||
}
|
||||
|
||||
}
|
|
@ -22,6 +22,7 @@ import { CoreFormatDatePipe } from './format-date';
|
|||
import { CoreNoTagsPipe } from './no-tags';
|
||||
import { CoreSecondsToHMSPipe } from './seconds-to-hms';
|
||||
import { CoreTimeAgoPipe } from './time-ago';
|
||||
import { CoreNoPeriodPipe } from './no-period';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
|
@ -30,6 +31,7 @@ import { CoreTimeAgoPipe } from './time-ago';
|
|||
CoreDateDayOrTimePipe,
|
||||
CoreDurationPipe,
|
||||
CoreFormatDatePipe,
|
||||
CoreNoPeriodPipe,
|
||||
CoreNoTagsPipe,
|
||||
CoreSecondsToHMSPipe,
|
||||
CoreTimeAgoPipe,
|
||||
|
@ -41,6 +43,7 @@ import { CoreTimeAgoPipe } from './time-ago';
|
|||
CoreDurationPipe,
|
||||
CoreFormatDatePipe,
|
||||
CoreNoTagsPipe,
|
||||
CoreNoPeriodPipe,
|
||||
CoreSecondsToHMSPipe,
|
||||
CoreTimeAgoPipe,
|
||||
],
|
||||
|
|
|
@ -41,6 +41,7 @@ import { Push } from '@features/native/plugins';
|
|||
import { AsyncInstance, asyncInstance } from '@/core/utils/async-instance';
|
||||
import { CoreDatabaseTable } from '@classes/database/database-table';
|
||||
import { CoreDatabaseCachingStrategy, CoreDatabaseTableProxy } from '@classes/database/database-table-proxy';
|
||||
import { CoreDomUtils } from './utils/dom';
|
||||
|
||||
/**
|
||||
* Service to handle local notifications.
|
||||
|
@ -119,6 +120,41 @@ export class CoreLocalNotificationsProvider {
|
|||
this.cancelSiteNotifications(site.id);
|
||||
}
|
||||
});
|
||||
|
||||
CoreEvents.on(CoreEvents.LOGIN, async () => {
|
||||
const [hasNotificationsPermission, canScheduleExact] = await Promise.all([
|
||||
this.hasNotificationsPermission(),
|
||||
this.canScheduleExactAlarms(),
|
||||
]);
|
||||
|
||||
if (!hasNotificationsPermission || canScheduleExact) {
|
||||
return;
|
||||
}
|
||||
|
||||
const dontShowWarning = await CoreConfig.get(CoreConstants.EXACT_ALARMS_WARNING_DISPLAYED, 0);
|
||||
if (dontShowWarning) {
|
||||
return;
|
||||
}
|
||||
|
||||
CoreDomUtils.showAlertWithOptions({
|
||||
header: Translate.instant('core.turnonexactalarms'),
|
||||
message: Translate.instant('core.exactalarmsturnedoffmessage'),
|
||||
buttons: [
|
||||
{
|
||||
text: Translate.instant('core.notnow'),
|
||||
role: 'cancel',
|
||||
},
|
||||
{
|
||||
text: Translate.instant('core.turnon'),
|
||||
handler: (): void => {
|
||||
this.openAlarmSettings();
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
CoreConfig.set(CoreConstants.EXACT_ALARMS_WARNING_DISPLAYED, 1);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -163,6 +199,40 @@ export class CoreLocalNotificationsProvider {
|
|||
this.triggeredTable.setInstance(triggeredTable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the app has the permission to display notifications.
|
||||
*
|
||||
* @returns Whether has notifications permission.
|
||||
*/
|
||||
async hasNotificationsPermission(): Promise<boolean> {
|
||||
if (!CorePlatform.isMobile()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return LocalNotifications.hasPermission();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the app can schedule exact alarms.
|
||||
*
|
||||
* @returns Whether can schedule exact alarms.
|
||||
*/
|
||||
async canScheduleExactAlarms(): Promise<boolean> {
|
||||
if (!CorePlatform.isAndroid()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const plugin = this.getCordovaPlugin();
|
||||
if (!plugin || !plugin.canScheduleExactAlarms) {
|
||||
// Cannot check, assume it's enabled.
|
||||
return true;
|
||||
}
|
||||
|
||||
return new Promise(resolve => {
|
||||
plugin.canScheduleExactAlarms(canSchedule => resolve(canSchedule));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel a local notification.
|
||||
*
|
||||
|
@ -370,11 +440,18 @@ export class CoreLocalNotificationsProvider {
|
|||
* @returns Whether local notifications plugin is available.
|
||||
*/
|
||||
isPluginAvailable(): boolean {
|
||||
const win = <any> window; // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
return !!this.getCordovaPlugin() && CorePlatform.is('cordova');
|
||||
}
|
||||
|
||||
const enabled = !!win.cordova?.plugins?.notification?.local;
|
||||
|
||||
return enabled && CorePlatform.is('cordova');
|
||||
/**
|
||||
* Get the Cordova plugin object.
|
||||
*
|
||||
* @returns Cordova plugin, undefined if not found.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
protected getCordovaPlugin(): any | undefined {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
return (<any> window).cordova?.plugins?.notification?.local;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -740,6 +817,28 @@ export class CoreLocalNotificationsProvider {
|
|||
await this.componentsTable.update({ id: newId }, { id: oldId });
|
||||
}
|
||||
|
||||
/**
|
||||
* Open notification settings.
|
||||
*/
|
||||
openNotificationSettings(): void {
|
||||
if (!CorePlatform.isMobile()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.getCordovaPlugin()?.openNotificationSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Open alarm settings (Android only).
|
||||
*/
|
||||
openAlarmSettings(): void {
|
||||
if (!CorePlatform.isAndroid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.getCordovaPlugin()?.openAlarmSettings();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export const CoreLocalNotifications = makeSingleton(CoreLocalNotificationsProvider);
|
||||
|
|
|
@ -931,6 +931,25 @@ ion-card {
|
|||
ion-card-title {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
&.core-card-with-buttons .item ion-label {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.core-card-buttons {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
@include margin(0, 8px, 8px, 8px);
|
||||
|
||||
ion-button {
|
||||
text-transform: none;
|
||||
|
||||
&[fill="outline"] {
|
||||
--background: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.core-course-module-handler:not(.addon-mod-label-handler) .item-heading .filter_mathjaxloader_equation div {
|
||||
|
|
Loading…
Reference in New Issue