Skip to content

Commit a11abad

Browse files
authored
[Messaging] Include theme in AndroidManifest (#1267)
* [Messaging] Include theme in AndroidManifest * Update FirebaseMessagingActivityGenerator.cs
1 parent 91a76dc commit a11abad

File tree

3 files changed

+147
-71
lines changed

3 files changed

+147
-71
lines changed

docs/readme.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ Release Notes
8181
- Messaging (Android): Fix issue with `SubscribeAsync` task not completing when
8282
a cached token is available.
8383
([#1245](https://github.com/firebase/firebase-unity-sdk/issues/1245)).
84+
- Messaging (Android): Fix issue with missing theme causing a crash on Unity 6.
85+
([#1229](https://github.com/firebase/firebase-unity-sdk/issues/1229))
8486

8587
### 12.9.0
8688
- Changes

messaging/activity/AndroidManifest.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
UnityPlayerActivity to work around a known issue when receiving
1111
notification data payloads in the background. -->
1212
<activity android:name="com.google.firebase.MessagingUnityPlayerActivity"
13+
android:theme="@style/UnityThemeSelector"
1314
android:configChanges="fontScale|keyboard|keyboardHidden|locale|mnc|mcc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|touchscreen">
1415
<intent-filter>
1516
<action android:name="android.intent.action.MAIN" />

messaging/activity/FirebaseMessagingActivityGenerator.cs

Lines changed: 144 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -15,25 +15,26 @@
1515
*/
1616
using System.IO;
1717
using System.Linq;
18+
using System.Xml;
1819
using UnityEngine;
1920
using UnityEditor;
2021
using UnityEditor.Build;
2122
using UnityEditor.Build.Reporting;
2223

2324
namespace Firebase.Messaging.Editor {
2425

25-
// Handles the generation of the MessagingUnityPlayerActivity java file.
26-
// Note this regenerates the file every time an Android build occurs,
27-
// but local changes can be preserved by using the PreserveTag below.
28-
// This is needed because the source code needs to be present to work across
29-
// different Unity versions, due to changes in mUnityPlayer.
30-
// It also adjusts the base class of the file based on if GameActivity is being
31-
// used (a new feature in Unity 2023).
32-
public class FirebaseMessagingActivityGenerator : IPreprocessBuildWithReport {
33-
// TODO: Ideally this should use a template file, the tricky part is locating
34-
// the template file when it is either in the Assets path, or the Packages path.
35-
// There are some similar cases in EDM4U, so a solution might be to use that.
36-
private readonly string[] ActivityClassContents = new string[]{
26+
// Handles the generation of the MessagingUnityPlayerActivity java file.
27+
// Note this regenerates the file every time an Android build occurs,
28+
// but local changes can be preserved by using the PreserveTag below.
29+
// This is needed because the source code needs to be present to work across
30+
// different Unity versions, due to changes in mUnityPlayer.
31+
// It also adjusts the base class of the file based on if GameActivity is being
32+
// used (a new feature in Unity 2023).
33+
public class FirebaseMessagingActivityGenerator : IPreprocessBuildWithReport {
34+
// TODO: Ideally this should use a template file, the tricky part is locating
35+
// the template file when it is either in the Assets path, or the Packages path.
36+
// There are some similar cases in EDM4U, so a solution might be to use that.
37+
private readonly string[] ActivityClassContents = new string[]{
3738
"/*",
3839
" * This file is generated by the FirebaseMessagingActivityGenerator script.",
3940
" * Refer to that script for more information.",
@@ -116,83 +117,155 @@ public class FirebaseMessagingActivityGenerator : IPreprocessBuildWithReport {
116117
" super.onCreate(savedInstanceState);",
117118
" }}",
118119
"}}"
119-
};
120-
private readonly string BaseActivityClass = "UnityPlayerActivity";
120+
};
121+
private readonly string BaseActivityClass = "UnityPlayerActivity";
121122
#if UNITY_2023_1_OR_NEWER
122-
private readonly string BaseGameActivityClass = "UnityPlayerGameActivity";
123+
private readonly string BaseGameActivityClass = "UnityPlayerGameActivity";
123124
#endif
124125

125126
#if UNITY_2023_1_OR_NEWER
126-
private readonly string UnityPlayerQuitFunction = "destroy";
127+
private readonly string UnityPlayerQuitFunction = "destroy";
127128
#else
128-
private readonly string UnityPlayerQuitFunction = "quit";
129+
private readonly string UnityPlayerQuitFunction = "quit";
129130
#endif
130131

131-
private readonly string GeneratedFileTag = "FirebaseMessagingActivityGenerated";
132-
// If this tag is present on the generated file, it will not be replaced.
133-
private readonly string PreserveTag = "FirebasePreserve";
132+
private readonly string GeneratedFileTag = "FirebaseMessagingActivityGenerated";
133+
// If this tag is present on the generated file, it will not be replaced.
134+
private readonly string PreserveTag = "FirebasePreserve";
134135

135-
private readonly string OutputPath = Path.Combine("Plugins", "Android");
136-
private readonly string OutputFilename = "MessagingUnityPlayerActivity.java";
136+
private readonly string OutputPath = Path.Combine("Plugins", "Android");
137+
private readonly string OutputFilename = "MessagingUnityPlayerActivity.java";
137138

138-
public int callbackOrder { get { return 0; } }
139-
public void OnPreprocessBuild(BuildReport report) {
140-
// Only run this logic when building for Android.
141-
if (EditorUserBuildSettings.activeBuildTarget != BuildTarget.Android) {
142-
return;
143-
}
139+
public int callbackOrder { get { return 0; } }
140+
public void OnPreprocessBuild(BuildReport report) {
141+
// Only run this logic when building for Android.
142+
if (EditorUserBuildSettings.activeBuildTarget != BuildTarget.Android) {
143+
return;
144+
}
144145

145-
// Determine what the contents of the generated file should be.
146-
string baseClass = BaseActivityClass;
146+
// Determine what the contents of the generated file should be.
147+
string baseClass = BaseActivityClass;
147148
#if UNITY_2023_1_OR_NEWER
148-
// If using the new GameActivity logic, we want to generate with that base class.
149-
if (PlayerSettings.Android.applicationEntry.HasFlag(AndroidApplicationEntry.GameActivity)) {
150-
baseClass = BaseGameActivityClass;
149+
// If using the new GameActivity logic, we want to generate with that base class.
150+
if (PlayerSettings.Android.applicationEntry.HasFlag(AndroidApplicationEntry.GameActivity)) {
151+
baseClass = BaseGameActivityClass;
152+
}
153+
#endif
154+
string fileContents = System.String.Format(System.String.Join("\n", ActivityClassContents),
155+
baseClass, UnityPlayerQuitFunction);
156+
157+
// Check if the file has already been generated.
158+
string[] oldAssetGuids = AssetDatabase.FindAssets("l:" + GeneratedFileTag);
159+
if (oldAssetGuids != null && oldAssetGuids.Length > 0) {
160+
if (oldAssetGuids.Length != 1) {
161+
Debug.LogWarning("FirebaseMessagingActivityEditor found multiple generated files with the label: " +
162+
GeneratedFileTag + " \n" +
163+
"No changes will be made, but this can potentially cause problems on Android with duplicate classes.\n" +
164+
"Please check for duplicate classes, and remove any unnecessary uses of the label.");
165+
return;
166+
}
167+
string oldAssetPath = AssetDatabase.GUIDToAssetPath(oldAssetGuids[0]);
168+
Object oldAsset = AssetDatabase.LoadMainAssetAtPath(oldAssetPath);
169+
if (oldAsset != null) {
170+
string oldAssetFullPath = Path.Combine(Application.dataPath, "..", oldAssetPath);
171+
string oldFileContents = System.IO.File.ReadAllText(oldAssetFullPath);
172+
// If the old file matches what we would generate, exit early.
173+
if (oldFileContents == fileContents) {
174+
return;
175+
}
176+
// If the generated file has been tagged to be preserved, don't change it.
177+
string[] labelList = AssetDatabase.GetLabels(oldAsset);
178+
if (labelList.Contains(PreserveTag)) {
179+
return;
180+
}
181+
// Delete the old asset.
182+
Debug.Log("Changes detected, regenerating " + oldAssetPath + "\n" +
183+
"To preserve local changes to that file, add the label: " + PreserveTag);
184+
AssetDatabase.DeleteAsset(oldAssetPath);
185+
}
186+
}
187+
188+
// Generate the new file.
189+
string newAssetFullDirectory = Path.Combine(Application.dataPath, OutputPath);
190+
System.IO.Directory.CreateDirectory(newAssetFullDirectory);
191+
System.IO.File.WriteAllText(Path.Combine(newAssetFullDirectory, OutputFilename), fileContents);
192+
string newAssetLocalPath = Path.Combine("Assets", OutputPath, OutputFilename);
193+
AssetDatabase.ImportAsset(newAssetLocalPath);
194+
Object newAsset = AssetDatabase.LoadMainAssetAtPath(newAssetLocalPath);
195+
AssetDatabase.SetLabels(newAsset, new[]{GeneratedFileTag});
151196
}
197+
}
198+
199+
// Handles adding the android:theme to the AndroidManifest file if it isn't present,
200+
// and swaps it on Unity versions that support GameActivity, which uses a different theme
201+
// by default.
202+
public class FirebaseMessagingAndroidManifestModifier : IPreprocessBuildWithReport {
203+
// Hard coded directories and file names.
204+
private static readonly string ANDROID_MANIFEST_DIRECTORY =
205+
Path.Combine(Path.Combine("Assets", "Plugins"), "Android");
206+
private static readonly string ANDROID_MANIFEST_FILE = "AndroidManifest.xml";
207+
private static readonly string ANDROID_MANIFEST_PATH =
208+
Path.Combine(ANDROID_MANIFEST_DIRECTORY, ANDROID_MANIFEST_FILE);
209+
210+
private static readonly string MESSAGING_ACTIVITY = "com.google.firebase.MessagingUnityPlayerActivity";
211+
212+
private static readonly string DEFAULT_THEME = "@style/UnityThemeSelector";
213+
#if UNITY_2023_1_OR_NEWER
214+
private static readonly string DEFAULT_GAME_THEME = "@style/BaseUnityGameActivityTheme";
152215
#endif
153-
string fileContents = System.String.Format(System.String.Join("\n", ActivityClassContents),
154-
baseClass, UnityPlayerQuitFunction);
155-
156-
// Check if the file has already been generated.
157-
string[] oldAssetGuids = AssetDatabase.FindAssets("l:" + GeneratedFileTag);
158-
if (oldAssetGuids != null && oldAssetGuids.Length > 0) {
159-
if (oldAssetGuids.Length != 1) {
160-
Debug.LogWarning("FirebaseMessagingActivityEditor found multiple generated files with the label: " +
161-
GeneratedFileTag + " \n" +
162-
"No changes will be made, but this can potentially cause problems on Android with duplicate classes.\n" +
163-
"Please check for duplicate classes, and remove any unnecessary uses of the label.");
216+
217+
public int callbackOrder { get { return 0; } }
218+
public void OnPreprocessBuild(BuildReport report) {
219+
string projectDir = Path.Combine(Application.dataPath, "..");
220+
string manifestFile = Path.Combine(projectDir, ANDROID_MANIFEST_PATH);
221+
222+
if (!File.Exists(manifestFile)) {
223+
// Unable to locate the file, so exit early.
224+
return;
225+
}
226+
227+
var manifest = new XmlDocument();
228+
manifest.Load(manifestFile);
229+
230+
CheckTheme(manifest);
231+
232+
manifest.Save(manifestFile);
233+
}
234+
235+
private void CheckTheme(XmlDocument manifest) {
236+
// Create a NamespaceManager with the 'android' namespace.
237+
var nsManager = new XmlNamespaceManager(manifest.NameTable);
238+
var androidNamespace = manifest.DocumentElement.GetNamespaceOfPrefix("android");
239+
if (string.IsNullOrEmpty(androidNamespace)) {
240+
// If unable to find the android namespace, just exit out early, since we depend on it
164241
return;
165242
}
166-
string oldAssetPath = AssetDatabase.GUIDToAssetPath(oldAssetGuids[0]);
167-
Object oldAsset = AssetDatabase.LoadMainAssetAtPath(oldAssetPath);
168-
if (oldAsset != null) {
169-
string oldAssetFullPath = Path.Combine(Application.dataPath, "..", oldAssetPath);
170-
string oldFileContents = System.IO.File.ReadAllText(oldAssetFullPath);
171-
// If the old file matches what we would generate, exit early.
172-
if (oldFileContents == fileContents) {
173-
return;
243+
nsManager.AddNamespace("android", androidNamespace);
244+
245+
// Find the activity node
246+
XmlNode activityNode = manifest.SelectSingleNode(
247+
$"//application/activity[@android:name='{MESSAGING_ACTIVITY}']",
248+
nsManager);
249+
250+
if (activityNode != null && activityNode is XmlElement activityElement) {
251+
string existingTheme = activityElement.GetAttribute("theme", androidNamespace);
252+
253+
string defaultTheme = DEFAULT_THEME;
254+
bool setTheme = string.IsNullOrEmpty(existingTheme);
255+
#if UNITY_2023_1_OR_NEWER
256+
string otherTheme = DEFAULT_GAME_THEME;
257+
// If using the new GameActivity logic, we want to use the default game theme.
258+
if (PlayerSettings.Android.applicationEntry.HasFlag(AndroidApplicationEntry.GameActivity)) {
259+
defaultTheme = DEFAULT_GAME_THEME;
260+
otherTheme = DEFAULT_THEME;
174261
}
175-
// If the generated file has been tagged to be preserved, don't change it.
176-
string[] labelList = AssetDatabase.GetLabels(oldAsset);
177-
if (labelList.Contains(PreserveTag)) {
178-
return;
262+
// We also want to change the theme if is is using the other default.
263+
setTheme |= existingTheme == otherTheme;
264+
#endif
265+
if (setTheme) {
266+
activityElement.SetAttribute("theme", androidNamespace, defaultTheme);
179267
}
180-
// Delete the old asset.
181-
Debug.Log("Changes detected, regenerating " + oldAssetPath + "\n" +
182-
"To preserve local changes to that file, add the label: " + PreserveTag);
183-
AssetDatabase.DeleteAsset(oldAssetPath);
184268
}
185269
}
186-
187-
// Generate the new file.
188-
string newAssetFullDirectory = Path.Combine(Application.dataPath, OutputPath);
189-
System.IO.Directory.CreateDirectory(newAssetFullDirectory);
190-
System.IO.File.WriteAllText(Path.Combine(newAssetFullDirectory, OutputFilename), fileContents);
191-
string newAssetLocalPath = Path.Combine("Assets", OutputPath, OutputFilename);
192-
AssetDatabase.ImportAsset(newAssetLocalPath);
193-
Object newAsset = AssetDatabase.LoadMainAssetAtPath(newAssetLocalPath);
194-
AssetDatabase.SetLabels(newAsset, new[]{GeneratedFileTag});
195270
}
196-
}
197-
198271
} // namespace Firebase.Messaging.Editor

0 commit comments

Comments
 (0)