@maccesar/titools 5.2.5 → 5.4.0
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/CONTRIBUTING.md +1 -1
- package/README.md +58 -97
- package/agents/ti-pro.md +4 -2
- package/commands/ti-audit.md +7 -5
- package/docs/examples.md +24 -13
- package/docs/knowledge-index.md +2 -1
- package/lib/config.js +1 -0
- package/package.json +1 -1
- package/skills/alloy-guides/SKILL.md +18 -179
- package/skills/alloy-guides/references/CLI_TASKS.md +17 -216
- package/skills/alloy-guides/references/CONCEPTS.md +27 -162
- package/skills/alloy-guides/references/CONTROLLERS.md +38 -236
- package/skills/alloy-guides/references/MODELS.md +47 -723
- package/skills/alloy-guides/references/MODELS_ADVANCED.md +21 -482
- package/skills/alloy-guides/references/VIEWS_DYNAMIC.md +12 -222
- package/skills/alloy-guides/references/VIEWS_STYLES.md +39 -358
- package/skills/alloy-guides/references/VIEWS_XML.md +36 -552
- package/skills/alloy-guides/references/WIDGETS.md +49 -127
- package/skills/alloy-howtos/SKILL.md +14 -169
- package/skills/alloy-howtos/references/best_practices.md +9 -113
- package/skills/alloy-howtos/references/cli_reference.md +18 -223
- package/skills/alloy-howtos/references/config_files.md +50 -133
- package/skills/alloy-howtos/references/debugging_troubleshooting.md +19 -63
- package/skills/alloy-howtos/references/samples.md +21 -138
- package/skills/ti-api/SKILL.md +10 -2
- package/skills/ti-api/references/api-android.md +36 -2
- package/skills/ti-api/references/api-app-platform.md +39 -3
- package/skills/ti-api/references/api-core.md +50 -18
- package/skills/ti-api/references/api-data-network.md +49 -35
- package/skills/ti-api/references/api-media.md +45 -27
- package/skills/ti-api/references/api-services.md +39 -9
- package/skills/ti-api/references/api-ui-android.md +33 -13
- package/skills/ti-api/references/api-ui-extras.md +41 -15
- package/skills/ti-api/references/api-ui-ios-animator.md +42 -26
- package/skills/ti-api/references/api-ui-ios.md +74 -2
- package/skills/ti-api/references/api-ui-lists.md +32 -16
- package/skills/ti-api/references/api-ui-text-input.md +34 -17
- package/skills/ti-api/references/api-ui-views.md +27 -1
- package/skills/ti-api/references/api-ui-windows-navigation.md +32 -18
- package/skills/ti-api/references/api-xml-global.md +66 -16
- package/skills/ti-create-release/SKILL.md +98 -0
- package/skills/ti-create-release/references/module-discovery.md +69 -0
- package/skills/ti-expert/references/adaptive-layouts.md +1 -1
- package/skills/ti-guides/SKILL.md +37 -76
- package/skills/ti-guides/references/advanced-data-and-images.md +122 -126
- package/skills/ti-guides/references/android-manifest.md +24 -83
- package/skills/ti-guides/references/app-distribution.md +31 -377
- package/skills/ti-guides/references/application-frameworks.md +127 -335
- package/skills/ti-guides/references/cli-reference.md +184 -633
- package/skills/ti-guides/references/commonjs-advanced.md +81 -231
- package/skills/ti-guides/references/compatibility-matrix.md +36 -75
- package/skills/ti-guides/references/hello-world.md +95 -70
- package/skills/ti-guides/references/hyperloop-native-access.md +52 -448
- package/skills/ti-guides/references/resources.md +174 -142
- package/skills/ti-guides/references/sdk-release-notes.md +28 -71
- package/skills/ti-guides/references/tiapp-config.md +222 -598
- package/skills/ti-howtos/SKILL.md +23 -123
- package/skills/ti-howtos/references/cross-platform-development.md +119 -324
- package/skills/ti-howtos/references/debugging-profiling.md +77 -445
- package/skills/ti-howtos/references/extending-titanium.md +199 -673
- package/skills/ti-howtos/references/local-data-sources.md +87 -300
- package/skills/ti-howtos/references/location-and-maps.md +86 -213
- package/skills/ti-howtos/references/media-apis.md +103 -223
- package/skills/ti-howtos/references/notification-services.md +86 -506
- package/skills/ti-howtos/references/remote-data-sources.md +97 -316
- package/skills/ti-howtos/references/using-modules.md +77 -133
- package/skills/ti-synthengine/SKILL.md +62 -111
- package/skills/ti-synthengine/assets/example/README.md +50 -59
- package/skills/ti-synthengine/assets/example/app.js +67 -65
- package/skills/ti-synthengine/assets/example/lib/sequenceOwner.js +39 -40
- package/skills/ti-synthengine/assets/example/lib/soundPreset.js +34 -0
- package/skills/ti-synthengine/assets/index.d.ts +122 -2
- package/skills/ti-synthengine/references/advanced-examples.md +4 -4
- package/skills/ti-synthengine/references/api.md +40 -18
- package/skills/ti-synthengine/references/audio-engine.md +15 -57
- package/skills/ti-synthengine/references/examples.md +63 -6
- package/skills/ti-synthengine/references/guide.md +61 -57
- package/skills/ti-synthengine/references/instruments-and-effects.md +15 -15
- package/skills/ti-synthengine/references/native-variation.md +53 -14
- package/skills/ti-synthengine/references/recipes-interaction.md +25 -20
- package/skills/ti-synthengine/references/recipes.md +19 -16
- package/skills/ti-synthengine/references/recommendations.md +12 -12
- package/skills/ti-synthengine/references/sound-api.md +197 -0
- package/skills/ti-synthengine/references/sound-design.md +7 -7
- package/skills/ti-synthengine/references/source-notes.md +62 -172
- package/skills/ti-synthengine/references/troubleshooting.md +17 -19
- package/skills/ti-synthengine/references/upgrade-2.0.0.md +40 -0
- package/skills/ti-ui/SKILL.md +28 -143
- package/skills/ti-ui/references/icons-and-splash-screens.md +74 -403
- package/skills/ti-ui/references/layouts-and-positioning.md +175 -402
- package/skills/ti-ui/references/listviews-and-performance.md +100 -703
- package/skills/ti-ui/references/platform-conventions.md +180 -0
- package/skills/ti-ui/references/user-interface.md +43 -0
- package/skills/alloy-guides/references/VIEWS_WITHOUT_CONTROLLERS.md +0 -102
- package/skills/alloy-howtos/references/custom_tags.md +0 -148
- package/skills/ti-guides/references/coding-best-practices.md +0 -149
- package/skills/ti-guides/references/javascript-primer.md +0 -414
- package/skills/ti-guides/references/reserved-words.md +0 -36
- package/skills/ti-guides/references/style-and-conventions.md +0 -103
- package/skills/ti-howtos/references/android-platform-deep-dives.md +0 -624
- package/skills/ti-howtos/references/automation-fastlane-appium.md +0 -96
- package/skills/ti-howtos/references/buffer-codec-streams.md +0 -162
- package/skills/ti-howtos/references/google-maps-v2.md +0 -172
- package/skills/ti-howtos/references/ios-map-kit.md +0 -149
- package/skills/ti-howtos/references/ios-platform-deep-dives.md +0 -611
- package/skills/ti-howtos/references/tutorials.md +0 -565
- package/skills/ti-howtos/references/web-content-integration.md +0 -287
- package/skills/ti-howtos/references/webpack-build-pipeline.md +0 -124
- package/skills/ti-synthengine/references/android-sim80-correction-evidence.zip +0 -0
- package/skills/ti-synthengine/references/android-sim80-correction.json +0 -162
- package/skills/ti-synthengine/references/audio-pipeline-validation.md +0 -19
- package/skills/ti-synthengine/references/bypass-physical-recheck-evidence.zip +0 -0
- package/skills/ti-synthengine/references/frontier-android-evidence.zip +0 -0
- package/skills/ti-synthengine/references/frontier-ipad-evidence.zip +0 -0
- package/skills/ti-synthengine/references/frontier-physical-validation.json +0 -1918
- package/skills/ti-synthengine/references/native-tone-repetition-validation.md +0 -59
- package/skills/ti-synthengine/references/native-variation-proposal.md +0 -182
- package/skills/ti-synthengine/references/native-variation-validation.md +0 -63
- package/skills/ti-synthengine/references/performance-review-evidence.zip +0 -0
- package/skills/ti-synthengine/references/performance-review.json +0 -1146
- package/skills/ti-synthengine/references/physical-validation.json +0 -1457
- package/skills/ti-synthengine/references/source-snapshot.json +0 -542
- package/skills/ti-synthengine/references/stats-performance.json +0 -2992
- package/skills/ti-synthengine/references/stats-performance.md +0 -82
- package/skills/ti-synthengine/references/studio-validation.json +0 -6202
- package/skills/ti-synthengine/references/unified-bypass-performance.json +0 -22425
- package/skills/ti-synthengine/references/unified-validation.json +0 -314
- package/skills/ti-ui/references/accessibility-deep-dive.md +0 -288
- package/skills/ti-ui/references/animation-and-matrices.md +0 -569
- package/skills/ti-ui/references/application-structures.md +0 -626
- package/skills/ti-ui/references/custom-fonts-styling.md +0 -549
- package/skills/ti-ui/references/event-handling.md +0 -401
- package/skills/ti-ui/references/gestures.md +0 -448
- package/skills/ti-ui/references/orientation.md +0 -374
- package/skills/ti-ui/references/platform-ui-android.md +0 -727
- package/skills/ti-ui/references/platform-ui-ios.md +0 -576
- package/skills/ti-ui/references/scrolling-views.md +0 -288
- package/skills/ti-ui/references/tableviews.md +0 -542
|
@@ -1,551 +1,131 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Notifications
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Based on the [current official guide](https://titaniumsdk.com/docs/build/notifications.md). Examples target released SDK 13.4.1; verify the selected SDK before adopting version-specific behavior.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
Two kinds, and they share almost nothing in Titanium. A **local** notification
|
|
6
|
+
is scheduled by the app on the device. A **push** notification arrives from a
|
|
7
|
+
server through APNs or FCM.
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
- [Interactive notifications: iOS (iOS 8+)](#interactive-notifications-ios-ios-8)
|
|
11
|
-
- [Push notifications](#push-notifications)
|
|
12
|
-
- [Common patterns](#common-patterns)
|
|
13
|
-
- [Platform-specific notes](#platform-specific-notes)
|
|
9
|
+
Both need the user's permission on both platforms. Android has required it
|
|
10
|
+
since Android 13.
|
|
14
11
|
|
|
15
|
-
|
|
12
|
+
## Local notifications
|
|
16
13
|
|
|
17
|
-
|
|
14
|
+
The APIs are platform-specific, so this is one of the places you write both
|
|
15
|
+
halves.
|
|
18
16
|
|
|
19
|
-
|
|
17
|
+
**iOS**
|
|
20
18
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const notification = Ti.Android.createNotification({
|
|
30
|
-
contentTitle: 'Notification Title',
|
|
31
|
-
contentText: 'Notification message',
|
|
32
|
-
contentIntent: Ti.Android.createPendingIntent({
|
|
33
|
-
intent: Ti.Android.createIntent({})
|
|
34
|
-
}),
|
|
35
|
-
icon: Ti.App.Android.R.drawable.warn, // or '/images/warn.png'
|
|
36
|
-
number: 5, // Badge number
|
|
37
|
-
when: new Date(), // Timestamp (does not schedule)
|
|
38
|
-
tickerText: 'Text in status bar when notification first appears'
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
// Send immediately
|
|
42
|
-
Ti.Android.NotificationManager.notify(1, notification);
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
Icon paths:
|
|
46
|
-
- Density-specific: `/app/assets/android/images/` (Alloy) or `/Resources/android/images/` (Classic), reference as `/images/filename.png`
|
|
47
|
-
- Drawable folder: `/platform/android/res/drawable/filename.png`, reference as `Ti.App.Android.R.drawable.filename`
|
|
48
|
-
|
|
49
|
-
### Sound
|
|
50
|
-
|
|
51
|
-
```javascript
|
|
52
|
-
const notification = Ti.Android.createNotification({
|
|
53
|
-
// ... other properties
|
|
54
|
-
sound: `${Ti.Filesystem.getResRawDirectory()}sound.wav`
|
|
55
|
-
});
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
Play only once (add flag):
|
|
59
|
-
```javascript
|
|
60
|
-
notification.flags |= Ti.Android.FLAG_ONLY_ALERT_ONCE;
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
### Custom layout with RemoteViews
|
|
64
|
-
|
|
65
|
-
1. Create XML layout (`/platform/android/res/layout/customview.xml`):
|
|
66
|
-
```xml
|
|
67
|
-
<?xml version="1.0" encoding="utf-8"?>
|
|
68
|
-
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
69
|
-
android:layout_width="match_parent"
|
|
70
|
-
android:layout_height="match_parent"
|
|
71
|
-
android:orientation="horizontal">
|
|
72
|
-
<TextView android:id="@+id/message"
|
|
73
|
-
android:layout_width="wrap_content"
|
|
74
|
-
android:layout_height="wrap_content"
|
|
75
|
-
android:text="Default text" />
|
|
76
|
-
<Button android:id="@+id/okbutton"
|
|
77
|
-
android:layout_width="wrap_content"
|
|
78
|
-
android:layout_height="wrap_content"
|
|
79
|
-
android:text="OK" />
|
|
80
|
-
</LinearLayout>
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
2. Create RemoteViews and bind intents:
|
|
84
|
-
```javascript
|
|
85
|
-
const customView = Ti.Android.createRemoteViews({
|
|
86
|
-
layoutId: Ti.App.Android.R.layout.customview
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
// Modify text
|
|
90
|
-
customView.setTextViewText(Ti.App.Android.R.id.message, 'Update available!');
|
|
91
|
-
|
|
92
|
-
// Bind intents to buttons
|
|
93
|
-
const downloadIntent = Ti.Android.createPendingIntent({ intent: Ti.Android.createIntent() });
|
|
94
|
-
customView.setOnClickPendingIntent(Ti.App.Android.R.id.okbutton, downloadIntent);
|
|
95
|
-
|
|
96
|
-
const notification = Ti.Android.createNotification({
|
|
97
|
-
contentView: customView // Avoid setting contentTitle/contentText or they override this
|
|
98
|
-
});
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
### Additional properties
|
|
102
|
-
|
|
103
|
-
Defaults (device settings):
|
|
104
|
-
```javascript
|
|
105
|
-
notification.defaults = Ti.Android.DEFAULT_ALL; // or DEFAULT_SOUND, DEFAULT_VIBRATE, DEFAULT_LIGHTS
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
Flags (behavior):
|
|
109
|
-
```javascript
|
|
110
|
-
notification.flags |= Ti.Android.FLAG_AUTO_CANCEL; // Clear on tap
|
|
111
|
-
notification.flags |= Ti.Android.FLAG_INSISTENT; // Repeat sound until canceled
|
|
112
|
-
notification.flags |= Ti.Android.FLAG_NO_CLEAR; // Do not clear with "clear all"
|
|
113
|
-
notification.flags |= Ti.Android.FLAG_ONGOING_EVENT; // Ongoing event (for example, music player)
|
|
114
|
-
notification.flags |= Ti.Android.FLAG_ONLY_ALERT_ONCE; // Do not replay sound/vibrate
|
|
115
|
-
notification.flags |= Ti.Android.FLAG_SHOW_LIGHTS; // Use LED (if device allows)
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
Priority (Android 4.1+):
|
|
119
|
-
```javascript
|
|
120
|
-
notification.priority = Ti.Android.PRIORITY_HIGH; // or PRIORITY_MAX, PRIORITY_DEFAULT, PRIORITY_LOW, PRIORITY_MIN
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
Category (Android 5.0+):
|
|
124
|
-
```javascript
|
|
125
|
-
notification.category = Ti.Android.CATEGORY_MESSAGE; // or CATEGORY_ALARM, CALL, EMAIL, ERROR, EVENT, etc.
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
Visibility (Android 5.0+ - lock screen):
|
|
129
|
-
```javascript
|
|
130
|
-
notification.visibility = Ti.Android.VISIBILITY_PUBLIC; // Show all
|
|
131
|
-
// or VISIBILITY_PRIVATE (hide content), VISIBILITY_SECRET (do not show)
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
### Update notification
|
|
135
|
-
|
|
136
|
-
```javascript
|
|
137
|
-
notification.setLatestEventInfo('New Title', 'New Message', notification.contentIntent);
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
### Remove notifications
|
|
141
|
-
|
|
142
|
-
```javascript
|
|
143
|
-
// Remove specific notification
|
|
144
|
-
Ti.Android.NotificationManager.cancel(1);
|
|
145
|
-
|
|
146
|
-
// Remove all notifications
|
|
147
|
-
Ti.Android.NotificationManager.cancelAll();
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
### Respond to notification tap
|
|
151
|
-
|
|
152
|
-
Launch app when tapped:
|
|
153
|
-
```javascript
|
|
154
|
-
const intent = Ti.Android.createIntent({
|
|
155
|
-
action: Ti.Android.ACTION_MAIN,
|
|
156
|
-
className: 'com.titaniumsdk.testapp.MyappActivity', // ProjectName + Activity
|
|
157
|
-
packageName: 'com.titaniumsdk.testapp'
|
|
158
|
-
});
|
|
159
|
-
intent.flags |= Ti.Android.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED | Ti.Android.FLAG_ACTIVITY_SINGLE_TOP;
|
|
160
|
-
intent.addCategory(Ti.Android.CATEGORY_LAUNCHER);
|
|
161
|
-
|
|
162
|
-
notification.contentIntent = Ti.Android.createPendingIntent({ intent: intent });
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
### Delete intent (notification cleared)
|
|
166
|
-
|
|
167
|
-
```javascript
|
|
168
|
-
// Execute intent when user clears notification
|
|
169
|
-
notification.deleteIntent = Ti.Android.createPendingIntent({
|
|
170
|
-
intent: Ti.Android.createIntent({
|
|
171
|
-
action: 'com.myapp.NOTIFICATION_CLEARED'
|
|
172
|
-
})
|
|
173
|
-
});
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
### Schedule future notification (background service)
|
|
177
|
-
|
|
178
|
-
`tiapp.xml`:
|
|
179
|
-
```xml
|
|
180
|
-
<android xmlns:android="http://schemas.android.com/apk/res/android">
|
|
181
|
-
<services>
|
|
182
|
-
<service url="ExampleService.js" type="interval" />
|
|
183
|
-
</services>
|
|
184
|
-
</android>
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
`ExampleService.js`:
|
|
188
|
-
```javascript
|
|
189
|
-
const service = Ti.Android.currentService;
|
|
190
|
-
const serviceIntent = service.getIntent();
|
|
191
|
-
const timestamp = new Date(serviceIntent.getStringExtra('timestamp'));
|
|
192
|
-
|
|
193
|
-
if (new Date() > timestamp) {
|
|
194
|
-
const title = serviceIntent.getStringExtra('title');
|
|
195
|
-
const message = serviceIntent.getStringExtra('message');
|
|
196
|
-
|
|
197
|
-
const notification = Ti.Android.createNotification({
|
|
198
|
-
contentTitle: title,
|
|
199
|
-
contentText: message,
|
|
200
|
-
contentIntent: Ti.Android.createPendingIntent({ intent: Ti.Android.createIntent() })
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
Ti.Android.NotificationManager.notify(1, notification);
|
|
204
|
-
Ti.Android.stopService(serviceIntent);
|
|
205
|
-
}
|
|
206
|
-
```
|
|
207
|
-
|
|
208
|
-
Main app:
|
|
209
|
-
```javascript
|
|
210
|
-
const intent = Ti.Android.createServiceIntent({ url: 'ExampleService.js' });
|
|
211
|
-
intent.putExtra('interval', 5000); // Check every 5 seconds
|
|
212
|
-
intent.putExtra('timestamp', new Date(new Date().getTime() + (30 * 1000))); // Fire in 30 seconds
|
|
213
|
-
intent.putExtra('title', 'Scheduled Notification');
|
|
214
|
-
intent.putExtra('message', 'This was scheduled!');
|
|
215
|
-
Ti.Android.startService(intent);
|
|
216
|
-
```
|
|
217
|
-
|
|
218
|
-
## Local notifications: iOS
|
|
219
|
-
|
|
220
|
-
### Notification display locations
|
|
221
|
-
|
|
222
|
-
- Alert dialog: Open or Close buttons (background, unlocked)
|
|
223
|
-
- Banner message: swipe down for actions, tap to launch (background, unlocked)
|
|
224
|
-
- Lock screen: swipe right to launch
|
|
225
|
-
- Notification Center: queued notifications
|
|
226
|
-
- Badge: number on app icon
|
|
227
|
-
- Sound: audio alert
|
|
228
|
-
|
|
229
|
-
### Register for notifications (iOS 8+)
|
|
230
|
-
|
|
231
|
-
```javascript
|
|
232
|
-
if (Ti.Platform.name === 'iPhone OS' && parseInt(Ti.Platform.version.split('.')[0]) >= 8) {
|
|
233
|
-
Ti.App.iOS.registerUserNotificationSettings({
|
|
234
|
-
types: [
|
|
235
|
-
Ti.App.iOS.USER_NOTIFICATION_TYPE_ALERT,
|
|
236
|
-
Ti.App.iOS.USER_NOTIFICATION_TYPE_SOUND,
|
|
237
|
-
Ti.App.iOS.USER_NOTIFICATION_TYPE_BADGE
|
|
238
|
-
]
|
|
239
|
-
// Add 'categories' for interactive notifications
|
|
240
|
-
});
|
|
241
|
-
}
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
Note: iOS shows a system prompt the first time. Check current settings with:
|
|
245
|
-
```javascript
|
|
246
|
-
Ti.App.iOS.addEventListener('usernotificationsettings', (e) => {
|
|
247
|
-
Ti.API.info(`Notification types allowed: ${e.types}`);
|
|
248
|
-
});
|
|
249
|
-
```
|
|
250
|
-
|
|
251
|
-
### Schedule local notification
|
|
252
|
-
|
|
253
|
-
```javascript
|
|
254
|
-
const notification = Ti.App.iOS.scheduleLocalNotification({
|
|
255
|
-
date: new Date(new Date().getTime() + 3000), // 3 seconds from now
|
|
256
|
-
alertBody: 'New content available!',
|
|
257
|
-
alertAction: 'update', // Changes "slide to view" to "slide to update" or "Open" to "Update"
|
|
258
|
-
alertLaunchImage: 'splash.png', // Custom splash image
|
|
259
|
-
badge: 1, // Set badge number (negative to clear)
|
|
260
|
-
sound: '/alert.wav', // Sound file in Resources or app/assets
|
|
261
|
-
category: 'CATEGORY_ID', // For interactive notifications
|
|
262
|
-
repeat: 'daily', // or 'weekly', 'monthly', 'yearly'
|
|
263
|
-
timezone: 'America/Los_Angeles', // Optional (default: system timezone)
|
|
264
|
-
userInfo: { url: 'http://example.com/data.json', id: '123' } // Custom data
|
|
265
|
-
});
|
|
266
|
-
```
|
|
267
|
-
|
|
268
|
-
### Monitor notifications
|
|
269
|
-
|
|
270
|
-
While app is in foreground or returns to foreground:
|
|
271
|
-
```javascript
|
|
272
|
-
Ti.App.iOS.addEventListener('notification', (e) => {
|
|
273
|
-
// Process custom data
|
|
274
|
-
if (e.userInfo && e.userInfo.url) {
|
|
275
|
-
// Handle URL
|
|
19
|
+
```js
|
|
20
|
+
// Install the result listener before requesting permission.
|
|
21
|
+
Ti.App.iOS.addEventListener('usernotificationsettings', onSettings);
|
|
22
|
+
function onSettings(event) {
|
|
23
|
+
Ti.App.iOS.removeEventListener('usernotificationsettings', onSettings);
|
|
24
|
+
if (!event.types || event.types.length === 0) {
|
|
25
|
+
Ti.API.warn('Notification presentation permission denied');
|
|
26
|
+
return;
|
|
276
27
|
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
if (e.badge > 0) {
|
|
280
|
-
Ti.App.iOS.scheduleLocalNotification({
|
|
281
|
-
date: new Date(),
|
|
282
|
-
badge: -1
|
|
283
|
-
});
|
|
284
|
-
}
|
|
285
|
-
});
|
|
286
|
-
```
|
|
287
|
-
|
|
288
|
-
### Cancel notifications
|
|
289
|
-
|
|
290
|
-
```javascript
|
|
291
|
-
// Cancel all
|
|
292
|
-
Ti.App.iOS.cancelAllLocalNotifications();
|
|
293
|
-
|
|
294
|
-
// Cancel specific notification
|
|
295
|
-
const notification = Ti.App.iOS.scheduleLocalNotification({ ... });
|
|
296
|
-
notification.cancel();
|
|
297
|
-
|
|
298
|
-
// Or by ID
|
|
299
|
-
Ti.App.iOS.scheduleLocalNotification({
|
|
300
|
-
userInfo: { id: 'foo' },
|
|
301
|
-
alertBody: 'Test'
|
|
302
|
-
});
|
|
303
|
-
Ti.App.iOS.cancelLocalNotification('foo');
|
|
304
|
-
```
|
|
305
|
-
|
|
306
|
-
## Interactive notifications: iOS (iOS 8+)
|
|
307
|
-
|
|
308
|
-
### Create notification actions
|
|
309
|
-
|
|
310
|
-
```javascript
|
|
311
|
-
const acceptAction = Ti.App.iOS.createUserNotificationAction({
|
|
312
|
-
identifier: 'ACCEPT_IDENTIFIER',
|
|
313
|
-
title: 'Accept',
|
|
314
|
-
activationMode: Ti.App.iOS.USER_NOTIFICATION_ACTIVATION_MODE_FOREGROUND, // or BACKGROUND
|
|
315
|
-
destructive: false,
|
|
316
|
-
authenticationRequired: true // Require device unlock
|
|
317
|
-
});
|
|
318
|
-
|
|
319
|
-
const rejectAction = Ti.App.iOS.createUserNotificationAction({
|
|
320
|
-
identifier: 'REJECT_IDENTIFIER',
|
|
321
|
-
title: 'Reject',
|
|
322
|
-
activationMode: Ti.App.iOS.USER_NOTIFICATION_ACTIVATION_MODE_BACKGROUND,
|
|
323
|
-
destructive: true, // Red background in lock screen/notification center
|
|
324
|
-
authenticationRequired: false
|
|
325
|
-
});
|
|
326
|
-
```
|
|
327
|
-
|
|
328
|
-
### Create notification category
|
|
329
|
-
|
|
330
|
-
```javascript
|
|
331
|
-
const category = Ti.App.iOS.createUserNotificationCategory({
|
|
332
|
-
identifier: 'INVITE_CATEGORY',
|
|
333
|
-
actionsForDefaultContext: [acceptAction, rescheduleAction, delayAction, rejectAction], // Alert dialog (4 max)
|
|
334
|
-
actionsForMinimalContext: [acceptAction, rejectAction] // Other styles (2 max)
|
|
335
|
-
});
|
|
336
|
-
```
|
|
337
|
-
|
|
338
|
-
### Register categories
|
|
339
|
-
|
|
340
|
-
```javascript
|
|
28
|
+
scheduleReminder();
|
|
29
|
+
}
|
|
341
30
|
Ti.App.iOS.registerUserNotificationSettings({
|
|
342
31
|
types: [
|
|
343
32
|
Ti.App.iOS.USER_NOTIFICATION_TYPE_ALERT,
|
|
344
|
-
Ti.App.iOS.
|
|
345
|
-
Ti.App.iOS.
|
|
346
|
-
]
|
|
347
|
-
categories: [category]
|
|
33
|
+
Ti.App.iOS.USER_NOTIFICATION_TYPE_SOUND,
|
|
34
|
+
Ti.App.iOS.USER_NOTIFICATION_TYPE_BADGE
|
|
35
|
+
]
|
|
348
36
|
});
|
|
37
|
+
|
|
38
|
+
function scheduleReminder() {
|
|
39
|
+
Ti.App.iOS.scheduleLocalNotification({
|
|
40
|
+
alertTitle: 'Standup',
|
|
41
|
+
alertBody: 'Daily standup in five minutes',
|
|
42
|
+
date: new Date(Date.now() + 5 * 60 * 1000)
|
|
43
|
+
});
|
|
44
|
+
}
|
|
349
45
|
```
|
|
350
46
|
|
|
351
|
-
|
|
47
|
+
Use `Ti.App.iOS.UserNotificationCenter.getPendingNotifications()` and `removePendingNotifications()` to cancel pending alerts; `removeDeliveredNotifications()` clears delivered entries. The older `cancelLocalNotification`, `cancelAllLocalNotifications` and returned object's `cancel()` are deprecated since SDK 7.3.0. Canceling a delivered entry is different from canceling its future schedule.
|
|
352
48
|
|
|
353
|
-
|
|
354
|
-
```javascript
|
|
355
|
-
Ti.App.iOS.addEventListener('localnotificationaction', (e) => {
|
|
356
|
-
// e.category: category identifier
|
|
357
|
-
// e.identifier: action identifier
|
|
358
|
-
// e.userInfo: custom data
|
|
49
|
+
**Android**
|
|
359
50
|
|
|
360
|
-
|
|
361
|
-
// Handle accept
|
|
362
|
-
if (e.userInfo && e.userInfo.url) {
|
|
363
|
-
// Process URL
|
|
364
|
-
}
|
|
365
|
-
}
|
|
51
|
+
Declare `android.permission.POST_NOTIFICATIONS` in `<android><manifest>` and request Android 13+ permission from an opened window before posting. `Ti.Network.registerForPushNotifications({ success, error })` requests that permission on Android; its success callback does not supply an FCM token. Handle denial and settings restrictions.
|
|
366
52
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
53
|
+
```js
|
|
54
|
+
const channel = Ti.Android.NotificationManager.createNotificationChannel({
|
|
55
|
+
id: 'reminders',
|
|
56
|
+
name: 'Reminders',
|
|
57
|
+
importance: Ti.Android.IMPORTANCE_DEFAULT
|
|
371
58
|
});
|
|
372
|
-
```
|
|
373
59
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
// e.identifier: action identifier
|
|
379
|
-
// e.data: push notification payload (JSON)
|
|
380
|
-
|
|
381
|
-
if (e.category === 'DOWNLOAD_CATEGORY' && e.identifier === 'ACCEPT_IDENTIFIER') {
|
|
382
|
-
if (e.data && e.data.url) {
|
|
383
|
-
// Process URL
|
|
384
|
-
}
|
|
385
|
-
}
|
|
60
|
+
const notification = Ti.Android.createNotification({
|
|
61
|
+
contentTitle: 'Standup',
|
|
62
|
+
contentText: 'Daily standup in five minutes',
|
|
63
|
+
channelId: 'reminders'
|
|
386
64
|
});
|
|
387
|
-
```
|
|
388
65
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
```javascript
|
|
392
|
-
Ti.App.iOS.scheduleLocalNotification({
|
|
393
|
-
date: new Date(new Date().getTime() + 3000),
|
|
394
|
-
alertBody: 'New content available! Download now?',
|
|
395
|
-
badge: 1,
|
|
396
|
-
userInfo: { url: 'http://example.com/data.json' },
|
|
397
|
-
category: 'INVITE_CATEGORY' // Must match registered category identifier
|
|
398
|
-
});
|
|
66
|
+
Ti.Android.NotificationManager.notify(1, notification);
|
|
399
67
|
```
|
|
400
68
|
|
|
401
|
-
|
|
69
|
+
The integer passed to `notify` is the notification's id: notify again with the
|
|
70
|
+
same one and it replaces rather than stacks.
|
|
402
71
|
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
```
|
|
72
|
+
> [!IMPORTANT]
|
|
73
|
+
> **Android needs a channel.** Since Android 8 a notification without a valid
|
|
74
|
+
> `channelId` is dropped by the system, with nothing in the log to say so. Create
|
|
75
|
+
> the channel once at startup - creating it again with the same id is a no-op.
|
|
76
|
+
>
|
|
77
|
+
> Channel importance is not freely reconfigured after creation. The user owns it
|
|
78
|
+
> from then on, and a release that wants a channel to behave differently needs a
|
|
79
|
+
> new id.
|
|
412
80
|
|
|
413
81
|
## Push notifications
|
|
414
82
|
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
Push notifications allow server-to-device communication. Infrastructure differs by platform:
|
|
418
|
-
|
|
419
|
-
- Android: Firebase Cloud Messaging (FCM)
|
|
420
|
-
- iOS: Apple Push Notification service (APNs)
|
|
421
|
-
|
|
422
|
-
Note: the legacy Appcelerator Cloud Services (ACS/ArrowDB) push backend is no longer available. Use FCM for Android and APNs for iOS, either directly or via a third-party service.
|
|
423
|
-
|
|
424
|
-
Google has deprecated Google Cloud Messaging (GCM) in favor of FCM. All new implementations should use FCM.
|
|
425
|
-
|
|
426
|
-
For FCM on Titanium, use the community module `titanium-firebase-cloud-messaging` (by Hans Knöchel).
|
|
83
|
+
APNs and FCM registration are platform-specific. Send the resulting platform token to your backend, which sends the notifications. The following core API example is for **iOS APNs**, after the `usernotificationsettings` callback; it is not an Android FCM registration recipe.
|
|
427
84
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
```javascript
|
|
85
|
+
```js
|
|
431
86
|
Ti.Network.registerForPushNotifications({
|
|
432
|
-
success
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
// Send this token to your server
|
|
87
|
+
success(e) {
|
|
88
|
+
Ti.API.info(`device token ${e.deviceToken}`);
|
|
89
|
+
// POST it to your own server here.
|
|
436
90
|
},
|
|
437
|
-
error
|
|
438
|
-
Ti.API.error(`
|
|
91
|
+
error(e) {
|
|
92
|
+
Ti.API.error(`push registration failed: ${e.error}`);
|
|
439
93
|
},
|
|
440
|
-
callback
|
|
441
|
-
//
|
|
442
|
-
|
|
443
|
-
// e.data: JSON object (Android)
|
|
444
|
-
Ti.API.info(`Push received: ${JSON.stringify(e.data)}`);
|
|
94
|
+
callback(e) {
|
|
95
|
+
// A notification arrived. `e.data` is the payload.
|
|
96
|
+
Ti.API.info(e.data);
|
|
445
97
|
}
|
|
446
98
|
});
|
|
447
99
|
```
|
|
448
100
|
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
3. Export the certificate as PKCS#12 (.p12) from Keychain Access.
|
|
454
|
-
4. Upload the .p12 to your push service provider.
|
|
455
|
-
5. In your app, call `Ti.Network.registerForPushNotifications()` to get the device token.
|
|
456
|
-
|
|
457
|
-
### FCM configuration (Android)
|
|
458
|
-
|
|
459
|
-
1. Create a project in Firebase Console.
|
|
460
|
-
2. Add your Android app (use your app's package name).
|
|
461
|
-
3. Download `google-services.json` and place it in the project root.
|
|
462
|
-
4. Add `titanium-firebase-cloud-messaging` to `tiapp.xml`.
|
|
463
|
-
5. Register for push and obtain the FCM token.
|
|
464
|
-
|
|
465
|
-
### Push notification payload format
|
|
466
|
-
|
|
467
|
-
iOS:
|
|
468
|
-
```json
|
|
469
|
-
{
|
|
470
|
-
"aps": {
|
|
471
|
-
"alert": "Message text",
|
|
472
|
-
"badge": 1,
|
|
473
|
-
"sound": "default"
|
|
474
|
-
},
|
|
475
|
-
"custom_field": "custom_value"
|
|
476
|
-
}
|
|
477
|
-
```
|
|
478
|
-
|
|
479
|
-
Android:
|
|
480
|
-
```json
|
|
481
|
-
{
|
|
482
|
-
"message": "Message text",
|
|
483
|
-
"title": "Notification Title",
|
|
484
|
-
"custom_field": "custom_value"
|
|
485
|
-
}
|
|
486
|
-
```
|
|
487
|
-
|
|
488
|
-
### Send push notifications
|
|
489
|
-
|
|
490
|
-
Send push notifications from your server using the FCM HTTP API or APNs. Collect device tokens from client registration and target them with your push payload.
|
|
491
|
-
|
|
492
|
-
### Best practices
|
|
493
|
-
|
|
494
|
-
1. Permissions: iOS requires user permission (prompted on first call to `registerForPushNotifications`).
|
|
495
|
-
2. Device tokens: cache and send to your server; they can change on app updates.
|
|
496
|
-
3. Background handling: use proper activation modes for interactive notifications.
|
|
497
|
-
4. Badge management: reset the badge after processing a notification.
|
|
498
|
-
5. Error handling: handle registration failures gracefully.
|
|
499
|
-
6. Testing: use development certificates/sandbox for iOS testing.
|
|
500
|
-
|
|
501
|
-
## Common patterns
|
|
502
|
-
|
|
503
|
-
### Check notification permissions
|
|
504
|
-
|
|
505
|
-
```javascript
|
|
506
|
-
// Android 4.1+ can disable notifications per app
|
|
507
|
-
// Check via Settings or handle gracefully (notification may not be shown)
|
|
508
|
-
```
|
|
509
|
-
|
|
510
|
-
### PendingIntent for notification actions
|
|
511
|
-
|
|
512
|
-
```javascript
|
|
513
|
-
const intent = Ti.Android.createIntent({
|
|
514
|
-
action: 'com.example.MY_ACTION'
|
|
515
|
-
// Add data/flags
|
|
516
|
-
});
|
|
517
|
-
const pendingIntent = Ti.Android.createPendingIntent({
|
|
518
|
-
intent: intent,
|
|
519
|
-
flags: Ti.Android.FLAG_UPDATE_CURRENT
|
|
520
|
-
});
|
|
521
|
-
```
|
|
522
|
-
|
|
523
|
-
### Notification groups (Android 7.0+)
|
|
101
|
+
`callback` fires when a notification is received **and** when one is tapped, so
|
|
102
|
+
if opening a screen is the response, check whether the app was in the
|
|
103
|
+
foreground first - otherwise a notification arriving while someone is using the
|
|
104
|
+
app navigates them away from what they were doing.
|
|
524
105
|
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
```
|
|
106
|
+
On iOS, ask for notification permission with
|
|
107
|
+
`registerUserNotificationSettings` before registering for push. On Android, use a native FCM integration and its own token/message APIs. The guide mentions `google-services.json` and `ti.playservices`, but those alone do not implement FCM registration. Follow the selected integration's current installation instructions, including dependencies and configuration-file placement. Core `callback` / `success.deviceToken` are Apple-only.
|
|
528
108
|
|
|
529
|
-
|
|
109
|
+
The token changes. It changes on reinstall, on restore to a new device, and at
|
|
110
|
+
the platform's discretion. Send it on every launch rather than once.
|
|
530
111
|
|
|
531
|
-
|
|
112
|
+
## Testing
|
|
532
113
|
|
|
533
|
-
|
|
114
|
+
Use a signed device build to validate APNs end to end; simulator capabilities depend on the selected OS/Xcode environment. An Android emulator needs the required Play Services/FCM integration. Local notification testing does not prove remote delivery.
|
|
534
115
|
|
|
535
|
-
|
|
536
|
-
// iOS: include "content-available": 1 in payload
|
|
537
|
-
// Android: use data messages (FCM)
|
|
538
|
-
```
|
|
116
|
+
The most common "push is broken" causes, in the order worth checking:
|
|
539
117
|
|
|
540
|
-
|
|
118
|
+
1. **The wrong environment.** A development APNs token is not valid against the
|
|
119
|
+
production gateway, and the failure is silent.
|
|
120
|
+
2. **No permission.** Handle permission denial and explain the relevant system settings; do not assume another request will always show a dialog.
|
|
121
|
+
3. **Missing channel on Android.** See above.
|
|
122
|
+
4. **The token is stale.** See above.
|
|
541
123
|
|
|
542
|
-
|
|
124
|
+

|
|
543
125
|
|
|
544
|
-
|
|
545
|
-
- Notification channels: Android 8.0+ requires notification channels.
|
|
126
|
+
## Next
|
|
546
127
|
|
|
547
|
-
|
|
128
|
+
[Using modules](https://titaniumsdk.com/docs/build/modules.md) covers adding capabilities the core SDK
|
|
129
|
+
does not have, which is what `ti.playservices` above is.
|
|
548
130
|
|
|
549
|
-
|
|
550
|
-
- Background app refresh: may be needed for certain push scenarios.
|
|
551
|
-
- LiveView: callbacks only work properly when LiveView is enabled.
|
|
131
|
+
Contract corrections: [released Network API](https://github.com/tidev/titanium-sdk/blob/13_4_1_GA/apidoc/Titanium/Network/Network.yml), [UserNotificationCenter](https://github.com/tidev/titanium-sdk/blob/13_4_1_GA/apidoc/Titanium/App/iOS/UserNotificationCenter.yml), [NotificationManager](https://github.com/tidev/titanium-sdk/blob/13_4_1_GA/apidoc/Titanium/Android/NotificationManager/NotificationManager.yml).
|