Skip to content

Commit 0b375fe

Browse files
dellis1972jonathanpeppers
authored andcommitted
[templates] Add androidwear template for Android Wear OS (#7013)
Context: #7003 Add .NET 6+ template for Android Wear OS, creatable as: dotnet new androidwear Additionally, add a `wear_tests` stage to `azure-pipelines.yaml` which creates a Wear OS emulator, and runs the Android Wear template on the Wear OS emulator to ensure that the template works.
1 parent d86aa3d commit 0b375fe

File tree

30 files changed

+251
-2
lines changed

30 files changed

+251
-2
lines changed

build-tools/automation/azure-pipelines.yaml

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1068,6 +1068,81 @@ stages:
10681068
target_framework: 'net6.0'
10691069
provisionatorChannel: ${{ parameters.provisionatorChannel }}
10701070

1071+
- stage: wear_tests
1072+
displayName: WearOS Tests
1073+
dependsOn: mac_build
1074+
condition: and(succeeded(), or(eq(variables['RunAllTests'], true), contains(dependencies.mac_build.outputs['mac_build_create_installers.TestConditions.TestAreas'], 'MSBuildDevice')))
1075+
jobs:
1076+
- job: wear_tests
1077+
displayName: wear_tests
1078+
timeoutInMinutes: 180
1079+
cancelTimeoutInMinutes: 2
1080+
strategy:
1081+
matrix:
1082+
Android30-x86:
1083+
avdApiLevel: 30
1084+
avdAbi: x86
1085+
avdType: android-wear
1086+
deviceName: wear_square
1087+
pool:
1088+
vmImage: $(HostedMacImage)
1089+
workspace:
1090+
clean: all
1091+
steps:
1092+
- template: yaml-templates/setup-test-environment.yaml
1093+
parameters:
1094+
configuration: $(XA.Build.Configuration)
1095+
1096+
- template: yaml-templates/run-xaprepare.yaml
1097+
parameters:
1098+
displayName: install required brew tools and prepare java.interop
1099+
arguments: --s=Required --auto-provision=yes --auto-provision-uses-sudo=yes
1100+
1101+
- template: yaml-templates/run-xaprepare.yaml
1102+
parameters:
1103+
displayName: install emulator
1104+
arguments: --s=EmulatorTestDependencies
1105+
1106+
- script: echo "##vso[task.setvariable variable=Java8SdkDirectory]$JAVA_HOME_8_X64"
1107+
displayName: set Java8SdkDirectory
1108+
1109+
- task: DownloadPipelineArtifact@2
1110+
inputs:
1111+
artifactName: $(TestAssembliesArtifactName)
1112+
downloadPath: $(System.DefaultWorkingDirectory)/bin/Test$(XA.Build.Configuration)
1113+
1114+
- task: MSBuild@1
1115+
displayName: install and launch emulator
1116+
inputs:
1117+
solution: tests/Mono.Android-Tests/Mono.Android-Tests.csproj
1118+
configuration: $(XA.Build.Configuration)
1119+
msbuildArguments: /t:InstallAvdImage;AcquireAndroidTarget /p:TestDeviceName=$(deviceName) /p:TestAvdApiLevel=$(avdApiLevel) /p:TestAvdAbi=$(avdAbi) /p:TestAvdType=$(avdType) /bl:$(System.DefaultWorkingDirectory)/bin/Test$(XA.Build.Configuration)/install-emulator-$(avdApiLevel).binlog
1120+
1121+
- template: yaml-templates/run-nunit-tests.yaml
1122+
parameters:
1123+
useDotNet: true
1124+
testRunTitle: WearOS On Device - macOS
1125+
testAssembly: $(System.DefaultWorkingDirectory)/bin/Test$(XA.Build.Configuration)/MSBuildDeviceIntegration/$(DotNetStableTargetFramework)/MSBuildDeviceIntegration.dll
1126+
dotNetTestExtraArgs: --filter "TestCategory = WearOS"
1127+
testResultsFile: TestResult-WearOS--$(XA.Build.Configuration).xml
1128+
1129+
- task: MSBuild@1
1130+
displayName: shut down emulator
1131+
inputs:
1132+
solution: tests/Mono.Android-Tests/Mono.Android-Tests.csproj
1133+
configuration: $(XA.Build.Configuration)
1134+
msbuildArguments: /t:AcquireAndroidTarget,ReleaseAndroidTarget /p:TestDeviceName=$(deviceName) /p:TestAvdApiLevel=$(avdApiLevel) /p:TestAvdAbi=$(avdAbi) /p:TestAvdType=$(avdType) /bl:$(System.DefaultWorkingDirectory)/bin/Test$(XA.Build.Configuration)/shutdown-emulator.binlog
1135+
condition: always()
1136+
1137+
- template: yaml-templates/upload-results.yaml
1138+
parameters:
1139+
configuration: $(XA.Build.Configuration)
1140+
artifactName: Test Results - Emulator $(avdApiLevel)-$(avdAbi)-$(avdType) - macOS
1141+
1142+
- template: yaml-templates/fail-on-issue.yaml
1143+
1144+
1145+
10711146
- stage: designer_tests
10721147
displayName: Designer Tests
10731148
dependsOn: mac_build

build-tools/scripts/TestApks.targets

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
<TestAvdApiLevel Condition=" '$(TestAvdApiLevel)' == '' ">29</TestAvdApiLevel>
2121
<TestAvdAbi Condition=" '$(TestAvdAbi)' == '' ">x86_64</TestAvdAbi>
2222
<TestAvdType Condition=" '$(TestAvdType)' == '' ">default</TestAvdType>
23+
<TestDeviceName Condition=" '$(TestDeviceName)' == '' ">pixel_4</TestDeviceName>
2324
<SdkManagerImageName Condition=" '$(SdkManagerImageName)' == '' ">system-images;android-$(TestAvdApiLevel);$(TestAvdType);$(TestAvdAbi)</SdkManagerImageName>
2425
<TestAvdName>XamarinAndroidTestRunner$(TestAvdApiLevel)-$(TestAvdAbi)</TestAvdName>
2526
<_AdbEmulatorPort>5570</_AdbEmulatorPort>
@@ -62,6 +63,7 @@
6263
TargetId="$(SdkManagerImageName)"
6364
ImageName="$(TestAvdName)"
6465
ImageType="$(TestAvdType)"
66+
DeviceName="$(TestDeviceName)"
6567
ToolExe="$(AvdManagerToolExe)"
6668
ToolPath="$(CommandLineToolsBinPath)"
6769
RamSizeMB="3072"
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
{
2+
"$schema": "http://json.schemastore.org/template",
3+
"author": "Microsoft",
4+
"classifications": [ "Android", "Mobile" ],
5+
"identity": "Microsoft.Android.AndroidWearApp",
6+
"name": "Android Wear Application",
7+
"description": "A project for creating a .NET Android Wear application",
8+
"shortName": "androidwear",
9+
"tags": {
10+
"language": "C#",
11+
"type": "project"
12+
},
13+
"sourceName": "AndroidApp1",
14+
"sources": [
15+
{
16+
"source": "./",
17+
"target": "./",
18+
"copyOnly": "**/Resources/**/*.png"
19+
}
20+
],
21+
"preferNameDirectory": true,
22+
"primaryOutputs": [
23+
{ "path": "AndroidApp1.csproj" }
24+
],
25+
"symbols": {
26+
"packageName": {
27+
"type": "parameter",
28+
"description": "Overrides the package name in the AndroidManifest.xml",
29+
"datatype": "string",
30+
"replaces": "com.companyname.AndroidApp1"
31+
},
32+
"supportedOSVersion": {
33+
"type": "parameter",
34+
"description": "Overrides $(SupportedOSPlatformVersion) in the project",
35+
"datatype": "string",
36+
"replaces": "SUPPORTED_OS_PLATFORM_VERSION",
37+
"defaultValue": "23"
38+
}
39+
},
40+
"defaultName": "AndroidApp1"
41+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<TargetFramework>net7.0-android</TargetFramework>
4+
<SupportedOSPlatformVersion>SUPPORTED_OS_PLATFORM_VERSION</SupportedOSPlatformVersion>
5+
<RootNamespace Condition="'$(name)' != '$(name{-VALUE-FORMS-}safe_namespace)'">AndroidApp1</RootNamespace>
6+
<OutputType>Exe</OutputType>
7+
<Nullable>enable</Nullable>
8+
<ImplicitUsings>enable</ImplicitUsings>
9+
<ApplicationId>com.companyname.AndroidApp1</ApplicationId>
10+
<ApplicationVersion>1</ApplicationVersion>
11+
<ApplicationDisplayVersion>1.0</ApplicationDisplayVersion>
12+
<MSBuildWarningsAsMessages>$(MSBuildWarningsAsMessages);XA4218</MSBuildWarningsAsMessages>
13+
</PropertyGroup>
14+
<ItemGroup>
15+
<PackageReference Include="Xamarin.AndroidX.Wear" Version="1.2.0.5" />
16+
</ItemGroup>
17+
</Project>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0">
3+
<uses-feature android:name="android.hardware.type.watch" />
4+
<uses-permission android:name="android.permission.WAKE_LOCK" />
5+
<application android:allowBackup="true" android:icon="@mipmap/appicon" android:roundIcon="@mipmap/appicon_round" android:label="@string/app_name" android:supportsRtl="true" android:theme="@android:style/Theme.DeviceDefault">
6+
<uses-library android:name="com.google.android.wearable" android:required="true" />
7+
</application>
8+
</manifest>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
namespace AndroidApp1;
2+
3+
[Activity(Label = "@string/app_name", MainLauncher = true)]
4+
public class MainActivity : Activity
5+
{
6+
protected override void OnCreate(Bundle? savedInstanceState)
7+
{
8+
base.OnCreate(savedInstanceState);
9+
10+
// Set our view from the "main" layout resource
11+
SetContentView(Resource.Layout.activity_main);
12+
}
13+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
Images, layout descriptions, binary blobs and string dictionaries can be included
2+
in your application as resource files. Various Android APIs are designed to
3+
operate on the resource IDs instead of dealing with images, strings or binary blobs
4+
directly.
5+
6+
For example, a sample Android app that contains a user interface layout (main.xml),
7+
an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png)
8+
would keep its resources in the "Resources" directory of the application:
9+
10+
Resources/
11+
drawable/
12+
icon.png
13+
14+
layout/
15+
main.xml
16+
17+
values/
18+
strings.xml
19+
20+
In order to get the build system to recognize Android resources, set the build action to
21+
"AndroidResource". The native Android APIs do not operate directly with filenames, but
22+
instead operate on resource IDs. When you compile an Android application that uses resources,
23+
the build system will package the resources for distribution and generate a class called "Resource"
24+
(this is an Android convention) that contains the tokens for each one of the resources
25+
included. For example, for the above Resources layout, this is what the Resource class would expose:
26+
27+
public class Resource {
28+
public class Drawable {
29+
public const int icon = 0x123;
30+
}
31+
32+
public class Layout {
33+
public const int main = 0x456;
34+
}
35+
36+
public class Strings {
37+
public const int first_string = 0xabc;
38+
public const int second_string = 0xbcd;
39+
}
40+
}
41+
42+
You would then use Resource.Drawable.icon to reference the drawable/icon.png file, or
43+
Resource.Layout.main to reference the layout/main.xml file, or Resource.Strings.first_string
44+
to reference the first string in the dictionary file values/strings.xml.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<androidx.wear.widget.BoxInsetLayout xmlns:android="http://schemas.android.com/apk/res/android"
3+
xmlns:app="http://schemas.android.com/apk/res-auto"
4+
xmlns:tools="http://schemas.android.com/tools"
5+
android:layout_width="match_parent"
6+
android:layout_height="match_parent"
7+
android:padding="@dimen/box_inset_layout_padding"
8+
tools:deviceIds="wear">
9+
<FrameLayout
10+
android:layout_width="match_parent"
11+
android:layout_height="match_parent"
12+
android:padding="@dimen/inner_frame_layout_padding"
13+
app:boxedEdges="all">
14+
<TextView
15+
android:layout_width="wrap_content"
16+
android:layout_height="wrap_content"
17+
android:text="@string/app_text" />
18+
</FrameLayout>
19+
</androidx.wear.widget.BoxInsetLayout>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"><background android:drawable="@mipmap/appicon_background" /><foreground android:drawable="@mipmap/appicon_foreground" /></adaptive-icon>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"><background android:drawable="@mipmap/appicon_background" /><foreground android:drawable="@mipmap/appicon_foreground" /></adaptive-icon>

0 commit comments

Comments
 (0)