github palincat/gama v1.3

6 hours ago

Architecture and Code Organisation

  • Decomposed the monolithic gama_ui.kt (11 948 lines) into a proper multi-file architecture, going from 6 files in v1.2 to 19 files in v1.3
  • Extracted GamaPanels.kt to contain all settings sub-panels (2 077 lines)
  • Extracted GamaComponents.kt to contain all reusable UI primitives, cards, buttons, and layout helpers (3 107 lines)
  • Extracted GamaDialogs.kt to contain all modal dialogs (1 443 lines)
  • Extracted GamaTheme.kt to own the theme, colour, and typography system (376 lines)
  • Extracted GamaParticles.kt to contain the entire particle and celestial rendering system
  • Extracted GamaIntegrations.kt to contain the Tasker/integration panel and its info dialog
  • Extracted GamaNotifications.kt to contain notification sending logic and its associated UI
  • Promoted the three concrete tile classes out of the combined RendererTileService.kt into their own dedicated files: VulkanTileService.kt, OpenGLTileService.kt and DozeTileService.kt; the original file is now an intentional tombstone

New Features

Backup and Restore System

  • Added BackupHelper, a new singleton object that serialises every recognised SharedPreferences key (integers, booleans, floats, longs, strings, and string sets) into a versioned, timestamped JSON file named GAMA_backup_YYYY-MM-DD.json
  • Implemented schema validation on import that rejects files lacking the gama_backup_version field, preventing accidental restoration of foreign JSON
  • Both export and import are suspend functions that run on Dispatchers.IO so SharedPreferences operations never block the main thread
  • Added the corresponding BackupPanel in GamaPanels.kt with full UI for triggering export and import
  • Wired SAF file creation and opening launchers (ActivityResultContracts.CreateDocument and OpenDocument) into MainActivity so the backup flows through the official Android file picker

Crash Log Panel

  • Added CrashLogPanel inside GamaPanels.kt allowing users to browse and export recent system crash and ANR entries
  • Added fetchCrashLogs() to ShizukuHelper, a suspend function that reads dumpsys dropbox --print and parses its output into a list of CrashEntry data class instances
  • Implemented a parser that splits on Drop box tag: boundaries, filters for crash/ANR tags, marks SystemUI and HWUI entries as higher priority, truncates body text to 4 000 characters per entry, and returns the 50 most recent entries sorted newest-first
  • Added a crash log export path in MainActivity using a dedicated SAF CreateDocument launcher for plain-text files

Renderer Settings Panel

  • Extracted renderer-specific settings into a dedicated RendererPanel with its own navigation card in the settings hub
  • Added the Kill Launcher toggle inside RendererPanel, off by default, with a visible Xiaomi/Samsung warning in its description text
  • Threaded the killLauncher state through to ShizukuHelper's runVulkan and runOpenGL calls as a new killLauncher: Boolean parameter

Doze Quick Settings Tile

  • Added DozeTileService, a new Quick Settings tile for forcing Android deep doze on demand
  • Implemented the correct two-command entry sequence (dumpsys battery unplug then dumpsys deviceidle force-idle) required on Samsung and Android 15+ devices, and the matching exit sequence (dumpsys deviceidle unforce then dumpsys battery reset)
  • Added state verification via dumpsys deviceidle get deep after a 800 ms delay so the tile reflects the actual device state rather than trusting SharedPreferences
  • Tile subtitle and active/inactive state update in real time based on the verified device state

Widget Configuration Activity and Background Theme Selector

  • Added GamaWidgetConfigureActivity, opened when the user long-presses and configures the home screen widget
  • Implemented a Samsung One UI-style bottom sheet with three background modes: Match App Theme (follows the GAMA dark/light setting), Dark (near-black translucent), and Light (near-white translucent)
  • Stored the selected mode under a new widget_bg_mode SharedPreferences key
  • Designed the sheet with a Samsung-blue apply button, filled-circle radio buttons, a drag handle, and section headers matching the One UI aesthetic

Renderer Guess Without Shizuku

  • Added guessRendererWithoutShizuku(prefs) to ShizukuHelper, providing the best available renderer estimate when Shizuku is unavailable
  • Used SystemClock.elapsedRealtime() captured at switch time as last_switch_uptime for reboot detection; because elapsedRealtime() resets to near zero on every boot, a stored uptime larger than the current one reliably indicates a reboot occurred and the runtime props were cleared
  • Added a backward-compatible fallback for pre-v1.3 installs that have only the old last_switch_time wall-clock key, using currentTimeMillis() - elapsedRealtime() to derive boot time

Boot Renderer Worker via WorkManager

  • Replaced the fragile goAsync()-based boot re-apply logic (which had a hard ~10 s deadline that Shizuku almost always outlasted) with a proper WorkManager pipeline
  • Implemented BootReceiver to enqueue a OneTimeWorkRequest for BootRendererWorker using ExistingWorkPolicy.KEEP so quick reboots never accumulate duplicate jobs, with exponential backoff starting at 30 seconds
  • Implemented BootRendererWorker which polls for Shizuku readiness for up to 90 seconds per attempt, retries up to 5 times, posts a notification on both success and final failure, and deliberately does not overwrite the saved renderer pref to "OpenGL" on failure
  • Added handling for Xiaomi and HTC quick-boot broadcast actions (QUICKBOOT_POWERON) in BootReceiver
    Skipped boot job enqueuing entirely when the saved renderer is already "OpenGL", since that is Android's default after reboot

Changes to Existing Systems

ShizukuHelper

  • Converted runCommand from a blocking function that spawned two Thread objects per call to a proper suspend fun running on Dispatchers.IO
  • Reduced the command timeout from 10 seconds to 3 seconds, which cuts the worst-case UI freeze on a hanging Shizuku from 10 s to 3 s while still covering all legitimate slow cases
  • Changed stdout/stderr reading from parallel Thread-based reads to sequential reads after waitFor(), which is safe on Dispatchers.IO and eliminates hundreds of short-lived threads during aggressive mode force-stops
  • Unified runVulkanSuspend and runOpenGLSuspend into a single private switchRendererSuspend function parameterised by propValue and label, eliminating roughly 140 lines of duplicated code
  • Added a private guardedLaunch helper that consolidates the repeated checkBinder/checkPermission guard pattern across runVulkan, runOpenGL, and applyCustomRenderers, and centralises the requestPermissionFallback Toast
  • Upgraded renderer detection in getCurrentRenderer from a single getprop call to a three-source chain: the prop GAMA directly sets, a full property scan via getprop | grep -Ei 'hwui|renderer', and finally dumpsys hwui 2>/dev/null | head -20 as a last resort; "Unknown" is only returned when all three sources fail
  • Changed the "empty prop" result from returning "Default" to returning "OpenGL", correctly reflecting that Android's default renderer when debug.hwui.renderer is absnt is OpenGL
  • Renamed getAllInstalledPackages to getAllPackageNames and rewrote it as a suspend function that uses concurrent async coroutines to drain stdout and stderr before waitFor(), fixing a pipe-buffer overflow bug on MIUI devices with 500+ packages that caused the entire package list to be silently discarded
  • Extended the getAllPackageNames timeout to 30 seconds to handle the larger output safely
  • Removed com.android.settings and com.google.android.inputmethod.latin from the default app-stop sequence to prevent killing them beforeClearRecentsDialog finishes on MIUI
  • Removed the checkVersion method and its parseVersion helper from ShizukuHelper entirely
  • Changed exception catch variables from catch (e: Exception) to catch (_: Exception) throughout, following the Kotlin unused-variable convention

MainActivity

  • Replaced the single lazily-initialised permissionListener with three properly typed Shizuku listeners registered as class properties: binderReceivedListener (which also auto-requests permission if not already granted on pre-v11 Shizuku), binderDeadListener, and permissionResultListener
  • Updated permissionResultListener to trigger a full app restart via FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TASK after permission is granted, so Shizuku-dependent UI initialises cleanly
  • Installed a process-level uncaught exception handler in onCreate before any other setup; it prepends each crash entry (timestamp, thread name, full stack trace) to a crash_log.txt file in filesDir, trims the file to 64 KB, and chains to the default handler so the normal crash dialog and restart behaviour is preserved
  • Wired SAF launchers for backup export, backup import, and crash log export inside setContent using rememberLauncherForActivityResult
  • Updated GamaUI() call to pass four new lambda parameters: onRequestNotificationPermission, onExportBackup, onImportBackup, and onExportCrashLog
  • Changed the notification permission request flow to a proper ActivityResultContracts.RequestPermission launcher triggered by a LaunchedEffect keyed on an integer trigger counter
  • Updated onDestroy to correctly remove all three Shizuku listeners using their typed references

GamaWidget

  • Replaced the old C colour object with a new W object containing a Samsung-inspired muted palette covering both dark and light variants, including renderer-specific accents (violet for Vulkan, emerald for OpenGL) and a Samsung blue for settings
  • Added the WC data class and wc(ctx) factory function that reads widget_bg_mode and theme_preference at runtime to return the correct colour set, making the widget follow the app theme automatically
  • Adjusted the six responsive size breakpoints to 73 dp per cell (from 74 dp) to match Samsung One UI cell dimensions more accurately
  • Aliased all conflicting Glance imports (Alignment, Box, Column, Row, Spacer, FontWeight, Text, TextAlign) to short prefixed names (GA, GB, GC, GR, GS, GFW, GT, GTA) to resolve Compose namespace collisions
  • Replaced SwitchBtn components with new RendererPill components in the widget switch row
  • Increased the active renderer font size in the 4×4 layout from 36 sp to 44 sp and updated the label from "CURRENTLY ACTIVE" to "ACTIVE RENDERER"
  • Simplified the widget footer copy from "Tap button to switch • Tap card to open" to "Tap card to open GAMA" and added a thin divider above it
  • Updated the widget action callback to also write last_switch_time and last_switch_uptime on every switch, supporting the new boot-detection logic
  • Fixed a coroutine scope leak by replacing CoroutineScope(Dispatchers.IO).launch in the action callback with withContext(Dispatchers.IO)

TaskerReceiver

  • Added a secondary caller-verification layer inside onReceive that checks the calling UID against a TRUSTED_PACKAGES set (the four known Tasker/Locale package names plus GAMA's own package); unknown callers are silently rejected without a Toast to avoid leaking permission structure information
  • Added a TASKER_PERMISSION constant to the companion object
  • Added a full KDoc security setup block documenting the exact manifest XML needed to declare and guard the receiver, plus the ADB grant command for Tasker

Removed

  • Removed AppSelectorPanel and all associated logic; the feature was found to be unreliable on Android 11+ due to PackageManager visibility filtering making the majority of third-party packages invisible without QUERY_ALL_PACKAGES
  • Removed the inline ChangelogDialog and ChangelogShimmer composables from the old monolithic file
  • Removed the checkVersion and parseVersion version-check network call from ShizukuHelper
  • Removed the bgDrawable lookup and per-renderer background drawables from the widget

Don't miss a new gama release

NewReleases is sending notifications on new releases.