Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 30 additions & 5 deletions src/Xamarin.Android.Tools.AndroidSdk/AndroidVersion.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Xml.Linq;

Expand All @@ -9,6 +10,9 @@ public class AndroidVersion
// Android API Level. *Usually* corresponds to $(AndroidSdkPath)/platforms/android-$(ApiLevel)/android.jar
public int ApiLevel { get; private set; }

// Android API Level; includes "minor" version bumps, e.g. Android 16 QPR2 is "36.1" while ApiLevel=36
public Version VersionCodeFull { get; private set; }

// Android API Level ID. == ApiLevel on stable versions, will be e.g. `N` for previews: $(AndroidSdkPath)/platforms/android-N/android.jar
public string Id { get; private set; }

Expand All @@ -27,26 +31,42 @@ public class AndroidVersion
// Is this API level stable? Should be False for non-numeric Id values.
public bool Stable { get; private set; }

internal HashSet<string> Ids { get; } = new ();

// Alternate Ids for a given API level. Allows for historical mapping, e.g. API-11 has alternate ID 'H'.
internal string[]? AlternateIds { get; set; }
internal string[]? AlternateIds {
set => Ids.UnionWith (value);
}

public AndroidVersion (int apiLevel, string osVersion, string? codeName = null, string? id = null, bool stable = true)
: this (new Version (apiLevel, 0), osVersion, codeName, id, stable)
{
}

public AndroidVersion (Version versionCodeFull, string osVersion, string? codeName = null, string? id = null, bool stable = true)
{
if (versionCodeFull == null)
throw new ArgumentNullException (nameof (versionCodeFull));
if (osVersion == null)
throw new ArgumentNullException (nameof (osVersion));

ApiLevel = apiLevel;
Id = id ?? ApiLevel.ToString ();
ApiLevel = versionCodeFull.Major;
VersionCodeFull = versionCodeFull;
Id = id ?? (versionCodeFull.Minor != 0 ? versionCodeFull.ToString () : ApiLevel.ToString ());
CodeName = codeName;
OSVersion = osVersion;
TargetFrameworkVersion = Version.Parse (osVersion);
FrameworkVersion = "v" + osVersion;
Stable = stable;

Ids.Add (ApiLevel.ToString ());
Ids.Add (VersionCodeFull.ToString ());
Ids.Add (Id);
}

public override string ToString ()
{
return $"(AndroidVersion: ApiLevel={ApiLevel} Id={Id} OSVersion={OSVersion} CodeName='{CodeName}' TargetFrameworkVersion={TargetFrameworkVersion} Stable={Stable})";
return $"(AndroidVersion: ApiLevel={ApiLevel} VersionCodeFull={VersionCodeFull} Id={Id} OSVersion={OSVersion} CodeName='{CodeName}' TargetFrameworkVersion={TargetFrameworkVersion} Stable={Stable})";
}

public static AndroidVersion Load (Stream stream)
Expand Down Expand Up @@ -76,8 +96,13 @@ static AndroidVersion Load (XDocument doc)
var name = (string?) doc.Root?.Element ("Name") ?? throw new InvalidOperationException ("Missing Name element");
var version = (string?) doc.Root?.Element ("Version") ?? throw new InvalidOperationException ("Missing Version element");
var stable = (bool?) doc.Root?.Element ("Stable") ?? throw new InvalidOperationException ("Missing Stable element");
var versionCodeFull = (string?) doc.Root?.Element ("VersionCodeFull");

var fullLevel = string.IsNullOrWhiteSpace (versionCodeFull)
? new Version (level, 0)
: Version.Parse (versionCodeFull);

return new AndroidVersion (level, version.TrimStart ('v'), name, id, stable);
return new AndroidVersion (fullLevel, version.TrimStart ('v'), name, id, stable);
}
}
}
Expand Down
14 changes: 11 additions & 3 deletions src/Xamarin.Android.Tools.AndroidSdk/AndroidVersions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,14 +93,13 @@ static bool MatchesFrameworkVersion (AndroidVersion version, string frameworkVer
{
return installedVersions.FirstOrDefault (v => MatchesId (v, id))?.ApiLevel ??
KnownVersions.FirstOrDefault (v => MatchesId (v, id))?.ApiLevel ??
(Version.TryParse (id, out var versionCodeFull) ? (int?) versionCodeFull.Major : default (int?)) ??
(int.TryParse (id, out int apiLevel) ? apiLevel : default (int?));
}

static bool MatchesId (AndroidVersion version, string id)
{
return version.Id == id ||
(version.AlternateIds?.Contains (id) ?? false) ||
(version.ApiLevel.ToString () == id);
return version.Ids.Contains (id);
}

public string? GetIdFromApiLevel (int apiLevel)
Expand All @@ -110,12 +109,21 @@ static bool MatchesId (AndroidVersion version, string id)
apiLevel.ToString ();
}

public string? GetIdFromVersionCodeFull (Version versionCodeFull)
{
return installedVersions.FirstOrDefault (v => v.VersionCodeFull == versionCodeFull)?.Id ??
KnownVersions.FirstOrDefault (v => v.VersionCodeFull == versionCodeFull)?.Id ??
versionCodeFull.ToString ();
}

// Sometimes, e.g. when new API levels are introduced, the "API level" is a letter, not a number,
// e.g. 'API-H' for API-11, 'API-O' for API-26, etc.
public string? GetIdFromApiLevel (string apiLevel)
{
if (int.TryParse (apiLevel, out var platform))
return GetIdFromApiLevel (platform);
if (Version.TryParse (apiLevel, out var versionCodeFull))
return GetIdFromVersionCodeFull (versionCodeFull);
return installedVersions.FirstOrDefault (v => MatchesId (v, apiLevel))?.Id ??
KnownVersions.FirstOrDefault (v => MatchesId (v, apiLevel))?.Id ??
apiLevel;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<PropertyGroup>
<TargetFrameworks>netstandard2.0</TargetFrameworks>
<TargetFrameworks Condition=" '$(AndroidToolsDisableMultiTargeting)' != 'true' ">$(TargetFrameworks);$(DotNetTargetFramework)</TargetFrameworks>
<LangVersion>8.0</LangVersion>
<LangVersion>9.0</LangVersion>
<Nullable>enable</Nullable>
<DefineConstants>INTERNAL_NULLABLE_ATTRIBUTES</DefineConstants>
<SignAssembly>true</SignAssembly>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,41 @@ public void Constructor_Exceptions ()
{
Assert.Throws<ArgumentNullException> (() => new AndroidVersion (0, null));
Assert.Throws<ArgumentException> (() => new AndroidVersion (0, "not a number"));
Assert.Throws<ArgumentNullException> (() => new AndroidVersion ((Version) null, osVersion: "1.0"));
}

[Test]
public void Constructor ()
{
var v = new AndroidVersion (apiLevel: 1, osVersion: "2.3", codeName: "Four", id: "E", stable: false);
Assert.AreEqual (1, v.ApiLevel);
Assert.AreEqual (new Version (1, 0), v.VersionCodeFull);
Assert.AreEqual ("E", v.Id);
Assert.AreEqual ("Four", v.CodeName);
Assert.AreEqual ("2.3", v.OSVersion);
Assert.AreEqual (new Version (2, 3), v.TargetFrameworkVersion);
Assert.AreEqual ("v2.3", v.FrameworkVersion);
Assert.AreEqual (false, v.Stable);
Assert.IsTrue (v.Ids.SetEquals (new [] { "1", "1.0", "E" }), $"Actual Ids: {{ {string.Join (", ", v.Ids)} }}");
}

[Test]
public void Constructor_NoId ()
{
var v = new AndroidVersion (apiLevel: 1, osVersion: "2.3", codeName: "Four", stable: false);
Assert.AreEqual (1, v.ApiLevel);
Assert.AreEqual (new Version (1, 0), v.VersionCodeFull);
Assert.AreEqual ("1", v.Id);
Assert.AreEqual ("Four", v.CodeName);
Assert.AreEqual ("2.3", v.OSVersion);
Assert.AreEqual (new Version (2, 3), v.TargetFrameworkVersion);
Assert.AreEqual ("v2.3", v.FrameworkVersion);
Assert.AreEqual (false, v.Stable);
Assert.IsTrue (v.Ids.SetEquals (new [] { "1", "1.0" }));

v = new AndroidVersion (new Version (2, 3), osVersion: "2.3", codeName: "Four", stable: false);
Assert.AreEqual ("2.3", v.Id);
Assert.IsTrue (v.Ids.SetEquals (new [] { "2", "2.3" }), $"Actual Ids: {{ {string.Join (", ", v.Ids)} }}");
}

[Test]
Expand Down Expand Up @@ -55,14 +77,39 @@ public void Load ()
<Version>v7.99.0</Version>
<Stable>False</Stable>
</AndroidApiInfo>";
var v = AndroidVersion.Load (new MemoryStream (Encoding.UTF8.GetBytes (xml)));
var v = AndroidVersion.Load (new MemoryStream (Encoding.UTF8.GetBytes (xml)));
Assert.AreEqual (26, v.ApiLevel);
Assert.AreEqual (new Version (26, 0), v.VersionCodeFull);
Assert.AreEqual ("O", v.Id);
Assert.AreEqual ("Android O", v.CodeName);
Assert.AreEqual ("7.99.0", v.OSVersion);
Assert.AreEqual (new Version (7, 99, 0), v.TargetFrameworkVersion);
Assert.AreEqual ("v7.99.0", v.FrameworkVersion);
Assert.AreEqual (false, v.Stable);
Assert.IsTrue (v.Ids.SetEquals (new [] { "26", "26.0", "O" }), $"Actual Ids: {{ {string.Join (", ", v.Ids)} }}");
}

[Test]
public void Load_VersionCodeFull_Replaces_Level ()
{
var xml = @"<AndroidApiInfo>
<Id>O</Id>
<Level>26</Level>
<VersionCodeFull>27.1</VersionCodeFull>
<Name>Android O</Name>
<Version>v7.99.0</Version>
<Stable>False</Stable>
</AndroidApiInfo>";
var v = AndroidVersion.Load (new MemoryStream (Encoding.UTF8.GetBytes (xml)));
Assert.AreEqual (27, v.ApiLevel);
Assert.AreEqual (new Version (27, 1), v.VersionCodeFull);
Assert.AreEqual ("O", v.Id);
Assert.AreEqual ("Android O", v.CodeName);
Assert.AreEqual ("7.99.0", v.OSVersion);
Assert.AreEqual (new Version (7, 99, 0), v.TargetFrameworkVersion);
Assert.AreEqual ("v7.99.0", v.FrameworkVersion);
Assert.AreEqual (false, v.Stable);
Assert.IsTrue (v.Ids.SetEquals (new [] { "27", "27.1", "O" }), $"Actual Ids: {{ {string.Join (", ", v.Ids)} }}");
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;

using Microsoft.VisualStudio.TestPlatform.Utilities;
using NUnit.Framework;

namespace Xamarin.Android.Tools.Tests
Expand Down Expand Up @@ -92,6 +92,7 @@ public void Constructor_FrameworkDirectories ()
"<AndroidApiInfo>",
" <Id>Z</Id>",
" <Level>127</Level>",
" <VersionCodeFull>127.1</VersionCodeFull>",
" <Name>Z</Name>",
" <Version>v108.1.99</Version>",
" <Stable>False</Stable>",
Expand Down Expand Up @@ -138,6 +139,9 @@ static AndroidVersions CreateTestVersions ()
new AndroidVersion (apiLevel: 3, osVersion: "1.2", id: "C", stable: true),
// Hides/shadows a Known Version
new AndroidVersion (apiLevel: 14, osVersion: "4.0", id: "II", stable: false),
// Demonstrates new "minor" release support
new AndroidVersion (versionCodeFull: new Version (36, 0), osVersion: "16.0", id: "Baklava", stable: true),
new AndroidVersion (versionCodeFull: new Version (36, 1), osVersion: "16.1", id: "CANARY", stable: false),
});
}

Expand All @@ -157,6 +161,7 @@ public void GetApiLevelFromFrameworkVersion ()
Assert.AreEqual (null, versions.GetApiLevelFromFrameworkVersion ("1.3"));
Assert.AreEqual (14, versions.GetApiLevelFromFrameworkVersion ("v4.0"));
Assert.AreEqual (14, versions.GetApiLevelFromFrameworkVersion ("4.0"));
Assert.AreEqual (36, versions.GetApiLevelFromFrameworkVersion ("16.1"));

// via KnownVersions
Assert.AreEqual (4, versions.GetApiLevelFromFrameworkVersion ("v1.6"));
Expand All @@ -177,6 +182,8 @@ public void GetApiLevelFromId ()
Assert.AreEqual (3, versions.GetApiLevelFromId ("3"));
Assert.AreEqual (14, versions.GetApiLevelFromId ("14"));
Assert.AreEqual (14, versions.GetApiLevelFromId ("II"));
Assert.AreEqual (36, versions.GetApiLevelFromId ("36"));
Assert.AreEqual (36, versions.GetApiLevelFromId ("CANARY"));

Assert.AreEqual (null, versions.GetApiLevelFromId ("D"));

Expand All @@ -202,6 +209,13 @@ public void GetIdFromApiLevel ()
Assert.AreEqual ("II", versions.GetIdFromApiLevel ("14"));
Assert.AreEqual ("II", versions.GetIdFromApiLevel ("II"));

Assert.AreEqual ("Baklava", versions.GetIdFromApiLevel (36));
Assert.AreEqual ("Baklava", versions.GetIdFromApiLevel ("36"));
Assert.AreEqual ("Baklava", versions.GetIdFromApiLevel ("36.0"));
Assert.AreEqual ("Baklava", versions.GetIdFromApiLevel ("Baklava"));
Assert.AreEqual ("CANARY", versions.GetIdFromApiLevel ("36.1"));
Assert.AreEqual ("CANARY", versions.GetIdFromApiLevel ("CANARY"));

Assert.AreEqual ("-1", versions.GetIdFromApiLevel (-1));
Assert.AreEqual ("-1", versions.GetIdFromApiLevel ("-1"));
Assert.AreEqual ("D", versions.GetIdFromApiLevel ("D"));
Expand All @@ -226,6 +240,7 @@ public void GetIdFromFrameworkVersion ()
Assert.AreEqual ("C", versions.GetIdFromFrameworkVersion ("1.2"));
Assert.AreEqual ("II", versions.GetIdFromFrameworkVersion ("v4.0"));
Assert.AreEqual ("II", versions.GetIdFromFrameworkVersion ("4.0"));
Assert.AreEqual ("CANARY", versions.GetIdFromFrameworkVersion ("16.1"));

Assert.AreEqual (null, versions.GetIdFromFrameworkVersion ("v0.99"));
Assert.AreEqual (null, versions.GetIdFromFrameworkVersion ("0.99"));
Expand All @@ -245,6 +260,7 @@ public void GetFrameworkVersionFromApiLevel ()
Assert.AreEqual ("v1.1", versions.GetFrameworkVersionFromApiLevel (2));
Assert.AreEqual ("v1.2", versions.GetFrameworkVersionFromApiLevel (3));
Assert.AreEqual ("v4.0", versions.GetFrameworkVersionFromApiLevel (14));
Assert.AreEqual ("v16.0", versions.GetFrameworkVersionFromApiLevel (36));

// via KnownVersions
Assert.AreEqual ("v2.3", versions.GetFrameworkVersionFromApiLevel (10));
Expand All @@ -264,6 +280,10 @@ public void GetFrameworkVersionFromId ()
Assert.AreEqual ("v1.2", versions.GetFrameworkVersionFromId ("C"));
Assert.AreEqual ("v4.0", versions.GetFrameworkVersionFromId ("14"));
Assert.AreEqual ("v4.0", versions.GetFrameworkVersionFromId ("II"));
Assert.AreEqual ("v16.0", versions.GetFrameworkVersionFromId ("36"));
Assert.AreEqual ("v16.0", versions.GetFrameworkVersionFromId ("Baklava"));
Assert.AreEqual ("v16.1", versions.GetFrameworkVersionFromId ("36.1"));
Assert.AreEqual ("v16.1", versions.GetFrameworkVersionFromId ("CANARY"));

// via KnownVersions
Assert.AreEqual ("v3.0", versions.GetFrameworkVersionFromId ("11"));
Expand Down