feat(android): add adaptive icons, dynamic shortcuts, in-process hot-swap, and exit-to-launcher

This commit is contained in:
1jamie
2026-08-18 20:37:25 -05:00
parent 580449b8df
commit 302b2c9591
45 changed files with 792 additions and 16 deletions
+1
View File
@@ -174,6 +174,7 @@ function Game:makeTitleState()
self:restoreSave(loaded, recovered, { freshBoot = true })
end
end,
onExit = self.onExit,
})
title.screenId = title.screenId or "TitleState"
return title
+1
View File
@@ -318,6 +318,7 @@ function Game2:showMainMenu()
onNewGame = function() self:newGame() end,
onContinue = function(save) self:continueGame(save) end,
onOption = function() self:showOptions(function() self:showMainMenu() end) end,
onExit = self.onExit,
})
end
+13
View File
@@ -67,10 +67,23 @@ local function argFlag(argv, name)
return false
end
local cachedIntentGame = nil
-- Returns version, slotId (either may be nil). Command line wins over env,
-- so a shortcut can override a machine-wide default.
function LaunchOptions.resolve(argv)
if cachedIntentGame == nil then
if love.system and love.system.getOS and love.system.getOS() == "Android"
and love.system.getLaunchGame then
cachedIntentGame = normalizeVersion(love.system.getLaunchGame()) or false
else
cachedIntentGame = false
end
end
local intentGame = cachedIntentGame or nil
local game = normalizeVersion(argValue(argv, "game"))
or intentGame
or normalizeVersion(os.getenv("POKEPORT_GAME"))
or normalizeVersion(os.getenv("POKEPORT_LAUNCH"))
local slot = argValue(argv, "slot") or os.getenv("POKEPORT_SLOT")
+28
View File
@@ -326,6 +326,32 @@ function RomImporter.isReady(version)
return marker == markerFor(version) and allRequiredFilesExist(version)
end
function RomImporter.syncAndroidShortcuts(activeVersion)
if not (love.system and love.system.getOS and love.system.getOS() == "Android"
and love.system.updateShortcuts) then
return false
end
local allVersions = { "red", "blue", "yellow", "gold" }
local ready = {}
local seen = {}
if activeVersion and RomImporter.isReady(activeVersion) then
table.insert(ready, activeVersion)
seen[activeVersion] = true
end
for _, v in ipairs(allVersions) do
if not seen[v] and RomImporter.isReady(v) then
table.insert(ready, v)
seen[v] = true
if #ready >= 4 then break end
end
end
return love.system.updateShortcuts(ready)
end
-- Load the import manifest for a version and confirm it matches that ROM.
local function sha1(data)
local digest = love.data.hash("sha1", data)
@@ -1392,6 +1418,7 @@ function RomImporter.new(onComplete, opts)
self.romName[version] = "pokemon_" .. info.id
.. ((info.id == "yellow" or info.id == "gold") and ".gbc" or ".gb")
end
RomImporter.syncAndroidShortcuts()
self:_applyLastVersionTab()
self:_queueBaseRomScan()
@@ -1786,6 +1813,7 @@ function RomImporter:_completeImport(version, prefix, displayName)
self.workState = "complete"
self.completeVersion = version
self.status = "Ready"
RomImporter.syncAndroidShortcuts(version)
-- NX launcher stays put: keep the imports/ cleanup hint instead of
-- overwriting it with a "Starting…" line that never boots from here.
if self.launcher and self.isNX and type(displayName) == "string" then
+8
View File
@@ -38,6 +38,14 @@ function Runtime.install(events, hooks, errors)
Runtime.errors = errors
end
function Runtime.reset()
Runtime.events = NullEvents
Runtime.hooks = NullHooks
Runtime.errors = nil
Runtime.currentMod = nil
Runtime.modRequire = nil
end
-- attribute a runtime failure to the mod that owns the offending record.
-- "base" is the engine's own owner id: a vanilla record that fails is a
-- console line, not something the manager can ask the player to disable.
+4 -1
View File
@@ -198,6 +198,7 @@ function TitleState.new(game, opts)
self.game = game
self.onNewGame = opts.onNewGame
self.onContinue = opts.onContinue
self.onExit = opts.onExit
-- branding comes from field.title with the shipped art as fallback, so
-- a total conversion rebrands the title without replacing the screen
local title = (game.data.field and game.data.field.title) or {}
@@ -514,7 +515,9 @@ function TitleState:openMenu()
require("src.ui.Screens").push(game, "OptionsMenu")
end })
table.insert(items, { label = Strings("EXIT GAME"), onSelect = function()
if love.event and love.event.quit then
if self.onExit then
self.onExit()
elseif love.event and love.event.quit then
love.event.quit()
end
end })