kasy-cli 1.14.0 → 1.16.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/bin/kasy.js +18 -5
- package/lib/commands/icon.js +29 -1
- package/lib/commands/ios.js +8 -2
- package/lib/commands/reset.js +100 -2
- package/lib/commands/run.js +61 -2
- package/lib/commands/splash.js +11 -0
- package/lib/scaffold/backends/api/pubspec.yaml.tpl +4 -2
- package/lib/scaffold/backends/supabase/pubspec.yaml.tpl +4 -2
- package/lib/utils/apple-release.js +30 -0
- package/lib/utils/checks.js +41 -2
- package/lib/utils/debug.js +75 -0
- package/lib/utils/friendly-error.js +91 -0
- package/lib/utils/i18n/messages-en.js +977 -0
- package/lib/utils/i18n/messages-es.js +975 -0
- package/lib/utils/i18n/messages-pt.js +975 -0
- package/lib/utils/i18n.js +21 -2818
- package/lib/utils/png-padding.js +252 -0
- package/package.json +8 -3
- package/templates/firebase/android/app/src/main/kotlin/com/aicrus/firebase/kit/MyWidget.kt +12 -11
- package/templates/firebase/android/app/src/main/res/drawable-hdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-hdpi/ic_launcher_background.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-mdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-mdpi/ic_launcher_background.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-night-hdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-night-mdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-night-xhdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-night-xxhdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-night-xxxhdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-xhdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-xhdpi/ic_launcher_background.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-xxhdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-xxhdpi/ic_launcher_background.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-xxxhdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_background.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png +0 -0
- package/templates/firebase/android/app/src/main/res/layout/widget_preview.xml +18 -11
- package/templates/firebase/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +9 -0
- package/templates/firebase/assets/images/icon_android.png +0 -0
- package/templates/firebase/assets/images/icon_foreground_empty.png +0 -0
- package/templates/firebase/assets/images/splash_logo_dark_android12.png +0 -0
- package/templates/firebase/assets/images/splash_logo_light_android12.png +0 -0
- package/templates/firebase/lib/components/components.dart +1 -0
- package/templates/firebase/lib/components/kasy_avatar.dart +88 -57
- package/templates/firebase/lib/components/kasy_avatar_presets.dart +116 -74
- package/templates/firebase/lib/components/kasy_tabs.dart +431 -0
- package/templates/firebase/lib/core/home_widgets/home_widget_mywidget_service.dart +12 -6
- package/templates/firebase/lib/features/home/home_components_page.dart +1 -1
- package/templates/firebase/lib/features/home/home_components_preview_registry.dart +316 -93
- package/templates/firebase/pubspec.yaml +4 -2
- package/templates/firebase/web/index.html +9 -0
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
const fsp = require('node:fs/promises');
|
|
2
|
+
const { PNG } = require('pngjs');
|
|
3
|
+
|
|
4
|
+
const ANDROID12_SAFE_RATIO = 0.4;
|
|
5
|
+
const ANDROID_ADAPTIVE_LOGO_RATIO = 0.65;
|
|
6
|
+
const ANDROID_ADAPTIVE_CANVAS = 1024;
|
|
7
|
+
|
|
8
|
+
async function readPng(filePath) {
|
|
9
|
+
const buffer = await fsp.readFile(filePath);
|
|
10
|
+
return new Promise((resolve, reject) => {
|
|
11
|
+
new PNG().parse(buffer, (err, data) => {
|
|
12
|
+
if (err) reject(err);
|
|
13
|
+
else resolve(data);
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function resizeBilinear(src, dstW, dstH) {
|
|
19
|
+
const dst = new PNG({ width: dstW, height: dstH });
|
|
20
|
+
const srcW = src.width;
|
|
21
|
+
const srcH = src.height;
|
|
22
|
+
const xRatio = srcW > 1 ? (srcW - 1) / dstW : 0;
|
|
23
|
+
const yRatio = srcH > 1 ? (srcH - 1) / dstH : 0;
|
|
24
|
+
|
|
25
|
+
for (let y = 0; y < dstH; y++) {
|
|
26
|
+
const sy = (y + 0.5) * yRatio;
|
|
27
|
+
const y0 = Math.floor(sy);
|
|
28
|
+
const y1 = Math.min(y0 + 1, srcH - 1);
|
|
29
|
+
const wy = sy - y0;
|
|
30
|
+
|
|
31
|
+
for (let x = 0; x < dstW; x++) {
|
|
32
|
+
const sx = (x + 0.5) * xRatio;
|
|
33
|
+
const x0 = Math.floor(sx);
|
|
34
|
+
const x1 = Math.min(x0 + 1, srcW - 1);
|
|
35
|
+
const wx = sx - x0;
|
|
36
|
+
|
|
37
|
+
const i00 = (y0 * srcW + x0) * 4;
|
|
38
|
+
const i10 = (y0 * srcW + x1) * 4;
|
|
39
|
+
const i01 = (y1 * srcW + x0) * 4;
|
|
40
|
+
const i11 = (y1 * srcW + x1) * 4;
|
|
41
|
+
|
|
42
|
+
const dstIdx = (y * dstW + x) * 4;
|
|
43
|
+
for (let c = 0; c < 4; c++) {
|
|
44
|
+
const top = src.data[i00 + c] * (1 - wx) + src.data[i10 + c] * wx;
|
|
45
|
+
const bot = src.data[i01 + c] * (1 - wx) + src.data[i11 + c] * wx;
|
|
46
|
+
dst.data[dstIdx + c] = Math.round(top * (1 - wy) + bot * wy);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return dst;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function compositeOnTransparentSquare(logo, canvasSize) {
|
|
54
|
+
const canvas = new PNG({ width: canvasSize, height: canvasSize });
|
|
55
|
+
canvas.data.fill(0);
|
|
56
|
+
|
|
57
|
+
const offsetX = Math.floor((canvasSize - logo.width) / 2);
|
|
58
|
+
const offsetY = Math.floor((canvasSize - logo.height) / 2);
|
|
59
|
+
|
|
60
|
+
for (let y = 0; y < logo.height; y++) {
|
|
61
|
+
for (let x = 0; x < logo.width; x++) {
|
|
62
|
+
const srcIdx = (y * logo.width + x) * 4;
|
|
63
|
+
const dstIdx = ((y + offsetY) * canvasSize + (x + offsetX)) * 4;
|
|
64
|
+
canvas.data[dstIdx] = logo.data[srcIdx];
|
|
65
|
+
canvas.data[dstIdx + 1] = logo.data[srcIdx + 1];
|
|
66
|
+
canvas.data[dstIdx + 2] = logo.data[srcIdx + 2];
|
|
67
|
+
canvas.data[dstIdx + 3] = logo.data[srcIdx + 3];
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return canvas;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async function writePng(png, filePath) {
|
|
74
|
+
return new Promise((resolve, reject) => {
|
|
75
|
+
const chunks = [];
|
|
76
|
+
png.pack()
|
|
77
|
+
.on('data', (chunk) => chunks.push(chunk))
|
|
78
|
+
.on('end', async () => {
|
|
79
|
+
try {
|
|
80
|
+
await fsp.writeFile(filePath, Buffer.concat(chunks));
|
|
81
|
+
resolve();
|
|
82
|
+
} catch (e) {
|
|
83
|
+
reject(e);
|
|
84
|
+
}
|
|
85
|
+
})
|
|
86
|
+
.on('error', reject);
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Read a PNG and write a new one of the same dimensions, with the source
|
|
92
|
+
* logo scaled down to fit inside the Android 12+ splash safe area (centered,
|
|
93
|
+
* transparent padding around it). This is what `windowSplashScreenAnimatedIcon`
|
|
94
|
+
* needs so the OS-applied circular mask doesn't clip the logo edges.
|
|
95
|
+
*
|
|
96
|
+
* @param {string} srcPath
|
|
97
|
+
* @param {string} dstPath
|
|
98
|
+
* @param {number} safeRatio fraction of the canvas the logo should occupy (default 0.6)
|
|
99
|
+
*/
|
|
100
|
+
async function writeAndroid12Variant(srcPath, dstPath, safeRatio = ANDROID12_SAFE_RATIO) {
|
|
101
|
+
const src = await readPng(srcPath);
|
|
102
|
+
const canvasSize = Math.max(src.width, src.height);
|
|
103
|
+
const safeSide = Math.round(canvasSize * safeRatio);
|
|
104
|
+
|
|
105
|
+
const aspect = src.width / src.height;
|
|
106
|
+
let logoW;
|
|
107
|
+
let logoH;
|
|
108
|
+
if (aspect >= 1) {
|
|
109
|
+
logoW = safeSide;
|
|
110
|
+
logoH = Math.round(safeSide / aspect);
|
|
111
|
+
} else {
|
|
112
|
+
logoH = safeSide;
|
|
113
|
+
logoW = Math.round(safeSide * aspect);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const resized = resizeBilinear(src, logoW, logoH);
|
|
117
|
+
const composited = compositeOnTransparentSquare(resized, canvasSize);
|
|
118
|
+
await writePng(composited, dstPath);
|
|
119
|
+
return { canvasSize, logoW, logoH };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function sampleSolidBackground(png) {
|
|
123
|
+
const inset = Math.max(2, Math.floor(Math.min(png.width, png.height) * 0.02));
|
|
124
|
+
const corners = [
|
|
125
|
+
[inset, inset],
|
|
126
|
+
[png.width - inset - 1, inset],
|
|
127
|
+
[inset, png.height - inset - 1],
|
|
128
|
+
[png.width - inset - 1, png.height - inset - 1],
|
|
129
|
+
];
|
|
130
|
+
|
|
131
|
+
let r = 0;
|
|
132
|
+
let g = 0;
|
|
133
|
+
let b = 0;
|
|
134
|
+
let opaque = 0;
|
|
135
|
+
for (const [x, y] of corners) {
|
|
136
|
+
const idx = (y * png.width + x) * 4;
|
|
137
|
+
const alpha = png.data[idx + 3];
|
|
138
|
+
if (alpha < 250) continue;
|
|
139
|
+
r += png.data[idx];
|
|
140
|
+
g += png.data[idx + 1];
|
|
141
|
+
b += png.data[idx + 2];
|
|
142
|
+
opaque += 1;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (opaque === 0) {
|
|
146
|
+
return { r: 255, g: 255, b: 255, sampled: false };
|
|
147
|
+
}
|
|
148
|
+
return {
|
|
149
|
+
r: Math.round(r / opaque),
|
|
150
|
+
g: Math.round(g / opaque),
|
|
151
|
+
b: Math.round(b / opaque),
|
|
152
|
+
sampled: true,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function fillSolidColor(png, color) {
|
|
157
|
+
for (let i = 0; i < png.data.length; i += 4) {
|
|
158
|
+
png.data[i] = color.r;
|
|
159
|
+
png.data[i + 1] = color.g;
|
|
160
|
+
png.data[i + 2] = color.b;
|
|
161
|
+
png.data[i + 3] = 255;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function compositeLogoOnBackground(background, logo) {
|
|
166
|
+
const offsetX = Math.floor((background.width - logo.width) / 2);
|
|
167
|
+
const offsetY = Math.floor((background.height - logo.height) / 2);
|
|
168
|
+
|
|
169
|
+
for (let y = 0; y < logo.height; y++) {
|
|
170
|
+
for (let x = 0; x < logo.width; x++) {
|
|
171
|
+
const srcIdx = (y * logo.width + x) * 4;
|
|
172
|
+
const alpha = logo.data[srcIdx + 3] / 255;
|
|
173
|
+
if (alpha === 0) continue;
|
|
174
|
+
|
|
175
|
+
const dstIdx = ((y + offsetY) * background.width + (x + offsetX)) * 4;
|
|
176
|
+
const inv = 1 - alpha;
|
|
177
|
+
background.data[dstIdx] = Math.round(logo.data[srcIdx] * alpha + background.data[dstIdx] * inv);
|
|
178
|
+
background.data[dstIdx + 1] = Math.round(logo.data[srcIdx + 1] * alpha + background.data[dstIdx + 1] * inv);
|
|
179
|
+
background.data[dstIdx + 2] = Math.round(logo.data[srcIdx + 2] * alpha + background.data[dstIdx + 2] * inv);
|
|
180
|
+
background.data[dstIdx + 3] = 255;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Generate the Android adaptive icon background asset. Reads the user-supplied
|
|
187
|
+
* icon PNG, samples a solid background color from its corners (falling back to
|
|
188
|
+
* white when the image is transparent), and writes a square canvas with the
|
|
189
|
+
* logo scaled down and centered. The result is what `flutter_launcher_icons`
|
|
190
|
+
* passes to `adaptive_icon_background`, fully filling the launcher's circular
|
|
191
|
+
* mask without the default white halo around square icons.
|
|
192
|
+
*
|
|
193
|
+
* @param {string} srcPath source icon PNG path
|
|
194
|
+
* @param {string} dstPath output path for icon_android.png
|
|
195
|
+
* @param {object} [options]
|
|
196
|
+
* @param {number} [options.canvasSize] output square size (default 1024)
|
|
197
|
+
* @param {number} [options.logoRatio] logo fill ratio inside canvas (default 0.65)
|
|
198
|
+
* @param {string} [options.backgroundColor] explicit hex like "#01171f" to override sampling
|
|
199
|
+
*/
|
|
200
|
+
async function writeAndroidAdaptiveBackground(srcPath, dstPath, options = {}) {
|
|
201
|
+
const canvasSize = options.canvasSize || ANDROID_ADAPTIVE_CANVAS;
|
|
202
|
+
const logoRatio = options.logoRatio || ANDROID_ADAPTIVE_LOGO_RATIO;
|
|
203
|
+
|
|
204
|
+
const src = await readPng(srcPath);
|
|
205
|
+
|
|
206
|
+
let color;
|
|
207
|
+
if (options.backgroundColor) {
|
|
208
|
+
const hex = options.backgroundColor.replace('#', '');
|
|
209
|
+
color = {
|
|
210
|
+
r: parseInt(hex.slice(0, 2), 16),
|
|
211
|
+
g: parseInt(hex.slice(2, 4), 16),
|
|
212
|
+
b: parseInt(hex.slice(4, 6), 16),
|
|
213
|
+
sampled: false,
|
|
214
|
+
};
|
|
215
|
+
} else {
|
|
216
|
+
color = sampleSolidBackground(src);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const canvas = new PNG({ width: canvasSize, height: canvasSize });
|
|
220
|
+
fillSolidColor(canvas, color);
|
|
221
|
+
|
|
222
|
+
const logoSize = Math.round(canvasSize * logoRatio);
|
|
223
|
+
const resized = resizeBilinear(src, logoSize, logoSize);
|
|
224
|
+
compositeLogoOnBackground(canvas, resized);
|
|
225
|
+
|
|
226
|
+
await writePng(canvas, dstPath);
|
|
227
|
+
return { canvasSize, logoSize, color };
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Write a fully transparent square PNG. Used to satisfy
|
|
232
|
+
* `adaptive_icon_foreground` when the entire image already lives in the
|
|
233
|
+
* background layer.
|
|
234
|
+
*
|
|
235
|
+
* @param {string} dstPath
|
|
236
|
+
* @param {number} [size]
|
|
237
|
+
*/
|
|
238
|
+
async function writeTransparentSquare(dstPath, size = ANDROID_ADAPTIVE_CANVAS) {
|
|
239
|
+
const png = new PNG({ width: size, height: size });
|
|
240
|
+
png.data.fill(0);
|
|
241
|
+
await writePng(png, dstPath);
|
|
242
|
+
return { size };
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
module.exports = {
|
|
246
|
+
writeAndroid12Variant,
|
|
247
|
+
writeAndroidAdaptiveBackground,
|
|
248
|
+
writeTransparentSquare,
|
|
249
|
+
ANDROID12_SAFE_RATIO,
|
|
250
|
+
ANDROID_ADAPTIVE_LOGO_RATIO,
|
|
251
|
+
ANDROID_ADAPTIVE_CANVAS,
|
|
252
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kasy-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.16.0",
|
|
4
4
|
"description": "CLI for scaffolding production-ready Flutter SaaS apps with Firebase, Supabase, or API REST backends.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"kasy": "./bin/kasy.js"
|
|
@@ -39,10 +39,12 @@
|
|
|
39
39
|
"validate": "node ./bin/kasy.js validate --analyze-only",
|
|
40
40
|
"extract:patch": "node ./scripts/extract_patch.js",
|
|
41
41
|
"check:firebase": "node ./scripts/check-firebase-template.js",
|
|
42
|
+
"test": "for f in test/*.test.js; do node \"$f\" || exit 1; done",
|
|
42
43
|
"test:google-ios": "node ./test/google-ios-url-scheme.test.js",
|
|
43
44
|
"test:apple-release": "node ./test/apple-release.test.js",
|
|
44
45
|
"test:localize-docs": "node ./test/localize-release-docs.test.js",
|
|
45
|
-
"test:i18n-accents": "node ./test/i18n-accents.test.js"
|
|
46
|
+
"test:i18n-accents": "node ./test/i18n-accents.test.js",
|
|
47
|
+
"lint": "eslint bin lib scripts"
|
|
46
48
|
},
|
|
47
49
|
"dependencies": {
|
|
48
50
|
"@clack/prompts": "^1.4.0",
|
|
@@ -51,7 +53,10 @@
|
|
|
51
53
|
"fs-extra": "^11.2.0",
|
|
52
54
|
"gradient-string": "^1.2.0",
|
|
53
55
|
"kleur": "^4.1.5",
|
|
54
|
-
"
|
|
56
|
+
"pngjs": "^7.0.0",
|
|
55
57
|
"yaml": "^2.4.2"
|
|
58
|
+
},
|
|
59
|
+
"devDependencies": {
|
|
60
|
+
"eslint": "^9.39.4"
|
|
56
61
|
}
|
|
57
62
|
}
|
|
@@ -220,17 +220,18 @@ class MyWidgetWidget : GlanceAppWidget() {
|
|
|
220
220
|
return DefaultStrings(greeting, hello)
|
|
221
221
|
}
|
|
222
222
|
|
|
223
|
-
/// Builds the
|
|
224
|
-
/// app icon
|
|
225
|
-
///
|
|
226
|
-
///
|
|
223
|
+
/// Builds the exact Intent the system launcher fires when the user taps
|
|
224
|
+
/// the app icon. We must NOT add extra flags here — getLaunchIntentForPackage
|
|
225
|
+
/// already returns `FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_RESET_IF_NEEDED`,
|
|
226
|
+
/// which is the same combo the launcher uses. Adding `CLEAR_TOP` destroys
|
|
227
|
+
/// go_router's navigation stack on warm starts and lands the user on the
|
|
228
|
+
/// errorBuilder ("404 - Page not found").
|
|
227
229
|
private fun launchAppIntent(context: Context): Intent {
|
|
228
|
-
return
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
}
|
|
230
|
+
return context.packageManager.getLaunchIntentForPackage(context.packageName)
|
|
231
|
+
?: Intent(context, MainActivity::class.java).apply {
|
|
232
|
+
action = Intent.ACTION_MAIN
|
|
233
|
+
addCategory(Intent.CATEGORY_LAUNCHER)
|
|
234
|
+
flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
|
235
|
+
}
|
|
235
236
|
}
|
|
236
237
|
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/templates/firebase/android/app/src/main/res/drawable-night-xhdpi/android12splash.png
CHANGED
|
Binary file
|
package/templates/firebase/android/app/src/main/res/drawable-night-xxhdpi/android12splash.png
CHANGED
|
Binary file
|
package/templates/firebase/android/app/src/main/res/drawable-night-xxxhdpi/android12splash.png
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/templates/firebase/android/app/src/main/res/drawable-xxhdpi/ic_launcher_background.png
ADDED
|
Binary file
|
package/templates/firebase/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png
ADDED
|
Binary file
|
|
Binary file
|
package/templates/firebase/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_background.png
ADDED
|
Binary file
|
package/templates/firebase/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png
ADDED
|
Binary file
|
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
-
<!-- Static preview shown in the
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
<!-- Static preview shown in the widget gallery (Android 12+).
|
|
3
|
+
Only uses Views allowed by RemoteViews — no <Space>, no
|
|
4
|
+
paddingHorizontal/Vertical, no fontFamily. Anything outside
|
|
5
|
+
the allowlist makes the launcher silently fall back to a gray box. -->
|
|
6
6
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
7
7
|
android:layout_width="match_parent"
|
|
8
8
|
android:layout_height="match_parent"
|
|
9
9
|
android:background="@drawable/widget_gradient_bg"
|
|
10
10
|
android:orientation="vertical"
|
|
11
|
-
android:
|
|
11
|
+
android:paddingLeft="16dp"
|
|
12
|
+
android:paddingRight="16dp"
|
|
13
|
+
android:paddingTop="16dp"
|
|
14
|
+
android:paddingBottom="16dp">
|
|
12
15
|
|
|
13
16
|
<TextView
|
|
14
17
|
android:layout_width="wrap_content"
|
|
15
18
|
android:layout_height="wrap_content"
|
|
16
|
-
android:fontFamily="sans-serif-medium"
|
|
17
19
|
android:text="Boa noite"
|
|
18
20
|
android:textColor="#8CFFFFFF"
|
|
19
21
|
android:textSize="11sp" />
|
|
@@ -24,20 +26,25 @@
|
|
|
24
26
|
android:layout_marginTop="4dp"
|
|
25
27
|
android:text="Olá!"
|
|
26
28
|
android:textColor="#FFFFFFFF"
|
|
27
|
-
android:textSize="
|
|
29
|
+
android:textSize="22sp"
|
|
28
30
|
android:textStyle="bold" />
|
|
29
31
|
|
|
30
|
-
|
|
32
|
+
<!-- Filler row uses TextView with empty text + weight to push the pill
|
|
33
|
+
to the bottom (Space isn't on the RemoteViews allowlist). -->
|
|
34
|
+
<TextView
|
|
31
35
|
android:layout_width="match_parent"
|
|
32
36
|
android:layout_height="0dp"
|
|
33
|
-
android:layout_weight="1"
|
|
37
|
+
android:layout_weight="1"
|
|
38
|
+
android:text="" />
|
|
34
39
|
|
|
35
40
|
<TextView
|
|
36
41
|
android:layout_width="wrap_content"
|
|
37
42
|
android:layout_height="wrap_content"
|
|
38
43
|
android:background="@drawable/widget_pro_pill_bg"
|
|
39
|
-
android:
|
|
40
|
-
android:
|
|
44
|
+
android:paddingLeft="10dp"
|
|
45
|
+
android:paddingRight="10dp"
|
|
46
|
+
android:paddingTop="5dp"
|
|
47
|
+
android:paddingBottom="5dp"
|
|
41
48
|
android:text="⭐ PRO"
|
|
42
49
|
android:textColor="#FFFFD700"
|
|
43
50
|
android:textSize="11sp"
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
|
3
|
+
<background android:drawable="@drawable/ic_launcher_background"/>
|
|
4
|
+
<foreground>
|
|
5
|
+
<inset
|
|
6
|
+
android:drawable="@drawable/ic_launcher_foreground"
|
|
7
|
+
android:inset="16%" />
|
|
8
|
+
</foreground>
|
|
9
|
+
</adaptive-icon>
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -22,6 +22,7 @@ export 'kasy_dialog.dart';
|
|
|
22
22
|
export 'kasy_otp_verification_bottom_sheet.dart';
|
|
23
23
|
export 'kasy_skeleton.dart';
|
|
24
24
|
export 'kasy_swipe_action.dart';
|
|
25
|
+
export 'kasy_tabs.dart';
|
|
25
26
|
export 'kasy_text_area.dart';
|
|
26
27
|
export 'kasy_text_field.dart';
|
|
27
28
|
export 'kasy_text_field_otp.dart';
|