Current section

Files

Jump to
mob_dev lib mix tasks mob.icon.ex
Raw

lib/mix/tasks/mob.icon.ex

defmodule Mix.Tasks.Mob.Icon do
use Mix.Task
@shortdoc "Generate or regenerate app icons"
@moduledoc """
Generates platform icons for the current Mob project.
Must be run from the project root (the directory containing `mix.exs`).
mix mob.icon # random robot avatar
mix mob.icon --source PATH # resize an existing image
mix mob.icon --source PATH --adaptive # also emit adaptive Android icons
mix mob.icon --source PATH --adaptive --adaptive-bg "#E8B53C"
## Output
Writes icons into the current project directory:
- `android/app/src/main/res/mipmap-*/ic_launcher.png` (legacy)
- `ios/Assets.xcassets/AppIcon.appiconset/icon_*.png`
- `ios/Assets.xcassets/AppIcon.appiconset/Contents.json`
- `icon_source.png` (1024×1024 master, only when generating)
With `--adaptive`, also writes:
- `android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml`
- `android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml`
- `android/app/src/main/res/mipmap-*/ic_launcher_foreground.png`
- `android/app/src/main/res/values/ic_launcher_background.xml`
Adaptive icons are what modern Android launchers (Pixel, Samsung, Moto…)
expect: a foreground layer + a background colour, masked by the launcher
to whatever shape it prefers (circle, squircle, teardrop). Without them,
legacy icons get shrunk inside a launcher-supplied white circle.
## Under the hood
`mix mob.icon` uses the `image` Elixir library (backed by `libvips`) to
resize a 1024×1024 source PNG into every required platform size.
No external tools (ImageMagick, Xcode, etc.) are required — `libvips` is
bundled as a precompiled NIF via the `image` dependency.
"""
@switches [source: :string, adaptive: :boolean, adaptive_bg: :string]
@impl Mix.Task
def run(argv) do
{opts, _args, _} = OptionParser.parse(argv, strict: @switches)
project_dir = File.cwd!()
unless File.exists?(Path.join(project_dir, "mix.exs")) do
Mix.raise("No mix.exs found. Run mix mob.icon from your project root.")
end
# `--adaptive-bg` (if given) sets the flatten background for the opaque iOS
# icons too, so both platforms share one background colour; otherwise it's
# sampled from the source per platform.
icon_opts = if opts[:adaptive_bg], do: [background_color: opts[:adaptive_bg]], else: []
source =
case opts[:source] do
nil ->
Mix.shell().info("Generating random robot icon...")
MobDev.IconGenerator.generate_random(project_dir, icon_opts)
Path.join(project_dir, "icon_source.png")
source ->
unless File.exists?(source) do
Mix.raise("Source file not found: #{source}")
end
Mix.shell().info("Resizing icon from #{source}...")
MobDev.IconGenerator.generate_from_source(source, project_dir, icon_opts)
source
end
if opts[:adaptive] do
Mix.shell().info("Generating adaptive Android icons...")
MobDev.IconGenerator.generate_adaptive(source, project_dir, icon_opts)
end
Mix.shell().info("Icons written to #{project_dir}")
Mix.shell().info(" Android: android/app/src/main/res/mipmap-*/ic_launcher.png")
if opts[:adaptive] do
Mix.shell().info(
" Android (adaptive): mipmap-anydpi-v26/, ic_launcher_foreground.png, values/ic_launcher_background.xml"
)
end
Mix.shell().info(" iOS: ios/Assets.xcassets/AppIcon.appiconset/")
end
end