1+ // Copyright (c) Microsoft Corporation. All rights reserved.
2+ //
3+ // Licensed under the MIT license.
4+
5+ using System ;
6+ using System . Collections . Generic ;
7+ using System . IO ;
8+ using System . Linq ;
9+ using System . Reflection ;
10+ using UnityEditor ;
11+ using UnityEngine ;
12+
13+ /// <summary>
14+ /// This file is used to define dependencies, and pass them along to a system which can resolve dependencies.
15+ /// </summary>
16+ public class FirebaseDependency
17+ {
18+ private const string GoogleServicesFileBasename = "google-services" ;
19+ private const string GoogleServicesInputFile = GoogleServicesFileBasename + ".json" ;
20+ private const string GoogleServicesOutputFile = GoogleServicesFileBasename + ".xml" ;
21+ private const string GoogleServicesOutputDirectory = "Assets/Plugins/Android/res/values" ;
22+ private const string GoogleServicesOutputPath = GoogleServicesOutputDirectory + "/" + GoogleServicesOutputFile ;
23+
24+ private const string DefaultWebClientIdKey = "default_web_client_id" ;
25+ private const string FirebaseDatabaseUrlKey = "firebase_database_url" ;
26+ private const string GATrackingIdKey = "ga_trackingId" ;
27+ private const string GSMDefaultSenderIdKey = "gcm_defaultSenderId" ;
28+ private const string GoogleAPIKey = "google_api_key" ;
29+ private const string GoogleAppIdKey = "google_app_id" ;
30+ private const string CrashReportingApiKey = "google_crash_reporting_api_key" ;
31+ private const string GoogleStorageBucketKey = "google_storage_bucket" ;
32+ private const string ProjectIdKey = "project_id" ;
33+
34+ private const string FirebaseMessagingVersion = "17.0.0" ;
35+ private const string FirebaseCoreVersion = "16.0.1" ;
36+
37+ static void SetupDependencies ( )
38+ {
39+ #if UNITY_ANDROID
40+ // Setup the resolver using reflection as the module may not be available at compile time.
41+ Type versionHandler = Type . GetType ( "Google.VersionHandler, Google.VersionHandler, Version=1.2.0.0, Culture=neutral, PublicKeyToken=null" ) ;
42+ if ( versionHandler == null )
43+ {
44+ Debug . LogError ( "Unable to set up Android dependencies, class `Google.VersionHandler` is not found" ) ;
45+ return ;
46+ }
47+ Type playServicesSupport = ( Type ) versionHandler . InvokeMember ( "FindClass" , BindingFlags . Public | BindingFlags . Static | BindingFlags . InvokeMethod , null , null , new object [ ]
48+ {
49+ "Google.JarResolver" , "Google.JarResolver.PlayServicesSupport"
50+ } ) ;
51+ if ( playServicesSupport == null )
52+ {
53+ Debug . LogError ( "Unable to set up Android dependencies, class `Google.JarResolver.PlayServicesSupport` is not found" ) ;
54+ return ;
55+ }
56+ object svcSupport = versionHandler . InvokeMember ( "InvokeStaticMethod" , BindingFlags . Public | BindingFlags . Static | BindingFlags . InvokeMethod , null , null , new object [ ]
57+ {
58+ playServicesSupport , "CreateInstance" , new object [ ] { "FirebaseMessaging" , EditorPrefs . GetString ( "AndroidSdkRoot" ) , "ProjectSettings" } , null
59+ } ) ;
60+ versionHandler . InvokeMember ( "InvokeInstanceMethod" , BindingFlags . Public | BindingFlags . Static | BindingFlags . InvokeMethod , null , null , new object [ ]
61+ {
62+ svcSupport , "DependOn" , new object [ ] { "com.google.firebase" , "firebase-messaging" , FirebaseMessagingVersion } ,
63+ new Dictionary < string , object > ( ) {
64+ { "packageIds" , new string [ ] { "extra-google-m2repository" , "extra-android-m2repository" } } ,
65+ { "repositories" , null }
66+ }
67+ } ) ;
68+ versionHandler . InvokeMember ( "InvokeInstanceMethod" , BindingFlags . Public | BindingFlags . Static | BindingFlags . InvokeMethod , null , null , new object [ ]
69+ {
70+ svcSupport , "DependOn" ,
71+ new object [ ] { "com.google.firebase" , "firebase-core" , FirebaseCoreVersion } ,
72+ new Dictionary < string , object > ( ) {
73+ { "packageIds" , new string [ ] { "extra-google-m2repository" , "extra-android-m2repository" } } ,
74+ { "repositories" , null }
75+ }
76+ } ) ;
77+ // Update editor project view.
78+ AssetDatabase . Refresh ( ) ;
79+ #endif
80+ }
81+
82+ static string [ ] FindGoogleServicesFiles ( )
83+ {
84+ var googleServicesFiles = new List < string > ( ) ;
85+ foreach ( var asset in AssetDatabase . FindAssets ( GoogleServicesFileBasename ) )
86+ {
87+ string assetPath = AssetDatabase . GUIDToAssetPath ( asset ) ;
88+ if ( Path . GetFileName ( assetPath ) == GoogleServicesInputFile )
89+ {
90+ googleServicesFiles . Add ( assetPath ) ;
91+ }
92+ }
93+ return googleServicesFiles . Count > 0 ? googleServicesFiles . ToArray ( ) : null ;
94+ }
95+
96+ static void UpdateJson ( )
97+ {
98+ #if UNITY_ANDROID
99+ var bundleId = ApplicationIdHelper . GetApplicationId ( ) ;
100+ var projectDir = Path . Combine ( Application . dataPath , ".." ) ;
101+ var googleServicesFiles = FindGoogleServicesFiles ( ) ;
102+ if ( googleServicesFiles == null )
103+ {
104+ return ;
105+ }
106+ if ( googleServicesFiles . Length > 1 )
107+ {
108+ Debug . LogWarning ( "More than one " + GoogleServicesInputFile + " file found, using first one." ) ;
109+ }
110+ var inputPath = Path . Combine ( projectDir , googleServicesFiles [ 0 ] ) ;
111+ var outputPath = Path . Combine ( projectDir , GoogleServicesOutputPath ) ;
112+ var outputDir = Path . Combine ( projectDir , GoogleServicesOutputDirectory ) ;
113+ if ( ! Directory . Exists ( outputDir ) )
114+ {
115+ try
116+ {
117+ Directory . CreateDirectory ( outputDir ) ;
118+ }
119+ catch ( Exception ex )
120+ {
121+ Debug . LogException ( ex ) ;
122+ return ;
123+ }
124+ }
125+ if ( File . Exists ( outputPath ) &&
126+ File . GetLastWriteTime ( outputPath ) . CompareTo ( File . GetLastWriteTime ( inputPath ) ) >= 0 )
127+ {
128+ return ;
129+ }
130+ var json = File . ReadAllText ( inputPath ) ;
131+ var googleServices = JsonUtility . FromJson < GoogleServices > ( json ) ;
132+ var resolvedClientInfo = googleServices . GetClient ( bundleId ) ;
133+ if ( resolvedClientInfo == null )
134+ {
135+ Debug . LogWarning ( "Failed to find client_info in " + GoogleServicesInputFile + " matching package name: " + bundleId ) ;
136+ }
137+ var valuesItems = new Dictionary < string , string > {
138+ { DefaultWebClientIdKey , googleServices . GetDefaultWebClientId ( bundleId ) } ,
139+ { FirebaseDatabaseUrlKey , googleServices . GetFirebaseDatabaseUrl ( ) } ,
140+ { GATrackingIdKey , googleServices . GetGATrackingId ( bundleId ) } ,
141+ { GSMDefaultSenderIdKey , googleServices . GetDefaultGcmSenderId ( ) } ,
142+ { GoogleAPIKey , googleServices . GetGoogleApiKey ( bundleId ) } ,
143+ { GoogleAppIdKey , googleServices . GetGoogleAppId ( bundleId ) } ,
144+ { CrashReportingApiKey , googleServices . GetCrashReportingApiKey ( bundleId ) } ,
145+ { GoogleStorageBucketKey , googleServices . GetStorageBucket ( bundleId ) } ,
146+ { ProjectIdKey , googleServices . GetProjectId ( ) } ,
147+ } ;
148+ XmlResourceHelper . WriteXmlResource ( outputPath , valuesItems ) ;
149+ // Update editor project view.
150+ AssetDatabase . Refresh ( ) ;
151+ #endif
152+ }
153+
154+ /// <summary>
155+ /// Handle delayed loading of the dependency resolvers.
156+ /// </summary>
157+ public static void SetupPush ( )
158+ {
159+ string [ ] importedAssets = AssetDatabase . GetAllAssetPaths ( ) ;
160+ foreach ( string asset in importedAssets )
161+ {
162+ if ( asset . Contains ( "JarResolver" ) )
163+ {
164+ SetupDependencies ( ) ;
165+ }
166+ else if ( Path . GetFileName ( asset ) == GoogleServicesInputFile )
167+ {
168+ UpdateJson ( ) ;
169+ }
170+ }
171+ }
172+
173+ #region Models
174+
175+ [ Serializable ]
176+ public class ProjectInfo
177+ {
178+ public string project_id ;
179+ public string project_number ;
180+ public string name ;
181+ public string firebase_url ;
182+ public string storage_bucket ;
183+ }
184+
185+ [ Serializable ]
186+ public class AndroidClientInfo
187+ {
188+ public string package_name ;
189+ public string [ ] certificate_hash ;
190+ }
191+
192+ [ Serializable ]
193+ public class ClientInfo
194+ {
195+ public string mobilesdk_app_id ;
196+ public string client_id ;
197+ public int client_type ;
198+ public AndroidClientInfo android_client_info ;
199+ }
200+
201+ [ Serializable ]
202+ public class AndroidInfo
203+ {
204+ public string package_name ;
205+ public string certificate_hash ;
206+ }
207+
208+ [ Serializable ]
209+ public class OauthClient
210+ {
211+ public string client_id ;
212+ public int client_type ;
213+ public AndroidInfo android_info ;
214+ }
215+
216+ [ Serializable ]
217+ public class AnalyticsProperty
218+ {
219+ public string tracking_id ;
220+ }
221+
222+ [ Serializable ]
223+ public class AnalyticsService
224+ {
225+ public int status ;
226+ public AnalyticsProperty analytics_property ;
227+ }
228+
229+ [ Serializable ]
230+ public class Services
231+ {
232+ public AnalyticsService analytics_service ;
233+ }
234+
235+ [ Serializable ]
236+ public class Client
237+ {
238+ public ClientInfo client_info ;
239+ public OauthClient [ ] oauth_client ;
240+ public ApiKey [ ] api_key ;
241+ public Services services ;
242+ }
243+
244+ [ Serializable ]
245+ public class ApiKey
246+ {
247+ public string current_key ;
248+ }
249+
250+ [ Serializable ]
251+ public class GoogleServices
252+ {
253+ public ProjectInfo project_info ;
254+ public Client [ ] client ;
255+ public object [ ] client_info ;
256+ public string ARTIFACT_VERSION ;
257+
258+ public Client GetClient ( string packageName )
259+ {
260+ if ( client == null || ! client . Any ( ) )
261+ return null ;
262+ return client . FirstOrDefault ( c => c . client_info . android_client_info . package_name == packageName ) ;
263+ }
264+
265+ public string GetGATrackingId ( string packageName )
266+ {
267+ // {YOUR_CLIENT}/services/analytics-service/analytics_property/tracking_id
268+ var client = GetClient ( packageName ) ;
269+ if ( client == null )
270+ return null ;
271+ if ( client . services != null &&
272+ client . services . analytics_service != null &&
273+ client . services . analytics_service . analytics_property != null )
274+ return client . services . analytics_service . analytics_property . tracking_id ;
275+ return null ;
276+ }
277+
278+ public string GetProjectId ( )
279+ {
280+ // project_info/project_id
281+ if ( project_info != null )
282+ return project_info . project_id ;
283+ return null ;
284+ }
285+
286+ public string GetDefaultGcmSenderId ( )
287+ {
288+ // project_info/project_number
289+ if ( project_info != null )
290+ return project_info . project_number ;
291+ return null ;
292+ }
293+
294+ public string GetGoogleAppId ( string packageName )
295+ {
296+ // {YOUR_CLIENT}/client_info/mobilesdk_app_id
297+ var client = GetClient ( packageName ) ;
298+ if ( client == null )
299+ return null ;
300+ if ( client . client_info != null )
301+ return client . client_info . mobilesdk_app_id ;
302+ return null ;
303+ }
304+
305+ public string GetDefaultWebClientId ( string packageName )
306+ {
307+ // default_web_client_id:
308+ // {YOUR_CLIENT}/oauth_client/client_id(client_type == 3)
309+ var client = GetClient ( packageName ) ;
310+ if ( client == null )
311+ return null ;
312+ if ( client . oauth_client != null && client . oauth_client . Any ( ) )
313+ {
314+ var oauthClient = client . oauth_client . FirstOrDefault ( c => c . client_type == 3 ) ;
315+ if ( oauthClient != null )
316+ return oauthClient . client_id ;
317+ }
318+ return null ;
319+ }
320+
321+ public string GetGoogleApiKey ( string packageName )
322+ {
323+ // google_api_key:
324+ // {YOUR_CLIENT}/api_key/current_key
325+ var client = GetClient ( packageName ) ;
326+ if ( client == null )
327+ return null ;
328+ if ( client . api_key != null && client . api_key . Any ( ) )
329+ return client . api_key . FirstOrDefault ( ) . current_key ;
330+ return null ;
331+ }
332+
333+ public string GetFirebaseDatabaseUrl ( )
334+ {
335+ // firebase_database_url:
336+ // project_info/firebase_url
337+ if ( project_info != null )
338+ return project_info . firebase_url ;
339+ return null ;
340+ }
341+
342+ public string GetCrashReportingApiKey ( string packageName )
343+ {
344+ // google_crash_reporting_api_key:
345+ // {YOUR_CLIENT}/api_key/current_key
346+ var client = GetClient ( packageName ) ;
347+ if ( client == null )
348+ return null ;
349+ if ( client . api_key != null && client . api_key . Any ( ) )
350+ return client . api_key . FirstOrDefault ( ) . current_key ;
351+ return null ;
352+ }
353+
354+ public string GetStorageBucket ( string packageName )
355+ {
356+ // google_storage_bucket:
357+ // project_info/storage_bucket
358+ if ( project_info != null )
359+ return project_info . storage_bucket ;
360+ return null ;
361+ }
362+ }
363+
364+ #endregion
365+ }
0 commit comments