push notifications

This commit is contained in:
2026-11-03 20:26:24 +05:30
parent a1b2b41912
commit 3ee348c09f
221 changed files with 9440 additions and 4899 deletions
@@ -0,0 +1,23 @@
<!-- Copyright (C) 2019 Google Inc. All Rights Reserved.
FirebaseApp iOS and Android Dependencies.
-->
<dependencies>
<remoteSwiftPackage url="https://github.com/firebase/firebase-ios-sdk.git" version="13.1.1">
<swiftPackage name="FirebaseCore"/>
</remoteSwiftPackage>
<androidPackages>
<androidPackage spec="com.google.firebase:firebase-common:23.0.0">
</androidPackage>
<androidPackage spec="com.google.firebase:firebase-analytics:24.0.0">
</androidPackage>
<androidPackage spec="com.google.android.gms:play-services-base:18.11.1">
</androidPackage>
<androidPackage spec="com.google.firebase:firebase-app-unity:14.1.0">
<repositories>
<repository>Assets/Firebase/m2repository</repository>
</repositories>
</androidPackage>
</androidPackages>
</dependencies>
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9b63af95d9364af4a3d8ce58738b6223
labels:
- gvh
- gvh_version-14.1.0
- gvhp_exportpath-Firebase/Editor/AppDependencies.xml
timeCreated: 1480838400
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
Binary file not shown.
@@ -0,0 +1,67 @@
fileFormatVersion: 2
guid: 9f2edbf81053418f879076c05f816dc2
labels:
- gvh
- gvh_targets-editor
- gvh_version-14.1.0
- gvhp_exportpath-Firebase/Editor/Firebase.Editor.dll
PluginImporter:
externalObjects: {}
serializedVersion: 3
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 0
platformData:
Android:
enabled: 0
settings:
CPU: AnyCPU
Any:
enabled: 0
settings: {}
Editor:
enabled: 1
settings:
CPU: AnyCPU
DefaultValueInitialized: true
OS: AnyOS
Linux64:
enabled: 0
settings:
CPU: None
OSXUniversal:
enabled: 0
settings:
CPU: None
WebGL:
enabled: 0
settings: {}
Win:
enabled: 0
settings:
CPU: None
Win64:
enabled: 0
settings:
CPU: None
WindowsStoreApps:
enabled: 0
settings:
CPU: AnyCPU
iOS:
enabled: 0
settings:
CompileFlags:
FrameworkDependencies:
tvOS:
enabled: 0
settings:
CompileFlags:
FrameworkDependencies:
userData:
assetBundleName:
assetBundleVariant:
Binary file not shown.
@@ -0,0 +1,67 @@
fileFormatVersion: 2
guid: 9555198c219241e0b850872f8732192f
labels:
- gvh
- gvh_targets-editor
- gvh_version-14.1.0
- gvhp_exportpath-Firebase/Editor/Firebase.Messaging.Editor.dll
PluginImporter:
externalObjects: {}
serializedVersion: 3
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
Android:
enabled: 0
settings:
CPU: AnyCPU
Any:
enabled: 0
settings: {}
Editor:
enabled: 1
settings:
CPU: AnyCPU
DefaultValueInitialized: true
OS: AnyOS
Linux64:
enabled: 0
settings:
CPU: None
OSXUniversal:
enabled: 0
settings:
CPU: None
WebGL:
enabled: 0
settings: {}
Win:
enabled: 0
settings:
CPU: None
Win64:
enabled: 0
settings:
CPU: None
WindowsStoreApps:
enabled: 0
settings:
CPU: AnyCPU
iOS:
enabled: 0
settings:
CompileFlags:
FrameworkDependencies:
tvOS:
enabled: 0
settings:
CompileFlags:
FrameworkDependencies:
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,77 @@
Assets/Editor Default Resources/Firebase/fb_analytics.png
Assets/Editor Default Resources/Firebase/fb_analytics_dark.png
Assets/Editor Default Resources/Firebase/fb_auth.png
Assets/Editor Default Resources/Firebase/fb_auth_dark.png
Assets/Editor Default Resources/Firebase/fb_cloud_messaging.png
Assets/Editor Default Resources/Firebase/fb_cloud_messaging_dark.png
Assets/Editor Default Resources/Firebase/fb_config.png
Assets/Editor Default Resources/Firebase/fb_config_dark.png
Assets/Editor Default Resources/Firebase/fb_crashlytics.png
Assets/Editor Default Resources/Firebase/fb_crashlytics_dark.png
Assets/Editor Default Resources/Firebase/fb_database.png
Assets/Editor Default Resources/Firebase/fb_database_dark.png
Assets/Editor Default Resources/Firebase/fb_functions.png
Assets/Editor Default Resources/Firebase/fb_functions_dark.png
Assets/Editor Default Resources/Firebase/fb_storage.png
Assets/Editor Default Resources/Firebase/fb_storage_dark.png
Assets/Editor Default Resources/Firebase/firebase_lockup.png
Assets/Editor Default Resources/Firebase/firebase_lockup_dark.png
Assets/ExternalDependencyManager/Editor/1.2.190/Google.IOSResolver.dll
Assets/ExternalDependencyManager/Editor/1.2.190/Google.IOSResolver.pdb
Assets/ExternalDependencyManager/Editor/1.2.190/Google.JarResolver.dll
Assets/ExternalDependencyManager/Editor/1.2.190/Google.JarResolver.pdb
Assets/ExternalDependencyManager/Editor/1.2.190/Google.PackageManagerResolver.dll
Assets/ExternalDependencyManager/Editor/1.2.190/Google.PackageManagerResolver.pdb
Assets/ExternalDependencyManager/Editor/1.2.190/Google.VersionHandlerImpl.dll
Assets/ExternalDependencyManager/Editor/1.2.190/Google.VersionHandlerImpl.pdb
Assets/ExternalDependencyManager/Editor/CHANGELOG.md
Assets/ExternalDependencyManager/Editor/Google.VersionHandler.dll
Assets/ExternalDependencyManager/Editor/Google.VersionHandler.pdb
Assets/ExternalDependencyManager/Editor/LICENSE
Assets/ExternalDependencyManager/Editor/README.md
Assets/ExternalDependencyManager/Editor/external-dependency-manager_version-1.2.190_manifest.txt
Assets/Firebase/Editor/AppDependencies.xml
Assets/Firebase/Editor/Firebase.Editor.dll
Assets/Firebase/Editor/Firebase.Editor.pdb
Assets/Firebase/Editor/Firebase.Messaging.Editor.dll
Assets/Firebase/Editor/Firebase.Messaging.Editor.pdb
Assets/Firebase/Editor/Messaging/FirebaseMessagingActivityGenerator.asmdef
Assets/Firebase/Editor/Messaging/FirebaseMessagingActivityGenerator.cs
Assets/Firebase/Editor/MessagingDependencies.xml
Assets/Firebase/Editor/generate_xml_from_google_services_json.exe
Assets/Firebase/Editor/generate_xml_from_google_services_json.py
Assets/Firebase/FirebaseApp/Internal/AssemblyInfo.cs
Assets/Firebase/FirebaseApp/Internal/Firebase.App.Internal.asmdef
Assets/Firebase/FirebaseApp/Internal/FirebaseInterops.cs
Assets/Firebase/FirebaseApp/Internal/HttpHelpers.cs
Assets/Firebase/FirebaseApp/Internal/link.xml
Assets/Firebase/Plugins/Android/firebase-messaging-cpp.aar
Assets/Firebase/Plugins/Firebase.App.dll
Assets/Firebase/Plugins/Firebase.App.pdb
Assets/Firebase/Plugins/Firebase.Messaging.dll
Assets/Firebase/Plugins/Firebase.Messaging.pdb
Assets/Firebase/Plugins/Firebase.Platform.dll
Assets/Firebase/Plugins/Firebase.Platform.pdb
Assets/Firebase/Plugins/Firebase.TaskExtension.dll
Assets/Firebase/Plugins/Firebase.TaskExtension.pdb
Assets/Firebase/Plugins/Google.MiniJson.dll
Assets/Firebase/Plugins/iOS/Firebase.App.dll
Assets/Firebase/Plugins/iOS/Firebase.App.pdb
Assets/Firebase/Plugins/iOS/Firebase.Messaging.dll
Assets/Firebase/Plugins/iOS/Firebase.Messaging.pdb
Assets/Firebase/Plugins/x86_64/FirebaseCppApp-14_1_0.bundle
Assets/Firebase/Plugins/x86_64/FirebaseCppApp-14_1_0.dll
Assets/Firebase/Plugins/x86_64/FirebaseCppApp-14_1_0.so
Assets/Firebase/Plugins/x86_64/FirebaseCppMessaging.bundle
Assets/Firebase/Plugins/x86_64/FirebaseCppMessaging.dll
Assets/Firebase/Plugins/x86_64/FirebaseCppMessaging.so
Assets/Firebase/m2repository/com/google/firebase/firebase-app-unity/14.1.0/firebase-app-unity-14.1.0.pom
Assets/Firebase/m2repository/com/google/firebase/firebase-app-unity/14.1.0/firebase-app-unity-14.1.0.srcaar
Assets/Firebase/m2repository/com/google/firebase/firebase-app-unity/maven-metadata.xml
Assets/Firebase/m2repository/com/google/firebase/firebase-messaging-unity/14.1.0/firebase-messaging-unity-14.1.0.pom
Assets/Firebase/m2repository/com/google/firebase/firebase-messaging-unity/14.1.0/firebase-messaging-unity-14.1.0.srcaar
Assets/Firebase/m2repository/com/google/firebase/firebase-messaging-unity/maven-metadata.xml
Assets/Plugins/iOS/Firebase/libFirebaseCppApp.xcframework
Assets/Plugins/iOS/Firebase/libFirebaseCppMessaging.xcframework
Assets/Plugins/tvOS/Firebase/libFirebaseCppApp.xcframework
Assets/Plugins/tvOS/Firebase/libFirebaseCppMessaging.xcframework
@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 39457a35127e4968b10e66b8b183ebda
labels:
- gvh
- gvh_manifest
- gvh_version-14.1.0
- gvhp_exportpath-Firebase/Editor/FirebaseMessaging_version-14.1.0_manifest.txt
- gvhp_manifestname-0Firebase Cloud Messaging
- gvhp_manifestname-1FirebaseMessaging
timeCreated: 0
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: caa4413cd6e0e91849352e01a496c42c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,16 @@
{
"name": "FirebaseMessagingActivityGenerator",
"rootNamespace": "",
"references": [],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 23dad9d59a474cb6a052bb9322271ae3
labels:
- gvh
- gvh_version-14.1.0
- gvhp_exportpath-Firebase/Editor/Messaging/FirebaseMessagingActivityGenerator.asmdef
timeCreated: 1480838400
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,271 @@
/*
* 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 System.Xml;
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});
}
}
// Handles adding the android:theme to the AndroidManifest file if it isn't present,
// and swaps it on Unity versions that support GameActivity, which uses a different theme
// by default.
public class FirebaseMessagingAndroidManifestModifier : IPreprocessBuildWithReport {
// Hard coded directories and file names.
private static readonly string ANDROID_MANIFEST_DIRECTORY =
Path.Combine(Path.Combine("Assets", "Plugins"), "Android");
private static readonly string ANDROID_MANIFEST_FILE = "AndroidManifest.xml";
private static readonly string ANDROID_MANIFEST_PATH =
Path.Combine(ANDROID_MANIFEST_DIRECTORY, ANDROID_MANIFEST_FILE);
private static readonly string MESSAGING_ACTIVITY = "com.google.firebase.MessagingUnityPlayerActivity";
private static readonly string DEFAULT_THEME = "@style/UnityThemeSelector";
#if UNITY_2023_1_OR_NEWER
private static readonly string DEFAULT_GAME_THEME = "@style/BaseUnityGameActivityTheme";
#endif
public int callbackOrder { get { return 0; } }
public void OnPreprocessBuild(BuildReport report) {
string projectDir = Path.Combine(Application.dataPath, "..");
string manifestFile = Path.Combine(projectDir, ANDROID_MANIFEST_PATH);
if (!File.Exists(manifestFile)) {
// Unable to locate the file, so exit early.
return;
}
var manifest = new XmlDocument();
manifest.Load(manifestFile);
CheckTheme(manifest);
manifest.Save(manifestFile);
}
private void CheckTheme(XmlDocument manifest) {
// Create a NamespaceManager with the 'android' namespace.
var nsManager = new XmlNamespaceManager(manifest.NameTable);
var androidNamespace = manifest.DocumentElement.GetNamespaceOfPrefix("android");
if (string.IsNullOrEmpty(androidNamespace)) {
// If unable to find the android namespace, just exit out early, since we depend on it
return;
}
nsManager.AddNamespace("android", androidNamespace);
// Find the activity node
XmlNode activityNode = manifest.SelectSingleNode(
$"//application/activity[@android:name='{MESSAGING_ACTIVITY}']",
nsManager);
if (activityNode != null && activityNode is XmlElement activityElement) {
string existingTheme = activityElement.GetAttribute("theme", androidNamespace);
string defaultTheme = DEFAULT_THEME;
bool setTheme = string.IsNullOrEmpty(existingTheme);
#if UNITY_2023_1_OR_NEWER
string otherTheme = DEFAULT_GAME_THEME;
// If using the new GameActivity logic, we want to use the default game theme.
if (PlayerSettings.Android.applicationEntry.HasFlag(AndroidApplicationEntry.GameActivity)) {
defaultTheme = DEFAULT_GAME_THEME;
otherTheme = DEFAULT_THEME;
}
// We also want to change the theme if is is using the other default.
setTheme |= existingTheme == otherTheme;
#endif
if (setTheme) {
activityElement.SetAttribute("theme", androidNamespace, defaultTheme);
}
}
}
}
} // namespace Firebase.Messaging.Editor
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 38cf8316372e4943aee735c624262e05
labels:
- gvh
- gvh_version-14.1.0
- gvhp_exportpath-Firebase/Editor/Messaging/FirebaseMessagingActivityGenerator.cs
timeCreated: 1480838400
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,25 @@
<!-- Copyright (C) 2019 Google Inc. All Rights Reserved.
FirebaseMessaging iOS and Android Dependencies.
-->
<dependencies>
<remoteSwiftPackage url="https://github.com/firebase/firebase-ios-sdk.git" version="13.1.1">
<swiftPackage name="FirebaseMessaging"/>
</remoteSwiftPackage>
<androidPackages>
<androidPackage spec="com.google.firebase:firebase-messaging:26.0.1">
</androidPackage>
<androidPackage spec="com.google.firebase:firebase-analytics:24.0.0">
</androidPackage>
<androidPackage spec="com.google.firebase:firebase-iid:21.1.0">
</androidPackage>
<androidPackage spec="com.google.flatbuffers:flatbuffers-java:1.12.0">
</androidPackage>
<androidPackage spec="com.google.firebase:firebase-messaging-unity:14.1.0">
<repositories>
<repository>Assets/Firebase/m2repository</repository>
</repositories>
</androidPackage>
</androidPackages>
</dependencies>
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 992fedef98ac42209a9d26d5bf364adf
labels:
- gvh
- gvh_version-14.1.0
- gvhp_exportpath-Firebase/Editor/MessagingDependencies.xml
timeCreated: 1480838400
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ae88c0972b7448b5b36def1716f1d711
labels:
- gvh
- gvh_version-14.1.0
- gvhp_exportpath-Firebase/Editor/generate_xml_from_google_services_json.exe
timeCreated: 1480838400
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,498 @@
#!/usr/bin/python
# Copyright 2016 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.
"""Stand-alone implementation of the Gradle Firebase plugin.
Converts the services json file to xml:
https://googleplex-android.googlesource.com/platform/tools/base/+/studio-master-dev/build-system/google-services/src/main/groovy/com/google/gms/googleservices
"""
__author__ = 'Wouter van Oortmerssen'
import argparse
import ctypes
import json
import os
import platform
import sys
from xml.etree import ElementTree
if platform.system().lower() == 'windows':
import ctypes.wintypes # pylint: disable=g-import-not-at-top
# Map Python 2's unicode method to encode a string as bytes in python 3.
try:
unicode('') # See whether unicode class is available (Python < 3)
except NameError:
unicode = str # pylint: disable=redefined-builtin,invalid-name
# Input filename if it isn't set.
DEFAULT_INPUT_FILENAME = 'app/google-services.json'
# Output filename if it isn't set.
DEFAULT_OUTPUT_FILENAME = 'res/values/googleservices.xml'
# Input filename for .plist files, if it isn't set.
DEFAULT_PLIST_INPUT_FILENAME = 'GoogleService-Info.plist'
# Output filename for .json files, if it isn't set.
DEFAULT_JSON_OUTPUT_FILENAME = 'google-services-desktop.json'
OAUTH_CLIENT_TYPE_ANDROID_APP = 1
OAUTH_CLIENT_TYPE_WEB = 3
def read_xml_value(xml_node):
"""Utility method for reading values from the plist XML.
Args:
xml_node: An ElementTree node, that contains a value.
Returns:
The value of the node, or None, if it could not be read.
"""
if xml_node.tag == 'string':
return xml_node.text
elif xml_node.tag == 'integer':
return int(xml_node.text)
elif xml_node.tag == 'real':
return float(xml_node.text)
elif xml_node.tag == 'false':
return 0
elif xml_node.tag == 'true':
return 1
else:
# other types of input are ignored. (data, dates, arrays, etc.)
return None
def construct_plist_dictionary(xml_root):
"""Constructs a dictionary of values based on the contents of a plist file.
Args:
xml_root: An ElementTree node, that represents the root of the xml file
that is to be parsed. (Which should be a dictionary containing
key-value pairs of the properties that need to be extracted.)
Returns:
A dictionary, containing key-value pairs for all (supported) entries in the
node.
"""
xml_dict = xml_root.find('dict')
if xml_dict is None:
return None
plist_dict = {}
i = 0
while i < len(xml_dict):
if xml_dict[i].tag == 'key':
key = xml_dict[i].text
i += 1
if i < len(xml_dict):
value = read_xml_value(xml_dict[i])
if value is not None:
plist_dict[key] = value
i += 1
return plist_dict
def update_dict_keys(key_map, input_dict):
"""Creates a dict from input_dict with the same values but new keys.
Two dictionaries are passed to this function: the key_map that represents a
mapping of source keys to destination keys, and the input_dict that is the
dictionary that is to be duplicated, replacing any key that matches a source
key with a destination key. Source keys that are not present in the
input_dict will not have their destination key represented in the result.
In other words, if key_map is `{'old': 'new', 'foo': 'bar'}`, and input_dict
is `{'old': 10}`, the result will be `{'new': 10}`.
Args:
key_map (dict): A dictionary of strings to strings that maps source keys to
destination keys.
input_dict (dict): The dictionary of string keys to any value type, which
is to be duplicated, replacing source keys with the corresponding
destination keys from key_map.
Returns:
dict: A new dictionary with updated keys.
"""
return {
new_key: input_dict[old_key]
for (old_key, new_key) in key_map.items()
if old_key in input_dict
}
def construct_google_services_json(xml_dict):
"""Constructs a google services json file from a dictionary.
Args:
xml_dict: A dictionary of all the key/value pairs that are needed for the
output json file.
Returns:
A string representing the output json file.
"""
try:
json_struct = {
'project_info':
update_dict_keys(
{
'GCM_SENDER_ID': 'project_number',
'DATABASE_URL': 'firebase_url',
'PROJECT_ID': 'project_id',
'STORAGE_BUCKET': 'storage_bucket'
}, xml_dict),
'client': [{
'client_info': {
'mobilesdk_app_id': xml_dict['GOOGLE_APP_ID'],
'android_client_info': {
'package_name': xml_dict['BUNDLE_ID']
}
},
'api_key': [{
'current_key': xml_dict['API_KEY']
}],
'services': {
'analytics_service': {
'status': xml_dict['IS_ANALYTICS_ENABLED']
},
'appinvite_service': {
'status': xml_dict['IS_APPINVITE_ENABLED']
}
}
},],
'configuration_version':
'1'
}
# OAuth client is optional, but include it if present.
if 'CLIENT_ID' in xml_dict:
json_struct['client'][0]['oauth_client'] = [{
'client_id': xml_dict['CLIENT_ID'],
}]
return json.dumps(json_struct, indent=2)
except KeyError as e:
sys.stderr.write('Could not find key in plist file: [%s]\n' % (e.args[0]))
return None
def convert_plist_to_json(plist_string, input_filename):
"""Converts an input plist string into a .json file and saves it.
Args:
plist_string: The contents of the loaded plist file.
input_filename: The file name that the plist data was read from.
Returns:
the converted string, or None if there were errors.
"""
try:
root = ElementTree.fromstring(plist_string)
except ElementTree.ParseError:
sys.stderr.write('Error parsing file %s.\n'
'It does not appear to be valid XML.\n' % (input_filename))
return None
plist_dict = construct_plist_dictionary(root)
if plist_dict is None:
sys.stderr.write('In file %s, could not locate a top-level \'dict\' '
'element.\n'
'File format should be plist XML, with a top-level '
'dictionary containing project settings as key-value '
'pairs.\n' % (input_filename))
return None
json_string = construct_google_services_json(plist_dict)
return json_string
def gen_string(parent, name, text):
"""Generate one <string /> element and put into the list of keeps.
Args:
parent: The object that will hold the string.
name: The name to store the string under.
text: The text of the string.
"""
if text:
prev = parent.get('tools:keep', '')
if prev:
prev += ','
parent.set('tools:keep', prev + '@string/' + name)
child = ElementTree.SubElement(parent, 'string', {
'name': name,
'translatable': 'false'
})
child.text = text
def indent(elem, level=0):
"""Recurse through XML tree and add indentation.
Args:
elem: The element to recurse over
level: The current indentation level.
"""
i = '\n' + level*' '
if elem is not None:
if not elem.text or not elem.text.strip():
elem.text = i + ' '
if not elem.tail or not elem.tail.strip():
elem.tail = i
for elem in elem:
indent(elem, level+1)
if not elem.tail or not elem.tail.strip():
elem.tail = i
else:
if level and (not elem.tail or not elem.tail.strip()):
elem.tail = i
def argv_as_unicode_win32():
"""Returns unicode command line arguments on windows.
"""
get_command_line_w = ctypes.cdll.kernel32.GetCommandLineW
get_command_line_w.restype = ctypes.wintypes.LPCWSTR
# CommandLineToArgvW parses the Unicode command line
command_line_to_argv_w = ctypes.windll.shell32.CommandLineToArgvW
command_line_to_argv_w.argtypes = [
ctypes.wintypes.LPCWSTR,
ctypes.POINTER(ctypes.c_int)
]
command_line_to_argv_w.restype = ctypes.POINTER(
ctypes.wintypes.LPWSTR)
argc = ctypes.c_int(0)
argv = command_line_to_argv_w(get_command_line_w(), argc)
# Strip the python executable from the arguments if it exists
# (It would be listed as the first argument on the windows command line, but
# not in the arguments to the python script)
sys_argv_len = len(sys.argv)
return [unicode(argv[i]) for i in
range(argc.value - sys_argv_len, argc.value)]
def main():
parser = argparse.ArgumentParser(
description=((
'Converts a Firebase %s into %s similar to the Gradle plugin, or '
'converts a Firebase %s into a %s suitible for use on desktop apps.' %
(DEFAULT_INPUT_FILENAME, DEFAULT_OUTPUT_FILENAME,
DEFAULT_PLIST_INPUT_FILENAME, DEFAULT_JSON_OUTPUT_FILENAME))))
parser.add_argument('-i', help='Override input file name',
metavar='FILE', required=False)
parser.add_argument('-o', help='Override destination file name',
metavar='FILE', required=False)
parser.add_argument('-p', help=('Package ID to select within the set of '
'packages in the input file. If this is '
'not specified, the first package in the '
'input file is selected.'))
parser.add_argument('-l', help=('List all package IDs referenced by the '
'input file. If this is specified, '
'the output file is not created.'),
action='store_true', default=False, required=False)
parser.add_argument('-f', help=('Print project fields from the input file '
'in the form \'name=value\\n\' for each '
'field. If this is specified, the output '
'is not created.'),
action='store_true', default=False, required=False)
parser.add_argument(
'--plist',
help=(
'Specifies a plist file to convert to a JSON configuration file. '
'If this is enabled, the script will expect a .plist file as input, '
'which it will convert into %s file. The output file is '
'*not* suitable for use with Firebase on Android.' %
(DEFAULT_JSON_OUTPUT_FILENAME)),
action='store_true',
default=False,
required=False)
# python 2 on Windows doesn't handle unicode arguments well, so we need to
# pre-process the command line arguments before trying to parse them.
if platform.system() == 'Windows':
sys.argv = argv_as_unicode_win32()
args = parser.parse_args()
if args.plist:
input_filename = DEFAULT_PLIST_INPUT_FILENAME
output_filename = DEFAULT_JSON_OUTPUT_FILENAME
else:
input_filename = DEFAULT_INPUT_FILENAME
output_filename = DEFAULT_OUTPUT_FILENAME
if args.i:
# Encode the input string (type unicode) as a normal string (type str)
# using the 'utf-8' encoding so that it can be worked with the same as
# input names from other sources (like the defaults).
input_filename_raw = args.i.encode('utf-8')
# Decode the filename to a unicode string using the 'utf-8' encoding to
# properly handle filepaths with unicode characters in them.
input_filename = input_filename_raw.decode('utf-8')
if args.o:
output_filename = args.o
with open(input_filename, 'r') as ifile:
file_string = ifile.read()
json_string = None
if args.plist:
json_string = convert_plist_to_json(file_string, input_filename)
if json_string is None:
return 1
jsobj = json.loads(json_string)
else:
jsobj = json.loads(file_string)
root = ElementTree.Element('resources')
root.set('xmlns:tools', 'http://schemas.android.com/tools')
project_info = jsobj.get('project_info')
if project_info:
gen_string(root, 'firebase_database_url', project_info.get('firebase_url'))
gen_string(root, 'gcm_defaultSenderId', project_info.get('project_number'))
gen_string(root, 'google_storage_bucket',
project_info.get('storage_bucket'))
gen_string(root, 'project_id', project_info.get('project_id'))
if args.f:
if not project_info:
sys.stderr.write('No project info found in %s.' % input_filename)
return 1
for field, value in sorted(project_info.items()):
sys.stdout.write('%s=%s\n' % (field, value))
return 0
packages = set()
client_list = jsobj.get('client')
if client_list:
# Search for the user specified package in the file.
selected_package_name = ''
selected_client = client_list[0]
find_package_name = args.p
for client in client_list:
package_name = client.get('client_info', {}).get(
'android_client_info', {}).get('package_name', '')
if not package_name:
package_name = client.get('oauth_client', {}).get(
'android_info', {}).get('package_name', '')
if package_name:
if not selected_package_name:
selected_package_name = package_name
selected_client = client
if package_name == find_package_name:
selected_package_name = package_name
selected_client = client
packages.add(package_name)
if args.p and selected_package_name != find_package_name:
sys.stderr.write('No packages found in %s which match the package '
'name %s\n'
'\n'
'Found the following:\n'
'%s\n' % (input_filename, find_package_name,
'\n'.join(packages)))
return 1
client_api_key = selected_client.get('api_key')
if client_api_key:
client_api_key0 = client_api_key[0]
gen_string(root, 'google_api_key', client_api_key0.get('current_key'))
gen_string(root, 'google_crash_reporting_api_key',
client_api_key0.get('current_key'))
client_info = selected_client.get('client_info')
if client_info:
gen_string(root, 'google_app_id', client_info.get('mobilesdk_app_id'))
# Only include the first matching OAuth client ID per type.
client_id_web_parsed = False
client_id_android_parsed = False
oauth_client_list = selected_client.get('oauth_client')
if oauth_client_list:
for oauth_client in oauth_client_list:
client_type = oauth_client.get('client_type')
client_id = oauth_client.get('client_id')
if not (client_type and client_id): continue
if (client_type == OAUTH_CLIENT_TYPE_WEB and
not client_id_web_parsed):
gen_string(root, 'default_web_client_id', client_id)
client_id_web_parsed = True
if (client_type == OAUTH_CLIENT_TYPE_ANDROID_APP and
not client_id_android_parsed):
gen_string(root, 'default_android_client_id', client_id)
client_id_android_parsed = True
services = selected_client.get('services')
if services:
ads_service = services.get('ads_service')
if ads_service:
gen_string(root, 'test_banner_ad_unit_id',
ads_service.get('test_banner_ad_unit_id'))
gen_string(root, 'test_interstitial_ad_unit_id',
ads_service.get('test_interstitial_ad_unit_id'))
analytics_service = services.get('analytics_service')
if analytics_service:
analytics_property = analytics_service.get('analytics_property')
if analytics_property:
gen_string(root, 'ga_trackingId',
analytics_property.get('tracking_id'))
# enable this once we have an example if this service being present
# in the json data:
maps_service_enabled = False
if maps_service_enabled:
maps_service = services.get('maps_service')
if maps_service:
maps_api_key = maps_service.get('api_key')
if maps_api_key:
for k in range(0, len(maps_api_key)):
# generates potentially multiple of these keys, which is
# the same behavior as the java plugin.
gen_string(root, 'google_maps_key',
maps_api_key[k].get('maps_api_key'))
tree = ElementTree.ElementTree(root)
indent(root)
if args.l:
for package in sorted(packages):
if package:
sys.stdout.write(package + '\n')
else:
path = os.path.dirname(output_filename)
if path and not os.path.exists(path):
os.makedirs(path)
if not args.plist:
tree.write(output_filename, 'utf-8', True)
else:
with open(output_filename, 'w') as ofile:
ofile.write(json_string)
return 0
if __name__ == '__main__':
sys.exit(main())
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8f18ed76c0f04ce0a65736104f913ef8
labels:
- gvh
- gvh_version-14.1.0
- gvhp_exportpath-Firebase/Editor/generate_xml_from_google_services_json.py
timeCreated: 1480838400
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant: