diff --git a/src/Mono.Android/Test/Mono.Android-Tests.csproj b/src/Mono.Android/Test/Mono.Android-Tests.csproj
index ef762d3d91d..0d107101d4e 100644
--- a/src/Mono.Android/Test/Mono.Android-Tests.csproj
+++ b/src/Mono.Android/Test/Mono.Android-Tests.csproj
@@ -97,6 +97,8 @@
+
+
diff --git a/src/Mono.Android/Test/Resources/layout/FragmentFixup.axml b/src/Mono.Android/Test/Resources/layout/FragmentFixup.axml
new file mode 100644
index 00000000000..db6bb86933f
--- /dev/null
+++ b/src/Mono.Android/Test/Resources/layout/FragmentFixup.axml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
diff --git a/src/Mono.Android/Test/Resources/layout/Main.axml b/src/Mono.Android/Test/Resources/layout/Main.axml
index 25d909baff7..97958e7bd90 100644
--- a/src/Mono.Android/Test/Resources/layout/Main.axml
+++ b/src/Mono.Android/Test/Resources/layout/Main.axml
@@ -3,15 +3,37 @@
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
+ xmlns:tools="http://schemas.xamarin.com/android/tools"
+ tools:class="Xamarin.Android.RuntimeTests.MainActivity"
>
+ />
+ />
+
+
+
diff --git a/src/Mono.Android/Test/Xamarin.Android.RuntimeTests/MainActivity.cs b/src/Mono.Android/Test/Xamarin.Android.RuntimeTests/MainActivity.cs
index a69679300d4..d2412fa1267 100644
--- a/src/Mono.Android/Test/Xamarin.Android.RuntimeTests/MainActivity.cs
+++ b/src/Mono.Android/Test/Xamarin.Android.RuntimeTests/MainActivity.cs
@@ -1,16 +1,26 @@
using System.Reflection;
using Android.App;
using Android.OS;
+using Android.Views;
+using Android.Widget;
using Xamarin.Android.NUnitLite;
namespace Xamarin.Android.RuntimeTests
{
[Activity (Label = "runtime", MainLauncher = true,
Name="xamarin.android.runtimetests.MainActivity")]
- public class MainActivity : TestSuiteActivity
+ public partial class MainActivity : TestSuiteActivity
{
protected override void OnCreate (Bundle bundle)
{
+ // Note; for testing fixup only.
+ // The actual view is set/replaced in `TestSuiteActivity.OnCreate()`
+ SetContentView (Resource.Layout.FragmentFixup);
+
+ first_text_view.Click += delegate {
+ // ignore
+ };
+
// tests can be inside the main assembly
AddTest (Assembly.GetExecutingAssembly ());
// or in any reference assemblies
@@ -20,5 +30,17 @@ protected override void OnCreate (Bundle bundle)
base.OnCreate (bundle);
}
}
+
+#if __ANDROID_11__
+ public class MyFragment : Fragment {
+
+ public override View OnCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
+ {
+ return new TextView (Activity) {
+ Text = "via fragment!",
+ };
+ }
+ }
+#endif // ANDROID_15
}
diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateLayoutCodeBehind.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateLayoutCodeBehind.cs
index a493d959c78..96bc08026fd 100644
--- a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateLayoutCodeBehind.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateLayoutCodeBehind.cs
@@ -46,6 +46,7 @@ public bool IsLeaf {
public Widget Parent { get; set; }
public bool IsInaccessible { get; set; }
public bool IsRoot { get; set; }
+ public bool IsFragment { get; set; }
public void AddChild (Widget child)
{
@@ -121,7 +122,7 @@ public override bool Execute ()
void GetLineInfo (IXmlLineInfo linfo, out int line, out int column)
{
- if (linfo == null || linfo.HasLineInfo ()) {
+ if (linfo == null || !linfo.HasLineInfo ()) {
line = column = 1;
return;
}
@@ -167,7 +168,6 @@ bool GenerateLayoutMembers (CodeTypeDeclaration mainClass, string fileName)
LoadWidgets (fileName, subtree, lineInfo, androidNS, root, globalIdCache);
}
}
-
}
}
}
@@ -194,8 +194,30 @@ void LoadWidgets (string fileName, XmlReader reader, IXmlLineInfo lineinfo, str
}
}
while (reader.Read ()) {
- LoadWidgets (fileName, reader, lineinfo, androidNS, root ?? widgetRoot, globalIdCache);
+ LoadWidgets (fileName, reader, lineinfo, androidNS, widgetRoot, globalIdCache);
+ }
+ }
+
+ static readonly Dictionary> WidgetCreators = new Dictionary> {
+ ["fragment"] = CreateFragmentWidget,
+ };
+
+ const string AndroidXmlNamespace = "http://schemas.android.com/apk/res/android";
+ const string AndroidXmlName = "name";
+
+ static Widget CreateFragmentWidget (Widget w, XmlReader e)
+ {
+ var type = e.GetAttribute (AndroidXmlName, AndroidXmlNamespace);
+ if (type == null) {
+ return null;
}
+
+ w.IsFragment = true;
+
+ var c = type.IndexOf (',');
+ w.Type = c < 0 ? type : type.Substring (0, c);
+ w.Type = w.Type;
+ return w;
}
Widget CreateWidget (string fileName, XmlReader e, IXmlLineInfo lineInfo, string id, Widget parent)
@@ -220,6 +242,11 @@ Widget CreateWidget (string fileName, XmlReader e, IXmlLineInfo lineInfo, string
Column = column
};
+ Func creator;
+ if (WidgetCreators.TryGetValue (e.LocalName, out creator)) {
+ return creator (ret, e);
+ }
+
return ret;
}
@@ -401,7 +428,7 @@ CodeMemberMethod ImplementWidgetCreator (Widget widget, CodeExpression parent, L
CodeMethodInvokeExpression CreateFindViewInvoke (Widget widget, CodeExpression parent, CodeExpression parentView)
{
- var findViewRef = new CodeMethodReferenceExpression (parent, "__FindView");
+ var findViewRef = new CodeMethodReferenceExpression (parent, widget.IsFragment ? "__FindFragment" : "__FindView");
findViewRef.TypeArguments.Add (new CodeTypeReference (widget.Type));
return new CodeMethodInvokeExpression (findViewRef, new CodeExpression [] { parentView, new CodeSnippetExpression (widget.ID) });
@@ -416,7 +443,7 @@ CodeMemberField CreateBackingField (Widget widget, string memberType)
CodeMemberField CreateBackingFuncField (Widget widget, string memberType)
{
- return new CodeMemberField ($"Func<{memberType}>", $"__{widget.Name}Func");
+ return new CodeMemberField ($"global::System.Func<{memberType}>", $"__{widget.Name}Func");
}
bool HasUniqueId (Widget widget, Dictionary globalIdCache)
@@ -484,7 +511,7 @@ ITaskItem GenerateCodeBehind (ITaskItem layoutFile, CodeGeneratorOptions generat
var ns = new CodeNamespace (namespaceName);
compileUnit.Namespaces.Add (ns);
foreach (string import in StandardImports)
- ns.Imports.Add (new CodeNamespaceImport (import));
+ ns.Imports.Add (new CodeNamespaceImport ($"global::{import}"));
CodeTypeDeclaration mainClass = AddMainClass (layoutFile, ns, className);
if (!GenerateLayoutMembers (mainClass, Path.GetFullPath (layoutFile.ItemSpec)))
@@ -578,7 +605,9 @@ void AddCommonMembers (CodeTypeDeclaration klass, ITaskItem layoutFile)
klass.Members.Add (ImplementFindView (activityTypeRef));
klass.Members.Add (ImplementFindView (new CodeTypeReference ("Android.App.Fragment", CodeTypeReferenceOptions.GlobalReference), activityTypeRef, (CodeVariableReferenceExpression parentView) => new CodePropertyReferenceExpression (parentView, "Activity")));
klass.Members.Add (ImplementEnsureView ());
+ klass.Members.Add (ImplementFindFragment (activityTypeRef));
klass.Members.Add (new CodeSnippetTypeMember ("\tpartial void OnLayoutViewNotFound (int resourceId, ref T type) where T : global::Android.Views.View;"));
+ klass.Members.Add (new CodeSnippetTypeMember ("\tpartial void OnLayoutFragmentNotFound (int resourceId, ref T type) where T : global::Android.App.Fragment;"));
}
CodeMemberMethod ImplementInitializeContentView (ITaskItem layoutFile)
@@ -609,7 +638,7 @@ CodeMemberMethod ImplementEnsureView ()
method.TypeParameters.Add (typeParam);
var tRef = new CodeTypeReference (typeParam);
- var funcRef = new CodeTypeReference (typeof (Func<>));
+ var funcRef = new CodeTypeReference (typeof (Func<>), CodeTypeReferenceOptions.GlobalReference);
funcRef.TypeArguments.Add (tRef);
method.Parameters.Add (new CodeParameterDeclarationExpression (funcRef, "creator"));
@@ -635,7 +664,7 @@ CodeMemberMethod ImplementEnsureView ()
var creatorVarRef = new CodeVariableReferenceExpression ("creator");
var argNullEx = new CodeThrowExceptionStatement (
new CodeObjectCreateExpression (
- new CodeTypeReference (typeof (ArgumentNullException)),
+ new CodeTypeReference (typeof (ArgumentNullException), CodeTypeReferenceOptions.GlobalReference),
new [] { new CodeSnippetExpression ("nameof (creator)") }
)
);
@@ -653,6 +682,75 @@ CodeMemberMethod ImplementEnsureView ()
return method;
}
+ CodeMemberMethod ImplementFindFragment (CodeTypeReference typeForParent, CodeTypeReference typeForOverloadCall = null, Func constructParentViewCall = null)
+ {
+ CodeMemberMethod method = CreateMethod ("__FindFragment", MethodAccessibility.Private, MethodScope.Final);
+
+ var typeParam = new CodeTypeParameter ("T") {
+ Constraints = {
+ new CodeTypeReference ("Android.App.Fragment", CodeTypeReferenceOptions.GlobalReference),
+ },
+ };
+ method.TypeParameters.Add (typeParam);
+ method.Parameters.Add (new CodeParameterDeclarationExpression (typeForParent, "activity"));
+ method.Parameters.Add (new CodeParameterDeclarationExpression (typeof (int), "id"));
+
+ var tReference = new CodeTypeReference (typeParam);
+ method.ReturnType = tReference;
+
+ // T fragment = FragmentManager.FindFragmentById (id);
+ var id = new CodeVariableReferenceExpression ("id");
+
+ var findByIdRef = new CodeMethodReferenceExpression (
+ new CodePropertyReferenceExpression (new CodeVariableReferenceExpression ("activity"), "FragmentManager"),
+ "FindFragmentById",
+ new [] { tReference }
+ );
+
+ var findByIdInvoke = new CodeMethodInvokeExpression (findByIdRef, new [] { id });
+ var viewVar = new CodeVariableDeclarationStatement (tReference, "fragment", findByIdInvoke);
+ method.Statements.Add (viewVar);
+
+ // if (view == null) {
+ // OnLayoutFragmentNotFound (resourceId, ref view);
+ // }
+ // if (view != null)
+ // return view;
+ // throw new System.InvalidOperationException($"Fragment not found (ID: {id})");
+
+ var viewVarRef = new CodeVariableReferenceExpression ("fragment");
+ var ifViewNotNull = new CodeConditionStatement (
+ new CodeBinaryOperatorExpression (viewVarRef, CodeBinaryOperatorType.IdentityInequality, new CodePrimitiveExpression (null)),
+ new CodeStatement [] { new CodeMethodReturnStatement (viewVarRef) }
+ );
+
+ var viewRefParam = new CodeDirectionExpression (FieldDirection.Ref, viewVarRef);
+ var viewNotFoundInvoke = new CodeMethodInvokeExpression (
+ new CodeThisReferenceExpression (),
+ "OnLayoutFragmentNotFound",
+ new CodeExpression [] { id, viewRefParam }
+ );
+
+ var ifViewNull = new CodeConditionStatement (
+ new CodeBinaryOperatorExpression (viewVarRef, CodeBinaryOperatorType.IdentityEquality, new CodePrimitiveExpression (null)),
+ new CodeStatement [] { new CodeExpressionStatement (viewNotFoundInvoke) }
+ );
+
+ method.Statements.Add (ifViewNull);
+ method.Statements.Add (ifViewNotNull);
+
+ var throwInvOp = new CodeThrowExceptionStatement (
+ new CodeObjectCreateExpression (
+ new CodeTypeReference (typeof (InvalidOperationException), CodeTypeReferenceOptions.GlobalReference),
+ new [] { new CodeSnippetExpression ("$\"Fragment not found (ID: {id})\"") }
+ )
+ );
+
+ method.Statements.Add (throwInvOp);
+
+ return method;
+ }
+
CodeMemberMethod ImplementFindView (CodeTypeReference typeForParent, CodeTypeReference typeForOverloadCall = null, Func constructParentViewCall = null)
{
CodeMemberMethod method = CreateMethod ("__FindView", MethodAccessibility.Private, MethodScope.Final);
@@ -729,7 +827,7 @@ CodeMemberMethod ImplementFindView (CodeTypeReference typeForParent, CodeTypeRef
var throwInvOp = new CodeThrowExceptionStatement (
new CodeObjectCreateExpression (
- new CodeTypeReference (typeof (InvalidOperationException)),
+ new CodeTypeReference (typeof (InvalidOperationException), CodeTypeReferenceOptions.GlobalReference),
new [] { new CodeSnippetExpression ("$\"View not found (ID: {resourceId})\"") }
)
);
@@ -746,7 +844,7 @@ CodeMemberMethod CreateMethod (string methodName, MethodAccessibility access, Me
CodeMemberMethod CreateMethod (string methodName, MethodAccessibility access, MethodScope scope, Type returnType)
{
- return CreateMethod (methodName, access, scope, new CodeTypeReference (returnType));
+ return CreateMethod (methodName, access, scope, new CodeTypeReference (returnType, CodeTypeReferenceOptions.GlobalReference));
}
CodeMemberMethod CreateMethod (string methodName, MethodAccessibility access, MethodScope scope, string returnType)
@@ -813,7 +911,7 @@ void MarkAsCompilerGenerated (CodeTypeMember member)
void AddCustomAttribute (CodeAttributeDeclarationCollection attributes, Type type)
{
- attributes.Add (new CodeAttributeDeclaration (new CodeTypeReference (type)));
+ attributes.Add (new CodeAttributeDeclaration (new CodeTypeReference (type, CodeTypeReferenceOptions.GlobalReference)));
}
}
}
\ No newline at end of file