@maccesar/titools 2.0.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-TEMPLATE.md +173 -0
- package/README.md +867 -0
- package/agents/ti-researcher.md +108 -0
- package/bin/titools.js +53 -0
- package/lib/commands/agents.js +126 -0
- package/lib/commands/install.js +188 -0
- package/lib/commands/uninstall.js +215 -0
- package/lib/commands/update.js +159 -0
- package/lib/config.js +119 -0
- package/lib/downloader.js +153 -0
- package/lib/installer.js +253 -0
- package/lib/platform.js +108 -0
- package/lib/symlink.js +142 -0
- package/lib/utils.js +270 -0
- package/package.json +67 -0
- package/skills/alloy-expert/SKILL.md +247 -0
- package/skills/alloy-expert/assets/ControllerAutoCleanup.js +182 -0
- package/skills/alloy-expert/references/alloy-structure.md +381 -0
- package/skills/alloy-expert/references/anti-patterns.md +133 -0
- package/skills/alloy-expert/references/code-conventions.md +469 -0
- package/skills/alloy-expert/references/contracts.md +280 -0
- package/skills/alloy-expert/references/controller-patterns.md +520 -0
- package/skills/alloy-expert/references/error-handling.md +484 -0
- package/skills/alloy-expert/references/examples.md +735 -0
- package/skills/alloy-expert/references/migration-patterns.md +298 -0
- package/skills/alloy-expert/references/patterns.md +448 -0
- package/skills/alloy-expert/references/performance-patterns.md +855 -0
- package/skills/alloy-expert/references/security-patterns.md +847 -0
- package/skills/alloy-expert/references/state-management.md +779 -0
- package/skills/alloy-expert/references/testing.md +872 -0
- package/skills/alloy-guides/SKILL.md +214 -0
- package/skills/alloy-guides/references/CLI_TASKS.md +243 -0
- package/skills/alloy-guides/references/CONCEPTS.md +191 -0
- package/skills/alloy-guides/references/CONTROLLERS.md +298 -0
- package/skills/alloy-guides/references/MODELS.md +1028 -0
- package/skills/alloy-guides/references/PURGETSS.md +56 -0
- package/skills/alloy-guides/references/VIEWS_DYNAMIC.md +242 -0
- package/skills/alloy-guides/references/VIEWS_STYLES.md +388 -0
- package/skills/alloy-guides/references/VIEWS_WITHOUT_CONTROLLERS.md +109 -0
- package/skills/alloy-guides/references/VIEWS_XML.md +558 -0
- package/skills/alloy-guides/references/WIDGETS.md +176 -0
- package/skills/alloy-howtos/SKILL.md +203 -0
- package/skills/alloy-howtos/references/best_practices.md +138 -0
- package/skills/alloy-howtos/references/cli_reference.md +253 -0
- package/skills/alloy-howtos/references/config_files.md +87 -0
- package/skills/alloy-howtos/references/custom_tags.md +147 -0
- package/skills/alloy-howtos/references/debugging_troubleshooting.md +101 -0
- package/skills/alloy-howtos/references/samples.md +167 -0
- package/skills/purgetss/SKILL.md +442 -0
- package/skills/purgetss/assets/purgetss.config.cjs +17 -0
- package/skills/purgetss/references/EXAMPLES.md +247 -0
- package/skills/purgetss/references/animation-system.md +1294 -0
- package/skills/purgetss/references/apply-directive.md +375 -0
- package/skills/purgetss/references/arbitrary-values.md +612 -0
- package/skills/purgetss/references/class-index.md +1350 -0
- package/skills/purgetss/references/cli-commands.md +948 -0
- package/skills/purgetss/references/configurable-properties.md +654 -0
- package/skills/purgetss/references/custom-rules.md +161 -0
- package/skills/purgetss/references/customization-deep-dive.md +722 -0
- package/skills/purgetss/references/dynamic-component-creation.md +489 -0
- package/skills/purgetss/references/grid-layout.md +455 -0
- package/skills/purgetss/references/icon-fonts.md +609 -0
- package/skills/purgetss/references/installation-setup.md +366 -0
- package/skills/purgetss/references/opacity-modifier.md +291 -0
- package/skills/purgetss/references/platform-modifiers.md +479 -0
- package/skills/purgetss/references/smart-mappings.md +42 -0
- package/skills/purgetss/references/titanium-resets.md +359 -0
- package/skills/purgetss/references/ui-ux-design.md +1526 -0
- package/skills/ti-guides/SKILL.md +94 -0
- package/skills/ti-guides/references/advanced-data-and-images.md +19 -0
- package/skills/ti-guides/references/alloy-cli-advanced.md +84 -0
- package/skills/ti-guides/references/alloy-data-mastery.md +29 -0
- package/skills/ti-guides/references/alloy-widgets-and-themes.md +19 -0
- package/skills/ti-guides/references/android-manifest.md +97 -0
- package/skills/ti-guides/references/app-distribution.md +258 -0
- package/skills/ti-guides/references/application-frameworks.md +377 -0
- package/skills/ti-guides/references/cli-reference.md +402 -0
- package/skills/ti-guides/references/coding-best-practices.md +102 -0
- package/skills/ti-guides/references/commonjs-advanced.md +134 -0
- package/skills/ti-guides/references/hello-world.md +100 -0
- package/skills/ti-guides/references/hyperloop-native-access.md +62 -0
- package/skills/ti-guides/references/javascript-primer.md +411 -0
- package/skills/ti-guides/references/reserved-words.md +36 -0
- package/skills/ti-guides/references/resources.md +183 -0
- package/skills/ti-guides/references/style-and-conventions.md +48 -0
- package/skills/ti-guides/references/tiapp-config.md +609 -0
- package/skills/ti-howtos/SKILL.md +174 -0
- package/skills/ti-howtos/references/android-platform-deep-dives.md +658 -0
- package/skills/ti-howtos/references/automation-fastlane-appium.md +95 -0
- package/skills/ti-howtos/references/buffer-codec-streams.md +140 -0
- package/skills/ti-howtos/references/cross-platform-development.md +348 -0
- package/skills/ti-howtos/references/debugging-profiling.md +543 -0
- package/skills/ti-howtos/references/extending-titanium.md +723 -0
- package/skills/ti-howtos/references/google-maps-v2.md +169 -0
- package/skills/ti-howtos/references/ios-map-kit.md +143 -0
- package/skills/ti-howtos/references/ios-platform-deep-dives.md +783 -0
- package/skills/ti-howtos/references/local-data-sources.md +301 -0
- package/skills/ti-howtos/references/location-and-maps.md +252 -0
- package/skills/ti-howtos/references/media-apis.md +210 -0
- package/skills/ti-howtos/references/notification-services.md +599 -0
- package/skills/ti-howtos/references/remote-data-sources.md +349 -0
- package/skills/ti-howtos/references/tutorials.md +502 -0
- package/skills/ti-howtos/references/using-modules.md +237 -0
- package/skills/ti-howtos/references/web-content-integration.md +307 -0
- package/skills/ti-howtos/references/webpack-build-pipeline.md +78 -0
- package/skills/ti-ui/SKILL.md +179 -0
- package/skills/ti-ui/references/accessibility-deep-dive.md +242 -0
- package/skills/ti-ui/references/animation-and-matrices.md +599 -0
- package/skills/ti-ui/references/application-structures.md +655 -0
- package/skills/ti-ui/references/custom-fonts-styling.md +579 -0
- package/skills/ti-ui/references/event-handling.md +393 -0
- package/skills/ti-ui/references/gestures.md +473 -0
- package/skills/ti-ui/references/icons-and-splash-screens.md +409 -0
- package/skills/ti-ui/references/layouts-and-positioning.md +462 -0
- package/skills/ti-ui/references/listviews-and-performance.md +619 -0
- package/skills/ti-ui/references/orientation.md +362 -0
- package/skills/ti-ui/references/platform-ui-android.md +635 -0
- package/skills/ti-ui/references/platform-ui-ios.md +469 -0
- package/skills/ti-ui/references/scrolling-views.md +252 -0
- package/skills/ti-ui/references/tableviews.md +568 -0
|
@@ -0,0 +1,599 @@
|
|
|
1
|
+
# Push and Local Notifications
|
|
2
|
+
|
|
3
|
+
Comprehensive guide for local and push notifications on iOS and Android.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Push and Local Notifications](#push-and-local-notifications)
|
|
8
|
+
- [Table of Contents](#table-of-contents)
|
|
9
|
+
- [Local Notifications Android](#local-notifications-android)
|
|
10
|
+
- [Notification Display Locations](#notification-display-locations)
|
|
11
|
+
- [Create a Notification](#create-a-notification)
|
|
12
|
+
- [Sound](#sound)
|
|
13
|
+
- [Custom Layout with RemoteViews](#custom-layout-with-remoteviews)
|
|
14
|
+
- [Additional Properties](#additional-properties)
|
|
15
|
+
- [Update Notification](#update-notification)
|
|
16
|
+
- [Remove Notifications](#remove-notifications)
|
|
17
|
+
- [Respond to Notification Tap](#respond-to-notification-tap)
|
|
18
|
+
- [Schedule Future Notification (Background Service)](#schedule-future-notification-background-service)
|
|
19
|
+
- [Local Notifications iOS](#local-notifications-ios)
|
|
20
|
+
- [Notification Display Locations](#notification-display-locations-1)
|
|
21
|
+
- [Register for Notifications (iOS 8+)](#register-for-notifications-ios-8)
|
|
22
|
+
- [Schedule Local Notification](#schedule-local-notification)
|
|
23
|
+
- [Monitor Notifications](#monitor-notifications)
|
|
24
|
+
- [Cancel Notifications](#cancel-notifications)
|
|
25
|
+
- [Interactive Notifications iOS (iOS 8+)](#interactive-notifications-ios-ios-8)
|
|
26
|
+
- [Create Notification Actions](#create-notification-actions)
|
|
27
|
+
- [Create Notification Category](#create-notification-category)
|
|
28
|
+
- [Register Categories](#register-categories)
|
|
29
|
+
- [Monitor Interactive Notifications](#monitor-interactive-notifications)
|
|
30
|
+
- [Schedule Interactive Local Notification](#schedule-interactive-local-notification)
|
|
31
|
+
- [Send Interactive Push Notification](#send-interactive-push-notification)
|
|
32
|
+
- [Push Notifications](#push-notifications)
|
|
33
|
+
- [Overview](#overview)
|
|
34
|
+
- [Register for Push Notifications](#register-for-push-notifications)
|
|
35
|
+
- [Configure Push Services](#configure-push-services)
|
|
36
|
+
- [Push Notification Payload Format](#push-notification-payload-format)
|
|
37
|
+
- [Subscribe to Channels](#subscribe-to-channels)
|
|
38
|
+
- [Send Push Notifications](#send-push-notifications)
|
|
39
|
+
- [Best Practices](#best-practices)
|
|
40
|
+
- [Common Patterns](#common-patterns)
|
|
41
|
+
- [Check Notification Permissions](#check-notification-permissions)
|
|
42
|
+
- [PendingIntent for Notification Actions](#pendingintent-for-notification-actions)
|
|
43
|
+
- [Notification Groups (Android 7.0+)](#notification-groups-android-70)
|
|
44
|
+
- [Rich Notifications (iOS 10+)](#rich-notifications-ios-10)
|
|
45
|
+
- [Silent Push](#silent-push)
|
|
46
|
+
- [Platform-Specific Notes](#platform-specific-notes)
|
|
47
|
+
- [Android](#android)
|
|
48
|
+
- [iOS](#ios)
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Local Notifications Android
|
|
53
|
+
|
|
54
|
+
### Notification Display Locations
|
|
55
|
+
|
|
56
|
+
- **Notification Drawer**: Swipe down from status bar
|
|
57
|
+
- **Lock Screen**: Android 5.0+ (API 21+)
|
|
58
|
+
- **Status Bar**: Icon and ticker text
|
|
59
|
+
|
|
60
|
+
### Create a Notification
|
|
61
|
+
|
|
62
|
+
**Basic Layout:**
|
|
63
|
+
```javascript
|
|
64
|
+
const notification = Ti.Android.createNotification({
|
|
65
|
+
contentTitle: 'Notification Title',
|
|
66
|
+
contentText: 'Notification message',
|
|
67
|
+
contentIntent: Ti.Android.createPendingIntent({
|
|
68
|
+
intent: Ti.Android.createIntent({})
|
|
69
|
+
}),
|
|
70
|
+
icon: Ti.App.Android.R.drawable.warn, // or '/images/warn.png'
|
|
71
|
+
number: 5, // Badge number
|
|
72
|
+
when: new Date(), // Timestamp (does NOT schedule)
|
|
73
|
+
tickerText: 'Text in status bar when notification first appears'
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// Send immediately
|
|
77
|
+
Ti.Android.NotificationManager.notify(1, notification);
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**Icon paths:**
|
|
81
|
+
- Density-specific: `/app/assets/android/images/` (Alloy) or `/Resources/android/images/` (Classic), reference as `/images/filename.png`
|
|
82
|
+
- Drawable folder: `/platform/android/res/drawable/filename.png`, reference as `Ti.App.Android.R.drawable.filename`
|
|
83
|
+
|
|
84
|
+
### Sound
|
|
85
|
+
|
|
86
|
+
```javascript
|
|
87
|
+
const notification = Ti.Android.createNotification({
|
|
88
|
+
// ... other properties
|
|
89
|
+
sound: `${Ti.Filesystem.getResRawDirectory()}sound.wav`
|
|
90
|
+
});
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Play only once (add flag):
|
|
94
|
+
```javascript
|
|
95
|
+
notification.flags |= Ti.Android.FLAG_ONLY_ALERT_ONCE;
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Custom Layout with RemoteViews
|
|
99
|
+
|
|
100
|
+
**1. Create XML Layout** (`/platform/android/res/layout/customview.xml`):
|
|
101
|
+
```xml
|
|
102
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
103
|
+
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
104
|
+
android:layout_width="match_parent"
|
|
105
|
+
android:layout_height="match_parent"
|
|
106
|
+
android:orientation="horizontal">
|
|
107
|
+
<TextView android:id="@+id/message"
|
|
108
|
+
android:layout_width="wrap_content"
|
|
109
|
+
android:layout_height="wrap_content"
|
|
110
|
+
android:text="Default text" />
|
|
111
|
+
<Button android:id="@+id/okbutton"
|
|
112
|
+
android:layout_width="wrap_content"
|
|
113
|
+
android:layout_height="wrap_content"
|
|
114
|
+
android:text="OK" />
|
|
115
|
+
</LinearLayout>
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
**2. Create RemoteViews and bind intents:**
|
|
119
|
+
```javascript
|
|
120
|
+
const customView = Ti.Android.createRemoteViews({
|
|
121
|
+
layoutId: Ti.App.Android.R.layout.customview
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
// Modify text
|
|
125
|
+
customView.setTextViewText(Ti.App.Android.R.id.message, "Update available!");
|
|
126
|
+
|
|
127
|
+
// Bind intents to buttons
|
|
128
|
+
const downloadIntent = Ti.Android.createPendingIntent({intent: Ti.Android.createIntent()});
|
|
129
|
+
customView.setOnClickPendingIntent(Ti.App.Android.R.id.okbutton, downloadIntent);
|
|
130
|
+
|
|
131
|
+
const notification = Ti.Android.createNotification({
|
|
132
|
+
contentView: customView // Avoid setting contentTitle/contentText or they override this
|
|
133
|
+
});
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Additional Properties
|
|
137
|
+
|
|
138
|
+
**Defaults (device settings):**
|
|
139
|
+
```javascript
|
|
140
|
+
notification.defaults = Ti.Android.DEFAULT_ALL; // or DEFAULT_SOUND, DEFAULT_VIBRATE, DEFAULT_LIGHTS
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
**Flags (behavior):**
|
|
144
|
+
```javascript
|
|
145
|
+
notification.flags |= Ti.Android.FLAG_AUTO_CANCEL; // Clear on tap
|
|
146
|
+
notification.flags |= Ti.Android.FLAG_INSISTENT; // Repeat sound until canceled
|
|
147
|
+
notification.flags |= Ti.Android.FLAG_NO_CLEAR; // Don't clear with "clear all"
|
|
148
|
+
notification.flags |= Ti.Android.FLAG_ONGOING_EVENT; // Ongoing event (e.g., music player)
|
|
149
|
+
notification.flags |= Ti.Android.FLAG_ONLY_ALERT_ONCE; // Don't replay sound/vibrate
|
|
150
|
+
notification.flags |= Ti.Android.FLAG_SHOW_LIGHTS; // Use LED (if device allows)
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
**Priority** (Android 4.1+):
|
|
154
|
+
```javascript
|
|
155
|
+
notification.priority = Ti.Android.PRIORITY_HIGH; // or PRIORITY_MAX, PRIORITY_DEFAULT, PRIORITY_LOW, PRIORITY_MIN
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
**Category** (Android 5.0+):
|
|
159
|
+
```javascript
|
|
160
|
+
notification.category = Ti.Android.CATEGORY_MESSAGE; // or CATEGORY_ALARM, CALL, EMAIL, ERROR, EVENT, etc.
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
**Visibility** (Android 5.0+ - lock screen):
|
|
164
|
+
```javascript
|
|
165
|
+
notification.visibility = Ti.Android.VISIBILITY_PUBLIC; // Show all
|
|
166
|
+
// or VISIBILITY_PRIVATE (hide content), VISIBILITY_SECRET (don't show)
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Update Notification
|
|
170
|
+
|
|
171
|
+
```javascript
|
|
172
|
+
notification.setLatestEventInfo('New Title', 'New Message', notification.contentIntent);
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Remove Notifications
|
|
176
|
+
|
|
177
|
+
```javascript
|
|
178
|
+
// Remove specific notification
|
|
179
|
+
Ti.Android.NotificationManager.cancel(1);
|
|
180
|
+
|
|
181
|
+
// Remove all notifications
|
|
182
|
+
Ti.Android.NotificationManager.cancelAll();
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Respond to Notification Tap
|
|
186
|
+
|
|
187
|
+
**Launch app when tapped:**
|
|
188
|
+
```javascript
|
|
189
|
+
const intent = Ti.Android.createIntent({
|
|
190
|
+
action: Ti.Android.ACTION_MAIN,
|
|
191
|
+
className: 'com.titaniumsdk.testapp.MyappActivity', // ProjectName + Activity
|
|
192
|
+
packageName: 'com.titaniumsdk.testapp'
|
|
193
|
+
});
|
|
194
|
+
intent.flags |= Ti.Android.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED | Ti.Android.FLAG_ACTIVITY_SINGLE_TOP;
|
|
195
|
+
intent.addCategory(Ti.Android.CATEGORY_LAUNCHER);
|
|
196
|
+
|
|
197
|
+
notification.contentIntent = Ti.Android.createPendingIntent({intent: intent});
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Schedule Future Notification (Background Service)
|
|
201
|
+
|
|
202
|
+
**tiapp.xml:**
|
|
203
|
+
```xml
|
|
204
|
+
<android xmlns:android="http://schemas.android.com/apk/res/android">
|
|
205
|
+
<services>
|
|
206
|
+
<service url="ExampleService.js" type="interval" />
|
|
207
|
+
</services>
|
|
208
|
+
</android>
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
**ExampleService.js:**
|
|
212
|
+
```javascript
|
|
213
|
+
const service = Ti.Android.currentService;
|
|
214
|
+
const serviceIntent = service.getIntent();
|
|
215
|
+
const timestamp = new Date(serviceIntent.getStringExtra('timestamp'));
|
|
216
|
+
|
|
217
|
+
if (new Date() > timestamp) {
|
|
218
|
+
const title = serviceIntent.getStringExtra('title');
|
|
219
|
+
const message = serviceIntent.getStringExtra('message');
|
|
220
|
+
|
|
221
|
+
const notification = Ti.Android.createNotification({
|
|
222
|
+
contentTitle: title,
|
|
223
|
+
contentText: message,
|
|
224
|
+
contentIntent: Ti.Android.createPendingIntent({intent: Ti.Android.createIntent()})
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
Ti.Android.NotificationManager.notify(1, notification);
|
|
228
|
+
Ti.Android.stopService(serviceIntent);
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
**Main app:**
|
|
233
|
+
```javascript
|
|
234
|
+
const intent = Ti.Android.createServiceIntent({url: 'ExampleService.js'});
|
|
235
|
+
intent.putExtra('interval', 5000); // Check every 5 seconds
|
|
236
|
+
intent.putExtra('timestamp', new Date(new Date().getTime() + (30 * 1000))); // Fire in 30 seconds
|
|
237
|
+
intent.putExtra('title', 'Scheduled Notification');
|
|
238
|
+
intent.putExtra('message', 'This was scheduled!');
|
|
239
|
+
Ti.Android.startService(intent);
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
## Local Notifications iOS
|
|
245
|
+
|
|
246
|
+
### Notification Display Locations
|
|
247
|
+
|
|
248
|
+
- **Alert Dialog**: "Open" or "Close" buttons (background, unlocked)
|
|
249
|
+
- **Banner Message**: Swipe down for actions, tap to launch (background, unlocked)
|
|
250
|
+
- **Lock Screen**: Swipe right to launch
|
|
251
|
+
- **Notification Center**: Queued notifications
|
|
252
|
+
- **Badge**: Number on app icon
|
|
253
|
+
- **Sound**: Audio alert
|
|
254
|
+
|
|
255
|
+
### Register for Notifications (iOS 8+)
|
|
256
|
+
|
|
257
|
+
```javascript
|
|
258
|
+
if (Ti.Platform.name === 'iPhone OS' && parseInt(Ti.Platform.version.split('.')[0]) >= 8) {
|
|
259
|
+
Ti.App.iOS.registerUserNotificationSettings({
|
|
260
|
+
types: [
|
|
261
|
+
Ti.App.iOS.USER_NOTIFICATION_TYPE_ALERT,
|
|
262
|
+
Ti.App.iOS.USER_NOTIFICATION_TYPE_SOUND,
|
|
263
|
+
Ti.App.iOS.USER_NOTIFICATION_TYPE_BADGE
|
|
264
|
+
]
|
|
265
|
+
// Add 'categories' for interactive notifications
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Schedule Local Notification
|
|
271
|
+
|
|
272
|
+
```javascript
|
|
273
|
+
const notification = Ti.App.iOS.scheduleLocalNotification({
|
|
274
|
+
date: new Date(new Date().getTime() + 3000), // 3 seconds from now
|
|
275
|
+
alertBody: 'New content available!',
|
|
276
|
+
alertAction: 'update', // Changes "slide to view" to "slide to update" or "Open" to "Update"
|
|
277
|
+
alertLaunchImage: 'splash.png', // Custom splash image
|
|
278
|
+
badge: 1, // Set badge number (negative to clear)
|
|
279
|
+
sound: '/alert.wav', // Sound file in Resources or app/assets
|
|
280
|
+
category: 'CATEGORY_ID', // For interactive notifications
|
|
281
|
+
repeat: 'daily', // or 'weekly', 'monthly', 'yearly'
|
|
282
|
+
timezone: 'America/Los_Angeles', // Optional (default: system timezone)
|
|
283
|
+
userInfo: { url: 'http://example.com/data.json', id: '123' } // Custom data
|
|
284
|
+
});
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### Monitor Notifications
|
|
288
|
+
|
|
289
|
+
**While app is in foreground or returns to foreground:**
|
|
290
|
+
```javascript
|
|
291
|
+
Ti.App.iOS.addEventListener('notification', (e) => {
|
|
292
|
+
// Process custom data
|
|
293
|
+
if (e.userInfo && e.userInfo.url) {
|
|
294
|
+
// Handle URL
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Reset badge
|
|
298
|
+
if (e.badge > 0) {
|
|
299
|
+
Ti.App.iOS.scheduleLocalNotification({
|
|
300
|
+
date: new Date(),
|
|
301
|
+
badge: -1
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### Cancel Notifications
|
|
308
|
+
|
|
309
|
+
```javascript
|
|
310
|
+
// Cancel all
|
|
311
|
+
Ti.App.iOS.cancelAllLocalNotifications();
|
|
312
|
+
|
|
313
|
+
// Cancel specific notification
|
|
314
|
+
const notification = Ti.App.iOS.scheduleLocalNotification({...});
|
|
315
|
+
notification.cancel();
|
|
316
|
+
|
|
317
|
+
// Or by ID
|
|
318
|
+
Ti.App.iOS.scheduleLocalNotification({
|
|
319
|
+
userInfo: {id: 'foo'},
|
|
320
|
+
alertBody: 'Test'
|
|
321
|
+
});
|
|
322
|
+
Ti.App.iOS.cancelLocalNotification('foo');
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
## Interactive Notifications iOS (iOS 8+)
|
|
328
|
+
|
|
329
|
+
### Create Notification Actions
|
|
330
|
+
|
|
331
|
+
```javascript
|
|
332
|
+
const acceptAction = Ti.App.iOS.createUserNotificationAction({
|
|
333
|
+
identifier: 'ACCEPT_IDENTIFIER',
|
|
334
|
+
title: 'Accept',
|
|
335
|
+
activationMode: Ti.App.iOS.USER_NOTIFICATION_ACTIVATION_MODE_FOREGROUND, // or BACKGROUND
|
|
336
|
+
destructive: false,
|
|
337
|
+
authenticationRequired: true // Require device unlock
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
const rejectAction = Ti.App.iOS.createUserNotificationAction({
|
|
341
|
+
identifier: 'REJECT_IDENTIFIER',
|
|
342
|
+
title: 'Reject',
|
|
343
|
+
activationMode: Ti.App.iOS.USER_NOTIFICATION_ACTIVATION_MODE_BACKGROUND,
|
|
344
|
+
destructive: true, // Red background in lock screen/notification center
|
|
345
|
+
authenticationRequired: false
|
|
346
|
+
});
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
### Create Notification Category
|
|
350
|
+
|
|
351
|
+
```javascript
|
|
352
|
+
const category = Ti.App.iOS.createUserNotificationCategory({
|
|
353
|
+
identifier: 'INVITE_CATEGORY',
|
|
354
|
+
actionsForDefaultContext: [acceptAction, rescheduleAction, delayAction, rejectAction], // Alert dialog (4 max)
|
|
355
|
+
actionsForMinimalContext: [acceptAction, rejectAction] // Other styles (2 max)
|
|
356
|
+
});
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
### Register Categories
|
|
360
|
+
|
|
361
|
+
```javascript
|
|
362
|
+
Ti.App.iOS.registerUserNotificationSettings({
|
|
363
|
+
types: [
|
|
364
|
+
Ti.App.iOS.USER_NOTIFICATION_TYPE_ALERT,
|
|
365
|
+
Ti.App.iOS.USER_NOTIFICATION_TYPE_BADGE,
|
|
366
|
+
Ti.App.iOS.USER_NOTIFICATION_TYPE_SOUND
|
|
367
|
+
],
|
|
368
|
+
categories: [category]
|
|
369
|
+
});
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
### Monitor Interactive Notifications
|
|
373
|
+
|
|
374
|
+
**Local notifications:**
|
|
375
|
+
```javascript
|
|
376
|
+
Ti.App.iOS.addEventListener('localnotificationaction', (e) => {
|
|
377
|
+
// e.category: category identifier
|
|
378
|
+
// e.identifier: action identifier
|
|
379
|
+
// e.userInfo: custom data
|
|
380
|
+
|
|
381
|
+
if (e.category === 'INVITE_CATEGORY' && e.identifier === 'ACCEPT_IDENTIFIER') {
|
|
382
|
+
// Handle accept
|
|
383
|
+
if (e.userInfo && e.userInfo.url) {
|
|
384
|
+
// Process URL
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// Reset badge
|
|
389
|
+
if (e.badge > 0) {
|
|
390
|
+
Ti.App.iOS.scheduleLocalNotification({ date: new Date(), badge: -1 });
|
|
391
|
+
}
|
|
392
|
+
});
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
**Push notifications:**
|
|
396
|
+
```javascript
|
|
397
|
+
Ti.App.iOS.addEventListener('remotenotificationaction', (e) => {
|
|
398
|
+
// e.category: category identifier
|
|
399
|
+
// e.identifier: action identifier
|
|
400
|
+
// e.data: push notification payload (JSON)
|
|
401
|
+
|
|
402
|
+
if (e.category === 'DOWNLOAD_CATEGORY' && e.identifier === 'ACCEPT_IDENTIFIER') {
|
|
403
|
+
if (e.data && e.data.url) {
|
|
404
|
+
// Process URL
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
});
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
### Schedule Interactive Local Notification
|
|
411
|
+
|
|
412
|
+
```javascript
|
|
413
|
+
Ti.App.iOS.scheduleLocalNotification({
|
|
414
|
+
date: new Date(new Date().getTime() + 3000),
|
|
415
|
+
alertBody: 'New content available! Download now?',
|
|
416
|
+
badge: 1,
|
|
417
|
+
userInfo: {url: 'http://example.com/data.json'},
|
|
418
|
+
category: 'INVITE_CATEGORY' // Must match registered category identifier
|
|
419
|
+
});
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
### Send Interactive Push Notification
|
|
423
|
+
|
|
424
|
+
**Payload format:**
|
|
425
|
+
```json
|
|
426
|
+
{
|
|
427
|
+
"alert": "New content available! Download now?",
|
|
428
|
+
"badge": 1,
|
|
429
|
+
"url": "http://example.com/data.json",
|
|
430
|
+
"category": "INVITE_CATEGORY"
|
|
431
|
+
}
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
---
|
|
435
|
+
|
|
436
|
+
## Push Notifications
|
|
437
|
+
|
|
438
|
+
### Overview
|
|
439
|
+
|
|
440
|
+
Push notifications allow server-to-device communication. Infrastructure differs by platform:
|
|
441
|
+
|
|
442
|
+
- **Android**: Firebase Cloud Messaging (FCM) or Google Cloud Messaging (GCM)
|
|
443
|
+
- **iOS**: Apple Push Notification service (APNs)
|
|
444
|
+
|
|
445
|
+
### Register for Push Notifications
|
|
446
|
+
|
|
447
|
+
```javascript
|
|
448
|
+
Ti.Network.registerForPushNotifications({
|
|
449
|
+
success: (e) => {
|
|
450
|
+
// e.deviceToken: token to send to your push server
|
|
451
|
+
Ti.API.info(`Device Token: ${e.deviceToken}`);
|
|
452
|
+
// Send this token to your server
|
|
453
|
+
},
|
|
454
|
+
error: (e) => {
|
|
455
|
+
Ti.API.error(`Push registration error: ${e.error}`);
|
|
456
|
+
},
|
|
457
|
+
callback: (e) => {
|
|
458
|
+
// Incoming push notification
|
|
459
|
+
// e.data: push payload (iOS)
|
|
460
|
+
// e.data: JSON object (Android)
|
|
461
|
+
Ti.API.info(`Push received: ${JSON.stringify(e.data)}`);
|
|
462
|
+
}
|
|
463
|
+
});
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
### Configure Push Services
|
|
467
|
+
|
|
468
|
+
**Android (FCM/GCM):**
|
|
469
|
+
1. Create project in Firebase Console or Google API Console
|
|
470
|
+
2. Get API key/Sender ID
|
|
471
|
+
3. Configure with your push provider (e.g., ArrowDB, ACS, or custom server)
|
|
472
|
+
|
|
473
|
+
**iOS (APNs):**
|
|
474
|
+
1. Create App ID in Apple Developer Portal
|
|
475
|
+
2. Enable Push Notifications
|
|
476
|
+
3. Create SSL certificate (development/production)
|
|
477
|
+
4. Configure with your push provider
|
|
478
|
+
|
|
479
|
+
### Push Notification Payload Format
|
|
480
|
+
|
|
481
|
+
**iOS:**
|
|
482
|
+
```json
|
|
483
|
+
{
|
|
484
|
+
"aps": {
|
|
485
|
+
"alert": "Message text",
|
|
486
|
+
"badge": 1,
|
|
487
|
+
"sound": "default"
|
|
488
|
+
},
|
|
489
|
+
"custom_field": "custom_value"
|
|
490
|
+
}
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
**Android:**
|
|
494
|
+
```json
|
|
495
|
+
{
|
|
496
|
+
"message": "Message text",
|
|
497
|
+
"title": "Notification Title",
|
|
498
|
+
"custom_field": "custom_value"
|
|
499
|
+
}
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
### Subscribe to Channels
|
|
503
|
+
|
|
504
|
+
**Using Arrow/ACS:**
|
|
505
|
+
```javascript
|
|
506
|
+
Cloud.PushNotifications.subscribeToken({
|
|
507
|
+
device_token: deviceToken,
|
|
508
|
+
channel: 'news',
|
|
509
|
+
type: Ti.Platform.name === 'iPhone OS' ? 'ios' : 'android'
|
|
510
|
+
}, (e) => {
|
|
511
|
+
if (e.success) {
|
|
512
|
+
Ti.API.info('Subscribed to news channel');
|
|
513
|
+
}
|
|
514
|
+
});
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
### Send Push Notifications
|
|
518
|
+
|
|
519
|
+
**Using Arrow/ACS:**
|
|
520
|
+
```javascript
|
|
521
|
+
Cloud.PushNotifications.notify({
|
|
522
|
+
channel: 'news',
|
|
523
|
+
payload: 'Hello subscribers!',
|
|
524
|
+
title: 'News Update'
|
|
525
|
+
}, (e) => {
|
|
526
|
+
if (e.success) {
|
|
527
|
+
Ti.API.info('Push sent successfully');
|
|
528
|
+
}
|
|
529
|
+
});
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
**Direct API call:**
|
|
533
|
+
Send to your push server with device tokens and payload.
|
|
534
|
+
|
|
535
|
+
### Best Practices
|
|
536
|
+
|
|
537
|
+
1. **Permissions**: iOS requires user permission (prompted on first call to `registerForPushNotifications`)
|
|
538
|
+
2. **Device Tokens**: Cache and send to your server; can change on app updates
|
|
539
|
+
3. **Background Handling**: Use proper activation modes for interactive notifications
|
|
540
|
+
4. **Badge Management**: Always reset badge after processing notification
|
|
541
|
+
5. **Error Handling**: Handle registration failures gracefully
|
|
542
|
+
6. **Testing**: Use development certificates/sandbox for testing iOS
|
|
543
|
+
|
|
544
|
+
---
|
|
545
|
+
|
|
546
|
+
## Common Patterns
|
|
547
|
+
|
|
548
|
+
### Check Notification Permissions
|
|
549
|
+
|
|
550
|
+
```javascript
|
|
551
|
+
// Android 4.1+ can disable notifications per app
|
|
552
|
+
// Check via Settings or handle gracefully (notification may not be shown)
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
### PendingIntent for Notification Actions
|
|
556
|
+
|
|
557
|
+
```javascript
|
|
558
|
+
const intent = Ti.Android.createIntent({
|
|
559
|
+
action: 'com.example.MY_ACTION',
|
|
560
|
+
// Add data/flags
|
|
561
|
+
});
|
|
562
|
+
const pendingIntent = Ti.Android.createPendingIntent({
|
|
563
|
+
intent: intent,
|
|
564
|
+
flags: Ti.Android.FLAG_UPDATE_CURRENT
|
|
565
|
+
});
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
### Notification Groups (Android 7.0+)
|
|
569
|
+
|
|
570
|
+
```javascript
|
|
571
|
+
// Requires API 24+; check documentation for latest implementation
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
### Rich Notifications (iOS 10+)
|
|
575
|
+
|
|
576
|
+
Use `UNMutableNotificationContent` with attachments (images, video, audio).
|
|
577
|
+
|
|
578
|
+
### Silent Push
|
|
579
|
+
|
|
580
|
+
```javascript
|
|
581
|
+
// iOS: include "content-available": 1 in payload
|
|
582
|
+
// Android: use data messages (FCM)
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
---
|
|
586
|
+
|
|
587
|
+
## Platform-Specific Notes
|
|
588
|
+
|
|
589
|
+
### Android
|
|
590
|
+
|
|
591
|
+
- **Service lifecycle**: Services may be killed if app is swiped away
|
|
592
|
+
- **Notification channels**: Android 8.0+ requires notification channels
|
|
593
|
+
- **Limitations**: Legacy MapView only supports single map view
|
|
594
|
+
|
|
595
|
+
### iOS
|
|
596
|
+
|
|
597
|
+
- **Sandbox vs Production**: Use correct certificates and endpoints
|
|
598
|
+
- **Background app refresh**: May be needed for certain push scenarios
|
|
599
|
+
- **LiveView**: Callbacks only work properly when LiveView is enabled
|