{
  description = "Moonlight game streaming client for Android — devShell + hermetic debug-APK build";

  inputs = {
    fleet.url = "git+https://git.oleks.space/oleks/fleet-pins";
    nixpkgs.follows = "fleet/nixpkgs-projects";
  };

  outputs =
    {
      self,
      fleet,
      nixpkgs,
    }:
    let
      # Android builds run on Linux hosts; the SDK is x86_64/aarch64-linux only.
      systems = [
        "x86_64-linux"
        "aarch64-linux"
      ];
      forAllSystems =
        f:
        nixpkgs.lib.genAttrs systems (
          system:
          f (
            import nixpkgs {
              inherit system;
              config = {
                allowUnfree = true; # Android SDK components are unfree
                android_sdk.accept_license = true;
              };
            }
          )
        );

      # Pinned to match app/build.gradle exactly.
      ndkVersion = "27.0.12077973";
      buildToolsVersion = "34.0.0";
      platformVersion = "34";
    in
    {
      # Compose the Android SDK once per system, reused by devShell + package.
      legacyPackages = forAllSystems (pkgs: {
        androidComposition = pkgs.androidenv.composeAndroidPackages {
          platformVersions = [ platformVersion ];
          buildToolsVersions = [ buildToolsVersion ];
          includeNDK = true;
          ndkVersions = [ ndkVersion ];
          includeEmulator = false;
          includeSystemImages = false;
          includeSources = false;
        };
      });

      # `nix develop` — JDK 17 + Android SDK/NDK + gradle, ready for `./gradlew`.
      devShells = forAllSystems (
        pkgs:
        let
          androidSdk = self.legacyPackages.${pkgs.system}.androidComposition.androidsdk;
          sdkRoot = "${androidSdk}/libexec/android-sdk";
        in
        {
          default = pkgs.mkShell {
            packages = [
              pkgs.jdk17
              pkgs.gradle
              androidSdk
              # adb/fastboot — for sideloading the built APK to a device or an
              # Android TV (Chromecast with Google TV) over the network.
              pkgs.android-tools
            ];

            ANDROID_HOME = sdkRoot;
            ANDROID_SDK_ROOT = sdkRoot;
            ANDROID_NDK_ROOT = "${sdkRoot}/ndk/${ndkVersion}";
            JAVA_HOME = pkgs.jdk17.home;
            # AGP downloads aapt2 from Maven; that prebuilt won't run on NixOS.
            # Point it at the SDK's patched aapt2 instead.
            GRADLE_OPTS = "-Dorg.gradle.project.android.aapt2FromMavenOverride=${sdkRoot}/build-tools/${buildToolsVersion}/aapt2";

            shellHook = ''
              echo "Android SDK: $ANDROID_HOME"
              echo "NDK:         $ANDROID_NDK_ROOT"
              echo "Build the nonRoot debug APK with:  ./gradlew assembleNonRootDebug"
            '';
          };
        }
      );

      # `nix build` — hermetic, offline debug APK build via gradle's mitm-cache.
      packages = forAllSystems (
        pkgs:
        let
          androidSdk = self.legacyPackages.${pkgs.system}.androidComposition.androidsdk;
          sdkRoot = "${androidSdk}/libexec/android-sdk";
        in
        {
          default = self.packages.${pkgs.system}.moonlight-android;

          moonlight-android = pkgs.stdenv.mkDerivation (finalAttrs: {
            pname = "moonlight-android";
            version = "12.1";

            # The repo, including the moonlight-common-c + enet submodules that must
            # already be checked out (git submodule update --init --recursive).
            src = ./.;

            nativeBuildInputs = [
              pkgs.gradle
              pkgs.jdk17
              androidSdk
            ];

            # Offline Maven dependency cache. Regenerate deps.json with:
            #   nix build .#moonlight-android.mitmCache.updateScript && ./result
            mitmCache = pkgs.gradle.fetchDeps {
              inherit (finalAttrs) pname;
              pkg = finalAttrs.finalPackage;
              data = ./deps.json;
            };

            gradleBuildTask = "assembleNonRootDebug";

            ANDROID_HOME = sdkRoot;
            ANDROID_SDK_ROOT = sdkRoot;
            ANDROID_NDK_ROOT = "${sdkRoot}/ndk/${ndkVersion}";

            gradleFlags = [
              "-Dorg.gradle.java.home=${pkgs.jdk17.home}"
              # Use the SDK's runnable aapt2 instead of the Maven prebuilt.
              "-Pandroid.aapt2FromMavenOverride=${sdkRoot}/build-tools/${buildToolsVersion}/aapt2"
              # Skip resolving androidTest/test classpaths during dependency capture
              # (AGP variant ambiguity on the self-referenced app project).
              "--init-script"
              "nix-deps-fixup.gradle"
            ];

            postPatch = ''
              # Tell Gradle where the SDK lives without relying on env detection.
              echo "sdk.dir=${sdkRoot}" > local.properties
            '';

            # The debug build type auto-signs with a debug keystore that lands in
            # $HOME/.android; the build sandbox's HOME is read-only, so redirect it.
            preBuild = ''
              export ANDROID_USER_HOME="$NIX_BUILD_TOP/.android"
              export HOME="$NIX_BUILD_TOP/home"
              mkdir -p "$ANDROID_USER_HOME" "$HOME"
            '';

            doCheck = false;

            installPhase = ''
              runHook preInstall
              mkdir -p "$out"
              find app/build/outputs/apk -name '*.apk' -exec cp -v {} "$out/" \;
              runHook postInstall
            '';

            meta = {
              description = "Moonlight game streaming client for Android (nonRoot debug APK)";
              homepage = "https://github.com/moonlight-stream/moonlight-android";
              license = pkgs.lib.licenses.gpl3Only;
              platforms = systems;
            };
          });
        }
      );
    };
}
