@maccesar/titools 2.10.1 → 3.1.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/AGENTS-VERCEL-RESEARCH.md +23 -12
- package/README.md +81 -182
- package/agents/ti-pro.md +8 -14
- package/lib/cleanup.js +13 -0
- package/lib/commands/skills.js +2 -1
- package/lib/config.js +9 -11
- package/lib/platform.js +1 -1
- package/package.json +2 -3
- package/skills/alloy-guides/SKILL.md +0 -190
- package/skills/alloy-guides/references/CLI_TASKS.md +0 -233
- package/skills/alloy-guides/references/CONCEPTS.md +0 -171
- package/skills/alloy-guides/references/CONTROLLERS.md +0 -279
- package/skills/alloy-guides/references/MODELS.md +0 -1214
- package/skills/alloy-guides/references/PURGETSS.md +0 -46
- package/skills/alloy-guides/references/VIEWS_DYNAMIC.md +0 -235
- package/skills/alloy-guides/references/VIEWS_STYLES.md +0 -375
- package/skills/alloy-guides/references/VIEWS_WITHOUT_CONTROLLERS.md +0 -102
- package/skills/alloy-guides/references/VIEWS_XML.md +0 -581
- package/skills/alloy-guides/references/WIDGETS.md +0 -160
- package/skills/alloy-howtos/SKILL.md +0 -181
- package/skills/alloy-howtos/references/best_practices.md +0 -121
- package/skills/alloy-howtos/references/cli_reference.md +0 -230
- package/skills/alloy-howtos/references/config_files.md +0 -158
- package/skills/alloy-howtos/references/custom_tags.md +0 -148
- package/skills/alloy-howtos/references/debugging_troubleshooting.md +0 -78
- package/skills/alloy-howtos/references/samples.md +0 -156
- package/skills/ti-api/SKILL.md +0 -109
- package/skills/ti-api/references/api-android.md +0 -675
- package/skills/ti-api/references/api-app-platform.md +0 -636
- package/skills/ti-api/references/api-core.md +0 -764
- package/skills/ti-api/references/api-data-network.md +0 -641
- package/skills/ti-api/references/api-media.md +0 -655
- package/skills/ti-api/references/api-modules-ble-bluetooth.md +0 -657
- package/skills/ti-api/references/api-modules-coremotion-urlsession.md +0 -411
- package/skills/ti-api/references/api-modules-map.md +0 -632
- package/skills/ti-api/references/api-modules-nfc.md +0 -725
- package/skills/ti-api/references/api-modules-social-misc.md +0 -526
- package/skills/ti-api/references/api-services.md +0 -700
- package/skills/ti-api/references/api-ui-android.md +0 -499
- package/skills/ti-api/references/api-ui-extras.md +0 -702
- package/skills/ti-api/references/api-ui-ios-animator.md +0 -378
- package/skills/ti-api/references/api-ui-ios.md +0 -756
- package/skills/ti-api/references/api-ui-lists.md +0 -581
- package/skills/ti-api/references/api-ui-text-input.md +0 -607
- package/skills/ti-api/references/api-ui-views.md +0 -572
- package/skills/ti-api/references/api-ui-windows-navigation.md +0 -676
- package/skills/ti-api/references/api-xml-global.md +0 -743
- package/skills/ti-guides/SKILL.md +0 -75
- package/skills/ti-guides/references/advanced-data-and-images.md +0 -155
- package/skills/ti-guides/references/android-manifest.md +0 -97
- package/skills/ti-guides/references/app-distribution.md +0 -373
- package/skills/ti-guides/references/application-frameworks.md +0 -366
- package/skills/ti-guides/references/cli-reference.md +0 -700
- package/skills/ti-guides/references/coding-best-practices.md +0 -150
- package/skills/ti-guides/references/commonjs-advanced.md +0 -279
- package/skills/ti-guides/references/hello-world.md +0 -99
- package/skills/ti-guides/references/hyperloop-native-access.md +0 -458
- package/skills/ti-guides/references/javascript-primer.md +0 -402
- package/skills/ti-guides/references/reserved-words.md +0 -36
- package/skills/ti-guides/references/resources.md +0 -172
- package/skills/ti-guides/references/style-and-conventions.md +0 -104
- package/skills/ti-guides/references/tiapp-config.md +0 -655
- package/skills/ti-howtos/SKILL.md +0 -143
- package/skills/ti-howtos/references/android-platform-deep-dives.md +0 -609
- 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/cross-platform-development.md +0 -358
- package/skills/ti-howtos/references/debugging-profiling.md +0 -473
- package/skills/ti-howtos/references/extending-titanium.md +0 -684
- 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 -595
- package/skills/ti-howtos/references/local-data-sources.md +0 -310
- package/skills/ti-howtos/references/location-and-maps.md +0 -267
- package/skills/ti-howtos/references/media-apis.md +0 -268
- package/skills/ti-howtos/references/notification-services.md +0 -539
- package/skills/ti-howtos/references/remote-data-sources.md +0 -339
- package/skills/ti-howtos/references/tutorials.md +0 -552
- package/skills/ti-howtos/references/using-modules.md +0 -182
- package/skills/ti-howtos/references/web-content-integration.md +0 -288
- package/skills/ti-howtos/references/webpack-build-pipeline.md +0 -125
|
@@ -1,539 +0,0 @@
|
|
|
1
|
-
# Push and local notifications
|
|
2
|
-
|
|
3
|
-
Guide to local and push notifications on iOS and Android.
|
|
4
|
-
|
|
5
|
-
## Local notifications: Android
|
|
6
|
-
|
|
7
|
-
### Notification display locations
|
|
8
|
-
|
|
9
|
-
- Notification drawer: swipe down from status bar
|
|
10
|
-
- Lock screen: Android 5.0+ (API 21+)
|
|
11
|
-
- Status bar: icon and ticker text
|
|
12
|
-
|
|
13
|
-
### Create a notification
|
|
14
|
-
|
|
15
|
-
Basic layout:
|
|
16
|
-
```javascript
|
|
17
|
-
const notification = Ti.Android.createNotification({
|
|
18
|
-
contentTitle: 'Notification Title',
|
|
19
|
-
contentText: 'Notification message',
|
|
20
|
-
contentIntent: Ti.Android.createPendingIntent({
|
|
21
|
-
intent: Ti.Android.createIntent({})
|
|
22
|
-
}),
|
|
23
|
-
icon: Ti.App.Android.R.drawable.warn, // or '/images/warn.png'
|
|
24
|
-
number: 5, // Badge number
|
|
25
|
-
when: new Date(), // Timestamp (does not schedule)
|
|
26
|
-
tickerText: 'Text in status bar when notification first appears'
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
// Send immediately
|
|
30
|
-
Ti.Android.NotificationManager.notify(1, notification);
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
Icon paths:
|
|
34
|
-
- Density-specific: `/app/assets/android/images/` (Alloy) or `/Resources/android/images/` (Classic), reference as `/images/filename.png`
|
|
35
|
-
- Drawable folder: `/platform/android/res/drawable/filename.png`, reference as `Ti.App.Android.R.drawable.filename`
|
|
36
|
-
|
|
37
|
-
### Sound
|
|
38
|
-
|
|
39
|
-
```javascript
|
|
40
|
-
const notification = Ti.Android.createNotification({
|
|
41
|
-
// ... other properties
|
|
42
|
-
sound: `${Ti.Filesystem.getResRawDirectory()}sound.wav`
|
|
43
|
-
});
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
Play only once (add flag):
|
|
47
|
-
```javascript
|
|
48
|
-
notification.flags |= Ti.Android.FLAG_ONLY_ALERT_ONCE;
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
### Custom layout with RemoteViews
|
|
52
|
-
|
|
53
|
-
1. Create XML layout (`/platform/android/res/layout/customview.xml`):
|
|
54
|
-
```xml
|
|
55
|
-
<?xml version="1.0" encoding="utf-8"?>
|
|
56
|
-
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
57
|
-
android:layout_width="match_parent"
|
|
58
|
-
android:layout_height="match_parent"
|
|
59
|
-
android:orientation="horizontal">
|
|
60
|
-
<TextView android:id="@+id/message"
|
|
61
|
-
android:layout_width="wrap_content"
|
|
62
|
-
android:layout_height="wrap_content"
|
|
63
|
-
android:text="Default text" />
|
|
64
|
-
<Button android:id="@+id/okbutton"
|
|
65
|
-
android:layout_width="wrap_content"
|
|
66
|
-
android:layout_height="wrap_content"
|
|
67
|
-
android:text="OK" />
|
|
68
|
-
</LinearLayout>
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
2. Create RemoteViews and bind intents:
|
|
72
|
-
```javascript
|
|
73
|
-
const customView = Ti.Android.createRemoteViews({
|
|
74
|
-
layoutId: Ti.App.Android.R.layout.customview
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
// Modify text
|
|
78
|
-
customView.setTextViewText(Ti.App.Android.R.id.message, 'Update available!');
|
|
79
|
-
|
|
80
|
-
// Bind intents to buttons
|
|
81
|
-
const downloadIntent = Ti.Android.createPendingIntent({ intent: Ti.Android.createIntent() });
|
|
82
|
-
customView.setOnClickPendingIntent(Ti.App.Android.R.id.okbutton, downloadIntent);
|
|
83
|
-
|
|
84
|
-
const notification = Ti.Android.createNotification({
|
|
85
|
-
contentView: customView // Avoid setting contentTitle/contentText or they override this
|
|
86
|
-
});
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
### Additional properties
|
|
90
|
-
|
|
91
|
-
Defaults (device settings):
|
|
92
|
-
```javascript
|
|
93
|
-
notification.defaults = Ti.Android.DEFAULT_ALL; // or DEFAULT_SOUND, DEFAULT_VIBRATE, DEFAULT_LIGHTS
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
Flags (behavior):
|
|
97
|
-
```javascript
|
|
98
|
-
notification.flags |= Ti.Android.FLAG_AUTO_CANCEL; // Clear on tap
|
|
99
|
-
notification.flags |= Ti.Android.FLAG_INSISTENT; // Repeat sound until canceled
|
|
100
|
-
notification.flags |= Ti.Android.FLAG_NO_CLEAR; // Do not clear with "clear all"
|
|
101
|
-
notification.flags |= Ti.Android.FLAG_ONGOING_EVENT; // Ongoing event (for example, music player)
|
|
102
|
-
notification.flags |= Ti.Android.FLAG_ONLY_ALERT_ONCE; // Do not replay sound/vibrate
|
|
103
|
-
notification.flags |= Ti.Android.FLAG_SHOW_LIGHTS; // Use LED (if device allows)
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
Priority (Android 4.1+):
|
|
107
|
-
```javascript
|
|
108
|
-
notification.priority = Ti.Android.PRIORITY_HIGH; // or PRIORITY_MAX, PRIORITY_DEFAULT, PRIORITY_LOW, PRIORITY_MIN
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
Category (Android 5.0+):
|
|
112
|
-
```javascript
|
|
113
|
-
notification.category = Ti.Android.CATEGORY_MESSAGE; // or CATEGORY_ALARM, CALL, EMAIL, ERROR, EVENT, etc.
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
Visibility (Android 5.0+ - lock screen):
|
|
117
|
-
```javascript
|
|
118
|
-
notification.visibility = Ti.Android.VISIBILITY_PUBLIC; // Show all
|
|
119
|
-
// or VISIBILITY_PRIVATE (hide content), VISIBILITY_SECRET (do not show)
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
### Update notification
|
|
123
|
-
|
|
124
|
-
```javascript
|
|
125
|
-
notification.setLatestEventInfo('New Title', 'New Message', notification.contentIntent);
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
### Remove notifications
|
|
129
|
-
|
|
130
|
-
```javascript
|
|
131
|
-
// Remove specific notification
|
|
132
|
-
Ti.Android.NotificationManager.cancel(1);
|
|
133
|
-
|
|
134
|
-
// Remove all notifications
|
|
135
|
-
Ti.Android.NotificationManager.cancelAll();
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
### Respond to notification tap
|
|
139
|
-
|
|
140
|
-
Launch app when tapped:
|
|
141
|
-
```javascript
|
|
142
|
-
const intent = Ti.Android.createIntent({
|
|
143
|
-
action: Ti.Android.ACTION_MAIN,
|
|
144
|
-
className: 'com.titaniumsdk.testapp.MyappActivity', // ProjectName + Activity
|
|
145
|
-
packageName: 'com.titaniumsdk.testapp'
|
|
146
|
-
});
|
|
147
|
-
intent.flags |= Ti.Android.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED | Ti.Android.FLAG_ACTIVITY_SINGLE_TOP;
|
|
148
|
-
intent.addCategory(Ti.Android.CATEGORY_LAUNCHER);
|
|
149
|
-
|
|
150
|
-
notification.contentIntent = Ti.Android.createPendingIntent({ intent: intent });
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
### Delete intent (notification cleared)
|
|
154
|
-
|
|
155
|
-
```javascript
|
|
156
|
-
// Execute intent when user clears notification
|
|
157
|
-
notification.deleteIntent = Ti.Android.createPendingIntent({
|
|
158
|
-
intent: Ti.Android.createIntent({
|
|
159
|
-
action: 'com.myapp.NOTIFICATION_CLEARED'
|
|
160
|
-
})
|
|
161
|
-
});
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
### Schedule future notification (background service)
|
|
165
|
-
|
|
166
|
-
`tiapp.xml`:
|
|
167
|
-
```xml
|
|
168
|
-
<android xmlns:android="http://schemas.android.com/apk/res/android">
|
|
169
|
-
<services>
|
|
170
|
-
<service url="ExampleService.js" type="interval" />
|
|
171
|
-
</services>
|
|
172
|
-
</android>
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
`ExampleService.js`:
|
|
176
|
-
```javascript
|
|
177
|
-
const service = Ti.Android.currentService;
|
|
178
|
-
const serviceIntent = service.getIntent();
|
|
179
|
-
const timestamp = new Date(serviceIntent.getStringExtra('timestamp'));
|
|
180
|
-
|
|
181
|
-
if (new Date() > timestamp) {
|
|
182
|
-
const title = serviceIntent.getStringExtra('title');
|
|
183
|
-
const message = serviceIntent.getStringExtra('message');
|
|
184
|
-
|
|
185
|
-
const notification = Ti.Android.createNotification({
|
|
186
|
-
contentTitle: title,
|
|
187
|
-
contentText: message,
|
|
188
|
-
contentIntent: Ti.Android.createPendingIntent({ intent: Ti.Android.createIntent() })
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
Ti.Android.NotificationManager.notify(1, notification);
|
|
192
|
-
Ti.Android.stopService(serviceIntent);
|
|
193
|
-
}
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
Main app:
|
|
197
|
-
```javascript
|
|
198
|
-
const intent = Ti.Android.createServiceIntent({ url: 'ExampleService.js' });
|
|
199
|
-
intent.putExtra('interval', 5000); // Check every 5 seconds
|
|
200
|
-
intent.putExtra('timestamp', new Date(new Date().getTime() + (30 * 1000))); // Fire in 30 seconds
|
|
201
|
-
intent.putExtra('title', 'Scheduled Notification');
|
|
202
|
-
intent.putExtra('message', 'This was scheduled!');
|
|
203
|
-
Ti.Android.startService(intent);
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
## Local notifications: iOS
|
|
207
|
-
|
|
208
|
-
### Notification display locations
|
|
209
|
-
|
|
210
|
-
- Alert dialog: Open or Close buttons (background, unlocked)
|
|
211
|
-
- Banner message: swipe down for actions, tap to launch (background, unlocked)
|
|
212
|
-
- Lock screen: swipe right to launch
|
|
213
|
-
- Notification Center: queued notifications
|
|
214
|
-
- Badge: number on app icon
|
|
215
|
-
- Sound: audio alert
|
|
216
|
-
|
|
217
|
-
### Register for notifications (iOS 8+)
|
|
218
|
-
|
|
219
|
-
```javascript
|
|
220
|
-
if (Ti.Platform.name === 'iPhone OS' && parseInt(Ti.Platform.version.split('.')[0]) >= 8) {
|
|
221
|
-
Ti.App.iOS.registerUserNotificationSettings({
|
|
222
|
-
types: [
|
|
223
|
-
Ti.App.iOS.USER_NOTIFICATION_TYPE_ALERT,
|
|
224
|
-
Ti.App.iOS.USER_NOTIFICATION_TYPE_SOUND,
|
|
225
|
-
Ti.App.iOS.USER_NOTIFICATION_TYPE_BADGE
|
|
226
|
-
]
|
|
227
|
-
// Add 'categories' for interactive notifications
|
|
228
|
-
});
|
|
229
|
-
}
|
|
230
|
-
```
|
|
231
|
-
|
|
232
|
-
Note: iOS shows a system prompt the first time. Check current settings with:
|
|
233
|
-
```javascript
|
|
234
|
-
Ti.App.iOS.addEventListener('usernotificationsettings', (e) => {
|
|
235
|
-
Ti.API.info(`Notification types allowed: ${e.types}`);
|
|
236
|
-
});
|
|
237
|
-
```
|
|
238
|
-
|
|
239
|
-
### Schedule local notification
|
|
240
|
-
|
|
241
|
-
```javascript
|
|
242
|
-
const notification = Ti.App.iOS.scheduleLocalNotification({
|
|
243
|
-
date: new Date(new Date().getTime() + 3000), // 3 seconds from now
|
|
244
|
-
alertBody: 'New content available!',
|
|
245
|
-
alertAction: 'update', // Changes "slide to view" to "slide to update" or "Open" to "Update"
|
|
246
|
-
alertLaunchImage: 'splash.png', // Custom splash image
|
|
247
|
-
badge: 1, // Set badge number (negative to clear)
|
|
248
|
-
sound: '/alert.wav', // Sound file in Resources or app/assets
|
|
249
|
-
category: 'CATEGORY_ID', // For interactive notifications
|
|
250
|
-
repeat: 'daily', // or 'weekly', 'monthly', 'yearly'
|
|
251
|
-
timezone: 'America/Los_Angeles', // Optional (default: system timezone)
|
|
252
|
-
userInfo: { url: 'http://example.com/data.json', id: '123' } // Custom data
|
|
253
|
-
});
|
|
254
|
-
```
|
|
255
|
-
|
|
256
|
-
### Monitor notifications
|
|
257
|
-
|
|
258
|
-
While app is in foreground or returns to foreground:
|
|
259
|
-
```javascript
|
|
260
|
-
Ti.App.iOS.addEventListener('notification', (e) => {
|
|
261
|
-
// Process custom data
|
|
262
|
-
if (e.userInfo && e.userInfo.url) {
|
|
263
|
-
// Handle URL
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
// Reset badge
|
|
267
|
-
if (e.badge > 0) {
|
|
268
|
-
Ti.App.iOS.scheduleLocalNotification({
|
|
269
|
-
date: new Date(),
|
|
270
|
-
badge: -1
|
|
271
|
-
});
|
|
272
|
-
}
|
|
273
|
-
});
|
|
274
|
-
```
|
|
275
|
-
|
|
276
|
-
### Cancel notifications
|
|
277
|
-
|
|
278
|
-
```javascript
|
|
279
|
-
// Cancel all
|
|
280
|
-
Ti.App.iOS.cancelAllLocalNotifications();
|
|
281
|
-
|
|
282
|
-
// Cancel specific notification
|
|
283
|
-
const notification = Ti.App.iOS.scheduleLocalNotification({ ... });
|
|
284
|
-
notification.cancel();
|
|
285
|
-
|
|
286
|
-
// Or by ID
|
|
287
|
-
Ti.App.iOS.scheduleLocalNotification({
|
|
288
|
-
userInfo: { id: 'foo' },
|
|
289
|
-
alertBody: 'Test'
|
|
290
|
-
});
|
|
291
|
-
Ti.App.iOS.cancelLocalNotification('foo');
|
|
292
|
-
```
|
|
293
|
-
|
|
294
|
-
## Interactive notifications: iOS (iOS 8+)
|
|
295
|
-
|
|
296
|
-
### Create notification actions
|
|
297
|
-
|
|
298
|
-
```javascript
|
|
299
|
-
const acceptAction = Ti.App.iOS.createUserNotificationAction({
|
|
300
|
-
identifier: 'ACCEPT_IDENTIFIER',
|
|
301
|
-
title: 'Accept',
|
|
302
|
-
activationMode: Ti.App.iOS.USER_NOTIFICATION_ACTIVATION_MODE_FOREGROUND, // or BACKGROUND
|
|
303
|
-
destructive: false,
|
|
304
|
-
authenticationRequired: true // Require device unlock
|
|
305
|
-
});
|
|
306
|
-
|
|
307
|
-
const rejectAction = Ti.App.iOS.createUserNotificationAction({
|
|
308
|
-
identifier: 'REJECT_IDENTIFIER',
|
|
309
|
-
title: 'Reject',
|
|
310
|
-
activationMode: Ti.App.iOS.USER_NOTIFICATION_ACTIVATION_MODE_BACKGROUND,
|
|
311
|
-
destructive: true, // Red background in lock screen/notification center
|
|
312
|
-
authenticationRequired: false
|
|
313
|
-
});
|
|
314
|
-
```
|
|
315
|
-
|
|
316
|
-
### Create notification category
|
|
317
|
-
|
|
318
|
-
```javascript
|
|
319
|
-
const category = Ti.App.iOS.createUserNotificationCategory({
|
|
320
|
-
identifier: 'INVITE_CATEGORY',
|
|
321
|
-
actionsForDefaultContext: [acceptAction, rescheduleAction, delayAction, rejectAction], // Alert dialog (4 max)
|
|
322
|
-
actionsForMinimalContext: [acceptAction, rejectAction] // Other styles (2 max)
|
|
323
|
-
});
|
|
324
|
-
```
|
|
325
|
-
|
|
326
|
-
### Register categories
|
|
327
|
-
|
|
328
|
-
```javascript
|
|
329
|
-
Ti.App.iOS.registerUserNotificationSettings({
|
|
330
|
-
types: [
|
|
331
|
-
Ti.App.iOS.USER_NOTIFICATION_TYPE_ALERT,
|
|
332
|
-
Ti.App.iOS.USER_NOTIFICATION_TYPE_BADGE,
|
|
333
|
-
Ti.App.iOS.USER_NOTIFICATION_TYPE_SOUND
|
|
334
|
-
],
|
|
335
|
-
categories: [category]
|
|
336
|
-
});
|
|
337
|
-
```
|
|
338
|
-
|
|
339
|
-
### Monitor interactive notifications
|
|
340
|
-
|
|
341
|
-
Local notifications:
|
|
342
|
-
```javascript
|
|
343
|
-
Ti.App.iOS.addEventListener('localnotificationaction', (e) => {
|
|
344
|
-
// e.category: category identifier
|
|
345
|
-
// e.identifier: action identifier
|
|
346
|
-
// e.userInfo: custom data
|
|
347
|
-
|
|
348
|
-
if (e.category === 'INVITE_CATEGORY' && e.identifier === 'ACCEPT_IDENTIFIER') {
|
|
349
|
-
// Handle accept
|
|
350
|
-
if (e.userInfo && e.userInfo.url) {
|
|
351
|
-
// Process URL
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
// Reset badge
|
|
356
|
-
if (e.badge > 0) {
|
|
357
|
-
Ti.App.iOS.scheduleLocalNotification({ date: new Date(), badge: -1 });
|
|
358
|
-
}
|
|
359
|
-
});
|
|
360
|
-
```
|
|
361
|
-
|
|
362
|
-
Push notifications:
|
|
363
|
-
```javascript
|
|
364
|
-
Ti.App.iOS.addEventListener('remotenotificationaction', (e) => {
|
|
365
|
-
// e.category: category identifier
|
|
366
|
-
// e.identifier: action identifier
|
|
367
|
-
// e.data: push notification payload (JSON)
|
|
368
|
-
|
|
369
|
-
if (e.category === 'DOWNLOAD_CATEGORY' && e.identifier === 'ACCEPT_IDENTIFIER') {
|
|
370
|
-
if (e.data && e.data.url) {
|
|
371
|
-
// Process URL
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
});
|
|
375
|
-
```
|
|
376
|
-
|
|
377
|
-
### Schedule interactive local notification
|
|
378
|
-
|
|
379
|
-
```javascript
|
|
380
|
-
Ti.App.iOS.scheduleLocalNotification({
|
|
381
|
-
date: new Date(new Date().getTime() + 3000),
|
|
382
|
-
alertBody: 'New content available! Download now?',
|
|
383
|
-
badge: 1,
|
|
384
|
-
userInfo: { url: 'http://example.com/data.json' },
|
|
385
|
-
category: 'INVITE_CATEGORY' // Must match registered category identifier
|
|
386
|
-
});
|
|
387
|
-
```
|
|
388
|
-
|
|
389
|
-
### Send interactive push notification
|
|
390
|
-
|
|
391
|
-
Payload format:
|
|
392
|
-
```json
|
|
393
|
-
{
|
|
394
|
-
"alert": "New content available! Download now?",
|
|
395
|
-
"badge": 1,
|
|
396
|
-
"url": "http://example.com/data.json",
|
|
397
|
-
"category": "INVITE_CATEGORY"
|
|
398
|
-
}
|
|
399
|
-
```
|
|
400
|
-
|
|
401
|
-
## Push notifications
|
|
402
|
-
|
|
403
|
-
### Overview
|
|
404
|
-
|
|
405
|
-
Push notifications allow server-to-device communication. Infrastructure differs by platform:
|
|
406
|
-
|
|
407
|
-
- Android: Firebase Cloud Messaging (FCM)
|
|
408
|
-
- iOS: Apple Push Notification service (APNs)
|
|
409
|
-
|
|
410
|
-
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.
|
|
411
|
-
|
|
412
|
-
Google has deprecated Google Cloud Messaging (GCM) in favor of FCM. All new implementations should use FCM.
|
|
413
|
-
|
|
414
|
-
For FCM on Titanium, use the community module `titanium-firebase-cloud-messaging` (by Hans Knöchel).
|
|
415
|
-
|
|
416
|
-
### Register for push notifications
|
|
417
|
-
|
|
418
|
-
```javascript
|
|
419
|
-
Ti.Network.registerForPushNotifications({
|
|
420
|
-
success: (e) => {
|
|
421
|
-
// e.deviceToken: token to send to your push server
|
|
422
|
-
Ti.API.info(`Device Token: ${e.deviceToken}`);
|
|
423
|
-
// Send this token to your server
|
|
424
|
-
},
|
|
425
|
-
error: (e) => {
|
|
426
|
-
Ti.API.error(`Push registration error: ${e.error}`);
|
|
427
|
-
},
|
|
428
|
-
callback: (e) => {
|
|
429
|
-
// Incoming push notification
|
|
430
|
-
// e.data: push payload (iOS)
|
|
431
|
-
// e.data: JSON object (Android)
|
|
432
|
-
Ti.API.info(`Push received: ${JSON.stringify(e.data)}`);
|
|
433
|
-
}
|
|
434
|
-
});
|
|
435
|
-
```
|
|
436
|
-
|
|
437
|
-
### APNs configuration (iOS)
|
|
438
|
-
|
|
439
|
-
1. Register an explicit App ID (not wildcard) with Push Notifications enabled in Apple Developer Portal.
|
|
440
|
-
2. Generate an APNs certificate (Development for sandbox, Production for App Store).
|
|
441
|
-
3. Export the certificate as PKCS#12 (.p12) from Keychain Access.
|
|
442
|
-
4. Upload the .p12 to your push service provider.
|
|
443
|
-
5. In your app, call `Ti.Network.registerForPushNotifications()` to get the device token.
|
|
444
|
-
|
|
445
|
-
### FCM configuration (Android)
|
|
446
|
-
|
|
447
|
-
1. Create a project in Firebase Console.
|
|
448
|
-
2. Add your Android app (use your app's package name).
|
|
449
|
-
3. Download `google-services.json` and place it in the project root.
|
|
450
|
-
4. Add `titanium-firebase-cloud-messaging` to `tiapp.xml`.
|
|
451
|
-
5. Register for push and obtain the FCM token.
|
|
452
|
-
|
|
453
|
-
### Push notification payload format
|
|
454
|
-
|
|
455
|
-
iOS:
|
|
456
|
-
```json
|
|
457
|
-
{
|
|
458
|
-
"aps": {
|
|
459
|
-
"alert": "Message text",
|
|
460
|
-
"badge": 1,
|
|
461
|
-
"sound": "default"
|
|
462
|
-
},
|
|
463
|
-
"custom_field": "custom_value"
|
|
464
|
-
}
|
|
465
|
-
```
|
|
466
|
-
|
|
467
|
-
Android:
|
|
468
|
-
```json
|
|
469
|
-
{
|
|
470
|
-
"message": "Message text",
|
|
471
|
-
"title": "Notification Title",
|
|
472
|
-
"custom_field": "custom_value"
|
|
473
|
-
}
|
|
474
|
-
```
|
|
475
|
-
|
|
476
|
-
### Send push notifications
|
|
477
|
-
|
|
478
|
-
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.
|
|
479
|
-
|
|
480
|
-
### Best practices
|
|
481
|
-
|
|
482
|
-
1. Permissions: iOS requires user permission (prompted on first call to `registerForPushNotifications`).
|
|
483
|
-
2. Device tokens: cache and send to your server; they can change on app updates.
|
|
484
|
-
3. Background handling: use proper activation modes for interactive notifications.
|
|
485
|
-
4. Badge management: reset the badge after processing a notification.
|
|
486
|
-
5. Error handling: handle registration failures gracefully.
|
|
487
|
-
6. Testing: use development certificates/sandbox for iOS testing.
|
|
488
|
-
|
|
489
|
-
## Common patterns
|
|
490
|
-
|
|
491
|
-
### Check notification permissions
|
|
492
|
-
|
|
493
|
-
```javascript
|
|
494
|
-
// Android 4.1+ can disable notifications per app
|
|
495
|
-
// Check via Settings or handle gracefully (notification may not be shown)
|
|
496
|
-
```
|
|
497
|
-
|
|
498
|
-
### PendingIntent for notification actions
|
|
499
|
-
|
|
500
|
-
```javascript
|
|
501
|
-
const intent = Ti.Android.createIntent({
|
|
502
|
-
action: 'com.example.MY_ACTION'
|
|
503
|
-
// Add data/flags
|
|
504
|
-
});
|
|
505
|
-
const pendingIntent = Ti.Android.createPendingIntent({
|
|
506
|
-
intent: intent,
|
|
507
|
-
flags: Ti.Android.FLAG_UPDATE_CURRENT
|
|
508
|
-
});
|
|
509
|
-
```
|
|
510
|
-
|
|
511
|
-
### Notification groups (Android 7.0+)
|
|
512
|
-
|
|
513
|
-
```javascript
|
|
514
|
-
// Requires API 24+; check documentation for latest implementation
|
|
515
|
-
```
|
|
516
|
-
|
|
517
|
-
### Rich notifications (iOS 10+)
|
|
518
|
-
|
|
519
|
-
Use `UNMutableNotificationContent` with attachments (images, video, audio).
|
|
520
|
-
|
|
521
|
-
### Silent push
|
|
522
|
-
|
|
523
|
-
```javascript
|
|
524
|
-
// iOS: include "content-available": 1 in payload
|
|
525
|
-
// Android: use data messages (FCM)
|
|
526
|
-
```
|
|
527
|
-
|
|
528
|
-
## Platform-specific notes
|
|
529
|
-
|
|
530
|
-
### Android
|
|
531
|
-
|
|
532
|
-
- Service lifecycle: services may be killed if app is swiped away. For critical notifications, use FCM instead of local services.
|
|
533
|
-
- Notification channels: Android 8.0+ requires notification channels.
|
|
534
|
-
|
|
535
|
-
### iOS
|
|
536
|
-
|
|
537
|
-
- Sandbox vs production: use correct certificates and endpoints.
|
|
538
|
-
- Background app refresh: may be needed for certain push scenarios.
|
|
539
|
-
- LiveView: callbacks only work properly when LiveView is enabled.
|