kasy-cli 1.15.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/lib/commands/icon.js +29 -1
- package/lib/commands/run.js +61 -2
- package/lib/scaffold/backends/api/pubspec.yaml.tpl +2 -0
- package/lib/scaffold/backends/supabase/pubspec.yaml.tpl +2 -0
- package/lib/utils/i18n/messages-en.js +7 -0
- package/lib/utils/i18n/messages-es.js +7 -0
- package/lib/utils/i18n/messages-pt.js +7 -0
- package/lib/utils/png-padding.js +134 -2
- package/package.json +1 -1
- package/templates/firebase/android/app/src/main/res/drawable-hdpi/android12splash.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-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-xxhdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-xxxhdpi/android12splash.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/web/index.html +3 -0
package/lib/commands/icon.js
CHANGED
|
@@ -7,11 +7,17 @@ const ui = require('../utils/ui');
|
|
|
7
7
|
const { printCompactHeader } = require('../utils/brand');
|
|
8
8
|
const { createTranslator, detectDefaultLanguage } = require('../utils/i18n');
|
|
9
9
|
const { inspectPng } = require('./splash');
|
|
10
|
+
const {
|
|
11
|
+
writeAndroidAdaptiveBackground,
|
|
12
|
+
writeTransparentSquare,
|
|
13
|
+
} = require('../utils/png-padding');
|
|
10
14
|
|
|
11
15
|
const execAsync = promisify(exec);
|
|
12
16
|
|
|
13
17
|
const ASSETS_DIR = path.join('assets', 'images');
|
|
14
18
|
const ICON_NAME = 'icon.png';
|
|
19
|
+
const ANDROID_BG_NAME = 'icon_android.png';
|
|
20
|
+
const ANDROID_FG_EMPTY_NAME = 'icon_foreground_empty.png';
|
|
15
21
|
|
|
16
22
|
async function assertKasyProject(projectDir, t) {
|
|
17
23
|
const kitSetupPath = path.join(projectDir, 'kit_setup.json');
|
|
@@ -85,13 +91,35 @@ async function runIcon(projectDir, options = {}) {
|
|
|
85
91
|
ui.note(warnings.map((w) => `${kleur.yellow('⚠')} ${w}`).join('\n'), t('icon.warn.title'));
|
|
86
92
|
}
|
|
87
93
|
|
|
88
|
-
const
|
|
94
|
+
const assetsDir = path.join(projectDir, ASSETS_DIR);
|
|
95
|
+
const dest = path.join(assetsDir, ICON_NAME);
|
|
96
|
+
const androidBgDest = path.join(assetsDir, ANDROID_BG_NAME);
|
|
97
|
+
const androidFgEmptyDest = path.join(assetsDir, ANDROID_FG_EMPTY_NAME);
|
|
89
98
|
|
|
90
99
|
const copySpinner = ui.spinner();
|
|
91
100
|
copySpinner.start(t('icon.copying'));
|
|
92
101
|
await fs.copy(imagePath, dest, { overwrite: true });
|
|
93
102
|
copySpinner.stop(t('icon.copied'));
|
|
94
103
|
|
|
104
|
+
const adaptiveSpinner = ui.spinner();
|
|
105
|
+
adaptiveSpinner.start(t('icon.adaptive.generating'));
|
|
106
|
+
let adaptiveInfo;
|
|
107
|
+
try {
|
|
108
|
+
adaptiveInfo = await writeAndroidAdaptiveBackground(dest, androidBgDest);
|
|
109
|
+
if (!(await fs.pathExists(androidFgEmptyDest))) {
|
|
110
|
+
await writeTransparentSquare(androidFgEmptyDest);
|
|
111
|
+
}
|
|
112
|
+
adaptiveSpinner.stop(t('icon.adaptive.generated'));
|
|
113
|
+
} catch (err) {
|
|
114
|
+
adaptiveSpinner.stop(`⚠ ${t('icon.adaptive.failed')}`);
|
|
115
|
+
ui.log.message(kleur.dim(err.message || String(err)));
|
|
116
|
+
process.exit(1);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (!adaptiveInfo.color.sampled) {
|
|
120
|
+
ui.note(t('icon.adaptive.fallbackColor'), t('icon.adaptive.fallbackTitle'));
|
|
121
|
+
}
|
|
122
|
+
|
|
95
123
|
if (options.skipGenerate) {
|
|
96
124
|
ui.note(t('icon.skipGenerate.hint'), t('icon.skipGenerate.title'));
|
|
97
125
|
ui.outro(t('icon.done'));
|
package/lib/commands/run.js
CHANGED
|
@@ -1,10 +1,52 @@
|
|
|
1
1
|
const path = require('node:path');
|
|
2
|
+
const { spawnSync } = require('node:child_process');
|
|
2
3
|
const fs = require('fs-extra');
|
|
3
4
|
const kleur = require('kleur');
|
|
5
|
+
const ui = require('../utils/ui');
|
|
4
6
|
const { createTranslator, detectDefaultLanguage } = require('../utils/i18n');
|
|
5
7
|
const { printCompactHeader } = require('../utils/brand');
|
|
6
8
|
const { spawnFlutterWithSpinner } = require('../utils/flutter-run');
|
|
7
9
|
|
|
10
|
+
function listFlutterDevices(projectDir) {
|
|
11
|
+
const res = spawnSync('flutter', ['devices', '--machine'], {
|
|
12
|
+
cwd: projectDir,
|
|
13
|
+
encoding: 'utf8',
|
|
14
|
+
});
|
|
15
|
+
if (res.status !== 0) return [];
|
|
16
|
+
try {
|
|
17
|
+
const parsed = JSON.parse(res.stdout);
|
|
18
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
19
|
+
} catch {
|
|
20
|
+
return [];
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function classifyDevice(device) {
|
|
25
|
+
const platform = (device.targetPlatform || '').toLowerCase();
|
|
26
|
+
if (platform === 'ios') return device.emulator ? 'ios-simulator' : 'ios-device';
|
|
27
|
+
if (platform.startsWith('android')) {
|
|
28
|
+
return device.emulator ? 'android-emulator' : 'android-device';
|
|
29
|
+
}
|
|
30
|
+
if (platform.startsWith('web')) return 'web';
|
|
31
|
+
if (platform.startsWith('darwin')) return 'macos';
|
|
32
|
+
if (platform.startsWith('linux')) return 'linux';
|
|
33
|
+
if (platform.startsWith('windows')) return 'windows';
|
|
34
|
+
return 'unknown';
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async function pickDevice(devices, t) {
|
|
38
|
+
if (devices.length === 0) return null;
|
|
39
|
+
if (devices.length === 1) return devices[0];
|
|
40
|
+
const choice = await ui.select({
|
|
41
|
+
message: t('run.prompt.pickDevice'),
|
|
42
|
+
options: devices.map((d) => ({
|
|
43
|
+
value: d.id,
|
|
44
|
+
label: `${d.name} ${kleur.dim(`(${classifyDevice(d)})`)}`,
|
|
45
|
+
})),
|
|
46
|
+
});
|
|
47
|
+
return devices.find((d) => d.id === choice) || null;
|
|
48
|
+
}
|
|
49
|
+
|
|
8
50
|
/**
|
|
9
51
|
* Read dart-define args from .vscode/launch.json.
|
|
10
52
|
* Returns the args array from the matching config (dev or prod).
|
|
@@ -35,8 +77,11 @@ async function runRun(directory, options = {}) {
|
|
|
35
77
|
throw new Error(t('run.error.notFlutterProject'));
|
|
36
78
|
}
|
|
37
79
|
|
|
38
|
-
// Resolve device flag
|
|
80
|
+
// Resolve device flag. If none of the platform shortcuts or -d is set,
|
|
81
|
+
// ask the user when more than one device is available — `flutter run`
|
|
82
|
+
// bails out with "More than one device connected" otherwise.
|
|
39
83
|
const deviceArgs = [];
|
|
84
|
+
let resolvedDeviceLabel = null;
|
|
40
85
|
if (options.web) {
|
|
41
86
|
deviceArgs.push('-d', 'chrome');
|
|
42
87
|
} else if (options.ios) {
|
|
@@ -45,6 +90,20 @@ async function runRun(directory, options = {}) {
|
|
|
45
90
|
deviceArgs.push('-d', 'android');
|
|
46
91
|
} else if (options.device) {
|
|
47
92
|
deviceArgs.push('-d', options.device);
|
|
93
|
+
} else {
|
|
94
|
+
const devices = listFlutterDevices(projectDir);
|
|
95
|
+
if (devices.length > 1) {
|
|
96
|
+
printCompactHeader(t);
|
|
97
|
+
const picked = await pickDevice(devices, t);
|
|
98
|
+
if (!picked) {
|
|
99
|
+
console.log(kleur.yellow(` ⚠ ${t('run.warn.nothingSelected')}`));
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
deviceArgs.push('-d', picked.id);
|
|
103
|
+
resolvedDeviceLabel = `${picked.name} (${picked.id})`;
|
|
104
|
+
}
|
|
105
|
+
// 0 or 1 device → let flutter handle it; it picks the only one or
|
|
106
|
+
// prints its own "no devices" message.
|
|
48
107
|
}
|
|
49
108
|
|
|
50
109
|
// Read dart-defines from .vscode/launch.json (skip if --no-defines)
|
|
@@ -62,7 +121,7 @@ async function runRun(directory, options = {}) {
|
|
|
62
121
|
? 'ios'
|
|
63
122
|
: options.android
|
|
64
123
|
? 'android'
|
|
65
|
-
: options.device || null;
|
|
124
|
+
: options.device || resolvedDeviceLabel || null;
|
|
66
125
|
const summaryParts = [];
|
|
67
126
|
if (envValue) summaryParts.push(`ENV=${envValue}`);
|
|
68
127
|
if (deviceLabel) summaryParts.push(`device: ${deviceLabel}`);
|
|
@@ -98,6 +98,8 @@ flutter_launcher_icons:
|
|
|
98
98
|
android: ic_launcher
|
|
99
99
|
ios: true
|
|
100
100
|
remove_alpha_ios: true
|
|
101
|
+
adaptive_icon_background: assets/images/icon_android.png
|
|
102
|
+
adaptive_icon_foreground: assets/images/icon_foreground_empty.png
|
|
101
103
|
web:
|
|
102
104
|
generate: true
|
|
103
105
|
image_path: assets/images/favicon.png
|
|
@@ -100,6 +100,8 @@ flutter_launcher_icons:
|
|
|
100
100
|
android: ic_launcher
|
|
101
101
|
ios: true
|
|
102
102
|
remove_alpha_ios: true
|
|
103
|
+
adaptive_icon_background: assets/images/icon_android.png
|
|
104
|
+
adaptive_icon_foreground: assets/images/icon_foreground_empty.png
|
|
103
105
|
web:
|
|
104
106
|
generate: true
|
|
105
107
|
image_path: assets/images/favicon.png
|
|
@@ -645,6 +645,8 @@ module.exports = {
|
|
|
645
645
|
// run command
|
|
646
646
|
'cli.command.run.description': 'Run your app on phone, simulator, or browser',
|
|
647
647
|
'run.launching': 'Launching Flutter app...',
|
|
648
|
+
'run.prompt.pickDevice': 'Multiple devices detected. Which one do you want to run on?',
|
|
649
|
+
'run.warn.nothingSelected': 'No device selected.',
|
|
648
650
|
'run.updateHint.prefix': 'Project improvements available —',
|
|
649
651
|
'run.updateHint.suffix': 'to see what\'s new',
|
|
650
652
|
'run.spinner.building': 'Starting Flutter…',
|
|
@@ -746,6 +748,11 @@ module.exports = {
|
|
|
746
748
|
'icon.validated': 'PNG looks good',
|
|
747
749
|
'icon.copying': 'Copying icon to assets/images/icon.png...',
|
|
748
750
|
'icon.copied': 'Icon copied',
|
|
751
|
+
'icon.adaptive.generating': 'Generating Android adaptive variant (fills the launcher circle)...',
|
|
752
|
+
'icon.adaptive.generated': 'Android adaptive icon generated',
|
|
753
|
+
'icon.adaptive.failed': 'Failed to generate Android adaptive icon',
|
|
754
|
+
'icon.adaptive.fallbackTitle': 'Android background',
|
|
755
|
+
'icon.adaptive.fallbackColor': 'Could not sample a background color from the PNG (transparent). Used white. Edit icon_android.png if you want a different color.',
|
|
749
756
|
'icon.generating': 'Regenerating native icons (Android + iOS)...',
|
|
750
757
|
'icon.generated': 'Native icons regenerated',
|
|
751
758
|
'icon.done': 'Done. Uninstall the app from the device and reinstall to see the new icon.',
|
|
@@ -678,6 +678,8 @@ module.exports = {
|
|
|
678
678
|
'reset.warn.launcherCacheFailed': 'No se pudo limpiar la caché del launcher.',
|
|
679
679
|
'reset.warn.launcherNotDetected': 'Launcher por defecto no detectado — saltando limpieza de caché.',
|
|
680
680
|
'run.launching': 'Iniciando app Flutter...',
|
|
681
|
+
'run.prompt.pickDevice': 'Varios dispositivos detectados. ¿En cuál quieres ejecutar?',
|
|
682
|
+
'run.warn.nothingSelected': 'Ningún dispositivo seleccionado.',
|
|
681
683
|
'run.updateHint.prefix': 'Mejoras disponibles para el proyecto —',
|
|
682
684
|
'run.updateHint.suffix': 'para ver las novedades',
|
|
683
685
|
'run.spinner.building': 'Iniciando Flutter…',
|
|
@@ -744,6 +746,11 @@ module.exports = {
|
|
|
744
746
|
'icon.validated': 'PNG está bien',
|
|
745
747
|
'icon.copying': 'Copiando ícono a assets/images/icon.png...',
|
|
746
748
|
'icon.copied': 'Ícono copiado',
|
|
749
|
+
'icon.adaptive.generating': 'Generando variante adaptive para Android (llena el círculo del launcher)...',
|
|
750
|
+
'icon.adaptive.generated': 'Adaptive icon Android generado',
|
|
751
|
+
'icon.adaptive.failed': 'Fallo al generar adaptive icon Android',
|
|
752
|
+
'icon.adaptive.fallbackTitle': 'Fondo Android',
|
|
753
|
+
'icon.adaptive.fallbackColor': 'No pude muestrear el color de fondo del PNG (transparente). Usé blanco. Edita icon_android.png si quieres otro color.',
|
|
747
754
|
'icon.generating': 'Regenerando íconos nativos (Android + iOS)...',
|
|
748
755
|
'icon.generated': 'Íconos nativos regenerados',
|
|
749
756
|
'icon.done': 'Listo. Desinstala el app del dispositivo y reinstala para ver el nuevo ícono.',
|
|
@@ -678,6 +678,8 @@ module.exports = {
|
|
|
678
678
|
'reset.warn.launcherCacheFailed': 'Não foi possível limpar o cache do launcher.',
|
|
679
679
|
'reset.warn.launcherNotDetected': 'Launcher padrão não detectado — pulando limpeza de cache.',
|
|
680
680
|
'run.launching': 'Iniciando app Flutter...',
|
|
681
|
+
'run.prompt.pickDevice': 'Vários dispositivos detectados. Em qual deles rodar?',
|
|
682
|
+
'run.warn.nothingSelected': 'Nenhum dispositivo selecionado.',
|
|
681
683
|
'run.updateHint.prefix': 'Melhorias disponíveis para o projeto —',
|
|
682
684
|
'run.updateHint.suffix': 'para ver o que há de novo',
|
|
683
685
|
'run.spinner.building': 'Iniciando Flutter…',
|
|
@@ -744,6 +746,11 @@ module.exports = {
|
|
|
744
746
|
'icon.validated': 'PNG está bom',
|
|
745
747
|
'icon.copying': 'Copiando ícone para assets/images/icon.png...',
|
|
746
748
|
'icon.copied': 'Ícone copiado',
|
|
749
|
+
'icon.adaptive.generating': 'Gerando versão adaptive para Android (preenche o círculo do launcher)...',
|
|
750
|
+
'icon.adaptive.generated': 'Adaptive icon Android gerado',
|
|
751
|
+
'icon.adaptive.failed': 'Falha ao gerar adaptive icon Android',
|
|
752
|
+
'icon.adaptive.fallbackTitle': 'Fundo Android',
|
|
753
|
+
'icon.adaptive.fallbackColor': 'Não consegui amostrar a cor de fundo do PNG (transparente). Usei branco. Se quiser outra cor, edite icon_android.png à mão.',
|
|
747
754
|
'icon.generating': 'Regenerando ícones nativos (Android + iOS)...',
|
|
748
755
|
'icon.generated': 'Ícones nativos regenerados',
|
|
749
756
|
'icon.done': 'Pronto. Desinstale o app do dispositivo e reinstale para ver o novo ícone.',
|
package/lib/utils/png-padding.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
const fsp = require('node:fs/promises');
|
|
2
2
|
const { PNG } = require('pngjs');
|
|
3
3
|
|
|
4
|
-
const ANDROID12_SAFE_RATIO = 0.
|
|
4
|
+
const ANDROID12_SAFE_RATIO = 0.4;
|
|
5
|
+
const ANDROID_ADAPTIVE_LOGO_RATIO = 0.65;
|
|
6
|
+
const ANDROID_ADAPTIVE_CANVAS = 1024;
|
|
5
7
|
|
|
6
8
|
async function readPng(filePath) {
|
|
7
9
|
const buffer = await fsp.readFile(filePath);
|
|
@@ -117,4 +119,134 @@ async function writeAndroid12Variant(srcPath, dstPath, safeRatio = ANDROID12_SAF
|
|
|
117
119
|
return { canvasSize, logoW, logoH };
|
|
118
120
|
}
|
|
119
121
|
|
|
120
|
-
|
|
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
|
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
|
|
Binary file
|