@maccesar/titools 5.1.0 → 5.2.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.
Files changed (85) hide show
  1. package/README.md +44 -12
  2. package/agents/ti-pro.md +4 -0
  3. package/commands/ti-audit.md +4 -0
  4. package/lib/config.js +2 -0
  5. package/package.json +1 -1
  6. package/skills/purgetss/SKILL.md +4 -4
  7. package/skills/purgetss/references/EXAMPLES.md +3 -3
  8. package/skills/purgetss/references/adopting-purgetss.md +2 -2
  9. package/skills/purgetss/references/animation-advanced.md +2 -2
  10. package/skills/purgetss/references/arbitrary-values.md +8 -5
  11. package/skills/purgetss/references/class-categories.md +5 -5
  12. package/skills/purgetss/references/class-index-properties.md +6 -6
  13. package/skills/purgetss/references/class-index.md +2 -2
  14. package/skills/purgetss/references/cli-commands.md +2 -2
  15. package/skills/purgetss/references/dynamic-component-creation.md +2 -2
  16. package/skills/purgetss/references/grid-layout.md +1 -1
  17. package/skills/purgetss/references/migration-guide.md +34 -4
  18. package/skills/purgetss/references/multi-density-images.md +6 -6
  19. package/skills/purgetss/references/platform-modifiers.md +21 -0
  20. package/skills/purgetss/references/ui-ux-design.md +1 -1
  21. package/skills/purgetss/references/version-history.md +9 -4
  22. package/skills/ti-admob-unified/SKILL.md +44 -0
  23. package/skills/ti-admob-unified/agents/openai.yaml +4 -0
  24. package/skills/ti-admob-unified/assets/example/app.js +1003 -0
  25. package/skills/ti-admob-unified/assets/example/lib/ads.js +618 -0
  26. package/skills/ti-admob-unified/assets/example/library-usage.js +104 -0
  27. package/skills/ti-admob-unified/references/api-reference.md +592 -0
  28. package/skills/ti-admob-unified/references/banners.md +435 -0
  29. package/skills/ti-admob-unified/references/contributing.md +228 -0
  30. package/skills/ti-admob-unified/references/dependencies.md +85 -0
  31. package/skills/ti-admob-unified/references/fullscreen.md +223 -0
  32. package/skills/ti-admob-unified/references/game-monetization.md +17 -0
  33. package/skills/ti-admob-unified/references/getting-started.md +275 -0
  34. package/skills/ti-admob-unified/references/index.md +30 -0
  35. package/skills/ti-admob-unified/references/integration.md +85 -0
  36. package/skills/ti-admob-unified/references/library.md +189 -0
  37. package/skills/ti-admob-unified/references/mediation.md +132 -0
  38. package/skills/ti-admob-unified/references/migration.md +255 -0
  39. package/skills/ti-admob-unified/references/module-changelog.md +54 -0
  40. package/skills/ti-admob-unified/references/module-license.txt +21 -0
  41. package/skills/ti-admob-unified/references/privacy.md +254 -0
  42. package/skills/ti-admob-unified/references/roadmap.md +146 -0
  43. package/skills/ti-admob-unified/references/source-notes.md +33 -0
  44. package/skills/ti-admob-unified/references/source-snapshot.json +440 -0
  45. package/skills/ti-admob-unified/references/third-party-notices.md +22 -0
  46. package/skills/ti-game/SKILL.md +10 -1
  47. package/skills/ti-game/references/api.md +10 -2
  48. package/skills/ti-game/references/debugging-performance.md +6 -0
  49. package/skills/ti-game/references/examples.md +11 -1
  50. package/skills/ti-game/references/patterns.md +24 -2
  51. package/skills/ti-game/references/physics.md +502 -0
  52. package/skills/ti-game/references/project-setup.md +23 -1
  53. package/skills/ti-game/references/recipes-action.md +46 -14
  54. package/skills/ti-game/references/recipes-adventure-effects.md +12 -1
  55. package/skills/ti-game/references/roadmap.md +2 -2
  56. package/skills/ti-game/references/tilemaps.md +2 -1
  57. package/skills/ti-game-assets/SKILL.md +53 -0
  58. package/skills/ti-game-assets/agents/openai.yaml +4 -0
  59. package/skills/ti-game-assets/references/alpha.md +26 -0
  60. package/skills/ti-game-assets/references/art-direction.md +37 -0
  61. package/skills/ti-game-assets/references/integration.md +49 -0
  62. package/skills/ti-game-assets/references/manifest.example.json +429 -0
  63. package/skills/ti-game-assets/references/manifest.md +39 -0
  64. package/skills/ti-game-assets/references/offline-audio.md +33 -0
  65. package/skills/ti-game-assets/references/prompts.md +33 -0
  66. package/skills/ti-game-assets/references/repeating-textures.md +17 -0
  67. package/skills/ti-game-assets/references/responsible-ads.md +31 -0
  68. package/skills/ti-game-assets/references/sound-assets.md +42 -0
  69. package/skills/ti-game-assets/references/vector-sources.md +21 -0
  70. package/skills/ti-game-assets/scripts/alpha_tools.py +160 -0
  71. package/skills/ti-game-assets/scripts/requirements.txt +1 -0
  72. package/skills/ti-game-assets/scripts/validate_manifest.py +267 -0
  73. package/skills/ti-synthengine/SKILL.md +37 -13
  74. package/skills/ti-synthengine/references/api.md +397 -31
  75. package/skills/ti-synthengine/references/audio-engine.md +235 -14
  76. package/skills/ti-synthengine/references/examples.md +396 -17
  77. package/skills/ti-synthengine/references/guide.md +331 -12
  78. package/skills/ti-synthengine/references/recipes.md +212 -22
  79. package/skills/ti-synthengine/references/recommendations.md +53 -16
  80. package/skills/ti-synthengine/references/sound-design.md +359 -15
  81. package/skills/ti-synthengine/references/source-notes.md +121 -0
  82. package/skills/ti-synthengine/references/source-snapshot.json +105 -0
  83. package/skills/ti-synthengine/references/stats-performance.json +2992 -0
  84. package/skills/ti-synthengine/references/stats-performance.md +38 -0
  85. package/skills/ti-synthengine/references/troubleshooting.md +61 -4
@@ -0,0 +1,618 @@
1
+ 'use strict'
2
+
3
+ /**
4
+ * Copy-ready TiAdMobUnified integration.
5
+ *
6
+ * Alloy: app/lib/ads.js
7
+ * Classic: Resources/lib/ads.js
8
+ *
9
+ * The Google Mobile Ads SDK owns banner refresh. This library never creates a
10
+ * refresh timer and never changes banner visibility while pausing.
11
+ */
12
+
13
+ const Admob = require('ti.admob.unified')
14
+
15
+ const resources = new Set()
16
+
17
+ let startup = null
18
+ let startupOptions = {}
19
+ let gateResolved = false
20
+ let canRequest = false
21
+ let sdkReady = false
22
+ let privacyOperation = null
23
+ let inspectorOperation = null
24
+
25
+ function call(handler, event) {
26
+ if (typeof handler !== 'function') {
27
+ return
28
+ }
29
+
30
+ try {
31
+ handler(event || {})
32
+ } catch (error) {
33
+ Ti.API.error(`[ads] Event handler failed: ${error.message || error}`)
34
+ }
35
+ }
36
+
37
+ function waitForEvent(name, invoke) {
38
+ return new Promise(resolve => {
39
+ let settled = false
40
+
41
+ function finish(event) {
42
+ if (settled) {
43
+ return
44
+ }
45
+
46
+ settled = true
47
+ Admob.removeEventListener(name, finish)
48
+ resolve(event || {})
49
+ }
50
+
51
+ Admob.addEventListener(name, finish)
52
+
53
+ try {
54
+ invoke()
55
+ } catch (error) {
56
+ finish({ success: false, error: error.message || String(error) })
57
+ }
58
+ })
59
+ }
60
+
61
+ function synchronizeResources(method) {
62
+ Array.from(resources).forEach(resource => {
63
+ if (typeof resource[method] === 'function') {
64
+ resource[method]()
65
+ }
66
+ })
67
+ }
68
+
69
+ async function initializeIfAllowed() {
70
+ canRequest = Admob.canRequestAds()
71
+
72
+ if (!canRequest) {
73
+ return false
74
+ }
75
+
76
+ if (sdkReady) {
77
+ return true
78
+ }
79
+
80
+ const initialized = await waitForEvent('module_initialized', () => {
81
+ Admob.initialize(startupOptions.initialization || {})
82
+ })
83
+
84
+ sdkReady = initialized.success === true || initialized.success === 1
85
+ if (!sdkReady) {
86
+ Ti.API.warn(`[ads] Google Mobile Ads initialization failed: ${initialized.error || 'unknown error'}`)
87
+ }
88
+
89
+ return sdkReady
90
+ }
91
+
92
+ /**
93
+ * Runs UMP -> optional ATT -> eligibility -> SDK initialization once.
94
+ *
95
+ * options.consent: dictionary passed to requestConsent()
96
+ * options.requestTrackingAuthorization: true only when the app has a real ATT use case
97
+ * options.initialization: dictionary passed to initialize()
98
+ */
99
+ function start(options) {
100
+ if (startup) {
101
+ return startup
102
+ }
103
+
104
+ startupOptions = options || {}
105
+ startup = (async () => {
106
+ const consent = await waitForEvent('consent_complete', () => {
107
+ Admob.requestConsent(startupOptions.consent || {})
108
+ })
109
+
110
+ if (consent.success === false || consent.success === 0) {
111
+ Ti.API.warn(`[ads] UMP completed with an error: ${consent.error || 'unknown error'}`)
112
+ }
113
+
114
+ let tracking = null
115
+ if (startupOptions.requestTrackingAuthorization === true) {
116
+ tracking = await waitForEvent('tracking_authorization_completed', () => {
117
+ Admob.requestTrackingAuthorization()
118
+ })
119
+ }
120
+
121
+ await initializeIfAllowed()
122
+ gateResolved = true
123
+ synchronizeResources('sync')
124
+
125
+ return {
126
+ consent,
127
+ tracking,
128
+ canRequestAds: canRequest,
129
+ initialized: sdkReady
130
+ }
131
+ })()
132
+
133
+ return startup
134
+ }
135
+
136
+ function addListener(proxy, listeners, name, handler) {
137
+ if (!name || typeof handler !== 'function') {
138
+ return
139
+ }
140
+
141
+ proxy.addEventListener(name, handler)
142
+ listeners.push([name, handler])
143
+ }
144
+
145
+ function removeListeners(proxy, listeners) {
146
+ listeners.forEach(entry => proxy.removeEventListener(entry[0], entry[1]))
147
+ listeners.length = 0
148
+ }
149
+
150
+ function bannerProperties(options, adaptive) {
151
+ const properties = {
152
+ adUnitId: options.adUnitId,
153
+ width: options.width || Ti.UI.FILL
154
+ }
155
+
156
+ for (const key of ['keywords', 'contentUrl', 'requestAgent', 'extras']) {
157
+ if (options[key] !== undefined) {
158
+ properties[key] = options[key]
159
+ }
160
+ }
161
+
162
+ if (adaptive) {
163
+ if (Ti.Platform && Ti.Platform.osname !== 'android') properties.height = Ti.UI.FILL
164
+ properties.adaptiveType = options.adaptiveType || Admob.ADAPTIVE_ANCHORED
165
+ for (const key of ['maxHeight', 'collapsible']) {
166
+ if (options[key] !== undefined) {
167
+ properties[key] = options[key]
168
+ }
169
+ }
170
+ } else {
171
+ properties.adSize = options.adSize || Admob.BANNER
172
+ if (options.customAdSize !== undefined) {
173
+ properties.customAdSize = options.customAdSize
174
+ }
175
+ }
176
+
177
+ return properties
178
+ }
179
+
180
+ function createBannerPlacement(options, adaptive) {
181
+ if (!options || !options.host || !options.adUnitId) {
182
+ throw new Error('A banner requires host and adUnitId')
183
+ }
184
+
185
+ const host = options.host
186
+ const content = options.content
187
+ const reservedHeight = Number(options.reservedHeight)
188
+
189
+ if (!Number.isFinite(reservedHeight) || reservedHeight <= 0) {
190
+ throw new Error('A banner requires a positive reservedHeight in dp')
191
+ }
192
+
193
+ let proxy = null
194
+ let listeners = []
195
+ let destroyed = false
196
+ let locallyPaused = options.paused === true
197
+
198
+ function reserve(height) {
199
+ const dimension = `${Math.max(0, Number(height) || 0)}dp`
200
+ host.height = dimension
201
+ if (content) {
202
+ content.bottom = dimension
203
+ }
204
+ }
205
+
206
+ function detach(destroyNative) {
207
+ if (!proxy) {
208
+ return
209
+ }
210
+
211
+ const target = proxy
212
+ proxy = null
213
+ removeListeners(target, listeners)
214
+
215
+ if (destroyNative) {
216
+ target.destroy()
217
+ }
218
+
219
+ try {
220
+ host.remove(target)
221
+ } catch (error) {
222
+ Ti.API.debug(`[ads] Banner was already detached: ${error.message || error}`)
223
+ }
224
+ }
225
+
226
+ function onLoaded(event) {
227
+ const actualHeight = Number(event.height)
228
+ reserve(actualHeight > 0 ? actualHeight : reservedHeight)
229
+ call(options.onLoaded, event)
230
+ }
231
+
232
+ function onFailedToLoad(event) {
233
+ detach(true)
234
+ if (options.collapseOnFailure !== false) {
235
+ reserve(0)
236
+ }
237
+ call(options.onFailedToLoad, event)
238
+ }
239
+
240
+ function onDestroyed(event) {
241
+ detach(false)
242
+ reserve(0)
243
+ call(options.onDestroyed, event)
244
+ }
245
+
246
+ function attachForwarder(name, optionName) {
247
+ addListener(proxy, listeners, name, event => call(options[optionName], event))
248
+ }
249
+
250
+ const placement = {
251
+ sync() {
252
+ if (destroyed || locallyPaused || proxy) {
253
+ return
254
+ }
255
+
256
+ if (!canRequest || !sdkReady) {
257
+ if (gateResolved) {
258
+ reserve(0)
259
+ }
260
+ return
261
+ }
262
+
263
+ reserve(reservedHeight)
264
+ proxy = adaptive
265
+ ? Admob.createAdaptiveBanner(bannerProperties(options, true))
266
+ : Admob.createBanner(bannerProperties(options, false))
267
+
268
+ addListener(proxy, listeners, Admob.AD_LOADED, onLoaded)
269
+ addListener(proxy, listeners, Admob.AD_FAILED_TO_LOAD, onFailedToLoad)
270
+ addListener(proxy, listeners, Admob.AD_DESTROYED, onDestroyed)
271
+ attachForwarder(Admob.AD_CLICKED, 'onClicked')
272
+ attachForwarder(Admob.AD_IMPRESSION, 'onImpression')
273
+ attachForwarder(Admob.AD_OPENED, 'onOpened')
274
+ attachForwarder(Admob.AD_CLOSED, 'onClosed')
275
+
276
+ if (adaptive && Ti.Platform && Ti.Platform.osname !== 'android') {
277
+ const target = proxy
278
+ const anchored = !options.adaptiveType || options.adaptiveType === Admob.ADAPTIVE_ANCHORED
279
+ let measuredWidth = 0
280
+ function prepareSize(width) {
281
+ if (!anchored || width <= 0 || measuredWidth === width) return false
282
+ measuredWidth = width
283
+ const height = Admob.getAnchoredAdaptiveBannerSize(width).height
284
+ const changed = host.height !== `${height}dp`
285
+ reserve(height)
286
+ return changed
287
+ }
288
+ prepareSize(host.rect ? host.rect.width : 0)
289
+ function loadAfterLayout() {
290
+ if (destroyed || proxy !== target || target.rect.width <= 0 || target.rect.height <= 0) return
291
+ if (prepareSize(target.rect.width)) return
292
+ target.removeEventListener('postlayout', loadAfterLayout)
293
+ target.load()
294
+ }
295
+ addListener(target, listeners, 'postlayout', loadAfterLayout)
296
+ host.add(target)
297
+ } else {
298
+ host.add(proxy)
299
+ proxy.load()
300
+ }
301
+ },
302
+
303
+ pause() {
304
+ if (destroyed || locallyPaused) {
305
+ return
306
+ }
307
+
308
+ locallyPaused = true
309
+ if (proxy) {
310
+ proxy.pause()
311
+ }
312
+ },
313
+
314
+ resume() {
315
+ if (destroyed || !locallyPaused) {
316
+ return
317
+ }
318
+
319
+ locallyPaused = false
320
+ if (proxy) {
321
+ proxy.resume()
322
+ }
323
+ placement.sync()
324
+ },
325
+
326
+ rebuild() {
327
+ if (destroyed) {
328
+ return
329
+ }
330
+
331
+ detach(true)
332
+ reserve(reservedHeight)
333
+ placement.sync()
334
+ },
335
+
336
+ retry() {
337
+ placement.rebuild()
338
+ },
339
+
340
+ authorizationChanged() {
341
+ if (destroyed) {
342
+ return
343
+ }
344
+
345
+ detach(true)
346
+ reserve(canRequest && sdkReady ? reservedHeight : 0)
347
+ placement.sync()
348
+ },
349
+
350
+ isLoaded() {
351
+ return Boolean(proxy && proxy.isLoaded())
352
+ },
353
+
354
+ destroy() {
355
+ if (destroyed) {
356
+ return
357
+ }
358
+
359
+ destroyed = true
360
+ resources.delete(placement)
361
+ detach(true)
362
+ reserve(0)
363
+ }
364
+ }
365
+
366
+ resources.add(placement)
367
+ reserve(reservedHeight)
368
+ placement.sync()
369
+ return placement
370
+ }
371
+
372
+ function createBanner(options) {
373
+ return createBannerPlacement(options, false)
374
+ }
375
+
376
+ function createAdaptiveBanner(options) {
377
+ return createBannerPlacement(options, true)
378
+ }
379
+
380
+ const fullscreenEvents = [
381
+ ['AD_LOADED', 'onLoaded'],
382
+ ['AD_FAILED_TO_LOAD', 'onFailedToLoad'],
383
+ ['AD_OPENED', 'onOpened'],
384
+ ['AD_SHOWED_FULLSCREEN_CONTENT', 'onShowed'],
385
+ ['AD_CLOSED', 'onClosed'],
386
+ ['AD_CLICKED', 'onClicked'],
387
+ ['AD_IMPRESSION', 'onImpression'],
388
+ ['AD_FAILED_TO_SHOW', 'onFailedToShow'],
389
+ ['AD_REWARDED', 'onRewarded'],
390
+ ['AD_DESTROYED', 'onDestroyed']
391
+ ]
392
+
393
+ function fullscreenProperties(options) {
394
+ const properties = {
395
+ adUnitId: options.adUnitId,
396
+ autoReload: options.autoReload === true
397
+ }
398
+
399
+ for (const key of ['keywords', 'requestAgent', 'extras']) {
400
+ if (options[key] !== undefined) {
401
+ properties[key] = options[key]
402
+ }
403
+ }
404
+
405
+ return properties
406
+ }
407
+
408
+ // Shared by the managers and the raw-API playground. Rotation alone does no work.
409
+ function createFullscreenSizeGuard(getSize) {
410
+ let requestedSize = null
411
+ function sizeKey() {
412
+ const size = getSize()
413
+ return size && size.width > 0 && size.height > 0
414
+ ? `${Math.round(size.width)}x${Math.round(size.height)}` : null
415
+ }
416
+ return {
417
+ recordLoad() { requestedSize = sizeKey() },
418
+ show(present, reload) {
419
+ const currentSize = sizeKey()
420
+ if (currentSize && currentSize !== requestedSize) {
421
+ reload()
422
+ return false
423
+ }
424
+ present()
425
+ return true
426
+ }
427
+ }
428
+ }
429
+
430
+ function createFullscreen(options, factoryName) {
431
+ if (!options || !options.adUnitId) {
432
+ throw new Error(`${factoryName} requires adUnitId`)
433
+ }
434
+
435
+ let proxy = null
436
+ let listeners = []
437
+ let destroyed = false
438
+ let loadRequested = options.autoLoad === true
439
+ let loading = false
440
+ const sizeGuard = createFullscreenSizeGuard(() => (options.window || Ti.UI.currentWindow)?.size)
441
+
442
+ function detach(destroyNative) {
443
+ if (!proxy) {
444
+ return
445
+ }
446
+
447
+ const target = proxy
448
+ proxy = null
449
+ loading = false
450
+ removeListeners(target, listeners)
451
+ if (destroyNative) {
452
+ target.destroy()
453
+ }
454
+ }
455
+
456
+ const manager = {
457
+ sync() {
458
+ if (destroyed || proxy || !loadRequested || !canRequest || !sdkReady) {
459
+ return
460
+ }
461
+
462
+ proxy = Admob[factoryName](fullscreenProperties(options))
463
+
464
+ fullscreenEvents.forEach(entry => {
465
+ const eventName = Admob[entry[0]]
466
+ if (!eventName) {
467
+ return
468
+ }
469
+
470
+ addListener(proxy, listeners, eventName, event => {
471
+ if (entry[0] === 'AD_LOADED' || entry[0] === 'AD_FAILED_TO_LOAD') loading = false
472
+ if (entry[0] === 'AD_CLOSED' && options.autoReload === true) {
473
+ loading = true
474
+ sizeGuard.recordLoad()
475
+ }
476
+ if (entry[0] === 'AD_CLOSED' && options.autoReload !== true) {
477
+ loadRequested = false
478
+ }
479
+ if (entry[0] === 'AD_FAILED_TO_SHOW') {
480
+ loadRequested = false
481
+ }
482
+ if (entry[0] === 'AD_DESTROYED') {
483
+ detach(false)
484
+ call(options[entry[1]], event)
485
+ return
486
+ }
487
+ call(options[entry[1]], event)
488
+ })
489
+ })
490
+
491
+ loading = true
492
+ sizeGuard.recordLoad()
493
+ proxy.load()
494
+ },
495
+
496
+ load() {
497
+ if (destroyed || loading) {
498
+ return false
499
+ }
500
+
501
+ loadRequested = true
502
+ if (proxy) {
503
+ loading = true
504
+ sizeGuard.recordLoad()
505
+ proxy.load()
506
+ } else {
507
+ manager.sync()
508
+ }
509
+ return true
510
+ },
511
+
512
+ show() {
513
+ if (!proxy || loading || !proxy.isLoaded()) {
514
+ return false
515
+ }
516
+
517
+ return sizeGuard.show(() => proxy.show(), () => manager.retry())
518
+ },
519
+
520
+ isLoaded() {
521
+ return Boolean(proxy && proxy.isLoaded())
522
+ },
523
+
524
+ retry() {
525
+ if (destroyed) {
526
+ return false
527
+ }
528
+
529
+ detach(true)
530
+ loadRequested = true
531
+ manager.sync()
532
+ return true
533
+ },
534
+
535
+ authorizationChanged() {
536
+ if (destroyed) {
537
+ return
538
+ }
539
+
540
+ detach(true)
541
+ manager.sync()
542
+ },
543
+
544
+ destroy() {
545
+ if (destroyed) {
546
+ return
547
+ }
548
+
549
+ destroyed = true
550
+ resources.delete(manager)
551
+ detach(true)
552
+ }
553
+ }
554
+
555
+ resources.add(manager)
556
+ manager.sync()
557
+ return manager
558
+ }
559
+
560
+ function createInterstitial(options) {
561
+ return createFullscreen(options, 'createInterstitial')
562
+ }
563
+
564
+ function createRewarded(options) {
565
+ return createFullscreen(options, 'createRewarded')
566
+ }
567
+
568
+ function createAppOpen(options) {
569
+ return createFullscreen(options, 'createAppOpen')
570
+ }
571
+
572
+ function showPrivacyOptions() {
573
+ if (privacyOperation) return privacyOperation
574
+ privacyOperation = (async () => {
575
+ const result = await waitForEvent('privacy_options_dismissed', () => {
576
+ Admob.presentPrivacyOptions({})
577
+ })
578
+
579
+ await initializeIfAllowed()
580
+ synchronizeResources('authorizationChanged')
581
+ return result
582
+ })().finally(() => { privacyOperation = null })
583
+ return privacyOperation
584
+ }
585
+
586
+ function isPrivacyOptionsRequired() {
587
+ return Admob.isPrivacyOptionsRequired()
588
+ }
589
+
590
+ function openAdInspector() {
591
+ if (inspectorOperation) return inspectorOperation
592
+ inspectorOperation = waitForEvent('ad_inspector_closed', () => Admob.openAdInspector())
593
+ .finally(() => { inspectorOperation = null })
594
+ return inspectorOperation
595
+ }
596
+
597
+ function isReady() {
598
+ return canRequest && sdkReady
599
+ }
600
+
601
+ function destroyAll() {
602
+ Array.from(resources).forEach(resource => resource.destroy())
603
+ }
604
+
605
+ module.exports = {
606
+ start,
607
+ isReady,
608
+ isPrivacyOptionsRequired,
609
+ showPrivacyOptions,
610
+ openAdInspector,
611
+ createBanner,
612
+ createAdaptiveBanner,
613
+ createFullscreenSizeGuard,
614
+ createInterstitial,
615
+ createRewarded,
616
+ createAppOpen,
617
+ destroyAll
618
+ }
@@ -0,0 +1,104 @@
1
+ 'use strict'
2
+
3
+ /**
4
+ * Usage of example/lib/ads.js after copying it to the consuming app's lib/.
5
+ * This file is intentionally separate from app.js, which demonstrates the raw
6
+ * module API. Pick one integration style; do not initialize both.
7
+ */
8
+
9
+ const Admob = require('ti.admob.unified')
10
+ const ads = require('lib/ads')
11
+
12
+ const win = Ti.UI.createWindow({ layout: 'composite', backgroundColor: '#ffffff' })
13
+ const content = Ti.UI.createScrollView({ top: 0, left: 0, right: 0, bottom: 0 })
14
+ const bannerHost = Ti.UI.createView({ left: 0, right: 0, bottom: 0, height: 50 })
15
+
16
+ win.add(content)
17
+ win.add(bannerHost)
18
+
19
+ const banner = ads.createBanner({
20
+ host: bannerHost,
21
+ content,
22
+ reservedHeight: 50,
23
+ adUnitId: Admob.TEST_BANNER,
24
+ adSize: Admob.BANNER
25
+ })
26
+
27
+ // Use this instead of createBanner() when the placement should adapt to width.
28
+ function createAdaptiveBanner() {
29
+ banner.destroy()
30
+ return ads.createAdaptiveBanner({
31
+ host: bannerHost,
32
+ content,
33
+ reservedHeight: 50,
34
+ adUnitId: Admob.TEST_ADAPTIVE_BANNER,
35
+ adaptiveType: Admob.ADAPTIVE_ANCHORED
36
+ })
37
+ }
38
+
39
+ const interstitial = ads.createInterstitial({
40
+ window: win,
41
+ adUnitId: Admob.TEST_INTERSTITIAL,
42
+ onLoaded: () => Ti.API.info('[ads] Interstitial ready')
43
+ })
44
+
45
+ const rewarded = ads.createRewarded({
46
+ window: win,
47
+ adUnitId: Admob.TEST_REWARDED,
48
+ onRewarded: event => grantReward(event.rewardAmount, event.rewardType)
49
+ })
50
+
51
+ const appOpen = ads.createAppOpen({
52
+ window: win,
53
+ adUnitId: Admob.TEST_APP_OPEN,
54
+ onLoaded: () => Ti.API.info('[ads] App-open ready for the next foreground')
55
+ })
56
+
57
+ function grantReward(amount, type) {
58
+ Ti.API.info(`[ads] Grant ${amount} ${type}`)
59
+ }
60
+
61
+ function onNaturalBreak() {
62
+ interstitial.show()
63
+ }
64
+
65
+ function onRewardAccepted() {
66
+ rewarded.show()
67
+ }
68
+
69
+ function onApplicationResumed() {
70
+ appOpen.show()
71
+ }
72
+
73
+ function openChildWindow() {
74
+ const child = Ti.UI.createWindow({ backgroundColor: '#ffffff' })
75
+ banner.pause()
76
+ child.addEventListener('close', () => banner.resume())
77
+ child.open()
78
+ }
79
+
80
+ win.addEventListener('close', event => {
81
+ if (!event._closeFromActivityForcedToDestroy) ads.destroyAll()
82
+ })
83
+ win.open()
84
+
85
+ ads.start({
86
+ consent: { tagForUnderAgeOfConsent: false },
87
+ initialization: {}
88
+ }).then(result => {
89
+ if (!result.initialized) {
90
+ return
91
+ }
92
+
93
+ interstitial.load()
94
+ rewarded.load()
95
+ appOpen.load()
96
+ })
97
+
98
+ // References kept here to make the intended call sites explicit in this
99
+ // compact example. Real apps connect them to their own navigation/lifecycle.
100
+ void createAdaptiveBanner
101
+ void onNaturalBreak
102
+ void onRewardAccepted
103
+ void onApplicationResumed
104
+ void openChildWindow