@maccesar/titools 3.3.0 → 4.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/README.md +28 -62
- package/lib/cleanup.js +29 -0
- package/lib/commands/agents.js +8 -7
- package/lib/commands/skills.js +118 -23
- package/lib/config.js +13 -15
- package/lib/installer.js +5 -3
- package/lib/utils.js +36 -0
- package/package.json +1 -1
- package/skills/alloy-guides/SKILL.md +188 -0
- package/skills/alloy-guides/references/CLI_TASKS.md +233 -0
- package/skills/alloy-guides/references/CONCEPTS.md +171 -0
- package/skills/alloy-guides/references/CONTROLLERS.md +279 -0
- package/skills/alloy-guides/references/MODELS.md +742 -0
- package/skills/alloy-guides/references/MODELS_ADVANCED.md +482 -0
- package/skills/alloy-guides/references/PURGETSS.md +57 -0
- package/skills/alloy-guides/references/VIEWS_DYNAMIC.md +235 -0
- package/skills/alloy-guides/references/VIEWS_STYLES.md +375 -0
- package/skills/alloy-guides/references/VIEWS_WITHOUT_CONTROLLERS.md +102 -0
- package/skills/alloy-guides/references/VIEWS_XML.md +581 -0
- package/skills/alloy-guides/references/WIDGETS.md +160 -0
- package/skills/alloy-howtos/SKILL.md +179 -0
- package/skills/alloy-howtos/references/best_practices.md +121 -0
- package/skills/alloy-howtos/references/cli_reference.md +230 -0
- package/skills/alloy-howtos/references/config_files.md +158 -0
- package/skills/alloy-howtos/references/custom_tags.md +148 -0
- package/skills/alloy-howtos/references/debugging_troubleshooting.md +78 -0
- package/skills/alloy-howtos/references/samples.md +156 -0
- package/skills/purgetss/SKILL.md +6 -3
- package/skills/purgetss/references/EXAMPLES.md +8 -6
- package/skills/purgetss/references/app-branding.md +5 -0
- package/skills/purgetss/references/apply-directive.md +1 -1
- package/skills/purgetss/references/class-categories.md +1 -1
- package/skills/purgetss/references/class-index-properties.md +618 -0
- package/skills/purgetss/references/class-index.md +4 -610
- package/skills/purgetss/references/cli-commands.md +15 -0
- package/skills/purgetss/references/custom-fonts.md +39 -36
- package/skills/purgetss/references/custom-rules.md +1 -1
- package/skills/purgetss/references/customization-deep-dive.md +64 -1
- package/skills/purgetss/references/icon-fonts.md +2 -0
- package/skills/purgetss/references/migration-guide.md +53 -1
- package/skills/purgetss/references/multi-density-images.md +53 -2
- package/skills/purgetss/references/svg-pipeline.md +249 -0
- package/skills/purgetss/references/tikit-components.md +2 -2
- package/skills/purgetss/references/titanium-resets.md +2 -2
- package/skills/purgetss/references/ui-ux-design.md +7 -5
- package/skills/purgetss/references/version-history.md +12 -0
- package/skills/ti-api/SKILL.md +128 -0
- package/skills/ti-api/references/api-android.md +675 -0
- package/skills/ti-api/references/api-app-platform.md +636 -0
- package/skills/ti-api/references/api-core.md +764 -0
- package/skills/ti-api/references/api-data-network.md +641 -0
- package/skills/ti-api/references/api-media.md +655 -0
- package/skills/ti-api/references/api-modules-ble-bluetooth.md +657 -0
- package/skills/ti-api/references/api-modules-coremotion-urlsession.md +411 -0
- package/skills/ti-api/references/api-modules-map.md +632 -0
- package/skills/ti-api/references/api-modules-nfc.md +725 -0
- package/skills/ti-api/references/api-modules-social-misc.md +526 -0
- package/skills/ti-api/references/api-services.md +700 -0
- package/skills/ti-api/references/api-ui-android.md +499 -0
- package/skills/ti-api/references/api-ui-extras.md +702 -0
- package/skills/ti-api/references/api-ui-ios-animator.md +378 -0
- package/skills/ti-api/references/api-ui-ios.md +756 -0
- package/skills/ti-api/references/api-ui-lists.md +581 -0
- package/skills/ti-api/references/api-ui-text-input.md +607 -0
- package/skills/ti-api/references/api-ui-views.md +572 -0
- package/skills/ti-api/references/api-ui-windows-navigation.md +676 -0
- package/skills/ti-api/references/api-xml-global.md +743 -0
- package/skills/ti-expert/SKILL.md +1 -1
- package/skills/ti-expert/references/adaptive-layouts.md +164 -1
- package/skills/ti-guides/SKILL.md +82 -0
- package/skills/ti-guides/references/advanced-data-and-images.md +155 -0
- package/skills/ti-guides/references/android-manifest.md +97 -0
- package/skills/ti-guides/references/app-distribution.md +373 -0
- package/skills/ti-guides/references/application-frameworks.md +366 -0
- package/skills/ti-guides/references/cli-reference.md +700 -0
- package/skills/ti-guides/references/coding-best-practices.md +150 -0
- package/skills/ti-guides/references/commonjs-advanced.md +279 -0
- package/skills/ti-guides/references/hello-world.md +99 -0
- package/skills/ti-guides/references/hyperloop-native-access.md +458 -0
- package/skills/ti-guides/references/javascript-primer.md +402 -0
- package/skills/ti-guides/references/reserved-words.md +36 -0
- package/skills/ti-guides/references/resources.md +172 -0
- package/skills/ti-guides/references/style-and-conventions.md +104 -0
- package/skills/ti-guides/references/tiapp-config.md +655 -0
- package/skills/ti-howtos/SKILL.md +131 -0
- package/skills/ti-howtos/references/android-platform-deep-dives.md +609 -0
- package/skills/ti-howtos/references/automation-fastlane-appium.md +96 -0
- package/skills/ti-howtos/references/buffer-codec-streams.md +162 -0
- package/skills/ti-howtos/references/cross-platform-development.md +358 -0
- package/skills/ti-howtos/references/debugging-profiling.md +473 -0
- package/skills/ti-howtos/references/extending-titanium.md +684 -0
- package/skills/ti-howtos/references/google-maps-v2.md +172 -0
- package/skills/ti-howtos/references/ios-map-kit.md +149 -0
- package/skills/ti-howtos/references/ios-platform-deep-dives.md +595 -0
- package/skills/ti-howtos/references/local-data-sources.md +310 -0
- package/skills/ti-howtos/references/location-and-maps.md +267 -0
- package/skills/ti-howtos/references/media-apis.md +268 -0
- package/skills/ti-howtos/references/notification-services.md +539 -0
- package/skills/ti-howtos/references/remote-data-sources.md +339 -0
- package/skills/ti-howtos/references/tutorials.md +552 -0
- package/skills/ti-howtos/references/using-modules.md +182 -0
- package/skills/ti-howtos/references/web-content-integration.md +288 -0
- package/skills/ti-howtos/references/webpack-build-pipeline.md +125 -0
|
@@ -149,7 +149,7 @@ For the complete reference with examples, see [Alloy builtins and globals](refer
|
|
|
149
149
|
- [Contracts & Communication](references/contracts.md)
|
|
150
150
|
- [State Management](references/state-management.md)
|
|
151
151
|
- [Anti-patterns to Avoid](references/anti-patterns.md)
|
|
152
|
-
- [Adaptive Layouts](references/adaptive-layouts.md) (responsive breakpoints, tablet/foldable/desktop support, Android 17 resizability compliance)
|
|
152
|
+
- [Adaptive Layouts](references/adaptive-layouts.md) (responsive breakpoints, tablet/foldable/desktop support, Android 16/17 resizability compliance, orientation locks and which Android activities they must cover)
|
|
153
153
|
|
|
154
154
|
### Implementation & API
|
|
155
155
|
- [Alloy Builtins & Globals](references/alloy-builtins.md)
|
|
@@ -19,6 +19,26 @@ Scope: Only affects screens >= 600dp. Phones (< 600dp) and games (`android:appCa
|
|
|
19
19
|
|
|
20
20
|
What this means: portrait-only apps will be forced into landscape/resized on large screens. If the UI doesn't support it, it will break.
|
|
21
21
|
|
|
22
|
+
### It already started in Android 16 (API 36)
|
|
23
|
+
|
|
24
|
+
This is usually described as an API 37 change, but it lands one release earlier: an app
|
|
25
|
+
targeting **API 36** already has `android:screenOrientation` ignored on >= 600dp displays
|
|
26
|
+
running Android 16. The difference is that API 36 still ships an opt-out:
|
|
27
|
+
|
|
28
|
+
```xml
|
|
29
|
+
<!-- inside <application>, or on an individual <activity> -->
|
|
30
|
+
<property
|
|
31
|
+
android:name="android.window.PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY"
|
|
32
|
+
android:value="true"/>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The opt-out stops working the moment you target API 37, so treat it as a bridge while the
|
|
36
|
+
layout is made adaptive — not as a fix. Source: [Android 16 behavior changes](https://developer.android.com/about/versions/16/behavior-changes-16).
|
|
37
|
+
|
|
38
|
+
Worth knowing when debugging: this only kicks in at >= 600dp. If a **phone** is rotating
|
|
39
|
+
despite a portrait lock, the cause is in the app, not in Android — see the orientation
|
|
40
|
+
debugging recipe in section 7.
|
|
41
|
+
|
|
22
42
|
### Related: edge-to-edge (API 36)
|
|
23
43
|
|
|
24
44
|
Before the resizability requirement (API 37), Android SDK 36 removes the opt-out for edge-to-edge rendering, causing Titanium layouts to overlap system navigation bars. The Titanium core team has a PR to handle this at the SDK level: tidev/titanium-sdk#14399 — adds an `EdgeToEdgeHelper` with correct padding so existing apps work without code changes.
|
|
@@ -343,6 +363,24 @@ const dpWidth = $.win.size.width / density // WRONG: halves the value
|
|
|
343
363
|
Ti.Gesture.isPortrait() // TypeError!
|
|
344
364
|
Ti.Gesture.portrait // correct — boolean property
|
|
345
365
|
|
|
366
|
+
// ❌ Setting orientation imperatively — silently overrides the manifest
|
|
367
|
+
$.win.activity.requestedOrientation = Ti.Android.SCREEN_ORIENTATION_PORTRAIT
|
|
368
|
+
// The manifest value is applied in onCreate; this runs afterwards and always wins.
|
|
369
|
+
// Nothing in tiapp.xml can beat it, which makes it invisible when debugging.
|
|
370
|
+
// Declare `orientationModes` on the window instead (see section 7).
|
|
371
|
+
|
|
372
|
+
// ❌ Detecting tablets with a pixel threshold
|
|
373
|
+
Math.min(displayCaps.platformWidth, displayCaps.platformHeight) >= 600
|
|
374
|
+
// platformWidth returns PIXELS on Android. A 720x1604 @xhdpi phone is 360dp wide,
|
|
375
|
+
// but 720 >= 600 reports "tablet". Use Alloy.isTablet / the formFactor query, which
|
|
376
|
+
// read Ti.Platform.Android.physicalSizeCategory instead of raw pixels.
|
|
377
|
+
|
|
378
|
+
// ❌ Passing an empty orientationModes array to mean "no preference"
|
|
379
|
+
orientationModes: []
|
|
380
|
+
// An empty array maps to SCREEN_ORIENTATION_SENSOR (TiWindowProxy.setOrientationModes),
|
|
381
|
+
// so it doesn't clear the setting — it actively unlocks rotation, overriding the manifest.
|
|
382
|
+
// To leave orientation alone, don't set the property at all.
|
|
383
|
+
|
|
346
384
|
// ❌ Using gap property — does NOT exist in Titanium
|
|
347
385
|
{ layout: 'horizontal', gap: 10 }
|
|
348
386
|
|
|
@@ -373,7 +411,129 @@ Titanium generates the main activity name as `<app-id>.<Appname>Activity`. For e
|
|
|
373
411
|
</android>
|
|
374
412
|
```
|
|
375
413
|
|
|
376
|
-
To verify the exact generated activity name, check `build/android/AndroidManifest.xml` after a build.
|
|
414
|
+
To verify the exact generated activity name, check `build/android/app/src/main/AndroidManifest.xml` after a build.
|
|
415
|
+
|
|
416
|
+
### Which activities a lock has to cover
|
|
417
|
+
|
|
418
|
+
`android:screenOrientation` is per-activity — it is not inherited from `<application>`. Every
|
|
419
|
+
window the app opens runs inside one of a small, fixed set of activities declared by the
|
|
420
|
+
Titanium AAR, so read the canonical list from the SDK instead of memorising it:
|
|
421
|
+
|
|
422
|
+
```bash
|
|
423
|
+
cat ~/.gradle/caches/*/transforms/*/transformed/jetified-titanium-*/AndroidManifest.xml
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
As of 13.4.0 there are exactly six, and `android:name` takes the fully-qualified form:
|
|
427
|
+
|
|
428
|
+
```
|
|
429
|
+
org.appcelerator.titanium.TiActivity
|
|
430
|
+
org.appcelerator.titanium.TiTranslucentActivity
|
|
431
|
+
ti.modules.titanium.media.TiCameraActivity
|
|
432
|
+
ti.modules.titanium.media.TiCameraXActivity
|
|
433
|
+
ti.modules.titanium.media.TiVideoActivity
|
|
434
|
+
ti.modules.titanium.ui.android.TiPreferencesActivity
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
The seventh is the app's own root activity, generated from the `<name>` in tiapp.xml (see the
|
|
438
|
+
derivation rule above). Which one hosts what:
|
|
439
|
+
|
|
440
|
+
| What the app does | Activity |
|
|
441
|
+
|---|---|
|
|
442
|
+
| `Window.open()`, Tab, TabGroup, NavigationWindow | `org.appcelerator.titanium.TiActivity` |
|
|
443
|
+
| Window with `modal:true`, `opacity`, or a `backgroundColor` with alpha < 255 | `org.appcelerator.titanium.TiTranslucentActivity` |
|
|
444
|
+
| `showCamera({ overlay })` | `ti.modules.titanium.media.TiCameraActivity` (`TiCameraXActivity` when `useCameraX:true`) |
|
|
445
|
+
| `showCamera()` with no overlay | none of yours — Titanium launches the system camera app |
|
|
446
|
+
| VideoPlayer added to a regular Window | `org.appcelerator.titanium.TiActivity` |
|
|
447
|
+
|
|
448
|
+
Two things are better left undeclared:
|
|
449
|
+
|
|
450
|
+
- **`android:configChanges`.** The CLI strips it before the merge even happens — `_build.js`
|
|
451
|
+
keeps a list of the `TiBaseActivity`-derived activities and calls
|
|
452
|
+
`removeActivityAttribute(name, 'android:configChanges')` on each, with the reason spelled out
|
|
453
|
+
in the source: *"Most devs don't set this right, causing UI to disappear when a config change
|
|
454
|
+
occurs for a missing setting."* So a hand-written subset is not dangerous, just dead weight —
|
|
455
|
+
Titanium's full list always wins.
|
|
456
|
+
- **A lock on `TiTranslucentActivity`.** Android 8–11 throws `IllegalStateException` ("Only
|
|
457
|
+
fullscreen opaque activities can request orientation") when a translucent activity requests a
|
|
458
|
+
fixed orientation. Declaring `orientationModes` on those windows is safer: the SDK applies it
|
|
459
|
+
inside a try/catch (`TiWindowProxy.setOrientationModes`), so the worst case is a modal that
|
|
460
|
+
rotates instead of an app that crashes.
|
|
461
|
+
|
|
462
|
+
### Prefer orientationModes over the manifest
|
|
463
|
+
|
|
464
|
+
`orientationModes` is defined on `TiWindowProxy`, so the same declaration works on `Window`,
|
|
465
|
+
`TabGroup` and `NavigationWindow`. Being per-window it also composes with form-factor queries,
|
|
466
|
+
which is what you usually want:
|
|
467
|
+
|
|
468
|
+
```xml
|
|
469
|
+
<!-- PurgeTSS -->
|
|
470
|
+
<TabGroup class="handheld:portrait tablet:landscape">
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
```tss
|
|
474
|
+
/* plain TSS equivalent */
|
|
475
|
+
'#tabs[formFactor=handheld]': { orientationModes: [ Ti.UI.PORTRAIT ] }
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
Alloy resolves `formFactor` from `Ti.Platform.Android.physicalSizeCategory`, which is why this
|
|
479
|
+
is more reliable than any width comparison you could write yourself.
|
|
480
|
+
|
|
481
|
+
Keep the manifest for what Alloy cannot reach: the root activity, which shows the splash before
|
|
482
|
+
any JS runs, and optionally `TiActivity` as a blanket fallback for windows that carry no class.
|
|
483
|
+
|
|
484
|
+
A single Ti Element rule covers every window in the app without touching a single view, which
|
|
485
|
+
is usually all an Alloy app needs:
|
|
486
|
+
|
|
487
|
+
```javascript
|
|
488
|
+
// purgetss/config.cjs
|
|
489
|
+
module.exports = {
|
|
490
|
+
theme: { extend: {} },
|
|
491
|
+
'Window': { handheld: { apply: 'portrait' } }
|
|
492
|
+
}
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
```tss
|
|
496
|
+
/* generated — applies to every Window, including modal ones */
|
|
497
|
+
'Window[formFactor=handheld]': { orientationModes: [ Ti.UI.PORTRAIT ] }
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
Note it matches `Window` only, so a `TabGroup` still needs its own class. And because the
|
|
501
|
+
declaration is per-window, it does nothing for the splash — that one stays in the manifest.
|
|
502
|
+
|
|
503
|
+
### Debugging a rotation that shouldn't happen
|
|
504
|
+
|
|
505
|
+
Ask the system for the effective value instead of re-reading the manifest:
|
|
506
|
+
|
|
507
|
+
```bash
|
|
508
|
+
adb shell dumpsys activity activities | grep -E "topResumedActivity|requestedOrientation"
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
If `requestedOrientation` disagrees with what tiapp.xml declares, something overrode it at
|
|
512
|
+
runtime — grep the app for `requestedOrientation` and `orientationModes` before touching the
|
|
513
|
+
manifest again. Note that an *existing* `orientationModes` can be the bug rather than the fix:
|
|
514
|
+
an empty array unlocks rotation (see the anti-patterns above).
|
|
515
|
+
|
|
516
|
+
Before blaming the OS, let the SDK tell you. Titanium logs a warning when Android drops the
|
|
517
|
+
request on a large screen, so this either hits or rules that cause out immediately:
|
|
518
|
+
|
|
519
|
+
```bash
|
|
520
|
+
adb logcat | grep "Orientation request will be ignored"
|
|
521
|
+
# Fixed orientation is not supported on large screens (smallest width: <N>dp).
|
|
522
|
+
# Orientation request will be ignored on Android 16+.
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
And to see the size bucket the device actually reports — `normal` means a phone, so the >= 600dp
|
|
526
|
+
rule does not apply and the cause is in the app:
|
|
527
|
+
|
|
528
|
+
```bash
|
|
529
|
+
adb shell am get-config # e.g. ...-sw360dp-w360dp-h802dp-normal-...
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
To confirm what actually shipped in the binary:
|
|
533
|
+
|
|
534
|
+
```bash
|
|
535
|
+
aapt2 dump xmltree --file AndroidManifest.xml <apk> | grep -A2 "E: activity"
|
|
536
|
+
```
|
|
377
537
|
|
|
378
538
|
### iOS: support all orientations on iPad
|
|
379
539
|
|
|
@@ -405,6 +565,9 @@ To verify the exact generated activity name, check `build/android/AndroidManifes
|
|
|
405
565
|
| Device screen size | `Ti.Platform.displayCaps.platformWidth` — pixels on Android, needs `/logicalDensityFactor` |
|
|
406
566
|
| Orientation (boolean) | `Ti.Gesture.portrait`, `Ti.Gesture.landscape` — properties, not methods |
|
|
407
567
|
| Orientation event | `Ti.Gesture.addEventListener('orientationchange', handler)` |
|
|
568
|
+
| Lock orientation | `orientationModes` on the window — not `activity.requestedOrientation` |
|
|
569
|
+
| Tablet detection | `Alloy.isTablet` / `[formFactor=tablet]` — never a pixel threshold |
|
|
570
|
+
| Effective orientation at runtime | `adb shell dumpsys activity activities \| grep requestedOrientation` |
|
|
408
571
|
| Window resize event | `$.win.addEventListener('postlayout', handler)` — fires on rotation, split-screen, resize |
|
|
409
572
|
| Auto-size | `Ti.UI.SIZE` (fit content), `Ti.UI.FILL` (fill remaining) |
|
|
410
573
|
| Percentages | `width: '50%'` relative to parent |
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ti-guides
|
|
3
|
+
description: 'Use when working on Titanium SDK fundamentals: project configuration (`tiapp.xml`, `timodule.xml`), CommonJS modules, Hyperloop native access, app distribution (App Store / Google Play, certificates, provisioning), Titanium CLI usage, memory management, bridge optimization, SQLite transactions, image memory, or coding standards. Triggers include `tiapp.xml` in the project root, for either Alloy (`app/`) or Classic (`Resources/`) Titanium projects.'
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# ti-guides
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
Reference for Titanium SDK fundamentals: project configuration, distribution, native access via Hyperloop, CommonJS module patterns, memory and bridge optimization, and TiDev coding standards. Applies to both Alloy and Classic projects.
|
|
11
|
+
|
|
12
|
+
## When to use
|
|
13
|
+
|
|
14
|
+
Use this skill when:
|
|
15
|
+
|
|
16
|
+
- The project contains `tiapp.xml` (definitive Titanium project indicator)
|
|
17
|
+
- Configuring `tiapp.xml` or `timodule.xml`
|
|
18
|
+
- Setting up app distribution (App Store / Google Play, certificates, provisioning, AAB / IPA)
|
|
19
|
+
- Using Hyperloop to call native APIs (Objective-C / Swift / Java)
|
|
20
|
+
- Optimizing memory: removing global listeners, caching native properties, image memory footprints
|
|
21
|
+
- Working with the Titanium CLI (`ti build`, `ti sdk install`, `ti sdk select`)
|
|
22
|
+
- Writing CommonJS modules (stateful caching, ES6+ patterns, antipatterns)
|
|
23
|
+
- Authoring `AndroidManifest.xml` overrides
|
|
24
|
+
- Following TiDev style and naming conventions
|
|
25
|
+
|
|
26
|
+
Do NOT use for:
|
|
27
|
+
|
|
28
|
+
- Alloy MVC concepts, controllers, models (use `alloy-guides`)
|
|
29
|
+
- Alloy CLI usage and configuration files (use `alloy-howtos`)
|
|
30
|
+
- Looking up specific Titanium API surfaces (use `ti-api`)
|
|
31
|
+
- Native module dependency updates (use `ti-module-update`)
|
|
32
|
+
|
|
33
|
+
## Quick reference
|
|
34
|
+
|
|
35
|
+
| Topic | Reference |
|
|
36
|
+
|---|---|
|
|
37
|
+
| Project creation, structure, getting started (Alloy or Classic) | [hello-world.md](references/hello-world.md) |
|
|
38
|
+
| JavaScript fundamentals, learning resources, ES6+ features | [javascript-primer.md](references/javascript-primer.md) |
|
|
39
|
+
| Alloy vs Classic Titanium, framework selection | [application-frameworks.md](references/application-frameworks.md) |
|
|
40
|
+
| Memory leaks, bridge efficiency, event naming, security, lazy loading | [coding-best-practices.md](references/coding-best-practices.md) |
|
|
41
|
+
| Stateful modules, caching, ES6+ support, antipatterns | [commonjs-advanced.md](references/commonjs-advanced.md) |
|
|
42
|
+
| SQLite transactions and image memory optimization | [advanced-data-and-images.md](references/advanced-data-and-images.md) |
|
|
43
|
+
| Hyperloop: Objective-C / Swift / Java syntax, casting, debugging, XIB / Storyboards | [hyperloop-native-access.md](references/hyperloop-native-access.md) |
|
|
44
|
+
| Naming standards and formatting rules | [style-and-conventions.md](references/style-and-conventions.md) |
|
|
45
|
+
| ECMAScript, iOS, and Alloy reserved keywords to avoid | [reserved-words.md](references/reserved-words.md) |
|
|
46
|
+
| Custom `AndroidManifest.xml`, permissions, manifest merge | [android-manifest.md](references/android-manifest.md) |
|
|
47
|
+
| Google Play (APK / AAB), App Store (IPA), certificates, provisioning, deployment | [app-distribution.md](references/app-distribution.md) |
|
|
48
|
+
| Complete `tiapp.xml` and `timodule.xml` reference | [tiapp-config.md](references/tiapp-config.md) |
|
|
49
|
+
| Titanium CLI commands, options, tasks, configuration, build processes | [cli-reference.md](references/cli-reference.md) |
|
|
50
|
+
| Community support, modules, sample code, Slack, learning materials | [resources.md](references/resources.md) |
|
|
51
|
+
|
|
52
|
+
## Core practices
|
|
53
|
+
|
|
54
|
+
1. Validate that the project follows a modular pattern (CommonJS or Alloy).
|
|
55
|
+
2. Ensure global listeners are removed and heavy objects are nulled during cleanup.
|
|
56
|
+
3. Cache frequently accessed native properties to reduce bridge crossings.
|
|
57
|
+
4. Use Hyperloop for specialized native functionality and handle casting and threading correctly.
|
|
58
|
+
5. Use transactions for database work and manage image memory footprints.
|
|
59
|
+
|
|
60
|
+
## Procedural rules
|
|
61
|
+
|
|
62
|
+
- Always remove `Ti.App` and `Ti.Geolocation` listeners during controller cleanup.
|
|
63
|
+
- Do not access `Ti.Platform` or `Ti.DisplayCaps` inside loops. Store values in local variables.
|
|
64
|
+
- Concatenate Hyperloop selectors accurately (for example, `addAttribute:value:range:` → `addAttributeValueRange`).
|
|
65
|
+
- Close result sets and database handles after every transaction block.
|
|
66
|
+
|
|
67
|
+
## Response format
|
|
68
|
+
|
|
69
|
+
When answering Titanium SDK questions:
|
|
70
|
+
|
|
71
|
+
1. **Technical recommendation** — cite the specific TiDev best practice.
|
|
72
|
+
2. **Optimized implementation** — provide modern ES6+ code.
|
|
73
|
+
3. **Rationale** — briefly explain the performance or memory impact.
|
|
74
|
+
|
|
75
|
+
## Related skills
|
|
76
|
+
|
|
77
|
+
| Task | Use this skill |
|
|
78
|
+
|---|---|
|
|
79
|
+
| Alloy MVC concepts, controllers, models, data binding | `alloy-guides` |
|
|
80
|
+
| Alloy CLI, `alloy.jmk`, `config.json`, debugging | `alloy-howtos` |
|
|
81
|
+
| Titanium API lookup (Ti.UI, Ti.Network, modules) | `ti-api` |
|
|
82
|
+
| Native module dependency updates | `ti-module-update` |
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# Advanced database and image best practices
|
|
2
|
+
|
|
3
|
+
## 1. SQLite optimization
|
|
4
|
+
|
|
5
|
+
- Close result sets and the database after each operation to avoid locks and memory growth. SQLite allows one writer at a time, and leaving a connection open after `INSERT` or `UPDATE` can trigger `DatabaseObjectNotClosed` on the next write.
|
|
6
|
+
- Use transactions for batch inserts. If any statement fails, SQLite rolls back the entire batch.
|
|
7
|
+
```javascript
|
|
8
|
+
const db = Ti.Database.open('myDatabase');
|
|
9
|
+
db.execute('BEGIN');
|
|
10
|
+
for (const item of playlist) {
|
|
11
|
+
db.execute('INSERT INTO albums (disc, artist, rating) VALUES (?, ?, ?)', item.disc, item.artist, item.rating);
|
|
12
|
+
}
|
|
13
|
+
db.execute('COMMIT');
|
|
14
|
+
db.close();
|
|
15
|
+
```
|
|
16
|
+
- Do not ship large pre-populated databases. Ship a small skeleton and download data on first boot to reduce IPA/APK size. The `Resources` directory is read-only, so installing a database copies it to `applicationDataDirectory`, which creates two copies on the device.
|
|
17
|
+
- Android note: in Android 2.2 and earlier, the installer could not uncompress assets over 1 MB. One workaround was to rename the file with a `.mp3` extension to prevent `aapt` compression.
|
|
18
|
+
- Pattern for downloading a replacement database on first boot:
|
|
19
|
+
```javascript
|
|
20
|
+
const updateDatabase = (newData) => {
|
|
21
|
+
// delete existing content and rehydrate from downloaded JSON
|
|
22
|
+
}
|
|
23
|
+
downloadButton.addEventListener('click', () => {
|
|
24
|
+
const client = Ti.Network.createHTTPClient({
|
|
25
|
+
timeout: 10000,
|
|
26
|
+
onload() {
|
|
27
|
+
updateDatabase(this.responseData)
|
|
28
|
+
Ti.UI.createAlertDialog({ title: 'Info', message: 'Database installed' }).show()
|
|
29
|
+
},
|
|
30
|
+
onerror(e) {
|
|
31
|
+
Ti.UI.createAlertDialog({ title: 'Error', message: e.error }).show()
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
client.open('GET', 'https://example.com/data.json')
|
|
35
|
+
client.send()
|
|
36
|
+
})
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Database version management
|
|
40
|
+
|
|
41
|
+
Store a version number in your database so you can detect upgrades and run migrations. If you do not have a version table, use `PRAGMA TABLE_INFO` to detect column presence:
|
|
42
|
+
|
|
43
|
+
```javascript
|
|
44
|
+
const addColumn = (dbname, tblName, newFieldName, colSpec) => {
|
|
45
|
+
const db = Ti.Database.open(dbname);
|
|
46
|
+
let fieldExists = false;
|
|
47
|
+
const resultSet = db.execute(`PRAGMA TABLE_INFO(${tblName})`);
|
|
48
|
+
while (resultSet.isValidRow()) {
|
|
49
|
+
if (resultSet.field(1) === newFieldName) {
|
|
50
|
+
fieldExists = true;
|
|
51
|
+
}
|
|
52
|
+
resultSet.next();
|
|
53
|
+
}
|
|
54
|
+
resultSet.close();
|
|
55
|
+
if (!fieldExists) {
|
|
56
|
+
db.execute(`ALTER TABLE ${tblName} ADD COLUMN ${newFieldName} ${colSpec}`);
|
|
57
|
+
}
|
|
58
|
+
db.close();
|
|
59
|
+
};
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## 2. Image format selection
|
|
65
|
+
|
|
66
|
+
Choose the right format for each use case:
|
|
67
|
+
|
|
68
|
+
| Format | Compression | Best for | Avoid for |
|
|
69
|
+
| ------ | --------------------- | ------------------------------ | ------------------------------------------------------------------ |
|
|
70
|
+
| PNG | Lossless | Icons, text, line art, buttons | Photos (large files) |
|
|
71
|
+
| JPG | Lossy | Photographs | Text, icons, line drawings (artifacts) |
|
|
72
|
+
| GIF | Lossless (256 colors) | Rare cases | Most cases (limited colors, animated GIF not supported everywhere) |
|
|
73
|
+
|
|
74
|
+
For flip-book animations, use `ImageView.images` with an array of PNG or optimized JPG files instead of animated GIFs.
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## 3. Image memory management
|
|
79
|
+
|
|
80
|
+
- A JPG is small on disk but expands to `width * height * 3 bytes` in RAM. Each pixel uses 24 bits (8 bits per R/G/B channel).
|
|
81
|
+
- Example: `(640 × 480 × 3) / 1024 = 900 KB` in memory for a single image
|
|
82
|
+
- Device RAM available to your app can be as low as 12 MB, shared with your code and the Titanium framework
|
|
83
|
+
- Unloading:
|
|
84
|
+
- `view.remove(imageView)` removes it from the view hierarchy and lets the OS free memory
|
|
85
|
+
- `imageView = null` releases the native proxy object
|
|
86
|
+
- Resizing: resize images to their display dimensions using `imageAsResized` or `imageAsCropped` (both on `Ti.Blob`) before setting the `image` property. Do not display a 1024×768 image on a 640×480 screen.
|
|
87
|
+
- Placeholder: set `defaultImage` on ImageView to show a local placeholder while a remote image downloads:
|
|
88
|
+
```javascript
|
|
89
|
+
const imageView = Ti.UI.createImageView({
|
|
90
|
+
image: 'https://example.com/photo.jpg',
|
|
91
|
+
defaultImage: '/images/placeholder.png',
|
|
92
|
+
width: 300,
|
|
93
|
+
height: 200
|
|
94
|
+
});
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## 4. Image optimization tools
|
|
100
|
+
|
|
101
|
+
Resize and compress images before including them in your app to reduce IPA/APK size and network usage.
|
|
102
|
+
|
|
103
|
+
| Platform | File types | Tool |
|
|
104
|
+
| ------------------- | ------------- | ----------- |
|
|
105
|
+
| Mac | PNG, JPG, GIF | ImageOptim |
|
|
106
|
+
| Mac, Windows, Linux | PNG, JPG, GIF | ImageMagick |
|
|
107
|
+
| Windows/DOS | PNG | PNGCrush |
|
|
108
|
+
| Windows | JPG | Nikkho |
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## 5. Caching remote images
|
|
113
|
+
|
|
114
|
+
Remote images are cached automatically on iOS, Android, and Windows.
|
|
115
|
+
|
|
116
|
+
Platform differences:
|
|
117
|
+
- Android: cache limited to 25 MB, persists for the lifetime of the app (while installed)
|
|
118
|
+
- iOS: cache size not fixed, cleared by iOS when the device needs storage
|
|
119
|
+
|
|
120
|
+
To manually clean the cache, delete files in `applicationCacheDirectory`.
|
|
121
|
+
|
|
122
|
+
Android 6+ requirement: call `Ti.Filesystem.requestStoragePermissions()` before loading remote images.
|
|
123
|
+
|
|
124
|
+
### Manual caching utility
|
|
125
|
+
|
|
126
|
+
For critical assets, cache manually in `applicationDataDirectory` for more predictable behavior:
|
|
127
|
+
|
|
128
|
+
```javascript
|
|
129
|
+
const CacheUtils = {
|
|
130
|
+
getExtension: (fn) => {
|
|
131
|
+
const match = /(?:\.([^.]+))?$/.exec(fn);
|
|
132
|
+
return match[1] || '';
|
|
133
|
+
},
|
|
134
|
+
|
|
135
|
+
createCachedImageView: (options) => {
|
|
136
|
+
const md5 = Ti.Utils.md5HexDigest(options.image) + CacheUtils.getExtension(options.image);
|
|
137
|
+
const savedFile = Ti.Filesystem.getFile(Ti.Filesystem.applicationDataDirectory, md5);
|
|
138
|
+
|
|
139
|
+
if (savedFile.exists()) {
|
|
140
|
+
options.image = savedFile;
|
|
141
|
+
return Ti.UI.createImageView(options);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const imageView = Ti.UI.createImageView(options);
|
|
145
|
+
const saveImage = () => {
|
|
146
|
+
imageView.removeEventListener('load', saveImage);
|
|
147
|
+
savedFile.write(
|
|
148
|
+
Ti.UI.createImageView({ image: imageView.image, width: Ti.UI.FILL, height: Ti.UI.FILL }).toImage()
|
|
149
|
+
);
|
|
150
|
+
};
|
|
151
|
+
imageView.addEventListener('load', saveImage);
|
|
152
|
+
return imageView;
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
```
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Custom AndroidManifest.xml
|
|
2
|
+
|
|
3
|
+
For most Android manifest settings, use `tiapp.xml`. Only create a custom manifest when you really need it.
|
|
4
|
+
|
|
5
|
+
## Preferred method: tiapp.xml
|
|
6
|
+
|
|
7
|
+
Add manifest entries in `tiapp.xml` inside the `<android>` section:
|
|
8
|
+
|
|
9
|
+
```xml
|
|
10
|
+
<android xmlns:android="http://schemas.android.com/apk/res/android">
|
|
11
|
+
<manifest>
|
|
12
|
+
<!-- SDK version -->
|
|
13
|
+
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="34" />
|
|
14
|
+
|
|
15
|
+
<!-- Screen support -->
|
|
16
|
+
<supports-screens
|
|
17
|
+
android:smallScreens="false"
|
|
18
|
+
android:normalScreens="true"
|
|
19
|
+
android:largeScreens="true"
|
|
20
|
+
android:xlargeScreens="true"
|
|
21
|
+
/>
|
|
22
|
+
|
|
23
|
+
<!-- Permissions -->
|
|
24
|
+
<uses-permission android:name="android.permission.CAMERA" />
|
|
25
|
+
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
|
26
|
+
|
|
27
|
+
<!-- Application attributes (merged, not replaced) -->
|
|
28
|
+
<application android:debuggable="true" />
|
|
29
|
+
</manifest>
|
|
30
|
+
</android>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## How merging works
|
|
34
|
+
|
|
35
|
+
- Most elements are replaced by your entry.
|
|
36
|
+
- The `<application>` element is merged additively by attribute.
|
|
37
|
+
- Activities and services are added to existing ones.
|
|
38
|
+
|
|
39
|
+
## Custom manifest (rare cases)
|
|
40
|
+
|
|
41
|
+
If you need full control, create `platform/android/AndroidManifest.xml`:
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
MyApp/
|
|
45
|
+
├── app/
|
|
46
|
+
├── platform/
|
|
47
|
+
│ └── android/
|
|
48
|
+
│ └── AndroidManifest.xml # Custom manifest
|
|
49
|
+
└── tiapp.xml
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Warning: you must maintain this file manually through SDK updates.
|
|
53
|
+
|
|
54
|
+
## Common use cases
|
|
55
|
+
|
|
56
|
+
### Camera permission
|
|
57
|
+
```xml
|
|
58
|
+
<uses-permission android:name="android.permission.CAMERA" />
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Hardware requirements
|
|
62
|
+
```xml
|
|
63
|
+
<uses-feature android:name="android.hardware.camera" android:required="false" />
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Launch mode
|
|
67
|
+
```xml
|
|
68
|
+
<application android:launchMode="singleTask" />
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Deep links
|
|
72
|
+
```xml
|
|
73
|
+
<intent-filter>
|
|
74
|
+
<action android:name="android.intent.action.VIEW" />
|
|
75
|
+
<category android:name="android.intent.category.DEFAULT" />
|
|
76
|
+
<category android:name="android.intent.category.BROWSABLE" />
|
|
77
|
+
<data android:scheme="myapp" android:host="path" />
|
|
78
|
+
</intent-filter>
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Debugging
|
|
82
|
+
|
|
83
|
+
To see the generated manifest:
|
|
84
|
+
```bash
|
|
85
|
+
ti build -p android
|
|
86
|
+
# Check: build/android/AndroidManifest.xml.gen
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Enable manifest merge (CLI 7.0+)
|
|
90
|
+
|
|
91
|
+
If you use a custom manifest, enable merge in `tiapp.xml`:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
ti config android.mergeCustomAndroidManifest true
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
This merges your custom manifest with the generated one instead of replacing it.
|