You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
199 lines
8.8 KiB
C#
199 lines
8.8 KiB
C#
4 weeks ago
|
/*
|
||
|
* Copyright 2023 Google LLC
|
||
|
*
|
||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
* you may not use this file except in compliance with the License.
|
||
|
* You may obtain a copy of the License at
|
||
|
*
|
||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
|
* Unless required by applicable law or agreed to in writing, software
|
||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
* See the License for the specific language governing permissions and
|
||
|
* limitations under the License.
|
||
|
*/
|
||
|
using System.IO;
|
||
|
using System.Linq;
|
||
|
using UnityEngine;
|
||
|
using UnityEditor;
|
||
|
using UnityEditor.Build;
|
||
|
using UnityEditor.Build.Reporting;
|
||
|
|
||
|
namespace Firebase.Messaging.Editor {
|
||
|
|
||
|
// Handles the generation of the MessagingUnityPlayerActivity java file.
|
||
|
// Note this regenerates the file every time an Android build occurs,
|
||
|
// but local changes can be preserved by using the PreserveTag below.
|
||
|
// This is needed because the source code needs to be present to work across
|
||
|
// different Unity versions, due to changes in mUnityPlayer.
|
||
|
// It also adjusts the base class of the file based on if GameActivity is being
|
||
|
// used (a new feature in Unity 2023).
|
||
|
public class FirebaseMessagingActivityGenerator : IPreprocessBuildWithReport {
|
||
|
// TODO: Ideally this should use a template file, the tricky part is locating
|
||
|
// the template file when it is either in the Assets path, or the Packages path.
|
||
|
// There are some similar cases in EDM4U, so a solution might be to use that.
|
||
|
private readonly string[] ActivityClassContents = new string[]{
|
||
|
"/*",
|
||
|
" * This file is generated by the FirebaseMessagingActivityGenerator script.",
|
||
|
" * Refer to that script for more information.",
|
||
|
" */",
|
||
|
"",
|
||
|
"package com.google.firebase;",
|
||
|
"",
|
||
|
"import android.content.Intent;",
|
||
|
"import android.os.Bundle;",
|
||
|
"import com.google.firebase.messaging.MessageForwardingService;",
|
||
|
"import com.unity3d.player.{0};",
|
||
|
"",
|
||
|
"/**",
|
||
|
" * MessagingUnityPlayerActivity is a {0} that updates its intent when new intents",
|
||
|
" * are sent to it.",
|
||
|
" *",
|
||
|
" * This is a workaround for a known issue that prevents Firebase Cloud Messaging from responding to",
|
||
|
" * data payloads when both a data and notification payload are sent to the app while it is in the",
|
||
|
" * background.",
|
||
|
" */",
|
||
|
"public class MessagingUnityPlayerActivity extends {0} {{",
|
||
|
" // The key in the intent's extras that maps to the incoming message's message ID. Only sent by",
|
||
|
" // the server, GmsCore sends EXTRA_MESSAGE_ID_KEY below. Server can't send that as it would get",
|
||
|
" // stripped by the client.",
|
||
|
" private static final String EXTRA_MESSAGE_ID_KEY_SERVER = \"message_id\";",
|
||
|
"",
|
||
|
" // An alternate key value in the intent's extras that also maps to the incoming message's message",
|
||
|
" // ID. Used by upstream, and set by GmsCore.",
|
||
|
" private static final String EXTRA_MESSAGE_ID_KEY = \"google.message_id\";",
|
||
|
"",
|
||
|
" // The key in the intent's extras that maps to the incoming message's sender value.",
|
||
|
" private static final String EXTRA_FROM = \"google.message_id\";",
|
||
|
"",
|
||
|
" /**",
|
||
|
" * Workaround for when a message is sent containing both a Data and Notification payload.",
|
||
|
" *",
|
||
|
" * <p>When the app is in the background, if a message with both a data and notification payload is",
|
||
|
" * received the data payload is stored on the Intent passed to onNewIntent. By default, that",
|
||
|
" * intent does not get set as the Intent that started the app, so when the app comes back online",
|
||
|
" * it doesn't see a new FCM message to respond to. As a workaround, we override onNewIntent so",
|
||
|
" * that it sends the intent to the MessageForwardingService which forwards the message to the",
|
||
|
" * FirebaseMessagingService which in turn sends the message to the application.",
|
||
|
" */",
|
||
|
" @Override",
|
||
|
" protected void onNewIntent(Intent intent) {{",
|
||
|
" super.onNewIntent(intent);",
|
||
|
"",
|
||
|
" // If we do not have a 'from' field this intent was not a message and should not be handled. It",
|
||
|
" // probably means this intent was fired by tapping on the app icon.",
|
||
|
" Bundle extras = intent.getExtras();",
|
||
|
" if (extras == null) {{",
|
||
|
" return;",
|
||
|
" }}",
|
||
|
" String from = extras.getString(EXTRA_FROM);",
|
||
|
" String messageId = extras.getString(EXTRA_MESSAGE_ID_KEY);",
|
||
|
" if (messageId == null) {{",
|
||
|
" messageId = extras.getString(EXTRA_MESSAGE_ID_KEY_SERVER);",
|
||
|
" }}",
|
||
|
" if (from != null && messageId != null) {{",
|
||
|
" Intent message = new Intent(this, MessageForwardingService.class);",
|
||
|
" message.setAction(MessageForwardingService.ACTION_REMOTE_INTENT);",
|
||
|
" message.putExtras(intent);",
|
||
|
" message.setData(intent.getData());",
|
||
|
" MessageForwardingService.enqueueWork(this, message);",
|
||
|
" }}",
|
||
|
" setIntent(intent);",
|
||
|
" }}",
|
||
|
"",
|
||
|
" /**",
|
||
|
" * Dispose of the mUnityPlayer when restarting the app.",
|
||
|
" *",
|
||
|
" * <p>This ensures that when the app starts up again it does not start with stale data.",
|
||
|
" */",
|
||
|
" @Override",
|
||
|
" protected void onCreate(Bundle savedInstanceState) {{",
|
||
|
" if (mUnityPlayer != null) {{",
|
||
|
" mUnityPlayer.{1}();",
|
||
|
" mUnityPlayer = null;",
|
||
|
" }}",
|
||
|
" super.onCreate(savedInstanceState);",
|
||
|
" }}",
|
||
|
"}}"
|
||
|
};
|
||
|
private readonly string BaseActivityClass = "UnityPlayerActivity";
|
||
|
#if UNITY_2023_1_OR_NEWER
|
||
|
private readonly string BaseGameActivityClass = "UnityPlayerGameActivity";
|
||
|
#endif
|
||
|
|
||
|
#if UNITY_2023_1_OR_NEWER
|
||
|
private readonly string UnityPlayerQuitFunction = "destroy";
|
||
|
#else
|
||
|
private readonly string UnityPlayerQuitFunction = "quit";
|
||
|
#endif
|
||
|
|
||
|
private readonly string GeneratedFileTag = "FirebaseMessagingActivityGenerated";
|
||
|
// If this tag is present on the generated file, it will not be replaced.
|
||
|
private readonly string PreserveTag = "FirebasePreserve";
|
||
|
|
||
|
private readonly string OutputPath = Path.Combine("Plugins", "Android");
|
||
|
private readonly string OutputFilename = "MessagingUnityPlayerActivity.java";
|
||
|
|
||
|
public int callbackOrder { get { return 0; } }
|
||
|
public void OnPreprocessBuild(BuildReport report) {
|
||
|
// Only run this logic when building for Android.
|
||
|
if (EditorUserBuildSettings.activeBuildTarget != BuildTarget.Android) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Determine what the contents of the generated file should be.
|
||
|
string baseClass = BaseActivityClass;
|
||
|
#if UNITY_2023_1_OR_NEWER
|
||
|
// If using the new GameActivity logic, we want to generate with that base class.
|
||
|
if (PlayerSettings.Android.applicationEntry.HasFlag(AndroidApplicationEntry.GameActivity)) {
|
||
|
baseClass = BaseGameActivityClass;
|
||
|
}
|
||
|
#endif
|
||
|
string fileContents = System.String.Format(System.String.Join("\n", ActivityClassContents),
|
||
|
baseClass, UnityPlayerQuitFunction);
|
||
|
|
||
|
// Check if the file has already been generated.
|
||
|
string[] oldAssetGuids = AssetDatabase.FindAssets("l:" + GeneratedFileTag);
|
||
|
if (oldAssetGuids != null && oldAssetGuids.Length > 0) {
|
||
|
if (oldAssetGuids.Length != 1) {
|
||
|
Debug.LogWarning("FirebaseMessagingActivityEditor found multiple generated files with the label: " +
|
||
|
GeneratedFileTag + " \n" +
|
||
|
"No changes will be made, but this can potentially cause problems on Android with duplicate classes.\n" +
|
||
|
"Please check for duplicate classes, and remove any unnecessary uses of the label.");
|
||
|
return;
|
||
|
}
|
||
|
string oldAssetPath = AssetDatabase.GUIDToAssetPath(oldAssetGuids[0]);
|
||
|
Object oldAsset = AssetDatabase.LoadMainAssetAtPath(oldAssetPath);
|
||
|
if (oldAsset != null) {
|
||
|
string oldAssetFullPath = Path.Combine(Application.dataPath, "..", oldAssetPath);
|
||
|
string oldFileContents = System.IO.File.ReadAllText(oldAssetFullPath);
|
||
|
// If the old file matches what we would generate, exit early.
|
||
|
if (oldFileContents == fileContents) {
|
||
|
return;
|
||
|
}
|
||
|
// If the generated file has been tagged to be preserved, don't change it.
|
||
|
string[] labelList = AssetDatabase.GetLabels(oldAsset);
|
||
|
if (labelList.Contains(PreserveTag)) {
|
||
|
return;
|
||
|
}
|
||
|
// Delete the old asset.
|
||
|
Debug.Log("Changes detected, regenerating " + oldAssetPath + "\n" +
|
||
|
"To preserve local changes to that file, add the label: " + PreserveTag);
|
||
|
AssetDatabase.DeleteAsset(oldAssetPath);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Generate the new file.
|
||
|
string newAssetFullDirectory = Path.Combine(Application.dataPath, OutputPath);
|
||
|
System.IO.Directory.CreateDirectory(newAssetFullDirectory);
|
||
|
System.IO.File.WriteAllText(Path.Combine(newAssetFullDirectory, OutputFilename), fileContents);
|
||
|
string newAssetLocalPath = Path.Combine("Assets", OutputPath, OutputFilename);
|
||
|
AssetDatabase.ImportAsset(newAssetLocalPath);
|
||
|
Object newAsset = AssetDatabase.LoadMainAssetAtPath(newAssetLocalPath);
|
||
|
AssetDatabase.SetLabels(newAsset, new[]{GeneratedFileTag});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} // namespace Firebase.Messaging.Editor
|