fix: Bug - Android app is zoomed in
Scale launcher and splash brand assets to the adaptive-icon safe zone and a smaller splash ratio so circular launcher masks and the launch splash no longer crop the cat face. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -23,6 +23,11 @@ const RES_DIR = resolve(REPO_ROOT, 'toju-app/android/app/src/main/res');
|
||||
const BRAND_BACKGROUND = { r: 74, g: 33, b: 122 };
|
||||
const BRAND_BACKGROUND_HEX = '#4A217A';
|
||||
|
||||
/** Adaptive-icon safe zone (66dp inside 108dp). Keep in sync with the rules file. */
|
||||
const ADAPTIVE_FOREGROUND_ICON_RATIO = 66 / 108;
|
||||
const LEGACY_LAUNCHER_ICON_RATIO = ADAPTIVE_FOREGROUND_ICON_RATIO;
|
||||
const SPLASH_ICON_RATIO = 0.32;
|
||||
|
||||
/** Legacy square/round launcher bitmap edge length per density. */
|
||||
const LEGACY_ICON_PX = { mdpi: 48, hdpi: 72, xhdpi: 96, xxhdpi: 144, xxxhdpi: 192 };
|
||||
|
||||
@@ -65,20 +70,36 @@ async function centeredIconOnBackground(width, height, iconRatio, background) {
|
||||
.toBuffer();
|
||||
}
|
||||
|
||||
/** Compose the brand icon centred on a transparent canvas for adaptive foreground layers. */
|
||||
async function centeredIconOnTransparentCanvas(canvasSize, iconRatio) {
|
||||
const iconSize = Math.round(canvasSize * iconRatio);
|
||||
const icon = await squareIcon(iconSize);
|
||||
|
||||
return sharp({
|
||||
create: {
|
||||
width: canvasSize,
|
||||
height: canvasSize,
|
||||
channels: 4,
|
||||
background: { r: 0, g: 0, b: 0, alpha: 0 }
|
||||
}
|
||||
})
|
||||
.composite([{ input: icon, gravity: 'center' }])
|
||||
.png()
|
||||
.toBuffer();
|
||||
}
|
||||
|
||||
async function generateLauncherIcons() {
|
||||
const written = [];
|
||||
|
||||
for (const [density, size] of Object.entries(LEGACY_ICON_PX)) {
|
||||
const bitmap = await squareIcon(size);
|
||||
const background = { ...BRAND_BACKGROUND, alpha: 1 };
|
||||
const bitmap = await centeredIconOnBackground(size, size, LEGACY_LAUNCHER_ICON_RATIO, background);
|
||||
written.push(await writePng(bitmap, `mipmap-${density}/ic_launcher.png`));
|
||||
written.push(await writePng(bitmap, `mipmap-${density}/ic_launcher_round.png`));
|
||||
}
|
||||
|
||||
// Adaptive foreground: full-bleed circle on transparent canvas. The adaptive
|
||||
// background layer paints the brand purple, so the masked result matches the
|
||||
// source disc on every launcher mask shape.
|
||||
for (const [density, size] of Object.entries(FOREGROUND_PX)) {
|
||||
const foreground = await squareIcon(size);
|
||||
const foreground = await centeredIconOnTransparentCanvas(size, ADAPTIVE_FOREGROUND_ICON_RATIO);
|
||||
written.push(await writePng(foreground, `mipmap-${density}/ic_launcher_foreground.png`));
|
||||
}
|
||||
|
||||
@@ -97,14 +118,13 @@ async function generateSplashScreens() {
|
||||
const background = { ...BRAND_BACKGROUND, alpha: 1 };
|
||||
|
||||
for (const [density, [portW, portH]] of Object.entries(SPLASH_PORTRAIT)) {
|
||||
const portrait = await centeredIconOnBackground(portW, portH, 0.4, background);
|
||||
const landscape = await centeredIconOnBackground(portH, portW, 0.4, background);
|
||||
const portrait = await centeredIconOnBackground(portW, portH, SPLASH_ICON_RATIO, background);
|
||||
const landscape = await centeredIconOnBackground(portH, portW, SPLASH_ICON_RATIO, background);
|
||||
written.push(await writePng(portrait, `drawable-port-${density}/splash.png`));
|
||||
written.push(await writePng(landscape, `drawable-land-${density}/splash.png`));
|
||||
}
|
||||
|
||||
// Base fallback splash mirrors the landscape mdpi geometry (Capacitor default).
|
||||
const base = await centeredIconOnBackground(480, 320, 0.4, background);
|
||||
const base = await centeredIconOnBackground(480, 320, SPLASH_ICON_RATIO, background);
|
||||
written.push(await writePng(base, 'drawable/splash.png'));
|
||||
|
||||
return written;
|
||||
|
||||
Reference in New Issue
Block a user