Skip to content

Commit 751eb96

Browse files
authored
[LayoutBindings] Fix '[Preserve]' is obsolete warnings (#8529)
Fixes: #7480 Context: e604833 When [Layout Bindings][0] are enabled, a set of `partial` classes will be generated which "mirrors" the `.axml` structure in C#. Consider: dotnet new android -n com.example.codebehind You enable Code-Behind by: 1. Setting the `$(AndroidGenerateLayoutBindings)` MSBuild property to `true`. 2. Using `android:id` on elements within `.axml` files. Consider this patch to the above `dotnet new`: diff --git a/Resources/layout/activity_main.xml b/Resources/layout/activity_main.xml index f949852..c74521c 100644 --- a/Resources/layout/activity_main.xml +++ b/Resources/layout/activity_main.xml @@ -8,6 +8,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" + android:id="@+id/text" android:text="@string/app_text" /> </RelativeLayout> diff --git a/com.example.codebehind.csproj b/com.example.codebehind.csproj index 3fdbcb5..8190f84 100644 --- a/com.example.codebehind.csproj +++ b/com.example.codebehind.csproj @@ -8,5 +8,6 @@ <ApplicationId>com.companyname.com.example.codebehind</ApplicationId> <ApplicationVersion>1</ApplicationVersion> <ApplicationDisplayVersion>1.0</ApplicationDisplayVersion> + <AndroidGenerateLayoutBindings>true</AndroidGenerateLayoutBindings> </PropertyGroup> </Project> The resulting output contains `$(IntermediateOutputPath)codebehind\Binding.activity_main.g.cs`, which declares a type based on the `.axml` file name, and a mirror of the structure: namespace Binding { // typename based on `activity_main.xml` filename sealed partial class activity_main : global::Xamarin.Android.Design.LayoutBinding { [global::Android.Runtime.PreserveAttribute (Conditional=true)] public activity_main (Activity client, …); // … // `text` is via `android:id="@+id/text"` public TextView text => …; } } The problem is the use of `PreserveAttribute` in the generated code; it's been `[Obsolete]` since e604833 (over two years ago), which means all projects using Layout Bindings and Layout Code-Behind always have [CS0618][1] warnings. Remove the emission of `PreserveAttribute`, so that CS0618 is no longer generated. Additionally, within the generated files disable the [CS1591][2] and [CS8981][3] warnings: * CS1591 is around missing XML documentation comments. These code behind files do not have XML documentation comments, so if/when `$(DocumentationFile)` is set, these will be emitted for the generated code. We do not currently plan on emitting XML documentation comments, so disable this warning. * CS8981 is emitted when a type name consists solely of lowercase characters. As the generated Binding types are based on the filename -- which is frequently all lowercase -- this warning may be emitted. Finally, add `#nullable enable` to `LayoutBinding.cs` and fix the nullable reference type warnings. [0]: https://github.com/xamarin/xamarin-android/blob/2f4e01ec15102dd9cd922cbd833f6482d69512b5/Documentation/guides/LayoutCodeBehind.md [1]: https://learn.microsoft.com/dotnet/csharp/language-reference/compiler-messages/cs0618 [2]: https://learn.microsoft.com/dotnet/csharp/language-reference/compiler-messages/cs1591 [3]: https://learn.microsoft.com/dotnet/csharp/language-reference/compiler-messages/warning-waves#cs8981---the-type-name-only-contains-lower-cased-ascii-characters
1 parent 150e1d9 commit 751eb96

File tree

10 files changed

+86
-52
lines changed

10 files changed

+86
-52
lines changed

src/Xamarin.Android.Build.Tasks/Resources/LayoutBinding.cs

Lines changed: 22 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,29 @@
1-
using System;
1+
#nullable enable
2+
3+
using System;
24
using System.Diagnostics.CodeAnalysis;
35
using Android.App;
46
using Android.Views;
57

68
namespace Xamarin.Android.Design
79
{
8-
public delegate Java.Lang.Object OnLayoutItemNotFoundHandler (int resourceId, Type expectedViewType);
10+
public delegate Java.Lang.Object? OnLayoutItemNotFoundHandler (int resourceId, Type expectedViewType);
911

1012
abstract class LayoutBinding
1113
{
1214
const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors;
1315

14-
Activity boundActivity;
15-
View boundView;
16-
OnLayoutItemNotFoundHandler onLayoutItemNotFound;
16+
Activity? boundActivity;
17+
View? boundView;
18+
OnLayoutItemNotFoundHandler? onLayoutItemNotFound;
1719

18-
protected LayoutBinding (Activity activity, OnLayoutItemNotFoundHandler onLayoutItemNotFound = null)
20+
protected LayoutBinding (Activity activity, OnLayoutItemNotFoundHandler? onLayoutItemNotFound = null)
1921
{
2022
boundActivity = activity ?? throw new ArgumentNullException (nameof (activity));
2123
this.onLayoutItemNotFound = onLayoutItemNotFound;
2224
}
2325

24-
protected LayoutBinding (View view, OnLayoutItemNotFoundHandler onLayoutItemNotFound = null)
26+
protected LayoutBinding (View view, OnLayoutItemNotFoundHandler? onLayoutItemNotFound = null)
2527
{
2628
boundView = view ?? throw new ArgumentNullException (nameof (view));
2729
this.onLayoutItemNotFound = onLayoutItemNotFound;
@@ -38,14 +40,14 @@ protected T FindView <
3840
if (cachedField != null)
3941
return cachedField;
4042

41-
T ret;
43+
T? ret = null;
4244
if (boundActivity != null)
4345
ret = boundActivity.FindViewById <T> (resourceId);
44-
else
46+
else if (boundView != null)
4547
ret = boundView.FindViewById <T> (resourceId);
4648

4749
if (ret == null && onLayoutItemNotFound != null)
48-
ret = (T)onLayoutItemNotFound (resourceId, typeof (T));
50+
ret = (T?)onLayoutItemNotFound (resourceId, typeof (T));
4951

5052
if (ret == null)
5153
throw new global::System.InvalidOperationException ($"View not found (Resource ID: {resourceId})");
@@ -71,16 +73,16 @@ T __FindFragment<
7173
T
7274
> (
7375
int resourceId,
74-
Func<Activity, T> finder,
75-
ref T cachedField)
76+
Func<Activity, T?> finder,
77+
ref T? cachedField)
7678
where T: Java.Lang.Object
7779
{
7880
if (cachedField != null)
7981
return cachedField;
8082

8183
var ret = finder (EnsureActivity ());
8284
if (ret == null && onLayoutItemNotFound != null)
83-
ret = (T)onLayoutItemNotFound (resourceId, typeof (T));
85+
ret = (T?)onLayoutItemNotFound (resourceId, typeof (T));
8486

8587
if (ret == null)
8688
throw new InvalidOperationException ($"Fragment not found (ID: {resourceId}; Type: {typeof (T)})");
@@ -89,29 +91,25 @@ T __FindFragment<
8991
return ret;
9092
}
9193
#if __ANDROID_11__
94+
#pragma warning disable CA1422
9295
protected T FindFragment<
9396
[DynamicallyAccessedMembers (Constructors)]
9497
T
9598
> (
9699
int resourceId,
97-
global::Android.App.Fragment __ignoreMe,
98-
ref T cachedField
100+
global::Android.App.Fragment? __ignoreMe,
101+
ref T? cachedField
99102
)
100103
where T: global::Android.App.Fragment
101104
{
102-
return __FindFragment<T> (resourceId, (activity) => activity.FragmentManager.FindFragmentById<T> (resourceId), ref cachedField);
105+
return __FindFragment<T> (resourceId, (activity) => activity.FragmentManager?.FindFragmentById<T> (resourceId), ref cachedField);
103106
}
107+
#pragma warning restore CA1422
104108
#endif // __ANDROID_11__
105109

106-
#if __HAVE_SUPPORT__
107-
protected T FindFragment <T> (int resourceId, global::Android.Support.V4.App.Fragment __ignoreMe, ref T cachedField) where T: global::Android.Support.V4.App.Fragment
108-
{
109-
return __FindFragment<T> (resourceId, (activity) => activity.FragmentManager.FindFragmentById<T> (resourceId), ref cachedField);
110-
}
111-
#endif // __HAVE_SUPPORT__
112-
113110
#if __HAVE_ANDROIDX__
114-
protected T FindFragment<T> (int resourceId, global::AndroidX.Fragment.App.Fragment __ignoreMe, ref T cachedField) where T: global::AndroidX.Fragment.App.Fragment
111+
protected T FindFragment<T> (int resourceId, global::AndroidX.Fragment.App.Fragment? __ignoreMe, ref T? cachedField)
112+
where T : global::AndroidX.Fragment.App.Fragment
115113
{
116114
return __FindFragment(resourceId, (activity) => {
117115
if (activity is AndroidX.Fragment.App.FragmentActivity activity_) {

src/Xamarin.Android.Build.Tasks/Tasks/GenerateLayoutBindings.BindingGenerator.cs

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,6 @@ public sealed class State
3030
// generating code for the partial Activity class
3131
public string BindingClassName { get; }
3232

33-
public bool LinkerPreserveConstructors { get; set; }
34-
3533
public List<string> ExtraImportNamespaces { get; } = new List <string> ();
3634

3735
public string AndroidFragmentType { get; }
@@ -155,20 +153,18 @@ protected virtual void WriteOnSetContentViewPartials (State state)
155153
WritePartialClassOnSetContentViewPartial_Int (state);
156154
}
157155

158-
public State BeginBindingFile (StreamWriter writer, string layoutResourceId, string classNamespace, string className, string androidFragmentType, bool linkerPreserveConstructors = true)
156+
public State BeginBindingFile (StreamWriter writer, string layoutResourceId, string classNamespace, string className, string androidFragmentType)
159157
{
160-
var state = new State (writer, className, !String.IsNullOrWhiteSpace (classNamespace), androidFragmentType) {
161-
LinkerPreserveConstructors = linkerPreserveConstructors
162-
};
158+
var state = new State (writer, className, !String.IsNullOrWhiteSpace (classNamespace), androidFragmentType);
163159
BeginBindingFile (state, layoutResourceId, classNamespace, className);
164-
WriteBindingConstructors (state, className, state.LinkerPreserveConstructors);
160+
WriteBindingConstructors (state, className);
165161
return state;
166162
}
167163

168164
protected abstract void BeginBindingFile (State state, string layoutResourceId, string classNamespace, string className);
169165
public abstract void EndBindingFile (State state);
170166

171-
protected abstract void WriteBindingConstructors (State state, string className, bool linkerPreserve);
167+
protected abstract void WriteBindingConstructors (State state, string className);
172168
protected abstract void WriteBindingViewProperty (State state, LayoutWidget widget, string resourceNamespace);
173169
protected abstract void WriteBindingFragmentProperty (State state, LayoutWidget widget, string resourceNamespace);
174170
protected abstract void WriteBindingMixedProperty (State state, LayoutWidget widget, string resourceNamespace);
@@ -287,10 +283,11 @@ protected void WriteBindingPropertyBackingField (State state, LayoutWidget widge
287283
WriteResetLocation (state);
288284
}
289285

290-
public void WriteComment (State state, string text)
286+
public void WriteComment (State state, params string [] text)
291287
{
292288
EnsureArgument (state, nameof (state));
293-
WriteLineIndent (state, $"{LineCommentString}{text}");
289+
foreach (string line in text)
290+
WriteLineIndent (state, $"{LineCommentString}{line}");
294291
}
295292

296293
public void WriteComment (State state, ICollection<string> lines)

src/Xamarin.Android.Build.Tasks/Tasks/GenerateLayoutBindings.CSharpBindingGenerator.cs

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -157,8 +157,33 @@ void WriteClassClose (State state)
157157
WriteLineIndent (state, "}");
158158
}
159159

160+
void WriteDisableWarnings (State state)
161+
{
162+
state.WriteLine ("#pragma warning disable CS8981");
163+
state.WriteLine ("#pragma warning disable CS1591");
164+
}
165+
166+
void WriteEnableWarnings (State state)
167+
{
168+
state.WriteLine ("#pragma warning restore CS1591");
169+
state.WriteLine ("#pragma warning restore CS8981");
170+
}
171+
172+
void WriteAutoGeneratedComment (State state)
173+
{
174+
state.WriteLine (@"//------------------------------------------------------------------------------
175+
// <auto-generated>
176+
// This code was generated by a tool.
177+
//
178+
// Changes to this file may cause incorrect behavior and will be lost if
179+
// the code is regenerated.
180+
// </auto-generated>
181+
//------------------------------------------------------------------------------");
182+
}
183+
160184
void WriteFilePreamble (State state, string classNamespace)
161185
{
186+
WriteAutoGeneratedComment (state);
162187
WriteUsings (state);
163188
state.WriteLine ();
164189
WriteNamespaceOpen (state, classNamespace);
@@ -172,13 +197,15 @@ void WriteNamespaceOpen (State state, string classNamespace)
172197
state.WriteLine ($"namespace {classNamespace}");
173198
state.WriteLine ("{");
174199
state.IncreaseIndent ();
200+
WriteDisableWarnings (state);
175201
}
176202

177203
void WriteNamespaceClose (State state)
178204
{
179205
if (!state.IsInNamespace)
180206
return;
181207

208+
WriteEnableWarnings (state);
182209
state.DecreaseIndent ();
183210
state.WriteLine ("}");
184211
}
@@ -200,15 +227,14 @@ void WriteUsing (State state, string ns)
200227
state.WriteLine ($"using global::{ns};");
201228
}
202229

203-
protected override void WriteBindingConstructors (State state, string className, bool linkerPreserve)
230+
protected override void WriteBindingConstructors (State state, string className)
204231
{
205-
WriteConstructor (state, className, "Android.App.Activity", linkerPreserve);
206-
WriteConstructor (state, className, "Android.Views.View", linkerPreserve);
232+
WriteConstructor (state, className, "Android.App.Activity");
233+
WriteConstructor (state, className, "Android.Views.View");
207234
}
208235

209-
void WriteConstructor (State state, string className, string clientType, bool linkerPreserve)
236+
void WriteConstructor (State state, string className, string clientType)
210237
{
211-
WritePreserveAtribute (state, linkerPreserve);
212238
WriteLineIndent (state, $"public {className} (");
213239
state.IncreaseIndent ();
214240
WriteLineIndent (state, $"global::{clientType} client,");
@@ -221,14 +247,6 @@ void WriteConstructor (State state, string className, string clientType, bool li
221247
state.WriteLine ();
222248
}
223249

224-
void WritePreserveAtribute (State state, bool linkerPreserve)
225-
{
226-
if (!linkerPreserve)
227-
return;
228-
229-
WriteLineIndent (state, $"[global::Android.Runtime.PreserveAttribute (Conditional=true)]");
230-
}
231-
232250
public override void EndBindingFile (State state)
233251
{
234252
EnsureArgument (state, nameof (state));

src/Xamarin.Android.Build.Tasks/Tasks/GenerateResourceDesignerIntermediateClass.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ public class GenerateResourceDesignerIntermediateClass : AndroidTask
2424
2525
namespace %NAMESPACE% {
2626
#pragma warning disable IDE0002
27+
/// <summary>
28+
/// Android Resource Designer class.
29+
/// Exposes the Android Resource designer assembly into the project Namespace.
30+
/// </summary>
2731
public partial class Resource : %BASECLASS% {
2832
}
2933
#pragma warning restore IDE0002

src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/CodeBehindTests.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,7 @@ void SuccessfulBuild_AndroidX (TestProjectInfo testInfo, bool many, bool dtb, Lo
339339

340340
CopyLogs (testInfo, true);
341341
Assert.That (success, Is.True, "Build should have succeeded");
342+
Assert.IsTrue (StringAssertEx.ContainsText (builder.LastBuildOutput, " 0 Warning(s)"), $"{builder.BuildLogFile} should have no MSBuild warnings.");
342343

343344
CopyGeneratedFiles (testInfo);
344345

@@ -508,7 +509,8 @@ string GetBuildTarget (bool isDTB)
508509
string[] GetBuildProperties (LocalBuilder builder, bool manyBuild, bool dtbBuild, bool referenceAndroidX, params string[] extraConstants)
509510
{
510511
var ret = new List <string> {
511-
"AndroidGenerateLayoutBindings=true"
512+
"AndroidGenerateLayoutBindings=true",
513+
"\"NoWarn=CS0414;CA1416;CS1591;XA1005;XA4225\""
512514
};
513515
if (manyBuild)
514516
ret.Add ("ForceParallelBuild=true");

tests/CodeBehind/BuildTests/CodeBehindBuildTests.NET.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,6 @@
6464
<ProjectReference Include="..\CommonSampleLibrary\CommonSampleLibrary.NET.csproj" />
6565
</ItemGroup>
6666
<ItemGroup Condition=" '$(ReferenceAndroidX)' == 'True' ">
67-
<PackageReference Include="Xamarin.AndroidX.AppCompat" Version="1.4.1.1" />
67+
<PackageReference Include="Xamarin.AndroidX.AppCompat" Version="1.6.1.5" />
6868
</ItemGroup>
6969
</Project>

tests/CodeBehind/BuildTests/MainMergeActivity.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ protected override void OnCreate (Bundle savedInstanceState)
2424

2525
#if NOT_CONFLICTING_FRAGMENT
2626
CommonSampleLibrary.LogFragment log2 = secondary_log_fragment;
27+
#elif __HAVE_ANDROIDX__
28+
global::AndroidX.Fragment.App.Fragment log2 = secondary_log_fragment;
2729
#else
2830
global::Android.App.Fragment log2 = secondary_log_fragment;
2931
#endif
Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.xamarin.CodeBehindBuildTests">
3-
<uses-sdk android:minSdkVersion="21" />
43
<application android:allowBackup="true" android:icon="@mipmap/icon" android:label="@string/app_name">
54
</application>
65
</manifest>

tests/CodeBehind/CommonSampleLibrary/CommonSampleLibrary.NET.csproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,10 @@
55
<AssemblyName>CommonSampleLibrary</AssemblyName>
66
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
77
</PropertyGroup>
8+
<PropertyGroup>
9+
<DefineConstants Condition=" '$(ExtraConstants)' != '' ">$(DefineConstants);$(ExtraConstants)</DefineConstants>
10+
</PropertyGroup>
11+
<ItemGroup Condition=" '$(ReferenceAndroidX)' == 'True' ">
12+
<PackageReference Include="Xamarin.AndroidX.AppCompat" Version="1.6.1.5" />
13+
</ItemGroup>
814
</Project>

tests/CodeBehind/CommonSampleLibrary/Logger/LogFragment.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,21 @@
1919
using Android.Views;
2020
using Android.Widget;
2121
using Android.Graphics;
22+
using Android.Util;
2223

2324
namespace CommonSampleLibrary
2425
{
26+
#pragma warning disable CA1422
2527
/**
2628
* Simple fraggment which contains a LogView and uses is to output log data it receives
2729
* through the LogNode interface.
2830
*/
29-
public class LogFragment : Fragment
31+
public class LogFragment :
32+
#if __HAVE_ANDROIDX__
33+
AndroidX.Fragment.App.Fragment
34+
#else
35+
Fragment
36+
#endif
3037
{
3138
LogView mLogView;
3239
ScrollView mScrollView;
@@ -60,7 +67,7 @@ public View InflateViews ()
6067

6168
mLogView.Gravity = GravityFlags.Bottom;
6269
mLogView.SetTextAppearance (Activity, Android.Resource.Style.TextAppearanceMedium);
63-
70+
6471
mScrollView.AddView (mLogView);
6572
return mScrollView;
6673
}
@@ -77,5 +84,6 @@ public LogView LogView {
7784
get { return mLogView; }
7885
}
7986
}
87+
#pragma warning restore CA1422
8088
}
8189

0 commit comments

Comments
 (0)