implemented foreground service for timer alerts

This commit is contained in:
vishnuraghavb 2021-05-26 21:14:50 +05:30
parent dbd08f2eb3
commit e3d690e42d
7 changed files with 189 additions and 140 deletions

View file

@ -1,51 +1,39 @@
var ForegroundService = /** @class */ (function(_super) {
__extends(ForegroundService, _super)
function ForegroundService() {
return (_super !== null && _super.apply(this, arguments)) || this
}
ForegroundService.prototype.onStartCommand = function(
intent,
flags,
startId
) {
import { TimerNotif } from './shared/utils'
const superProto = android.app.Service.prototype
android.app.Service.extend('com.tns.ForegroundService', {
onStartCommand: function(intent, flags, startId) {
console.log('onStartCommand')
_super.prototype.onStartCommand.call(this, intent, flags, startId)
superProto.onStartCommand.call(this, intent, flags, startId)
return android.app.Service.START_STICKY
}
ForegroundService.prototype.onCreate = function() {
},
onCreate: function() {
console.log('onCreate')
_super.prototype.onCreate.call(this)
this.startForeground(1, this.getNotification())
}
ForegroundService.prototype.onBind = function(intent) {
return _super.prototype.onBind.call(this, intent)
}
ForegroundService.prototype.onUnbind = function(intent) {
return _super.prototype.onUnbind.call(this, intent)
}
ForegroundService.prototype.onDestroy = function() {
superProto.onCreate.call(this)
this.startForeground(777, this.getNotification())
},
onBind: function(intent) {
return superProto.onBind.call(this, intent)
},
onUnbind: function(intent) {
return superProto.onUnbind.call(this, intent)
},
onDestroy: function() {
console.log('onDestroy')
this.stopForeground(true)
}
ForegroundService.prototype.getNotification = function() {
var channel = new android.app.NotificationChannel(
'channel_01',
'ForegroundService Channel',
android.app.NotificationManager.IMPORTANCE_DEFAULT
},
getNotification: function() {
return TimerNotif.getNotification(
{
bID: 'bringToFront',
cID: 'cti',
cName: 'Cooking Timer info',
description: `0 ongoing, 0 paused`,
nID: 777,
priority: -2,
sound: null,
title: 'EnRecipes is running',
},
this.getApplicationContext()
)
var notificationManager = this.getSystemService(
android.content.Context.NOTIFICATION_SERVICE
)
notificationManager.createNotificationChannel(channel)
var builder = new android.app.Notification.Builder(
this.getApplicationContext(),
'channel_01'
)
return builder.build()
}
ForegroundService = __decorate(
[JavaProxy('com.tns.ForegroundService')],
ForegroundService
)
return ForegroundService
})(android.app.Service)
},
})

View file

@ -19,7 +19,6 @@
<Timer
v-for="(timer, i) in activeTimers"
:key="timer.id"
ref="singleTimer"
:timer="timer"
:timerIndex="i"
:formattedTime="formattedTime"
@ -62,21 +61,23 @@ import {
ApplicationSettings,
AndroidApplication,
Utils,
Device,
} from "@nativescript/core";
import { mapState, mapActions } from "vuex";
import Action from "./modals/Action.vue";
import CookingTimer from "./CookingTimer.vue";
import CTSettings from "./settings/CTSettings.vue";
import TimePickerHMS from "./modals/TimePickerHMS.vue";
import TimerReminder from "./modals/TimerReminder.vue";
import Timer from "./sub/Timer.vue";
import Toast from "./sub/Toast.vue";
import SnackBar from "./sub/SnackBar.vue";
import * as utils from "~/shared/utils";
// import { fgs } from "~/foreground.android";
import { EventBus } from "~/main";
let undoTimer;
let undoTimer,
firingTimers = [];
declare const com: any;
export default {
components: { Timer, Toast, SnackBar },
@ -177,33 +178,12 @@ export default {
cID: "cti",
cName: "Cooking Timer info",
description: `${ongoingCount} ongoing, ${pausedCount} paused`,
nID: 999,
nID: 777,
priority: -2,
sound: null,
title: localize("timer"),
});
if (activeCount <= 0) utils.TimerNotif.clear(999);
},
intentListener({ intent, android }) {
let ct = "CookingTimer";
let action = (intent || android).getStringExtra("action");
console.log("calling: ", action);
let comp = this.currentComponent;
if (action == "open_timer" && this.currentComponent != ct) {
let openTimer = setInterval(() => {
if (comp == ct) clearInterval(openTimer);
else {
if (comp == "CTSettings") this.$navigateBack();
else this.$navigateTo(CookingTimer);
comp = ct;
}
Application.off(Application.launchEvent, this.intentListener);
Application.android.off(
AndroidApplication.activityNewIntentEvent,
this.intentListener
);
}, 250);
}
if (activeCount <= 0) this.foregroundService(false);
},
fireTimer(timer) {
console.log("firing");
@ -230,18 +210,39 @@ export default {
console.log(action, "firing");
EventBus.$emit(bID, action);
});
firingTimers.push(timer);
// if (firingTimers.length == 1) {
// this.$showModal(TimerReminder, {
// fullscreen: true,
// props: {
// timers: firingTimers,
// stop: this.stopFiringTimers,
// formattedTime: this.formattedTime,
// },
// });
// }
},
startForegroundService() {
stopFiringTimers() {
firingTimers.forEach((e) => utils.TimerNotif.clear(e.id));
firingTimers = [];
},
openReminder() {},
foregroundService(bool) {
const ctx = Utils.ad.getApplicationContext();
const intent = new android.content.Intent();
intent.setClassName(ctx, "com.tns.ForegroundService");
ctx.startService(intent);
const intent = new android.content.Intent(
ctx,
com.tns.ForegroundService.class
);
if (bool)
parseInt(Device.sdkVersion) < 26
? ctx.startService(intent)
: ctx.startForegroundService(intent);
else ctx.stopService(intent);
},
// DATA HANDLERS
addTimer() {
// fgs.class
this.startForegroundService();
this.foregroundService(true);
this.$showModal(TimePickerHMS, {
props: {
title: "ntmr",
@ -300,6 +301,7 @@ export default {
if (!noUndo) {
this.showUndoBar("tmrClr")
.then(() => {
this.foregroundService(true);
this.addActiveTimer({
timer: temp,
index,
@ -385,11 +387,6 @@ export default {
},
},
created() {
Application.on(Application.launchEvent, this.intentListener);
Application.android.on(
AndroidApplication.activityNewIntentEvent,
this.intentListener
);
this.clearTimerInterval();
},
};

View file

@ -4,9 +4,9 @@
columns="auto, *, auto, auto, auto"
class="singleTimer"
>
<!-- :class="{ blink: done }" -->
<Button
class="ico"
:class="{ blink: done }"
:text="done ? icon.ring : timer.isPaused ? icon.start : icon.pause"
@tap="!done && toggleProgress()"
/>

View file

@ -12,22 +12,38 @@ const keepScreenOn = () => {
console.log('keepScreenOn')
const window = Application.android.startActivity.getWindow()
const windowMgr = android.view.WindowManager
window.addFlags(
const flags =
windowMgr.LayoutParams.FLAG_SHOW_WHEN_LOCKED |
windowMgr.LayoutParams.FLAG_TURN_SCREEN_ON |
windowMgr.LayoutParams.FLAG_KEEP_SCREEN_ON
windowMgr.LayoutParams.FLAG_TURN_SCREEN_ON |
windowMgr.LayoutParams.FLAG_KEEP_SCREEN_ON
window.addFlags(flags)
function clearFlags(args) {
args.cancel = true
window.clearFlags(flags)
Application.android.off(
AndroidApplication.activityBackPressedEvent,
clearFlags
)
}
Application.android.on(
AndroidApplication.activityBackPressedEvent,
clearFlags
)
}
}
Application.on(Application.resumeEvent, keepScreenOn)
Application.on(Application.launchEvent, ({ android }) => {
Application.on(Application.launchEvent, (args) => {
console.log('launching')
if (android) androidLaunchEventLocalizationHandler()
if (args.android) {
androidLaunchEventLocalizationHandler()
intentListener(args)
}
})
import Vue from 'nativescript-vue'
import EnRecipes from './components/EnRecipes.vue'
import CookingTimer from './components/CookingTimer.vue'
import store from './store'
export const EventBus = new Vue()
@ -46,3 +62,35 @@ new Vue({
store,
render: (h) => h(EnRecipes),
}).$start()
const intentListener = ({ intent, android }: any) => {
let ct = 'CookingTimer'
let action = (intent || android).getStringExtra('action')
console.log('calling: ', action)
if (action == 'open_timer' && store.state.currentComponent != ct) {
let openTimer = setInterval(() => {
let comp = store.state.currentComponent
if (comp == ct) clearInterval(openTimer)
else {
if (comp == 'CTSettings') Vue.navigateBack()
else {
Vue.navigateTo(CookingTimer)
store.commit('setComponent', 'CookingTimer')
}
}
}, 250)
}
}
Application.on(Application.launchEvent, () => {
Application.android.on(
AndroidApplication.activityNewIntentEvent,
intentListener
)
})
Application.on(Application.exitEvent, () => {
store.commit('setComponent', 'EnRecipes')
Application.android.off(
AndroidApplication.activityNewIntentEvent,
intentListener
)
})

View file

@ -3,8 +3,8 @@
<supports-screens android:smallScreens="true" android:normalScreens="true" android:largeScreens="true" android:xlargeScreens="true" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" tools:targetApi="28" />
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" tools:targetApi="29" />
<uses-permission android:name="android.permission.CAMERA" tools:node="remove" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" tools:node="remove" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" tools:node="remove" />

View file

@ -6,6 +6,7 @@ import {
Color,
path,
knownFolders,
TimerInfo,
} from '@nativescript/core'
let timerOne
declare const global, android, androidx, com, java, Array: any
@ -318,39 +319,45 @@ export class TimerNotif {
const NotifySrv = ctx.getSystemService(
android.content.Context.NOTIFICATION_SERVICE
)
// nID ? NotifySrv.cancel(nID) : NotifySrv.cancelAll()
NotifySrv.cancel(nID)
}
static show({
actions,
bID,
cID,
cName,
description,
nID,
priority,
sound,
title,
vibrate,
}: {
actions?: boolean
bID: string
cID: string
cName: string
description: string
nID: number
priority: number
sound: string
title: string
vibrate?: number
}) {
static getNotification(
{
actions,
bID,
cID,
cName,
description,
nID,
priority,
sound,
title,
vibrate,
}: {
actions?: boolean
bID: string
cID: string
cName: string
description: string
nID: number
priority: number
sound: string
title: string
vibrate?: number
},
ctx
) {
let sdkv: number = parseInt(Device.sdkVersion)
let soundUri: any
if (sound) soundUri = new android.net.Uri.parse(sound)
const NotifyMgr = android.app.NotificationManager
let ctx = Utils.ad.getApplicationContext()
const NotifySrv = ctx.getSystemService(
android.content.Context.NOTIFICATION_SERVICE
)
const NotificationCompat = androidx.core.app.NotificationCompat
const AudioManager = android.media.AudioManager
if (sdkv >= 26) {
const importance =
priority > 0 ? NotifyMgr.IMPORTANCE_HIGH : NotifyMgr.IMPORTANCE_MIN
@ -371,6 +378,7 @@ export class TimerNotif {
if (sound) Channel.setSound(soundUri, audioAttributes)
else Channel.setSound(null, null)
Channel.setShowBadge(true)
Channel.setLockscreenVisibility(NotificationCompat.VISIBILITY_PUBLIC)
NotifySrv.createNotificationChannel(Channel)
}
@ -390,7 +398,7 @@ export class TimerNotif {
let actionInt1, actionInt2, actionPInt1, actionPInt2
if (actions) {
actionInt1 = new Intent(bID)
actionInt1.putExtra('action', 'delay')
actionInt1.putExtra('action', 'stop')
actionPInt1 = PendingIntent.getBroadcast(
ctx,
2,
@ -398,7 +406,7 @@ export class TimerNotif {
PendingIntent.FLAG_UPDATE_CURRENT
)
actionInt2 = new Intent(bID)
actionInt2.putExtra('action', 'stop')
actionInt2.putExtra('action', 'delay')
actionPInt2 = PendingIntent.getBroadcast(
ctx,
3,
@ -408,8 +416,7 @@ export class TimerNotif {
}
// CREATE NOTIFICATION
const NotificationCompat = androidx.core.app.NotificationCompat
const AudioManager = android.media.AudioManager
let icon = TimerNotif.getIcon(ctx, 'ic_stat_notify_silhouette')
let builder = new NotificationCompat.Builder(ctx, cID)
.setColor(new Color('#ff5200').android)
@ -421,6 +428,7 @@ export class TimerNotif {
.setSmallIcon(icon)
.setTicker(title)
.setAutoCancel(false)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
if (sound) builder.setSound(soundUri, AudioManager.STREAM_ALARM)
else builder.setSound(null)
if (description) builder.setContentText(description)
@ -428,13 +436,21 @@ export class TimerNotif {
if (actions) {
builder.setDeleteIntent(actionPInt2)
builder.setFullScreenIntent(mainPInt, true)
builder.addAction(null, 'Delay', actionPInt1)
builder.addAction(null, 'Stop', actionPInt2)
builder.addAction(null, 'Stop', actionPInt1)
builder.addAction(null, 'Delay', actionPInt2)
}
let notification = builder.build()
notification.flags =
NotificationCompat.FLAG_INSISTENT | NotificationCompat.FLAG_ONGOING_EVENT
NotifySrv.notify(nID, notification)
return notification
}
static show(data) {
const ctx = Utils.ad.getApplicationContext()
const NotifySrv = ctx.getSystemService(
android.content.Context.NOTIFICATION_SERVICE
)
NotifySrv.notify(data.nID, TimerNotif.getNotification(data, ctx))
}
}

36
package-lock.json generated
View file

@ -1388,9 +1388,9 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001228",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001228.tgz",
"integrity": "sha512-QQmLOGJ3DEgokHbMSA8cj2a+geXqmnpyOFT0lhQV6P3/YOJvGDEwoedcwxEQ30gJIwIIunHIicunJ2rzK5gB2A==",
"version": "1.0.30001230",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001230.tgz",
"integrity": "sha512-5yBd5nWCBS+jWKTcHOzXwo5xzcj4ePE/yjtkZyUV1BTUmrBaA9MRGC+e7mxnqXSA90CmCA8L3eKLaSUkt099IQ==",
"dev": true,
"funding": {
"type": "opencollective",
@ -1877,9 +1877,9 @@
"dev": true
},
"node_modules/electron-to-chromium": {
"version": "1.3.738",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.738.tgz",
"integrity": "sha512-vCMf4gDOpEylPSLPLSwAEsz+R3ShP02Y3cAKMZvTqule3XcPp7tgc/0ESI7IS6ZeyBlGClE50N53fIOkcIVnpw==",
"version": "1.3.739",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.739.tgz",
"integrity": "sha512-+LPJVRsN7hGZ9EIUUiWCpO7l4E3qBYHNadazlucBfsXBbccDFNKUBAgzE68FnkWGJPwD/AfKhSzL+G+Iqb8A4A==",
"dev": true
},
"node_modules/emoji-regex": {
@ -4718,9 +4718,9 @@
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
},
"node_modules/ws": {
"version": "7.4.5",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.5.tgz",
"integrity": "sha512-xzyu3hFvomRfXKH8vOFMU3OguG6oOvhXMo3xsGy3xWExqaM2dxBbVxuD99O7m3ZUFMvvscsZDqxfgMaRr/Nr1g==",
"version": "7.4.6",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz",
"integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==",
"dev": true,
"engines": {
"node": ">=8.3.0"
@ -5914,9 +5914,9 @@
"dev": true
},
"caniuse-lite": {
"version": "1.0.30001228",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001228.tgz",
"integrity": "sha512-QQmLOGJ3DEgokHbMSA8cj2a+geXqmnpyOFT0lhQV6P3/YOJvGDEwoedcwxEQ30gJIwIIunHIicunJ2rzK5gB2A==",
"version": "1.0.30001230",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001230.tgz",
"integrity": "sha512-5yBd5nWCBS+jWKTcHOzXwo5xzcj4ePE/yjtkZyUV1BTUmrBaA9MRGC+e7mxnqXSA90CmCA8L3eKLaSUkt099IQ==",
"dev": true
},
"chalk": {
@ -6281,9 +6281,9 @@
"dev": true
},
"electron-to-chromium": {
"version": "1.3.738",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.738.tgz",
"integrity": "sha512-vCMf4gDOpEylPSLPLSwAEsz+R3ShP02Y3cAKMZvTqule3XcPp7tgc/0ESI7IS6ZeyBlGClE50N53fIOkcIVnpw==",
"version": "1.3.739",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.739.tgz",
"integrity": "sha512-+LPJVRsN7hGZ9EIUUiWCpO7l4E3qBYHNadazlucBfsXBbccDFNKUBAgzE68FnkWGJPwD/AfKhSzL+G+Iqb8A4A==",
"dev": true
},
"emoji-regex": {
@ -8386,9 +8386,9 @@
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
},
"ws": {
"version": "7.4.5",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.5.tgz",
"integrity": "sha512-xzyu3hFvomRfXKH8vOFMU3OguG6oOvhXMo3xsGy3xWExqaM2dxBbVxuD99O7m3ZUFMvvscsZDqxfgMaRr/Nr1g==",
"version": "7.4.6",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz",
"integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==",
"dev": true
},
"xmlbuilder": {