Skip to content

Commit 835d9d1

Browse files
committed
Merge pull request #7 from dellis1972/master
[Xamarin.Android.Build.Tasks] Added missing files from commit 59ec488
2 parents 45a0818 + 2aea0af commit 835d9d1

File tree

9 files changed

+867
-0
lines changed

9 files changed

+867
-0
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<Project>
2+
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.FSharp.targets" />
3+
</Project>
4+
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
2+
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
3+
</Project>
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<Project>
2+
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.Common.targets" />
3+
</Project>
4+
Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Linq;
5+
using System.Text.RegularExpressions;
6+
using System.Xml.Linq;
7+
using System.Xml.XPath;
8+
9+
namespace Monodroid {
10+
static class AndroidResource {
11+
12+
public static void UpdateXmlResource (string filename, Dictionary<string, string> acwMap, IEnumerable<string> additionalDirectories = null)
13+
{
14+
// use a temporary file so we only update the real file if things actually changed
15+
string tmpfile = filename + ".bk";
16+
try {
17+
XDocument doc = XDocument.Load (filename, LoadOptions.SetLineInfo);
18+
19+
// The assumption here is that the file we're fixing up is in a directory below the
20+
// obj/${Configuration}/res/ directory and so appending ../gives us the actual path to
21+
// 'res/'
22+
UpdateXmlResource (Path.Combine (Path.GetDirectoryName (filename), ".."), doc.Root, acwMap, additionalDirectories);
23+
using (var stream = File.OpenWrite (tmpfile))
24+
using (var xw = new LinePreservedXmlWriter (new StreamWriter (stream)))
25+
xw.WriteNode (doc.CreateNavigator (), false);
26+
Xamarin.Android.Tasks.MonoAndroidHelper.CopyIfChanged (tmpfile, filename);
27+
File.Delete (tmpfile);
28+
}
29+
catch (Exception e) {
30+
if (File.Exists (tmpfile)) {
31+
File.Delete (tmpfile);
32+
}
33+
Console.Error.WriteLine ("AndroidResgen: Warning while updating Resource XML '{0}': {1}", filename, e.Message);
34+
return;
35+
}
36+
}
37+
38+
static readonly XNamespace android = "http://schemas.android.com/apk/res/android";
39+
static readonly XNamespace res_auto = "http://schemas.android.com/apk/res-auto";
40+
static readonly Regex r = new Regex (@"^@\+?(?<package>[^:]+:)?(anim|color|drawable|layout|menu)/(?<file>.*)$");
41+
static readonly string[] fixResourcesAliasPaths = {
42+
"/resources/item",
43+
"/resources/integer-array/item",
44+
"/resources/array/item",
45+
"/resources/style/item",
46+
};
47+
48+
public static void UpdateXmlResource (XElement e)
49+
{
50+
UpdateXmlResource (e, new Dictionary<string,string> ());
51+
}
52+
53+
public static void UpdateXmlResource (XElement e, Dictionary<string, string> acwMap)
54+
{
55+
UpdateXmlResource (null, e, acwMap);
56+
}
57+
58+
static IEnumerable<T> Prepend<T> (this IEnumerable<T> l, T another) where T : XNode
59+
{
60+
yield return another;
61+
foreach (var e in l)
62+
yield return e;
63+
}
64+
65+
static void UpdateXmlResource (string resourcesBasePath, XElement e, Dictionary<string, string> acwMap, IEnumerable<string> additionalDirectories = null)
66+
{
67+
foreach (var elem in GetElements (e).Prepend (e)) {
68+
TryFixCustomView (elem, acwMap);
69+
}
70+
71+
foreach (var path in fixResourcesAliasPaths) {
72+
foreach(XElement item in e.XPathSelectElements (path).Prepend (e)) {
73+
TryFixResourceAlias (item, resourcesBasePath, additionalDirectories);
74+
}
75+
}
76+
77+
foreach (XAttribute a in GetAttributes (e)) {
78+
if (a.IsNamespaceDeclaration)
79+
continue;
80+
81+
if (TryFixFragment (a, acwMap))
82+
continue;
83+
84+
if (TryFixResAuto (a, acwMap))
85+
continue;
86+
87+
if (TryFixCustomClassAttribute (a, acwMap))
88+
continue;
89+
90+
if (a.Name.Namespace != android &&
91+
!(a.Name.LocalName == "layout" && a.Name.Namespace == XNamespace.None &&
92+
a.Parent.Name.LocalName == "include" && a.Parent.Name.Namespace == XNamespace.None))
93+
continue;
94+
95+
Match m = r.Match (a.Value);
96+
if (!m.Success)
97+
continue;
98+
if (m.Groups ["package"].Success)
99+
continue;
100+
a.Value = TryLowercaseValue (a.Value, resourcesBasePath, additionalDirectories);
101+
}
102+
}
103+
104+
static bool ResourceNeedsToBeLowerCased (string value, string resourceBasePath, IEnumerable<string> additionalDirectories)
105+
{
106+
// Might be a bit of an overkill, but the data comes (indirectly) from the user since it's the
107+
// path to the msbuild's intermediate output directory and that location can be changed by the
108+
// user. It's better to be safe than sorry.
109+
resourceBasePath = (resourceBasePath ?? String.Empty).Trim ();
110+
if (String.IsNullOrEmpty (resourceBasePath))
111+
return true;
112+
113+
// Avoid resource names that are all whitespace
114+
value = (value ?? String.Empty).Trim ();
115+
if (String.IsNullOrEmpty (value))
116+
return false; // let's save some time
117+
if (value.Length < 4 || value [0] != '@') // 4 is the minimum length since we need a string
118+
// that is at least of the following
119+
// form: @x/y. Checking it here saves some time
120+
// below.
121+
return true;
122+
123+
string filePath = null;
124+
int slash = value.IndexOf ('/');
125+
int colon = value.IndexOf (':');
126+
if (colon == -1)
127+
colon = 0;
128+
129+
// Determine the the potential definition file's path based on the resource type.
130+
string dirPrefix = value.Substring (colon + 1, slash - colon - 1).ToLowerInvariant ();
131+
string fileNamePattern = value.Substring (slash + 1).ToLowerInvariant () + ".*";
132+
133+
if (Directory.EnumerateDirectories (resourceBasePath, dirPrefix + "*").Any (dir => Directory.EnumerateFiles (dir, fileNamePattern).Any ()))
134+
return true;
135+
136+
// check additional directories if we have them incase the resource is in a library project
137+
if (additionalDirectories != null)
138+
foreach (var additionalDirectory in additionalDirectories)
139+
if (Directory.EnumerateDirectories (additionalDirectory, dirPrefix + "*").Any (dir => Directory.EnumerateFiles (dir, fileNamePattern).Any ()))
140+
return true;
141+
142+
// No need to change the reference case.
143+
return false;
144+
}
145+
146+
static IEnumerable<XAttribute> GetAttributes (XElement e)
147+
{
148+
foreach (XAttribute a in e.Attributes ())
149+
yield return a;
150+
foreach (XElement c in e.Elements ())
151+
foreach (XAttribute a in GetAttributes (c))
152+
yield return a;
153+
}
154+
155+
static IEnumerable<XElement> GetElements (XElement e)
156+
{
157+
foreach (var a in e.Elements ()) {
158+
yield return a;
159+
160+
foreach (var b in GetElements (a))
161+
yield return b;
162+
}
163+
}
164+
165+
private static void TryFixResourceAlias (XElement elem, string resourceBasePath, IEnumerable<string> additionalDirectories)
166+
{
167+
// Looks for any resources aliases:
168+
// <item type="layout" name="">@layout/Page1</item>
169+
// <item type="layout" name="">@drawable/Page1</item>
170+
// and corrects the alias to be lower case.
171+
if (elem.Name == "item" && !string.IsNullOrEmpty(elem.Value) ) {
172+
string value = elem.Value.Trim();
173+
Match m = r.Match (value);
174+
if (m.Success) {
175+
elem.Value = TryLowercaseValue (elem.Value, resourceBasePath, additionalDirectories);
176+
}
177+
}
178+
}
179+
180+
private static bool TryFixFragment (XAttribute attr, Dictionary<string, string> acwMap)
181+
{
182+
// Looks for any:
183+
// <fragment class="My.DotNet.Class"
184+
// <fragment android:name="My.DotNet.Class" ...
185+
// and tries to change it to the ACW name
186+
if (attr.Parent.Name != "fragment")
187+
return false;
188+
189+
if (attr.Name == "class" || attr.Name == android + "name") {
190+
if (acwMap.ContainsKey (attr.Value)) {
191+
attr.Value = acwMap[attr.Value];
192+
193+
return true;
194+
}
195+
}
196+
197+
return false;
198+
}
199+
200+
private static bool TryFixResAuto (XAttribute attr, Dictionary<string, string> acwMap)
201+
{
202+
if (attr.Name.Namespace != res_auto)
203+
return false;
204+
switch (attr.Name.LocalName) {
205+
case "rectLayout":
206+
case "roundLayout":
207+
attr.Value = attr.Value.ToLowerInvariant ();
208+
return true;
209+
}
210+
return false;
211+
}
212+
213+
private static bool TryFixCustomView (XElement elem, Dictionary<string, string> acwMap)
214+
{
215+
// Looks for any <My.DotNet.Class ...
216+
// and tries to change it to the ACW name
217+
if (acwMap.ContainsKey (elem.Name.ToString ())) {
218+
elem.Name = acwMap[elem.Name.ToString ()];
219+
return true;
220+
}
221+
222+
return false;
223+
}
224+
225+
private static bool TryFixCustomClassAttribute (XAttribute attr, Dictionary<string, string> acwMap)
226+
{
227+
/* Some attributes reference a Java class name.
228+
* try to convert those like for TryFixCustomView
229+
*/
230+
if (attr.Name != (res_auto + "layout_behavior") // For custom CoordinatorLayout behavior
231+
&& (attr.Parent.Name != "transition" || attr.Name.LocalName != "class")) // For custom transitions
232+
return false;
233+
234+
string mappedValue;
235+
if (!acwMap.TryGetValue (attr.Value, out mappedValue))
236+
return false;
237+
238+
attr.Value = mappedValue;
239+
return true;
240+
}
241+
242+
private static string TryLowercaseValue (string value, string resourceBasePath, IEnumerable<string> additionalDirectories)
243+
{
244+
int s = value.LastIndexOf ('/');
245+
if (s >= 0) {
246+
if (ResourceNeedsToBeLowerCased (value, resourceBasePath, additionalDirectories))
247+
return value.Substring (0, s) + "/" + value.Substring (s+1).ToLowerInvariant ();
248+
}
249+
return value;
250+
}
251+
}
252+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
namespace Xamarin.Android.Tools {
2+
3+
static class Features {
4+
}
5+
}

0 commit comments

Comments
 (0)