mirror of
https://github.com/saphid/frame-control.git
synced 2026-10-06 05:02:50 +02:00
Repair alias-only launchers for flat apps too; never fail on a VR alias
Review follow-up. inspect() now returns 'repairable' and the filters it can patch: the real activity's VR MAIN filter, or, when LAUNCHER/VR sits only on an <activity-alias>, any real MAIN filter with a category to copy. install() and patch() repair on 'repairable' instead of 'vr_activity', so a flat Godot 4 export is fixed and a VR category only on the alias no longer aborts install. Tests cover both shapes, an already-launchable activity with an alias, and an end-to-end patch. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
077eab2b79
commit
e1c0ac983c
3 files changed
+57
-23
No files matched your search
+6
-5
@@ -149,7 +149,7 @@ def install(apk_path, flatscreen=None, name=None, source=None, icon_png=None, xr
|
||||
# changes calls SteamVR would otherwise reject.
|
||||
add = xr_compat_files(apk_path) if (info['vr'] if xr_compat is None else xr_compat) else {}
|
||||
with _install_lock:
|
||||
if add or (not info['launchable'] and info['vr_activity']):
|
||||
if add or info['repairable']:
|
||||
with tempfile.TemporaryDirectory(prefix='frame-vr-') as tmp:
|
||||
patched = os.path.join(tmp, 'app.apk')
|
||||
info['patched'] = patch(apk_path, patched, add)['patched']
|
||||
@@ -320,8 +320,8 @@ def patch(src, dst, add=None):
|
||||
info = apk_info(src)
|
||||
with zipfile.ZipFile(src) as z:
|
||||
original = frame_apk._read(z, 'AndroidManifest.xml', frame_apk.MAX_MANIFEST)
|
||||
manifest = add_launcher_category(original) if info['vr_activity'] else original
|
||||
if not info['launchable'] and not info['vr_activity']:
|
||||
manifest = add_launcher_category(original) if info['repairable'] else original
|
||||
if not info['launchable'] and not info['repairable']:
|
||||
raise FrameError('APK has no MAIN/LAUNCHER or patchable VR activity')
|
||||
repack(src, dst, replace={'AndroidManifest.xml': manifest}, add=add)
|
||||
result = apk_info(dst)
|
||||
@@ -341,8 +341,9 @@ def main():
|
||||
if cmd == 'info':
|
||||
info = apk_info(args[0])
|
||||
print(frame_apk_versions.describe(info))
|
||||
if info.get('vr'):
|
||||
print('VR app' + ('' if info.get('launchable') else '; Frame Control adds the LAUNCHER entry Lepton needs'))
|
||||
fix = '; Frame Control adds the LAUNCHER entry Lepton needs' if info.get('repairable') else ''
|
||||
if info.get('vr') or fix:
|
||||
print(('VR app' if info.get('vr') else 'Android app') + fix)
|
||||
for note in info.get('vr_issues', []):
|
||||
print(note)
|
||||
return
|
||||
|
||||
+9
-5
@@ -35,9 +35,13 @@ def inspect(data):
|
||||
mains = [f for f in filters if MAIN in f['actions']]
|
||||
vr_filters = [f for f in mains if VR & f['categories']]
|
||||
# Lepton's apk-info-extractor ignores <activity-alias>; Godot 4 puts LAUNCHER only there.
|
||||
return {'launchable': any(LAUNCHER in f['categories'] for f in mains if not f['alias']),
|
||||
'vr_activity': bool(vr_filters), 'vr': bool(vr_filters) or samsung}, \
|
||||
[f for f in vr_filters if not f['alias']]
|
||||
real = [f for f in mains if not f['alias']]
|
||||
targets = [f for f in real if VR & f['categories']]
|
||||
if not targets and any(LAUNCHER in f['categories'] or VR & f['categories'] for f in mains if f['alias']):
|
||||
targets = [f for f in real if f['templates']] # the patch copies an existing <category>
|
||||
launchable = any(LAUNCHER in f['categories'] for f in real)
|
||||
return {'launchable': launchable, 'repairable': not launchable and bool(targets),
|
||||
'vr_activity': bool(vr_filters), 'vr': bool(vr_filters) or samsung}, targets
|
||||
|
||||
|
||||
def _append_string(chunk, text):
|
||||
@@ -64,7 +68,7 @@ def add_launcher_category(axml_bytes):
|
||||
if info['launchable']:
|
||||
return axml_bytes
|
||||
if not filters:
|
||||
raise frame_apk.ApkError('no VR activity with a MAIN intent filter to patch')
|
||||
raise frame_apk.ApkError('no activity with a MAIN intent filter that Frame Control can patch')
|
||||
target = filters[0]
|
||||
chunks = list(frame_apk._chunks(axml_bytes, 8, len(axml_bytes)))
|
||||
pool = next(c for c in chunks if c[0] == 1)
|
||||
@@ -87,7 +91,7 @@ def add_launcher_category(axml_bytes):
|
||||
struct.pack_into('<HBBI', start, a + 12, 8, 0, 3, index)
|
||||
break
|
||||
else:
|
||||
raise frame_apk.ApkError('VR category has no name attribute')
|
||||
raise frame_apk.ApkError('category has no name attribute')
|
||||
end = struct.pack('<HHI', 0x0103, hs, hs + 8) + start[8:hs] + start[hs:hs + 8]
|
||||
result = bytearray(axml_bytes[:8])
|
||||
for _, _, off, size in chunks:
|
||||
|
||||
Reference in new issue
Block a user