@use-voltra/expo-plugin 1.3.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 +34 -0
- package/build/cjs/android/files/assets.js +259 -0
- package/build/cjs/android/files/assets.js.map +1 -0
- package/build/cjs/android/files/fonts.js +69 -0
- package/build/cjs/android/files/fonts.js.map +1 -0
- package/build/cjs/android/files/index.js +81 -0
- package/build/cjs/android/files/index.js.map +1 -0
- package/build/cjs/android/files/initialStates.js +83 -0
- package/build/cjs/android/files/initialStates.js.map +1 -0
- package/build/cjs/android/files/kotlin.js +142 -0
- package/build/cjs/android/files/kotlin.js.map +1 -0
- package/build/cjs/android/files/xml.js +247 -0
- package/build/cjs/android/files/xml.js.map +1 -0
- package/build/cjs/android/index.js +33 -0
- package/build/cjs/android/index.js.map +1 -0
- package/build/cjs/android/manifest.js +63 -0
- package/build/cjs/android/manifest.js.map +1 -0
- package/build/cjs/constants.js +48 -0
- package/build/cjs/constants.js.map +1 -0
- package/build/cjs/index.js +71 -0
- package/build/cjs/index.js.map +1 -0
- package/build/cjs/ios/eas.js +28 -0
- package/build/cjs/ios/eas.js.map +1 -0
- package/build/cjs/ios/entitlements.js +46 -0
- package/build/cjs/ios/entitlements.js.map +1 -0
- package/build/cjs/ios/index.js +39 -0
- package/build/cjs/ios/index.js.map +1 -0
- package/build/cjs/ios/infoPlist.js +51 -0
- package/build/cjs/ios/infoPlist.js.map +1 -0
- package/build/cjs/ios/pushNotifications.js +22 -0
- package/build/cjs/ios/pushNotifications.js.map +1 -0
- package/build/cjs/ios-widget/eas.js +66 -0
- package/build/cjs/ios-widget/eas.js.map +1 -0
- package/build/cjs/ios-widget/files/assets.js +199 -0
- package/build/cjs/ios-widget/files/assets.js.map +1 -0
- package/build/cjs/ios-widget/files/entitlements.js +77 -0
- package/build/cjs/ios-widget/files/entitlements.js.map +1 -0
- package/build/cjs/ios-widget/files/index.js +92 -0
- package/build/cjs/ios-widget/files/index.js.map +1 -0
- package/build/cjs/ios-widget/files/infoPlist.js +91 -0
- package/build/cjs/ios-widget/files/infoPlist.js.map +1 -0
- package/build/cjs/ios-widget/files/swift.js +256 -0
- package/build/cjs/ios-widget/files/swift.js.map +1 -0
- package/build/cjs/ios-widget/fonts.js +151 -0
- package/build/cjs/ios-widget/fonts.js.map +1 -0
- package/build/cjs/ios-widget/index.js +46 -0
- package/build/cjs/ios-widget/index.js.map +1 -0
- package/build/cjs/ios-widget/podfile.js +43 -0
- package/build/cjs/ios-widget/podfile.js.map +1 -0
- package/build/cjs/ios-widget/widgetPlist.js +99 -0
- package/build/cjs/ios-widget/widgetPlist.js.map +1 -0
- package/build/cjs/ios-widget/xcode/buildPhases.js +245 -0
- package/build/cjs/ios-widget/xcode/buildPhases.js.map +1 -0
- package/build/cjs/ios-widget/xcode/configurationList.js +91 -0
- package/build/cjs/ios-widget/xcode/configurationList.js.map +1 -0
- package/build/cjs/ios-widget/xcode/groups.js +65 -0
- package/build/cjs/ios-widget/xcode/groups.js.map +1 -0
- package/build/cjs/ios-widget/xcode/index.js +155 -0
- package/build/cjs/ios-widget/xcode/index.js.map +1 -0
- package/build/cjs/ios-widget/xcode/mainAppSettings.js +71 -0
- package/build/cjs/ios-widget/xcode/mainAppSettings.js.map +1 -0
- package/build/cjs/ios-widget/xcode/productFile.js +74 -0
- package/build/cjs/ios-widget/xcode/productFile.js.map +1 -0
- package/build/cjs/ios-widget/xcode/target.js +113 -0
- package/build/cjs/ios-widget/xcode/target.js.map +1 -0
- package/build/cjs/package.json +3 -0
- package/build/cjs/types.js +3 -0
- package/build/cjs/types.js.map +1 -0
- package/build/cjs/utils/entitlements.js +21 -0
- package/build/cjs/utils/entitlements.js.map +1 -0
- package/build/cjs/utils/fileDiscovery.js +107 -0
- package/build/cjs/utils/fileDiscovery.js.map +1 -0
- package/build/cjs/utils/fonts.js +75 -0
- package/build/cjs/utils/fonts.js.map +1 -0
- package/build/cjs/utils/logger.js +51 -0
- package/build/cjs/utils/logger.js.map +1 -0
- package/build/cjs/utils/prerender.js +191 -0
- package/build/cjs/utils/prerender.js.map +1 -0
- package/build/cjs/utils/urlScheme.js +40 -0
- package/build/cjs/utils/urlScheme.js.map +1 -0
- package/build/cjs/validation.js +231 -0
- package/build/cjs/validation.js.map +1 -0
- package/build/esm/android/files/assets.js +223 -0
- package/build/esm/android/files/assets.js.map +1 -0
- package/build/esm/android/files/fonts.js +33 -0
- package/build/esm/android/files/fonts.js.map +1 -0
- package/build/esm/android/files/index.js +77 -0
- package/build/esm/android/files/index.js.map +1 -0
- package/build/esm/android/files/initialStates.js +44 -0
- package/build/esm/android/files/initialStates.js.map +1 -0
- package/build/esm/android/files/kotlin.js +103 -0
- package/build/esm/android/files/kotlin.js.map +1 -0
- package/build/esm/android/files/xml.js +206 -0
- package/build/esm/android/files/xml.js.map +1 -0
- package/build/esm/android/index.js +29 -0
- package/build/esm/android/index.js.map +1 -0
- package/build/esm/android/manifest.js +59 -0
- package/build/esm/android/manifest.js.map +1 -0
- package/build/esm/constants.js +45 -0
- package/build/esm/constants.js.map +1 -0
- package/build/esm/index.js +69 -0
- package/build/esm/index.js.map +1 -0
- package/build/esm/ios/eas.js +24 -0
- package/build/esm/ios/eas.js.map +1 -0
- package/build/esm/ios/entitlements.js +42 -0
- package/build/esm/ios/entitlements.js.map +1 -0
- package/build/esm/ios/index.js +34 -0
- package/build/esm/ios/index.js.map +1 -0
- package/build/esm/ios/infoPlist.js +47 -0
- package/build/esm/ios/infoPlist.js.map +1 -0
- package/build/esm/ios/pushNotifications.js +18 -0
- package/build/esm/ios/pushNotifications.js.map +1 -0
- package/build/esm/ios-widget/eas.js +62 -0
- package/build/esm/ios-widget/eas.js.map +1 -0
- package/build/esm/ios-widget/files/assets.js +163 -0
- package/build/esm/ios-widget/files/assets.js.map +1 -0
- package/build/esm/ios-widget/files/entitlements.js +37 -0
- package/build/esm/ios-widget/files/entitlements.js.map +1 -0
- package/build/esm/ios-widget/files/index.js +55 -0
- package/build/esm/ios-widget/files/index.js.map +1 -0
- package/build/esm/ios-widget/files/infoPlist.js +55 -0
- package/build/esm/ios-widget/files/infoPlist.js.map +1 -0
- package/build/esm/ios-widget/files/swift.js +217 -0
- package/build/esm/ios-widget/files/swift.js.map +1 -0
- package/build/esm/ios-widget/fonts.js +111 -0
- package/build/esm/ios-widget/fonts.js.map +1 -0
- package/build/esm/ios-widget/index.js +42 -0
- package/build/esm/ios-widget/index.js.map +1 -0
- package/build/esm/ios-widget/podfile.js +39 -0
- package/build/esm/ios-widget/podfile.js.map +1 -0
- package/build/esm/ios-widget/widgetPlist.js +92 -0
- package/build/esm/ios-widget/widgetPlist.js.map +1 -0
- package/build/esm/ios-widget/xcode/buildPhases.js +208 -0
- package/build/esm/ios-widget/xcode/buildPhases.js.map +1 -0
- package/build/esm/ios-widget/xcode/configurationList.js +87 -0
- package/build/esm/ios-widget/xcode/configurationList.js.map +1 -0
- package/build/esm/ios-widget/xcode/groups.js +61 -0
- package/build/esm/ios-widget/xcode/groups.js.map +1 -0
- package/build/esm/ios-widget/xcode/index.js +118 -0
- package/build/esm/ios-widget/xcode/index.js.map +1 -0
- package/build/esm/ios-widget/xcode/mainAppSettings.js +68 -0
- package/build/esm/ios-widget/xcode/mainAppSettings.js.map +1 -0
- package/build/esm/ios-widget/xcode/productFile.js +70 -0
- package/build/esm/ios-widget/xcode/productFile.js.map +1 -0
- package/build/esm/ios-widget/xcode/target.js +108 -0
- package/build/esm/ios-widget/xcode/target.js.map +1 -0
- package/build/esm/package.json +3 -0
- package/build/esm/types.js +2 -0
- package/build/esm/types.js.map +1 -0
- package/build/esm/utils/entitlements.js +18 -0
- package/build/esm/utils/entitlements.js.map +1 -0
- package/build/esm/utils/fileDiscovery.js +71 -0
- package/build/esm/utils/fileDiscovery.js.map +1 -0
- package/build/esm/utils/fonts.js +39 -0
- package/build/esm/utils/fonts.js.map +1 -0
- package/build/esm/utils/logger.js +48 -0
- package/build/esm/utils/logger.js.map +1 -0
- package/build/esm/utils/prerender.js +152 -0
- package/build/esm/utils/prerender.js.map +1 -0
- package/build/esm/utils/urlScheme.js +37 -0
- package/build/esm/utils/urlScheme.js.map +1 -0
- package/build/esm/validation.js +193 -0
- package/build/esm/validation.js.map +1 -0
- package/build/types/android/files/assets.d.ts +26 -0
- package/build/types/android/files/assets.d.ts.map +1 -0
- package/build/types/android/files/fonts.d.ts +19 -0
- package/build/types/android/files/fonts.d.ts.map +1 -0
- package/build/types/android/files/index.d.ts +21 -0
- package/build/types/android/files/index.d.ts.map +1 -0
- package/build/types/android/files/initialStates.d.ts +14 -0
- package/build/types/android/files/initialStates.d.ts.map +1 -0
- package/build/types/android/files/kotlin.d.ts +11 -0
- package/build/types/android/files/kotlin.d.ts.map +1 -0
- package/build/types/android/files/xml.d.ts +25 -0
- package/build/types/android/files/xml.d.ts.map +1 -0
- package/build/types/android/index.d.ts +12 -0
- package/build/types/android/index.d.ts.map +1 -0
- package/build/types/android/manifest.d.ts +15 -0
- package/build/types/android/manifest.d.ts.map +1 -0
- package/build/types/constants.d.ts +29 -0
- package/build/types/constants.d.ts.map +1 -0
- package/build/types/index.d.ts +13 -0
- package/build/types/index.d.ts.map +1 -0
- package/build/types/ios/eas.d.ts +12 -0
- package/build/types/ios/eas.d.ts.map +1 -0
- package/build/types/ios/entitlements.d.ts +14 -0
- package/build/types/ios/entitlements.d.ts.map +1 -0
- package/build/types/ios/index.d.ts +19 -0
- package/build/types/ios/index.d.ts.map +1 -0
- package/build/types/ios/infoPlist.d.ts +21 -0
- package/build/types/ios/infoPlist.d.ts.map +1 -0
- package/build/types/ios/pushNotifications.d.ts +10 -0
- package/build/types/ios/pushNotifications.d.ts.map +1 -0
- package/build/types/ios-widget/eas.d.ts +15 -0
- package/build/types/ios-widget/eas.d.ts.map +1 -0
- package/build/types/ios-widget/files/assets.d.ts +13 -0
- package/build/types/ios-widget/files/assets.d.ts.map +1 -0
- package/build/types/ios-widget/files/entitlements.d.ts +17 -0
- package/build/types/ios-widget/files/entitlements.d.ts.map +1 -0
- package/build/types/ios-widget/files/index.d.ts +24 -0
- package/build/types/ios-widget/files/index.d.ts.map +1 -0
- package/build/types/ios-widget/files/infoPlist.d.ts +10 -0
- package/build/types/ios-widget/files/infoPlist.d.ts.map +1 -0
- package/build/types/ios-widget/files/swift.d.ts +15 -0
- package/build/types/ios-widget/files/swift.d.ts.map +1 -0
- package/build/types/ios-widget/fonts.d.ts +22 -0
- package/build/types/ios-widget/fonts.d.ts.map +1 -0
- package/build/types/ios-widget/index.d.ts +31 -0
- package/build/types/ios-widget/index.d.ts.map +1 -0
- package/build/types/ios-widget/podfile.d.ts +9 -0
- package/build/types/ios-widget/podfile.d.ts.map +1 -0
- package/build/types/ios-widget/widgetPlist.d.ts +20 -0
- package/build/types/ios-widget/widgetPlist.d.ts.map +1 -0
- package/build/types/ios-widget/xcode/buildPhases.d.ts +25 -0
- package/build/types/ios-widget/xcode/buildPhases.d.ts.map +1 -0
- package/build/types/ios-widget/xcode/configurationList.d.ts +20 -0
- package/build/types/ios-widget/xcode/configurationList.d.ts.map +1 -0
- package/build/types/ios-widget/xcode/groups.d.ts +15 -0
- package/build/types/ios-widget/xcode/groups.d.ts.map +1 -0
- package/build/types/ios-widget/xcode/index.d.ts +20 -0
- package/build/types/ios-widget/xcode/index.d.ts.map +1 -0
- package/build/types/ios-widget/xcode/mainAppSettings.d.ts +15 -0
- package/build/types/ios-widget/xcode/mainAppSettings.d.ts.map +1 -0
- package/build/types/ios-widget/xcode/productFile.d.ts +14 -0
- package/build/types/ios-widget/xcode/productFile.d.ts.map +1 -0
- package/build/types/ios-widget/xcode/target.d.ts +42 -0
- package/build/types/ios-widget/xcode/target.d.ts.map +1 -0
- package/build/types/types.d.ts +261 -0
- package/build/types/types.d.ts.map +1 -0
- package/build/types/utils/entitlements.d.ts +9 -0
- package/build/types/utils/entitlements.d.ts.map +1 -0
- package/build/types/utils/fileDiscovery.d.ts +13 -0
- package/build/types/utils/fileDiscovery.d.ts.map +1 -0
- package/build/types/utils/fonts.d.ts +2 -0
- package/build/types/utils/fonts.d.ts.map +1 -0
- package/build/types/utils/logger.d.ts +30 -0
- package/build/types/utils/logger.d.ts.map +1 -0
- package/build/types/utils/prerender.d.ts +25 -0
- package/build/types/utils/prerender.d.ts.map +1 -0
- package/build/types/utils/urlScheme.d.ts +10 -0
- package/build/types/utils/urlScheme.d.ts.map +1 -0
- package/build/types/validation.d.ts +17 -0
- package/build/types/validation.d.ts.map +1 -0
- package/package.json +58 -0
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { vdConvert } from 'vd-tool';
|
|
4
|
+
import { DEFAULT_ANDROID_USER_IMAGES_PATH, MAX_IMAGE_SIZE_BYTES } from '../../constants';
|
|
5
|
+
import { logger } from '../../utils/logger';
|
|
6
|
+
// ============================================================================
|
|
7
|
+
// Main Function
|
|
8
|
+
// ============================================================================
|
|
9
|
+
/**
|
|
10
|
+
* Generates all asset files for Android widgets.
|
|
11
|
+
*
|
|
12
|
+
* This creates:
|
|
13
|
+
* - res/drawable/ directory structure
|
|
14
|
+
* - Copies user images to drawable resources as Android-compatible resources
|
|
15
|
+
* - Copies widget preview images to drawable resources
|
|
16
|
+
* - Validates image sizes for widget compatibility
|
|
17
|
+
*
|
|
18
|
+
* @returns Map of widgetId to preview image drawable resource name
|
|
19
|
+
*/
|
|
20
|
+
export async function generateAndroidAssets(options) {
|
|
21
|
+
const { platformProjectRoot, projectRoot, userImagesPath = DEFAULT_ANDROID_USER_IMAGES_PATH, widgets } = options;
|
|
22
|
+
// Create res/drawable directory
|
|
23
|
+
const drawablePath = path.join(platformProjectRoot, 'app', 'src', 'main', 'res', 'drawable');
|
|
24
|
+
logger.info(`Generating Android assets in ${drawablePath}`);
|
|
25
|
+
// Collect asset paths from the user images directory (including subdirectories)
|
|
26
|
+
const assets = [];
|
|
27
|
+
const fullUserImagesPath = path.join(projectRoot, userImagesPath);
|
|
28
|
+
function collectAssetsRecursively(dirPath) {
|
|
29
|
+
if (!fs.existsSync(dirPath) || !fs.lstatSync(dirPath).isDirectory()) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const files = fs.readdirSync(dirPath);
|
|
33
|
+
for (const file of files) {
|
|
34
|
+
const sourcePath = path.join(dirPath, file);
|
|
35
|
+
const stat = fs.lstatSync(sourcePath);
|
|
36
|
+
if (stat.isDirectory()) {
|
|
37
|
+
// Recursively collect from subdirectories
|
|
38
|
+
collectAssetsRecursively(sourcePath);
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
// Add relative path to assets array
|
|
42
|
+
const relativePath = path.relative(projectRoot, sourcePath);
|
|
43
|
+
assets.push(relativePath);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
collectAssetsRecursively(fullUserImagesPath);
|
|
48
|
+
// Copy user images to drawable resources
|
|
49
|
+
const copiedImages = await copyUserImagesToAndroid(assets, projectRoot, drawablePath);
|
|
50
|
+
if (copiedImages.length > 0) {
|
|
51
|
+
logger.info(`Copied ${copiedImages.length} user image(s) to Android drawable resources`);
|
|
52
|
+
}
|
|
53
|
+
// Copy preview images to drawable resources
|
|
54
|
+
const previewImageMap = await copyPreviewImagesToAndroid(widgets, projectRoot, drawablePath);
|
|
55
|
+
if (previewImageMap.size > 0) {
|
|
56
|
+
logger.info(`Copied ${previewImageMap.size} preview image(s) to Android drawable resources`);
|
|
57
|
+
}
|
|
58
|
+
return previewImageMap;
|
|
59
|
+
}
|
|
60
|
+
// ============================================================================
|
|
61
|
+
// Image Handling
|
|
62
|
+
// ============================================================================
|
|
63
|
+
// Android supports these image/drawable extensions
|
|
64
|
+
const VALID_EXTENSIONS = ['.png', '.jpg', '.jpeg', '.gif', '.webp', '.xml', '.svg'];
|
|
65
|
+
/**
|
|
66
|
+
* Checks if an image file exceeds the size limit for widgets.
|
|
67
|
+
* Logs a warning if the image is too large.
|
|
68
|
+
*
|
|
69
|
+
* @param imagePath - Path to the image file
|
|
70
|
+
* @returns true if the image is within the size limit, false otherwise
|
|
71
|
+
*/
|
|
72
|
+
function checkImageSize(imagePath) {
|
|
73
|
+
const stats = fs.statSync(imagePath);
|
|
74
|
+
const imageSizeInBytes = stats.size;
|
|
75
|
+
if (imageSizeInBytes >= MAX_IMAGE_SIZE_BYTES) {
|
|
76
|
+
const fileName = path.basename(imagePath);
|
|
77
|
+
logger.warnRed(`Image "${fileName}" is ${imageSizeInBytes} bytes (${(imageSizeInBytes / 1024).toFixed(2)}KB). ` +
|
|
78
|
+
`This image may not display correctly in Android widgets as images should be kept small for performance.`);
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Flattens path and sanitizes name.
|
|
85
|
+
* E.g. "assets/login/User-Icon.png" -> "login_user_icon"
|
|
86
|
+
*/
|
|
87
|
+
function sanitizeDrawableName(filePath) {
|
|
88
|
+
const dirName = path.dirname(filePath); // e.g. "assets/login" or "."
|
|
89
|
+
const fileName = path.parse(filePath).name; // e.g. "User-Icon"
|
|
90
|
+
// Combine directory parts (ignoring '.' or root) with filename
|
|
91
|
+
let nameParts = [];
|
|
92
|
+
// If the file is in a subfolder relative to the project root, include it
|
|
93
|
+
if (dirName !== '.' && dirName !== 'assets') {
|
|
94
|
+
// Split path by separator and add to parts
|
|
95
|
+
const parts = dirName.split(path.sep);
|
|
96
|
+
// Filter out 'assets', 'voltra', 'voltra-android' if they are the top folders to avoid redundant prefixes
|
|
97
|
+
const cleanParts = parts.filter((p) => p !== 'assets' && p !== 'voltra' && p !== 'voltra-android' && p !== '.');
|
|
98
|
+
nameParts = [...nameParts, ...cleanParts];
|
|
99
|
+
}
|
|
100
|
+
nameParts.push(fileName);
|
|
101
|
+
// Join with underscore
|
|
102
|
+
let finalName = nameParts.join('_').toLowerCase();
|
|
103
|
+
// Replace invalid characters
|
|
104
|
+
finalName = finalName.replace(/[^a-z0-9_]/g, '_');
|
|
105
|
+
// Ensure start with letter
|
|
106
|
+
if (!/^[a-z]/.test(finalName)) {
|
|
107
|
+
finalName = `img_${finalName}`;
|
|
108
|
+
}
|
|
109
|
+
return finalName;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Process SVG image: Convert to Android Vector Drawable (XML) and write to destination.
|
|
113
|
+
*/
|
|
114
|
+
async function processSvgImage(sourcePath, destinationPath) {
|
|
115
|
+
try {
|
|
116
|
+
// vd-tool converts a file to XML. We'll copy the SVG to the destination first
|
|
117
|
+
// so it has the sanitized name, then convert it there.
|
|
118
|
+
fs.copyFileSync(sourcePath, destinationPath);
|
|
119
|
+
await vdConvert(destinationPath);
|
|
120
|
+
// Remove the temporary SVG file at the destination
|
|
121
|
+
fs.unlinkSync(destinationPath);
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
// Cleanup if something failed
|
|
125
|
+
if (fs.existsSync(destinationPath)) {
|
|
126
|
+
fs.unlinkSync(destinationPath);
|
|
127
|
+
}
|
|
128
|
+
throw new Error(`Failed to convert SVG to Vector Drawable: ${sourcePath}. Error: ${error}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Process Bitmap/XML image: Check size and copy to destination.
|
|
133
|
+
*/
|
|
134
|
+
function processBitmapImage(sourcePath, destinationPath) {
|
|
135
|
+
const extension = path.extname(sourcePath).toLowerCase();
|
|
136
|
+
// Check image size for widget compatibility (warns if too large) - only for image files
|
|
137
|
+
if (extension !== '.xml') {
|
|
138
|
+
checkImageSize(sourcePath);
|
|
139
|
+
}
|
|
140
|
+
fs.copyFileSync(sourcePath, destinationPath);
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Copies assets to the Android drawable resources directory.
|
|
144
|
+
*
|
|
145
|
+
* Assets are validated for existence and extension, then copied to res/drawable/
|
|
146
|
+
* with Android-compatible names. SVGs are converted to XML.
|
|
147
|
+
*
|
|
148
|
+
* @param assets - Array of asset file paths (relative to project root)
|
|
149
|
+
* @param projectRoot - Path to the project root
|
|
150
|
+
* @param drawablePath - Path to the res/drawable directory in the Android project
|
|
151
|
+
* @returns Array of drawable resource names that were copied
|
|
152
|
+
*/
|
|
153
|
+
async function copyUserImagesToAndroid(assets, projectRoot, drawablePath) {
|
|
154
|
+
const copiedImages = [];
|
|
155
|
+
// Ensure drawable directory exists
|
|
156
|
+
if (!fs.existsSync(drawablePath)) {
|
|
157
|
+
fs.mkdirSync(drawablePath, { recursive: true });
|
|
158
|
+
}
|
|
159
|
+
// Iterate over the assets array
|
|
160
|
+
for (const assetPath of assets) {
|
|
161
|
+
const sourceFile = path.join(projectRoot, assetPath);
|
|
162
|
+
const extension = path.extname(assetPath).toLowerCase();
|
|
163
|
+
// VALIDATION: Check file existence
|
|
164
|
+
if (!fs.existsSync(sourceFile)) {
|
|
165
|
+
logger.warn(`⚠️ Warning: Asset not found at ${sourceFile}. Skipping.`);
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
// VALIDATION: Check extension
|
|
169
|
+
if (!VALID_EXTENSIONS.includes(extension)) {
|
|
170
|
+
throw new Error(`❌ Validating ${assetPath}: '${extension}' is not a supported Android drawable extension. Supported: ${VALID_EXTENSIONS.join(', ')}`);
|
|
171
|
+
}
|
|
172
|
+
// SANITIZATION: Generate new filename
|
|
173
|
+
const cleanName = sanitizeDrawableName(assetPath);
|
|
174
|
+
const destinationFile = path.join(drawablePath, `${cleanName}${extension}`);
|
|
175
|
+
// Process based on extension
|
|
176
|
+
if (extension === '.svg') {
|
|
177
|
+
await processSvgImage(sourceFile, destinationFile);
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
processBitmapImage(sourceFile, destinationFile);
|
|
181
|
+
}
|
|
182
|
+
copiedImages.push(cleanName);
|
|
183
|
+
}
|
|
184
|
+
return copiedImages;
|
|
185
|
+
}
|
|
186
|
+
// ============================================================================
|
|
187
|
+
// Preview Images
|
|
188
|
+
// ============================================================================
|
|
189
|
+
/**
|
|
190
|
+
* Copies preview images to Android drawable resources.
|
|
191
|
+
* Returns a map of widgetId to drawable resource name.
|
|
192
|
+
*/
|
|
193
|
+
async function copyPreviewImagesToAndroid(widgets, projectRoot, drawablePath) {
|
|
194
|
+
const previewImageMap = new Map();
|
|
195
|
+
// Ensure drawable directory exists
|
|
196
|
+
if (!fs.existsSync(drawablePath)) {
|
|
197
|
+
fs.mkdirSync(drawablePath, { recursive: true });
|
|
198
|
+
}
|
|
199
|
+
for (const widget of widgets) {
|
|
200
|
+
if (!widget.previewImage) {
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
const sourcePath = path.join(projectRoot, widget.previewImage);
|
|
204
|
+
// Validate file exists
|
|
205
|
+
if (!fs.existsSync(sourcePath)) {
|
|
206
|
+
logger.warn(`Preview image not found for widget '${widget.id}' at ${widget.previewImage}`);
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
// Warn if image exceeds size limit (does not block copy for Android widgets)
|
|
210
|
+
checkImageSize(sourcePath);
|
|
211
|
+
// Generate drawable resource name
|
|
212
|
+
const ext = path.extname(widget.previewImage).toLowerCase();
|
|
213
|
+
const drawableName = `voltra_widget_${widget.id}_preview`;
|
|
214
|
+
const destinationPath = path.join(drawablePath, `${drawableName}${ext}`);
|
|
215
|
+
// Copy file
|
|
216
|
+
fs.copyFileSync(sourcePath, destinationPath);
|
|
217
|
+
logger.info(`Copied preview image for widget '${widget.id}' to ${drawableName}${ext}`);
|
|
218
|
+
previewImageMap.set(widget.id, drawableName);
|
|
219
|
+
}
|
|
220
|
+
return previewImageMap;
|
|
221
|
+
}
|
|
222
|
+
export { copyPreviewImagesToAndroid };
|
|
223
|
+
//# sourceMappingURL=assets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assets.js","sourceRoot":"","sources":["../../../../src/android/files/assets.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Font configuration for Android widgets.
|
|
3
|
+
*
|
|
4
|
+
* Copies custom font files to the Android assets/fonts/ directory
|
|
5
|
+
* so they can be loaded by TextBitmapRenderer via Typeface.createFromAsset().
|
|
6
|
+
*/
|
|
7
|
+
import * as fs from 'fs';
|
|
8
|
+
import * as path from 'path';
|
|
9
|
+
import { resolveFontPaths } from '../../utils/fonts';
|
|
10
|
+
import { logger } from '../../utils/logger';
|
|
11
|
+
/**
|
|
12
|
+
* Copies custom font files to android/app/src/main/assets/fonts/.
|
|
13
|
+
*
|
|
14
|
+
* Resolves each path relative to the project root.
|
|
15
|
+
* If a path points to a directory, all .ttf/.otf files inside it are copied.
|
|
16
|
+
*/
|
|
17
|
+
export async function copyAndroidFonts(options) {
|
|
18
|
+
const { platformProjectRoot, projectRoot, fonts } = options;
|
|
19
|
+
if (!fonts || fonts.length === 0) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const fontsDir = path.join(platformProjectRoot, 'app', 'src', 'main', 'assets', 'fonts');
|
|
23
|
+
fs.mkdirSync(fontsDir, { recursive: true });
|
|
24
|
+
const resolvedPaths = await resolveFontPaths(fonts, projectRoot);
|
|
25
|
+
for (const fontPath of resolvedPaths) {
|
|
26
|
+
const fileName = path.basename(fontPath);
|
|
27
|
+
const dest = path.join(fontsDir, fileName);
|
|
28
|
+
fs.copyFileSync(fontPath, dest);
|
|
29
|
+
logger.info(`Copied font to Android assets: ${fileName}`);
|
|
30
|
+
}
|
|
31
|
+
logger.info(`Copied ${resolvedPaths.length} font(s) to Android assets/fonts/`);
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=fonts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fonts.js","sourceRoot":"","sources":["../../../../src/android/files/fonts.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,MAAM,IAAI,CAAA;AACxB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAA;AAE5B,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AACpD,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAA;AAQ3C;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,OAAgC;IACrE,MAAM,EAAE,mBAAmB,EAAE,WAAW,EAAE,KAAK,EAAE,GAAG,OAAO,CAAA;IAE3D,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACjC,OAAM;IACR,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,mBAAmB,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAA;IACxF,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IAE3C,MAAM,aAAa,GAAG,MAAM,gBAAgB,CAAC,KAAK,EAAE,WAAW,CAAC,CAAA;IAEhE,KAAK,MAAM,QAAQ,IAAI,aAAa,EAAE,CAAC;QACrC,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;QACxC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAA;QAE1C,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAA;QAC/B,MAAM,CAAC,IAAI,CAAC,kCAAkC,QAAQ,EAAE,CAAC,CAAA;IAC3D,CAAC;IAED,MAAM,CAAC,IAAI,CAAC,UAAU,aAAa,CAAC,MAAM,mCAAmC,CAAC,CAAA;AAChF,CAAC"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { withDangerousMod } from '@expo/config-plugins';
|
|
2
|
+
import { generateAndroidAssets } from './assets';
|
|
3
|
+
import { copyAndroidFonts } from './fonts';
|
|
4
|
+
import { generateAndroidInitialStates } from './initialStates';
|
|
5
|
+
import { generateWidgetReceivers } from './kotlin';
|
|
6
|
+
import { generateWidgetInfoFiles, generateWidgetPlaceholderLayouts, generateWidgetPreviewLayouts } from './xml';
|
|
7
|
+
/**
|
|
8
|
+
* Plugin step that generates all Android widget files.
|
|
9
|
+
*
|
|
10
|
+
* This creates:
|
|
11
|
+
* - Kotlin receiver classes for each widget
|
|
12
|
+
* - res/xml/{widget_id}_info.xml (widget provider info)
|
|
13
|
+
* - res/layout/voltra_widget_placeholder.xml (placeholder layout)
|
|
14
|
+
* - res/values/voltra_widgets.xml (widget descriptions)
|
|
15
|
+
* - res/drawable/ images (user images as drawable resources)
|
|
16
|
+
*
|
|
17
|
+
* This should run before configureAndroidManifest so the files exist when the manifest is configured.
|
|
18
|
+
*/
|
|
19
|
+
export const generateAndroidWidgetFiles = (config, props) => {
|
|
20
|
+
const { widgets, userImagesPath, fonts } = props;
|
|
21
|
+
return withDangerousMod(config, [
|
|
22
|
+
'android',
|
|
23
|
+
async (config) => {
|
|
24
|
+
if (config.modRequest.introspect) {
|
|
25
|
+
return config;
|
|
26
|
+
}
|
|
27
|
+
const { platformProjectRoot, projectRoot } = config.modRequest;
|
|
28
|
+
const packageName = config.android?.package;
|
|
29
|
+
if (!packageName) {
|
|
30
|
+
throw new Error('Voltra config plugin requires expo.android.package to be set in app.json/app.config.* to configure Android widgets.');
|
|
31
|
+
}
|
|
32
|
+
// Generate assets (drawable images and preview images)
|
|
33
|
+
const previewImageMap = await generateAndroidAssets({
|
|
34
|
+
platformProjectRoot,
|
|
35
|
+
projectRoot,
|
|
36
|
+
userImagesPath,
|
|
37
|
+
widgets,
|
|
38
|
+
});
|
|
39
|
+
// Generate Kotlin receiver classes
|
|
40
|
+
await generateWidgetReceivers({
|
|
41
|
+
platformProjectRoot,
|
|
42
|
+
packageName,
|
|
43
|
+
widgets,
|
|
44
|
+
});
|
|
45
|
+
// Generate XML files (widget info, layouts, strings)
|
|
46
|
+
await generateWidgetInfoFiles({
|
|
47
|
+
platformProjectRoot,
|
|
48
|
+
widgets,
|
|
49
|
+
});
|
|
50
|
+
await generateWidgetPlaceholderLayouts({
|
|
51
|
+
platformProjectRoot,
|
|
52
|
+
});
|
|
53
|
+
await generateWidgetPreviewLayouts({
|
|
54
|
+
platformProjectRoot,
|
|
55
|
+
projectRoot,
|
|
56
|
+
widgets,
|
|
57
|
+
previewImageMap,
|
|
58
|
+
});
|
|
59
|
+
// Copy custom fonts to assets/fonts/
|
|
60
|
+
if (fonts && fonts.length > 0) {
|
|
61
|
+
await copyAndroidFonts({
|
|
62
|
+
platformProjectRoot,
|
|
63
|
+
projectRoot: config.modRequest.projectRoot,
|
|
64
|
+
fonts,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
// Generate initial states (pre-rendered widgets)
|
|
68
|
+
await generateAndroidInitialStates({
|
|
69
|
+
platformProjectRoot,
|
|
70
|
+
projectRoot: config.modRequest.projectRoot,
|
|
71
|
+
widgets,
|
|
72
|
+
});
|
|
73
|
+
return config;
|
|
74
|
+
},
|
|
75
|
+
]);
|
|
76
|
+
};
|
|
77
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/android/files/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAGrE,OAAO,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAA;AAChD,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAC1C,OAAO,EAAE,4BAA4B,EAAE,MAAM,iBAAiB,CAAA;AAC9D,OAAO,EAAE,uBAAuB,EAAE,MAAM,UAAU,CAAA;AAClD,OAAO,EAAE,uBAAuB,EAAE,gCAAgC,EAAE,4BAA4B,EAAE,MAAM,OAAO,CAAA;AAQ/G;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAkD,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE;IACzG,MAAM,EAAE,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,GAAG,KAAK,CAAA;IAEhD,OAAO,gBAAgB,CAAC,MAAM,EAAE;QAC9B,SAAS;QACT,KAAK,EAAE,MAAM,EAAE,EAAE;YACf,IAAI,MAAM,CAAC,UAAU,CAAC,UAAU,EAAE,CAAC;gBACjC,OAAO,MAAM,CAAA;YACf,CAAC;YAED,MAAM,EAAE,mBAAmB,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC,UAAU,CAAA;YAC9D,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,EAAE,OAAO,CAAA;YAE3C,IAAI,CAAC,WAAW,EAAE,CAAC;gBACjB,MAAM,IAAI,KAAK,CACb,qHAAqH,CACtH,CAAA;YACH,CAAC;YAED,uDAAuD;YACvD,MAAM,eAAe,GAAG,MAAM,qBAAqB,CAAC;gBAClD,mBAAmB;gBACnB,WAAW;gBACX,cAAc;gBACd,OAAO;aACR,CAAC,CAAA;YAEF,mCAAmC;YACnC,MAAM,uBAAuB,CAAC;gBAC5B,mBAAmB;gBACnB,WAAW;gBACX,OAAO;aACR,CAAC,CAAA;YAEF,qDAAqD;YACrD,MAAM,uBAAuB,CAAC;gBAC5B,mBAAmB;gBACnB,OAAO;aACR,CAAC,CAAA;YAEF,MAAM,gCAAgC,CAAC;gBACrC,mBAAmB;aACpB,CAAC,CAAA;YAEF,MAAM,4BAA4B,CAAC;gBACjC,mBAAmB;gBACnB,WAAW;gBACX,OAAO;gBACP,eAAe;aAChB,CAAC,CAAA;YAEF,qCAAqC;YACrC,IAAI,KAAK,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC9B,MAAM,gBAAgB,CAAC;oBACrB,mBAAmB;oBACnB,WAAW,EAAE,MAAM,CAAC,UAAU,CAAC,WAAW;oBAC1C,KAAK;iBACN,CAAC,CAAA;YACJ,CAAC;YAED,iDAAiD;YACjD,MAAM,4BAA4B,CAAC;gBACjC,mBAAmB;gBACnB,WAAW,EAAE,MAAM,CAAC,UAAU,CAAC,WAAW;gBAC1C,OAAO;aACR,CAAC,CAAA;YAEF,OAAO,MAAM,CAAA;QACf,CAAC;KACF,CAAC,CAAA;AACJ,CAAC,CAAA"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { logger } from '../../utils/logger';
|
|
4
|
+
import { prerenderWidgetState } from '../../utils/prerender';
|
|
5
|
+
/**
|
|
6
|
+
* Generates the initial states JSON file for Android widgets.
|
|
7
|
+
*
|
|
8
|
+
* This file (voltra_initial_states.json) is placed in the Android assets directory
|
|
9
|
+
* and contains the pre-rendered payloads for all widgets that have an initialStatePath.
|
|
10
|
+
*/
|
|
11
|
+
export async function generateAndroidInitialStates(options) {
|
|
12
|
+
const { widgets, projectRoot, platformProjectRoot } = options;
|
|
13
|
+
// Dynamic import for ESM module compatibility
|
|
14
|
+
// voltra/android/server is an ESM module, but the plugin is compiled to CommonJS
|
|
15
|
+
const androidServerModuleId = 'voltra/android/server';
|
|
16
|
+
const { renderAndroidWidgetToString } = (await import(androidServerModuleId));
|
|
17
|
+
// Prerender widget states
|
|
18
|
+
const prerenderedStates = await prerenderWidgetState(widgets, projectRoot, renderAndroidWidgetToString);
|
|
19
|
+
if (prerenderedStates.size === 0) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
// Create assets directory if it doesn't exist
|
|
23
|
+
const assetsDir = path.join(platformProjectRoot, 'app', 'src', 'main', 'assets');
|
|
24
|
+
if (!fs.existsSync(assetsDir)) {
|
|
25
|
+
fs.mkdirSync(assetsDir, { recursive: true });
|
|
26
|
+
}
|
|
27
|
+
// Convert Map to Object for JSON serialization
|
|
28
|
+
const statesObj = {};
|
|
29
|
+
for (const [id, stateJson] of prerenderedStates.entries()) {
|
|
30
|
+
try {
|
|
31
|
+
// Parse the JSON string so it's embedded as an object in the final JSON
|
|
32
|
+
statesObj[id] = JSON.parse(stateJson);
|
|
33
|
+
}
|
|
34
|
+
catch (e) {
|
|
35
|
+
logger.warn(`Failed to parse prerendered state for widget ${id}: ${e}`);
|
|
36
|
+
// If it's not valid JSON, we might skip it or include it as string?
|
|
37
|
+
// renderWidgetToString should return valid JSON.
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
const outputFilePath = path.join(assetsDir, 'voltra_initial_states.json');
|
|
41
|
+
fs.writeFileSync(outputFilePath, JSON.stringify(statesObj, null, 2));
|
|
42
|
+
logger.info(`Generated voltra_initial_states.json with ${Object.keys(statesObj).length} pre-rendered widget states`);
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=initialStates.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"initialStates.js","sourceRoot":"","sources":["../../../../src/android/files/initialStates.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAA;AACnB,OAAO,IAAI,MAAM,MAAM,CAAA;AAGvB,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAA;AAC3C,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAA;AAU5D;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAAC,OAAqC;IACtF,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,GAAG,OAAO,CAAA;IAE7D,8CAA8C;IAC9C,iFAAiF;IACjF,MAAM,qBAAqB,GAAG,uBAAuB,CAAA;IACrD,MAAM,EAAE,2BAA2B,EAAE,GAAG,CAAC,MAAM,MAAM,CAAC,qBAAqB,CAAC,CAE3E,CAAA;IAED,0BAA0B;IAC1B,MAAM,iBAAiB,GAAG,MAAM,oBAAoB,CAAC,OAAO,EAAE,WAAW,EAAE,2BAA2B,CAAC,CAAA;IAEvG,IAAI,iBAAiB,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;QACjC,OAAM;IACR,CAAC;IAED,8CAA8C;IAC9C,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,mBAAmB,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAA;IAChF,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC9B,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IAC9C,CAAC;IAED,+CAA+C;IAC/C,MAAM,SAAS,GAAwB,EAAE,CAAA;IACzC,KAAK,MAAM,CAAC,EAAE,EAAE,SAAS,CAAC,IAAI,iBAAiB,CAAC,OAAO,EAAE,EAAE,CAAC;QAC1D,IAAI,CAAC;YACH,wEAAwE;YACxE,SAAS,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAA;QACvC,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,MAAM,CAAC,IAAI,CAAC,gDAAgD,EAAE,KAAK,CAAC,EAAE,CAAC,CAAA;YACvE,oEAAoE;YACpE,iDAAiD;QACnD,CAAC;IACH,CAAC;IAED,MAAM,cAAc,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,4BAA4B,CAAC,CAAA;IACzE,EAAE,CAAC,aAAa,CAAC,cAAc,EAAE,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAA;IAEpE,MAAM,CAAC,IAAI,CAAC,6CAA6C,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,MAAM,6BAA6B,CAAC,CAAA;AACtH,CAAC"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import dedent from 'dedent';
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
// ============================================================================
|
|
5
|
+
// Main Function
|
|
6
|
+
// ============================================================================
|
|
7
|
+
/**
|
|
8
|
+
* Generates Kotlin receiver classes for all configured widgets
|
|
9
|
+
*/
|
|
10
|
+
export async function generateWidgetReceivers(props) {
|
|
11
|
+
const { platformProjectRoot, packageName, widgets } = props;
|
|
12
|
+
// Determine the package path (e.g., com.example.app -> com/example/app)
|
|
13
|
+
const packagePath = packageName.replace(/\./g, '/');
|
|
14
|
+
const widgetDir = path.join(platformProjectRoot, 'app', 'src', 'main', 'java', packagePath, 'widget');
|
|
15
|
+
// Ensure the widget directory exists
|
|
16
|
+
if (!fs.existsSync(widgetDir)) {
|
|
17
|
+
fs.mkdirSync(widgetDir, { recursive: true });
|
|
18
|
+
}
|
|
19
|
+
// Generate a receiver class for each widget
|
|
20
|
+
for (const widget of widgets) {
|
|
21
|
+
const className = `VoltraWidget_${widget.id}Receiver`;
|
|
22
|
+
const filePath = path.join(widgetDir, `${className}.kt`);
|
|
23
|
+
const content = generateWidgetReceiverClass(widget, packageName);
|
|
24
|
+
fs.writeFileSync(filePath, content, 'utf8');
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
// ============================================================================
|
|
28
|
+
// Widget Receiver
|
|
29
|
+
// ============================================================================
|
|
30
|
+
/**
|
|
31
|
+
* Generates Kotlin code for a single widget receiver class.
|
|
32
|
+
* If the widget has serverUpdate configured, includes WorkManager scheduling.
|
|
33
|
+
*/
|
|
34
|
+
function generateWidgetReceiverClass(widget, packageName) {
|
|
35
|
+
const className = `VoltraWidget_${widget.id}Receiver`;
|
|
36
|
+
if (widget.serverUpdate) {
|
|
37
|
+
const refreshEnabled = widget.serverUpdate.refresh === true;
|
|
38
|
+
// Widget with server-driven updates: schedule WorkManager periodic task
|
|
39
|
+
return dedent `
|
|
40
|
+
package ${packageName}.widget
|
|
41
|
+
|
|
42
|
+
import android.appwidget.AppWidgetManager
|
|
43
|
+
import android.content.Context
|
|
44
|
+
import voltra.widget.VoltraWidgetReceiver
|
|
45
|
+
import voltra.widget.VoltraWidgetUpdateScheduler
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Auto-generated widget receiver for ${widget.displayName}
|
|
49
|
+
* Widget ID: ${widget.id}
|
|
50
|
+
* Server Update: ${widget.serverUpdate.url} (every ${widget.serverUpdate.intervalMinutes ?? 15} minutes)
|
|
51
|
+
* Refresh Button: ${refreshEnabled}
|
|
52
|
+
*/
|
|
53
|
+
class ${className} : VoltraWidgetReceiver() {
|
|
54
|
+
override val widgetId: String = "${widget.id}"
|
|
55
|
+
|
|
56
|
+
override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
|
|
57
|
+
super.onUpdate(context, appWidgetManager, appWidgetIds)
|
|
58
|
+
|
|
59
|
+
// Schedule periodic server updates via WorkManager
|
|
60
|
+
VoltraWidgetUpdateScheduler.schedulePeriodicUpdate(
|
|
61
|
+
context = context,
|
|
62
|
+
widgetId = "${widget.id}",
|
|
63
|
+
serverUrl = "${widget.serverUpdate.url}",
|
|
64
|
+
intervalMinutes = ${widget.serverUpdate.intervalMinutes ?? 15}L,
|
|
65
|
+
refreshEnabled = ${refreshEnabled}
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
override fun onDeleted(context: Context, appWidgetIds: IntArray) {
|
|
70
|
+
super.onDeleted(context, appWidgetIds)
|
|
71
|
+
|
|
72
|
+
// Cancel periodic updates when all instances of this widget are removed
|
|
73
|
+
val remaining = appWidgetManager(context, appWidgetIds)
|
|
74
|
+
if (remaining == 0) {
|
|
75
|
+
VoltraWidgetUpdateScheduler.cancelPeriodicUpdate(context, "${widget.id}")
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
private fun appWidgetManager(context: Context, deletedIds: IntArray): Int {
|
|
80
|
+
val manager = AppWidgetManager.getInstance(context)
|
|
81
|
+
val componentName = android.content.ComponentName(context, this::class.java)
|
|
82
|
+
val allIds = manager.getAppWidgetIds(componentName)
|
|
83
|
+
return allIds.count { it !in deletedIds }
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
`;
|
|
87
|
+
}
|
|
88
|
+
// Standard widget without server updates
|
|
89
|
+
return dedent `
|
|
90
|
+
package ${packageName}.widget
|
|
91
|
+
|
|
92
|
+
import voltra.widget.VoltraWidgetReceiver
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Auto-generated widget receiver for ${widget.displayName}
|
|
96
|
+
* Widget ID: ${widget.id}
|
|
97
|
+
*/
|
|
98
|
+
class ${className} : VoltraWidgetReceiver() {
|
|
99
|
+
override val widgetId: String = "${widget.id}"
|
|
100
|
+
}
|
|
101
|
+
`;
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=kotlin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kotlin.js","sourceRoot":"","sources":["../../../../src/android/files/kotlin.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAA;AAC3B,OAAO,KAAK,EAAE,MAAM,IAAI,CAAA;AACxB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAA;AAU5B,+EAA+E;AAC/E,gBAAgB;AAChB,+EAA+E;AAE/E;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,KAA+B;IAC3E,MAAM,EAAE,mBAAmB,EAAE,WAAW,EAAE,OAAO,EAAE,GAAG,KAAK,CAAA;IAE3D,wEAAwE;IACxE,MAAM,WAAW,GAAG,WAAW,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAA;IACnD,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,mBAAmB,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,QAAQ,CAAC,CAAA;IAErG,qCAAqC;IACrC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC9B,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IAC9C,CAAC;IAED,4CAA4C;IAC5C,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,MAAM,SAAS,GAAG,gBAAgB,MAAM,CAAC,EAAE,UAAU,CAAA;QACrD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,SAAS,KAAK,CAAC,CAAA;QACxD,MAAM,OAAO,GAAG,2BAA2B,CAAC,MAAM,EAAE,WAAW,CAAC,CAAA;QAEhE,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,CAAA;IAC7C,CAAC;AACH,CAAC;AAED,+EAA+E;AAC/E,kBAAkB;AAClB,+EAA+E;AAE/E;;;GAGG;AACH,SAAS,2BAA2B,CAAC,MAA2B,EAAE,WAAmB;IACnF,MAAM,SAAS,GAAG,gBAAgB,MAAM,CAAC,EAAE,UAAU,CAAA;IAErD,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;QACxB,MAAM,cAAc,GAAG,MAAM,CAAC,YAAY,CAAC,OAAO,KAAK,IAAI,CAAA;QAC3D,wEAAwE;QACxE,OAAO,MAAM,CAAA;gBACD,WAAW;;;;;;;;8CAQmB,MAAM,CAAC,WAAW;sBAC1C,MAAM,CAAC,EAAE;0BACL,MAAM,CAAC,YAAY,CAAC,GAAG,WAAW,MAAM,CAAC,YAAY,CAAC,eAAe,IAAI,EAAE;2BAC1E,cAAc;;cAE3B,SAAS;6CACsB,MAAM,CAAC,EAAE;;;;;;;;gCAQtB,MAAM,CAAC,EAAE;iCACR,MAAM,CAAC,YAAY,CAAC,GAAG;sCAClB,MAAM,CAAC,YAAY,CAAC,eAAe,IAAI,EAAE;qCAC1C,cAAc;;;;;;;;;;+EAU4B,MAAM,CAAC,EAAE;;;;;;;;;;;KAWnF,CAAA;IACH,CAAC;IAED,yCAAyC;IACzC,OAAO,MAAM,CAAA;cACD,WAAW;;;;;4CAKmB,MAAM,CAAC,WAAW;oBAC1C,MAAM,CAAC,EAAE;;YAEjB,SAAS;2CACsB,MAAM,CAAC,EAAE;;GAEjD,CAAA;AACH,CAAC"}
|