Skip to content

Commit 13a5305

Browse files
committed
Enable jetifier as a property in custom gradle properties
From Unity 2019.3 onwards, android projects in Unity have changed their structure where we have 2 separate modules - unity lib and launcher. Custom gradle main template settings are active only for unity lib. This causes problems when building Android applications with jetifier enabled. The fix was to use the root level gradle properties file and use the mechanism provided in Unity (from 2019.3 onwards) to specify a custom gradle properties file. Bug: 159070827 Fixes 360 Change-Id: Iaef9e7cee0107d218dc1b646685e0efb5ad58ed1
1 parent a88a050 commit 13a5305

File tree

3 files changed

+282
-60
lines changed

3 files changed

+282
-60
lines changed

source/AndroidResolver/src/GradleTemplateResolver.cs

Lines changed: 182 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,23 @@ namespace GooglePlayServices {
3030
/// </summary>
3131
internal class GradleTemplateResolver {
3232

33+
/// <summary>
34+
/// Path of the Gradle properties file.
35+
/// Available only from Unity version 2019.3 onwards
36+
/// </summary>
37+
public static string GradlePropertiesTemplatePath =
38+
Path.Combine(SettingsDialog.AndroidPluginsDir, "gradleTemplate.properties");
39+
40+
/// <summary>
41+
/// Line that indicates the start of the injected properties block in properties template.
42+
/// </summary>
43+
private const string PropertiesStartLine = "# Android Resolver Properties Start";
44+
45+
/// <summary>
46+
/// Line that indicates the end of the injected properties block in properties template.
47+
/// </summary>
48+
private const string PropertiesEndLine = "# Android Resolver Properties End";
49+
3350
/// <summary>
3451
/// Path of the Gradle template file.
3552
/// </summary>
@@ -52,6 +69,13 @@ internal class GradleTemplateResolver {
5269
private const string ReposInjectionLine =
5370
@".*apply plugin: 'com\.android\.(application|library)'.*";
5471

72+
/// <summary>
73+
/// Token that indicates where gradle properties should initially be injected.
74+
/// If this isn't present in the properties template, properties will not be
75+
/// injected or they'll be removed.
76+
/// </summary>
77+
private const string PropertiesInjectionLine = @"ADDITIONAL_PROPERTIES";
78+
5579
/// <summary>
5680
/// Line that indicates the start of the injected dependencies block in the template.
5781
/// </summary>
@@ -261,6 +285,149 @@ public List<string> ProcessLine(string line, out bool injectionApplied) {
261285
}
262286
}
263287

288+
/// <summary>
289+
/// Patch file contents by injecting custom data.
290+
/// </summary>
291+
/// <param name="filePath">Path to file to modify</param>
292+
/// <param name="fileDescription">Used in logs for describing the file</param>
293+
/// <param name="analyticsReportName">Name used in analytics logs</param>
294+
/// <param name="analyticsReportUrlToken">Token used in forming analytics path</param>
295+
/// <param name="injectors">Array of text injectors</param>
296+
/// <param name="resolutionMeasurementProperties">used in analytics reporting</param>
297+
/// <returns>true if successful, false otherwise.</returns>
298+
private static bool PatchFile(string filePath,
299+
string fileDescription,
300+
string analyticsReportName,
301+
string analyticsReportUrlToken,
302+
TextFileLineInjector[] injectors,
303+
ICollection<KeyValuePair<string, string>>
304+
resolutionMeasurementParameters) {
305+
IEnumerable<string> lines;
306+
try {
307+
lines = File.ReadAllLines(filePath);
308+
} catch (Exception ex) {
309+
PlayServicesResolver.analytics.Report(
310+
"/resolve/" + analyticsReportUrlToken + "/failed/templateunreadable",
311+
analyticsReportName + " Resolve: Failed Template Unreadable");
312+
PlayServicesResolver.Log(
313+
String.Format("Unable to patch {0} ({1})", fileDescription, ex.ToString()),
314+
level: LogLevel.Error);
315+
return false;
316+
}
317+
318+
// Lines that will be written to the output file.
319+
var outputLines = new List<string>();
320+
foreach (var line in lines) {
321+
var currentOutputLines = new List<string>();
322+
foreach (var injector in injectors) {
323+
bool injectionApplied = false;
324+
currentOutputLines = injector.ProcessLine(line, out injectionApplied);
325+
if (injectionApplied || currentOutputLines.Count == 0) break;
326+
}
327+
outputLines.AddRange(currentOutputLines);
328+
}
329+
330+
var inputText = String.Join("\n", (new List<string>(lines)).ToArray()) + "\n";
331+
var outputText = String.Join("\n", outputLines.ToArray()) + "\n";
332+
333+
if (inputText == outputText) {
334+
PlayServicesResolver.Log(String.Format("No changes to {0}", fileDescription),
335+
level: LogLevel.Verbose);
336+
return true;
337+
}
338+
return WriteToFile(filePath, fileDescription, outputText,
339+
analyticsReportName, analyticsReportUrlToken,
340+
resolutionMeasurementParameters);
341+
}
342+
343+
/// <summary>
344+
/// Write new contents to a file on disk
345+
/// </summary>
346+
/// <param name="filePath">Path to file to modify</param>
347+
/// <param name="fileDescription">Used in logs for describing the file</param>
348+
/// <param name="outputText">Updated contents to write to the file</param>
349+
/// <param name="analyticsReportName">Name used in analytics logs</param>
350+
/// <param name="analyticsReportUrlToken">Token used in forming analytics path</param>
351+
/// <param name="resolutionMeasurementProperties">used in analytics reporting</param>
352+
/// <returns>true if successful, false otherwise.</returns>
353+
private static bool WriteToFile(string filePath,
354+
string fileDescription,
355+
string outputText,
356+
string analyticsReportName,
357+
string analyticsReportUrlToken,
358+
ICollection<KeyValuePair<string, string>>
359+
resolutionMeasurementParameters) {
360+
if (!FileUtils.CheckoutFile(filePath, PlayServicesResolver.logger)) {
361+
PlayServicesResolver.Log(
362+
String.Format("Failed to checkout '{0}', unable to patch the file.",
363+
filePath), level: LogLevel.Error);
364+
PlayServicesResolver.analytics.Report(
365+
"/resolve/" + analyticsReportUrlToken + "/failed/checkout",
366+
analyticsReportName + " Resolve: Failed to checkout");
367+
return false;
368+
}
369+
PlayServicesResolver.Log(
370+
String.Format("Writing updated {0}", fileDescription),
371+
level: LogLevel.Verbose);
372+
try {
373+
File.WriteAllText(filePath, outputText);
374+
PlayServicesResolver.analytics.Report(
375+
"/resolve/"+analyticsReportUrlToken+"/success",
376+
resolutionMeasurementParameters,
377+
analyticsReportName + " Resolve Success");
378+
} catch (Exception ex) {
379+
PlayServicesResolver.analytics.Report(
380+
"/resolve/"+analyticsReportUrlToken+"/failed/write",
381+
analyticsReportName + " Resolve: Failed to write");
382+
PlayServicesResolver.Log(
383+
String.Format("Unable to patch {0} ({1})", fileDescription,
384+
ex.ToString()), level: LogLevel.Error);
385+
return false;
386+
}
387+
return true;
388+
}
389+
390+
/// <summary>
391+
/// Inject properties in the gradle properties template file.
392+
/// Because of a change in structure of android projects built with
393+
/// Unity 2019.3 and above, the correct way to enable jetifier and
394+
/// Android X is by updating the gradle properties template.
395+
/// </summary>
396+
/// <returns>true if successful, false otherwise.</returns>
397+
public static bool InjectProperties(){
398+
var resolutionMeasurementParameters =
399+
PlayServicesResolver.GetResolutionMeasurementParameters(null);
400+
PlayServicesResolver.analytics.Report(
401+
"/resolve/gradleproperties", resolutionMeasurementParameters,
402+
"Gradle Properties Resolve");
403+
var propertiesLines = new List<string>();
404+
// Lines to add Custom Gradle properties template to enable
405+
// jetifier and androidx
406+
propertiesLines.AddRange(new [] {
407+
"android.useAndroidX=true",
408+
"android.enableJetifier=true",
409+
});
410+
var propertiesFileDescription = String.Format(
411+
"gradle properties template" + GradlePropertiesTemplatePath);
412+
TextFileLineInjector[] propertiesInjectors = new [] {
413+
new TextFileLineInjector(PropertiesInjectionLine,
414+
PropertiesStartLine, PropertiesEndLine,
415+
propertiesLines,
416+
"Properties",
417+
propertiesFileDescription)
418+
};
419+
if (!PatchFile(GradlePropertiesTemplatePath, propertiesFileDescription,
420+
"Gradle Properties", "gradleproperties",
421+
propertiesInjectors,
422+
resolutionMeasurementParameters)) {
423+
PlayServicesResolver.Log(
424+
String.Format("Unable to patch " + propertiesFileDescription),
425+
level: LogLevel.Error);
426+
return false;
427+
}
428+
return true;
429+
}
430+
264431
/// <summary>
265432
/// Inject / update dependencies in the gradle template file.
266433
/// </summary>
@@ -328,14 +495,18 @@ public static bool InjectDependencies(ICollection<Dependency> dependencies) {
328495
var repoLines = new List<string>();
329496
// Optionally enable the jetifier.
330497
if (SettingsDialog.UseJetifier && dependencies.Count > 0) {
331-
repoLines.AddRange(new [] {
332-
"([rootProject] + (rootProject.subprojects as List)).each {",
333-
" ext {",
334-
" it.setProperty(\"android.useAndroidX\", true)",
335-
" it.setProperty(\"android.enableJetifier\", true)",
336-
" }",
337-
"}"
338-
});
498+
// For Unity versions lower than 2019.3 add jetifier and AndroidX
499+
// properties to custom main gradle template
500+
if (VersionHandler.GetUnityVersionMajorMinor() < 2019.3f) {
501+
repoLines.AddRange(new [] {
502+
"([rootProject] + (rootProject.subprojects as List)).each {",
503+
" ext {",
504+
" it.setProperty(\"android.useAndroidX\", true)",
505+
" it.setProperty(\"android.enableJetifier\", true)",
506+
" }",
507+
"}"
508+
});
509+
}
339510
}
340511
repoLines.AddRange(PlayServicesResolver.GradleMavenReposLines(dependencies));
341512

@@ -352,51 +523,9 @@ public static bool InjectDependencies(ICollection<Dependency> dependencies) {
352523
PlayServicesResolver.PackagingOptionsLines(dependencies),
353524
"Packaging Options", fileDescription),
354525
};
355-
// Lines that will be written to the output file.
356-
var outputLines = new List<string>();
357-
foreach (var line in lines) {
358-
var currentOutputLines = new List<string>();
359-
foreach (var injector in injectors) {
360-
bool injectionApplied = false;
361-
currentOutputLines = injector.ProcessLine(line, out injectionApplied);
362-
if (injectionApplied || currentOutputLines.Count == 0) break;
363-
}
364-
outputLines.AddRange(currentOutputLines);
365-
}
366-
var inputText = String.Join("\n", (new List<string>(lines)).ToArray()) + "\n";
367-
var outputText = String.Join("\n", outputLines.ToArray()) + "\n";
368-
if (inputText == outputText) {
369-
PlayServicesResolver.Log(String.Format("No changes to {0}", fileDescription),
370-
level: LogLevel.Verbose);
371-
return true;
372-
}
373-
if (!FileUtils.CheckoutFile(GradleTemplatePath, PlayServicesResolver.logger)) {
374-
PlayServicesResolver.Log(
375-
String.Format("Failed to checkout '{0}', unable to patch the file.",
376-
GradleTemplatePath), level: LogLevel.Error);
377-
PlayServicesResolver.analytics.Report(
378-
"/resolve/gradletemplate/failed/checkout",
379-
"Gradle Template Resolve: Failed to checkout");
380-
return false;
381-
}
382-
PlayServicesResolver.Log(
383-
String.Format("Writing updated {0}", fileDescription),
384-
level: LogLevel.Verbose);
385-
try {
386-
File.WriteAllText(GradleTemplatePath, outputText);
387-
PlayServicesResolver.analytics.Report(
388-
"/resolve/gradletemplate/success", resolutionMeasurementParameters,
389-
"Gradle Template Resolve Success");
390-
} catch (Exception ex) {
391-
PlayServicesResolver.analytics.Report(
392-
"/resolve/gradletemplate/failed/write",
393-
"Gradle Template Resolve: Failed to write");
394-
PlayServicesResolver.Log(
395-
String.Format("Unable to patch {0} ({1})", fileDescription,
396-
ex.ToString()), level: LogLevel.Error);
397-
return false;
398-
}
399-
return true;
526+
return PatchFile(GradleTemplatePath, fileDescription,
527+
"Gradle Template", "gradletemplate",
528+
injectors, resolutionMeasurementParameters);
400529
}
401530
}
402531
}

0 commit comments

Comments
 (0)