Rename application identifier to space.rcmd.android.sizzle
Global rename of the app/package identifier from com.reactorcoremeltdown.sizzletracker to space.rcmd.android.sizzle: - build.gradle.kts: applicationId + namespace. - Move the Kotlin source tree (main + test) and rewrite every package/import. - native_audio.cpp: JNI symbol names (Java_space_rcmd_android_sizzle_...) so the Oboe callback still links against the relocated NativeAudioBridge. - docs: package/file-map header. The desktop-project GitHub URLs (github.com/reactorcoremeltdown/sizzletracker) and the Reactorcoremeltdown copyright/SPDX author identity are intentionally left unchanged. Note: the new applicationId installs as a separate package (existing installs won't upgrade in place). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
95
app/src/main/java/space/rcmd/android/sizzle/SizzleApp.kt
Normal file
95
app/src/main/java/space/rcmd/android/sizzle/SizzleApp.kt
Normal file
@@ -0,0 +1,95 @@
|
||||
// SPDX-FileCopyrightText: 2026 Reactorcoremeltdown
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package space.rcmd.android.sizzle
|
||||
|
||||
import android.app.Application
|
||||
import space.rcmd.android.sizzle.audio.AudioEngine
|
||||
import space.rcmd.android.sizzle.input.BindingStore
|
||||
import space.rcmd.android.sizzle.input.GamepadInput
|
||||
import space.rcmd.android.sizzle.input.InputRouter
|
||||
import space.rcmd.android.sizzle.input.KeyboardInput
|
||||
import space.rcmd.android.sizzle.input.MidiInput
|
||||
import space.rcmd.android.sizzle.io.PresetLibrary
|
||||
import space.rcmd.android.sizzle.io.ProjectStore
|
||||
import space.rcmd.android.sizzle.io.SettingsStore
|
||||
import space.rcmd.android.sizzle.io.SongLibrary
|
||||
import space.rcmd.android.sizzle.io.ThemeLibrary
|
||||
import space.rcmd.android.sizzle.model.Project
|
||||
import java.io.File
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
/**
|
||||
* The Application object is our (very small) dependency container. Instead of a
|
||||
* DI framework — overkill for a learning-friendly codebase — the handful of
|
||||
* app-wide singletons live here and are reached via `application as SizzleApp`.
|
||||
*
|
||||
* These objects outlive any single screen so playback and input keep working
|
||||
* across rotation and tab switches.
|
||||
*/
|
||||
class SizzleApp : Application() {
|
||||
|
||||
/** The single in-memory song. Everything edits this one object. */
|
||||
val project: Project by lazy { Project() }
|
||||
|
||||
/** Neutral input bus shared by touch / keyboard / gamepad / MIDI. */
|
||||
val inputRouter: InputRouter by lazy { InputRouter() }
|
||||
|
||||
/** The real-time sound engine (started/stopped by the PlaybackService). */
|
||||
val audioEngine: AudioEngine by lazy { AudioEngine() }
|
||||
|
||||
// Input source handlers are singletons so their (re)bindable maps are shared
|
||||
// between MainActivity (which feeds them hardware events) and the Settings
|
||||
// screen (which edits their bindings via MIDI-learn / gamepad rebind).
|
||||
val keyboardInput: KeyboardInput by lazy { KeyboardInput(inputRouter) }
|
||||
val gamepadInput: GamepadInput by lazy { GamepadInput(inputRouter) }
|
||||
val midiInput: MidiInput by lazy { MidiInput(this, inputRouter) }
|
||||
|
||||
/** Persists the input binding maps across restarts (DataStore-backed). */
|
||||
val bindingStore: BindingStore by lazy { BindingStore(this) }
|
||||
|
||||
/** On-disk instrument/effect preset library in the app's scoped storage. */
|
||||
val presetLibrary: PresetLibrary by lazy { PresetLibrary(File(filesDir, "presets")) }
|
||||
|
||||
/** On-disk `.sng` song library in the app's scoped storage. */
|
||||
val songLibrary: SongLibrary by lazy { SongLibrary(File(filesDir, "songs")) }
|
||||
|
||||
/** On-disk user colour-theme library (`.szt`) in the app's scoped storage. */
|
||||
val themeLibrary: ThemeLibrary by lazy { ThemeLibrary(File(filesDir, "themes")) }
|
||||
|
||||
/** Autosave of the whole project (crash recovery / resume). */
|
||||
val projectStore: ProjectStore by lazy { ProjectStore(File(filesDir, "autosave.sng")) }
|
||||
|
||||
/** App-level settings persistence (theme, audio backend). */
|
||||
val settingsStore: SettingsStore by lazy { SettingsStore(this) }
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
// Crash recovery: restore the last autosaved project before the engine or
|
||||
// UI read it, so a crash / process-death resumes where the user left off.
|
||||
projectStore.restore(project)
|
||||
// Reload the sample audio for any preset-backed Sampler/SoundFont slots —
|
||||
// the autosave keeps file references, not the decoded PCM, so this makes a
|
||||
// selected preset's samples sound again on a fresh start.
|
||||
presetLibrary.rehydrate(project)
|
||||
// Write the built-in factory presets (e.g. the Ambience reverb spaces) to the
|
||||
// on-disk library on first launch so they appear in the standard browser.
|
||||
presetLibrary.seedFactoryPresets()
|
||||
audioEngine.setProject(project)
|
||||
// Restore saved bindings before any hardware event can arrive. This is a
|
||||
// single small read; blocking briefly at cold start keeps the input maps
|
||||
// authoritative from the first keypress rather than racing an async load.
|
||||
runBlocking { bindingStore.loadInto(keyboardInput, gamepadInput, midiInput) }
|
||||
installCrashAutosave()
|
||||
}
|
||||
|
||||
/** Flush an autosave of the current project on any uncaught exception, then let
|
||||
* the default handler crash/report as usual — so state survives a crash. */
|
||||
private fun installCrashAutosave() {
|
||||
val previous = Thread.getDefaultUncaughtExceptionHandler()
|
||||
Thread.setDefaultUncaughtExceptionHandler { thread, throwable ->
|
||||
runCatching { projectStore.save(project) }
|
||||
previous?.uncaughtException(thread, throwable)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user