@craft-native/android 0.0.72

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 ADDED
@@ -0,0 +1,606 @@
1
+ # @craft-native/android
2
+
3
+ Build native Android apps with web technologies using Craft.
4
+
5
+ ## Features
6
+
7
+ ### Core
8
+
9
+ - **WebView** - Native Android WebView with full JavaScript support
10
+ - **Dark Mode** - Native dark/light theme support
11
+
12
+ ### Input & Feedback
13
+
14
+ - **Native Speech Recognition** - Android SpeechRecognizer
15
+ - **Haptic Feedback** - Vibration patterns for different feedback types
16
+ - **Audio Recording** - MediaRecorder with compressed output
17
+ - **Video Recording** - Camera video capture
18
+
19
+ ### Device Access
20
+
21
+ - **Camera & Gallery** - Take photos or pick from gallery
22
+ - **Barcode/QR Scanner** - ML Kit barcode scanning
23
+ - **File Picker** - Document picker for any file type
24
+ - **File Download** - DownloadManager integration
25
+
26
+ ### Sensors & Location
27
+
28
+ - **Geolocation** - GPS and network location via FusedLocationProvider
29
+ - **Motion Sensors** - Accelerometer & gyroscope via SensorManager
30
+ - **NFC** - Read NFC tags via NfcAdapter
31
+
32
+ ### Communication
33
+
34
+ - **Share Sheet** - Native Android sharing
35
+ - **Clipboard** - Read and write to system clipboard
36
+ - **Push Notifications** - Firebase Cloud Messaging ready
37
+
38
+ ### Security & Auth
39
+
40
+ - **Biometric Auth** - Fingerprint / Face unlock
41
+ - **Social Auth** - Google Sign In
42
+ - **Secure Storage** - EncryptedSharedPreferences
43
+
44
+ ### Data & Storage
45
+
46
+ - **Local Database** - SQLite with full SQL support
47
+ - **Bluetooth LE** - BluetoothLeScanner
48
+
49
+ ### System
50
+
51
+ - **Device Info** - Device model, OS version, screen info
52
+ - **Network Status** - Connection type monitoring (WiFi/Cellular)
53
+ - **App Badge** - Notification badge count
54
+ - **App State** - Foreground/background detection
55
+ - **Flashlight** - Camera flash control
56
+ - **Open URL** - External browser launch
57
+ - **Vibration Pattern** - Custom vibration sequences
58
+ - **App Review** - Google Play In-App Review prompt
59
+ - **Screen Capture** - Take screenshots programmatically
60
+ - **Health/Fitness** - Google Fit integration
61
+
62
+ ## Installation
63
+
64
+ The Android support is built into the main `craft` CLI:
65
+
66
+ ```bash
67
+ bun add craft-native
68
+ ```
69
+
70
+ ## Quick Start
71
+
72
+ ### 1. Initialize Project
73
+
74
+ ```bash
75
+ craft android init MyApp --package com.example.myapp
76
+ cd android
77
+ ```
78
+
79
+ ### 2. Add Your Web Content
80
+
81
+ Replace `app/src/main/assets/index.html` with your web app, or point to a dev server:
82
+
83
+ ```bash
84
+ craft android build --html-path ../dist/index.html
85
+ # or
86
+ craft android build --dev-server http://192.168.1.100:3456
87
+ ```
88
+
89
+ ### 3. Open in Android Studio
90
+
91
+ ```bash
92
+ craft android open
93
+ ```
94
+
95
+ ### 4. Run on Device
96
+
97
+ ```bash
98
+ craft android run
99
+ # or specify device
100
+ craft android run --device emulator-5554
101
+ ```
102
+
103
+ ## Configuration
104
+
105
+ Edit `craft.config.json` in your Android project:
106
+
107
+ ```json
108
+ {
109
+ "appName": "MyApp",
110
+ "packageName": "com.example.myapp",
111
+ "version": "1.0.0",
112
+ "versionCode": 1,
113
+ "darkMode": true,
114
+ "backgroundColor": "#1a1a2e",
115
+ "enableSpeechRecognition": true,
116
+ "enableHaptics": true,
117
+ "enableShare": true,
118
+ "enableCamera": true,
119
+ "enableBiometric": true,
120
+ "enablePushNotifications": false,
121
+ "enableSecureStorage": true,
122
+ "enableGeolocation": true,
123
+ "enableClipboard": true,
124
+ "enableNetworkStatus": true,
125
+ "enableAppReview": true,
126
+ "enableFlashlight": true,
127
+ "enableQRScanner": true,
128
+ "enableFilePicker": true,
129
+ "enableFileDownload": true,
130
+ "enableSocialAuth": true,
131
+ "enableAudioRecording": true,
132
+ "enableVideoRecording": true,
133
+ "enableMotionSensors": true,
134
+ "enableLocalDatabase": true,
135
+ "enableBluetooth": true,
136
+ "enableNFC": true,
137
+ "enableFitness": false,
138
+ "enableScreenCapture": true,
139
+ "minSdk": 24,
140
+ "targetSdk": 34
141
+ }
142
+ ```
143
+
144
+ ## JavaScript Bridge
145
+
146
+ Once Craft is initialized, the `window.craft` object is available:
147
+
148
+ ```javascript
149
+ // Wait for Craft to be ready
150
+ window.addEventListener('craftReady', (e) => {
151
+ console.log('Platform:', e.detail.platform); // 'android'
152
+ console.log('Capabilities:', e.detail.capabilities);
153
+ });
154
+
155
+ // Haptic feedback
156
+ window.craft.haptic('light'); // light, medium, heavy
157
+ window.craft.haptic('success'); // success, warning, error
158
+ window.craft.haptic('selection');
159
+
160
+ // Speech recognition
161
+ window.craft.startListening();
162
+ window.craft.stopListening();
163
+
164
+ // Listen for speech events
165
+ window.addEventListener('craftSpeechStart', () => { /* recording started */ });
166
+ window.addEventListener('craftSpeechResult', (e) => {
167
+ console.log(e.detail.transcript); // "hello world"
168
+ console.log(e.detail.isFinal); // true/false
169
+ });
170
+ window.addEventListener('craftSpeechEnd', () => { /* recording stopped */ });
171
+ window.addEventListener('craftSpeechError', (e) => {
172
+ console.error(e.detail.error);
173
+ });
174
+
175
+ // Share
176
+ window.craft.share('Check out this app!', 'My App');
177
+
178
+ // Camera & Gallery
179
+ const imageBase64 = await window.craft.openCamera();
180
+ const selectedImage = await window.craft.pickImage();
181
+
182
+ // Barcode/QR Scanner (ML Kit)
183
+ const scannedCode = await window.craft.scanQRCode();
184
+ console.log(scannedCode); // "https://example.com"
185
+
186
+ // File Picker
187
+ const file = await window.craft.pickFile(['image/*', 'application/pdf']);
188
+ console.log(file.name, file.data); // base64 encoded
189
+
190
+ // File Download
191
+ await window.craft.downloadFile('https://example.com/file.pdf', 'document.pdf');
192
+ await window.craft.saveFile(base64Data, 'image.png', 'image/png');
193
+
194
+ // Social Auth (Google Sign In)
195
+ const user = await window.craft.signInWithGoogle();
196
+ console.log(user.userId, user.email, user.displayName, user.idToken);
197
+
198
+ // Audio Recording
199
+ await window.craft.startAudioRecording();
200
+ const audioBase64 = await window.craft.stopAudioRecording();
201
+
202
+ // Video Recording
203
+ const videoBase64 = await window.craft.startVideoRecording();
204
+
205
+ // Motion Sensors
206
+ window.craft.startMotionUpdates();
207
+ window.addEventListener('craftMotionUpdate', (e) => {
208
+ console.log('Accelerometer:', e.detail.accelerometer); // {x, y, z}
209
+ console.log('Gyroscope:', e.detail.gyroscope); // {x, y, z}
210
+ });
211
+ window.craft.stopMotionUpdates();
212
+
213
+ // Local Database (SQLite)
214
+ await window.craft.db.execute('CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)');
215
+ await window.craft.db.execute('INSERT INTO users (name) VALUES (?)', ['Alice']);
216
+ const users = await window.craft.db.query('SELECT * FROM users');
217
+
218
+ // Bluetooth LE Scanning
219
+ window.craft.startBluetoothScan();
220
+ window.addEventListener('craftBluetoothDevice', (e) => {
221
+ console.log(e.detail.name, e.detail.address, e.detail.rssi);
222
+ });
223
+ window.craft.stopBluetoothScan();
224
+
225
+ // NFC Tag Reading
226
+ const nfcData = await window.craft.scanNFC();
227
+ console.log(nfcData); // Tag content
228
+
229
+ // Health/Fitness (Google Fit)
230
+ await window.craft.requestFitnessAuthorization();
231
+ const steps = await window.craft.getFitnessData('steps', startDate, endDate);
232
+
233
+ // Screen Capture
234
+ const screenshotBase64 = await window.craft.takeScreenshot();
235
+
236
+ // Biometric authentication
237
+ try {
238
+ const authenticated = await window.craft.authenticate('Confirm your identity');
239
+ if (authenticated) {
240
+ console.log('User authenticated!');
241
+ }
242
+ } catch (error) {
243
+ console.log('Authentication failed:', error.message);
244
+ }
245
+
246
+ // Push notifications
247
+ const token = await window.craft.registerPush();
248
+ console.log('Push token:', token);
249
+
250
+ // Secure storage (encrypted)
251
+ await window.craft.secureStore.set('api*key', 'secret123');
252
+ const apiKey = await window.craft.secureStore.get('api*key');
253
+ await window.craft.secureStore.remove('api*key');
254
+
255
+ // Geolocation
256
+ const position = await window.craft.getCurrentPosition();
257
+ console.log(position.latitude, position.longitude);
258
+
259
+ // Watch position (continuous updates)
260
+ const watchId = window.craft.watchPosition((position) => {
261
+ console.log('New position:', position);
262
+ });
263
+ window.craft.clearWatch(watchId);
264
+
265
+ // Clipboard
266
+ await window.craft.clipboard.write('Hello World');
267
+ const text = await window.craft.clipboard.read();
268
+
269
+ // Device Info
270
+ const device = window.craft.getDeviceInfo();
271
+ console.log(device.model, device.manufacturer, device.systemVersion);
272
+
273
+ // App Badge
274
+ window.craft.setBadge(5);
275
+ window.craft.clearBadge();
276
+
277
+ // Network Status
278
+ const network = window.craft.getNetworkStatus();
279
+ console.log(network.isConnected, network.type); // true, 'wifi'
280
+
281
+ // Network change listener
282
+ window.craft.onNetworkChange((status) => {
283
+ console.log('Network changed:', status);
284
+ });
285
+ window.craft.offNetworkChange();
286
+
287
+ // App Review (Google Play In-App Review)
288
+ await window.craft.requestReview();
289
+
290
+ // Flashlight
291
+ window.craft.setFlashlight(true); // turn on
292
+ window.craft.setFlashlight(false); // turn off
293
+ window.craft.toggleFlashlight(); // toggle
294
+
295
+ // Open URL (external browser)
296
+ window.craft.openURL('https://example.com');
297
+
298
+ // Vibration Pattern (custom durations in ms)
299
+ window.craft.vibrate([100, 50, 100, 50, 200]); // [on, off, on, off, on]
300
+
301
+ // App State
302
+ const state = window.craft.getAppState(); // 'active', 'inactive', 'background'
303
+
304
+ // App state change listener
305
+ window.craft.onAppStateChange((state) => {
306
+ console.log('App state:', state);
307
+ });
308
+ window.craft.offAppStateChange();
309
+
310
+ // Logging (appears in Logcat)
311
+ window.craft.log('Debug message');
312
+
313
+ // ==================== High Value Bridges ====================
314
+
315
+ // Contacts
316
+ const contacts = await window.craft.getContacts();
317
+ console.log(contacts); // [{id, displayName, phoneNumbers, emailAddresses}]
318
+
319
+ const newContactId = await window.craft.addContact({
320
+ displayName: 'John Doe',
321
+ phone: '+1234567890',
322
+ email: 'john@example.com'
323
+ });
324
+
325
+ // Calendar Events
326
+ const events = await window.craft.getCalendarEvents(startDateMs, endDateMs);
327
+ console.log(events); // [{id, title, description, startDate, endDate, location, isAllDay}]
328
+
329
+ const newEventId = await window.craft.createCalendarEvent({
330
+ title: 'Meeting',
331
+ description: 'Discuss project',
332
+ location: 'Office',
333
+ startDate: Date.now(),
334
+ endDate: Date.now() + 3600000,
335
+ isAllDay: false
336
+ });
337
+
338
+ await window.craft.deleteCalendarEvent(eventId);
339
+
340
+ // Local Notifications
341
+ const notificationId = await window.craft.scheduleNotification({
342
+ id: 'reminder-1',
343
+ title: 'Reminder',
344
+ body: 'Don\'t forget!',
345
+ timestamp: Date.now() + 60000, // 1 minute from now
346
+ // or use delay: 60000 // delay in ms
347
+ });
348
+
349
+ await window.craft.cancelNotification('reminder-1');
350
+ await window.craft.cancelAllNotifications();
351
+
352
+ // In-App Purchase (Google Play Billing)
353
+ const products = await window.craft.getProducts(['product*id*1', 'product*id*2']);
354
+ console.log(products); // [{productId, title, description, price}]
355
+
356
+ const purchaseResult = await window.craft.purchase('product*id*1');
357
+ console.log(purchaseResult); // {purchaseToken, productId, ...}
358
+
359
+ await window.craft.restorePurchases();
360
+
361
+ // Keep Screen Awake
362
+ window.craft.setKeepAwake(true); // Prevent screen dimming
363
+ window.craft.setKeepAwake(false); // Allow screen dimming
364
+
365
+ // Orientation Lock
366
+ window.craft.lockOrientation('portrait'); // Lock to portrait
367
+ window.craft.lockOrientation('landscape'); // Lock to landscape
368
+ window.craft.unlockOrientation(); // Allow all orientations
369
+
370
+ // ==================== Medium Value Bridges ====================
371
+
372
+ // Background Tasks (WorkManager)
373
+ await window.craft.backgroundTask.register('sync-data');
374
+ await window.craft.backgroundTask.schedule('sync-data', {
375
+ delay: 900, // 15 minutes
376
+ requiresNetwork: true,
377
+ requiresCharging: false
378
+ });
379
+ await window.craft.backgroundTask.cancel('sync-data');
380
+ await window.craft.backgroundTask.cancelAll();
381
+
382
+ // PDF Viewer (opens in external PDF app)
383
+ await window.craft.openPDF('https://example.com/document.pdf');
384
+ await window.craft.openPDF(base64PdfData, 5); // Open at page 5
385
+ await window.craft.closePDF();
386
+
387
+ // Contacts Picker (shows native picker UI)
388
+ const contact = await window.craft.pickContact();
389
+ console.log(contact); // {id, displayName, phoneNumbers, emailAddresses}
390
+
391
+ const contacts = await window.craft.pickContact({multiple: true}); // Select multiple
392
+
393
+ // App Shortcuts (Android 7.1+, long press)
394
+ await window.craft.shortcuts.set([
395
+ {type: 'new-message', title: 'New Message', subtitle: 'Start composing'},
396
+ {type: 'search', title: 'Search'}
397
+ ]);
398
+ window.craft.shortcuts.onShortcut((shortcut) => {
399
+ console.log('Shortcut activated:', shortcut.type);
400
+ });
401
+ await window.craft.shortcuts.clear();
402
+
403
+ // Shared Preferences (cross-app data with named groups)
404
+ await window.craft.sharedKeychain.set('user*token', 'abc123', 'mygroup'); // group optional
405
+ const result = await window.craft.sharedKeychain.get('user*token', 'mygroup');
406
+ console.log(result.value); // 'abc123'
407
+ await window.craft.sharedKeychain.remove('user*token');
408
+
409
+ // Local Auth Persistence (skip re-auth for a duration)
410
+ await window.craft.authPersistence.enable(300); // 5 minutes
411
+ const status = await window.craft.authPersistence.check();
412
+ if (status.isValid) {
413
+ console.log('Session valid for', status.remainingSeconds, 'more seconds');
414
+ }
415
+ await window.craft.authPersistence.clear();
416
+
417
+ // ==================== Nice to Have Bridges ====================
418
+
419
+ // AR (ARCore) - Note: Full ARCore requires native Activity integration
420
+ // Use Sceneform or AR Fragment for complete AR functionality
421
+ // The bridge API is consistent with iOS for cross-platform code:
422
+ await window.craft.ar.start({planeDetection: true});
423
+ // Returns error on Android: "ARCore requires native Activity integration"
424
+
425
+ // ML (ML Kit) - Full support for ML operations
426
+ // First capture an image
427
+ const image = await window.craft.openCamera();
428
+
429
+ // Image Classification (Labeling) - Identify what's in the image
430
+ const labels = await window.craft.ml.classifyImage(image);
431
+ console.log(labels); // [{label: 'Food', confidence: 0.95, index: 1}, ...]
432
+
433
+ // Object Detection - Detect and locate objects
434
+ const objects = await window.craft.ml.detectObjects(image);
435
+ console.log(objects); // [{labels: [...], boundingBox: {x, y, width, height}, trackingId: 1}]
436
+
437
+ // Text Recognition (OCR) - Extract text from image
438
+ const textResults = await window.craft.ml.recognizeText(image);
439
+ console.log(textResults); // [{text: 'Hello World', confidence: 0.98, boundingBox: {...}}]
440
+
441
+ // ==================== Widgets (AppWidgetProvider) ====================
442
+
443
+ // Update widget data - displayed on home screen widget
444
+ await window.craft.widget.update({
445
+ title: 'My App',
446
+ subtitle: 'Latest update',
447
+ value: '42',
448
+ icon: 'ic*star' // Android drawable name
449
+ });
450
+
451
+ // Reload all widgets
452
+ await window.craft.widget.reload();
453
+
454
+ // ==================== Google Assistant (App Actions) ====================
455
+
456
+ // Note: App Actions are defined in shortcuts.xml, not dynamically
457
+ // This stores action handlers for incoming intents
458
+ await window.craft.siri.register('Open my app', 'open*app');
459
+
460
+ // Remove a voice action
461
+ await window.craft.siri.remove('open*app');
462
+
463
+ // Listen for voice assistant invocations
464
+ window.craft.siri.onInvoke((detail) => {
465
+ console.log('Voice action:', detail.action);
466
+ });
467
+
468
+ // ==================== Wear OS Connectivity ====================
469
+
470
+ // Note: Full Wear OS support requires a companion watch app
471
+ // This provides the JavaScript API for future integration
472
+
473
+ // Check if watch is reachable
474
+ const status = await window.craft.watch.isReachable();
475
+ console.log(status.reachable); // true/false
476
+
477
+ // Send message to watch (requires companion app)
478
+ const reply = await window.craft.watch.send({
479
+ action: 'ping',
480
+ data: { timestamp: Date.now() }
481
+ });
482
+
483
+ // Update application context
484
+ await window.craft.watch.updateContext({
485
+ lastUpdate: Date.now(),
486
+ status: 'active'
487
+ });
488
+
489
+ // Listen for messages from watch
490
+ window.craft.watch.onMessage((message) => {
491
+ console.log('Watch message:', message);
492
+ });
493
+ ```
494
+
495
+ ## CLI Reference
496
+
497
+ ```bash
498
+ craft android init <name> # Initialize new Android project
499
+ craft android build # Build APK
500
+ craft android build --release # Build release APK
501
+ craft android build --watch # Watch for changes and rebuild
502
+ craft android open # Open in Android Studio
503
+ craft android run # Run on connected device/emulator
504
+
505
+ Options:
506
+ --package <name> Package name (e.g., com.example.app)
507
+ --html-path <path> Path to HTML file
508
+ -d, --dev-server <url> Development server URL
509
+ -o, --output <dir> Output directory (default: ./android)
510
+ --release Build release APK
511
+ -w, --watch Watch mode
512
+ -d, --device <id> Target device ID
513
+ ```
514
+
515
+ ## Requirements
516
+
517
+ - Android Studio with Android SDK
518
+ - JDK 17+
519
+ - Gradle 8.4+
520
+ - ADB (for device deployment)
521
+
522
+ ## Permissions
523
+
524
+ Add to your AndroidManifest.xml as needed:
525
+
526
+ ```xml
527
+ <!-- Speech Recognition -->
528
+ <uses-permission android:name="android.permission.RECORD*AUDIO" />
529
+
530
+ <!-- Camera -->
531
+ <uses-permission android:name="android.permission.CAMERA" />
532
+ <uses-permission android:name="android.permission.WRITE*EXTERNAL*STORAGE" />
533
+
534
+ <!-- Location -->
535
+ <uses-permission android:name="android.permission.ACCESS*FINE*LOCATION" />
536
+ <uses-permission android:name="android.permission.ACCESS*COARSE*LOCATION" />
537
+
538
+ <!-- Biometric -->
539
+ <uses-permission android:name="android.permission.USE*BIOMETRIC" />
540
+
541
+ <!-- Vibration -->
542
+ <uses-permission android:name="android.permission.VIBRATE" />
543
+
544
+ <!-- NFC -->
545
+ <uses-permission android:name="android.permission.NFC" />
546
+ <uses-feature android:name="android.hardware.nfc" android:required="false" />
547
+
548
+ <!-- Bluetooth -->
549
+ <uses-permission android:name="android.permission.BLUETOOTH" />
550
+ <uses-permission android:name="android.permission.BLUETOOTH*ADMIN" />
551
+ <uses-permission android:name="android.permission.BLUETOOTH*SCAN" />
552
+ <uses-permission android:name="android.permission.BLUETOOTH*CONNECT" />
553
+
554
+ <!-- Fitness (Google Fit) -->
555
+ <uses-permission android:name="android.permission.ACTIVITY*RECOGNITION" />
556
+
557
+ <!-- Contacts -->
558
+ <uses-permission android:name="android.permission.READ*CONTACTS" />
559
+ <uses-permission android:name="android.permission.WRITE*CONTACTS" />
560
+
561
+ <!-- Calendar -->
562
+ <uses-permission android:name="android.permission.READ*CALENDAR" />
563
+ <uses-permission android:name="android.permission.WRITE*CALENDAR" />
564
+
565
+ <!-- Notifications (Android 13+) -->
566
+ <uses-permission android:name="android.permission.POST*NOTIFICATIONS" />
567
+ ```
568
+
569
+ ## Development Mode
570
+
571
+ For hot-reload during development:
572
+
573
+ ```bash
574
+ # Terminal 1: Start your dev server
575
+ bun run dev # e.g., http://localhost:3456
576
+
577
+ # Terminal 2: Build Android with dev server (use your Mac's IP)
578
+ craft android build --dev-server http://192.168.1.100:3456
579
+
580
+ # Open in Android Studio and run on device
581
+ craft android open
582
+ ```
583
+
584
+ The app will load from your dev server instead of bundled HTML.
585
+
586
+ ## Publishing
587
+
588
+ Build release APK/AAB:
589
+
590
+ ```bash
591
+ craft publish --android
592
+ ```
593
+
594
+ This will:
595
+
596
+ 1. Build a release AAB (Android App Bundle)
597
+ 2. Output path for manual upload to Play Console
598
+
599
+ For automated uploads, integrate with fastlane:
600
+ ```bash
601
+ fastlane supply --aab ./android/app/build/outputs/bundle/release/app-release.aab
602
+ ```
603
+
604
+ ## License
605
+
606
+ MIT