<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net10.0-android</TargetFramework>
    <OutputType>Exe</OutputType>
    <Nullable>enable</Nullable>
    <AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>

    <!--
      minSdk 28 (Android 9), targetSdk 36. Chosen rather than inherited, because docs/android-port.md
      flagged it as a real decision: the packages constrain targetSdk, not the floor.

      28 is where the three platform APIs this head actually depends on all exist in the framework
      itself rather than behind an AndroidX shim: BiometricPrompt, StrongBox-backed keys, and
      KeyGenParameterSpec.SetUnlockedDeviceRequired. Going lower would mean carrying androidx.biometric
      to reach devices that mostly cannot hold a hardware-backed key anyway — which is the one thing
      the device key store is for.
    -->
    <SupportedOSPlatformVersion>28</SupportedOSPlatformVersion>
    <TargetPlatformVersion>36</TargetPlatformVersion>

    <!--
      ============ WHICH CHANNEL THIS BUILD BELONGS TO ============

      Two of them, and they are two applications rather than two configurations. Android identifies an
      app by its package id and its signing certificate together, and refuses an update signed by a
      different key — so a build CI signs and a build a person signs cannot replace one another whatever
      they are called. Pretending otherwise would ship an installer that fails with
      INSTALL_FAILED_UPDATE_INCOMPATIBLE at the one moment somebody is trying to get a fix.

      So each channel gets its own id, its own key and its own feed, and neither can update the other by
      construction rather than by accident. Both can be installed at once, which is the useful half: a
      person testing a nightly does not lose the release they rely on. See ADR 0014.

      release — dev.dodotech.dodossh, signed by the key ADR 0011 puts on a machine that is not a runner.
                Cut from a v* tag by scripts/release-android.ps1, by a person.
      nightly — dev.dodotech.dodossh.nightly, signed by a keystore committed to this repository in the
                open. Cut from main by CI, every push.

      Default release, so that an unqualified `dotnet build` is the real application and the nightly is
      the one you have to ask for. A build with no keystore named at all falls back to the debug key,
      which is what a developer's own device gets and is neither channel.
    -->
    <DodoChannel Condition="'$(DodoChannel)' == ''">release</DodoChannel>

    <!--
      False here for the same reason the desktop head sets it false: this process formats timestamps
      and host names for a person. See DodoSSH.Client.App.csproj.
    -->
    <InvariantGlobalization>false</InvariantGlobalization>
  </PropertyGroup>

  <!-- ============ the release channel ============ -->
  <PropertyGroup Condition="'$(DodoChannel)' == 'release'">
    <ApplicationId>dev.dodotech.dodossh</ApplicationId>

    <!--
      versionCode, and it stays a hand-bumped literal on this channel. Android requires a monotonically
      increasing integer and SemVer does not give you one — deriving it from the version would work until
      the day a patch number reached 10 and the arithmetic went backwards, which is the sort of failure
      that surfaces as an install the phone refuses for reasons it will not explain.

      Forgetting to bump it is caught rather than shipped: scripts/release-android.ps1 reads the code
      already published to the release channel and refuses to build one that is not higher. A guard on a
      literal, which is the arrangement this comment argued for, rather than arithmetic nobody checks.
    -->
    <ApplicationVersion>1</ApplicationVersion>
  </PropertyGroup>

  <!-- ============ the nightly channel ============ -->
  <PropertyGroup Condition="'$(DodoChannel)' == 'nightly'">
    <ApplicationId>dev.dodotech.dodossh.nightly</ApplicationId>

    <!--
      Passed in by CI as the number of commits on the branch, which is monotonic by construction and needs
      nobody to remember anything. It is meaningless as a version and is not shown anywhere: versionName
      is what a person reads, and it carries MinVer's full answer on this channel including the height —
      see the target below.

      The fallback exists so that `-p:DodoChannel=nightly` builds outside CI at all, and 1 is deliberately
      the lowest it can be: a locally built nightly then cannot replace one installed from the feed, which
      is the right way round.
    -->
    <ApplicationVersion Condition="'$(DodoNightlyVersionCode)' != ''">$(DodoNightlyVersionCode)</ApplicationVersion>
    <ApplicationVersion Condition="'$(DodoNightlyVersionCode)' == ''">1</ApplicationVersion>

    <!--
      ◆ THE KEY IS IN THE REPOSITORY, IN THE OPEN, AND THAT IS THE DESIGN RATHER THAN AN OVERSIGHT.

      ADR 0011 rule 1 says the release key is never in CI, because a key a workflow can reach is a key held
      by everyone who can change a workflow file. This does not break that rule; it sidesteps it by making
      the CI key worth nothing. Everybody has it, so holding it grants nothing, so there is nothing to
      steal and no secret to configure — which is also why this channel works on a fresh runner with no
      setup at all.

      What it costs is stated rather than buried: anyone can build an APK this channel's phones will accept
      as an update. Reaching one still means being the thing they fetch from, which is a release on
      git.dodotech.cloud over TLS — so the practical set is whoever can write a release here, the same set
      ADR 0013 names for the desktop. That is an acceptable trade for a nightly and is not one for the
      release channel, which is the whole reason there are two.
    -->
    <AndroidKeyStore>true</AndroidKeyStore>
    <AndroidSigningKeyStore>$(MSBuildThisFileDirectory)../../build/nightly.keystore</AndroidSigningKeyStore>
    <AndroidSigningKeyAlias>dodossh-nightly</AndroidSigningKeyAlias>
    <AndroidSigningStorePass>nightly</AndroidSigningStorePass>
    <AndroidSigningKeyPass>nightly</AndroidSigningKeyPass>
  </PropertyGroup>

  <ItemGroup>
    <!--
      Which feed this build asks. Assembly metadata rather than a compile-time constant, because the
      updater needs the string and not a branch — and because a value baked into the assembly is one a
      crash report can be asked for. See AndroidUpdateChannel.
    -->
    <AssemblyMetadata Include="DodoChannel" Value="$(DodoChannel)" />
  </ItemGroup>

  <!--
    versionName, which is the string a person sees in Android's app info, taken from the same v* tag
    as everything else so the two heads cannot claim different versions of one product.

    It has to be a target rather than a property, and that is the whole reason this is nine lines
    instead of one. MinVer computes the version in a target of its own, so at evaluation time $(Version)
    is still the SDK's default 1.0.0 — an ApplicationDisplayVersion set in a PropertyGroup would read
    as though it were derived and would ship 1.0.0 forever. Running after MinVer is what makes it true.

    Major.Minor.Patch on the release channel, without MinVer's prerelease or build-metadata parts:
    versionName is a free string to Android, but it is shown to users, and "0.2.0-alpha.0.7+1a2b3c4" is
    not a version anybody can read back to you over a support conversation.

    The nightly channel keeps the whole thing, prerelease height and all, and for the opposite reason.
    Every nightly between two tags shares one Major.Minor.Patch, so trimming it would put the same three
    numbers on every build for weeks — on the one channel where "which build is this?" is the entire
    question being asked. Nobody reads a nightly's version aloud; they paste it.
  -->
  <!--
    ◆ BeforeTargets on the stub generator, and it assigns an underscore-prefixed property. Both are
    deliberate, and the shape this replaced looked more correct while shipping 1.0.0 on every build.

    ApplicationDisplayVersion is read by Xamarin.Android.Common.targets in a plain top-level
    PropertyGroup — `<_AndroidVersionName>$(ApplicationDisplayVersion)</_AndroidVersionName>` — which is
    *evaluation*, not a target. Every project property is already final by the time any target runs, so a
    target that sets ApplicationDisplayVersion sets it after the only thing that reads it has finished.
    MinVer cannot run at evaluation time, so there is no arrangement of the public property that works.

    Measured rather than reasoned about: aapt2 dump badging on the packaged APK said versionName='1.0.0'
    while `-getProperty:ApplicationDisplayVersion` said 0.0.0-alpha.0.124. The property was right and the
    manifest was not, which is exactly the shape of bug a -getProperty check cannot catch.

    So this reaches for _AndroidVersionName, which is the value GenerateMainAndroidManifest is actually
    handed, at the last moment before _GenerateJavaStubs runs it. An internal name is a real cost and it
    is the smaller one: the alternative is passing -p:ApplicationDisplayVersion from CI and from the
    release script, which makes an ordinary `dotnet build` of this head go on lying about its version and
    puts the number in two places that can disagree.

    ApplicationDisplayVersion is still assigned, and first, so that a caller passing it as a global
    property wins — a global beats a target assignment, and _AndroidVersionName then follows theirs.
  -->
  <Target Name="UseTheDerivedVersionForAndroid"
          BeforeTargets="_GenerateJavaStubs"
          DependsOnTargets="MinVer">
    <PropertyGroup>
      <!--
        Guarded, because the failure without it is silent and absurd: MinVerMajor and its two siblings
        are empty until MinVer has run, so an unguarded assignment yields the versionName ".." — three
        characters that are a legal Android versionName and are what the phone would then show. Measured,
        not imagined; invoking this target on its own produced exactly that.
      -->
      <ApplicationDisplayVersion Condition="'$(MinVerMajor)' != '' and '$(DodoChannel)' != 'nightly'">$(MinVerMajor).$(MinVerMinor).$(MinVerPatch)</ApplicationDisplayVersion>
      <ApplicationDisplayVersion Condition="'$(MinVerVersion)' != '' and '$(DodoChannel)' == 'nightly'">$(MinVerVersion)</ApplicationDisplayVersion>

      <_AndroidVersionName Condition="'$(ApplicationDisplayVersion)' != ''">$(ApplicationDisplayVersion)</_AndroidVersionName>
    </PropertyGroup>
  </Target>

  <ItemGroup>
    <!-- The phone's control styles. The palette they draw from lives in Shell and is shared. -->
    <AvaloniaResource Include="Theme/**" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="Avalonia" />
    <PackageReference Include="Avalonia.Android" />
    <PackageReference Include="Avalonia.Themes.Fluent" />
    <PackageReference Include="Avalonia.Fonts.Inter" />
    <PackageReference Include="Avalonia.Controls.WebView" />
    <PackageReference Include="CommunityToolkit.Mvvm" />
  </ItemGroup>

  <ItemGroup>
    <!--
      The same view models the desktop head drives. That sharing is the whole reason this head is a
      sibling project rather than a fork: docs/android-port.md's phone-first decision says the interface
      is a redesign, and that what survives it is every view model, command and piece of state.
    -->
    <ProjectReference Include="../DodoSSH.Client.Shell/DodoSSH.Client.Shell.csproj" />
    <ProjectReference Include="../DodoSSH.Client.Session/DodoSSH.Client.Session.csproj" />
    <ProjectReference Include="../DodoSSH.Client.Ssh/DodoSSH.Client.Ssh.csproj" />
    <ProjectReference Include="../DodoSSH.Client.Terminal/DodoSSH.Client.Terminal.csproj" />
  </ItemGroup>

</Project>
