cordova-notify-plugin 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +131 -0
- package/index.d.ts +66 -0
- package/package.json +47 -0
- package/plugin.xml +52 -0
- package/src/android/AlarmNotify.java +208 -0
- package/src/android/AlarmReceiver.java +44 -0
- package/src/android/BootReceiver.java +38 -0
- package/www/alarmNotify.js +66 -0
package/README.md
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# cordova-notify-plugin
|
|
2
|
+
|
|
3
|
+
A Cordova plugin to schedule and cancel local alarm notifications on Android.
|
|
4
|
+
Alarms fire even when the app is closed and survive device reboots.
|
|
5
|
+
|
|
6
|
+
## Supported Platforms
|
|
7
|
+
|
|
8
|
+
- Android (API 19+)
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
cordova plugin add cordova-notify-plugin
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Or from a local path:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
cordova plugin add ../cordova-plugin-alarm-notify
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Permissions
|
|
23
|
+
|
|
24
|
+
The plugin automatically adds the following permissions to `AndroidManifest.xml`:
|
|
25
|
+
|
|
26
|
+
| Permission | Purpose |
|
|
27
|
+
|---|---|
|
|
28
|
+
| `POST_NOTIFICATIONS` | Show notification when alarm fires (Android 13+) |
|
|
29
|
+
| `SCHEDULE_EXACT_ALARM` | Schedule exact-time alarms (Android 12+) |
|
|
30
|
+
| `RECEIVE_BOOT_COMPLETED` | Re-schedule alarms after device reboot |
|
|
31
|
+
| `REQUEST_IGNORE_BATTERY_OPTIMIZATIONS` | Prevent Doze mode from blocking alarms |
|
|
32
|
+
|
|
33
|
+
## Usage
|
|
34
|
+
|
|
35
|
+
### Schedule an Alarm
|
|
36
|
+
|
|
37
|
+
**Modern API (Promise):**
|
|
38
|
+
|
|
39
|
+
```javascript
|
|
40
|
+
cordova.plugins.alarmNotify.schedule({
|
|
41
|
+
id: 1,
|
|
42
|
+
title: 'Meeting',
|
|
43
|
+
text: 'Team standup starts now',
|
|
44
|
+
at: new Date('2026-04-17T09:00:00')
|
|
45
|
+
})
|
|
46
|
+
.then(msg => console.log('Scheduled:', msg))
|
|
47
|
+
.catch(err => console.error('Error:', err));
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
`at` also accepts a Unix millisecond timestamp:
|
|
51
|
+
|
|
52
|
+
```javascript
|
|
53
|
+
cordova.plugins.alarmNotify.schedule({
|
|
54
|
+
id: 2,
|
|
55
|
+
title: 'Reminder',
|
|
56
|
+
text: 'Check emails',
|
|
57
|
+
at: Date.now() + 5 * 60 * 1000 // 5 minutes from now
|
|
58
|
+
});
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
**Legacy API (Callbacks):**
|
|
62
|
+
|
|
63
|
+
```javascript
|
|
64
|
+
cordova.plugins.alarmNotify.schedule(
|
|
65
|
+
1, // id
|
|
66
|
+
'Meeting', // title
|
|
67
|
+
'Team standup', // text
|
|
68
|
+
Date.now() + 60000, // trigger time (ms)
|
|
69
|
+
msg => console.log(msg),
|
|
70
|
+
err => console.error(err)
|
|
71
|
+
);
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Cancel an Alarm
|
|
75
|
+
|
|
76
|
+
**Modern API (Promise):**
|
|
77
|
+
|
|
78
|
+
```javascript
|
|
79
|
+
cordova.plugins.alarmNotify.cancel(1)
|
|
80
|
+
.then(msg => console.log('Cancelled:', msg))
|
|
81
|
+
.catch(err => console.error('Error:', err));
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
**Legacy API (Callbacks):**
|
|
85
|
+
|
|
86
|
+
```javascript
|
|
87
|
+
cordova.plugins.alarmNotify.cancel(
|
|
88
|
+
1,
|
|
89
|
+
msg => console.log('Cancelled:', msg),
|
|
90
|
+
err => console.error('Error:', err)
|
|
91
|
+
);
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Runtime Permission Handling
|
|
95
|
+
|
|
96
|
+
On first use, the plugin may trigger the following system dialogs automatically:
|
|
97
|
+
|
|
98
|
+
- **Android 13+** — "Allow notifications" permission dialog.
|
|
99
|
+
- **Android 12+** — Redirects to the *Alarms & Reminders* settings screen if exact alarm permission is not granted.
|
|
100
|
+
- **All versions** — Requests battery optimization exemption so alarms work reliably in the background.
|
|
101
|
+
|
|
102
|
+
## TypeScript
|
|
103
|
+
|
|
104
|
+
Type definitions are included:
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
import { AlarmNotifyPlugin, AlarmOptions } from 'cordova-notify-plugin';
|
|
108
|
+
|
|
109
|
+
declare const cordova: {
|
|
110
|
+
plugins: { alarmNotify: AlarmNotifyPlugin }
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const options: AlarmOptions = {
|
|
114
|
+
id: 1,
|
|
115
|
+
title: 'Alarm',
|
|
116
|
+
text: 'Wake up!',
|
|
117
|
+
at: new Date()
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
await cordova.plugins.alarmNotify.schedule(options);
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Notes
|
|
124
|
+
|
|
125
|
+
- Reusing the same `id` overwrites the existing alarm with that id.
|
|
126
|
+
- Alarms with a trigger time in the past are silently ignored on reboot recovery.
|
|
127
|
+
- The plugin stores scheduled alarms in `SharedPreferences` so they can be restored after a reboot.
|
|
128
|
+
|
|
129
|
+
## License
|
|
130
|
+
|
|
131
|
+
Apache-2.0
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
export interface AlarmOptions {
|
|
2
|
+
/** Unique alarm identifier. Reusing the same id overwrites the previous alarm. */
|
|
3
|
+
id: number;
|
|
4
|
+
/** Notification title shown in the status bar. */
|
|
5
|
+
title: string;
|
|
6
|
+
/** Notification body text. */
|
|
7
|
+
text: string;
|
|
8
|
+
/** Trigger time as a Date object or Unix millisecond timestamp. */
|
|
9
|
+
at: Date | number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface AlarmNotifyPlugin {
|
|
13
|
+
/**
|
|
14
|
+
* Schedule a local alarm notification.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* // Promise
|
|
18
|
+
* await cordova.plugins.alarmNotify.schedule({
|
|
19
|
+
* id: 1,
|
|
20
|
+
* title: 'Meeting',
|
|
21
|
+
* text: 'Team standup',
|
|
22
|
+
* at: new Date('2026-04-17T09:00:00'),
|
|
23
|
+
* });
|
|
24
|
+
*
|
|
25
|
+
* // Callback (legacy)
|
|
26
|
+
* cordova.plugins.alarmNotify.schedule(1, 'Meeting', 'Standup', Date.now() + 60000,
|
|
27
|
+
* msg => console.log(msg),
|
|
28
|
+
* err => console.error(err)
|
|
29
|
+
* );
|
|
30
|
+
*/
|
|
31
|
+
schedule(options: AlarmOptions): Promise<string>;
|
|
32
|
+
schedule(
|
|
33
|
+
id: number,
|
|
34
|
+
title: string,
|
|
35
|
+
text: string,
|
|
36
|
+
atMillis: number,
|
|
37
|
+
success: (msg: string) => void,
|
|
38
|
+
error: (err: string) => void
|
|
39
|
+
): void;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Cancel a previously scheduled alarm.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* // Promise
|
|
46
|
+
* await cordova.plugins.alarmNotify.cancel(1);
|
|
47
|
+
*
|
|
48
|
+
* // Callback (legacy)
|
|
49
|
+
* cordova.plugins.alarmNotify.cancel(1,
|
|
50
|
+
* msg => console.log(msg),
|
|
51
|
+
* err => console.error(err)
|
|
52
|
+
* );
|
|
53
|
+
*/
|
|
54
|
+
cancel(id: number): Promise<string>;
|
|
55
|
+
cancel(
|
|
56
|
+
id: number,
|
|
57
|
+
success: (msg: string) => void,
|
|
58
|
+
error: (err: string) => void
|
|
59
|
+
): void;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
declare global {
|
|
63
|
+
interface CordovaPlugins {
|
|
64
|
+
alarmNotify: AlarmNotifyPlugin;
|
|
65
|
+
}
|
|
66
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "cordova-notify-plugin",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Cordova plugin to schedule and cancel local alarm notifications on Android. Survives device reboot and works while the app is closed.",
|
|
5
|
+
"main": "www/alarmNotify.js",
|
|
6
|
+
"types": "index.d.ts",
|
|
7
|
+
"cordova": {
|
|
8
|
+
"id": "com.solocode.alarmnotify",
|
|
9
|
+
"platforms": [
|
|
10
|
+
"android"
|
|
11
|
+
]
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"ecosystem:cordova",
|
|
15
|
+
"cordova-android",
|
|
16
|
+
"alarm",
|
|
17
|
+
"notification",
|
|
18
|
+
"local-notification",
|
|
19
|
+
"schedule",
|
|
20
|
+
"background"
|
|
21
|
+
],
|
|
22
|
+
"files": [
|
|
23
|
+
"plugin.xml",
|
|
24
|
+
"package.json",
|
|
25
|
+
"index.d.ts",
|
|
26
|
+
"www/",
|
|
27
|
+
"src/"
|
|
28
|
+
],
|
|
29
|
+
"engines": {
|
|
30
|
+
"cordovaDependencies": {
|
|
31
|
+
"1.0.0": {
|
|
32
|
+
"cordova-android": ">=12.0.0",
|
|
33
|
+
"cordova": ">=12.0.0"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"author": "",
|
|
38
|
+
"license": "MIT",
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": ""
|
|
42
|
+
},
|
|
43
|
+
"bugs": {
|
|
44
|
+
"url": ""
|
|
45
|
+
},
|
|
46
|
+
"homepage": ""
|
|
47
|
+
}
|
package/plugin.xml
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
<?xml version='1.0' encoding='utf-8'?>
|
|
2
|
+
<plugin id="com.solocode.alarmnotify" version="1.0.0" xmlns="http://apache.org/cordova/ns/plugins/1.0" xmlns:android="http://schemas.android.com/apk/res/android">
|
|
3
|
+
<name>alarmNotify</name>
|
|
4
|
+
|
|
5
|
+
<js-module name="alarmNotify" src="www/alarmNotify.js">
|
|
6
|
+
<clobbers target="cordova.plugins.alarmNotify" />
|
|
7
|
+
</js-module>
|
|
8
|
+
|
|
9
|
+
<platform name="android">
|
|
10
|
+
<config-file target="res/xml/config.xml" parent="/*">
|
|
11
|
+
<feature name="AlarmNotify">
|
|
12
|
+
<param name="android-package" value="com.solocode.alarmnotify.AlarmNotify" />
|
|
13
|
+
</feature>
|
|
14
|
+
</config-file>
|
|
15
|
+
|
|
16
|
+
<source-file src="src/android/AlarmNotify.java" target-dir="src/com/solocode/alarmnotify" />
|
|
17
|
+
<source-file src="src/android/AlarmReceiver.java" target-dir="src/com/solocode/alarmnotify" />
|
|
18
|
+
<source-file src="src/android/BootReceiver.java" target-dir="src/com/solocode/alarmnotify" />
|
|
19
|
+
|
|
20
|
+
<framework src="androidx.core:core:1.12.0" />
|
|
21
|
+
|
|
22
|
+
<config-file target="AndroidManifest.xml" parent="/manifest">
|
|
23
|
+
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
|
24
|
+
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
|
25
|
+
<!-- Tam zamanlı alarm için (Android 12+ bazı durumlarda gerekebilir) -->
|
|
26
|
+
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
|
|
27
|
+
|
|
28
|
+
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
|
29
|
+
</config-file>
|
|
30
|
+
|
|
31
|
+
<config-file target="AndroidManifest.xml" parent="/manifest/application">
|
|
32
|
+
|
|
33
|
+
<!-- Alarm tetikleyici receiver -->
|
|
34
|
+
<receiver
|
|
35
|
+
android:name="com.solocode.alarmnotify.AlarmReceiver"
|
|
36
|
+
android:exported="false" />
|
|
37
|
+
|
|
38
|
+
<!-- Reboot sonrası tekrar kuran receiver -->
|
|
39
|
+
<receiver
|
|
40
|
+
android:name="com.solocode.alarmnotify.BootReceiver"
|
|
41
|
+
android:exported="true">
|
|
42
|
+
<intent-filter>
|
|
43
|
+
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
|
44
|
+
</intent-filter>
|
|
45
|
+
</receiver>
|
|
46
|
+
|
|
47
|
+
</config-file>
|
|
48
|
+
</platform>
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
</plugin>
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
package com.solocode.alarmnotify;
|
|
2
|
+
|
|
3
|
+
import android.Manifest;
|
|
4
|
+
import android.app.AlarmManager;
|
|
5
|
+
import android.app.PendingIntent;
|
|
6
|
+
import android.content.Context;
|
|
7
|
+
import android.content.Intent;
|
|
8
|
+
import android.content.SharedPreferences;
|
|
9
|
+
import android.content.pm.PackageManager;
|
|
10
|
+
import android.net.Uri;
|
|
11
|
+
import android.os.Build;
|
|
12
|
+
import android.os.PowerManager;
|
|
13
|
+
import android.provider.Settings;
|
|
14
|
+
|
|
15
|
+
import org.apache.cordova.CallbackContext;
|
|
16
|
+
import org.apache.cordova.CordovaPlugin;
|
|
17
|
+
import org.apache.cordova.PermissionHelper;
|
|
18
|
+
|
|
19
|
+
import org.json.JSONArray;
|
|
20
|
+
import org.json.JSONException;
|
|
21
|
+
import org.json.JSONObject;
|
|
22
|
+
|
|
23
|
+
import java.util.HashSet;
|
|
24
|
+
import java.util.Set;
|
|
25
|
+
|
|
26
|
+
public class AlarmNotify extends CordovaPlugin {
|
|
27
|
+
|
|
28
|
+
private static final int REQ_POST_NOTIF = 7001;
|
|
29
|
+
|
|
30
|
+
private static final String PREFS = "AlarmNotifyPrefs";
|
|
31
|
+
private static final String KEY_IDS = "alarm_ids";
|
|
32
|
+
private static final String KEY_ALARM_PREFIX = "alarm_";
|
|
33
|
+
|
|
34
|
+
private CallbackContext pendingCallback;
|
|
35
|
+
private JSONArray pendingArgs;
|
|
36
|
+
|
|
37
|
+
@Override
|
|
38
|
+
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
|
|
39
|
+
|
|
40
|
+
if ("schedule".equals(action)) {
|
|
41
|
+
// Android 13+ bildirim izni
|
|
42
|
+
if (Build.VERSION.SDK_INT >= 33) {
|
|
43
|
+
if (!hasPostNotificationsPermission()) {
|
|
44
|
+
pendingCallback = callbackContext;
|
|
45
|
+
pendingArgs = args;
|
|
46
|
+
PermissionHelper.requestPermission(this, REQ_POST_NOTIF, Manifest.permission.POST_NOTIFICATIONS);
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// Android 12'de exact alarm izni kontrolü
|
|
51
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
|
52
|
+
AlarmManager am = (AlarmManager) cordova.getActivity().getSystemService(Context.ALARM_SERVICE);
|
|
53
|
+
if (!am.canScheduleExactAlarms()) {
|
|
54
|
+
// Kullanıcıyı exact alarm ayarları ekranına yönlendir
|
|
55
|
+
Intent intent = new Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM,
|
|
56
|
+
Uri.parse("package:" + cordova.getActivity().getPackageName()));
|
|
57
|
+
cordova.getActivity().startActivity(intent);
|
|
58
|
+
callbackContext.error("EXACT_ALARM_PERMISSION_REQUIRED");
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// Pil optimizasyon muafiyeti iste (Doze / agresif üretici optimizasyonlarına karşı)
|
|
63
|
+
requestBatteryExemptionIfNeeded();
|
|
64
|
+
scheduleInternal(args, callbackContext);
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if ("cancel".equals(action)) {
|
|
69
|
+
int id = args.optInt(0, 1);
|
|
70
|
+
cancelAlarm(cordova.getActivity().getApplicationContext(), id);
|
|
71
|
+
removeAlarm(cordova.getActivity().getApplicationContext(), id);
|
|
72
|
+
callbackContext.success("cancelled " + id);
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
private boolean hasPostNotificationsPermission() {
|
|
80
|
+
return cordova.getActivity().checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS)
|
|
81
|
+
== PackageManager.PERMISSION_GRANTED;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
private void requestBatteryExemptionIfNeeded() {
|
|
85
|
+
try {
|
|
86
|
+
PowerManager pm = (PowerManager) cordova.getActivity().getSystemService(Context.POWER_SERVICE);
|
|
87
|
+
String pkg = cordova.getActivity().getPackageName();
|
|
88
|
+
if (!pm.isIgnoringBatteryOptimizations(pkg)) {
|
|
89
|
+
Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS,
|
|
90
|
+
Uri.parse("package:" + pkg));
|
|
91
|
+
cordova.getActivity().startActivity(intent);
|
|
92
|
+
}
|
|
93
|
+
} catch (Exception ignored) {
|
|
94
|
+
// İzin yoksa veya cihaz desteklemiyorsa sessizce geç
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
@Override
|
|
99
|
+
public void onRequestPermissionResult(int requestCode, String[] permissions, int[] grantResults) throws JSONException {
|
|
100
|
+
if (requestCode == REQ_POST_NOTIF) {
|
|
101
|
+
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
|
102
|
+
// Android 12 exact alarm kontrolü de burada tekrarla
|
|
103
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
|
104
|
+
AlarmManager am = (AlarmManager) cordova.getActivity().getSystemService(Context.ALARM_SERVICE);
|
|
105
|
+
if (!am.canScheduleExactAlarms()) {
|
|
106
|
+
Intent intent = new Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM,
|
|
107
|
+
Uri.parse("package:" + cordova.getActivity().getPackageName()));
|
|
108
|
+
cordova.getActivity().startActivity(intent);
|
|
109
|
+
if (pendingCallback != null) pendingCallback.error("EXACT_ALARM_PERMISSION_REQUIRED");
|
|
110
|
+
pendingCallback = null;
|
|
111
|
+
pendingArgs = null;
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
requestBatteryExemptionIfNeeded();
|
|
116
|
+
scheduleInternal(pendingArgs, pendingCallback);
|
|
117
|
+
} else {
|
|
118
|
+
if (pendingCallback != null) pendingCallback.error("POST_NOTIFICATIONS denied");
|
|
119
|
+
}
|
|
120
|
+
pendingCallback = null;
|
|
121
|
+
pendingArgs = null;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
private void scheduleInternal(JSONArray args, CallbackContext cb) {
|
|
126
|
+
try {
|
|
127
|
+
int id = args.optInt(0, 1);
|
|
128
|
+
String title = args.optString(1, "Alarm");
|
|
129
|
+
String text = args.optString(2, "Time!");
|
|
130
|
+
long atMillis = args.optLong(3, System.currentTimeMillis() + 5000);
|
|
131
|
+
|
|
132
|
+
Context ctx = cordova.getActivity().getApplicationContext();
|
|
133
|
+
scheduleAlarm(ctx, id, title, text, atMillis);
|
|
134
|
+
saveAlarm(ctx, id, title, text, atMillis);
|
|
135
|
+
cb.success("scheduled " + id + " at " + atMillis);
|
|
136
|
+
} catch (Exception e) {
|
|
137
|
+
cb.error(e.getMessage());
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// --------- STATIC HELPERS (BootReceiver da kullanacak) ---------
|
|
142
|
+
|
|
143
|
+
public static void scheduleAlarm(Context ctx, int id, String title, String text, long atMillis) {
|
|
144
|
+
Intent intent = new Intent(ctx, AlarmReceiver.class);
|
|
145
|
+
intent.putExtra("id", id);
|
|
146
|
+
intent.putExtra("title", title);
|
|
147
|
+
intent.putExtra("text", text);
|
|
148
|
+
|
|
149
|
+
PendingIntent pi = PendingIntent.getBroadcast(
|
|
150
|
+
ctx, id, intent,
|
|
151
|
+
PendingIntent.FLAG_UPDATE_CURRENT | (Build.VERSION.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0)
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
AlarmManager am = (AlarmManager) ctx.getSystemService(Context.ALARM_SERVICE);
|
|
155
|
+
|
|
156
|
+
try {
|
|
157
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && am.canScheduleExactAlarms()) {
|
|
158
|
+
am.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, atMillis, pi);
|
|
159
|
+
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
160
|
+
am.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, atMillis, pi);
|
|
161
|
+
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
|
162
|
+
am.setExact(AlarmManager.RTC_WAKEUP, atMillis, pi);
|
|
163
|
+
} else {
|
|
164
|
+
am.set(AlarmManager.RTC_WAKEUP, atMillis, pi);
|
|
165
|
+
}
|
|
166
|
+
} catch (SecurityException se) {
|
|
167
|
+
am.set(AlarmManager.RTC_WAKEUP, atMillis, pi);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
public static void cancelAlarm(Context ctx, int id) {
|
|
172
|
+
Intent intent = new Intent(ctx, AlarmReceiver.class);
|
|
173
|
+
PendingIntent pi = PendingIntent.getBroadcast(
|
|
174
|
+
ctx, id, intent,
|
|
175
|
+
PendingIntent.FLAG_UPDATE_CURRENT | (Build.VERSION.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0)
|
|
176
|
+
);
|
|
177
|
+
AlarmManager am = (AlarmManager) ctx.getSystemService(Context.ALARM_SERVICE);
|
|
178
|
+
am.cancel(pi);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
private static void saveAlarm(Context ctx, int id, String title, String text, long atMillis) throws JSONException {
|
|
182
|
+
SharedPreferences sp = ctx.getSharedPreferences(PREFS, Context.MODE_PRIVATE);
|
|
183
|
+
Set<String> ids = new HashSet<>(sp.getStringSet(KEY_IDS, new HashSet<>()));
|
|
184
|
+
ids.add(String.valueOf(id));
|
|
185
|
+
JSONObject o = new JSONObject();
|
|
186
|
+
o.put("id", id); o.put("title", title); o.put("text", text); o.put("atMillis", atMillis);
|
|
187
|
+
sp.edit().putStringSet(KEY_IDS, ids).putString(KEY_ALARM_PREFIX + id, o.toString()).apply();
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
private static void removeAlarm(Context ctx, int id) {
|
|
191
|
+
SharedPreferences sp = ctx.getSharedPreferences(PREFS, Context.MODE_PRIVATE);
|
|
192
|
+
Set<String> ids = new HashSet<>(sp.getStringSet(KEY_IDS, new HashSet<>()));
|
|
193
|
+
ids.remove(String.valueOf(id));
|
|
194
|
+
sp.edit().putStringSet(KEY_IDS, ids).remove(KEY_ALARM_PREFIX + id).apply();
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
public static Set<String> getSavedAlarmIds(Context ctx) {
|
|
198
|
+
SharedPreferences sp = ctx.getSharedPreferences(PREFS, Context.MODE_PRIVATE);
|
|
199
|
+
return new HashSet<>(sp.getStringSet(KEY_IDS, new HashSet<>()));
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
public static JSONObject getSavedAlarm(Context ctx, String idStr) {
|
|
203
|
+
SharedPreferences sp = ctx.getSharedPreferences(PREFS, Context.MODE_PRIVATE);
|
|
204
|
+
String raw = sp.getString(KEY_ALARM_PREFIX + idStr, null);
|
|
205
|
+
if (raw == null) return null;
|
|
206
|
+
try { return new JSONObject(raw); } catch (Exception e) { return null; }
|
|
207
|
+
}
|
|
208
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
package com.solocode.alarmnotify;
|
|
2
|
+
|
|
3
|
+
import android.app.NotificationChannel;
|
|
4
|
+
import android.app.NotificationManager;
|
|
5
|
+
import android.content.BroadcastReceiver;
|
|
6
|
+
import android.content.Context;
|
|
7
|
+
import android.content.Intent;
|
|
8
|
+
import android.os.Build;
|
|
9
|
+
|
|
10
|
+
import androidx.core.app.NotificationCompat;
|
|
11
|
+
|
|
12
|
+
public class AlarmReceiver extends BroadcastReceiver {
|
|
13
|
+
|
|
14
|
+
private static final String CHANNEL_ID = "alarm_notify_channel";
|
|
15
|
+
|
|
16
|
+
@Override
|
|
17
|
+
public void onReceive(Context context, Intent intent) {
|
|
18
|
+
int id = intent.getIntExtra("id", 1);
|
|
19
|
+
String title = intent.getStringExtra("title");
|
|
20
|
+
String text = intent.getStringExtra("text");
|
|
21
|
+
|
|
22
|
+
NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
|
23
|
+
|
|
24
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
25
|
+
NotificationChannel ch = new NotificationChannel(
|
|
26
|
+
CHANNEL_ID,
|
|
27
|
+
"Alarm Notifications",
|
|
28
|
+
NotificationManager.IMPORTANCE_HIGH
|
|
29
|
+
);
|
|
30
|
+
nm.createNotificationChannel(ch);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
int iconRes = context.getApplicationInfo().icon;
|
|
34
|
+
|
|
35
|
+
NotificationCompat.Builder b = new NotificationCompat.Builder(context, CHANNEL_ID)
|
|
36
|
+
.setSmallIcon(iconRes)
|
|
37
|
+
.setContentTitle(title != null ? title : "Alarm")
|
|
38
|
+
.setContentText(text != null ? text : "Time!")
|
|
39
|
+
.setAutoCancel(true)
|
|
40
|
+
.setPriority(NotificationCompat.PRIORITY_HIGH);
|
|
41
|
+
|
|
42
|
+
nm.notify(id, b.build());
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
package com.solocode.alarmnotify;
|
|
2
|
+
|
|
3
|
+
import android.content.BroadcastReceiver;
|
|
4
|
+
import android.content.Context;
|
|
5
|
+
import android.content.Intent;
|
|
6
|
+
|
|
7
|
+
import org.json.JSONObject;
|
|
8
|
+
|
|
9
|
+
import java.util.Set;
|
|
10
|
+
|
|
11
|
+
public class BootReceiver extends BroadcastReceiver {
|
|
12
|
+
|
|
13
|
+
@Override
|
|
14
|
+
public void onReceive(Context context, Intent intent) {
|
|
15
|
+
if (intent == null) return;
|
|
16
|
+
if (!Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) return;
|
|
17
|
+
|
|
18
|
+
Set<String> ids = AlarmNotify.getSavedAlarmIds(context);
|
|
19
|
+
long now = System.currentTimeMillis();
|
|
20
|
+
|
|
21
|
+
for (String idStr : ids) {
|
|
22
|
+
try {
|
|
23
|
+
JSONObject o = AlarmNotify.getSavedAlarm(context, idStr);
|
|
24
|
+
if (o == null) continue;
|
|
25
|
+
|
|
26
|
+
int id = o.optInt("id", 1);
|
|
27
|
+
String title = o.optString("title", "Alarm");
|
|
28
|
+
String text = o.optString("text", "Time!");
|
|
29
|
+
long atMillis = o.optLong("atMillis", now);
|
|
30
|
+
|
|
31
|
+
// geçmişte kaldıysa istersen hemen göster, ya da atla:
|
|
32
|
+
if (atMillis < now) continue;
|
|
33
|
+
|
|
34
|
+
AlarmNotify.scheduleAlarm(context, id, title, text, atMillis);
|
|
35
|
+
} catch (Exception ignored) {}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
var exec = require('cordova/exec');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Schedule a local alarm notification.
|
|
5
|
+
*
|
|
6
|
+
* Modern usage (returns Promise):
|
|
7
|
+
* alarmNotify.schedule({ id: 1, title: 'Meeting', text: 'Standup', at: new Date() })
|
|
8
|
+
*
|
|
9
|
+
* Legacy usage (callbacks):
|
|
10
|
+
* alarmNotify.schedule(1, 'Meeting', 'Standup', Date.now() + 60000, onSuccess, onError)
|
|
11
|
+
*
|
|
12
|
+
* @param {Object|number} optionsOrId Options object or alarm id (legacy).
|
|
13
|
+
* @param {string} [title] Notification title (legacy).
|
|
14
|
+
* @param {string} [text] Notification body (legacy).
|
|
15
|
+
* @param {number} [atMillis] Trigger time as Unix ms timestamp (legacy).
|
|
16
|
+
* @param {Function} [success] Success callback (legacy).
|
|
17
|
+
* @param {Function} [error] Error callback (legacy).
|
|
18
|
+
* @returns {Promise<string>|undefined}
|
|
19
|
+
*/
|
|
20
|
+
exports.schedule = function (optionsOrId, title, text, atMillis, success, error) {
|
|
21
|
+
var id, _title, _text, _at;
|
|
22
|
+
|
|
23
|
+
if (optionsOrId !== null && typeof optionsOrId === 'object') {
|
|
24
|
+
// Modern: options object
|
|
25
|
+
id = optionsOrId.id != null ? optionsOrId.id : 1;
|
|
26
|
+
_title = optionsOrId.title != null ? optionsOrId.title : 'Alarm';
|
|
27
|
+
_text = optionsOrId.text != null ? optionsOrId.text : '';
|
|
28
|
+
_at = optionsOrId.at instanceof Date
|
|
29
|
+
? optionsOrId.at.getTime()
|
|
30
|
+
: (optionsOrId.at != null ? optionsOrId.at : Date.now());
|
|
31
|
+
|
|
32
|
+
return new Promise(function (resolve, reject) {
|
|
33
|
+
exec(resolve, reject, 'AlarmNotify', 'schedule', [id, _title, _text, _at]);
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Legacy: positional args + callbacks
|
|
38
|
+
id = optionsOrId;
|
|
39
|
+
_title = title;
|
|
40
|
+
_text = text;
|
|
41
|
+
_at = atMillis;
|
|
42
|
+
exec(success, error, 'AlarmNotify', 'schedule', [id, _title, _text, _at]);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Cancel a previously scheduled alarm.
|
|
47
|
+
*
|
|
48
|
+
* Modern usage (returns Promise):
|
|
49
|
+
* alarmNotify.cancel(1)
|
|
50
|
+
*
|
|
51
|
+
* Legacy usage (callbacks):
|
|
52
|
+
* alarmNotify.cancel(1, onSuccess, onError)
|
|
53
|
+
*
|
|
54
|
+
* @param {number} id Alarm id to cancel.
|
|
55
|
+
* @param {Function} [success] Success callback (legacy).
|
|
56
|
+
* @param {Function} [error] Error callback (legacy).
|
|
57
|
+
* @returns {Promise<string>|undefined}
|
|
58
|
+
*/
|
|
59
|
+
exports.cancel = function (id, success, error) {
|
|
60
|
+
if (typeof success !== 'function' && typeof error !== 'function') {
|
|
61
|
+
return new Promise(function (resolve, reject) {
|
|
62
|
+
exec(resolve, reject, 'AlarmNotify', 'cancel', [id]);
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
exec(success, error, 'AlarmNotify', 'cancel', [id]);
|
|
66
|
+
};
|