From a4b33316990e0e1bae1ccedf8c28d299afd0cca2 Mon Sep 17 00:00:00 2001 From: "drsanta@google.com" Date: Tue, 5 Apr 2022 10:11:57 -0400 Subject: [PATCH 01/33] Android build --- gma/testapp/AndroidManifest.xml | 26 + gma/testapp/CMakeLists.txt | 111 +++++ gma/testapp/LICENSE | 202 ++++++++ gma/testapp/LaunchScreen.storyboard | 7 + gma/testapp/Podfile | 7 + gma/testapp/build.gradle | 71 +++ gma/testapp/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 49896 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + gma/testapp/gradlew | 164 +++++++ gma/testapp/gradlew.bat | 90 ++++ gma/testapp/proguard.pro | 2 + gma/testapp/readme.md | 194 ++++++++ gma/testapp/res/values/strings.xml | 4 + gma/testapp/settings.gradle | 36 ++ gma/testapp/src/android/android_main.cc | 255 ++++++++++ .../google/firebase/example/LoggingUtils.java | 55 +++ gma/testapp/src/common_main.cc | 463 ++++++++++++++++++ gma/testapp/src/desktop/desktop_main.cc | 125 +++++ gma/testapp/src/ios/ios_main.mm | 119 +++++ gma/testapp/src/main.h | 63 +++ gma/testapp/testapp.xcodeproj/project.pbxproj | 312 ++++++++++++ .../AppIcon.appiconset/Contents.json | 73 +++ .../LaunchImage.launchimage/Contents.json | 36 ++ gma/testapp/testapp/Info.plist | 28 ++ 24 files changed, 2449 insertions(+) create mode 100644 gma/testapp/AndroidManifest.xml create mode 100644 gma/testapp/CMakeLists.txt create mode 100644 gma/testapp/LICENSE create mode 100644 gma/testapp/LaunchScreen.storyboard create mode 100644 gma/testapp/Podfile create mode 100644 gma/testapp/build.gradle create mode 100644 gma/testapp/gradle/wrapper/gradle-wrapper.jar create mode 100644 gma/testapp/gradle/wrapper/gradle-wrapper.properties create mode 100755 gma/testapp/gradlew create mode 100644 gma/testapp/gradlew.bat create mode 100644 gma/testapp/proguard.pro create mode 100644 gma/testapp/readme.md create mode 100644 gma/testapp/res/values/strings.xml create mode 100644 gma/testapp/settings.gradle create mode 100644 gma/testapp/src/android/android_main.cc create mode 100644 gma/testapp/src/android/java/com/google/firebase/example/LoggingUtils.java create mode 100644 gma/testapp/src/common_main.cc create mode 100644 gma/testapp/src/desktop/desktop_main.cc create mode 100644 gma/testapp/src/ios/ios_main.mm create mode 100644 gma/testapp/src/main.h create mode 100644 gma/testapp/testapp.xcodeproj/project.pbxproj create mode 100644 gma/testapp/testapp/Images.xcassets/AppIcon.appiconset/Contents.json create mode 100644 gma/testapp/testapp/Images.xcassets/LaunchImage.launchimage/Contents.json create mode 100644 gma/testapp/testapp/Info.plist diff --git a/gma/testapp/AndroidManifest.xml b/gma/testapp/AndroidManifest.xml new file mode 100644 index 00000000..092181fa --- /dev/null +++ b/gma/testapp/AndroidManifest.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + diff --git a/gma/testapp/CMakeLists.txt b/gma/testapp/CMakeLists.txt new file mode 100644 index 00000000..2132660c --- /dev/null +++ b/gma/testapp/CMakeLists.txt @@ -0,0 +1,111 @@ +cmake_minimum_required(VERSION 2.8) + +# User settings for Firebase samples. +# Path to Firebase SDK. +# Try to read the path to the Firebase C++ SDK from an environment variable. +if (NOT "$ENV{FIREBASE_CPP_SDK_DIR}" STREQUAL "") + set(DEFAULT_FIREBASE_CPP_SDK_DIR "$ENV{FIREBASE_CPP_SDK_DIR}") +else() + set(DEFAULT_FIREBASE_CPP_SDK_DIR "firebase_cpp_sdk") +endif() +if ("${FIREBASE_CPP_SDK_DIR}" STREQUAL "") + set(FIREBASE_CPP_SDK_DIR ${DEFAULT_FIREBASE_CPP_SDK_DIR}) +endif() +if(NOT EXISTS ${FIREBASE_CPP_SDK_DIR}) + message(FATAL_ERROR "The Firebase C++ SDK directory does not exist: ${FIREBASE_CPP_SDK_DIR}. See the readme.md for more information") +endif() + +# Sample source files. +set(FIREBASE_SAMPLE_COMMON_SRCS + src/main.h + src/common_main.cc +) + +# The include directory for the testapp. +include_directories(src) + +# Sample uses some features that require C++ 11, such as lambdas. +set (CMAKE_CXX_STANDARD 11) + +if(ANDROID) + # Build an Android application. + + # Source files used for the Android build. + set(FIREBASE_SAMPLE_ANDROID_SRCS + src/android/android_main.cc + ) + + # Build native_app_glue as a static lib + add_library(native_app_glue STATIC + ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c) + + # Export ANativeActivity_onCreate(), + # Refer to: https://github.com/android-ndk/ndk/issues/381. + set(CMAKE_SHARED_LINKER_FLAGS + "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate") + + # Define the target as a shared library, as that is what gradle expects. + set(target_name "android_main") + add_library(${target_name} SHARED + ${FIREBASE_SAMPLE_ANDROID_SRCS} + ${FIREBASE_SAMPLE_COMMON_SRCS} + ) + + target_link_libraries(${target_name} + log android atomic native_app_glue + ) + + target_include_directories(${target_name} PRIVATE + ${ANDROID_NDK}/sources/android/native_app_glue) + + set(ADDITIONAL_LIBS) +else() + # Build a desktop application. + + # Windows runtime mode, either MD or MT depending on whether you are using + # /MD or /MT. For more information see: + # https://msdn.microsoft.com/en-us/library/2kzt1wy3.aspx + set(MSVC_RUNTIME_MODE MD) + + # Platform abstraction layer for the desktop sample. + set(FIREBASE_SAMPLE_DESKTOP_SRCS + src/desktop/desktop_main.cc + ) + + set(target_name "desktop_testapp") + add_executable(${target_name} + ${FIREBASE_SAMPLE_DESKTOP_SRCS} + ${FIREBASE_SAMPLE_COMMON_SRCS} + ) + + if(APPLE) + set(ADDITIONAL_LIBS pthread) + elseif(MSVC) + set(ADDITIONAL_LIBS) + else() + set(ADDITIONAL_LIBS pthread) + endif() + + # If a config file is present, copy it into the binary location so that it's + # possible to create the default Firebase app. + set(FOUND_JSON_FILE FALSE) + foreach(config "google-services-desktop.json" "google-services.json") + if (EXISTS ${config}) + add_custom_command( + TARGET ${target_name} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + ${config} $) + set(FOUND_JSON_FILE TRUE) + break() + endif() + endforeach() + if(NOT FOUND_JSON_FILE) + message(WARNING "Failed to find either google-services-desktop.json or google-services.json. See the readme.md for more information.") + endif() +endif() + +# Add the Firebase libraries to the target using the function from the SDK. +add_subdirectory(${FIREBASE_CPP_SDK_DIR} bin/ EXCLUDE_FROM_ALL) +# Note that firebase_app needs to be last in the list. +set(firebase_libs firebase_gma firebase_app) +target_link_libraries(${target_name} "${firebase_libs}" ${ADDITIONAL_LIBS}) diff --git a/gma/testapp/LICENSE b/gma/testapp/LICENSE new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/gma/testapp/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/gma/testapp/LaunchScreen.storyboard b/gma/testapp/LaunchScreen.storyboard new file mode 100644 index 00000000..673e0f7e --- /dev/null +++ b/gma/testapp/LaunchScreen.storyboard @@ -0,0 +1,7 @@ + + + + + + + diff --git a/gma/testapp/Podfile b/gma/testapp/Podfile new file mode 100644 index 00000000..3d649e01 --- /dev/null +++ b/gma/testapp/Podfile @@ -0,0 +1,7 @@ +source 'https://github.com/CocoaPods/Specs.git' +platform :ios, '8.0' +# GMA test application. +target 'testapp' do + pod 'Google-Mobile-Ads-SDK', '8.13.0' + pod 'Firebase/Analytics', '8.10.0' +end diff --git a/gma/testapp/build.gradle b/gma/testapp/build.gradle new file mode 100644 index 00000000..e8c75724 --- /dev/null +++ b/gma/testapp/build.gradle @@ -0,0 +1,71 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + repositories { + mavenLocal() + maven { url 'https://maven.google.com' } + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:3.3.3' + classpath 'com.google.gms:google-services:4.0.1' } +} + +allprojects { + repositories { + mavenLocal() + maven { url 'https://maven.google.com' } + jcenter() + } +} + +apply plugin: 'com.android.application' + +android { + compileOptions { + sourceCompatibility 1.8 + targetCompatibility 1.8 + } + + compileSdkVersion 28 + buildToolsVersion '28.0.3' + + sourceSets { + main { + jniLibs.srcDirs = ['libs'] + manifest.srcFile 'AndroidManifest.xml' + java.srcDirs = ['src/android/java'] + res.srcDirs = ['res'] + } + } + + defaultConfig { + applicationId 'com.google.android.admob.testapp' + minSdkVersion 19 + targetSdkVersion 28 + versionCode 1 + versionName '1.0' + externalNativeBuild.cmake { + arguments "-DFIREBASE_CPP_SDK_DIR=$gradle.firebase_cpp_sdk_dir" + } + } + externalNativeBuild.cmake { + path 'CMakeLists.txt' + } + buildTypes { + release { + minifyEnabled true + proguardFile getDefaultProguardFile('proguard-android.txt') + proguardFile file('proguard.pro') + } + } +} + +apply from: "$gradle.firebase_cpp_sdk_dir/Android/firebase_dependencies.gradle" +firebaseCpp.dependencies { + gma +} + +apply plugin: 'com.google.gms.google-services' + +// com.google.gms.googleservices.GoogleServicesPlugin.config.disableVersionCheck = true diff --git a/gma/testapp/gradle/wrapper/gradle-wrapper.jar b/gma/testapp/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..8c0fb64a8698b08ecc4158d828ca593c4928e9dd GIT binary patch literal 49896 zcmagFb986H(k`5d^NVfUwr$(C?M#x1ZQHiZiEVpg+jrjgoQrerx!>1o_ul)D>ebz~ zs=Mmxr&>W81QY-S1PKWQ%N-;H^tS;2*XwVA`dej1RRn1z<;3VgfE4~kaG`A%QSPsR z#ovnZe+tS9%1MfeDyz`RirvdjPRK~p(#^q2(^5@O&NM19EHdvN-A&StN>0g6QA^VN z0Gx%Gq#PD$QMRFzmK+utjS^Y1F0e8&u&^=w5K<;4Rz|i3A=o|IKLY+g`iK6vfr9?+ z-`>gmU&i?FGSL5&F?TXFu`&Js6h;15QFkXp2M1H9|Eq~bpov-GU(uz%mH0n55wUl- zv#~ccAz`F5wlQ>e_KlJS3@{)B?^v*EQM=IxLa&76^y51a((wq|2-`qON>+4dLc{Oo z51}}o^Zen(oAjxDK7b++9_Yg`67p$bPo3~BCpGM7uAWmvIhWc5Gi+gQZ|Pwa-Gll@<1xmcPy z|NZmu6m)g5Ftu~BG&Xdxclw7Cij{xbBMBn-LMII#Slp`AElb&2^Hw+w>(3crLH!;I zN+Vk$D+wP1#^!MDCiad@vM>H#6+`Ct#~6VHL4lzmy;lSdk>`z6)=>Wh15Q2)dQtGqvn0vJU@+(B5{MUc*qs4!T+V=q=wy)<6$~ z!G>e_4dN@lGeF_$q9`Ju6Ncb*x?O7=l{anm7Eahuj_6lA{*#Gv*TaJclevPVbbVYu z(NY?5q+xxbO6%g1xF0r@Ix8fJ~u)VRUp`S%&rN$&e!Od`~s+64J z5*)*WSi*i{k%JjMSIN#X;jC{HG$-^iX+5f5BGOIHWAl*%15Z#!xntpk($-EGKCzKa zT7{siZ9;4TICsWQ$pu&wKZQTCvpI$Xvzwxoi+XkkpeE&&kFb!B?h2hi%^YlXt|-@5 zHJ~%AN!g_^tmn1?HSm^|gCE#!GRtK2(L{9pL#hp0xh zME}|DB>(5)`iE7CM)&_+S}-Bslc#@B5W4_+k4Cp$l>iVyg$KP>CN?SVGZ(&02>iZK zB<^HP$g$Lq*L$BWd?2(F?-MUbNWTJVQdW7$#8a|k_30#vHAD1Z{c#p;bETk0VnU5A zBgLe2HFJ3032$G<`m*OB!KM$*sdM20jm)It5OSru@tXpK5LT>#8)N!*skNu1$TpIw zufjjdp#lyH5bZ%|Iuo|iu9vG1HrIVWLH>278xo>aVBkPN3V$~!=KnlXQ4eDqS7%E% zQ!z^$Q$b^6Q)g#cLpwur(|<0gWHo6A6jc;n`t(V9T;LzTAU{IAu*uEQ%Ort1k+Kn+f_N`9|bxYC+~Z1 zCC1UCWv*Orx$_@ydv9mIe(liLfOr7mhbV@tKw{6)q^1DH1nmvZ0cj215R<~&I<4S| zgnr;9Cdjqpz#o8i0CQjtl`}{c*P)aSdH|abxGdrR)-3z+02-eX(k*B)Uqv6~^nh** z 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 \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/gma/testapp/gradlew.bat b/gma/testapp/gradlew.bat new file mode 100644 index 00000000..8a0b282a --- /dev/null +++ b/gma/testapp/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/gma/testapp/proguard.pro b/gma/testapp/proguard.pro new file mode 100644 index 00000000..54cd248b --- /dev/null +++ b/gma/testapp/proguard.pro @@ -0,0 +1,2 @@ +-ignorewarnings +-keep,includedescriptorclasses public class com.google.firebase.example.LoggingUtils { *; } diff --git a/gma/testapp/readme.md b/gma/testapp/readme.md new file mode 100644 index 00000000..018af54d --- /dev/null +++ b/gma/testapp/readme.md @@ -0,0 +1,194 @@ +Firebase GMA Quickstart +============================== + +The Firebase Google Mobile Ads Test Application (testapp) demonstrates +loading and showing AdMob-served banners, interstitials and rewarded ads +using the Firebase GMA C++ SDK. The application has no user interface and +simply logs actions it's performing to the console while displaying the ads. + +Introduction +------------ + +- [Read more about Firebase GMA](https://firebase.google.com/docs/gma) + +Getting Started +--------------- + +### iOS + - Link your iOS app to the Firebase libraries. + - Get CocoaPods version 1 or later by running, + ``` + sudo gem install cocoapods --pre + ``` + - From the testapp directory, install the CocoaPods listed in the Podfile + by running, + ``` + pod install + ``` + - Open the generated Xcode workspace (which now has the CocoaPods), + ``` + open testapp.xcworkspace + ``` + - For further details please refer to the + [general instructions for setting up an iOS app with Firebase](https://firebase.google.com/docs/ios/setup). + - Register your iOS app with Firebase. + - Create a new app on the [Firebase console](https://firebase.google.com/console/), + and attach your iOS app to it. + - You can use "com.google.ios.admob.testapp" as the iOS Bundle ID + while you're testing. You can omit App Store ID while testing. + - Download the Firebase C++ SDK linked from + [https://firebase.google.com/docs/cpp/setup](https://firebase.google.com/docs/cpp/setup) + and unzip it to a directory of your choice. + - Add the following frameworks from the Firebase C++ SDK to the project: + - frameworks/ios/universal/firebase.framework + - frameworks/ios/universal/firebase_gma.framework + - You will need to either, + 1. Check "Copy items if needed" when adding the frameworks, or + 2. Add the framework path in "Framework Search Paths" + - For example, if you downloaded the Firebase C++ SDK to + `/Users/me/firebase_cpp_sdk`, + then you would add the path + `/Users/me/firebase_cpp_sdk/frameworks/ios/universal`. + - To add the path, in Xcode, select your project in the project + navigator, then select your target in the main window. + Select the "Build Settings" tab, and click "All" to see all + the build settings. Scroll down to "Search Paths", and add + your path to "Framework Search Paths". + - Update the AdMob App ID: + - In the `testapp/Info.plist`, update `GADApplicationIdentifier` with the + app ID for your iOS app, replacing 'YOUR_IOS_ADMOB_APP_ID'. + - For more information, see + [Update your Info.plist](https://developers.google.com/admob/ios/quick-start#manual_download) + - In Xcode, build & run the sample on an iOS device or simulator. + - The testapp displays a banner ad, an interstitial ad and a rewarded ad. You must + dismiss each ad to see the next. + - The output of the app can be viewed onscreen or via the console. To view + the console in Xcode, select "View --> Debug Area --> Activate Console" + from the menu. + +### Android + - Register your Android app with Firebase. + - Create a new app on the [Firebase console](https://firebase.google.com/console/), + and attach your Android app to it. + - You can use "com.google.android.admob.testapp" as the Package Name + while you're testing. + - To [generate a SHA1](https://developers.google.com/android/guides/client-auth) + run this command on Mac and Linux, + ``` + keytool -exportcert -list -v -alias androiddebugkey -keystore ~/.android/debug.keystore + ``` + or this command on Windows, + ``` + keytool -exportcert -list -v -alias androiddebugkey -keystore %USERPROFILE%\.android\debug.keystore + ``` + - If keytool reports that you do not have a debug.keystore, you can + [create one with](http://developer.android.com/tools/publishing/app-signing.html#signing-manually), + ``` + keytool -genkey -v -keystore ~/.android/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US" + ``` + - Add the `google-services.json` file that you downloaded from Firebase + console to the root directory of testapp. This file identifies your + Android app to the Firebase backend. + - For further details please refer to the + [general instructions for setting up an Android app with Firebase](https://firebase.google.com/docs/android/setup). + - Download the Firebase C++ SDK linked from + [https://firebase.google.com/docs/cpp/setup](https://firebase.google.com/docs/cpp/setup) + and unzip it to a directory of your choice. + - Configure the location of the Firebase C++ SDK by setting the + firebase\_cpp\_sdk.dir Gradle property to the SDK install directory. + For example, in the project directory: + ``` + echo "systemProp.firebase\_cpp\_sdk.dir=/User/$USER/firebase\_cpp\_sdk" >> gradle.properties + ``` + - Ensure the Android SDK and NDK locations are set in Android Studio. + - From the Android Studio launch menu, go to `File/Project Structure...` or + `Configure/Project Defaults/Project Structure...` + (Shortcut: Control + Alt + Shift + S on windows, Command + ";" on a mac) + and download the SDK and NDK if the locations are not yet set. + - Open *build.gradle* in Android Studio. + - From the Android Studio launch menu, "Open an existing Android Studio + project", and select `build.gradle`. + - Install the SDK Platforms that Android Studio reports missing. + - Update the GMA App ID: + - In the `AndroidManifest.xml`, update + `com.google.android.gms.ads.APPLICATION_ID` with the same app ID, + replacing 'YOUR_ANDROID_ADMOB_APP_ID'. + - For more information, see + [Update your AndroidManifest.xml](https://developers.google.com/admob/android/quick-start#update_your_androidmanifestxml) + - Build the testapp and run it on an Android device or emulator. + - The testapp will initialize the GMA SDK, then load and display a test + banner ad, interstitial ad and rewarded ad. + - Tapping on an ad to verify the clickthrough process is possible, and the + test app will wait for each ad to be dismissed. + - While this is happening, information from the device log will be written + to an onscreen TextView. + - Logcat can also be used as normal. + +### Desktop + - Note: the testapp has no user interface, but the output can be viewed via + the console. Note that the GMA SDK uses a stubbed implementation on + desktop, so functionality is not expected. + - Register your app with Firebase. + - Create a new app on the [Firebase console](https://firebase.google.com/console/), + following the above instructions for Android or iOS. + - If you have an Android project, add the `google-services.json` file that + you downloaded from the Firebase console to the root directory of the + testapp. + - If you have an iOS project, and don't wish to use an Android project, + you can use the Python script `generate_xml_from_google_services_json.py --plist`, + located in the Firebase C++ SDK, to convert your `GoogleService-Info.plist` + file into a `google-services-desktop.json` file, which can then be + placed in the root directory of the testapp. + - Download the Firebase C++ SDK linked from + [https://firebase.google.com/docs/cpp/setup](https://firebase.google.com/docs/cpp/setup) + and unzip it to a directory of your choice. + - Configure the testapp with the location of the Firebase C++ SDK. + This can be done a couple different ways: + - When invoking cmake, pass in the location with + -DFIREBASE_CPP_SDK_DIR=/path/to/firebase_cpp_sdk. + - Set an environment variable for FIREBASE_CPP_SDK_DIR to the path to use. + - Edit the CMakeLists.txt file, changing the FIREBASE_CPP_SDK_DIR path + to the appropriate location. + - From the testapp directory, generate the build files by running, + ``` + cmake . + ``` + If you want to use XCode, you can use -G"Xcode" to generate the project. + Similarly, to use Visual Studio, -G"Visual Studio 15 2017". For more + information, see + [CMake generators](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html). + - Build the testapp, by either opening the generated project file based on + the platform, or running, + ``` + cmake --build . + ``` + - Execute the testapp by running, + ``` + ./desktop_testapp + ``` + Note that the executable might be under another directory, such as Debug. + +Support +------- + +[https://firebase.google.com/support/](https://firebase.google.com/support/) + +License +------- + +Copyright 2016 Google, Inc. + +Licensed to the Apache Software Foundation (ASF) under one or more contributor +license agreements. See the NOTICE file distributed with this work for +additional information regarding copyright ownership. The ASF licenses this +file to you 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. diff --git a/gma/testapp/res/values/strings.xml b/gma/testapp/res/values/strings.xml new file mode 100644 index 00000000..a9a313a3 --- /dev/null +++ b/gma/testapp/res/values/strings.xml @@ -0,0 +1,4 @@ + + + Firebase GMA Test + diff --git a/gma/testapp/settings.gradle b/gma/testapp/settings.gradle new file mode 100644 index 00000000..2a543b93 --- /dev/null +++ b/gma/testapp/settings.gradle @@ -0,0 +1,36 @@ +// Copyright 2018 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. + +def firebase_cpp_sdk_dir = System.getProperty('firebase_cpp_sdk.dir') +if (firebase_cpp_sdk_dir == null || firebase_cpp_sdk_dir.isEmpty()) { + firebase_cpp_sdk_dir = System.getenv('FIREBASE_CPP_SDK_DIR') + if (firebase_cpp_sdk_dir == null || firebase_cpp_sdk_dir.isEmpty()) { + if ((new File('firebase_cpp_sdk')).exists()) { + firebase_cpp_sdk_dir = 'firebase_cpp_sdk' + } else { + throw new StopActionException( + 'firebase_cpp_sdk.dir property or the FIREBASE_CPP_SDK_DIR ' + + 'environment variable must be set to reference the Firebase C++ ' + + 'SDK install directory. This is used to configure static library ' + + 'and C/C++ include paths for the SDK.') + } + } +} +if (!(new File(firebase_cpp_sdk_dir)).exists()) { + throw new StopActionException( + sprintf('Firebase C++ SDK directory %s does not exist', + firebase_cpp_sdk_dir)) +} +gradle.ext.firebase_cpp_sdk_dir = "$firebase_cpp_sdk_dir" +includeBuild "$firebase_cpp_sdk_dir" \ No newline at end of file diff --git a/gma/testapp/src/android/android_main.cc b/gma/testapp/src/android/android_main.cc new file mode 100644 index 00000000..73cb30e7 --- /dev/null +++ b/gma/testapp/src/android/android_main.cc @@ -0,0 +1,255 @@ +// Copyright 2016 Google Inc. All rights reserved. +// +// 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. + +#include +#include + +#include +#include +#include +#include + +#include "main.h" // NOLINT + +// This implementation is derived from http://github.com/google/fplutil + +extern "C" int common_main(int argc, const char* argv[]); + +static struct android_app* g_app_state = nullptr; +static bool g_destroy_requested = false; +static bool g_started = false; +static bool g_restarted = false; +static pthread_mutex_t g_started_mutex; + +// Handle state changes from via native app glue. +static void OnAppCmd(struct android_app* app, int32_t cmd) { + g_destroy_requested |= cmd == APP_CMD_DESTROY; +} + +// Process events pending on the main thread. +// Returns true when the app receives an event requesting exit. +bool ProcessEvents(int msec) { + struct android_poll_source* source = nullptr; + int events; + int looperId = ALooper_pollAll(msec, nullptr, &events, + reinterpret_cast(&source)); + if (looperId >= 0 && source) { + source->process(g_app_state, source); + } + return g_destroy_requested | g_restarted; +} + +// Get the activity. +jobject GetActivity() { return g_app_state->activity->clazz; } + +// Get the window context. For Android, it's a jobject pointing to the Activity. +jobject GetWindowContext() { return g_app_state->activity->clazz; } + +// Find a class, attempting to load the class if it's not found. +jclass FindClass(JNIEnv* env, jobject activity_object, const char* class_name) { + jclass class_object = env->FindClass(class_name); + if (env->ExceptionCheck()) { + env->ExceptionClear(); + // If the class isn't found it's possible NativeActivity is being used by + // the application which means the class path is set to only load system + // classes. The following falls back to loading the class using the + // Activity before retrieving a reference to it. + jclass activity_class = env->FindClass("android/app/Activity"); + jmethodID activity_get_class_loader = env->GetMethodID( + activity_class, "getClassLoader", "()Ljava/lang/ClassLoader;"); + + jobject class_loader_object = + env->CallObjectMethod(activity_object, activity_get_class_loader); + + jclass class_loader_class = env->FindClass("java/lang/ClassLoader"); + jmethodID class_loader_load_class = + env->GetMethodID(class_loader_class, "loadClass", + "(Ljava/lang/String;)Ljava/lang/Class;"); + jstring class_name_object = env->NewStringUTF(class_name); + + class_object = static_cast(env->CallObjectMethod( + class_loader_object, class_loader_load_class, class_name_object)); + + if (env->ExceptionCheck()) { + env->ExceptionClear(); + class_object = nullptr; + } + env->DeleteLocalRef(class_name_object); + env->DeleteLocalRef(class_loader_object); + } + return class_object; +} + +// Vars that we need available for appending text to the log window: +class LoggingUtilsData { + public: + LoggingUtilsData() + : logging_utils_class_(nullptr), + logging_utils_add_log_text_(0), + logging_utils_init_log_window_(0) {} + + ~LoggingUtilsData() { + JNIEnv* env = GetJniEnv(); + assert(env); + if (logging_utils_class_) { + env->DeleteGlobalRef(logging_utils_class_); + } + } + + void Init() { + JNIEnv* env = GetJniEnv(); + assert(env); + + jclass logging_utils_class = FindClass( + env, GetActivity(), "com/google/firebase/example/LoggingUtils"); + assert(logging_utils_class != 0); + + // Need to store as global references so it don't get moved during garbage + // collection. + logging_utils_class_ = + static_cast(env->NewGlobalRef(logging_utils_class)); + env->DeleteLocalRef(logging_utils_class); + + logging_utils_init_log_window_ = env->GetStaticMethodID( + logging_utils_class_, "initLogWindow", "(Landroid/app/Activity;)V"); + logging_utils_add_log_text_ = env->GetStaticMethodID( + logging_utils_class_, "addLogText", "(Ljava/lang/String;)V"); + + env->CallStaticVoidMethod(logging_utils_class_, + logging_utils_init_log_window_, GetActivity()); + } + + void AppendText(const char* text) { + if (logging_utils_class_ == 0) return; // haven't been initted yet + JNIEnv* env = GetJniEnv(); + assert(env); + jstring text_string = env->NewStringUTF(text); + env->CallStaticVoidMethod(logging_utils_class_, logging_utils_add_log_text_, + text_string); + env->DeleteLocalRef(text_string); + } + + private: + jclass logging_utils_class_; + jmethodID logging_utils_add_log_text_; + jmethodID logging_utils_init_log_window_; +}; + +LoggingUtilsData* g_logging_utils_data; + +// Checks if a JNI exception has happened, and if so, logs it to the console. +void CheckJNIException() { + JNIEnv* env = GetJniEnv(); + if (env->ExceptionCheck()) { + // Get the exception text. + jthrowable exception = env->ExceptionOccurred(); + env->ExceptionClear(); + + // Convert the exception to a string. + jclass object_class = env->FindClass("java/lang/Object"); + jmethodID toString = + env->GetMethodID(object_class, "toString", "()Ljava/lang/String;"); + jstring s = (jstring)env->CallObjectMethod(exception, toString); + const char* exception_text = env->GetStringUTFChars(s, nullptr); + + // Log the exception text. + __android_log_print(ANDROID_LOG_INFO, FIREBASE_TESTAPP_NAME, + "-------------------JNI exception:"); + __android_log_print(ANDROID_LOG_INFO, FIREBASE_TESTAPP_NAME, "%s", + exception_text); + __android_log_print(ANDROID_LOG_INFO, FIREBASE_TESTAPP_NAME, + "-------------------"); + + // Also, assert fail. + assert(false); + + // In the event we didn't assert fail, clean up. + env->ReleaseStringUTFChars(s, exception_text); + env->DeleteLocalRef(s); + env->DeleteLocalRef(exception); + } +} + +// Log a message that can be viewed in "adb logcat". +void LogMessage(const char* format, ...) { + static const int kLineBufferSize = 100; + char buffer[kLineBufferSize + 2]; + + va_list list; + va_start(list, format); + int string_len = vsnprintf(buffer, kLineBufferSize, format, list); + string_len = string_len < kLineBufferSize ? string_len : kLineBufferSize; + // append a linebreak to the buffer: + buffer[string_len] = '\n'; + buffer[string_len + 1] = '\0'; + + __android_log_vprint(ANDROID_LOG_INFO, FIREBASE_TESTAPP_NAME, format, list); + g_logging_utils_data->AppendText(buffer); + CheckJNIException(); + va_end(list); +} + +// Get the JNI environment. +JNIEnv* GetJniEnv() { + JavaVM* vm = g_app_state->activity->vm; + JNIEnv* env; + jint result = vm->AttachCurrentThread(&env, nullptr); + return result == JNI_OK ? env : nullptr; +} + +// Execute common_main(), flush pending events and finish the activity. +extern "C" void android_main(struct android_app* state) { + // native_app_glue spawns a new thread, calling android_main() when the + // activity onStart() or onRestart() methods are called. This code handles + // the case where we're re-entering this method on a different thread by + // signalling the existing thread to exit, waiting for it to complete before + // reinitializing the application. + if (g_started) { + g_restarted = true; + // Wait for the existing thread to exit. + pthread_mutex_lock(&g_started_mutex); + pthread_mutex_unlock(&g_started_mutex); + } else { + g_started_mutex = PTHREAD_MUTEX_INITIALIZER; + } + pthread_mutex_lock(&g_started_mutex); + g_started = true; + + // Save native app glue state and setup a callback to track the state. + g_destroy_requested = false; + g_app_state = state; + g_app_state->onAppCmd = OnAppCmd; + + // Create the logging display. + g_logging_utils_data = new LoggingUtilsData(); + g_logging_utils_data->Init(); + + // Execute cross platform entry point. + static const char* argv[] = {FIREBASE_TESTAPP_NAME}; + int return_value = common_main(1, argv); + (void)return_value; // Ignore the return value. + ProcessEvents(10); + + // Clean up logging display. + delete g_logging_utils_data; + g_logging_utils_data = nullptr; + + // Finish the activity. + if (!g_restarted) ANativeActivity_finish(state->activity); + + g_app_state->activity->vm->DetachCurrentThread(); + g_started = false; + g_restarted = false; + pthread_mutex_unlock(&g_started_mutex); +} diff --git a/gma/testapp/src/android/java/com/google/firebase/example/LoggingUtils.java b/gma/testapp/src/android/java/com/google/firebase/example/LoggingUtils.java new file mode 100644 index 00000000..11d67c5b --- /dev/null +++ b/gma/testapp/src/android/java/com/google/firebase/example/LoggingUtils.java @@ -0,0 +1,55 @@ +// Copyright 2016 Google Inc. All rights reserved. +// +// 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. + +package com.google.firebase.example; + +import android.app.Activity; +import android.os.Handler; +import android.os.Looper; +import android.view.Window; +import android.widget.LinearLayout; +import android.widget.ScrollView; +import android.widget.TextView; + +/** + * A utility class, encapsulating the data and methods required to log arbitrary + * text to the screen, via a non-editable TextView. + */ +public class LoggingUtils { + public static TextView sTextView = null; + + public static void initLogWindow(Activity activity) { + LinearLayout linearLayout = new LinearLayout(activity); + ScrollView scrollView = new ScrollView(activity); + TextView textView = new TextView(activity); + textView.setTag("Logger"); + linearLayout.addView(scrollView); + scrollView.addView(textView); + Window window = activity.getWindow(); + window.takeSurface(null); + window.setContentView(linearLayout); + sTextView = textView; + } + + public static void addLogText(final String text) { + new Handler(Looper.getMainLooper()).post(new Runnable() { + @Override + public void run() { + if (sTextView != null) { + sTextView.append(text); + } + } + }); + } +} diff --git a/gma/testapp/src/common_main.cc b/gma/testapp/src/common_main.cc new file mode 100644 index 00000000..4d51b1b6 --- /dev/null +++ b/gma/testapp/src/common_main.cc @@ -0,0 +1,463 @@ +// Copyright 2022 Google Inc. All rights reserved. +// +// 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. + +#include "firebase/gma.h" +#include "firebase/gma/ad_view.h" +#include "firebase/gma/interstitial_ad.h" +#include "firebase/gma/rewarded_ad.h" +#include "firebase/gma/types.h" +#include "firebase/app.h" +#include "firebase/future.h" + +// Thin OS abstraction layer. +#include "main.h" // NOLINT + +// A simple listener that logs changes to an AdView. +class LoggingAdViewListener : public firebase::gma::AdListener { + public: + LoggingAdViewListener() {} + + void OnAdClicked() override { + ::LogMessage("AdView ad clicked."); + } + + void OnAdClosed() override { + ::LogMessage("AdView ad closed."); + } + + void OnAdImpression() override { + ::LogMessage("AdView ad impression."); + } + + void OnAdOpened() override { + ::LogMessage("AdView ad opened."); + } +}; + +// A simple listener that logs changes to an AdView's bounding box. +class LoggingAdViewBoundedBoxListener + : public firebase::gma::AdViewBoundingBoxListener { + public: + void OnBoundingBoxChanged(firebase::gma::AdView* ad_view, + firebase::gma::BoundingBox box) override { + ::LogMessage("AdView bounding box update x: %d y: %d " + "width: %d height: %d", box.x, box.y, box.width, box.height); + } +}; + +// A simple listener track FullScreen content changes. +class LoggingFullScreenContentListener + : public firebase::gma::FullScreenContentListener { + public: + LoggingFullScreenContentListener() : num_ad_dismissed_(0) { } + + void OnAdClicked() override { + ::LogMessage("FullScreenContent ad clicked."); + } + + void OnAdDismissedFullScreenContent() override { + ::LogMessage("FullScreenContent ad dismissed."); + num_ad_dismissed_++; + } + + void OnAdFailedToShowFullScreenContent( + const firebase::gma::AdError& ad_error) override { + ::LogMessage("FullScreenContent ad failed to show full screen content," + " AdErrorCode: %d", ad_error.code()); + } + + void OnAdImpression() override { + ::LogMessage("FullScreenContent ad impression."); + } + + void OnAdShowedFullScreenContent() override { + ::LogMessage("FullScreenContent ad showed content."); + } + + uint32_t num_ad_dismissed() const { return num_ad_dismissed_; } + + private: + uint32_t num_ad_dismissed_; +}; + +// A simple listener track UserEarnedReward events. +class LoggingUserEarnedRewardListener + : public firebase::gma::UserEarnedRewardListener { + public: + LoggingUserEarnedRewardListener() { } + + void OnUserEarnedReward(const firebase::gma::AdReward& reward) override { + ::LogMessage("User earned reward amount: %d type: %s", + reward.amount(), reward.type().c_str()); + } +}; + +// A simple listener track ad pay events. +class LoggingPaidEventListener : public firebase::gma::PaidEventListener { + public: + LoggingPaidEventListener() { } + + void OnPaidEvent(const firebase::gma::AdValue& value) override { + ::LogMessage("PaidEvent value: %lld currency_code: %s", + value.value_micros(), value.currency_code().c_str()); + } +}; + +void LoadAndShowAdView(const firebase::gma::AdRequest& ad_request); +void LoadAndShowInterstitialAd(const firebase::gma::AdRequest& ad_request); +void LoadAndShowRewardedAd(const firebase::gma::AdRequest& ad_request); + +// These ad units IDs have been created specifically for testing, and will +// always return test ads. +#if defined(__ANDROID__) +const char* kBannerAdUnit = "ca-app-pub-3940256099942544/6300978111"; +const char* kInterstitialAdUnit = "ca-app-pub-3940256099942544/1033173712"; +const char* kRewardedAdUnit = "ca-app-pub-3940256099942544/5224354917"; +#else +const char* kBannerAdUnit = "ca-app-pub-3940256099942544/2934735716"; +const char* kInterstitialAdUnit = "ca-app-pub-3940256099942544/4411468910"; +const char* kRewardedAdUnit = "ca-app-pub-3940256099942544/1712485313"; +#endif + +// Sample keywords to use in making the request. +static const std::vector kKeywords({"GMA", "C++", "Fun"}); + +// Sample test device IDs to use in making the request. Add your own here. +const std::vector kTestDeviceIDs = { + "2077ef9a63d2b398840261c8221a0c9b", "098fe087d987c9a878965454a65654d7"}; + +#if defined(ANDROID) +static const char* kAdNetworkExtrasClassName = + "com/google/ads/mediation/admob/AdMobAdapter"; +#else +static const char* kAdNetworkExtrasClassName = "GADExtras"; +#endif + +// Function to wait for the completion of a future, and log the error +// if one is encountered. +static void WaitForFutureCompletion(firebase::FutureBase future) { + while (!ProcessEvents(1000)) { + if (future.status() != firebase::kFutureStatusPending) { + break; + } + } + + if (future.error() != firebase::gma::kAdErrorCodeNone) { + LogMessage("ERROR: Action failed with error code %d and message \"%s\".", + future.error(), future.error_message()); + } +} + +// Inittialize GMA, load a Banner, Interstitial and Rewarded Ad. +extern "C" int common_main(int argc, const char* argv[]) { + firebase::App* app; + LogMessage("Initializing Firebase App."); + +#if defined(__ANDROID__) + app = ::firebase::App::Create(GetJniEnv(), GetActivity()); +#else + app = ::firebase::App::Create(); +#endif // defined(__ANDROID__) + + LogMessage("Created the Firebase App %x.", + static_cast(reinterpret_cast(app))); + + LogMessage("Initializing the GMA with Firebase API."); + firebase::gma::Initialize(*app); + + WaitForFutureCompletion(firebase::gma::InitializeLastResult()); + if(firebase::gma::InitializeLastResult().error() != firebase::gma::kAdErrorCodeNone) { + // Initialization Failure. The error was already logged in WaitForFutureCompletion, + // so simply exit here. + return -1; + } + + // Log mediation adapter initialization status. + for (auto adapter_status : + firebase::gma::GetInitializationStatus().GetAdapterStatusMap()) { + LogMessage("GMA Mediation Adapter '%s' %s (latency %d ms): %s", + adapter_status.first.c_str(), + (adapter_status.second.is_initialized() ? "loaded" : "NOT loaded"), + adapter_status.second.latency(), + adapter_status.second.description().c_str()); + } + + // Configure test device ids before loading ads. + // + // This example uses ad units that are specially configured to return test ads + // for every request. When using your own ad unit IDs, however, it's important + // to register the device IDs associated with any devices that will be used to + // test the app. This ensures that regardless of the ad unit ID, those + // devices will always receive test ads in compliance with AdMob policy. + // + // Device IDs can be obtained by checking the logcat or the Xcode log while + // debugging. They appear as a long string of hex characters. + firebase::gma::RequestConfiguration request_configuration; + request_configuration.test_device_ids = kTestDeviceIDs; + firebase::gma::SetRequestConfiguration(request_configuration); + + // + // Load and Display a Banner Ad using AdView. + // + + // Create an AdRequest. + firebase::gma::AdRequest ad_request; + + // Configure additional keywords to be used in targeting. + for (auto keyword_iter = kKeywords.begin(); keyword_iter != kKeywords.end(); + ++keyword_iter) { + ad_request.add_keyword((*keyword_iter).c_str()); + } + + // "Extra" key value pairs can be added to the request as well. Typically + // these are used when testing new features. + ad_request.add_extra(kAdNetworkExtrasClassName, "the_name_of_an_extra", + "the_value_for_that_extra"); + + LoadAndShowAdView(ad_request); + LoadAndShowInterstitialAd(ad_request); + LoadAndShowRewardedAd(ad_request); + + LogMessage("\nAll ad operations complete, terminating GMA"); + + firebase::gma::Terminate(); + delete app; + + // Wait until the user kills the app. + while (!ProcessEvents(1000)) { } + + return 0; +} + +void LoadAndShowAdView(const firebase::gma::AdRequest& ad_request) { + // Initialize an AdView. + firebase::gma::AdView* ad_view = new firebase::gma::AdView(); + const firebase::gma::AdSize banner_ad_size = firebase::gma::AdSize::kBanner; + ad_view->Initialize(GetWindowContext(), kBannerAdUnit, banner_ad_size); + + // Block until the ad view completes initialization. + WaitForFutureCompletion(ad_view->InitializeLastResult()); + + // Check for errors. + if (ad_view->InitializeLastResult().error() != + firebase::gma::kAdErrorCodeNone) { + LogMessage("AdView initalization failed, error code: %d", + ad_view->InitializeLastResult().error()); + delete ad_view; + ad_view = nullptr; + return; + } + + // Setup the AdView's listeners. + LoggingAdViewListener ad_view_listener; + ad_view->SetAdListener(&ad_view_listener); + LoggingPaidEventListener paid_event_listener; + ad_view->SetPaidEventListener(&paid_event_listener); + LoggingAdViewBoundedBoxListener bounding_box_listener; + ad_view->SetBoundingBoxListener(&bounding_box_listener); + + // Load an ad. + ad_view->LoadAd(ad_request); + WaitForFutureCompletion(ad_view->LoadAdLastResult()); + + // Check for errors. + if (ad_view->LoadAdLastResult().error() != firebase::gma::kAdErrorCodeNone) { + // Log information as to why the loadAd request failed. + const firebase::gma::AdResult* result_ptr = + ad_view->LoadAdLastResult().result(); + if (result_ptr != nullptr) { + LogMessage("AdView::loadAd Failure - Code: %d Message: %s Domain: %s", + result_ptr->ad_error().code(), result_ptr->ad_error().message().c_str(), + result_ptr->ad_error().domain().c_str()); + } + WaitForFutureCompletion(ad_view->Destroy()); + delete ad_view; + ad_view = nullptr; + return; + } + + // Log the loaded ad's dimensions. + const firebase::gma::AdSize ad_size = ad_view->ad_size(); + LogMessage("AdView loaded ad width: %d height: %d", ad_size.width(), + ad_size.height()); + + // Show the ad. + LogMessage("Showing the banner ad."); + WaitForFutureCompletion(ad_view->Show()); + + // Move to each of the six pre-defined positions. + LogMessage("Moving the banner ad to top-center."); + ad_view->SetPosition(firebase::gma::AdView::kPositionTop); + WaitForFutureCompletion(ad_view->SetPositionLastResult()); + + LogMessage("Moving the banner ad to top-left."); + ad_view->SetPosition(firebase::gma::AdView::kPositionTopLeft); + WaitForFutureCompletion(ad_view->SetPositionLastResult()); + + LogMessage("Moving the banner ad to top-right."); + ad_view->SetPosition(firebase::gma::AdView::kPositionTopRight); + WaitForFutureCompletion(ad_view->SetPositionLastResult()); + + LogMessage("Moving the banner ad to bottom-center."); + ad_view->SetPosition(firebase::gma::AdView::kPositionBottom); + WaitForFutureCompletion(ad_view->SetPositionLastResult()); + + LogMessage("Moving the banner ad to bottom-left."); + ad_view->SetPosition(firebase::gma::AdView::kPositionBottomLeft); + WaitForFutureCompletion(ad_view->SetPositionLastResult()); + + LogMessage("Moving the banner ad to bottom-right."); + ad_view->SetPosition(firebase::gma::AdView::kPositionBottomRight); + WaitForFutureCompletion(ad_view->SetPositionLastResult()); + + // Try some coordinate moves. + LogMessage("Moving the banner ad to (100, 300)."); + ad_view->SetPosition(100, 300); + WaitForFutureCompletion(ad_view->SetPositionLastResult()); + + LogMessage("Moving the banner ad to (100, 400)."); + ad_view->SetPosition(100, 400); + WaitForFutureCompletion(ad_view->SetPositionLastResult()); + + // Try hiding and showing the BannerView. + LogMessage("Hiding the banner ad."); + ad_view->Hide(); + WaitForFutureCompletion(ad_view->HideLastResult()); + + LogMessage("Showing the banner ad."); + ad_view->Show(); + WaitForFutureCompletion(ad_view->ShowLastResult()); + + LogMessage("Hiding the banner ad again now that we're done with it."); + ad_view->Hide(); + WaitForFutureCompletion(ad_view->HideLastResult()); + + // Clean up the ad view. + ad_view->Destroy(); + WaitForFutureCompletion(ad_view->DestroyLastResult()); + delete ad_view; + ad_view = nullptr; +} + +void LoadAndShowInterstitialAd(const firebase::gma::AdRequest& ad_request) { + // Initialize an InterstitialAd. + firebase::gma::InterstitialAd* interstitial_ad = new firebase::gma::InterstitialAd(); + interstitial_ad->Initialize(GetWindowContext()); + + // Block until the interstitial ad completes initialization. + WaitForFutureCompletion(interstitial_ad->InitializeLastResult()); + + // Check for errors. + if (interstitial_ad->InitializeLastResult().error() != firebase::gma::kAdErrorCodeNone) { + delete interstitial_ad; + interstitial_ad = nullptr; + return; + } + + // Setup the interstitial ad's listeners. + LoggingFullScreenContentListener fullscreen_content_listener; + interstitial_ad->SetFullScreenContentListener(&fullscreen_content_listener); + LoggingPaidEventListener paid_event_listener; + interstitial_ad->SetPaidEventListener(&paid_event_listener); + + // Load an ad. + interstitial_ad->LoadAd(kInterstitialAdUnit, ad_request); + WaitForFutureCompletion(interstitial_ad->LoadAdLastResult()); + + // Check for errors. + if (interstitial_ad->LoadAdLastResult().error() != firebase::gma::kAdErrorCodeNone) { + // Log information as to why the loadAd request failed. + const firebase::gma::AdResult* result_ptr = + interstitial_ad->LoadAdLastResult().result(); + if (result_ptr != nullptr) { + LogMessage("InterstitialAd::loadAd Failure - Code: %d Message: %s Domain: %s", + result_ptr->ad_error().code(), result_ptr->ad_error().message().c_str(), + result_ptr->ad_error().domain().c_str()); + } + delete interstitial_ad; + interstitial_ad = nullptr; + return; + } + + // Show the ad. + LogMessage("Showing the interstitial ad."); + interstitial_ad->Show(); + WaitForFutureCompletion(interstitial_ad->ShowLastResult()); + + // Wait for the user to close the interstitial. + while (fullscreen_content_listener.num_ad_dismissed() == 0) { + ProcessEvents(1000); + } + + // Clean up the interstitial ad. + delete interstitial_ad; + interstitial_ad = nullptr; +} + +// WIP +void LoadAndShowRewardedAd(const firebase::gma::AdRequest& ad_request) { + // Initialize a RewardedAd. + firebase::gma::RewardedAd* rewarded_ad = new firebase::gma::RewardedAd(); + rewarded_ad->Initialize(GetWindowContext()); + + // Block until the interstitial ad completes initialization. + WaitForFutureCompletion(rewarded_ad->InitializeLastResult()); + + // Check for errors. + if (rewarded_ad->InitializeLastResult().error() != firebase::gma::kAdErrorCodeNone) { + delete rewarded_ad; + rewarded_ad = nullptr; + return; + } + + // Setup the rewarded ad's lifecycle listeners. + LoggingFullScreenContentListener fullscreen_content_listener; + rewarded_ad->SetFullScreenContentListener(&fullscreen_content_listener); + LoggingPaidEventListener paid_event_listener; + rewarded_ad->SetPaidEventListener(&paid_event_listener); + + // Load an ad. + rewarded_ad->LoadAd(kRewardedAdUnit, ad_request); + WaitForFutureCompletion(rewarded_ad->LoadAdLastResult()); + + // Check for errors. + if (rewarded_ad->LoadAdLastResult().error() != firebase::gma::kAdErrorCodeNone) { + // Log information as to why the loadAd request failed. + const firebase::gma::AdResult* result_ptr = + rewarded_ad->LoadAdLastResult().result(); + if (result_ptr != nullptr) { + LogMessage("RewardedAd::loadAd Failure - Code: %d Message: %s Domain: %s", + result_ptr->ad_error().code(), result_ptr->ad_error().message().c_str(), + result_ptr->ad_error().domain().c_str()); + } + delete rewarded_ad; + rewarded_ad = nullptr; + return; + } + + // Show the ad. + LogMessage("Showing the rewarded ad."); + LoggingUserEarnedRewardListener user_earned_reward_listener; + rewarded_ad->Show(&user_earned_reward_listener); + WaitForFutureCompletion(rewarded_ad->ShowLastResult()); + + // Wait for the user to close the interstitial. + while (fullscreen_content_listener.num_ad_dismissed() == 0) { + ProcessEvents(1000); + } + + // Clean up the interstitial ad. + delete rewarded_ad; + rewarded_ad = nullptr; +} \ No newline at end of file diff --git a/gma/testapp/src/desktop/desktop_main.cc b/gma/testapp/src/desktop/desktop_main.cc new file mode 100644 index 00000000..0220c688 --- /dev/null +++ b/gma/testapp/src/desktop/desktop_main.cc @@ -0,0 +1,125 @@ +// Copyright 2016 Google Inc. All rights reserved. +// +// 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. + +#include +#include +#include + +#ifdef _WIN32 +#include +#define chdir _chdir +#else +#include +#endif // _WIN32 + +#ifdef _WIN32 +#include +#endif // _WIN32 + +#include +#include + +#include "main.h" // NOLINT + +// The TO_STRING macro is useful for command line defined strings as the quotes +// get stripped. +#define TO_STRING_EXPAND(X) #X +#define TO_STRING(X) TO_STRING_EXPAND(X) + +// Path to the Firebase config file to load. +#ifdef FIREBASE_CONFIG +#define FIREBASE_CONFIG_STRING TO_STRING(FIREBASE_CONFIG) +#else +#define FIREBASE_CONFIG_STRING "" +#endif // FIREBASE_CONFIG + +extern "C" int common_main(int argc, const char* argv[]); + +static bool quit = false; + +#ifdef _WIN32 +static BOOL WINAPI SignalHandler(DWORD event) { + if (!(event == CTRL_C_EVENT || event == CTRL_BREAK_EVENT)) { + return FALSE; + } + quit = true; + return TRUE; +} +#else +static void SignalHandler(int /* ignored */) { quit = true; } +#endif // _WIN32 + +bool ProcessEvents(int msec) { +#ifdef _WIN32 + Sleep(msec); +#else + usleep(msec * 1000); +#endif // _WIN32 + return quit; +} + +std::string PathForResource() { + return std::string(); +} + +void LogMessage(const char* format, ...) { + va_list list; + va_start(list, format); + vprintf(format, list); + va_end(list); + printf("\n"); + fflush(stdout); +} + +WindowContext GetWindowContext() { return nullptr; } + +// Change the current working directory to the directory containing the +// specified file. +void ChangeToFileDirectory(const char* file_path) { + std::string path(file_path); + std::replace(path.begin(), path.end(), '\\', '/'); + auto slash = path.rfind('/'); + if (slash != std::string::npos) { + std::string directory = path.substr(0, slash); + if (!directory.empty()) chdir(directory.c_str()); + } +} + +int main(int argc, const char* argv[]) { + ChangeToFileDirectory( + FIREBASE_CONFIG_STRING[0] != '\0' ? + FIREBASE_CONFIG_STRING : argv[0]); // NOLINT +#ifdef _WIN32 + SetConsoleCtrlHandler((PHANDLER_ROUTINE)SignalHandler, TRUE); +#else + signal(SIGINT, SignalHandler); +#endif // _WIN32 + return common_main(argc, argv); +} + +#if defined(_WIN32) +// Returns the number of microseconds since the epoch. +int64_t WinGetCurrentTimeInMicroseconds() { + FILETIME file_time; + GetSystemTimeAsFileTime(&file_time); + + ULARGE_INTEGER now; + now.LowPart = file_time.dwLowDateTime; + now.HighPart = file_time.dwHighDateTime; + + // Windows file time is expressed in 100s of nanoseconds. + // To convert to microseconds, multiply x10. + return now.QuadPart * 10LL; +} +#endif diff --git a/gma/testapp/src/ios/ios_main.mm b/gma/testapp/src/ios/ios_main.mm new file mode 100644 index 00000000..6ccb2de5 --- /dev/null +++ b/gma/testapp/src/ios/ios_main.mm @@ -0,0 +1,119 @@ +// Copyright 2016 Google Inc. All rights reserved. +// +// 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. + +#import + +#include + +#include "main.h" + +extern "C" int common_main(int argc, const char* argv[]); + +@interface AppDelegate : UIResponder + +@property(nonatomic, strong) UIWindow *window; + +@end + +@interface FTAViewController : UIViewController + +@end + +static int g_exit_status = 0; +static bool g_shutdown = false; +static NSCondition *g_shutdown_complete; +static NSCondition *g_shutdown_signal; +static UITextView *g_text_view; +static UIView *g_parent_view; + +@implementation FTAViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + g_parent_view = self.view; + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + const char *argv[] = {FIREBASE_TESTAPP_NAME}; + [g_shutdown_signal lock]; + g_exit_status = common_main(1, argv); + [g_shutdown_complete signal]; + }); +} + +@end + +bool ProcessEvents(int msec) { + [g_shutdown_signal + waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:static_cast(msec) / 1000.0f]]; + return g_shutdown; +} + +WindowContext GetWindowContext() { + return g_parent_view; +} + +// Log a message that can be viewed in the console. +void LogMessage(const char* format, ...) { + va_list args; + NSString *formatString = @(format); + + va_start(args, format); + NSString *message = [[NSString alloc] initWithFormat:formatString arguments:args]; + va_end(args); + + NSLog(@"%@", message); + message = [message stringByAppendingString:@"\n"]; + + dispatch_async(dispatch_get_main_queue(), ^{ + g_text_view.text = [g_text_view.text stringByAppendingString:message]; + }); +} + +int main(int argc, char* argv[]) { + @autoreleasepool { + UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } + return g_exit_status; +} + +@implementation AppDelegate + +- (BOOL)application:(UIApplication*)application + didFinishLaunchingWithOptions:(NSDictionary*)launchOptions { + g_shutdown_complete = [[NSCondition alloc] init]; + g_shutdown_signal = [[NSCondition alloc] init]; + [g_shutdown_complete lock]; + + self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; + FTAViewController *viewController = [[FTAViewController alloc] init]; + self.window.rootViewController = viewController; + [self.window makeKeyAndVisible]; + + g_text_view = [[UITextView alloc] initWithFrame:viewController.view.bounds]; + + g_text_view.editable = NO; + g_text_view.scrollEnabled = YES; + g_text_view.userInteractionEnabled = YES; + + [viewController.view addSubview:g_text_view]; + + return YES; +} + +- (void)applicationWillTerminate:(UIApplication *)application { + g_shutdown = true; + [g_shutdown_signal signal]; + [g_shutdown_complete wait]; +} + +@end diff --git a/gma/testapp/src/main.h b/gma/testapp/src/main.h new file mode 100644 index 00000000..2eda2c10 --- /dev/null +++ b/gma/testapp/src/main.h @@ -0,0 +1,63 @@ +// Copyright 2016 Google Inc. All rights reserved. +// +// 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. + +#ifndef FIREBASE_TESTAPP_MAIN_H_ // NOLINT +#define FIREBASE_TESTAPP_MAIN_H_ // NOLINT + +#if defined(__ANDROID__) +#include +#include +#elif defined(__APPLE__) +extern "C" { +#include +} // extern "C" +#endif // __ANDROID__ + +// Defined using -DANDROID_MAIN_APP_NAME=some_app_name when compiling this +// file. +#ifndef FIREBASE_TESTAPP_NAME +#define FIREBASE_TESTAPP_NAME "android_main" +#endif // FIREBASE_TESTAPP_NAME + +// Cross platform logging method. +// Implemented by android/android_main.cc or ios/ios_main.mm. +extern "C" void LogMessage(const char* format, ...); + +// Platform-independent method to flush pending events for the main thread. +// Returns true when an event requesting program-exit is received. +bool ProcessEvents(int msec); + +// WindowContext represents the handle to the parent window. It's type +// (and usage) vary based on the OS. +#if defined(__ANDROID__) +typedef jobject WindowContext; // A jobject to the Java Activity. +#elif defined(__APPLE__) +typedef id WindowContext; // A pointer to an iOS UIView. +#else +typedef void* WindowContext; // A void* for any other environments. +#endif + +#if defined(__ANDROID__) +// Get the JNI environment. +JNIEnv* GetJniEnv(); +// Get the activity. +jobject GetActivity(); +#endif // defined(__ANDROID__) + +// Returns a variable that describes the window context for the app. On Android +// this will be a jobject pointing to the Activity. On iOS, it's an id pointing +// to the root view of the view controller. +WindowContext GetWindowContext(); + +#endif // FIREBASE_TESTAPP_MAIN_H_ // NOLINT diff --git a/gma/testapp/testapp.xcodeproj/project.pbxproj b/gma/testapp/testapp.xcodeproj/project.pbxproj new file mode 100644 index 00000000..14a0af7f --- /dev/null +++ b/gma/testapp/testapp.xcodeproj/project.pbxproj @@ -0,0 +1,312 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 4A7C015A1CEAA2480011C504 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4A7C01591CEAA2480011C504 /* Images.xcassets */; }; + 520BC0391C869159008CFBC3 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 520BC0381C869159008CFBC3 /* GoogleService-Info.plist */; }; + 529226D61C85F68000C89379 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 529226D51C85F68000C89379 /* Foundation.framework */; }; + 529226D81C85F68000C89379 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 529226D71C85F68000C89379 /* CoreGraphics.framework */; }; + 529226DA1C85F68000C89379 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 529226D91C85F68000C89379 /* UIKit.framework */; }; + 529227211C85FB6A00C89379 /* common_main.cc in Sources */ = {isa = PBXBuildFile; fileRef = 5292271F1C85FB6A00C89379 /* common_main.cc */; }; + 529227241C85FB7600C89379 /* ios_main.mm in Sources */ = {isa = PBXBuildFile; fileRef = 529227221C85FB7600C89379 /* ios_main.mm */; }; + D66B16871CE46E8900E5638A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D66B16861CE46E8900E5638A /* LaunchScreen.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 4A7C01591CEAA2480011C504 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = testapp/Images.xcassets; sourceTree = ""; }; + 520BC0381C869159008CFBC3 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; + 529226D21C85F68000C89379 /* testapp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = testapp.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 529226D51C85F68000C89379 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + 529226D71C85F68000C89379 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + 529226D91C85F68000C89379 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + 529226EE1C85F68000C89379 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; + 5292271F1C85FB6A00C89379 /* common_main.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = common_main.cc; path = src/common_main.cc; sourceTree = ""; }; + 529227201C85FB6A00C89379 /* main.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = main.h; path = src/main.h; sourceTree = ""; }; + 529227221C85FB7600C89379 /* ios_main.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ios_main.mm; path = src/ios/ios_main.mm; sourceTree = ""; }; + 52FD1FF81C85FFA000BC68E3 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = testapp/Info.plist; sourceTree = ""; }; + D66B16861CE46E8900E5638A /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 529226CF1C85F68000C89379 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 529226D81C85F68000C89379 /* CoreGraphics.framework in Frameworks */, + 529226DA1C85F68000C89379 /* UIKit.framework in Frameworks */, + 529226D61C85F68000C89379 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 529226C91C85F68000C89379 = { + isa = PBXGroup; + children = ( + 4A7C01591CEAA2480011C504 /* Images.xcassets */, + D66B16861CE46E8900E5638A /* LaunchScreen.storyboard */, + 520BC0381C869159008CFBC3 /* GoogleService-Info.plist */, + 52FD1FF81C85FFA000BC68E3 /* Info.plist */, + 5292271D1C85FB5500C89379 /* src */, + 529226D41C85F68000C89379 /* Frameworks */, + 529226D31C85F68000C89379 /* Products */, + ); + sourceTree = ""; + }; + 529226D31C85F68000C89379 /* Products */ = { + isa = PBXGroup; + children = ( + 529226D21C85F68000C89379 /* testapp.app */, + ); + name = Products; + sourceTree = ""; + }; + 529226D41C85F68000C89379 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 529226D51C85F68000C89379 /* Foundation.framework */, + 529226D71C85F68000C89379 /* CoreGraphics.framework */, + 529226D91C85F68000C89379 /* UIKit.framework */, + 529226EE1C85F68000C89379 /* XCTest.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 5292271D1C85FB5500C89379 /* src */ = { + isa = PBXGroup; + children = ( + 5292271F1C85FB6A00C89379 /* common_main.cc */, + 529227201C85FB6A00C89379 /* main.h */, + 5292271E1C85FB5B00C89379 /* ios */, + ); + name = src; + sourceTree = ""; + }; + 5292271E1C85FB5B00C89379 /* ios */ = { + isa = PBXGroup; + children = ( + 529227221C85FB7600C89379 /* ios_main.mm */, + ); + name = ios; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 529226D11C85F68000C89379 /* testapp */ = { + isa = PBXNativeTarget; + buildConfigurationList = 529226F91C85F68000C89379 /* Build configuration list for PBXNativeTarget "testapp" */; + buildPhases = ( + 529226CE1C85F68000C89379 /* Sources */, + 529226CF1C85F68000C89379 /* Frameworks */, + 529226D01C85F68000C89379 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = testapp; + productName = testapp; + productReference = 529226D21C85F68000C89379 /* testapp.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 529226CA1C85F68000C89379 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0640; + ORGANIZATIONNAME = Google; + TargetAttributes = { + 529226D11C85F68000C89379 = { + CreatedOnToolsVersion = 6.4; + }; + }; + }; + buildConfigurationList = 529226CD1C85F68000C89379 /* Build configuration list for PBXProject "testapp" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 529226C91C85F68000C89379; + productRefGroup = 529226D31C85F68000C89379 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 529226D11C85F68000C89379 /* testapp */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 529226D01C85F68000C89379 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D66B16871CE46E8900E5638A /* LaunchScreen.storyboard in Resources */, + 4A7C015A1CEAA2480011C504 /* Images.xcassets in Resources */, + 520BC0391C869159008CFBC3 /* GoogleService-Info.plist in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 529226CE1C85F68000C89379 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 529227241C85FB7600C89379 /* ios_main.mm in Sources */, + 529227211C85FB6A00C89379 /* common_main.cc in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 529226F71C85F68000C89379 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.4; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 529226F81C85F68000C89379 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.4; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 529226FA1C85F68000C89379 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "\"$(SRCROOT)/src\"", + ); + INFOPLIST_FILE = testapp/Info.plist; + PRODUCT_NAME = "$(TARGET_NAME)"; + WRAPPER_EXTENSION = app; + }; + name = Debug; + }; + 529226FB1C85F68000C89379 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "\"$(SRCROOT)/src\"", + ); + INFOPLIST_FILE = testapp/Info.plist; + PRODUCT_NAME = "$(TARGET_NAME)"; + WRAPPER_EXTENSION = app; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 529226CD1C85F68000C89379 /* Build configuration list for PBXProject "testapp" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 529226F71C85F68000C89379 /* Debug */, + 529226F81C85F68000C89379 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 529226F91C85F68000C89379 /* Build configuration list for PBXNativeTarget "testapp" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 529226FA1C85F68000C89379 /* Debug */, + 529226FB1C85F68000C89379 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 529226CA1C85F68000C89379 /* Project object */; +} diff --git a/gma/testapp/testapp/Images.xcassets/AppIcon.appiconset/Contents.json b/gma/testapp/testapp/Images.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..eeea76c2 --- /dev/null +++ b/gma/testapp/testapp/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,73 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "83.5x83.5", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/gma/testapp/testapp/Images.xcassets/LaunchImage.launchimage/Contents.json b/gma/testapp/testapp/Images.xcassets/LaunchImage.launchimage/Contents.json new file mode 100644 index 00000000..a0ad363c --- /dev/null +++ b/gma/testapp/testapp/Images.xcassets/LaunchImage.launchimage/Contents.json @@ -0,0 +1,36 @@ +{ + "images" : [ + { + "orientation" : "portrait", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "1x" + }, + { + "orientation" : "landscape", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "1x" + }, + { + "orientation" : "portrait", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "landscape", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/gma/testapp/testapp/Info.plist b/gma/testapp/testapp/Info.plist new file mode 100644 index 00000000..3f0f944e --- /dev/null +++ b/gma/testapp/testapp/Info.plist @@ -0,0 +1,28 @@ + + + + + GADApplicationIdentifier + YOUR_IOS_ADMOB_APP_ID + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + com.google.ios.admob.testapp + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + + From 3bbb47285474ba7de46560e6bce77c28ce7468c1 Mon Sep 17 00:00:00 2001 From: "drsanta@google.com" Date: Tue, 5 Apr 2022 11:22:39 -0400 Subject: [PATCH 02/33] iOS build --- gma/testapp/Podfile | 2 +- gma/testapp/src/common_main.cc | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/gma/testapp/Podfile b/gma/testapp/Podfile index 3d649e01..51c73e38 100644 --- a/gma/testapp/Podfile +++ b/gma/testapp/Podfile @@ -1,5 +1,5 @@ source 'https://github.com/CocoaPods/Specs.git' -platform :ios, '8.0' +platform :ios, '10.0' # GMA test application. target 'testapp' do pod 'Google-Mobile-Ads-SDK', '8.13.0' diff --git a/gma/testapp/src/common_main.cc b/gma/testapp/src/common_main.cc index 4d51b1b6..e5d61ed5 100644 --- a/gma/testapp/src/common_main.cc +++ b/gma/testapp/src/common_main.cc @@ -241,6 +241,8 @@ extern "C" int common_main(int argc, const char* argv[]) { } void LoadAndShowAdView(const firebase::gma::AdRequest& ad_request) { + LogMessage("\nLoad and show a banner ad in an AdView:"); + LogMessage("==="); // Initialize an AdView. firebase::gma::AdView* ad_view = new firebase::gma::AdView(); const firebase::gma::AdSize banner_ad_size = firebase::gma::AdSize::kBanner; @@ -351,6 +353,8 @@ void LoadAndShowAdView(const firebase::gma::AdRequest& ad_request) { } void LoadAndShowInterstitialAd(const firebase::gma::AdRequest& ad_request) { + LogMessage("\nLoad and show an interstitial ad:"); + LogMessage("==="); // Initialize an InterstitialAd. firebase::gma::InterstitialAd* interstitial_ad = new firebase::gma::InterstitialAd(); interstitial_ad->Initialize(GetWindowContext()); @@ -407,6 +411,8 @@ void LoadAndShowInterstitialAd(const firebase::gma::AdRequest& ad_request) { // WIP void LoadAndShowRewardedAd(const firebase::gma::AdRequest& ad_request) { + LogMessage("\nLoad and show a rewarded ad:"); + LogMessage("==="); // Initialize a RewardedAd. firebase::gma::RewardedAd* rewarded_ad = new firebase::gma::RewardedAd(); rewarded_ad->Initialize(GetWindowContext()); @@ -460,4 +466,4 @@ void LoadAndShowRewardedAd(const firebase::gma::AdRequest& ad_request) { // Clean up the interstitial ad. delete rewarded_ad; rewarded_ad = nullptr; -} \ No newline at end of file +} From a90467bc997ebf18e4fed3f612c952ee60a5392f Mon Sep 17 00:00:00 2001 From: "drsanta@google.com" Date: Tue, 5 Apr 2022 13:24:10 -0400 Subject: [PATCH 03/33] readme updates, formatted source --- gma/testapp/readme.md | 7 +- gma/testapp/src/android/android_main.cc | 57 +++--- .../google/firebase/example/LoggingUtils.java | 12 +- gma/testapp/src/common_main.cc | 188 +++++++++--------- gma/testapp/src/desktop/desktop_main.cc | 35 ++-- gma/testapp/src/ios/ios_main.mm | 36 ++-- gma/testapp/src/main.h | 24 +-- 7 files changed, 183 insertions(+), 176 deletions(-) diff --git a/gma/testapp/readme.md b/gma/testapp/readme.md index 018af54d..4143cb7e 100644 --- a/gma/testapp/readme.md +++ b/gma/testapp/readme.md @@ -126,8 +126,9 @@ Getting Started ### Desktop - Note: the testapp has no user interface, but the output can be viewed via - the console. Note that the GMA SDK uses a stubbed implementation on - desktop, so functionality is not expected. + the console. The GMA SDK uses a stubbed implementation on desktop, so + functionality is not expected, and the app will end waiting for the interstitial + ad to be dismissed. - Register your app with Firebase. - Create a new app on the [Firebase console](https://firebase.google.com/console/), following the above instructions for Android or iOS. @@ -176,7 +177,7 @@ Support License ------- -Copyright 2016 Google, Inc. +Copyright 2022 Google, Inc. Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for diff --git a/gma/testapp/src/android/android_main.cc b/gma/testapp/src/android/android_main.cc index 73cb30e7..7a6de455 100644 --- a/gma/testapp/src/android/android_main.cc +++ b/gma/testapp/src/android/android_main.cc @@ -17,33 +17,33 @@ #include #include -#include #include +#include -#include "main.h" // NOLINT +#include "main.h" // NOLINT // This implementation is derived from http://github.com/google/fplutil -extern "C" int common_main(int argc, const char* argv[]); +extern "C" int common_main(int argc, const char *argv[]); -static struct android_app* g_app_state = nullptr; +static struct android_app *g_app_state = nullptr; static bool g_destroy_requested = false; static bool g_started = false; static bool g_restarted = false; static pthread_mutex_t g_started_mutex; // Handle state changes from via native app glue. -static void OnAppCmd(struct android_app* app, int32_t cmd) { +static void OnAppCmd(struct android_app *app, int32_t cmd) { g_destroy_requested |= cmd == APP_CMD_DESTROY; } // Process events pending on the main thread. // Returns true when the app receives an event requesting exit. bool ProcessEvents(int msec) { - struct android_poll_source* source = nullptr; + struct android_poll_source *source = nullptr; int events; int looperId = ALooper_pollAll(msec, nullptr, &events, - reinterpret_cast(&source)); + reinterpret_cast(&source)); if (looperId >= 0 && source) { source->process(g_app_state, source); } @@ -57,7 +57,7 @@ jobject GetActivity() { return g_app_state->activity->clazz; } jobject GetWindowContext() { return g_app_state->activity->clazz; } // Find a class, attempting to load the class if it's not found. -jclass FindClass(JNIEnv* env, jobject activity_object, const char* class_name) { +jclass FindClass(JNIEnv *env, jobject activity_object, const char *class_name) { jclass class_object = env->FindClass(class_name); if (env->ExceptionCheck()) { env->ExceptionClear(); @@ -93,14 +93,13 @@ jclass FindClass(JNIEnv* env, jobject activity_object, const char* class_name) { // Vars that we need available for appending text to the log window: class LoggingUtilsData { - public: +public: LoggingUtilsData() - : logging_utils_class_(nullptr), - logging_utils_add_log_text_(0), + : logging_utils_class_(nullptr), logging_utils_add_log_text_(0), logging_utils_init_log_window_(0) {} ~LoggingUtilsData() { - JNIEnv* env = GetJniEnv(); + JNIEnv *env = GetJniEnv(); assert(env); if (logging_utils_class_) { env->DeleteGlobalRef(logging_utils_class_); @@ -108,7 +107,7 @@ class LoggingUtilsData { } void Init() { - JNIEnv* env = GetJniEnv(); + JNIEnv *env = GetJniEnv(); assert(env); jclass logging_utils_class = FindClass( @@ -130,9 +129,10 @@ class LoggingUtilsData { logging_utils_init_log_window_, GetActivity()); } - void AppendText(const char* text) { - if (logging_utils_class_ == 0) return; // haven't been initted yet - JNIEnv* env = GetJniEnv(); + void AppendText(const char *text) { + if (logging_utils_class_ == 0) + return; // haven't been initted yet + JNIEnv *env = GetJniEnv(); assert(env); jstring text_string = env->NewStringUTF(text); env->CallStaticVoidMethod(logging_utils_class_, logging_utils_add_log_text_, @@ -140,17 +140,17 @@ class LoggingUtilsData { env->DeleteLocalRef(text_string); } - private: +private: jclass logging_utils_class_; jmethodID logging_utils_add_log_text_; jmethodID logging_utils_init_log_window_; }; -LoggingUtilsData* g_logging_utils_data; +LoggingUtilsData *g_logging_utils_data; // Checks if a JNI exception has happened, and if so, logs it to the console. void CheckJNIException() { - JNIEnv* env = GetJniEnv(); + JNIEnv *env = GetJniEnv(); if (env->ExceptionCheck()) { // Get the exception text. jthrowable exception = env->ExceptionOccurred(); @@ -161,7 +161,7 @@ void CheckJNIException() { jmethodID toString = env->GetMethodID(object_class, "toString", "()Ljava/lang/String;"); jstring s = (jstring)env->CallObjectMethod(exception, toString); - const char* exception_text = env->GetStringUTFChars(s, nullptr); + const char *exception_text = env->GetStringUTFChars(s, nullptr); // Log the exception text. __android_log_print(ANDROID_LOG_INFO, FIREBASE_TESTAPP_NAME, @@ -182,7 +182,7 @@ void CheckJNIException() { } // Log a message that can be viewed in "adb logcat". -void LogMessage(const char* format, ...) { +void LogMessage(const char *format, ...) { static const int kLineBufferSize = 100; char buffer[kLineBufferSize + 2]; @@ -201,15 +201,15 @@ void LogMessage(const char* format, ...) { } // Get the JNI environment. -JNIEnv* GetJniEnv() { - JavaVM* vm = g_app_state->activity->vm; - JNIEnv* env; +JNIEnv *GetJniEnv() { + JavaVM *vm = g_app_state->activity->vm; + JNIEnv *env; jint result = vm->AttachCurrentThread(&env, nullptr); return result == JNI_OK ? env : nullptr; } // Execute common_main(), flush pending events and finish the activity. -extern "C" void android_main(struct android_app* state) { +extern "C" void android_main(struct android_app *state) { // native_app_glue spawns a new thread, calling android_main() when the // activity onStart() or onRestart() methods are called. This code handles // the case where we're re-entering this method on a different thread by @@ -236,9 +236,9 @@ extern "C" void android_main(struct android_app* state) { g_logging_utils_data->Init(); // Execute cross platform entry point. - static const char* argv[] = {FIREBASE_TESTAPP_NAME}; + static const char *argv[] = {FIREBASE_TESTAPP_NAME}; int return_value = common_main(1, argv); - (void)return_value; // Ignore the return value. + (void)return_value; // Ignore the return value. ProcessEvents(10); // Clean up logging display. @@ -246,7 +246,8 @@ extern "C" void android_main(struct android_app* state) { g_logging_utils_data = nullptr; // Finish the activity. - if (!g_restarted) ANativeActivity_finish(state->activity); + if (!g_restarted) + ANativeActivity_finish(state->activity); g_app_state->activity->vm->DetachCurrentThread(); g_started = false; diff --git a/gma/testapp/src/android/java/com/google/firebase/example/LoggingUtils.java b/gma/testapp/src/android/java/com/google/firebase/example/LoggingUtils.java index 11d67c5b..3cb37ecf 100644 --- a/gma/testapp/src/android/java/com/google/firebase/example/LoggingUtils.java +++ b/gma/testapp/src/android/java/com/google/firebase/example/LoggingUtils.java @@ -44,12 +44,12 @@ public static void initLogWindow(Activity activity) { public static void addLogText(final String text) { new Handler(Looper.getMainLooper()).post(new Runnable() { - @Override - public void run() { - if (sTextView != null) { - sTextView.append(text); - } + @Override + public void run() { + if (sTextView != null) { + sTextView.append(text); } - }); + } + }); } } diff --git a/gma/testapp/src/common_main.cc b/gma/testapp/src/common_main.cc index e5d61ed5..10a0a181 100644 --- a/gma/testapp/src/common_main.cc +++ b/gma/testapp/src/common_main.cc @@ -12,59 +12,50 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include "firebase/app.h" +#include "firebase/future.h" #include "firebase/gma.h" #include "firebase/gma/ad_view.h" #include "firebase/gma/interstitial_ad.h" #include "firebase/gma/rewarded_ad.h" #include "firebase/gma/types.h" -#include "firebase/app.h" -#include "firebase/future.h" // Thin OS abstraction layer. -#include "main.h" // NOLINT +#include "main.h" // NOLINT // A simple listener that logs changes to an AdView. class LoggingAdViewListener : public firebase::gma::AdListener { - public: +public: LoggingAdViewListener() {} - void OnAdClicked() override { - ::LogMessage("AdView ad clicked."); - } + void OnAdClicked() override { ::LogMessage("AdView ad clicked."); } - void OnAdClosed() override { - ::LogMessage("AdView ad closed."); - } + void OnAdClosed() override { ::LogMessage("AdView ad closed."); } - void OnAdImpression() override { - ::LogMessage("AdView ad impression."); - } + void OnAdImpression() override { ::LogMessage("AdView ad impression."); } - void OnAdOpened() override { - ::LogMessage("AdView ad opened."); - } + void OnAdOpened() override { ::LogMessage("AdView ad opened."); } }; // A simple listener that logs changes to an AdView's bounding box. class LoggingAdViewBoundedBoxListener : public firebase::gma::AdViewBoundingBoxListener { - public: - void OnBoundingBoxChanged(firebase::gma::AdView* ad_view, +public: + void OnBoundingBoxChanged(firebase::gma::AdView *ad_view, firebase::gma::BoundingBox box) override { ::LogMessage("AdView bounding box update x: %d y: %d " - "width: %d height: %d", box.x, box.y, box.width, box.height); + "width: %d height: %d", + box.x, box.y, box.width, box.height); } }; // A simple listener track FullScreen content changes. class LoggingFullScreenContentListener : public firebase::gma::FullScreenContentListener { - public: - LoggingFullScreenContentListener() : num_ad_dismissed_(0) { } +public: + LoggingFullScreenContentListener() : num_ad_dismissed_(0) {} - void OnAdClicked() override { - ::LogMessage("FullScreenContent ad clicked."); - } + void OnAdClicked() override { ::LogMessage("FullScreenContent ad clicked."); } void OnAdDismissedFullScreenContent() override { ::LogMessage("FullScreenContent ad dismissed."); @@ -72,9 +63,10 @@ class LoggingFullScreenContentListener } void OnAdFailedToShowFullScreenContent( - const firebase::gma::AdError& ad_error) override { + const firebase::gma::AdError &ad_error) override { ::LogMessage("FullScreenContent ad failed to show full screen content," - " AdErrorCode: %d", ad_error.code()); + " AdErrorCode: %d", + ad_error.code()); } void OnAdImpression() override { @@ -87,47 +79,47 @@ class LoggingFullScreenContentListener uint32_t num_ad_dismissed() const { return num_ad_dismissed_; } - private: +private: uint32_t num_ad_dismissed_; }; // A simple listener track UserEarnedReward events. class LoggingUserEarnedRewardListener : public firebase::gma::UserEarnedRewardListener { - public: - LoggingUserEarnedRewardListener() { } +public: + LoggingUserEarnedRewardListener() {} - void OnUserEarnedReward(const firebase::gma::AdReward& reward) override { - ::LogMessage("User earned reward amount: %d type: %s", - reward.amount(), reward.type().c_str()); + void OnUserEarnedReward(const firebase::gma::AdReward &reward) override { + ::LogMessage("User earned reward amount: %d type: %s", reward.amount(), + reward.type().c_str()); } }; // A simple listener track ad pay events. class LoggingPaidEventListener : public firebase::gma::PaidEventListener { - public: - LoggingPaidEventListener() { } +public: + LoggingPaidEventListener() {} - void OnPaidEvent(const firebase::gma::AdValue& value) override { + void OnPaidEvent(const firebase::gma::AdValue &value) override { ::LogMessage("PaidEvent value: %lld currency_code: %s", - value.value_micros(), value.currency_code().c_str()); - } + value.value_micros(), value.currency_code().c_str()); + } }; -void LoadAndShowAdView(const firebase::gma::AdRequest& ad_request); -void LoadAndShowInterstitialAd(const firebase::gma::AdRequest& ad_request); -void LoadAndShowRewardedAd(const firebase::gma::AdRequest& ad_request); +void LoadAndShowAdView(const firebase::gma::AdRequest &ad_request); +void LoadAndShowInterstitialAd(const firebase::gma::AdRequest &ad_request); +void LoadAndShowRewardedAd(const firebase::gma::AdRequest &ad_request); // These ad units IDs have been created specifically for testing, and will // always return test ads. #if defined(__ANDROID__) -const char* kBannerAdUnit = "ca-app-pub-3940256099942544/6300978111"; -const char* kInterstitialAdUnit = "ca-app-pub-3940256099942544/1033173712"; -const char* kRewardedAdUnit = "ca-app-pub-3940256099942544/5224354917"; +const char *kBannerAdUnit = "ca-app-pub-3940256099942544/6300978111"; +const char *kInterstitialAdUnit = "ca-app-pub-3940256099942544/1033173712"; +const char *kRewardedAdUnit = "ca-app-pub-3940256099942544/5224354917"; #else -const char* kBannerAdUnit = "ca-app-pub-3940256099942544/2934735716"; -const char* kInterstitialAdUnit = "ca-app-pub-3940256099942544/4411468910"; -const char* kRewardedAdUnit = "ca-app-pub-3940256099942544/1712485313"; +const char *kBannerAdUnit = "ca-app-pub-3940256099942544/2934735716"; +const char *kInterstitialAdUnit = "ca-app-pub-3940256099942544/4411468910"; +const char *kRewardedAdUnit = "ca-app-pub-3940256099942544/1712485313"; #endif // Sample keywords to use in making the request. @@ -138,10 +130,10 @@ const std::vector kTestDeviceIDs = { "2077ef9a63d2b398840261c8221a0c9b", "098fe087d987c9a878965454a65654d7"}; #if defined(ANDROID) -static const char* kAdNetworkExtrasClassName = +static const char *kAdNetworkExtrasClassName = "com/google/ads/mediation/admob/AdMobAdapter"; #else -static const char* kAdNetworkExtrasClassName = "GADExtras"; +static const char *kAdNetworkExtrasClassName = "GADExtras"; #endif // Function to wait for the completion of a future, and log the error @@ -160,15 +152,15 @@ static void WaitForFutureCompletion(firebase::FutureBase future) { } // Inittialize GMA, load a Banner, Interstitial and Rewarded Ad. -extern "C" int common_main(int argc, const char* argv[]) { - firebase::App* app; +extern "C" int common_main(int argc, const char *argv[]) { + firebase::App *app; LogMessage("Initializing Firebase App."); #if defined(__ANDROID__) app = ::firebase::App::Create(GetJniEnv(), GetActivity()); #else app = ::firebase::App::Create(); -#endif // defined(__ANDROID__) +#endif // defined(__ANDROID__) LogMessage("Created the Firebase App %x.", static_cast(reinterpret_cast(app))); @@ -177,20 +169,22 @@ extern "C" int common_main(int argc, const char* argv[]) { firebase::gma::Initialize(*app); WaitForFutureCompletion(firebase::gma::InitializeLastResult()); - if(firebase::gma::InitializeLastResult().error() != firebase::gma::kAdErrorCodeNone) { - // Initialization Failure. The error was already logged in WaitForFutureCompletion, - // so simply exit here. + if (firebase::gma::InitializeLastResult().error() != + firebase::gma::kAdErrorCodeNone) { + // Initialization Failure. The error was already logged in + // WaitForFutureCompletion, so simply exit here. return -1; } // Log mediation adapter initialization status. for (auto adapter_status : firebase::gma::GetInitializationStatus().GetAdapterStatusMap()) { - LogMessage("GMA Mediation Adapter '%s' %s (latency %d ms): %s", - adapter_status.first.c_str(), - (adapter_status.second.is_initialized() ? "loaded" : "NOT loaded"), - adapter_status.second.latency(), - adapter_status.second.description().c_str()); + LogMessage( + "GMA Mediation Adapter '%s' %s (latency %d ms): %s", + adapter_status.first.c_str(), + (adapter_status.second.is_initialized() ? "loaded" : "NOT loaded"), + adapter_status.second.latency(), + adapter_status.second.description().c_str()); } // Configure test device ids before loading ads. @@ -206,11 +200,11 @@ extern "C" int common_main(int argc, const char* argv[]) { firebase::gma::RequestConfiguration request_configuration; request_configuration.test_device_ids = kTestDeviceIDs; firebase::gma::SetRequestConfiguration(request_configuration); - - // + + // // Load and Display a Banner Ad using AdView. // - + // Create an AdRequest. firebase::gma::AdRequest ad_request; @@ -223,7 +217,7 @@ extern "C" int common_main(int argc, const char* argv[]) { // "Extra" key value pairs can be added to the request as well. Typically // these are used when testing new features. ad_request.add_extra(kAdNetworkExtrasClassName, "the_name_of_an_extra", - "the_value_for_that_extra"); + "the_value_for_that_extra"); LoadAndShowAdView(ad_request); LoadAndShowInterstitialAd(ad_request); @@ -235,16 +229,17 @@ extern "C" int common_main(int argc, const char* argv[]) { delete app; // Wait until the user kills the app. - while (!ProcessEvents(1000)) { } + while (!ProcessEvents(1000)) { + } return 0; } -void LoadAndShowAdView(const firebase::gma::AdRequest& ad_request) { +void LoadAndShowAdView(const firebase::gma::AdRequest &ad_request) { LogMessage("\nLoad and show a banner ad in an AdView:"); LogMessage("==="); // Initialize an AdView. - firebase::gma::AdView* ad_view = new firebase::gma::AdView(); + firebase::gma::AdView *ad_view = new firebase::gma::AdView(); const firebase::gma::AdSize banner_ad_size = firebase::gma::AdSize::kBanner; ad_view->Initialize(GetWindowContext(), kBannerAdUnit, banner_ad_size); @@ -253,9 +248,9 @@ void LoadAndShowAdView(const firebase::gma::AdRequest& ad_request) { // Check for errors. if (ad_view->InitializeLastResult().error() != - firebase::gma::kAdErrorCodeNone) { + firebase::gma::kAdErrorCodeNone) { LogMessage("AdView initalization failed, error code: %d", - ad_view->InitializeLastResult().error()); + ad_view->InitializeLastResult().error()); delete ad_view; ad_view = nullptr; return; @@ -268,7 +263,7 @@ void LoadAndShowAdView(const firebase::gma::AdRequest& ad_request) { ad_view->SetPaidEventListener(&paid_event_listener); LoggingAdViewBoundedBoxListener bounding_box_listener; ad_view->SetBoundingBoxListener(&bounding_box_listener); - + // Load an ad. ad_view->LoadAd(ad_request); WaitForFutureCompletion(ad_view->LoadAdLastResult()); @@ -276,12 +271,13 @@ void LoadAndShowAdView(const firebase::gma::AdRequest& ad_request) { // Check for errors. if (ad_view->LoadAdLastResult().error() != firebase::gma::kAdErrorCodeNone) { // Log information as to why the loadAd request failed. - const firebase::gma::AdResult* result_ptr = - ad_view->LoadAdLastResult().result(); + const firebase::gma::AdResult *result_ptr = + ad_view->LoadAdLastResult().result(); if (result_ptr != nullptr) { LogMessage("AdView::loadAd Failure - Code: %d Message: %s Domain: %s", - result_ptr->ad_error().code(), result_ptr->ad_error().message().c_str(), - result_ptr->ad_error().domain().c_str()); + result_ptr->ad_error().code(), + result_ptr->ad_error().message().c_str(), + result_ptr->ad_error().domain().c_str()); } WaitForFutureCompletion(ad_view->Destroy()); delete ad_view; @@ -292,7 +288,7 @@ void LoadAndShowAdView(const firebase::gma::AdRequest& ad_request) { // Log the loaded ad's dimensions. const firebase::gma::AdSize ad_size = ad_view->ad_size(); LogMessage("AdView loaded ad width: %d height: %d", ad_size.width(), - ad_size.height()); + ad_size.height()); // Show the ad. LogMessage("Showing the banner ad."); @@ -352,18 +348,20 @@ void LoadAndShowAdView(const firebase::gma::AdRequest& ad_request) { ad_view = nullptr; } -void LoadAndShowInterstitialAd(const firebase::gma::AdRequest& ad_request) { +void LoadAndShowInterstitialAd(const firebase::gma::AdRequest &ad_request) { LogMessage("\nLoad and show an interstitial ad:"); LogMessage("==="); // Initialize an InterstitialAd. - firebase::gma::InterstitialAd* interstitial_ad = new firebase::gma::InterstitialAd(); + firebase::gma::InterstitialAd *interstitial_ad = + new firebase::gma::InterstitialAd(); interstitial_ad->Initialize(GetWindowContext()); // Block until the interstitial ad completes initialization. WaitForFutureCompletion(interstitial_ad->InitializeLastResult()); // Check for errors. - if (interstitial_ad->InitializeLastResult().error() != firebase::gma::kAdErrorCodeNone) { + if (interstitial_ad->InitializeLastResult().error() != + firebase::gma::kAdErrorCodeNone) { delete interstitial_ad; interstitial_ad = nullptr; return; @@ -380,14 +378,17 @@ void LoadAndShowInterstitialAd(const firebase::gma::AdRequest& ad_request) { WaitForFutureCompletion(interstitial_ad->LoadAdLastResult()); // Check for errors. - if (interstitial_ad->LoadAdLastResult().error() != firebase::gma::kAdErrorCodeNone) { + if (interstitial_ad->LoadAdLastResult().error() != + firebase::gma::kAdErrorCodeNone) { // Log information as to why the loadAd request failed. - const firebase::gma::AdResult* result_ptr = - interstitial_ad->LoadAdLastResult().result(); + const firebase::gma::AdResult *result_ptr = + interstitial_ad->LoadAdLastResult().result(); if (result_ptr != nullptr) { - LogMessage("InterstitialAd::loadAd Failure - Code: %d Message: %s Domain: %s", - result_ptr->ad_error().code(), result_ptr->ad_error().message().c_str(), - result_ptr->ad_error().domain().c_str()); + LogMessage( + "InterstitialAd::loadAd Failure - Code: %d Message: %s Domain: %s", + result_ptr->ad_error().code(), + result_ptr->ad_error().message().c_str(), + result_ptr->ad_error().domain().c_str()); } delete interstitial_ad; interstitial_ad = nullptr; @@ -410,18 +411,19 @@ void LoadAndShowInterstitialAd(const firebase::gma::AdRequest& ad_request) { } // WIP -void LoadAndShowRewardedAd(const firebase::gma::AdRequest& ad_request) { +void LoadAndShowRewardedAd(const firebase::gma::AdRequest &ad_request) { LogMessage("\nLoad and show a rewarded ad:"); LogMessage("==="); // Initialize a RewardedAd. - firebase::gma::RewardedAd* rewarded_ad = new firebase::gma::RewardedAd(); + firebase::gma::RewardedAd *rewarded_ad = new firebase::gma::RewardedAd(); rewarded_ad->Initialize(GetWindowContext()); // Block until the interstitial ad completes initialization. WaitForFutureCompletion(rewarded_ad->InitializeLastResult()); // Check for errors. - if (rewarded_ad->InitializeLastResult().error() != firebase::gma::kAdErrorCodeNone) { + if (rewarded_ad->InitializeLastResult().error() != + firebase::gma::kAdErrorCodeNone) { delete rewarded_ad; rewarded_ad = nullptr; return; @@ -438,17 +440,19 @@ void LoadAndShowRewardedAd(const firebase::gma::AdRequest& ad_request) { WaitForFutureCompletion(rewarded_ad->LoadAdLastResult()); // Check for errors. - if (rewarded_ad->LoadAdLastResult().error() != firebase::gma::kAdErrorCodeNone) { + if (rewarded_ad->LoadAdLastResult().error() != + firebase::gma::kAdErrorCodeNone) { // Log information as to why the loadAd request failed. - const firebase::gma::AdResult* result_ptr = - rewarded_ad->LoadAdLastResult().result(); + const firebase::gma::AdResult *result_ptr = + rewarded_ad->LoadAdLastResult().result(); if (result_ptr != nullptr) { LogMessage("RewardedAd::loadAd Failure - Code: %d Message: %s Domain: %s", - result_ptr->ad_error().code(), result_ptr->ad_error().message().c_str(), - result_ptr->ad_error().domain().c_str()); + result_ptr->ad_error().code(), + result_ptr->ad_error().message().c_str(), + result_ptr->ad_error().domain().c_str()); } delete rewarded_ad; - rewarded_ad = nullptr; + rewarded_ad = nullptr; return; } diff --git a/gma/testapp/src/desktop/desktop_main.cc b/gma/testapp/src/desktop/desktop_main.cc index 0220c688..0a318786 100644 --- a/gma/testapp/src/desktop/desktop_main.cc +++ b/gma/testapp/src/desktop/desktop_main.cc @@ -21,16 +21,16 @@ #define chdir _chdir #else #include -#endif // _WIN32 +#endif // _WIN32 #ifdef _WIN32 #include -#endif // _WIN32 +#endif // _WIN32 #include #include -#include "main.h" // NOLINT +#include "main.h" // NOLINT // The TO_STRING macro is useful for command line defined strings as the quotes // get stripped. @@ -42,9 +42,9 @@ #define FIREBASE_CONFIG_STRING TO_STRING(FIREBASE_CONFIG) #else #define FIREBASE_CONFIG_STRING "" -#endif // FIREBASE_CONFIG +#endif // FIREBASE_CONFIG -extern "C" int common_main(int argc, const char* argv[]); +extern "C" int common_main(int argc, const char *argv[]); static bool quit = false; @@ -58,22 +58,20 @@ static BOOL WINAPI SignalHandler(DWORD event) { } #else static void SignalHandler(int /* ignored */) { quit = true; } -#endif // _WIN32 +#endif // _WIN32 bool ProcessEvents(int msec) { #ifdef _WIN32 Sleep(msec); #else usleep(msec * 1000); -#endif // _WIN32 +#endif // _WIN32 return quit; } -std::string PathForResource() { - return std::string(); -} +std::string PathForResource() { return std::string(); } -void LogMessage(const char* format, ...) { +void LogMessage(const char *format, ...) { va_list list; va_start(list, format); vprintf(format, list); @@ -86,25 +84,26 @@ WindowContext GetWindowContext() { return nullptr; } // Change the current working directory to the directory containing the // specified file. -void ChangeToFileDirectory(const char* file_path) { +void ChangeToFileDirectory(const char *file_path) { std::string path(file_path); std::replace(path.begin(), path.end(), '\\', '/'); auto slash = path.rfind('/'); if (slash != std::string::npos) { std::string directory = path.substr(0, slash); - if (!directory.empty()) chdir(directory.c_str()); + if (!directory.empty()) + chdir(directory.c_str()); } } -int main(int argc, const char* argv[]) { - ChangeToFileDirectory( - FIREBASE_CONFIG_STRING[0] != '\0' ? - FIREBASE_CONFIG_STRING : argv[0]); // NOLINT +int main(int argc, const char *argv[]) { + ChangeToFileDirectory(FIREBASE_CONFIG_STRING[0] != '\0' + ? FIREBASE_CONFIG_STRING + : argv[0]); // NOLINT #ifdef _WIN32 SetConsoleCtrlHandler((PHANDLER_ROUTINE)SignalHandler, TRUE); #else signal(SIGINT, SignalHandler); -#endif // _WIN32 +#endif // _WIN32 return common_main(argc, argv); } diff --git a/gma/testapp/src/ios/ios_main.mm b/gma/testapp/src/ios/ios_main.mm index 6ccb2de5..4c226d5e 100644 --- a/gma/testapp/src/ios/ios_main.mm +++ b/gma/testapp/src/ios/ios_main.mm @@ -18,9 +18,9 @@ #include "main.h" -extern "C" int common_main(int argc, const char* argv[]); +extern "C" int common_main(int argc, const char *argv[]); -@interface AppDelegate : UIResponder +@interface AppDelegate : UIResponder @property(nonatomic, strong) UIWindow *window; @@ -42,33 +42,35 @@ @implementation FTAViewController - (void)viewDidLoad { [super viewDidLoad]; g_parent_view = self.view; - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - const char *argv[] = {FIREBASE_TESTAPP_NAME}; - [g_shutdown_signal lock]; - g_exit_status = common_main(1, argv); - [g_shutdown_complete signal]; - }); + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), + ^{ + const char *argv[] = {FIREBASE_TESTAPP_NAME}; + [g_shutdown_signal lock]; + g_exit_status = common_main(1, argv); + [g_shutdown_complete signal]; + }); } @end bool ProcessEvents(int msec) { [g_shutdown_signal - waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:static_cast(msec) / 1000.0f]]; + waitUntilDate:[NSDate + dateWithTimeIntervalSinceNow:static_cast(msec) / + 1000.0f]]; return g_shutdown; } -WindowContext GetWindowContext() { - return g_parent_view; -} +WindowContext GetWindowContext() { return g_parent_view; } // Log a message that can be viewed in the console. -void LogMessage(const char* format, ...) { +void LogMessage(const char *format, ...) { va_list args; NSString *formatString = @(format); va_start(args, format); - NSString *message = [[NSString alloc] initWithFormat:formatString arguments:args]; + NSString *message = [[NSString alloc] initWithFormat:formatString + arguments:args]; va_end(args); NSLog(@"%@", message); @@ -79,7 +81,7 @@ void LogMessage(const char* format, ...) { }); } -int main(int argc, char* argv[]) { +int main(int argc, char *argv[]) { @autoreleasepool { UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } @@ -88,8 +90,8 @@ int main(int argc, char* argv[]) { @implementation AppDelegate -- (BOOL)application:(UIApplication*)application - didFinishLaunchingWithOptions:(NSDictionary*)launchOptions { +- (BOOL)application:(UIApplication *)application + didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { g_shutdown_complete = [[NSCondition alloc] init]; g_shutdown_signal = [[NSCondition alloc] init]; [g_shutdown_complete lock]; diff --git a/gma/testapp/src/main.h b/gma/testapp/src/main.h index 2eda2c10..d41017a7 100644 --- a/gma/testapp/src/main.h +++ b/gma/testapp/src/main.h @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef FIREBASE_TESTAPP_MAIN_H_ // NOLINT -#define FIREBASE_TESTAPP_MAIN_H_ // NOLINT +#ifndef FIREBASE_TESTAPP_MAIN_H_ // NOLINT +#define FIREBASE_TESTAPP_MAIN_H_ // NOLINT #if defined(__ANDROID__) #include @@ -21,18 +21,18 @@ #elif defined(__APPLE__) extern "C" { #include -} // extern "C" -#endif // __ANDROID__ +} // extern "C" +#endif // __ANDROID__ // Defined using -DANDROID_MAIN_APP_NAME=some_app_name when compiling this // file. #ifndef FIREBASE_TESTAPP_NAME #define FIREBASE_TESTAPP_NAME "android_main" -#endif // FIREBASE_TESTAPP_NAME +#endif // FIREBASE_TESTAPP_NAME // Cross platform logging method. // Implemented by android/android_main.cc or ios/ios_main.mm. -extern "C" void LogMessage(const char* format, ...); +extern "C" void LogMessage(const char *format, ...); // Platform-independent method to flush pending events for the main thread. // Returns true when an event requesting program-exit is received. @@ -41,23 +41,23 @@ bool ProcessEvents(int msec); // WindowContext represents the handle to the parent window. It's type // (and usage) vary based on the OS. #if defined(__ANDROID__) -typedef jobject WindowContext; // A jobject to the Java Activity. +typedef jobject WindowContext; // A jobject to the Java Activity. #elif defined(__APPLE__) -typedef id WindowContext; // A pointer to an iOS UIView. +typedef id WindowContext; // A pointer to an iOS UIView. #else -typedef void* WindowContext; // A void* for any other environments. +typedef void *WindowContext; // A void* for any other environments. #endif #if defined(__ANDROID__) // Get the JNI environment. -JNIEnv* GetJniEnv(); +JNIEnv *GetJniEnv(); // Get the activity. jobject GetActivity(); -#endif // defined(__ANDROID__) +#endif // defined(__ANDROID__) // Returns a variable that describes the window context for the app. On Android // this will be a jobject pointing to the Activity. On iOS, it's an id pointing // to the root view of the view controller. WindowContext GetWindowContext(); -#endif // FIREBASE_TESTAPP_MAIN_H_ // NOLINT +#endif // FIREBASE_TESTAPP_MAIN_H_ // NOLINT From b56f77712b70ab9253215869d28afa7ce33736b7 Mon Sep 17 00:00:00 2001 From: "drsanta@google.com" Date: Sat, 25 Jun 2022 10:40:40 -0400 Subject: [PATCH 04/33] update gradle wrapper properties --- gma/testapp/gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gma/testapp/gradle/wrapper/gradle-wrapper.properties b/gma/testapp/gradle/wrapper/gradle-wrapper.properties index 6349b295..9e09cdb6 100644 --- a/gma/testapp/gradle/wrapper/gradle-wrapper.properties +++ b/gma/testapp/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip +distributionUrl=https://services.gradle.org/distributions/gradle-5.6.4-all.zip From fa6ffee5e199ad2080ab85afd55303615a099d4b Mon Sep 17 00:00:00 2001 From: DellaBitta Date: Fri, 1 Jul 2022 13:12:50 -0400 Subject: [PATCH 05/33] Direct users to create branches off of main (#101) Update user docs to have contributions be branched off of main. --- CONTRIBUTING.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 02f251e9..c02eddc2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -109,7 +109,7 @@ Before you submit your pull request consider the following guidelines: * Make your changes in a new git branch: ```shell - git checkout -b my-fix-branch master + git checkout -b my-fix-branch main ``` * Create your patch, **including appropriate test cases**. @@ -133,14 +133,14 @@ Before you submit your pull request consider the following guidelines: git push origin my-fix-branch ``` -* In GitHub, send a pull request to `firebase/quickstart-cpp:master`. +* In GitHub, send a pull request to `firebase/quickstart-cpp:main`. * If we suggest changes then: * Make the required updates. * Rebase your branch and force push to your GitHub repository (this will update your Pull Request): ```shell - git rebase master -i + git rebase main -i git push origin my-fix-branch -f ``` @@ -158,10 +158,10 @@ the changes from the main (upstream) repository: git push origin --delete my-fix-branch ``` -* Check out the master branch: +* Check out the main branch: ```shell - git checkout master -f + git checkout main -f ``` * Delete the local branch: @@ -170,10 +170,10 @@ the changes from the main (upstream) repository: git branch -D my-fix-branch ``` -* Update your master with the latest upstream version: +* Update your main with the latest upstream version: ```shell - git pull --ff upstream master + git pull --ff upstream main ``` ## Coding Rules @@ -186,7 +186,7 @@ Please sign our [Contributor License Agreement][google-cla] (CLA) before sending pull requests. For any code changes to be accepted, the CLA must be signed. It's a quick process, we promise! -*This guide was inspired by the [AngularJS contribution guidelines](https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md).* +*This guide was inspired by the [AngularJS contribution guidelines](https://github.com/angular/angular.js/blob/main/CONTRIBUTING.md).* [github]: https://github.com/firebase/quickstart-cpp [google-cla]: https://cla.developers.google.com From 8e20c72271974b8cadab80ec3ac300353bb0500d Mon Sep 17 00:00:00 2001 From: DellaBitta Date: Wed, 6 Jul 2022 15:16:23 -0400 Subject: [PATCH 06/33] Update Android build config and source fixes (#102) A pass of the android build process to ensure the testapps can build prior to adding CI to the repo. --- admob/testapp/build.gradle | 4 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- analytics/testapp/build.gradle | 6 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- analytics/testapp/src/common_main.cc | 6 +- auth/testapp/build.gradle | 9 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- database/testapp/build.gradle | 8 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- dynamic_links/testapp/build.gradle | 6 +- .../gradle/wrapper/gradle-wrapper.properties | 3 +- firestore/testapp/build.gradle | 14 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- functions/testapp/build.gradle | 8 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- messaging/testapp/build.gradle | 15 +-- .../gradle/wrapper/gradle-wrapper.properties | 2 +- remote_config/testapp/build.gradle | 8 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- remote_config/testapp/src/common_main.cc | 124 +++++++++--------- storage/testapp/build.gradle | 8 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- 22 files changed, 137 insertions(+), 100 deletions(-) diff --git a/admob/testapp/build.gradle b/admob/testapp/build.gradle index 4562ea56..a6697fca 100644 --- a/admob/testapp/build.gradle +++ b/admob/testapp/build.gradle @@ -6,7 +6,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.2.1' + classpath 'com.android.tools.build:gradle:3.3.3' classpath 'com.google.gms:google-services:4.0.1' } } @@ -36,7 +36,7 @@ android { defaultConfig { applicationId 'com.google.android.admob.testapp' - minSdkVersion 16 + minSdkVersion 26 targetSdkVersion 28 versionCode 1 versionName '1.0' diff --git a/admob/testapp/gradle/wrapper/gradle-wrapper.properties b/admob/testapp/gradle/wrapper/gradle-wrapper.properties index 35732b09..9e09cdb6 100644 --- a/admob/testapp/gradle/wrapper/gradle-wrapper.properties +++ b/admob/testapp/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +distributionUrl=https://services.gradle.org/distributions/gradle-5.6.4-all.zip diff --git a/analytics/testapp/build.gradle b/analytics/testapp/build.gradle index 95aa238b..6928009e 100644 --- a/analytics/testapp/build.gradle +++ b/analytics/testapp/build.gradle @@ -22,6 +22,10 @@ allprojects { apply plugin: 'com.android.application' android { + compileOptions { + sourceCompatibility 1.8 + targetCompatibility 1.8 + } compileSdkVersion 28 buildToolsVersion '28.0.3' @@ -36,7 +40,7 @@ android { defaultConfig { applicationId 'com.google.android.analytics.testapp' - minSdkVersion 16 + minSdkVersion 19 targetSdkVersion 28 versionCode 1 versionName '1.0' diff --git a/analytics/testapp/gradle/wrapper/gradle-wrapper.properties b/analytics/testapp/gradle/wrapper/gradle-wrapper.properties index 35732b09..9e09cdb6 100644 --- a/analytics/testapp/gradle/wrapper/gradle-wrapper.properties +++ b/analytics/testapp/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +distributionUrl=https://services.gradle.org/distributions/gradle-5.6.4-all.zip diff --git a/analytics/testapp/src/common_main.cc b/analytics/testapp/src/common_main.cc index ee7f15da..e063e416 100644 --- a/analytics/testapp/src/common_main.cc +++ b/analytics/testapp/src/common_main.cc @@ -63,9 +63,9 @@ extern "C" int common_main(int argc, const char* argv[]) { // Set the user ID. analytics::SetUserId("uber_user_510"); - LogMessage("Set current screen."); - // Set the user's current screen. - analytics::SetCurrentScreen("Firebase Analytics C++ testapp", "testapp"); + LogMessage("Log current screen."); + // Log the user's current screen. + analytics::LogEvent(analytics::kEventScreenView, "Firebase Analytics C++ testapp", "testapp" ); // Log an event with no parameters. LogMessage("Log login event."); diff --git a/auth/testapp/build.gradle b/auth/testapp/build.gradle index 64cdb605..7a6525e4 100644 --- a/auth/testapp/build.gradle +++ b/auth/testapp/build.gradle @@ -6,7 +6,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.2.1' + classpath 'com.android.tools.build:gradle:3.3.3' classpath 'com.google.gms:google-services:4.0.1' } } @@ -22,6 +22,11 @@ allprojects { apply plugin: 'com.android.application' android { + compileOptions { + sourceCompatibility 1.8 + targetCompatibility 1.8 + } + compileSdkVersion 28 buildToolsVersion '28.0.3' @@ -36,7 +41,7 @@ android { defaultConfig { applicationId 'com.google.android.auth.testapp' - minSdkVersion 16 + minSdkVersion 19 targetSdkVersion 28 versionCode 1 versionName '1.0' diff --git a/auth/testapp/gradle/wrapper/gradle-wrapper.properties b/auth/testapp/gradle/wrapper/gradle-wrapper.properties index 35732b09..9e09cdb6 100644 --- a/auth/testapp/gradle/wrapper/gradle-wrapper.properties +++ b/auth/testapp/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +distributionUrl=https://services.gradle.org/distributions/gradle-5.6.4-all.zip diff --git a/database/testapp/build.gradle b/database/testapp/build.gradle index 464c7d34..b109c884 100644 --- a/database/testapp/build.gradle +++ b/database/testapp/build.gradle @@ -6,7 +6,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.2.1' + classpath 'com.android.tools.build:gradle:3.3.3' classpath 'com.google.gms:google-services:4.0.1' } } @@ -22,6 +22,10 @@ allprojects { apply plugin: 'com.android.application' android { + compileOptions { + sourceCompatibility 1.8 + targetCompatibility 1.8 + } compileSdkVersion 28 buildToolsVersion '28.0.3' @@ -36,7 +40,7 @@ android { defaultConfig { applicationId 'com.google.firebase.cpp.database.testapp' - minSdkVersion 16 + minSdkVersion 19 targetSdkVersion 28 versionCode 1 versionName '1.0' diff --git a/database/testapp/gradle/wrapper/gradle-wrapper.properties b/database/testapp/gradle/wrapper/gradle-wrapper.properties index 35732b09..9e09cdb6 100644 --- a/database/testapp/gradle/wrapper/gradle-wrapper.properties +++ b/database/testapp/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +distributionUrl=https://services.gradle.org/distributions/gradle-5.6.4-all.zip diff --git a/dynamic_links/testapp/build.gradle b/dynamic_links/testapp/build.gradle index c3961d97..bf4eff96 100644 --- a/dynamic_links/testapp/build.gradle +++ b/dynamic_links/testapp/build.gradle @@ -22,6 +22,10 @@ allprojects { apply plugin: 'com.android.application' android { + compileOptions { + sourceCompatibility 1.8 + targetCompatibility 1.8 + } compileSdkVersion 28 buildToolsVersion '28.0.3' @@ -36,7 +40,7 @@ android { defaultConfig { applicationId 'com.google.android.dynamiclinks.testapp' - minSdkVersion 16 + minSdkVersion 19 targetSdkVersion 28 versionCode 1 versionName '1.0' diff --git a/dynamic_links/testapp/gradle/wrapper/gradle-wrapper.properties b/dynamic_links/testapp/gradle/wrapper/gradle-wrapper.properties index 35732b09..5aa7f761 100644 --- a/dynamic_links/testapp/gradle/wrapper/gradle-wrapper.properties +++ b/dynamic_links/testapp/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +distributionUrl=https://services.gradle.org/distributions/gradle-5.6.4-all.zip + diff --git a/firestore/testapp/build.gradle b/firestore/testapp/build.gradle index 37ed5818..11751d34 100644 --- a/firestore/testapp/build.gradle +++ b/firestore/testapp/build.gradle @@ -6,7 +6,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.2.1' + classpath 'com.android.tools.build:gradle:3.3.3' classpath 'com.google.gms:google-services:4.0.1' } } @@ -22,8 +22,12 @@ allprojects { apply plugin: 'com.android.application' android { - compileSdkVersion 29 - buildToolsVersion "29.0.0" + compileOptions { + sourceCompatibility 1.8 + targetCompatibility 1.8 + } + compileSdkVersion 28 + buildToolsVersion "28.0.3" sourceSets { main { @@ -36,8 +40,8 @@ android { defaultConfig { applicationId 'com.google.firebase.cpp.firestore.testapp' - minSdkVersion 16 - targetSdkVersion 29 + minSdkVersion 19 + targetSdkVersion 28 versionCode 1 versionName '1.0' externalNativeBuild.cmake { diff --git a/firestore/testapp/gradle/wrapper/gradle-wrapper.properties b/firestore/testapp/gradle/wrapper/gradle-wrapper.properties index 35732b09..9e09cdb6 100644 --- a/firestore/testapp/gradle/wrapper/gradle-wrapper.properties +++ b/firestore/testapp/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +distributionUrl=https://services.gradle.org/distributions/gradle-5.6.4-all.zip diff --git a/functions/testapp/build.gradle b/functions/testapp/build.gradle index 28d47fc1..70632871 100644 --- a/functions/testapp/build.gradle +++ b/functions/testapp/build.gradle @@ -6,7 +6,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.2.1' + classpath 'com.android.tools.build:gradle:3.3.3' classpath 'com.google.gms:google-services:4.0.1' } } @@ -22,6 +22,10 @@ allprojects { apply plugin: 'com.android.application' android { + compileOptions { + sourceCompatibility 1.8 + targetCompatibility 1.8 + } compileSdkVersion 28 buildToolsVersion '28.0.3' @@ -36,7 +40,7 @@ android { defaultConfig { applicationId 'com.google.firebase.cpp.functions.testapp' - minSdkVersion 16 + minSdkVersion 19 targetSdkVersion 28 versionCode 1 versionName '1.0' diff --git a/functions/testapp/gradle/wrapper/gradle-wrapper.properties b/functions/testapp/gradle/wrapper/gradle-wrapper.properties index 35732b09..9e09cdb6 100644 --- a/functions/testapp/gradle/wrapper/gradle-wrapper.properties +++ b/functions/testapp/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +distributionUrl=https://services.gradle.org/distributions/gradle-5.6.4-all.zip diff --git a/messaging/testapp/build.gradle b/messaging/testapp/build.gradle index 0971c57b..d0565e63 100644 --- a/messaging/testapp/build.gradle +++ b/messaging/testapp/build.gradle @@ -6,7 +6,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.2.1' + classpath 'com.android.tools.build:gradle:3.3.3' classpath 'com.google.gms:google-services:4.0.1' } } @@ -22,16 +22,13 @@ allprojects { apply plugin: 'com.android.application' android { + compileOptions { + sourceCompatibility 1.8 + targetCompatibility 1.8 + } compileSdkVersion 28 buildToolsVersion '28.0.3' - android { - compileOptions { - sourceCompatibility 1.8 - targetCompatibility 1.8 - } - } - sourceSets { main { jniLibs.srcDirs = ['libs'] @@ -43,7 +40,7 @@ android { defaultConfig { applicationId 'com.google.android.messaging.testapp' - minSdkVersion 16 + minSdkVersion 19 targetSdkVersion 28 versionCode 1 versionName '1.0' diff --git a/messaging/testapp/gradle/wrapper/gradle-wrapper.properties b/messaging/testapp/gradle/wrapper/gradle-wrapper.properties index 35732b09..9e09cdb6 100644 --- a/messaging/testapp/gradle/wrapper/gradle-wrapper.properties +++ b/messaging/testapp/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +distributionUrl=https://services.gradle.org/distributions/gradle-5.6.4-all.zip diff --git a/remote_config/testapp/build.gradle b/remote_config/testapp/build.gradle index 650a11af..acce9ee7 100644 --- a/remote_config/testapp/build.gradle +++ b/remote_config/testapp/build.gradle @@ -6,7 +6,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.2.1' + classpath 'com.android.tools.build:gradle:3.3.3' classpath 'com.google.gms:google-services:4.0.1' } } @@ -22,6 +22,10 @@ allprojects { apply plugin: 'com.android.application' android { + compileOptions { + sourceCompatibility 1.8 + targetCompatibility 1.8 + } compileSdkVersion 28 buildToolsVersion '28.0.3' @@ -36,7 +40,7 @@ android { defaultConfig { applicationId 'com.google.android.remoteconfig.testapp' - minSdkVersion 16 + minSdkVersion 19 targetSdkVersion 28 versionCode 1 versionName '1.0' diff --git a/remote_config/testapp/gradle/wrapper/gradle-wrapper.properties b/remote_config/testapp/gradle/wrapper/gradle-wrapper.properties index 35732b09..9e09cdb6 100644 --- a/remote_config/testapp/gradle/wrapper/gradle-wrapper.properties +++ b/remote_config/testapp/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +distributionUrl=https://services.gradle.org/distributions/gradle-5.6.4-all.zip diff --git a/remote_config/testapp/src/common_main.cc b/remote_config/testapp/src/common_main.cc index 66a7039e..3bc35e2c 100644 --- a/remote_config/testapp/src/common_main.cc +++ b/remote_config/testapp/src/common_main.cc @@ -19,22 +19,26 @@ #include "firebase/util.h" // Thin OS abstraction layer. -#include "main.h" // NOLINT +#include "main.h" // NOLINT + +using firebase::remote_config::RemoteConfig; // Convert remote_config::ValueSource to a string. -const char* ValueSourceToString(firebase::remote_config::ValueSource source) { - static const char* kSourceToString[] = { - "Static", // kValueSourceStaticValue - "Remote", // kValueSourceRemoteValue - "Default", // kValueSourceDefaultValue +const char *ValueSourceToString(firebase::remote_config::ValueSource source) { + static const char *kSourceToString[] = { + "Static", // kValueSourceStaticValue + "Remote", // kValueSourceRemoteValue + "Default", // kValueSourceDefaultValue }; return kSourceToString[source]; } +RemoteConfig *rc_ = nullptr; + // Execute all methods of the C++ Remote Config API. -extern "C" int common_main(int argc, const char* argv[]) { +extern "C" int common_main(int argc, const char *argv[]) { namespace remote_config = ::firebase::remote_config; - ::firebase::App* app; + ::firebase::App *app; // Initialization @@ -43,20 +47,28 @@ extern "C" int common_main(int argc, const char* argv[]) { app = ::firebase::App::Create(GetJniEnv(), GetActivity()); #else app = ::firebase::App::Create(); -#endif // defined(__ANDROID__) +#endif // defined(__ANDROID__) LogMessage("Created the Firebase app %x", static_cast(reinterpret_cast(app))); ::firebase::ModuleInitializer initializer; - initializer.Initialize(app, nullptr, [](::firebase::App* app, void*) { - LogMessage("Try to initialize Remote Config"); - return ::firebase::remote_config::Initialize(*app); + + void *ptr = nullptr; + ptr = &rc_; + initializer.Initialize(app, ptr, [](::firebase::App *app, void *target) { + LogMessage("Try to initialize Firebase RemoteConfig"); + RemoteConfig **rc_ptr = reinterpret_cast(target); + *rc_ptr = RemoteConfig::GetInstance(app); + return firebase::kInitResultSuccess; }); + while (initializer.InitializeLastResult().status() != firebase::kFutureStatusComplete) { - if (ProcessEvents(100)) return 1; // exit if requested + if (ProcessEvents(100)) + return 1; // exit if requested } + if (initializer.InitializeLastResult().error() != 0) { LogMessage("Failed to initialize Firebase Remote Config: %s", initializer.InitializeLastResult().error_message()); @@ -79,47 +91,45 @@ extern "C" int common_main(int argc, const char* argv[]) { sizeof(kBinaryDefaults))}, {"TestDefaultOnly", "Default value that won't be overridden"}}; size_t default_count = sizeof(defaults) / sizeof(defaults[0]); - remote_config::SetDefaults(defaults, default_count); + rc_->SetDefaults(defaults, default_count); // The return values may not be the set defaults, if a fetch was previously // completed for the app that set them. remote_config::ValueInfo value_info; { - bool result = remote_config::GetBoolean("TestBoolean", &value_info); + bool result = rc_->GetBoolean("TestBoolean", &value_info); LogMessage("Get TestBoolean %d %s", result ? 1 : 0, ValueSourceToString(value_info.source)); } { - int64_t result = remote_config::GetLong("TestLong", &value_info); + int64_t result = rc_->GetLong("TestLong", &value_info); LogMessage("Get TestLong %lld %s", result, ValueSourceToString(value_info.source)); } { - double result = remote_config::GetDouble("TestDouble", &value_info); + double result = rc_->GetDouble("TestDouble", &value_info); LogMessage("Get TestDouble %f %s", result, ValueSourceToString(value_info.source)); } { - std::string result = remote_config::GetString("TestString", &value_info); + std::string result = rc_->GetString("TestString", &value_info); LogMessage("Get TestString \"%s\" %s", result.c_str(), ValueSourceToString(value_info.source)); } { - std::vector result = remote_config::GetData("TestData"); + std::vector result = rc_->GetData("TestData"); for (size_t i = 0; i < result.size(); ++i) { const unsigned char value = result[i]; LogMessage("TestData[%d] = 0x%02x", i, value); } } { - std::string result = remote_config::GetString("TestDefaultOnly", - &value_info); + std::string result = rc_->GetString("TestDefaultOnly", &value_info); LogMessage("Get TestDefaultOnly \"%s\" %s", result.c_str(), ValueSourceToString(value_info.source)); } { - std::string result = remote_config::GetString("TestNotSet", - &value_info); + std::string result = rc_->GetString("TestNotSet", &value_info); LogMessage("Get TestNotSet \"%s\" %s", result.c_str(), ValueSourceToString(value_info.source)); } @@ -127,30 +137,21 @@ extern "C" int common_main(int argc, const char* argv[]) { // Test the existence of the keys by name. { // Print out the keys with default values. - std::vector keys = remote_config::GetKeys(); + std::vector keys = rc_->GetKeys(); LogMessage("GetKeys:"); for (auto s = keys.begin(); s != keys.end(); ++s) { LogMessage(" %s", s->c_str()); } - keys = remote_config::GetKeysByPrefix("TestD"); + keys = rc_->GetKeysByPrefix("TestD"); LogMessage("GetKeysByPrefix(\"TestD\"):"); for (auto s = keys.begin(); s != keys.end(); ++s) { LogMessage(" %s", s->c_str()); } } - // Enable developer mode and verified it's enabled. - // NOTE: Developer mode should not be enabled in production applications. - remote_config::SetConfigSetting(remote_config::kConfigSettingDeveloperMode, - "1"); - if ((*remote_config::GetConfigSetting( - remote_config::kConfigSettingDeveloperMode) - .c_str()) != '1') { - LogMessage("Failed to enable developer mode"); - } // Test Fetch... LogMessage("Fetch..."); - auto future_result = remote_config::Fetch(0); + auto future_result = rc_->Fetch(0); while (future_result.status() == firebase::kFutureStatusPending) { if (ProcessEvents(1000)) { break; @@ -159,67 +160,71 @@ extern "C" int common_main(int argc, const char* argv[]) { if (future_result.status() == firebase::kFutureStatusComplete) { LogMessage("Fetch Complete"); - bool activate_result = remote_config::ActivateFetched(); - LogMessage("ActivateFetched %s", activate_result ? "succeeded" : "failed"); - - const remote_config::ConfigInfo& info = remote_config::GetInfo(); - LogMessage( - "Info last_fetch_time_ms=%d (year=%.2f) fetch_status=%d " - "failure_reason=%d throttled_end_time=%d", - static_cast(info.fetch_time), - 1970.0f + static_cast(info.fetch_time) / - (1000.0f * 60.0f * 60.0f * 24.0f * 365.0f), - info.last_fetch_status, info.last_fetch_failure_reason, - info.throttled_end_time); + auto activate_future_result = rc_->Activate(); + while (future_result.status() == firebase::kFutureStatusPending) { + if (ProcessEvents(1000)) { + break; + } + } + + bool activate_result = activate_future_result.result(); + LogMessage("Activate %s", activate_result ? "succeeded" : "failed"); + + const remote_config::ConfigInfo &info = rc_->GetInfo(); + LogMessage("Info last_fetch_time_ms=%d (year=%.2f) fetch_status=%d " + "failure_reason=%d throttled_end_time=%d", + static_cast(info.fetch_time), + 1970.0f + static_cast(info.fetch_time) / + (1000.0f * 60.0f * 60.0f * 24.0f * 365.0f), + info.last_fetch_status, info.last_fetch_failure_reason, + info.throttled_end_time); // Print out the new values, which may be updated from the Fetch. { - bool result = remote_config::GetBoolean("TestBoolean", &value_info); + bool result = rc_->GetBoolean("TestBoolean", &value_info); LogMessage("Updated TestBoolean %d %s", result ? 1 : 0, ValueSourceToString(value_info.source)); } { - int64_t result = remote_config::GetLong("TestLong", &value_info); + int64_t result = rc_->GetLong("TestLong", &value_info); LogMessage("Updated TestLong %lld %s", result, ValueSourceToString(value_info.source)); } { - double result = remote_config::GetDouble("TestDouble", &value_info); + double result = rc_->GetDouble("TestDouble", &value_info); LogMessage("Updated TestDouble %f %s", result, ValueSourceToString(value_info.source)); } { - std::string result = remote_config::GetString("TestString", &value_info); + std::string result = rc_->GetString("TestString", &value_info); LogMessage("Updated TestString \"%s\" %s", result.c_str(), ValueSourceToString(value_info.source)); } { - std::vector result = remote_config::GetData("TestData"); + std::vector result = rc_->GetData("TestData"); for (size_t i = 0; i < result.size(); ++i) { const unsigned char value = result[i]; LogMessage("TestData[%d] = 0x%02x", i, value); } } { - std::string result = remote_config::GetString("TestDefaultOnly", - &value_info); + std::string result = rc_->GetString("TestDefaultOnly", &value_info); LogMessage("Get TestDefaultOnly \"%s\" %s", result.c_str(), ValueSourceToString(value_info.source)); } { - std::string result = remote_config::GetString("TestNotSet", - &value_info); + std::string result = rc_->GetString("TestNotSet", &value_info); LogMessage("Get TestNotSet \"%s\" %s", result.c_str(), ValueSourceToString(value_info.source)); } { // Print out the keys that are now tied to data - std::vector keys = remote_config::GetKeys(); + std::vector keys = rc_->GetKeys(); LogMessage("GetKeys:"); for (auto s = keys.begin(); s != keys.end(); ++s) { LogMessage(" %s", s->c_str()); } - keys = remote_config::GetKeysByPrefix("TestD"); + keys = rc_->GetKeysByPrefix("TestD"); LogMessage("GetKeysByPrefix(\"TestD\"):"); for (auto s = keys.begin(); s != keys.end(); ++s) { LogMessage(" %s", s->c_str()); @@ -237,7 +242,8 @@ extern "C" int common_main(int argc, const char* argv[]) { while (!ProcessEvents(1000)) { } - remote_config::Terminate(); + delete rc_; + rc_ = nullptr; delete app; return 0; diff --git a/storage/testapp/build.gradle b/storage/testapp/build.gradle index 264b2a34..c47942ed 100644 --- a/storage/testapp/build.gradle +++ b/storage/testapp/build.gradle @@ -6,7 +6,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.2.1' + classpath 'com.android.tools.build:gradle:3.3.3' classpath 'com.google.gms:google-services:4.0.1' } } @@ -22,6 +22,10 @@ allprojects { apply plugin: 'com.android.application' android { + compileOptions { + sourceCompatibility 1.8 + targetCompatibility 1.8 + } compileSdkVersion 28 buildToolsVersion '28.0.3' @@ -36,7 +40,7 @@ android { defaultConfig { applicationId 'com.google.firebase.cpp.storage.testapp' - minSdkVersion 16 + minSdkVersion 19 targetSdkVersion 28 versionCode 1 versionName '1.0' diff --git a/storage/testapp/gradle/wrapper/gradle-wrapper.properties b/storage/testapp/gradle/wrapper/gradle-wrapper.properties index 35732b09..9e09cdb6 100644 --- a/storage/testapp/gradle/wrapper/gradle-wrapper.properties +++ b/storage/testapp/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +distributionUrl=https://services.gradle.org/distributions/gradle-5.6.4-all.zip From 5f8dc7e67cbb9f105f849d13bd98708044e3d24f Mon Sep 17 00:00:00 2001 From: DellaBitta Date: Thu, 7 Jul 2022 17:09:08 -0400 Subject: [PATCH 07/33] Add github action workflows for Android and iOS (#104) This change adds benign workflows for Android and iOS builds. --- .github/workflows/android.yml | 14 ++++++++++++++ .github/workflows/ios.yml | 14 ++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 .github/workflows/android.yml create mode 100644 .github/workflows/ios.yml diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml new file mode 100644 index 00000000..af35d584 --- /dev/null +++ b/.github/workflows/android.yml @@ -0,0 +1,14 @@ +name: Android builds + +on: + workflow_dispatch: + inputs: + apis: + description: 'CSV of apis whose quickstart examples we should build' + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: noop + run: true diff --git a/.github/workflows/ios.yml b/.github/workflows/ios.yml new file mode 100644 index 00000000..72c85f64 --- /dev/null +++ b/.github/workflows/ios.yml @@ -0,0 +1,14 @@ +name: iOS builds + +on: + workflow_dispatch: + inputs: + apis: + description: 'CSV of apis whose quickstart examples we should build' + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: noop + run: true From b74606b76c997e25acc05079b2ffd7618b3fcddc Mon Sep 17 00:00:00 2001 From: DellaBitta Date: Fri, 8 Jul 2022 12:52:32 -0400 Subject: [PATCH 08/33] added encrypted google services files (#103) Add the secrets for the test apps. This one step in many working toward creating CI builds of our testapps for all platforms. --- scripts/gha-encrypted/README | 4 ++++ .../admob/GoogleService-Info.plist.gpg | Bin 0 -> 671 bytes .../gha-encrypted/admob/google-services.json.gpg | Bin 0 -> 1002 bytes .../analytics/GoogleService-Info.plist.gpg | Bin 0 -> 683 bytes .../analytics/google-services.json.gpg | Bin 0 -> 958 bytes .../auth/GoogleService-Info.plist.gpg | Bin 0 -> 691 bytes .../gha-encrypted/auth/google-services.json.gpg | Bin 0 -> 1014 bytes .../database/GoogleService-Info.plist.gpg | Bin 0 -> 711 bytes .../database/google-services.json.gpg | Bin 0 -> 719 bytes .../dynamic_links/GoogleService-Info.plist.gpg | Bin 0 -> 736 bytes .../dynamic_links/google-services.json.gpg | Bin 0 -> 712 bytes .../firestore/GoogleService-Info.plist.gpg | Bin 0 -> 610 bytes .../firestore/google-services.json.gpg | Bin 0 -> 534 bytes .../functions/GoogleService-Info.plist.gpg | Bin 0 -> 688 bytes .../functions/google-services.json.gpg | Bin 0 -> 850 bytes .../gma/GoogleService-Info.plist.gpg | Bin 0 -> 672 bytes .../gha-encrypted/gma/google-services.json.gpg | Bin 0 -> 1002 bytes .../messaging/GoogleService-Info.plist.gpg | Bin 0 -> 692 bytes .../messaging/google-services.json.gpg | Bin 0 -> 911 bytes .../remote_config/GoogleService-Info.plist.gpg | Bin 0 -> 720 bytes .../remote_config/google-services.json.gpg | Bin 0 -> 1013 bytes .../storage/GoogleService-Info.plist.gpg | Bin 0 -> 682 bytes .../storage/google-services.json.gpg | Bin 0 -> 816 bytes 23 files changed, 4 insertions(+) create mode 100644 scripts/gha-encrypted/README create mode 100644 scripts/gha-encrypted/admob/GoogleService-Info.plist.gpg create mode 100644 scripts/gha-encrypted/admob/google-services.json.gpg create mode 100644 scripts/gha-encrypted/analytics/GoogleService-Info.plist.gpg create mode 100644 scripts/gha-encrypted/analytics/google-services.json.gpg create mode 100644 scripts/gha-encrypted/auth/GoogleService-Info.plist.gpg create mode 100644 scripts/gha-encrypted/auth/google-services.json.gpg create mode 100644 scripts/gha-encrypted/database/GoogleService-Info.plist.gpg create mode 100644 scripts/gha-encrypted/database/google-services.json.gpg create mode 100644 scripts/gha-encrypted/dynamic_links/GoogleService-Info.plist.gpg create mode 100644 scripts/gha-encrypted/dynamic_links/google-services.json.gpg create mode 100644 scripts/gha-encrypted/firestore/GoogleService-Info.plist.gpg create mode 100644 scripts/gha-encrypted/firestore/google-services.json.gpg create mode 100644 scripts/gha-encrypted/functions/GoogleService-Info.plist.gpg create mode 100644 scripts/gha-encrypted/functions/google-services.json.gpg create mode 100644 scripts/gha-encrypted/gma/GoogleService-Info.plist.gpg create mode 100644 scripts/gha-encrypted/gma/google-services.json.gpg create mode 100644 scripts/gha-encrypted/messaging/GoogleService-Info.plist.gpg create mode 100644 scripts/gha-encrypted/messaging/google-services.json.gpg create mode 100644 scripts/gha-encrypted/remote_config/GoogleService-Info.plist.gpg create mode 100644 scripts/gha-encrypted/remote_config/google-services.json.gpg create mode 100644 scripts/gha-encrypted/storage/GoogleService-Info.plist.gpg create mode 100644 scripts/gha-encrypted/storage/google-services.json.gpg diff --git a/scripts/gha-encrypted/README b/scripts/gha-encrypted/README new file mode 100644 index 00000000..eb31d713 --- /dev/null +++ b/scripts/gha-encrypted/README @@ -0,0 +1,4 @@ +See https://help.github.com/en/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets + +Googlers: code search firebase/cpp/Secrets/quickstart to find the sources. + diff --git a/scripts/gha-encrypted/admob/GoogleService-Info.plist.gpg b/scripts/gha-encrypted/admob/GoogleService-Info.plist.gpg new file mode 100644 index 0000000000000000000000000000000000000000..7a8dd56b43dacbfaf599257dc3c7228deffe524e GIT binary patch literal 671 zcmV;Q0$}}&4Fm}T0-Lp(S*XbSzyH$d0ccQ3S6V1c>`Vq{v@eT;(WXE&Mg7Sm$!4&< z@!pq8juOCk>c~ub=-BY`=|$N%sL8OnJhQx3;)u5G_mI7lb7x&|tZJ`xmU6Y|4}M`o-pzpt>cC;cW3n(fm#lMj0q5qrw#iXV@hH3!~0EURh?^Y}bnKHD}=G$)7BL6z=wX z61wI zU^IG4E^8z{RtfF8iGTe6w0ms+K{j{aCnwrF zQ(W`X)ryzv4IjF(R2}7emWWn?aS5FZaWa8+4BoL4^j}#qP{bD(UkyP^~9$knrMTAClQ`y;xhD(q}xeIq+JpqL8h^2E> zH%l>NZN>i!)bm8tWMoH+{9-#))^qYDF~q_RhG(d*n*RRsaoQ+$jVH Fi_{?GSk3?d literal 0 HcmV?d00001 diff --git a/scripts/gha-encrypted/admob/google-services.json.gpg b/scripts/gha-encrypted/admob/google-services.json.gpg new file mode 100644 index 0000000000000000000000000000000000000000..b62f63cbd715bac8245378fe906b83c40f168bbe GIT binary patch literal 1002 zcmV|*kk0})|U8{>`vX{A^rB0mUfNRZ3b@0(nBs0uCLT5=f&CS7md#Vj&Q8(kxYI|_37n{ zJ}{jbQu@aHPH&_>z`JK!8@DwEYY^&yKTQ0dvkSeP*eUR(u1Uo5TYJf*^mDeGN9duk z8nii0zNRM>G4S=ub?k1o&K;x!!c%~Je;MAmq2m7_C;TP?FoA#-0aF-v#3`vetun(a%*PI# z-YEo)EDP?8qw7I;P;${whXK)^q5UYT`namLHDbE zv1#5*IDnu2pd4ACmBp^Av6(*@k|^+B^yML1DV_j+UAG*8?ovqQRQ{ z*{e3BDhx2%z*jOvllCRg!51fA0cDg$ymDS0b4BXgU#W)*kg69Ayb`d;C#Wt^5{Oxq z&LG)P>~?QI7BYDLKMlfL)Vfa5RqCvDdHuuNtN_H{y1h}xPGJ`#eM^_A@Ft#VHnP+m z+%l8o45e`Ql2}r~?NdQmIz2Cfvi(t9!^Ne-*o*|f*iIre@_eihE5slG-9_xHNlR54 zd|>Y0(rKpKTGZ%-lt}y({)arrs8Snc4GBf48&t1I`HN5o-?>I%-_i#MU@L9n$_}Lt z%>~;AA;Z13$f5C(fNXk8&Aox%LXL&b0Dp4Cm?d6wEb_1Q`}{2%m43+QSioY9j*9lx zfi&XaUSG~Qp_EOTU31bi0Z)!OzzJP4Qu|9Isy=iG>pi_|m;h2(U0A=U=Zs^MQ0Eig z@>P&AdHOoJG;=J0xZA>?(KL1xCX)wctgo1|yv@!RTFChPvNK(K!bUox+U6aQ5gAXd zipi1IvmVTR+>zj$82f9Y0>mLfFf5C1&+q|X!Fg%ST!JNNvbNXO*b-PoZ63MT5sxiV z&S&PyOhi6OBMXcedg~ut+-5@gSV|E4;bP8(z467TvcX;x`x0hYO{WmPoKO$Z_8Ey* Yboa4XrsEr5lqq?)xW4iT*rtkxy%%)*%m4rY literal 0 HcmV?d00001 diff --git a/scripts/gha-encrypted/analytics/GoogleService-Info.plist.gpg b/scripts/gha-encrypted/analytics/GoogleService-Info.plist.gpg new file mode 100644 index 0000000000000000000000000000000000000000..094a665cff13969b6a82cb9a4ffc017316aae368 GIT binary patch literal 683 zcmV;c0#yBs4Fm}T0$9EM+5(-VK>yO|0i2OGg6KgFaNq@Qa0xP)ug||uQE}`=`2k86 zz`Ol|O@_9$cQym3YRKN#BX!RvOO_DMuG?3Z3GeV|BCO`>=2UkEU_MMw2%BivP=uiV zVm|E-o5EJH+F!8i+@yKeuq-3jP#Z?J_OcIriJ`|?j(M92cGr{iKX5XIThZ|?U`0)_M9FFE0)N13828W(5SyTxb;KeC=PI8jZ6avy? z8%tBEv8W99m)Fy;wNGjei88@E?p7Vx5dXSh7nDnt640O?PG4wy2&7r}=d)L7@d?); z+JHmXHFS<+K}$_SNEts5k(?}#MWu>{NdQpRmJUk?@x8zHpTfG~JLP@FtV<_M7iuon zS7Pw<+b?9!0mqll6c;D$MUl96OpKtPVd1pvSB$uwKK_u@8u&{qkX8gaP{{t6wqFW6 z{Qa#JYc#BT-UDlr`AZy}tBm{BxHANZ6%%eozM%NbU=GklVvM}CYs@vuKt)}940KPx zWuXnGzti=BXdHpj9iP%xvJHOG6!LlZ1efvOX7>qH(~{J9im(b1HZ|T1b-w1ye|5il zH|v5@00QR#0eukrPE9uTDL+S4O_y{GM+-@G-hHX#RX)xlDFR{a%FfYwYKX-pcOPsbPG{u!oPt_P(IcQOUB&{Ml$Hr1=~^m3-C1A z2+!JO<9GG(5BVP;_fvEQeO7H>+9)lpDp8{!_0 RUmC{{rNp@RriZN!T-3M=P}%?h literal 0 HcmV?d00001 diff --git a/scripts/gha-encrypted/analytics/google-services.json.gpg b/scripts/gha-encrypted/analytics/google-services.json.gpg new file mode 100644 index 0000000000000000000000000000000000000000..58d461365e9d4a8c78e6ba066a36ddc91e212659 GIT binary patch literal 958 zcmV;v13~T!1C6|W5>d-`i}bU6bA{|IG`5R zQK%DNYAwaq57z|kLC#*^RMa>!u&xPi+r9jLD<~bCqhQ^FE{Ese@uZPen3?v>*;>@< z-Ui5zxdFkXoEK`Aui(c47d#?&&E692D*xmcsT`6ddfh#x7A9U&bB!v6&Xv$aPYF!O zqn+K@wwH`7{w@|(*l>P-`+4@v>2|_FyazJ$y@Z?MW{h{dsZ$Jk# zIHn?v1D4efSn%9@xMVYYCugM%(qoutO{_21}S;!0QpC5-UJl2aeo0^?iwlL&f`|4Xi7zOc&Qrh<_vOA0(C_7s1DW zK`@GoM&l@(B6{xY$=Di$7W!7rcRH17{f3T{dlL98uIZaE;^6epw1O!5@kU_x2Ee?&ZA9pwH~v@DzjE{j2IM5M*vreO3))AeAax z&L>NtX;1M#WKBI8-}FywR%b-Y2&ogHBFSD=qg7mp(@d+)`bUzhJwmueo_^=+A zA;DO*eirh=OQ}pjI}8}Qvn>567FBtJH2JbDS@FWWA$Iu!YJ_gn=?i-}W)aJU^+`C{lh!8$qjHNuFGV4L~$9?sg-PKb=(_y7O^ literal 0 HcmV?d00001 diff --git a/scripts/gha-encrypted/auth/GoogleService-Info.plist.gpg b/scripts/gha-encrypted/auth/GoogleService-Info.plist.gpg new file mode 100644 index 0000000000000000000000000000000000000000..03490af4d0a7d8e6b73326e72709402670e41656 GIT binary patch literal 691 zcmV;k0!;mk4Fm}T0w?MSpTF7wZU55g0hFNOJ#vCjDV*7UC84++3FtMB)|!-2Y&d2# z4zA+i^!R#IkL|)QzRbI9@%;RdZF%EnOgI%WWA1{S)-XG_n~jPdcNmZUy5HsYM<2$< zbQQt~Z|(C&CFnV|zv#?eeu&IILcUZSSJAuz!3wb><*T%r9|=Pw*{dD9oenq%xPt}x zZohS$^X4lsLS}utirGsEzD{}&#g6scx4+nPx%H@~E(9+2{P+WO6@_*S<5>*)iEYBx zO5PA3mo?c!P5qM;4o6Oq+vAUr<+9V**PGTof#f6z>ydrZqK@rQr#kLfUCfA%Wp3d1P5>y+K?ntnpEzv|3NC~1F^^>%M<@0UXrW@qE*+M@iOP@mT|Gd1F@uR zmtxbpx}2aM7sj_~Q1<&I&`GoTa0e@Yykh7WQmk!U`1+UU#^dENB#Hj*efF|pQ0)Q3 za<0_gkOs+^1g%{!mNXF6h_`M$a(Trf7$c=(%20YmfDE}b#*Criw2=F(uVH|9Z_5Xy z3nMw8ScGIZ7X4YarR~a$O`}<^^36$2)dfZK3~V>FZt&7R2Ceg#d#7?J8?_d4s$<$+ zpg^q;YubOaxOJ)EiUZEQiK)bRAL>8^0{Vo$&&t1M3gsx%3!&4Onz}7GgP%^H$$L5+`QQp;Y4qR$6k)FDa*b1Kal-JuQ%;IOOS+5 ZL01p~douxq(TboFv_+~H0P(i1k1AEvVGRHP literal 0 HcmV?d00001 diff --git a/scripts/gha-encrypted/auth/google-services.json.gpg b/scripts/gha-encrypted/auth/google-services.json.gpg new file mode 100644 index 0000000000000000000000000000000000000000..dab68319270549a5feae30e2acbcb3299c7ded9c GIT binary patch literal 1014 zcmVL+WaXk?c zq*3kUynx<@M#=c@bk6dFVqn;6)ZBtB)Wwn5fcn1EvV-XPRFLVvqy4LGEuuYRa$lRv zyBsj80xKu49y!e9E+OW02mJ1BMTzC|*|XXG7v5>Bw`*yd6!(*$Hvf`8$_k4M;ev0- z7awGnaBFSRaVznrM}6yo_L<{-FL*wcOI!O0^Ata#RrFkB|?7LItTZv|eOY7wP+w>={~Jf%C(g21cPg zX|Mvab+=EgQBj+U#vETzaXZLXrr_P-UJJsZe7vBTAJO27 z-BteNGd+X?peYpy?U11xL)A5`LifKSDb@VK1TE;YkeI=2e=B^k2jiw69tbOX)?CAX z?AAp(G9!5}F5CK8Z6^6^Ap6^U;gtz~H?#ktH)!%2^~q+7%JL1^o9$={h@dkn+oV)= zEh6r-s#+gMQ8zXGQe@dE)IZbvVXVz8n zKC03CdF4!zxcskORJJQ!t7Qo{9fmJZmkwMN1kMAaHQD(-?(e)$olg1UB~Kx6<(1sJ kR+oCUDSlOJ$ZZ4zWZ!VAr$Sp_hSGH|$scxe{CmA+ApOk* z*AcJ9sh^H-4Y~JV3Ck26vB5BNaqheDd{-KqxxS9G?R<~O&vpSx>P1I(^nw;PczR!k zsFMY7EgTi|*GmA6EXrD)o>OwdCY@iY{h&>B1M^? z;T90c0fXV>`|TfoN)QWJNr+DsAWK8H7G0%&+2c@S5#OyeA>kmb9(alX8ONFnOc&+s z>1n%zgZ$`|4$$H-BQX@}vj*W>$zf~~?Mn7Q(_qE4@ZcL~EX?IzPHK@;Yv7f3(73Yf z3za5ER(%9uG&Va4bwRwIAqi%dBq8?M5+&?v6I@skmF*k*} z?rU{J>>BKpviowaGkUV`)<_S4q^8x^3pkQ+c6LVU7#~ZNF?I-lfLsH8=_HTq8H)TD z;Yr6hYz%`-)R%??jKCW$iMbm3aSM@YdXgqI(}lduq2GRB@Xy2NSk((zni8v$XykUb zw8JLO`AIA52|*#WU15yA+?L$F@bl8zyea!cN`LnHeaHanIDaQ8%mum`UsUx?Nx^sr zKDdv(_*56ScLp?@{(tQEh;J$DY~#4SR%@r z(hek?DkYFR;EPv69{Efcs~{pA#1`Pj%0(?6^g%^C@Fb21BnFF8BqF(c=A$u&ooC)s zbjFHc`gAB*^gXRx9-$n#ZSN5C#eIlgVgwdLqcm*CT1>{H(~wM?-olk~90Sn5!dTho zeOTUEyNv;^raBh_XolvWN$(-yyBIG15u440aPXMWqC5|f9FCXVN|9(wS`hT|;Az>y zs@n;ad+ipmQpP0Gw6Y(Dt+uYwvhJBqQCSFcw4=IT*U7}&y1!$>0wOk?M>GF~kM+)u z@!ME-Kn}_k22-~n1;UrIq6hs`g)e}0p;GDl|1G0-X^Dr?2Pp)?yCiEbtl-0|TzTdk zkZ?b#95c;Hb{UO?Iq(@I{jb7IKvgkux1Q2Q1w7)0gFLHb63S*g&-BOW)4J#DGz1I{ z@4jpWTrg6;qo+7Cdqi@2>y-@^Ahq1_(|#)y-xqV&0p=@jpxcQ4QR+`a6nm`U^*y#L zx#RMkGKkt?aVK3^LqmCjr9Y=?JO%a06tygu@a9;lW}Nl!=1XEkD8KG@Xm>UqJ@&NZ zH1K56y}?TgW4FSK3eHG(y#=7iMyx?zyYB?sSxL%uAp7Q2RU}7H1gU3kn3_|*Jl-|z-TtiD)i~cf_QIF1;C`k6 zE{PCqjgsZ5n&EryV%QAvg37X;v5ektJ4DC%1hb~fnKmKVwXe`bGdw21y7YN)CRam1 z!EG{$hct?=x!w^6PIW%|zV)@!!$%njIkgzgW^%I-4kM=+tvZ(>P9(QA(IEEAQe>Zb BWBC98 literal 0 HcmV?d00001 diff --git a/scripts/gha-encrypted/dynamic_links/GoogleService-Info.plist.gpg b/scripts/gha-encrypted/dynamic_links/GoogleService-Info.plist.gpg new file mode 100644 index 0000000000000000000000000000000000000000..871ee6e1b6d0ffed52db74b79cc38f0bb04b760f GIT binary patch literal 736 zcmV<60w4X14Fm}T0-a>Pk%V-U0sqqJ0gQULn-+z*r~OT~&p>@Ip;(%)zYa_Lj8#!a z*bw+An6v=*wjbxj@N0;LkhyiS=}>KZ1NIe(U~?HHQ5DzF@9lQe%zl9qB$q|_#*_CH z=Z(TytDjxU?w=KNC2V}W(9-18*R{~j%hA%7YuoUggxxIuIS3ZbpiieZ(2onuhM_G1KNZf}sb=#`e&piw;sU)05Bee-KkI(5v$+CPlrTt}^D(_dLpmlG+u zdV3^pNsrqQ&QrxwFfNrt-O3;xRyL4ap>#T-ifDIU3G5v$^8Jo;a3$3SlZ-bv4%jHi z*Ub^TE-?}#OHRgFGQNAZ=N%d^5($um1(+jAdpVK-tRUd_pWKcE3Q*FTnNJ85g-($7 zQcWF+C~C=JH_)6Ut6=T9tv7nTE)#`l;^vRbpc;Qnc)59+?lR%aMO%43X^>5m=j-I=QBP(Zg3DA_)3^+wZR#XHsbf% zSR#7b_?aIrl&N@`w=)&`;MwYB{7oPOz|OJ}e=NEQ{Q)ZZIu7y0tRO~Gp%WwTIlP77 z;I)RJ8Xcm0f@E)R;QqY0c$Q&VuU1UJtq<=f#k{O&PcBKtP>Ig)x{0A=?ILn z5R4Jgk$I`2m{oC>tuEukzzy;#*;gS)dqV9R(+;6pu0YlusFY$vXyh3v)xGjT zA^3$ZcQIM!H{W$Q%go9KAB81=8(ay)dsn;cEZ>v&<4&E{LVI3SQX=boIOd#*tMFG) SzZ_K7y2`7uphu8mqCB-8c5L1N literal 0 HcmV?d00001 diff --git a/scripts/gha-encrypted/dynamic_links/google-services.json.gpg b/scripts/gha-encrypted/dynamic_links/google-services.json.gpg new file mode 100644 index 0000000000000000000000000000000000000000..846543f60e983f1a137e51e0507045d05d1283ce GIT binary patch literal 712 zcmV;(0yq7P4Fm}T0=!ac4)@c#1pm_M0R%J&734OnZvXV#^!SR)4))}YPv{aV{{=)DLjeUF#+jC>>09SBzkk^)ALBo#EFP0)GObqCkRYE zSVnRoR`>BI4;$0_mU8)iH{{=k|~lmu8vN1O*D47 zgK@Mqx6E`%cBbZIYGaCsP!8q@dx*c@`ZovCtA7zf5j3LLrqJ~JAuoj4DOnF9 z17)4Ip7wJ3k^>@58&?OxBvku;%UfVO8}jI1G8~w<5Ky+a?9MlNf?2iCkr^Qv4~dv`c@hq4?aWM!-=pM`&=r=nQ?BG0_qZ|*1B^$5Fv#BvPoC}>#1}z`l^Ny z3xsc3>rVtnz%vK06b6icE(M$+ON^!nnWdlAD843Yo1o-hAa`4Oo$Oy3ZXeI42K^LF ubm?XGZzX5!($>B!R4x@>1V+ZJtXoIH$qYp!*P95aNyp?RgcLU{(Zi>x1zpJi literal 0 HcmV?d00001 diff --git a/scripts/gha-encrypted/firestore/GoogleService-Info.plist.gpg b/scripts/gha-encrypted/firestore/GoogleService-Info.plist.gpg new file mode 100644 index 0000000000000000000000000000000000000000..15ce08669627292013ee908e87fac31acaff9eef GIT binary patch literal 610 zcmV-o0-gPg4Fm}T0xc2w(rnNZ1pm_M0ov7uKoVB}O2oLRGmQ|u6O0Fa->Q!HM!sKL zcj=Ua)ec_!&!8yf4x#T^0@LcL|7Y^6d=5O*7wE8nn0`m-$0zS2n$GyuIr#buX(P56zqFy{+8cj-^nm z7Hr`-^1Ib|&hdg`jqOnMzS{wJLETZv)ILql7fJ4G8Y9sm3Kw#ZXk`z*0p=Gz{^_Q9 zD6XXqLvp3{QB#$}{p)qKRF5EGv)^rRw>Y3U!k43tVFI1<>U=FZ>ZIY#%VNTF)W6TA zMy&V^A7JF3Nt+PflxJa$K)p%xXxfM13>}@#tsyVPUmcW(m*5LBWH}HWK2wMoi~!i< zX%*|jXBuPpnwyYnEv{OZYt*7qD_o;^i+1o4(KPIl`@F!S_hoFWQ*$1>XACe|Q}8e+ zmtW@+Pbu$gS0F4zJi|Ft@1goj7YKAH>34mCp_YNpQ_Rwso$l$=fti$RO9exDwpXZ- zw+Z4>8m5|Kv#N45#nqmb(xNqQd=d-tj7nkf&BzrkB-3CID;WU-PLjstJGpfU5}SaV z;gxqdeulvxNduT_2~RMoEcXPVpu9Nt4t|QVPS?YVbKP9h!_G7~ev%)APM8n6o&Fd0 zfyrWrZYQl4kWTL|qoBhtQ0%<~HckpxK2&C}b?%>)!PE{X(xCeaGpz0V47jqZSlHYr w$ffhj1DuNqC%2YWTH+>#c<2nOvA-Bxn>ei(bn*{vGHYAZx-li$$u9-F3)^EXtpET3 literal 0 HcmV?d00001 diff --git a/scripts/gha-encrypted/firestore/google-services.json.gpg b/scripts/gha-encrypted/firestore/google-services.json.gpg new file mode 100644 index 0000000000000000000000000000000000000000..9067b0d5720e88b7e5627be95bb7897e258cf234 GIT binary patch literal 534 zcmV+x0_pvX4Fm}T0{*~q9{iWGv;WfR0s0HDOIVBGy4RwLz=KOkGZl<#eRes!xNxZH zEr9T5qJao+Z+qJD_H3R37DAFN~B8iJrg!)5d;nTvU>cz{{a0p>{2E*r1Ry~k8yXT?!E)gV`d1X za}VXAa$QY6m-fVmlmyK)k?2j_j2?KQH}#N-@~-=wNAMpM9GzBRoBg;PaoxaJG+u)MOH z!+mlgVZ7+2%gSzc?S?Ap%;AhZoP%EO9-8$w(|H~~ieCi2Kwdc~vPMKm({%&y6#3!q zL_g#P==9VHc5J6~FH1|Xu374+o7WTwz}lk~zFUF^yjjUDFz@JP2i6gQ5@7h{Vs)CGwSuv8y0yJg!xEF!H6d5b&yxvHMdm12%;^qJ?Ajpf+fxN2SCg zZ&-!)Z^`wH7+C@2MsFn#yLG``)2k1~e;V%pRh=dHai9KsMyXNhqZiFkm$-Q3lG+K2 z+^m;&ONh=g6DQ@3GQ{V6II|y)589iPvtn_D<3P1x->V=wPLyt=ALGGkur6H{HO=Ji6AzGA!@h|n;F7Sw&dU^R*x$Ll>dfA+*;))2Y)!E-z%sg$L?t{)enbUV?& z`hSA57Z@*U!wC{Y%%v3%h!zk>YxL7Z3w2-%5gEcEWTUFQh$Xw)VHuZ@6BjZDyyh&$;=UV%*9$kt6 literal 0 HcmV?d00001 diff --git a/scripts/gha-encrypted/functions/google-services.json.gpg b/scripts/gha-encrypted/functions/google-services.json.gpg new file mode 100644 index 0000000000000000000000000000000000000000..41eed7b7e5c5ae888e7b53b148e46c2935ae3802 GIT binary patch literal 850 zcmV-Y1Figw4Fm}T0<5<~zsUPs`Tx@C0g9{?M|q!2!(7*n{|}12)Isp^j$OFXkE4=I z5Sz>kwv`%#X)qzKTSVergpNoTNMu*U(|=K30aZ&T>+cv(q;@;8*>lyC=!3`cmuwN^u(~OVmAGK?y({HVgkh>ccYX(j{AShDmYSxu_R_OmaxO!U9=oDD9hf!~lU`^g4cfYn*WZ*k>s-@jTs;-<2e)0rQ6$*9EYj{}dg;ec8X5@t=6 z=r%5ID0`_n@XJTIcM1a*kWhQ=FBrhQHN1&0@<=o{vC5qI1~l_`+2=kGIMN)RS#R3z z$xTBx-~Qp}z<(JMTQl{QKzIWT(O`S2LJ(U^a z*Z$XxwsvZCBn=3%IPbatNFZVoWa^Qfp_b3Q7_Xm5c??~obpZUHoPRJ}ah<;WMDJqn z!x3q2rck+`M3Xiq_?C3~NJg-kAy9vsh8aVs?7}Slp~R(vh2ude;vHR3Dyg%PlxYyY zp@c=X~7{>A-v>tpsvfc(4}wdaA7;5IP)&gyl24ZypZ_f*w@{ zPG(Wdu5Vqm>_ZYJrkV}OPMCvOnM=az9CGeGv5GN-`z_0uhj&A9U0;f+gWxrt7>46p cn&8@9NHxUxA72=_ZY(Z2$lO literal 0 HcmV?d00001 diff --git a/scripts/gha-encrypted/gma/GoogleService-Info.plist.gpg b/scripts/gha-encrypted/gma/GoogleService-Info.plist.gpg new file mode 100644 index 0000000000000000000000000000000000000000..58896342a9fe3079458eb8aa63a9e806e139d62d GIT binary patch literal 672 zcmV;R0$=@%4Fm}T0_%(Zwo`_NuK&{M0fc3b`Gu6#?+=rYlG&2v>SbN1s>zvyX}l#I z8j1Vm4I$k9@A8oRrn0q!CqDY?S1kilKw9M(bN+vFy@RA?O(K290W&ez+sgu$mdk=muyM$D6neiHbIBQkqmGzi(akogx z#+3L0oIAG3zErL(tN8;f(XbBq4Fw3zJRk>+BhhHuPFo8a|weImAK9D5a>qRDK!X`y_2xCziolhO?|G zP_C?h%S2(VBU7>ci6Q=!bZkjqQQ?CcVN)2cfWzeyv4!}PRO|>jkKhLu)>M&1cO1}> z9R+cbFf-W#wE!Gk-nheNj;Zb`h@jOLmT3@rTwdXI7~>!{){q+aofdyoKhAYJh7{)& zVa^db&^D=$E#%?8!Gg2a8m<_TM5ALeV>Y#JT4KCxRUCF{5F;%fy=fX>JKd6bwIB GtRThF2}+Rw literal 0 HcmV?d00001 diff --git a/scripts/gha-encrypted/gma/google-services.json.gpg b/scripts/gha-encrypted/gma/google-services.json.gpg new file mode 100644 index 0000000000000000000000000000000000000000..b118a4cb346e8149418cf8fa86ab5a616d648a3e GIT binary patch literal 1002 zcmVFe&;$LD*U2V6jbw7A0UT^9NN8WQ= zL!01j1k_|^rv{=>;&kSH`%sP)Jy8+TO4R42x+jJ^{Q!FNY^pj@%BNQl3hSE>k?G?o zFuv2|$LYzUe{;GREk-smY>;EG@<+uV{)giF;8tP_h+-w#(wV8RWYhq3$M^RkS68PT zu$KB_Hrp&Y^TP3m+spKMuk_zU#q6_0KMP$R$o zwqE-{!H)SNDumqbBP1ngESQYS3Rgw9gE)*eLl2b~0l%50U*(83nS{7qXGe>Crz4w$ z@=i(ek~StY#h7hX?gL0)y{8=ZxXPflsJMJ&29Y-z7WG4&vI-R6l5n3E{~NaTEcdb! zs|*VCeRf#XKF_L@9B6As!50I`ahtj*yb8U7Ma3xl+gp_@7(NvTv)=~4YIVNnT@)&1 z)<5pNDkgT+yM69s*%Qg5SbcaSA!VWUuO2ZtFV75jjc~lKPM;)uRw|*%0L}5R5J@?%!9@* z{~04Fr5G@3^4mufbyGeaB>+-i@(KBkP*-S;oRSgJ9AijFhJ!$66D)<N6-G#<{<}U;MLm~VQ7gG2cXwSZ$Yc@%Ho6;OE*_DK+q4Xg{xmEBx`>Ug3!?V8 z_&qr0#OkxP4^l!&F*-MFswC*V#eDlIZAa2IpO>Awj&iYgt+K4j+BHKD@JFri)DtJj zcx-VH-Q|!NYo0ebe%T{?@QE>z*y4xFB@yCYQG`B=$8jixSNHWY(ng}IQF?K9K0ngd zW;_sIr8*^J&W|b8@Y`bjXTVS}+TPxGwOZXBMq;L|9qah*s>f(e>@k(+Cl;R;mpkM0 z{%YydGK@kl`8padeC6Ma`@0SG>Yp#+g-s6wp8J#2Y_sLBV;_Q@p4@bi?D{CW20|W$p^>asU7T literal 0 HcmV?d00001 diff --git a/scripts/gha-encrypted/messaging/GoogleService-Info.plist.gpg b/scripts/gha-encrypted/messaging/GoogleService-Info.plist.gpg new file mode 100644 index 0000000000000000000000000000000000000000..f8581e8822441373c038c20ddd7ce20b58e81c76 GIT binary patch literal 692 zcmV;l0!#gj4Fm}T0!Qr(8PpM5Q~%QG0iufd%S9@{ip$Prq%TT$>!08&@sQV14q?--w70#e3P*g0Fgn{F@t1QMLrUwdX9@RK%s*F<-PoM{#L!|q-MU$lj)v-t?i6NW z16$JzO$57WEn}f0tl2b|7fM!6G1w?4JtfM14uluB{JM`Gclo3CSgJ%`@(i3 z$3R3FD^_52ESBC6U58IY;8aALAu8M}MHdg94Ltq6KRosurqpqw!#B zaI6MxLL+j}M*;wg>+smq8b&YZCAk2Ks4Bx^EL4#A%@Px2l`AoQ;d!qy~V znsI<3LX_-OxBjoQs1j;NcOzFMEUvI172wL$4svH`;_T zC(C|(rC#WaZwHQubwrUw$Mn@ZXeXq<``kE=oAMgUIm9ij@&L;y?FePfLYm%I6u~ZcE9k$avopDHeC+k!Gf0{^@$L&F)@1*Gj2{gIZ{6E@N{4v$YS;%Q!*MF@L*MN{$=c55>HY1-LER12MGAZ;_&6AouH}hQRXz(goDsP8S{33 z1BD}81eO)Daae5AmPRUy+6a0`hqO^c&Sif^&C9^-=m1UMfnZ0;=7=ME=SKzHE^&6- zwG~-V*d5_5Wv{PZ6zEvmkOtXsO-=hY1_qcFKt3SPs5>}XKUj@Jf^*d+)BYF8C@6UL+3Dg;j5V$fss|w2>}y@Ca3KIW#&Fi&N)c2UP+pSLc7f- zr;{M(n#C3Jzr1q#qn;a>_|P_6IKVT^y-5W;E9m&|w7Zf4BP*-(biYdQD=B?joPef& z@ItJN^I1Y*{!cMDlPBKSMRpwcn_|z>YaPa0E~Vedr4`cICx98(?Scayu{`(~G7=r=)+GI}i!`gNZiPViR7G0kb}UNA|MOx$QZ zeOnvxg-j(m$vUm(8pvR!YhzEvR2mvacg=XFb(U4sLs^Qe3M$KVR|a8LP+H#}!9SBi zQ0r8NC$8JPw3RIqnApv&r;QuhGP65x9R2dP;~V#G(b9ZZ>Y^8DTOYpadqW}@kOoh@ zA3x%P+4WlIAAqHI6z%#5odl#>Oh>h&cRMu#cDraU)7=hIUP2xG*oW{GQB2U`!=;&odZ=Bl62`TF)_{9k;n(cXE l?=3pxlZ=9HYOa1P=pe1Y+YnnzXD&){J}f3+zbi0-yi6L{yQ}~J literal 0 HcmV?d00001 diff --git a/scripts/gha-encrypted/remote_config/GoogleService-Info.plist.gpg b/scripts/gha-encrypted/remote_config/GoogleService-Info.plist.gpg new file mode 100644 index 0000000000000000000000000000000000000000..67535e90eb6aab32f1b9456bff223a7de20d5302 GIT binary patch literal 720 zcmV;>0x$iH4Fm}T0=^8^*~I=}N&nL60rw$}bMwNO=&7@TbiLg|!F%^?VFL9!Ij|Yv zlId#GD!R4GB(k9ZL087&vLAiP!4VzDRV18~b7ccc`es=y>ptcZeA0wNr*I@@j(?($ zB>qY{E*litsZJOk=saq-vTjjR;P^+1qA#zhAh(1QX56xThw>DKzn5#6q2;)!BQ7iN z8K#x*B>oTkvtes%mUp(L$N+R?%XH@?!r4$rlPdkXqiS+Xdb}vrs03LQp_3CRG^Ub9 z%4R4_v?sde90iAAVVKOSrRyJkACGRuasEoO7PzF?z~3-IGNdMtIXm)H+cg!}6$7vq zu#5J(6b}`D8v}*!`xI)_pf^5<6j1pGWz9yzw#J(P(om_g%`KhtS@{2!Dp7QI+DEIc6`TuCEL zFfcSd={;8v-+^wJ@0T$|Na|7`9j?gRa%!Gi1ey+mb-Yn$cLpHGUU9Q;+Zf}}iO;or z`Sudx@%P>LU8RJnbQ^V+hCFjRar?_G=d`9;CXM)4e-~-)F!U*k&K`73 z#i+^z{_resHsWoY1T6g^T+9x%Z*CnQC-KO3mw(7zLZ0{H_Y@4tTNp)K%Vi>#K#saZ`(5 zfG52v4akYo4e=E|q>atMcp^y+v09yf&{g8<8DSKx)>B3b4hlGH(dAUJ!?ze9TH*rO zq;J52BtOup$i$JVvw?c?)MST6igt!>A1bdU_F?aH+Rc>r_hezs4V+4=%et0kig@49 z)bjciZ(5@3#}*VA40yO;4$8+}2Z|8dA-P7+FIvz4xJfWco~B zn7;Q{wI2%&|5Vd1g7Q%_lQsYt0Q{uX55z1<7ptSDh*bsP7;5U+bCWN1kL>~Mb0nw2 zN+6oV+s zwCMVhU6tp5opfCTVZaEiR~AD~Z5SEAY?-@1(tgc3A}U@wduJxb`Ay3eg`ltGyu$nU zy`RHPqMm-Q&z$UvM(mW4!6M+=koY*J9)J6q{-!FddT+^ap{WaM$3*!mFi{jiY|#yg z^EUAJi|XE8wLx3-t4BoKBq6um;|aRgXUhs|nH}$nXorEcBF?W`HWtZ%;<7bmcsaT!$ek#vqx0` z|EM@zba;246$iSz3WlTtQ89EFozI+CQ4i7NQg00PZsAezumz~8oT$YHyVS#M+B;Zy zB$JL+ktITHorf|}GLKI&6 zuiZ*29RM;*19IiNE`eBJE@XyHv3y^6408JGL&J)kitFVGj$E-giszDeASqUmTMzFQ zh>&cDFiBj03LP!u$9G@^0#OW!ymA;nd$UNr8dqP5a=uC{VmXC8C7IIpg}nO$Ju=c{ zB~sfm^v)mya=8)D!to|y>^0zyUb-R}8JdBH>?kKWgpSQ<1ODF<2d|-_|9TEO1 literal 0 HcmV?d00001 diff --git a/scripts/gha-encrypted/storage/GoogleService-Info.plist.gpg b/scripts/gha-encrypted/storage/GoogleService-Info.plist.gpg new file mode 100644 index 0000000000000000000000000000000000000000..de4c9c060ef900c64209cf477719da2b2b6e3847 GIT binary patch literal 682 zcmV;b0#*Ht4Fm}T0v!pkCXcJ+#sAXj0Z@8>?Hh4R@FkB5D3Uypi7`t7BSeCWHt?KO zg8Fy%ocVvr@D@~SMZLWa(L=^0g*WAmz3v$JWvi(defSRf7zRvQ+x;eBLu`JUOPSMw zn*TK-xVS+nChk}-XFq`yMa6?I@{k$$kq zza1!Pd~g&%NCJ-ze#J97&Eq2prlN)5-PT_yq0WO6HJW<-_3zlltL*I^x;?p^Fbv!K zG~Qno?;uL*Yc{`c{-)$ay}~6c4ty}{$kP3-i!2NkwD`~IW(XYILwijf5x9X8GO&$= zQ%&nV%m3c!?1>{`v7FyZ<#AW=DbxwgIUm1IAg|NdsTsih#F|x_jy0_C{TFM93r&mb zeg}TnkolFtEPvZvdPx5#D$qRGSU_5+S=yx$zsDwJ5`yW0lxk?mv(;$FsxsY(vTYR5 z7VPo5y&B%{tN_(W6=m{%xM$y-iE`=aCM5&*3PFm&+lR6K1io;UFD3Sy&V00Xu;M;M z650@GYf*prNbc3Ek1OcQm>(%l?rreoTwM_X0ICasoJUx;K6mf7qh@=Z|ACz`6(RC) z_p>Q-9LF*fV-?81bEkBMLFUX0@szZw)0a8};;D`;Q2|U!>X$$3wDJ?kUKDvumBDNX ze`2dhU6N3Mp9JX6339M;RO4Y?A#Waw6#GN%c5s@e+%O31FNCY8z$6_Va4>D@oBB^r Q@RD4!kXrp;=I5_ED>va;vj6}9 literal 0 HcmV?d00001 diff --git a/scripts/gha-encrypted/storage/google-services.json.gpg b/scripts/gha-encrypted/storage/google-services.json.gpg new file mode 100644 index 0000000000000000000000000000000000000000..1393dce3671f22a200dcc706133d0b4f95f23b5a GIT binary patch literal 816 zcmV-01JC@74Fm}T0+C7+B&Suz`~TAE0fHW-zaTRl)Of`7Ux-~>I{cifK?&2Cmvoph zVeX}Y2=EdIRM6+7s%-E;SPq~+L|oB#|GH6KI>Z+}JN#ayQovGuJg>igmM8$SD_rAj zy}5T+JM%kx4Ob`Cio}}uNR@u{>WS1rleQk_>GZuRp>2cXkr2#-h+O;E@QYwR5}81< zvfhaN$GE-*ATt67HE4i5L+8PImT7=M9~yX)Dyi}!t7(fumWFTDhJfvMdn;R{77Z3= z%sAFx`Qt zqW2YZ2jy7db)!C&hI3#aJ>%>>*-X~$YL9^fDFDYH^5?WQw#ON=5E=RW$>#XXL})YX z&SrDEOL}R;m0>YEC$iaM@EHt`O@3F|Y$Cp}t$jKtGu&^6Tm?ON%$~5)y;-MpXS~eX^3ptoq^@303CA))iMYnA2!@j`?`p=u#P8R0b)9@a9svQy`21NY6oNM+aUs)mO z{!$JyC$Ze9+$EJ literal 0 HcmV?d00001 From 84c6f1e7d0140de999823739e2c7bd701125e7ca Mon Sep 17 00:00:00 2001 From: DellaBitta Date: Fri, 22 Jul 2022 08:37:54 -0400 Subject: [PATCH 09/33] Add CI Android targets (#105) Adds a CI Workflow to build the testapps against the publicly available packaged SDK. --- .github/workflows/android.yml | 205 ++++- analytics/testapp/build.gradle | 2 +- dynamic_links/testapp/build.gradle | 2 +- .../build_scripts/android/install_prereqs.sh | 75 ++ scripts/build_scripts/build_testapps.json | 183 +++++ scripts/build_scripts/build_testapps.py | 718 ++++++++++++++++++ scripts/build_scripts/config_reader.py | 139 ++++ scripts/build_scripts/python_requirements.txt | 2 + scripts/build_scripts/utils.py | 94 +++ scripts/build_scripts/xcodebuild.py | 119 +++ scripts/restore_secrets.py | 186 +++++ 11 files changed, 1718 insertions(+), 7 deletions(-) create mode 100755 scripts/build_scripts/android/install_prereqs.sh create mode 100644 scripts/build_scripts/build_testapps.json create mode 100644 scripts/build_scripts/build_testapps.py create mode 100644 scripts/build_scripts/config_reader.py create mode 100644 scripts/build_scripts/python_requirements.txt create mode 100644 scripts/build_scripts/utils.py create mode 100644 scripts/build_scripts/xcodebuild.py create mode 100644 scripts/restore_secrets.py diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index af35d584..d8876f55 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -1,14 +1,209 @@ -name: Android builds +name: Android Builds on: + schedule: + - cron: "0 9 * * *" # 9am UTC = 1am PST / 2am PDT. for all testapps except firestore + pull_request: + types: [opened, reopened, synchronize] workflow_dispatch: inputs: apis: - description: 'CSV of apis whose quickstart examples we should build' + description: 'CSV of apis to build and test' + default: 'admob,analytics,auth,database,dynamic_links,firestore,functions,gma,messaging,remote_config,storage' + required: true + +env: + CCACHE_DIR: ${{ github.workspace }}/ccache_dir + GITHUB_TOKEN: ${{ github.token }} + xcodeVersion: "13.3.1" # Only affects Mac runners, and only for prerequisites. + +concurrency: + group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.head_ref || github.ref }} + cancel-in-progress: true jobs: - build: + check_and_prepare: runs-on: ubuntu-latest + outputs: + apis: ${{ steps.set_outputs.outputs.apis }} steps: - - name: noop - run: true + - id: set_outputs + run: | + if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then + apis="${{ github.event.inputs.apis }}" + else + apis="admob,analytics,auth,database,dynamic_links,firestore,functions,gma,messaging,remote_config,storage" + fi + echo apis: ${apis} + echo "::set-output name=apis::${apis}" + + build: + name: android-${{ matrix.os }}-${{ matrix.architecture }}-${{ matrix.python_version }} + runs-on: ${{ matrix.os }} + needs: [check_and_prepare] + strategy: + fail-fast: false + matrix: + os: [macos-12, ubuntu-latest, windows-latest] + architecture: [x64] + python_version: [3.7] + steps: + - name: setup Xcode version (macos) + if: runner.os == 'macOS' + run: sudo xcode-select -s /Applications/Xcode_${{ env.xcodeVersion }}.app/Contents/Developer + + - name: Store git credentials for all git commands + # Forces all git commands to use authenticated https, to prevent throttling. + shell: bash + run: | + git config --global credential.helper 'store --file /tmp/git-credentials' + echo 'https://${{ github.token }}@github.com' > /tmp/git-credentials + + - name: Enable Git Long-paths Support + if: runner.os == 'Windows' + run: git config --system core.longpaths true + + - uses: actions/checkout@v2 + with: + submodules: true + + - name: Set env variables for subsequent steps (all) + shell: bash + run: | + echo "MATRIX_UNIQUE_NAME=${{ matrix.os }}-${{ matrix.architecture }}" >> $GITHUB_ENV + echo "GHA_INSTALL_CCACHE=1" >> $GITHUB_ENV + + - name: Setup python + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python_version }} + architecture: ${{ matrix.architecture }} + + - name: Add msbuild to PATH + if: startsWith(matrix.os, 'windows') + uses: microsoft/setup-msbuild@v1.0.2 + + - name: Cache NDK + id: cache_ndk + uses: actions/cache@v2 + with: + path: /tmp/android-ndk-r21e + key: android-ndk-${{ matrix.os }}-r21e + + - name: Check cached NDK + shell: bash + if: steps.cache_ndk.outputs.cache-hit != 'true' + run: | + # If the NDK failed to download from the cache, but there is a + # /tmp/android-ndk-r21e directory, it's incomplete, so remove it. + if [[ -d "/tmp/android-ndk-r21e" ]]; then + echo "Removing incomplete download of NDK" + rm -rf /tmp/android-ndk-r21e + fi + + - name: Update homebrew (avoid bintray errors) + uses: nick-invision/retry@v2 + if: startsWith(matrix.os, 'macos') + with: + timeout_minutes: 10 + max_attempts: 3 + command: | + # Temporarily here until Github runners have updated their version of + # homebrew. This prevents errors arising from the shut down of + # binutils, used by older version of homebrew for hosting packages. + brew update + + - name: Install prerequisites + uses: nick-invision/retry@v2 + with: + timeout_minutes: 10 + max_attempts: 3 + shell: bash + command: | + scripts/build_scripts/android/install_prereqs.sh + echo "NDK_ROOT=/tmp/android-ndk-r21e" >> $GITHUB_ENV + echo "ANDROID_NDK_HOME=/tmp/android-ndk-r21e" >> $GITHUB_ENV + pip install -r scripts/build_scripts/python_requirements.txt + python scripts/restore_secrets.py --passphrase "${{ secrets.TEST_SECRET }}" + + - name: Download C++ SDK + shell: bash + run: | + set +e + # Retry up to 10 times because Curl has a tendency to timeout on + # Github runners. + for retry in {1..10} error; do + if [[ $retry == "error" ]]; then exit 5; fi + curl -LSs \ + "https://firebase.google.com/download/cpp" \ + --output firebase_cpp_sdk.zip && break + sleep 300 + done + set -e + mkdir /tmp/downloaded_sdk + unzip -q firebase_cpp_sdk.zip -d /tmp/downloaded_sdk/ + rm -f firebase_cpp_sdk.zip + + - name: Cache ccache files + id: cache_ccache + uses: actions/cache@v2 + with: + path: ccache_dir + key: dev-test-ccache-${{ env.MATRIX_UNIQUE_NAME }} + + - name: Build testapp + shell: bash + run: | + set -x + python scripts/build_scripts/build_testapps.py --p Android \ + --t ${{ needs.check_and_prepare.outputs.apis }} \ + --output_directory "${{ github.workspace }}" \ + --artifact_name "android-${{ matrix.os }}" \ + --noadd_timestamp \ + --short_output_paths \ + --gha_build \ + --packaged_sdk /tmp/downloaded_sdk/firebase_cpp_sdk + + - name: Stats for ccache (mac and linux) + if: startsWith(matrix.os, 'ubuntu') || startsWith(matrix.os, 'macos') + run: ccache -s + + - name: Prepare results summary artifact + if: ${{ !cancelled() }} + shell: bash + run: | + if [ ! -f build-results-android-${{ matrix.os }}.log.json ]; then + echo "__SUMMARY_MISSING__" > build-results-android-${{ matrix.os }}.log.json + fi + + - name: Upload Android integration tests artifact + uses: actions/upload-artifact@v3 + if: ${{ !cancelled() }} + with: + name: testapps-android-${{ matrix.os }} + path: testapps-android-${{ matrix.os }} + retention-days: ${{ env.artifactRetentionDays }} + + - name: Upload Android build results artifact + uses: actions/upload-artifact@v3 + if: ${{ !cancelled() }} + with: + name: log-artifact + path: build-results-android-${{ matrix.os }}* + retention-days: ${{ env.artifactRetentionDays }} + + - name: Download log artifacts + if: ${{ needs.check_and_prepare.outputs.pr_number && failure() && !cancelled() }} + uses: actions/download-artifact@v3 + with: + path: test_results + name: log-artifact + + - name: Summarize build results + if: ${{ !cancelled() }} + shell: bash + run: | + cat build-results-android-${{ matrix.os }}.log + if [[ "${{ job.status }}" != "success" ]]; then + exit 1 + fi diff --git a/analytics/testapp/build.gradle b/analytics/testapp/build.gradle index 6928009e..7b5d686e 100644 --- a/analytics/testapp/build.gradle +++ b/analytics/testapp/build.gradle @@ -6,7 +6,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.2.1' + classpath 'com.android.tools.build:gradle:3.3.3' classpath 'com.google.gms:google-services:4.0.1' } } diff --git a/dynamic_links/testapp/build.gradle b/dynamic_links/testapp/build.gradle index bf4eff96..49bcf71b 100644 --- a/dynamic_links/testapp/build.gradle +++ b/dynamic_links/testapp/build.gradle @@ -6,7 +6,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.2.1' + classpath 'com.android.tools.build:gradle:3.3.3' classpath 'com.google.gms:google-services:4.0.1' } } diff --git a/scripts/build_scripts/android/install_prereqs.sh b/scripts/build_scripts/android/install_prereqs.sh new file mode 100755 index 00000000..f5d6efe9 --- /dev/null +++ b/scripts/build_scripts/android/install_prereqs.sh @@ -0,0 +1,75 @@ +#!/bin/bash -e + +# Copyright 2022 Google LLC + +if [[ $(uname) == "Darwin" ]]; then + platform=darwin + if [[ ! -z "${GHA_INSTALL_CCACHE}" ]]; then + brew install ccache + echo "CCACHE_INSTALLED=1" >> $GITHUB_ENV + fi +elif [[ $(uname) == "Linux" ]]; then + platform=linux + if [[ ! -z "${GHA_INSTALL_CCACHE}" ]]; then + sudo apt install ccache + echo "CCACHE_INSTALLED=1" >> $GITHUB_ENV + fi +else + platform=windows +fi + +if [[ -z $(which cmake) ]]; then + echo "Error, cmake is not installed or is not in the PATH." + exit 1 +fi + +if [[ -z $(which python) ]]; then + echo "Error, python is not installed or is not in the PATH." + exit 1 +else + updated_pip=0 + if ! $(echo "import absl"$'\n' | python - 2> /dev/null); then + echo "Installing python packages." + set -x + # On Windows bash shell, sudo doesn't exist + if [[ $(uname) == "Linux" ]] || [[ $(uname) == "Darwin" ]]; then + sudo python -m pip install --upgrade pip + else + python -m pip install --upgrade pip + fi + pip install absl-py + set +x + fi +fi + +if [[ -z "${ANDROID_HOME}" ]]; then + echo "Error, ANDROID_HOME environment variable is not set." + exit 1 +fi + +if [[ -z "${NDK_ROOT}" || -z $(grep "Pkg\.Revision = 21\." "${NDK_ROOT}/source.properties") ]]; then + if [[ -d /tmp/android-ndk-r21e && \ + -n $(grep "Pkg\.Revision = 21\." "/tmp/android-ndk-r21e/source.properties") ]]; then + echo "Using NDK r21e in /tmp/android-ndk-r21e". + else + echo "NDK_ROOT environment variable is not set, or NDK version is incorrect." + echo "This build recommends NDK r21e, downloading..." + if [[ -z $(which curl) ]]; then + echo "Error, could not run 'curl' to download NDK. Is it in your PATH?" + exit 1 + fi + set +e + # Retry up to 10 times because Curl has a tendency to timeout on + # Github runners. + for retry in {1..10} error; do + if [[ $retry == "error" ]]; then exit 5; fi + curl --http1.1 -LSs \ + "https://dl.google.com/android/repository/android-ndk-r21e-${platform}-x86_64.zip" \ + --output /tmp/android-ndk-r21e.zip && break + sleep 300 + done + set -e + (cd /tmp && unzip -oq android-ndk-r21e.zip && rm -f android-ndk-r21e.zip) + echo "NDK r21e has been downloaded into /tmp/android-ndk-r21e" + fi +fi diff --git a/scripts/build_scripts/build_testapps.json b/scripts/build_scripts/build_testapps.json new file mode 100644 index 00000000..7dc1d078 --- /dev/null +++ b/scripts/build_scripts/build_testapps.json @@ -0,0 +1,183 @@ +{ + "apis": [ + { + "name": "admob", + "full_name": "FirebaseAdmob", + "bundle_id": "com.google.ios.admob.testapp", + "ios_target": "testapp", + "tvos_target": "", + "testapp_path": "admob/testapp", + "frameworks": [ + "firebase_admob.xcframework", + "firebase.xcframework" + ], + "provision": "Google_Development.mobileprovision" + }, + { + "name": "analytics", + "full_name": "FirebaseAnalytics", + "bundle_id": "com.google.ios.analytics.testapp", + "ios_target": "testapp", + "tvos_target": "", + "testapp_path": "analytics/testapp", + "frameworks": [ + "firebase_analytics.xcframework", + "firebase.xcframework" + ], + "provision": "Google_Development.mobileprovision" + }, + { + "name": "auth", + "full_name": "FirebaseAuth", + "bundle_id": "com.google.FirebaseCppAuthTestApp.dev", + "ios_target": "testapp", + "tvos_target": "", + "testapp_path": "auth/testapp", + "frameworks": [ + "firebase_auth.xcframework", + "firebase.xcframework" + ], + "provision": "Firebase_Cpp_Auth_Test_App_Dev.mobileprovision" + }, + { + "name": "database", + "full_name": "FirebaseDatabase", + "bundle_id": "com.google.firebase.cpp.database.testapp", + "ios_target": "testapp", + "tvos_target": "", + "testapp_path": "database/testapp", + "frameworks": [ + "firebase_auth.xcframework", + "firebase_database.xcframework", + "firebase.xcframework" + ], + "provision": "Firebase_Dev_Wildcard.mobileprovision" + }, + { + "name": "dynamic_links", + "full_name": "FirebaseDynamicLinks", + "bundle_id": "com.google.FirebaseCppDynamicLinksTestApp.dev", + "ios_target": "testapp", + "tvos_target": "", + "testapp_path": "dynamic_links/testapp", + "frameworks": [ + "firebase_dynamic_links.xcframework", + "firebase.xcframework" + ], + "provision": "Firebase_Cpp_Dynamic_Links_Test_App_Dev.mobileprovision" + }, + { + "name": "functions", + "full_name": "FirebaseFunctions", + "bundle_id": "com.google.firebase.cpp.functions.testapp", + "ios_target": "testapp", + "tvos_target": "", + "testapp_path": "functions/testapp", + "frameworks": [ + "firebase_auth.xcframework", + "firebase_functions.xcframework", + "firebase.xcframework" + ], + "provision": "Firebase_Dev_Wildcard.mobileprovision" + }, + { + "name": "gma", + "full_name": "FirebaseGma", + "bundle_id": "com.google.ios.admob.testapp", + "ios_target": "testapp", + "tvos_target": "", + "testapp_path": "gma/testapp", + "frameworks": [ + "firebase_gma.xcframework", + "firebase.xcframework" + ], + "provision": "Google_Development.mobileprovision" + }, + { + "name": "messaging", + "full_name": "FirebaseMessaging", + "bundle_id": "com.google.FirebaseCppMessagingTestApp.dev", + "ios_target": "testapp", + "tvos_target": "", + "testapp_path": "messaging/testapp", + "frameworks": [ + "firebase_messaging.xcframework", + "firebase.xcframework" + ], + "provision": "Firebase_Cpp_Messaging_Test_App_Dev.mobileprovision" + }, + { + "name": "remote_config", + "full_name": "FirebaseRemoteConfig", + "bundle_id": "com.google.ios.remoteconfig.testapp", + "ios_target": "testapp", + "tvos_target": "", + "testapp_path": "remote_config/testapp", + "frameworks": [ + "firebase_remote_config.xcframework", + "firebase.xcframework" + ], + "provision": "Google_Development.mobileprovision" + }, + { + "name": "storage", + "full_name": "FirebaseStorage", + "bundle_id": "com.google.firebase.cpp.storage.testapp", + "ios_target": "testapp", + "tvos_target": "", + "testapp_path": "storage/testapp", + "frameworks": [ + "firebase_storage.xcframework", + "firebase_auth.xcframework", + "firebase.xcframework" + ], + "provision": "Firebase_Dev_Wildcard.mobileprovision" + }, + { + "name": "firestore", + "full_name": "FirebaseFirestore", + "bundle_id": "com.google.firebase.cpp.firestore.testapp", + "ios_target": "testapp", + "tvos_target": "", + "testapp_path": "firestore/testapp", + "frameworks": [ + "firebase_firestore.xcframework", + "firebase_auth.xcframework", + "firebase.xcframework" + ], + "provision": "Firebase_Dev_Wildcard.mobileprovision", + "minify": "proguard" + } + ], + "apple_team_id": "REPLACE_ME_TEMP_INVALID_ID", + "compiler_dict": { + "gcc-4.8": [ + "-DCMAKE_C_COMPILER=gcc-4.8", + "-DCMAKE_CXX_COMPILER=g++-4.8" + ], + "gcc-7": [ + "-DCMAKE_C_COMPILER=gcc-7", + "-DCMAKE_CXX_COMPILER=g++-7" + ], + "gcc-9": [ + "-DCMAKE_C_COMPILER=gcc-9", + "-DCMAKE_CXX_COMPILER=g++-9" + ], + "clang-5.0": [ + "-DCMAKE_C_COMPILER=clang-5.0", + "-DCMAKE_CXX_COMPILER=clang++-5.0" + ], + "VisualStudio2015": [ + "-G", + "Visual Studio 14 2015 Win64" + ], + "VisualStudio2017": [ + "-G", + "Visual Studio 15 2017 Win64" + ], + "VisualStudio2019": [ + "-G", + "Visual Studio 16 2019" + ] + } + } diff --git a/scripts/build_scripts/build_testapps.py b/scripts/build_scripts/build_testapps.py new file mode 100644 index 00000000..3caf85f0 --- /dev/null +++ b/scripts/build_scripts/build_testapps.py @@ -0,0 +1,718 @@ +# Copyright 2022 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. + +r"""Build automation tool for Firebase C++ testapps for desktop and mobile. + +USAGE: + +This tool has a number of dependencies (listed below). Once those are taken +care of, here is an example of an execution of the tool (on MacOS): + +python build_testapps.py --t auth,messaging --p iOS --s /tmp/firebase-cpp-sdk + +Critical flags: +--t (full name: testapps, default: None) +--p (full name: platforms, default: None) +--s (full name: packaged_sdk, default: None) + +By default, this tool will build integration tests from source, which involves + +Under most circumstances the other flags don't need to be set, but can be +seen by running --help. Note that all path flags will forcefully expand +the user ~. + + +DEPENDENCIES: + +----Firebase Repo---- +The Firebase C++ Quickstart repo must be locally present. +Path specified by the flag: + + --repo_dir (default: current working directory) + +----Python Dependencies---- +The requirements.txt file has the required dependencies for this Python tool. + + pip install -r requirements.txt + +----CMake (Desktop only)---- +CMake must be installed and on the system path. + +----Environment Variables (Android only)---- +If building for Android, gradle requires several environment variables. +The following lists expected variables, and examples of what +a configured value may look like on MacOS: + + JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk-8-latest/Contents/Home + ANDROID_HOME=/Users/user_name/Library/Android/sdk + ANDROID_SDK_HOME=/Users/user_name/Library/Android/sdk + ANDROID_NDK_HOME=/Users/user_name/Library/Android/sdk/ndk-bundle + +Or on Linux: + JAVA_HOME=/usr/local/buildtools/java/jdk/ + ANDROID_HOME=~/Android/Sdk + ANDROID_SDK_HOME=~/Android/Sdk + ANDROID_NDK_HOME=~/Android/Sdk/ndk + +If using this tool frequently, you will likely find it convenient to +modify your bashrc file to automatically set these variables. + +""" + +import attr +import datetime +import json +import os +import platform +import shutil +import stat +import subprocess +import sys +import tempfile + +from absl import app +from absl import flags +from absl import logging +from distutils import dir_util + +import utils +import config_reader +import xcodebuild + +# Environment variables +_JAVA_HOME = "JAVA_HOME" +_ANDROID_HOME = "ANDROID_HOME" +_ANDROID_SDK_HOME = "ANDROID_SDK_HOME" +_NDK_ROOT = "NDK_ROOT" +_ANDROID_NDK_HOME = "ANDROID_NDK_HOME" + +# Platforms +_ANDROID = "Android" +_IOS = "iOS" +_TVOS = "tvOS" +_DESKTOP = "Desktop" +_SUPPORTED_PLATFORMS = (_ANDROID, _IOS, _TVOS, _DESKTOP) + +# Architecture +_SUPPORTED_ARCHITECTURES = ("x64", "x86", "arm64") + +# Values for iOS SDK flag (where the iOS app will run) +_APPLE_SDK_DEVICE = "real" +_APPLE_SDK_SIMULATOR = "virtual" +_SUPPORTED_APPLE_SDK = (_APPLE_SDK_DEVICE, _APPLE_SDK_SIMULATOR) + +_DEFAULT_RUN_TIMEOUT_SECONDS = 4800 # 1 hour 20 min + +FLAGS = flags.FLAGS + +flags.DEFINE_string( + "packaged_sdk", None, "Firebase SDK directory.") + +flags.DEFINE_string( + "output_directory", "~", + "Build output will be placed in this directory.") + +flags.DEFINE_string( + "artifact_name", "local-build", + "artifacts will be created and placed in output_directory." + " testapps artifact is testapps-$artifact_name;" + " build log artifact is build-results-$artifact_name.log.") + +flags.DEFINE_string( + "repo_dir", os.getcwd(), + "Firebase C++ Quickstart Git repository. Current directory by default.") + +flags.DEFINE_list( + "testapps", None, "Which testapps (Firebase APIs) to build, e.g." + " 'analytics,auth'.", + short_name="t") + +flags.DEFINE_list( + "platforms", None, "Which platforms to build. Can be Android, iOS and/or" + " Desktop", short_name="p") + +flags.DEFINE_bool( + "add_timestamp", True, + "Add a timestamp to the output directory for disambiguation." + " Recommended when running locally, so each execution gets its own " + " directory.") + +flags.DEFINE_list( + "ios_sdk", _APPLE_SDK_DEVICE, + "(iOS only) Build for real device (.ipa), virtual device / simulator (.app), " + "or both. Building for both will produce both an .app and an .ipa.") + +flags.DEFINE_list( + "tvos_sdk", _APPLE_SDK_SIMULATOR, + "(tvOS only) Build for real device (.ipa), virtual device / simulator (.app), " + "or both. Building for both will produce both an .app and an .ipa.") + +flags.DEFINE_bool( + "update_pod_repo", True, + "(iOS/tvOS only) Will run 'pod repo update' before building for iOS/tvOS to update" + " the local spec repos available on this machine. Must also include iOS/tvOS" + " in platforms flag.") + +flags.DEFINE_string( + "compiler", None, + "(Desktop only) Specify the compiler with CMake during the testapps build." + " Check the config file to see valid choices for this flag." + " If none, will invoke cmake without specifying a compiler.") + +flags.DEFINE_string( + "arch", "x64", + "(Desktop only) Which architecture to build: x64 (all), x86 (Windows/Linux), " + "or arm64 (Mac only).") + +# Get the number of CPUs for the default value of FLAGS.jobs +CPU_COUNT = os.cpu_count(); +# If CPU count couldn't be determined, default to 2. +DEFAULT_CPU_COUNT = 2 +if CPU_COUNT is None: CPU_COUNT = DEFAULT_CPU_COUNT +# Cap at 4 CPUs. +MAX_CPU_COUNT = 4 +if CPU_COUNT > MAX_CPU_COUNT: CPU_COUNT = MAX_CPU_COUNT + +flags.DEFINE_integer( + "jobs", CPU_COUNT, + "(Desktop only) If > 0, pass in -j to make CMake parallelize the" + " build. Defaults to the system's CPU count (max %s)." % MAX_CPU_COUNT) + +flags.DEFINE_multi_string( + "cmake_flag", None, + "Pass an additional flag to the CMake configure step." + " This option can be specified multiple times.") + +flags.register_validator( + "platforms", + lambda p: all(platform in _SUPPORTED_PLATFORMS for platform in p), + message="Valid platforms: " + ",".join(_SUPPORTED_PLATFORMS), + flag_values=FLAGS) + +flags.register_validator( + "ios_sdk", + lambda s: all(ios_sdk in _SUPPORTED_APPLE_SDK for ios_sdk in s), + message="Valid platforms: " + ",".join(_SUPPORTED_APPLE_SDK), + flag_values=FLAGS) + +flags.register_validator( + "tvos_sdk", + lambda s: all(tvos_sdk in _SUPPORTED_APPLE_SDK for tvos_sdk in s), + message="Valid platforms: " + ",".join(_SUPPORTED_APPLE_SDK), + flag_values=FLAGS) + +flags.DEFINE_bool( + "short_output_paths", False, + "Use short directory names for output paths. Useful to avoid hitting file " + "path limits on Windows.") + +flags.DEFINE_bool( + "gha_build", False, + "Set to true if this is a GitHub Actions build.") + +def main(argv): + if len(argv) > 1: + raise app.UsageError("Too many command-line arguments.") + + platforms = FLAGS.platforms + testapps = FLAGS.testapps + + sdk_dir = _fix_path(FLAGS.packaged_sdk) + root_output_dir = _fix_path(FLAGS.output_directory) + repo_dir = _fix_path(FLAGS.repo_dir) + + update_pod_repo = FLAGS.update_pod_repo + if FLAGS.add_timestamp: + timestamp = datetime.datetime.now().strftime("%Y_%m_%d-%H_%M_%S") + else: + timestamp = "" + + if FLAGS.short_output_paths: + output_dir = os.path.join(root_output_dir, "ta") + else: + output_dir = os.path.join(root_output_dir, "testapps" + timestamp) + + config = config_reader.read_config() + + xcframework_dir = os.path.join(sdk_dir, "xcframeworks") + xcframework_exist = os.path.isdir(xcframework_dir) + if not xcframework_exist: + if _IOS in platforms: + _build_xcframework_from_repo(repo_dir, "ios", testapps, config) + if _TVOS in platforms: + _build_xcframework_from_repo(repo_dir, "tvos", testapps, config) + + if update_pod_repo and (_IOS in platforms or _TVOS in platforms): + _run(["pod", "repo", "update"]) + + cmake_flags = _get_desktop_compiler_flags(FLAGS.compiler, config.compilers) + + if (_DESKTOP in platforms and utils.is_linux_os() and FLAGS.arch == "x86"): + # Write out a temporary toolchain file to force 32-bit Linux builds, as + # the SDK-included toolchain file may not be present when building against + # the packaged SDK. + temp_toolchain_file = tempfile.NamedTemporaryFile("w+", suffix=".cmake") + temp_toolchain_file.writelines([ + 'set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32")\n', + 'set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m32")\n', + 'set(CMAKE_LIBRARY_PATH "/usr/lib/i386-linux-gnu")\n', + 'set(INCLUDE_DIRECTORIES ${INCLUDE_DIRECTORIES} "/usr/include/i386-linux-gnu")\n']) + temp_toolchain_file.flush() + # Leave the file open, as it will be deleted on close, i.e. when this script exits. + # (On Linux, the file can be opened a second time by cmake while still open by + # this script) + cmake_flags.extend(["-DCMAKE_TOOLCHAIN_FILE=%s" % temp_toolchain_file.name]) + + if FLAGS.cmake_flag: + cmake_flags.extend(FLAGS.cmake_flag) + + failures = [] + for testapp in testapps: + api_config = config.get_api(testapp) + testapp_dirs = [api_config.testapp_path] + for testapp_dir in testapp_dirs: + logging.info("BEGIN building for %s: %s", testapp, testapp_dir) + failures += _build( + testapp=testapp, + platforms=platforms, + api_config=config.get_api(testapp), + testapp_dir=testapp_dir, + output_dir=output_dir, + sdk_dir=sdk_dir, + xcframework_exist=xcframework_exist, + repo_dir=repo_dir, + ios_sdk=FLAGS.ios_sdk, + tvos_sdk=FLAGS.tvos_sdk, + cmake_flags=cmake_flags, + short_output_paths=FLAGS.short_output_paths) + logging.info("END building for %s", testapp) + + _collect_integration_tests(testapps, root_output_dir, output_dir, FLAGS.artifact_name) + + _summarize_results(testapps, platforms, failures, root_output_dir, FLAGS.artifact_name) + return 1 if failures else 0 + + +def _build( + testapp, platforms, api_config, testapp_dir, output_dir, sdk_dir, xcframework_exist, + repo_dir, ios_sdk, tvos_sdk, cmake_flags, short_output_paths): + """Builds one testapp on each of the specified platforms.""" + os.chdir(repo_dir) + project_dir = os.path.join(output_dir, api_config.name) + if short_output_paths: + # Combining the first letter of every part separated by underscore for + # testapp paths. This is a trick to reduce file path length as we were + # exceeding the limit on Windows. + testapp_dir_parts = os.path.basename(testapp_dir).split('_') + output_testapp_dir = ''.join([x[0] for x in testapp_dir_parts]) + else: + output_testapp_dir = os.path.basename(testapp_dir) + + project_dir = os.path.join(project_dir, output_testapp_dir) + + logging.info("Copying testapp project to %s", project_dir) + os.makedirs(project_dir) + dir_util.copy_tree(testapp_dir, project_dir) + + logging.info("Changing directory to %s", project_dir) + os.chdir(project_dir) + + # TODO(DDB): remove + # _run_setup_script(repo_dir, project_dir) + + failures = [] + + if _DESKTOP in platforms: + logging.info("BEGIN %s, %s", testapp, _DESKTOP) + try: + _build_desktop(sdk_dir, cmake_flags) + except subprocess.SubprocessError as e: + failures.append( + Failure(testapp=testapp, platform=_DESKTOP, error_message=str(e))) + _rm_dir_safe(os.path.join(project_dir, "bin")) + logging.info("END %s, %s", testapp, _DESKTOP) + + if _ANDROID in platforms: + logging.info("BEGIN %s, %s", testapp, _ANDROID) + try: + _validate_android_environment_variables() + _build_android(project_dir, sdk_dir) + except subprocess.SubprocessError as e: + failures.append( + Failure(testapp=testapp, platform=_ANDROID, error_message=str(e))) + _rm_dir_safe(os.path.join(project_dir, "build", "intermediates")) + _rm_dir_safe(os.path.join(project_dir, ".externalNativeBuild")) + logging.info("END %s, %s", testapp, _ANDROID) + + if _IOS in platforms: + logging.info("BEGIN %s, %s", testapp, _IOS) + try: + _build_apple( + sdk_dir=sdk_dir, + xcframework_exist=xcframework_exist, + project_dir=project_dir, + repo_dir=repo_dir, + api_config=api_config, + target=api_config.ios_target, + scheme=api_config.ios_scheme, + apple_platfrom=_IOS, + apple_sdk=ios_sdk) + + except subprocess.SubprocessError as e: + failures.append( + Failure(testapp=testapp, platform=_IOS, error_message=str(e))) + logging.info("END %s, %s", testapp, _IOS) + + if _TVOS in platforms and api_config.tvos_target: + logging.info("BEGIN %s, %s", testapp, _TVOS) + try: + _build_apple( + sdk_dir=sdk_dir, + xcframework_exist=xcframework_exist, + project_dir=project_dir, + repo_dir=repo_dir, + api_config=api_config, + target=api_config.tvos_target, + scheme=api_config.tvos_scheme, + apple_platfrom=_TVOS, + apple_sdk=tvos_sdk) + except subprocess.SubprocessError as e: + failures.append( + Failure(testapp=testapp, platform=_TVOS, error_message=str(e))) + logging.info("END %s, %s", testapp, _TVOS) + + return failures + + +def _collect_integration_tests(testapps, root_output_dir, output_dir, artifact_name): + testapps_artifact_dir = "testapps-" + artifact_name + android_testapp_extension = ".apk" + ios_testapp_extension = ".ipa" + ios_simualtor_testapp_extension = ".app" + desktop_testapp_name = "testapp" + if platform.system() == "Windows": + desktop_testapp_name += ".exe" + + testapp_paths = [] + testapp_google_services = {} + for file_dir, directories, file_names in os.walk(output_dir): + for directory in directories: + if directory.endswith(ios_simualtor_testapp_extension): + testapp_paths.append(os.path.join(file_dir, directory)) + for file_name in file_names: + if ((file_name == desktop_testapp_name and "ios_build" not in file_dir) + or file_name.endswith(android_testapp_extension) + or file_name.endswith(ios_testapp_extension)): + testapp_paths.append(os.path.join(file_dir, file_name)) + if (file_name == "google-services.json"): + testapp_google_services[file_dir.split(os.path.sep)[-2]] = os.path.join(file_dir, file_name) + + artifact_path = os.path.join(root_output_dir, testapps_artifact_dir) + _rm_dir_safe(artifact_path) + for testapp in testapps: + os.makedirs(os.path.join(artifact_path, testapp)) + for path in testapp_paths: + for testapp in testapps: + if testapp in path: + if os.path.isfile(path): + shutil.copy(path, os.path.join(artifact_path, testapp)) + if path.endswith(desktop_testapp_name) and testapp_google_services.get(testapp): + shutil.copy(testapp_google_services[testapp], os.path.join(artifact_path, testapp)) + else: + dir_util.copy_tree(path, os.path.join(artifact_path, testapp, os.path.basename(path))) + break + + +def _write_summary(testapp_dir, summary, file_name="summary.log"): + with open(os.path.join(testapp_dir, file_name), "a") as f: + timestamp = datetime.datetime.now().strftime("%Y_%m_%d-%H_%M_%S") + f.write("\n%s\n%s\n" % (timestamp, summary)) + + +def _summarize_results(testapps, platforms, failures, root_output_dir, artifact_name): + """Logs a readable summary of the results of the build.""" + file_name = "build-results-" + artifact_name + ".log" + + summary = [] + summary.append("BUILD SUMMARY:") + summary.append("TRIED TO BUILD: " + ",".join(testapps)) + summary.append("ON PLATFORMS: " + ",".join(platforms)) + + if not failures: + summary.append("ALL BUILDS SUCCEEDED") + else: + summary.append("SOME ERRORS OCCURRED:") + for i, failure in enumerate(failures, start=1): + summary.append("%d: %s" % (i, failure.describe())) + summary = "\n".join(summary) + + logging.info(summary) + _write_summary(root_output_dir, summary, file_name=file_name) + + summary_json = {} + summary_json["type"] = "build" + summary_json["testapps"] = testapps + summary_json["errors"] = {failure.testapp:failure.error_message for failure in failures} + with open(os.path.join(root_output_dir, file_name+".json"), "a") as f: + f.write(json.dumps(summary_json, indent=2)) + + +def _build_desktop(sdk_dir, cmake_flags): + cmake_configure_cmd = ["cmake", ".", "-DCMAKE_BUILD_TYPE=Debug", + "-DFIREBASE_CPP_SDK_DIR=" + sdk_dir] + if utils.is_windows_os(): + cmake_configure_cmd += ["-A", + "Win32" if FLAGS.arch == "x86" else FLAGS.arch] + elif utils.is_mac_os(): + # Ensure that correct Mac architecture is built. + cmake_configure_cmd += ["-DCMAKE_OSX_ARCHITECTURES=%s" % + ("arm64" if FLAGS.arch == "arm64" else "x86_64")] + + _run(cmake_configure_cmd + cmake_flags) + _run(["cmake", "--build", ".", "--config", "Debug"] + + ["-j", str(FLAGS.jobs)] if FLAGS.jobs > 0 else []) + + +def _get_desktop_compiler_flags(compiler, compiler_table): + """Returns the command line flags for this compiler.""" + if not compiler: # None is an acceptable default value + return [] + try: + return compiler_table[compiler] + except KeyError: + valid_keys = ", ".join(compiler_table.keys()) + raise ValueError( + "Given compiler: %s. Valid compilers: %s" % (compiler, valid_keys)) + + +def _build_android(project_dir, sdk_dir): + """Builds an Android binary (apk).""" + if platform.system() == "Windows": + gradlew = "gradlew.bat" + sdk_dir = sdk_dir.replace("\\", "/") # Gradle misinterprets backslashes. + else: + gradlew = "./gradlew" + logging.info("Patching gradle properties with path to SDK") + gradle_properties = os.path.join(project_dir, "gradle.properties") + with open(gradle_properties, "a+") as f: + f.write("systemProp.firebase_cpp_sdk.dir=" + sdk_dir + "\n") + f.write("http.keepAlive=false\n") + f.write("maven.wagon.http.pool=false\n") + f.write("maven.wagon.httpconnectionManager.ttlSeconds=120") + # This will log the versions of dependencies for debugging purposes. + _run([gradlew, "dependencies", "--configuration", "debugCompileClasspath",]) + _run([gradlew, "assembleDebug", "--stacktrace"]) + + +def _validate_android_environment_variables(): + """Checks environment variables that may be required for Android.""" + # Ultimately we let the gradle build be the source of truth on what env vars + # are required, but try to repair holes and log warnings if we can't. + android_home = os.environ.get(_ANDROID_HOME) + if not os.environ.get(_JAVA_HOME): + logging.warning("%s not set", _JAVA_HOME) + if not os.environ.get(_ANDROID_SDK_HOME): + if android_home: # Use ANDROID_HOME as backup for ANDROID_SDK_HOME + os.environ[_ANDROID_SDK_HOME] = android_home + logging.info("%s not found, using %s", _ANDROID_SDK_HOME, _ANDROID_HOME) + else: + logging.warning("Missing: %s and %s", _ANDROID_SDK_HOME, _ANDROID_HOME) + # Different environments may have different NDK env vars specified. We look + # for these, in this order, and set the others to the first found. + # If none are set, we check the default location for the ndk. + ndk_path = None + ndk_vars = [_NDK_ROOT, _ANDROID_NDK_HOME] + for env_var in ndk_vars: + val = os.environ.get(env_var) + if val: + ndk_path = val + break + if not ndk_path: + if android_home: + default_ndk_path = os.path.join(android_home, "ndk-bundle") + if os.path.isdir(default_ndk_path): + ndk_path = default_ndk_path + if ndk_path: + logging.info("Found ndk: %s", ndk_path) + for env_var in ndk_vars: + if os.environ.get(env_var) != ndk_path: + logging.info("Setting %s to %s", env_var, ndk_path) + os.environ[env_var] = ndk_path + else: + logging.warning("No NDK env var set. Set one of %s", ", ".join(ndk_vars)) + +# build required ios xcframeworks based on makefiles +# the xcframeworks locates at repo_dir/ios_build +def _build_xcframework_from_repo(repo_dir, apple_platform, testapps, config): + """Builds xcframework from SDK source.""" + output_path = os.path.join(repo_dir, apple_platform + "_build") + _rm_dir_safe(output_path) + xcframework_builder = os.path.join( + repo_dir, "scripts", "gha", "build_ios_tvos.py") + + # build only required targets to save time + target = set() + for testapp in testapps: + api_config = config.get_api(testapp) + if apple_platform == "ios" or (apple_platform == "tvos" and api_config.tvos_target): + for framework in api_config.frameworks: + # firebase_analytics.framework -> firebase_analytics + target.add(os.path.splitext(framework)[0]) + + # firebase is not a target in CMake, firebase_app is the target + # firebase_app will be built by other target as well + target.remove("firebase") + + framework_builder_args = [ + sys.executable, xcframework_builder, + "-b", output_path, + "-s", repo_dir, + "-o", apple_platform, + "-t" + ] + framework_builder_args.extend(target) + _run(framework_builder_args) + + +def _build_apple( + sdk_dir, xcframework_exist, project_dir, repo_dir, api_config, + target, scheme, apple_platfrom, apple_sdk): + """Builds an iOS application (.app, .ipa or both).""" + build_dir = apple_platfrom.lower() + "_build" + if not xcframework_exist: + sdk_dir = os.path.join(repo_dir, build_dir) + + build_dir = os.path.join(project_dir, build_dir) + os.makedirs(build_dir) + + logging.info("Copying XCFrameworks") + framework_src_dir = os.path.join(sdk_dir, "xcframeworks") + framework_paths = [] # Paths to the copied frameworks. + for framework in api_config.frameworks: + framework_src_path = os.path.join(framework_src_dir, framework) + framework_dest_path = os.path.join(project_dir, "Frameworks", framework) + dir_util.copy_tree(framework_src_path, framework_dest_path) + framework_paths.append(framework_dest_path) + + _run(["pod", "install"]) + + entitlements_path = os.path.join( + project_dir, api_config.ios_target + ".entitlements") + xcode_tool_path = os.path.join( + repo_dir, "scripts", "gha", "integration_testing", "xcode_tool.rb") + xcode_patcher_args = [ + "ruby", xcode_tool_path, + "--XCodeCPP.xcodeProjectDir", project_dir, + "--XCodeCPP.target", target, + "--XCodeCPP.frameworks", ",".join(framework_paths) + ] + # Internal integration tests require the SDK root as an include path. + if repo_dir and api_config.internal_testapp_path: + xcode_patcher_args.extend(("--XCodeCPP.include", repo_dir)) + if os.path.isfile(entitlements_path): # Not all testapps require entitlements + logging.info("Entitlements file detected.") + xcode_patcher_args.extend(("--XCodeCPP.entitlement", entitlements_path)) + else: + logging.info("No entitlements found at %s.", entitlements_path) + _run(xcode_patcher_args) + + xcode_path = os.path.join(project_dir, "integration_test.xcworkspace") + if _APPLE_SDK_SIMULATOR in apple_sdk: + _run( + xcodebuild.get_args_for_build( + path=xcode_path, + scheme=scheme, + output_dir=build_dir, + apple_platfrom=apple_platfrom, + apple_sdk=_APPLE_SDK_SIMULATOR, + configuration="Debug")) + + if _APPLE_SDK_DEVICE in apple_sdk: + _run( + xcodebuild.get_args_for_build( + path=xcode_path, + scheme=scheme, + output_dir=build_dir, + apple_platfrom=apple_platfrom, + apple_sdk=_APPLE_SDK_DEVICE, + configuration="Debug")) + + xcodebuild.generate_unsigned_ipa( + output_dir=build_dir, configuration="Debug") + + +# This should be executed before performing any builds. +def _run_setup_script(root_dir, testapp_dir): + """Runs the setup_integration_tests.py script.""" + # This script will download gtest to its own directory. + # The CMake projects were configured to download gtest, but this was + # found to be flaky and errors didn't propagate up the build system + # layers. The workaround is to download gtest with this script and copy it. + downloader_dir = os.path.join(root_dir, "testing", "test_framework") + _run([sys.executable, os.path.join(downloader_dir, "download_googletest.py")]) + # Copies shared test framework files into the project, including gtest. + script_path = os.path.join(root_dir, "setup_integration_tests.py") + _run([sys.executable, script_path, testapp_dir]) + + +def _run(args, timeout=_DEFAULT_RUN_TIMEOUT_SECONDS, capture_output=False, text=None, check=True): + """Executes a command in a subprocess.""" + logging.info("Running in subprocess: %s", " ".join(args)) + return subprocess.run( + args=args, + timeout=timeout, + capture_output=capture_output, + text=text, + check=check) + + +def _handle_readonly_file(func, path, excinfo): + """Function passed into shutil.rmtree to handle Access Denied error""" + os.chmod(path, stat.S_IWRITE) + func(path) # will re-throw if a different error occurrs + + +def _rm_dir_safe(directory_path): + """Removes directory at given path. No error if dir doesn't exist.""" + logging.info("Deleting %s...", directory_path) + try: + shutil.rmtree(directory_path, onerror=_handle_readonly_file) + except OSError as e: + # There are two known cases where this can happen: + # The directory doesn't exist (FileNotFoundError) + # A file in the directory is open in another process (PermissionError) + logging.warning("Failed to remove directory:\n%s", e.strerror) + + +def _fix_path(path): + """Expands ~, normalizes slashes, and converts relative paths to absolute.""" + return os.path.abspath(os.path.expanduser(path)) + + +@attr.s(frozen=True, eq=False) +class Failure(object): + """Holds context for the failure of a testapp to build/run.""" + testapp = attr.ib() + platform = attr.ib() + error_message = attr.ib() + + def describe(self): + return "%s, %s: %s" % (self.testapp, self.platform, self.error_message) + + +if __name__ == "__main__": + flags.mark_flag_as_required("testapps") + flags.mark_flag_as_required("platforms") + flags.mark_flag_as_required("packaged_sdk") + app.run(main) diff --git a/scripts/build_scripts/config_reader.py b/scripts/build_scripts/config_reader.py new file mode 100644 index 00000000..a24f5d7a --- /dev/null +++ b/scripts/build_scripts/config_reader.py @@ -0,0 +1,139 @@ +# Copyright 2022 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. + +"""A utility for working with testapp builder JSON files. + +This module handles loading the central configuration file for a testapp +builder, returning a 'Config' object that exposes all the data. + +The motivation for loading the config into a class as opposed to returning +the loaded JSON directly is to validate the data upfront, to fail fast if +anything is missing or formatted incorrectly. + +Example of such a configuration file: + +{ + "apis": [ + { + "name": "analytics", + "full_name": "FirebaseAnalytics", + "bundle_id": "com.google.ios.analytics.testapp", + "ios_target": "testapp", + "tvos_target": "", + "testapp_path": "analytics/testapp", + "frameworks": [ + "firebase_analytics.framework", + "firebase.framework" + ], + "provision": "Google_Development.mobileprovision" + }, + { + "name": "admob", + "full_name": "FirebaseAdmob", + "bundle_id": "com.google.ios.admob.testapp", + "ios_target": "testapp", + "tvos_target": "", + "testapp_path": "admob/testapp", + "frameworks": [ + "firebase_admob.framework", + "firebase.framework" + ], + "provision": "Google_Development.mobileprovision" + } + ], + "dev_team": "ABCDEFGHIJK" +} + +""" + +import json +import os +import pathlib + +import attr + +_DEFAULT_CONFIG_NAME = "build_testapps.json" + + +def read_config(path=None): + """Creates an in-memory 'Config' object out of a testapp config file. + + Args: + path (str): Path to a testapp builder config file. If not specified, will + look for 'build_testapps.json' in the same directory as this file. + + Returns: + Config: All of the testapp builder's configuration. + + """ + if not path: + directory = pathlib.Path(__file__).parent.absolute() + path = os.path.join(directory, _DEFAULT_CONFIG_NAME) + with open(path, "r") as config: + config = json.load(config) + api_configs = dict() + try: + for api in config["apis"]: + api_name = api["name"] + api_configs[api_name] = APIConfig( + name=api_name, + full_name=api["full_name"], + bundle_id=api["bundle_id"], + ios_target=api["ios_target"], + tvos_target=api["tvos_target"], + ios_scheme=api["ios_target"], # Scheme assumed to be same as target. + tvos_scheme=api["tvos_target"], + testapp_path=api["testapp_path"], + internal_testapp_path=api.get("internal_testapp_path", None), + frameworks=api["frameworks"], + provision=api["provision"], + minify=api.get("minify", None)) + return Config( + apis=api_configs, + compilers=config["compiler_dict"]) + except (KeyError, TypeError, IndexError): + # The error will be cryptic on its own, so we dump the JSON to + # offer context, then reraise the error. + print( + "Error occurred while parsing config. Full config dump:\n" + + json.dumps(config, sort_keys=True, indent=4, separators=(",", ":"))) + raise + + +@attr.s(frozen=True, eq=False) +class Config(object): + apis = attr.ib() # Mapping of str: APIConfig + compilers = attr.ib() + + def get_api(self, api): + """Returns the APIConfig object for the given api, e.g. 'analytics'.""" + return self.apis[api] + + +@attr.s(frozen=True, eq=False) +class APIConfig(object): + """Holds all the configuration for a single testapp project.""" + name = attr.ib() + full_name = attr.ib() + bundle_id = attr.ib() + ios_target = attr.ib() + tvos_target = attr.ib() + ios_scheme = attr.ib() + tvos_scheme = attr.ib() + testapp_path = attr.ib() # testapp dir relative to sdk root + internal_testapp_path = attr.ib() # Internal testdir dir relative to sdk root + frameworks = attr.ib() # Required custom xcode frameworks + provision = attr.ib() # Path to the local mobile provision + minify = attr.ib() # (Optional) Android minification. + diff --git a/scripts/build_scripts/python_requirements.txt b/scripts/build_scripts/python_requirements.txt new file mode 100644 index 00000000..214a8aac --- /dev/null +++ b/scripts/build_scripts/python_requirements.txt @@ -0,0 +1,2 @@ +attrs +absl-py diff --git a/scripts/build_scripts/utils.py b/scripts/build_scripts/utils.py new file mode 100644 index 00000000..631dc8df --- /dev/null +++ b/scripts/build_scripts/utils.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python + +# Copyright 2022 Google +# +# 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. + +""" +Helper functions that are shared amongst prereqs and build scripts across various +platforms. +""" + +import distutils.spawn +import platform +import shutil +import subprocess +import os +import urllib.request + +def run_command(cmd, capture_output=False, cwd=None, check=False, as_root=False, + print_cmd=True): + """Run a command. + + Args: + cmd (list(str)): Command to run as a list object. + Eg: ['ls', '-l']. + capture_output (bool): Capture the output of this command. + Output can be accessed as .stdout + cwd (str): Directory to execute the command from. + check (bool): Raises a CalledProcessError if True and the command errored out + as_root (bool): Run command as root user with admin priveleges (supported on mac and linux). + print_cmd (bool): Print the command we are running to stdout. + + Raises: + (subprocess.CalledProcessError): If command errored out and `text=True` + + Returns: + (`subprocess.CompletedProcess`): object containing information from + command execution + """ + + if as_root and (is_mac_os() or is_linux_os()): + cmd.insert(0, 'sudo') + + cmd_string = ' '.join(cmd) + if print_cmd: + print('Running cmd: {0}\n'.format(cmd_string)) + # If capture_output is requested, we also set text=True to store the returned value of the + # command as a string instead of bytes object + return subprocess.run(cmd, capture_output=capture_output, cwd=cwd, + check=check, text=capture_output) + + +def is_command_installed(tool): + """Check if a command is installed on the system.""" + return distutils.spawn.find_executable(tool) + + +def delete_directory(dir_path): + """Recursively delete a valid directory""" + if os.path.exists(dir_path): + shutil.rmtree(dir_path) + + +def download_file(url, file_path): + """Download from url and save to specified file path.""" + with urllib.request.urlopen(url) as response, open(file_path, 'wb') as out_file: + shutil.copyfileobj(response, out_file) + + +def unpack_files(archive_file_path, output_dir=None): + """Unpack/extract an archive to specified output_directory""" + shutil.unpack_archive(archive_file_path, output_dir) + + +def is_windows_os(): + return platform.system() == 'Windows' + + +def is_mac_os(): + return platform.system() == 'Darwin' + + +def is_linux_os(): + return platform.system() == 'Linux' diff --git a/scripts/build_scripts/xcodebuild.py b/scripts/build_scripts/xcodebuild.py new file mode 100644 index 00000000..b60c70dc --- /dev/null +++ b/scripts/build_scripts/xcodebuild.py @@ -0,0 +1,119 @@ +# Copyright 2020 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. + +"""Helper module for working with xcode projects. + +The tool xcodebuild provides support to build xcode projects from the command +line. The motivation was to simplify usage of xcodebuild, since it was non-trivial +to figure out which flags were needed to get it working in a CI environment. +The options required by the methods in this module were found to work both +locally and on CI, with both the Unity and C++ projects. + +get_args_for_build() method doesn't performing operations with xcodebuild directly, +this module returns arg sequences. These sequences can be passed to e.g. +subprocess.run to execute the operations. + +get_args_for_build() support either device or simulator builds. For simulator +builds, it suffices to use get_args_for_build() to create a .app that can be +used with simulators. For unsigned device builds, generate .app via +get_args_for_build() step and then use generate_unsigned_ipa() to package +the .app to .ipa. + +""" + +import os +import shutil + +def get_args_for_build( + path, scheme, output_dir, apple_platfrom, apple_sdk, configuration): + """Constructs subprocess args for an unsigned xcode build. + + Args: + path (str): Full path to the project or workspace to build. Must end in + either .xcodeproj or .xcworkspace. + scheme (str): Name of the scheme to build. + output_dir (str): Directory for the resulting build artifacts. Will be + created if it doesn't already exist. + apple_platfrom (str): iOS or tvOS. + apple_sdk (str): Where this build will be run: real device or virtual device (simulator). + configuration (str): Value for the -configuration flag. + + Returns: + Sequence of strings, corresponding to valid args for a subprocess call. + + """ + args = [ + "xcodebuild", + "-sdk", _get_apple_env_from_target(apple_platfrom, apple_sdk), + "-scheme", scheme, + "-configuration", configuration, + "-quiet", + "BUILD_DIR=" + output_dir + ] + + if apple_sdk == "real": + args.extend(['CODE_SIGN_IDENTITY=""', + "CODE_SIGNING_REQUIRED=NO", + "CODE_SIGNING_ALLOWED=NO"]) + elif apple_sdk == "virtual" and apple_platfrom == "tvOS": + args.extend(['-arch', "x86_64"]) + + if not path: + raise ValueError("Must supply a path.") + if path.endswith(".xcworkspace"): + args.extend(("-workspace", path)) + elif path.endswith(".xcodeproj"): + args.extend(("-project", path)) + else: + raise ValueError("Path must end with .xcworkspace or .xcodeproj: %s" % path) + return args + + +def _get_apple_env_from_target(apple_platfrom, apple_sdk): + """Return a value for the -sdk flag based on the target (device/simulator).""" + if apple_platfrom == "iOS": + if apple_sdk == "real": + return "iphoneos" + elif apple_sdk == "virtual": + return "iphonesimulator" + else: + raise ValueError("Unrecognized apple_sdk: %s" % apple_sdk) + elif apple_platfrom == "tvOS": + if apple_sdk == "real": + return "appletvos" + elif apple_sdk == "virtual": + return "appletvsimulator" + else: + raise ValueError("Unrecognized apple_sdk: %s" % apple_sdk) + else: + raise ValueError("Unrecognized apple_sdk: %s" % apple_sdk) + + +def generate_unsigned_ipa(output_dir, configuration): + """create unsigned .ipa from .app, then remove .app afterwards + + Args: + output_dir (str): Same value as get_args_for_build. generated unsigned .ipa + will be placed within the subdirectory "Debug-iphoneos" or "Release-iphoneos". + configuration (str): Same value as get_args_for_build. + """ + iphone_build_dir = os.path.join(output_dir, configuration + "-iphoneos") + payload_path = os.path.join(iphone_build_dir, "Payload") + app_path = os.path.join(iphone_build_dir, "integration_test.app") + ipa_path = os.path.join(iphone_build_dir, "integration_test.ipa") + os.mkdir(payload_path) + shutil.move(app_path, payload_path) + shutil.make_archive(payload_path, 'zip', root_dir=iphone_build_dir, base_dir='Payload') + shutil.move('%s.%s'%(payload_path, 'zip'), ipa_path) + shutil.rmtree(payload_path) diff --git a/scripts/restore_secrets.py b/scripts/restore_secrets.py new file mode 100644 index 00000000..cd72cf58 --- /dev/null +++ b/scripts/restore_secrets.py @@ -0,0 +1,186 @@ +# Copyright 2022 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. + +"""Script for restoring secrets into the testapp projects. + +Usage: + +python restore_secrets.py --passphrase [--repo_dir ] +python restore_secrets.py --passphrase_file [--repo_dir ] + +--passphrase: Passphrase to decrypt the files. This option is insecure on a + multi-user machine; use the --passphrase_file option instead. +--passphrase_file: Specify a file to read the passphrase from (only reads the + first line). Use "-" (without quotes) for stdin. +--repo_dir: Path to C++ Quickstart Github repository. Defaults to current + directory. +--apis: Specify a list of particular product APIs and retrieve only their + secrets. + +This script will perform the following: + +- Google Service files (plist and json) will be restored into the + testapp directories. +- The reverse id will be patched into all Info.plist files, using the value from + the decrypted Google Service plist files as the source of truth. + +""" + +import os +import plistlib +import subprocess + +from absl import app +from absl import flags + + +FLAGS = flags.FLAGS + +flags.DEFINE_string("repo_dir", os.getcwd(), "Path to C++ SDK Github repo.") +flags.DEFINE_string("passphrase", None, "The passphrase itself.") +flags.DEFINE_string("passphrase_file", None, + "Path to file with passphrase. Use \"-\" (without quotes) for stdin.") +flags.DEFINE_string("artifact", None, "Artifact Path, google-services.json will be placed here.") +flags.DEFINE_list("apis",[], "Optional comma-separated list of APIs for which to retreive " + " secrets. All secrets will be fetched if this is flag is not defined.") + + +def main(argv): + if len(argv) > 1: + raise app.UsageError("Too many command-line arguments.") + + repo_dir = FLAGS.repo_dir + # The passphrase is sensitive, do not log. + if FLAGS.passphrase: + passphrase = FLAGS.passphrase + elif FLAGS.passphrase_file == "-": + passphrase = input() + elif FLAGS.passphrase_file: + with open(FLAGS.passphrase_file, "r") as f: + passphrase = f.readline().strip() + else: + raise ValueError("Must supply passphrase or passphrase_file arg.") + + if FLAGS.apis: + print("Retrieving secrets for product APIs: ", FLAGS.apis) + + secrets_dir = os.path.join(repo_dir, "scripts", "gha-encrypted") + encrypted_files = _find_encrypted_files(secrets_dir) + print("Found these encrypted files:\n%s" % "\n".join(encrypted_files)) + + for path in encrypted_files: + if "google-services" in path or "GoogleService" in path: + # We infer the destination from the file's directory, example: + # /scripts/gha-encrypted/auth/google-services.json.gpg turns into + # //auth/testapp/google-services.json + api = os.path.basename(os.path.dirname(path)) + if FLAGS.apis and api not in FLAGS.apis: + print("Skipping secret found in product api", api) + continue + print("Encrypted Google Service file found: %s" % path) + file_name = os.path.basename(path).replace(".gpg", "") + dest_paths = [os.path.join(repo_dir, api, "testapp", file_name)] + if FLAGS.artifact: + # ///auth/google-services.json + if "google-services" in path and os.path.isdir(os.path.join(repo_dir, FLAGS.artifact, api)): + dest_paths = [os.path.join(repo_dir, FLAGS.artifact, api, file_name)] + else: + continue + + decrypted_text = _decrypt(path, passphrase) + for dest_path in dest_paths: + with open(dest_path, "w") as f: + f.write(decrypted_text) + print("Copied decrypted google service file to %s" % dest_path) + # We use a Google Service file as the source of truth for the reverse id + # that needs to be patched into the Info.plist files. + if dest_path.endswith(".plist"): + _patch_reverse_id(dest_path) + _patch_bundle_id(dest_path) + + if FLAGS.artifact: + return + +def _find_encrypted_files(directory_to_search): + """Returns a list of full paths to all files encrypted with gpg.""" + encrypted_files = [] + for prefix, _, files in os.walk(directory_to_search): + for relative_path in files: + if relative_path.endswith(".gpg"): + encrypted_files.append(os.path.join(prefix, relative_path)) + return encrypted_files + + +def _decrypt(encrypted_file, passphrase): + """Returns the decrypted contents of the given .gpg file.""" + print("Decrypting %s" % encrypted_file) + # Note: if setting check=True, be sure to catch the error and not rethrow it + # or print a traceback, as the message will include the passphrase. + result = subprocess.run( + args=[ + "gpg", + "--passphrase", passphrase, + "--quiet", + "--batch", + "--yes", + "--decrypt", + encrypted_file], + check=False, + text=True, + capture_output=True) + if result.returncode: + # Remove any instances of the passphrase from error before logging it. + raise RuntimeError(result.stderr.replace(passphrase, "****")) + print("Decryption successful") + # rstrip to eliminate a linebreak that GPG may introduce. + return result.stdout.rstrip() + + +def _patch_reverse_id(service_plist_path): + """Patches the Info.plist file with the reverse id from the Service plist.""" + print("Attempting to patch reverse id in Info.plist") + with open(service_plist_path, "rb") as f: + service_plist = plistlib.load(f) + _patch_file( + path=os.path.join(os.path.dirname(service_plist_path), "testapp", "Info.plist"), + placeholder="REPLACE_WITH_REVERSED_CLIENT_ID", + value=service_plist["REVERSED_CLIENT_ID"]) + + +def _patch_bundle_id(service_plist_path): + """Patches the Info.plist file with the bundle id from the Service plist.""" + print("Attempting to patch bundle id in Info.plist") + with open(service_plist_path, "rb") as f: + service_plist = plistlib.load(f) + _patch_file( + path=os.path.join(os.path.dirname(service_plist_path), "testapp", "Info.plist"), + placeholder="$(PRODUCT_BUNDLE_IDENTIFIER)", + value=service_plist["BUNDLE_ID"]) + + +def _patch_file(path, placeholder, value): + """Patches instances of the placeholder with the given value.""" + # Note: value may be sensitive, so do not log. + with open(path, "r") as f_read: + text = f_read.read() + # Count number of times placeholder appears for debugging purposes. + replacements = text.count(placeholder) + patched_text = text.replace(placeholder, value) + with open(path, "w") as f_write: + f_write.write(patched_text) + print("Patched %d instances of %s in %s" % (replacements, placeholder, path)) + + +if __name__ == "__main__": + app.run(main) From c33b2c18a3205aa3dbab7006c0e2c5c7b5ae7806 Mon Sep 17 00:00:00 2001 From: Morgan Chen Date: Thu, 18 Jan 2024 14:37:10 -0800 Subject: [PATCH 10/33] Deprecate Dynamic Links --- dynamic_links/testapp/readme.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dynamic_links/testapp/readme.md b/dynamic_links/testapp/readme.md index df15e578..66190975 100644 --- a/dynamic_links/testapp/readme.md +++ b/dynamic_links/testapp/readme.md @@ -1,6 +1,11 @@ Firebase Dynamic Links Quickstart ================================= +> [!IMPORTANT] +> Firebase Dynamic Links is **deprecated** and should not be used in new projects. The service will shut down on August 25, 2025. +> +> Please see our [Dynamic Links Deprecation FAQ documentation](https://firebase.google.com/support/dynamic-links-faq) for more guidance. + The Firebase Dynamic Links Quickstart demonstrates logging a range of different events using the Firebase Dynamic Links C++ SDK. The application has no user interface and simply logs actions it's performing to the console. From 2a169779a0f20d34fc8aee38b93d3635b7a0cbf3 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Wed, 22 May 2024 10:46:29 -0700 Subject: [PATCH 11/33] Update testapp for breaking changes. --- auth/testapp/src/common_main.cc | 352 ++++++++++++++++---------------- 1 file changed, 174 insertions(+), 178 deletions(-) diff --git a/auth/testapp/src/common_main.cc b/auth/testapp/src/common_main.cc index 8907bcc4..d7be0ca9 100644 --- a/auth/testapp/src/common_main.cc +++ b/auth/testapp/src/common_main.cc @@ -32,6 +32,7 @@ using ::firebase::Variant; using ::firebase::auth::AdditionalUserInfo; using ::firebase::auth::Auth; using ::firebase::auth::AuthError; +using ::firebase::auth::AuthResult; using ::firebase::auth::Credential; using ::firebase::auth::EmailAuthProvider; using ::firebase::auth::FacebookAuthProvider; @@ -43,8 +44,8 @@ using ::firebase::auth::kAuthErrorInvalidProviderId; using ::firebase::auth::kAuthErrorNone; using ::firebase::auth::OAuthProvider; using ::firebase::auth::PhoneAuthProvider; +using ::firebase::auth::PhoneAuthCredential; using ::firebase::auth::PlayGamesAuthProvider; -using ::firebase::auth::SignInResult; using ::firebase::auth::TwitterAuthProvider; using ::firebase::auth::User; using ::firebase::auth::UserInfoInterface; @@ -118,39 +119,38 @@ static bool WaitForFuture(const FutureBase& future, const char* fn, return false; } -static bool WaitForSignInFuture(Future sign_in_future, const char* fn, +static bool WaitForSignInFuture(Future sign_in_future, const char* fn, AuthError expected_error, Auth* auth) { if (WaitForFuture(sign_in_future, fn, expected_error)) return true; - const User* const* sign_in_user_ptr = sign_in_future.result(); - const User* sign_in_user = - sign_in_user_ptr == nullptr ? nullptr : *sign_in_user_ptr; - const User* auth_user = auth->current_user(); + const User sign_in_user = sign_in_future.result() ? *sign_in_future.result() : + User(); + const User auth_user = auth->current_user(); if (expected_error == ::firebase::auth::kAuthErrorNone && sign_in_user != auth_user) { - LogMessage("ERROR: future's user (%x) and current_user (%x) don't match", - static_cast(reinterpret_cast(sign_in_user)), - static_cast(reinterpret_cast(auth_user))); + LogMessage("ERROR: future's user (%s) and current_user (%s) don't match", + sign_in_user.uid().c_str(), + auth_user.uid().c_str()); } return false; } -static bool WaitForSignInFuture(const Future& sign_in_future, +static bool WaitForSignInFuture(const Future& sign_in_future, const char* fn, AuthError expected_error, Auth* auth) { if (WaitForFuture(sign_in_future, fn, expected_error)) return true; - const SignInResult* sign_in_result = sign_in_future.result(); - const User* sign_in_user = sign_in_result ? sign_in_result->user : nullptr; - const User* auth_user = auth->current_user(); + const AuthResult* auth_result = sign_in_future.result(); + const User sign_in_user = auth_result ? auth_result->user : User(); + const User auth_user = auth->current_user(); if (expected_error == ::firebase::auth::kAuthErrorNone && sign_in_user != auth_user) { - LogMessage("ERROR: future's user (%x) and current_user (%x) don't match", - static_cast(reinterpret_cast(sign_in_user)), - static_cast(reinterpret_cast(auth_user))); + LogMessage("ERROR: future's user (%s) and current_user (%s) don't match", + sign_in_user.uid().c_str(), + auth_user.uid().c_str()); } return false; @@ -159,7 +159,7 @@ static bool WaitForSignInFuture(const Future& sign_in_future, // Wait for the current user to sign out. Typically you should use the // state listener to determine whether the user has signed out. static bool WaitForSignOut(firebase::auth::Auth* auth) { - while (auth->current_user() != nullptr) { + while (!auth->current_user().is_valid()) { if (ProcessEvents(100)) return true; } // Wait - hopefully - long enough for listeners to be signalled. @@ -251,17 +251,17 @@ static void LogVariantMap(const std::map& variant_map, } // Display the sign-in result. -static void LogSignInResult(const SignInResult& result) { - if (!result.user) { +static void LogAuthResult(const AuthResult result) { + if (!result.user.is_valid()) { LogMessage("ERROR: User not signed in"); return; } - LogMessage("* User ID %s", result.user->uid().c_str()); - const AdditionalUserInfo& info = result.info; + LogMessage("* User ID %s", result.user.uid().c_str()); + const AdditionalUserInfo& info = result.additional_user_info; LogMessage("* Provider ID %s", info.provider_id.c_str()); LogMessage("* User Name %s", info.user_name.c_str()); LogVariantMap(info.profile, 0); - const UserMetadata& metadata = result.meta; + UserMetadata metadata = result.user.metadata(); LogMessage("* Sign in timestamp %d", static_cast(metadata.last_sign_in_timestamp)); LogMessage("* Creation timestamp %d", @@ -274,8 +274,8 @@ class AuthStateChangeCounter : public firebase::auth::AuthStateListener { virtual void OnAuthStateChanged(Auth* auth) { // NOLINT num_state_changes_++; - LogMessage("OnAuthStateChanged User %p (state changes %d)", - auth->current_user(), num_state_changes_); + LogMessage("OnAuthStateChanged User %s (state changes %d)", + auth->current_user().uid().c_str(), num_state_changes_); } void CompleteTest(const char* test_name, int expected_state_changes) { @@ -302,8 +302,8 @@ class IdTokenChangeCounter : public firebase::auth::IdTokenListener { virtual void OnIdTokenChanged(Auth* auth) { // NOLINT num_token_changes_++; - LogMessage("OnIdTokenChanged User %p (token changes %d)", - auth->current_user(), num_token_changes_); + LogMessage("OnIdTokenChanged User %s (token changes %d)", + auth->current_user().uid().c_str(), num_token_changes_); } void CompleteTest(const char* test_name, int token_changes) { @@ -331,7 +331,6 @@ class UserLogin { : auth_(auth), email_(email), password_(password), - user_(nullptr), log_errors_(true) {} explicit UserLogin(Auth* auth) : auth_(auth) { @@ -340,48 +339,48 @@ class UserLogin { } ~UserLogin() { - if (user_ != nullptr) { + if (user_.is_valid()) { log_errors_ = false; Delete(); } } void Register() { - Future register_test_account = + Future register_test_account = auth_->CreateUserWithEmailAndPassword(email(), password()); WaitForSignInFuture(register_test_account, "CreateUserWithEmailAndPassword() to create temp user", kAuthErrorNone, auth_); - user_ = register_test_account.result() ? *register_test_account.result() - : nullptr; + user_ = register_test_account.result() ? register_test_account.result()->user + : User(); } void Login() { Credential email_cred = EmailAuthProvider::GetCredential(email(), password()); - Future sign_in_cred = auth_->SignInWithCredential(email_cred); + Future sign_in_cred = auth_->SignInWithCredential(email_cred); WaitForSignInFuture(sign_in_cred, "Auth::SignInWithCredential() for UserLogin", kAuthErrorNone, auth_); } void Delete() { - if (user_ != nullptr) { - Future delete_future = user_->Delete(); + if (user_.is_valid()) { + Future delete_future = user_.Delete(); if (delete_future.status() == ::firebase::kFutureStatusInvalid) { Login(); - delete_future = user_->Delete(); + delete_future = user_.Delete(); } WaitForFuture(delete_future, "User::Delete()", kAuthErrorNone, log_errors_); } - user_ = nullptr; + user_ = User(); } const char* email() const { return email_.c_str(); } const char* password() const { return password_.c_str(); } - User* user() const { return user_; } + User user() const { return user_; } void set_email(const char* email) { email_ = email; } void set_password(const char* password) { password_ = password; } @@ -389,7 +388,7 @@ class UserLogin { Auth* auth_; std::string email_; std::string password_; - User* user_; + User user_; bool log_errors_; }; @@ -401,7 +400,7 @@ class PhoneListener : public PhoneAuthProvider::Listener { num_calls_on_code_sent_(0), num_calls_on_code_auto_retrieval_time_out_(0) {} - void OnVerificationCompleted(Credential /*credential*/) override { + void OnVerificationCompleted(PhoneAuthCredential /*credential*/) override { LogMessage("PhoneListener: successful automatic verification."); num_calls_on_verification_complete_++; } @@ -493,13 +492,13 @@ extern "C" int common_main(int argc, const char* argv[]) { // It's possible for current_user() to be non-null if the previous run // left us in a signed-in state. - if (auth->current_user() == nullptr) { + if (!auth->current_user().is_valid()) { LogMessage("No user signed in at creation time."); } else { LogMessage( "Current user uid(%s) name(%s) already signed in, so signing them out.", - auth->current_user()->uid().c_str(), - auth->current_user()->display_name().c_str()); + auth->current_user().uid().c_str(), + auth->current_user().display_name().c_str()); auth->SignOut(); } @@ -523,7 +522,7 @@ extern "C" int common_main(int argc, const char* argv[]) { if (kTestCustomEmail) { // Test Auth::SignInWithEmailAndPassword(). // Sign in with email and password that have already been registered. - Future sign_in_future = + Future sign_in_future = auth->SignInWithEmailAndPassword(kCustomEmail, kCustomPassword); WaitForSignInFuture(sign_in_future, "Auth::SignInWithEmailAndPassword() existing " @@ -532,11 +531,11 @@ extern "C" int common_main(int argc, const char* argv[]) { // Test SignOut() after signed in with email and password. if (sign_in_future.status() == ::firebase::kFutureStatusComplete) { auth->SignOut(); - if (auth->current_user() != nullptr) { + if (auth->current_user().is_valid()) { LogMessage( - "ERROR: current_user() returning %x instead of nullptr after " + "ERROR: current_user() returning %s instead of nullptr after " "SignOut()", - auth->current_user()); + auth->current_user().uid().c_str()); } } } @@ -560,7 +559,7 @@ extern "C" int common_main(int argc, const char* argv[]) { token_counter.CompleteTest("SignOut() when already signed-out", 0); // Test notification on SignIn(). - Future sign_in_future = auth->SignInAnonymously(); + Future sign_in_future = auth->SignInAnonymously(); WaitForSignInFuture(sign_in_future, "Auth::SignInAnonymously()", kAuthErrorNone, auth); // Notified when the user is about to change and after the user has @@ -569,21 +568,21 @@ extern "C" int common_main(int argc, const char* argv[]) { token_counter.CompleteTest("SignInAnonymously()", 1, 5); // Refresh the token. - if (auth->current_user() != nullptr) { - Future token_future = auth->current_user()->GetToken(true); + if (auth->current_user().is_valid()) { + Future token_future = auth->current_user().GetToken(true); WaitForFuture(token_future, "GetToken()", kAuthErrorNone); counter.CompleteTest("GetToken()", 0); token_counter.CompleteTest("GetToken()", 1); } // Test notification on SignOut(), when signed-in. - LogMessage("Current user %p", auth->current_user()); // DEBUG + LogMessage("Current user %s", auth->current_user().uid().c_str()); // DEBUG auth->SignOut(); // Wait for the sign out to complete. WaitForSignOut(auth); counter.CompleteTest("SignOut()", 1); token_counter.CompleteTest("SignOut()", 1); - LogMessage("Current user %p", auth->current_user()); // DEBUG + LogMessage("Current user %s", auth->current_user().uid().c_str()); // DEBUG auth->RemoveAuthStateListener(&counter); auth->RemoveIdTokenListener(&token_counter); @@ -638,18 +637,18 @@ extern "C" int common_main(int argc, const char* argv[]) { const Credential phone_credential = phone_provider.GetCredential( listener.verification_id().c_str(), verification_code.c_str()); - Future phone_future = + Futurephone_future = auth->SignInWithCredential(phone_credential); WaitForSignInFuture(phone_future, "Auth::SignInWithCredential() phone credential", kAuthErrorNone, auth); if (phone_future.error() == kAuthErrorNone) { - User* user = *phone_future.result(); - Future update_future = - user->UpdatePhoneNumberCredential(phone_credential); + User user = *phone_future.result(); + Future update_future = + user.UpdatePhoneNumberCredential(phone_credential); WaitForSignInFuture( update_future, - "user->UpdatePhoneNumberCredential(phone_credential)", + "user.UpdatePhoneNumberCredential(phone_credential)", kAuthErrorNone, auth); } @@ -664,12 +663,12 @@ extern "C" int common_main(int argc, const char* argv[]) { { UserLogin user_login(auth); // Generate a random name/password user_login.Register(); - if (!user_login.user()) { + if (!user_login.user().is_valid()) { LogMessage("ERROR: Could not register new user."); } else { // Test Auth::SignInAnonymously(). { - Future sign_in_future = auth->SignInAnonymously(); + Future sign_in_future = auth->SignInAnonymously(); WaitForSignInFuture(sign_in_future, "Auth::SignInAnonymously()", kAuthErrorNone, auth); ExpectTrue("SignInAnonymouslyLastResult matches returned Future", @@ -678,11 +677,11 @@ extern "C" int common_main(int argc, const char* argv[]) { // Test SignOut() after signed in anonymously. if (sign_in_future.status() == ::firebase::kFutureStatusComplete) { auth->SignOut(); - if (auth->current_user() != nullptr) { + if (auth->current_user().is_valid()) { LogMessage( - "ERROR: current_user() returning %x instead of nullptr after " + "ERROR: current_user() returning valid user %s instead of invalid user after " "SignOut()", - auth->current_user()); + auth->current_user().uid().c_str()); } } } @@ -711,7 +710,7 @@ extern "C" int common_main(int argc, const char* argv[]) { // Test Auth::SignInWithEmailAndPassword(). // Sign in with email and password that have already been registered. { - Future sign_in_future = auth->SignInWithEmailAndPassword( + Future sign_in_future = auth->SignInWithEmailAndPassword( user_login.email(), user_login.password()); WaitForSignInFuture( sign_in_future, @@ -724,39 +723,38 @@ extern "C" int common_main(int argc, const char* argv[]) { // Test SignOut() after signed in with email and password. if (sign_in_future.status() == ::firebase::kFutureStatusComplete) { auth->SignOut(); - if (auth->current_user() != nullptr) { + if (auth->current_user().is_valid()) { LogMessage( - "ERROR: current_user() returning %x instead of nullptr after " - "SignOut()", - auth->current_user()); + "ERROR: current_user() returning valid user %s instead of invalid user after " + "SignOut()", auth->current_user().uid().c_str()); } } } // Test User::UpdateUserProfile { - Future sign_in_future = auth->SignInWithEmailAndPassword( + Future sign_in_future = auth->SignInWithEmailAndPassword( user_login.email(), user_login.password()); WaitForSignInFuture( sign_in_future, "Auth::SignInWithEmailAndPassword() existing email and password", kAuthErrorNone, auth); if (sign_in_future.error() == kAuthErrorNone) { - User* user = *sign_in_future.result(); + User user = sign_in_future.result()->user; const char* kDisplayName = "Hello World"; const char* kPhotoUrl = "http://test.com/image.jpg"; User::UserProfile user_profile; user_profile.display_name = kDisplayName; user_profile.photo_url = kPhotoUrl; Future update_profile_future = - user->UpdateUserProfile(user_profile); + user.UpdateUserProfile(user_profile); WaitForFuture(update_profile_future, "User::UpdateUserProfile", kAuthErrorNone); if (update_profile_future.error() == kAuthErrorNone) { ExpectStringsEqual("User::display_name", kDisplayName, - user->display_name().c_str()); + user.display_name().c_str()); ExpectStringsEqual("User::photo_url", kPhotoUrl, - user->photo_url().c_str()); + user.photo_url().c_str()); } } } @@ -764,33 +762,33 @@ extern "C" int common_main(int argc, const char* argv[]) { // Sign in anonymously, link an email credential, reauthenticate with the // credential, unlink the credential and finally sign out. { - Future sign_in_anonymously_future = auth->SignInAnonymously(); + Future sign_in_anonymously_future = auth->SignInAnonymously(); WaitForSignInFuture(sign_in_anonymously_future, "Auth::SignInAnonymously", kAuthErrorNone, auth); if (sign_in_anonymously_future.error() == kAuthErrorNone) { - User* user = *sign_in_anonymously_future.result(); + User user = sign_in_anonymously_future.result()->user; std::string email = CreateNewEmail(); Credential credential = EmailAuthProvider::GetCredential(email.c_str(), kTestPassword); // Link with an email / password credential. - Future link_future = - user->LinkAndRetrieveDataWithCredential(credential); + Future link_future = + user.LinkWithCredential(credential); WaitForSignInFuture(link_future, "User::LinkAndRetrieveDataWithCredential", kAuthErrorNone, auth); if (link_future.error() == kAuthErrorNone) { - LogSignInResult(*link_future.result()); - Future reauth_future = - user->ReauthenticateAndRetrieveData(credential); + LogAuthResult(*link_future.result()); + Future reauth_future = + user.ReauthenticateAndRetrieveData(credential); WaitForSignInFuture(reauth_future, "User::ReauthenticateAndRetrieveData", kAuthErrorNone, auth); if (reauth_future.error() == kAuthErrorNone) { - LogSignInResult(*reauth_future.result()); + LogAuthResult(*reauth_future.result()); } // Unlink email / password from credential. - Future unlink_future = - user->Unlink(credential.provider().c_str()); + Future unlink_future = + user.Unlink(credential.provider().c_str()); WaitForSignInFuture(unlink_future, "User::Unlink", kAuthErrorNone, auth); } @@ -800,7 +798,7 @@ extern "C" int common_main(int argc, const char* argv[]) { // Sign in user with bad email. Should fail. { - Future sign_in_future_bad_email = + Future sign_in_future_bad_email = auth->SignInWithEmailAndPassword(kTestEmailBad, kTestPassword); WaitForSignInFuture(sign_in_future_bad_email, "Auth::SignInWithEmailAndPassword() bad email", @@ -809,7 +807,7 @@ extern "C" int common_main(int argc, const char* argv[]) { // Sign in user with correct email but bad password. Should fail. { - Future sign_in_future_bad_password = + Future sign_in_future_bad_password = auth->SignInWithEmailAndPassword(user_login.email(), kTestPasswordBad); WaitForSignInFuture(sign_in_future_bad_password, @@ -819,7 +817,7 @@ extern "C" int common_main(int argc, const char* argv[]) { // Try to create with existing email. Should fail. { - Future create_future_bad = auth->CreateUserWithEmailAndPassword( + Future create_future_bad = auth->CreateUserWithEmailAndPassword( user_login.email(), user_login.password()); WaitForSignInFuture( create_future_bad, @@ -836,13 +834,11 @@ extern "C" int common_main(int argc, const char* argv[]) { { Credential email_cred_ok = EmailAuthProvider::GetCredential( user_login.email(), user_login.password()); - Future sign_in_cred_ok = + Futuresign_in_cred_ok = auth->SignInWithCredential(email_cred_ok); WaitForSignInFuture(sign_in_cred_ok, "Auth::SignInWithCredential() existing email", kAuthErrorNone, auth); - ExpectTrue("SignInWithCredentialLastResult matches returned Future", - sign_in_cred_ok == auth->SignInWithCredentialLastResult()); } // Test Auth::SignInAndRetrieveDataWithCredential using email & password. @@ -850,7 +846,7 @@ extern "C" int common_main(int argc, const char* argv[]) { { Credential email_cred = EmailAuthProvider::GetCredential( user_login.email(), user_login.password()); - Future sign_in_future = + Future sign_in_future = auth->SignInAndRetrieveDataWithCredential(email_cred); WaitForSignInFuture(sign_in_future, "Auth::SignInAndRetrieveDataWithCredential " @@ -862,10 +858,10 @@ extern "C" int common_main(int argc, const char* argv[]) { sign_in_future == auth->SignInAndRetrieveDataWithCredentialLastResult()); if (sign_in_future.error() == kAuthErrorNone) { - const SignInResult* sign_in_result = sign_in_future.result(); - if (sign_in_result != nullptr && sign_in_result->user) { + const AuthResult* sign_in_result = sign_in_future.result(); + if (sign_in_result != nullptr && sign_in_result->user.is_valid()) { LogMessage("SignInAndRetrieveDataWithCredential"); - LogSignInResult(*sign_in_result); + LogAuthResult(*sign_in_result); } else { LogMessage( "ERROR: SignInAndRetrieveDataWithCredential returned no " @@ -878,7 +874,7 @@ extern "C" int common_main(int argc, const char* argv[]) { { Credential facebook_cred_bad = FacebookAuthProvider::GetCredential(kTestAccessTokenBad); - Future facebook_bad = + Futurefacebook_bad = auth->SignInWithCredential(facebook_cred_bad); WaitForSignInFuture( facebook_bad, @@ -890,7 +886,7 @@ extern "C" int common_main(int argc, const char* argv[]) { { Credential git_hub_cred_bad = GitHubAuthProvider::GetCredential(kTestAccessTokenBad); - Future git_hub_bad = + Futuregit_hub_bad = auth->SignInWithCredential(git_hub_cred_bad); WaitForSignInFuture( git_hub_bad, "Auth::SignInWithCredential() bad GitHub credentials", @@ -901,7 +897,7 @@ extern "C" int common_main(int argc, const char* argv[]) { { Credential google_cred_bad = GoogleAuthProvider::GetCredential( kTestIdTokenBad, kTestAccessTokenBad); - Future google_bad = auth->SignInWithCredential(google_cred_bad); + Futuregoogle_bad = auth->SignInWithCredential(google_cred_bad); WaitForSignInFuture( google_bad, "Auth::SignInWithCredential() bad Google credentials", kAuthErrorInvalidCredential, auth); @@ -911,7 +907,7 @@ extern "C" int common_main(int argc, const char* argv[]) { { Credential google_cred_bad = GoogleAuthProvider::GetCredential(kTestIdTokenBad, nullptr); - Future google_bad = auth->SignInWithCredential(google_cred_bad); + Futuregoogle_bad = auth->SignInWithCredential(google_cred_bad); WaitForSignInFuture( google_bad, "Auth::SignInWithCredential() bad Google credentials", kAuthErrorInvalidCredential, auth); @@ -922,7 +918,7 @@ extern "C" int common_main(int argc, const char* argv[]) { { Credential play_games_cred_bad = PlayGamesAuthProvider::GetCredential(kTestServerAuthCodeBad); - Future play_games_bad = + Futureplay_games_bad = auth->SignInWithCredential(play_games_cred_bad); WaitForSignInFuture( play_games_bad, @@ -959,7 +955,7 @@ extern "C" int common_main(int argc, const char* argv[]) { if (gc_credential_ptr == nullptr) { LogMessage("Failed to retrieve Game Center credential."); } else { - Future game_center_user = + Futuregame_center_user = auth->SignInWithCredential(*gc_credential_ptr); WaitForFuture(game_center_user, "Auth::SignInWithCredential() test Game Center " @@ -974,7 +970,7 @@ extern "C" int common_main(int argc, const char* argv[]) { { Credential twitter_cred_bad = TwitterAuthProvider::GetCredential( kTestIdTokenBad, kTestAccessTokenBad); - Future twitter_bad = + Futuretwitter_bad = auth->SignInWithCredential(twitter_cred_bad); WaitForSignInFuture( twitter_bad, "Auth::SignInWithCredential() bad Twitter credentials", @@ -999,7 +995,7 @@ extern "C" int common_main(int argc, const char* argv[]) { { Credential oauth_cred_bad = OAuthProvider::GetCredential( kTestIdProviderIdBad, kTestIdTokenBad, kTestAccessTokenBad); - Future oauth_bad = auth->SignInWithCredential(oauth_cred_bad); + Futureoauth_bad = auth->SignInWithCredential(oauth_cred_bad); WaitForSignInFuture( oauth_bad, "Auth::SignInWithCredential() bad OAuth credentials", kAuthErrorFailure, auth); @@ -1010,7 +1006,7 @@ extern "C" int common_main(int argc, const char* argv[]) { Credential oauth_cred_bad = OAuthProvider::GetCredential(kTestIdProviderIdBad, kTestIdTokenBad, kTestNonceBad, kTestAccessTokenBad); - Future oauth_bad = auth->SignInWithCredential(oauth_cred_bad); + Futureoauth_bad = auth->SignInWithCredential(oauth_cred_bad); WaitForSignInFuture( oauth_bad, "Auth::SignInWithCredential() bad OAuth credentials", kAuthErrorFailure, auth); @@ -1042,48 +1038,48 @@ extern "C" int common_main(int argc, const char* argv[]) { // --- User tests ------------------------------------------------------------ // Test anonymous user info strings. { - Future anon_sign_in_for_user = auth->SignInAnonymously(); + Future anon_sign_in_for_user = auth->SignInAnonymously(); WaitForSignInFuture(anon_sign_in_for_user, "Auth::SignInAnonymously() for User", kAuthErrorNone, auth); if (anon_sign_in_for_user.status() == ::firebase::kFutureStatusComplete) { - User* anonymous_user = anon_sign_in_for_user.result() - ? *anon_sign_in_for_user.result() - : nullptr; - if (anonymous_user != nullptr) { - LogMessage("Anonymous uid is %s", anonymous_user->uid().c_str()); + User anonymous_user = anon_sign_in_for_user.result() + ? anon_sign_in_for_user.result()->user + : User(); + if (anonymous_user.is_valid()) { + LogMessage("Anonymous uid is %s", anonymous_user.uid().c_str()); ExpectStringsEqual("Anonymous user email", "", - anonymous_user->email().c_str()); + anonymous_user.email().c_str()); ExpectStringsEqual("Anonymous user display_name", "", - anonymous_user->display_name().c_str()); + anonymous_user.display_name().c_str()); ExpectStringsEqual("Anonymous user photo_url", "", - anonymous_user->photo_url().c_str()); + anonymous_user.photo_url().c_str()); ExpectStringsEqual("Anonymous user provider_id", kFirebaseProviderId, - anonymous_user->provider_id().c_str()); + anonymous_user.provider_id().c_str()); ExpectTrue("Anonymous user is_anonymous()", - anonymous_user->is_anonymous()); + anonymous_user.is_anonymous()); ExpectFalse("Anonymous user is_email_verified()", - anonymous_user->is_email_verified()); + anonymous_user.is_email_verified()); ExpectTrue("Anonymous user metadata().last_sign_in_timestamp != 0", - anonymous_user->metadata().last_sign_in_timestamp != 0); + anonymous_user.metadata().last_sign_in_timestamp != 0); ExpectTrue("Anonymous user metadata().creation_timestamp != 0", - anonymous_user->metadata().creation_timestamp != 0); + anonymous_user.metadata().creation_timestamp != 0); // Test User::LinkWithCredential(), linking with email & password. const std::string newer_email = CreateNewEmail(); Credential user_cred = EmailAuthProvider::GetCredential( newer_email.c_str(), kTestPassword); { - Future link_future = - anonymous_user->LinkWithCredential(user_cred); + Future link_future = + anonymous_user.LinkWithCredential(user_cred); WaitForSignInFuture(link_future, "User::LinkWithCredential()", kAuthErrorNone, auth); } // Test User::LinkWithCredential(), linking with same email & password. { - Future link_future = - anonymous_user->LinkWithCredential(user_cred); + Future link_future = + anonymous_user.LinkWithCredential(user_cred); WaitForSignInFuture(link_future, "User::LinkWithCredential() again", ::firebase::auth::kAuthErrorProviderAlreadyLinked, auth); @@ -1092,14 +1088,14 @@ extern "C" int common_main(int argc, const char* argv[]) { // Test User::LinkWithCredential(), linking with bad credential. // Call should fail and Auth's current user should be maintained. { - const User* pre_link_user = auth->current_user(); + const User pre_link_user = auth->current_user(); ExpectTrue("Test precondition requires active user", - pre_link_user != nullptr); + pre_link_user.is_valid()); Credential twitter_cred_bad = TwitterAuthProvider::GetCredential( kTestIdTokenBad, kTestAccessTokenBad); - Future link_bad_future = - anonymous_user->LinkWithCredential(twitter_cred_bad); + Future link_bad_future = + anonymous_user.LinkWithCredential(twitter_cred_bad); WaitForFuture(link_bad_future, "User::LinkWithCredential() with bad credential", kAuthErrorInvalidCredential); @@ -1110,12 +1106,12 @@ extern "C" int common_main(int argc, const char* argv[]) { // Test Auth::SignInWithCredential(), signing in with bad credential. // Call should fail, and Auth's current user should be maintained. { - const User* pre_signin_user = auth->current_user(); + const User pre_signin_user = auth->current_user(); ExpectTrue("Test precondition requires active user", - pre_signin_user != nullptr); + pre_signin_user.is_valid()); Credential twitter_cred_bad = TwitterAuthProvider::GetCredential( kTestIdTokenBad, kTestAccessTokenBad); - Future signin_bad_future = + Futuresignin_bad_future = auth->SignInWithCredential(twitter_cred_bad); WaitForFuture(signin_bad_future, "Auth::SignInWithCredential() with bad credential", @@ -1127,41 +1123,41 @@ extern "C" int common_main(int argc, const char* argv[]) { UserLogin user_login(auth); user_login.Register(); - if (!user_login.user()) { + if (!user_login.user().is_valid()) { LogMessage("Error - Could not create new user."); } else { // Test email user info strings. - Future email_sign_in_for_user = + Future email_sign_in_for_user = auth->SignInWithEmailAndPassword(user_login.email(), user_login.password()); WaitForSignInFuture(email_sign_in_for_user, "Auth::SignInWithEmailAndPassword() for User", kAuthErrorNone, auth); - User* email_user = email_sign_in_for_user.result() - ? *email_sign_in_for_user.result() - : nullptr; - if (email_user != nullptr) { - LogMessage("Email uid is %s", email_user->uid().c_str()); + User email_user = email_sign_in_for_user.result() + ? email_sign_in_for_user.result()->user + : User(); + if (email_user.is_valid()) { + LogMessage("Email uid is %s", email_user.uid().c_str()); ExpectStringsEqual("Email user email", user_login.email(), - email_user->email().c_str()); + email_user.email().c_str()); ExpectStringsEqual("Email user display_name", "", - email_user->display_name().c_str()); + email_user.display_name().c_str()); ExpectStringsEqual("Email user photo_url", "", - email_user->photo_url().c_str()); + email_user.photo_url().c_str()); ExpectStringsEqual("Email user provider_id", kFirebaseProviderId, - email_user->provider_id().c_str()); + email_user.provider_id().c_str()); ExpectFalse("Email user is_anonymous()", - email_user->is_anonymous()); + email_user.is_anonymous()); ExpectFalse("Email user is_email_verified()", - email_user->is_email_verified()); + email_user.is_email_verified()); ExpectTrue("Email user metadata().last_sign_in_timestamp != 0", - email_user->metadata().last_sign_in_timestamp != 0); + email_user.metadata().last_sign_in_timestamp != 0); ExpectTrue("Email user metadata().creation_timestamp != 0", - email_user->metadata().creation_timestamp != 0); + email_user.metadata().creation_timestamp != 0); // Test User::GetToken(). // with force_refresh = false. - Future token_no_refresh = email_user->GetToken(false); + Future token_no_refresh = email_user.GetToken(false); WaitForFuture(token_no_refresh, "User::GetToken(false)", kAuthErrorNone); LogMessage("User::GetToken(false) = %s", @@ -1171,7 +1167,7 @@ extern "C" int common_main(int argc, const char* argv[]) { // with force_refresh = true. Future token_force_refresh = - email_user->GetToken(true); + email_user.GetToken(true); WaitForFuture(token_force_refresh, "User::GetToken(true)", kAuthErrorNone); LogMessage("User::GetToken(true) = %s", @@ -1180,59 +1176,59 @@ extern "C" int common_main(int argc, const char* argv[]) { : ""); // Test Reload(). - Future reload_future = email_user->Reload(); + Future reload_future = email_user.Reload(); WaitForFuture(reload_future, "User::Reload()", kAuthErrorNone); // Test User::Unlink(). - Future unlink_future = email_user->Unlink("firebase"); + Future unlink_future = email_user.Unlink("firebase"); WaitForSignInFuture(unlink_future, "User::Unlink()", ::firebase::auth::kAuthErrorNoSuchProvider, auth); // Sign in again if user is now invalid. - if (auth->current_user() == nullptr) { - Future email_sign_in_again = + if (!auth->current_user().is_valid()) { + Future email_sign_in_again = auth->SignInWithEmailAndPassword(user_login.email(), user_login.password()); WaitForSignInFuture(email_sign_in_again, "Auth::SignInWithEmailAndPassword() again", kAuthErrorNone, auth); email_user = email_sign_in_again.result() - ? *email_sign_in_again.result() - : nullptr; + ? email_sign_in_again.result()->user + : User(); } } - if (email_user != nullptr) { + if (email_user.is_valid()) { // Test User::provider_data(). - const std::vector& provider_data = - email_user->provider_data(); + const std::vector provider_data = + email_user.provider_data(); LogMessage("User::provider_data() returned %d interface%s", provider_data.size(), provider_data.size() == 1 ? "" : "s"); for (size_t i = 0; i < provider_data.size(); ++i) { - const UserInfoInterface* user_info = provider_data[i]; + const UserInfoInterface user_info = provider_data[i]; LogMessage( " UID() = %s\n" " Email() = %s\n" " DisplayName() = %s\n" " PhotoUrl() = %s\n" " ProviderId() = %s", - user_info->uid().c_str(), user_info->email().c_str(), - user_info->display_name().c_str(), - user_info->photo_url().c_str(), - user_info->provider_id().c_str()); + user_info.uid().c_str(), user_info.email().c_str(), + user_info.display_name().c_str(), + user_info.photo_url().c_str(), + user_info.provider_id().c_str()); } // Test User::UpdateEmail(). const std::string newest_email = CreateNewEmail(); Future update_email_future = - email_user->UpdateEmail(newest_email.c_str()); + email_user.UpdateEmail(newest_email.c_str()); WaitForFuture(update_email_future, "User::UpdateEmail()", kAuthErrorNone); // Test User::UpdatePassword(). Future update_password_future = - email_user->UpdatePassword(kTestPasswordUpdated); + email_user.UpdatePassword(kTestPasswordUpdated); WaitForFuture(update_password_future, "User::UpdatePassword()", kAuthErrorNone); @@ -1240,13 +1236,13 @@ extern "C" int common_main(int argc, const char* argv[]) { Credential email_cred_reauth = EmailAuthProvider::GetCredential( newest_email.c_str(), kTestPasswordUpdated); Future reauthenticate_future = - email_user->Reauthenticate(email_cred_reauth); + email_user.Reauthenticate(email_cred_reauth); WaitForFuture(reauthenticate_future, "User::Reauthenticate()", kAuthErrorNone); // Test User::SendEmailVerification(). Future send_email_verification_future = - email_user->SendEmailVerification(); + email_user.SendEmailVerification(); WaitForFuture(send_email_verification_future, "User::SendEmailVerification()", kAuthErrorNone); } @@ -1256,29 +1252,29 @@ extern "C" int common_main(int argc, const char* argv[]) { // Test User::Delete(). const std::string new_email_for_delete = CreateNewEmail(); - Future create_future_for_delete = + Future create_future_for_delete = auth->CreateUserWithEmailAndPassword(new_email_for_delete.c_str(), kTestPassword); WaitForSignInFuture( create_future_for_delete, "Auth::CreateUserWithEmailAndPassword() new email for delete", kAuthErrorNone, auth); - User* email_user_for_delete = create_future_for_delete.result() - ? *create_future_for_delete.result() - : nullptr; - if (email_user_for_delete != nullptr) { - Future delete_future = email_user_for_delete->Delete(); + User email_user_for_delete = create_future_for_delete.result() + ? create_future_for_delete.result()->user + : User(); + if (email_user_for_delete.is_valid()) { + Future delete_future = email_user_for_delete.Delete(); WaitForFuture(delete_future, "User::Delete()", kAuthErrorNone); } } { // We end with a login so that we can test if a second run will detect // that we're already logged-in. - Future sign_in_future = auth->SignInAnonymously(); + Future sign_in_future = auth->SignInAnonymously(); WaitForSignInFuture(sign_in_future, "Auth::SignInAnonymously() at end", kAuthErrorNone, auth); - LogMessage("Anonymous uid(%s)", auth->current_user()->uid().c_str()); + LogMessage("Anonymous uid(%s)", auth->current_user().uid().c_str()); } #ifdef INTERNAL_EXPERIMENTAL @@ -1305,13 +1301,13 @@ extern "C" int common_main(int argc, const char* argv[]) { firebase::auth::FederatedOAuthProvider provider; provider.SetProviderData(provider_data); LogMessage("invoking linkwithprovider"); - Future sign_in_future = - user_login.user()->LinkWithProvider(&provider); + Future sign_in_future = + user_login.user().LinkWithProvider(&provider); WaitForSignInFuture(sign_in_future, "LinkWithProvider", kAuthErrorNone, auth); if (sign_in_future.error() == kAuthErrorNone) { - const SignInResult* result_ptr = sign_in_future.result(); - LogMessage("user email %s", result_ptr->user->email().c_str()); + const AuthResult* result_ptr = sign_in_future.result(); + LogMessage("user email %s", result_ptr->user.email().c_str()); LogMessage("Additonal user info provider_id: %s", result_ptr->info.provider_id.c_str()); LogMessage("LinkWithProviderDone"); @@ -1330,12 +1326,12 @@ extern "C" int common_main(int argc, const char* argv[]) { firebase::auth::FederatedOAuthProvider provider; provider.SetProviderData(provider_data); LogMessage("SignInWithProvider SETUP COMPLETE"); - Future sign_in_future = auth->SignInWithProvider(&provider); + Future sign_in_future = auth->SignInWithProvider(&provider); WaitForSignInFuture(sign_in_future, "SignInWithProvider", kAuthErrorNone, auth); if (sign_in_future.error() == kAuthErrorNone && sign_in_future.result() != nullptr) { - LogSignInResult(*sign_in_future.result()); + LogAuthResult(*sign_in_future.result()); } } @@ -1351,13 +1347,13 @@ extern "C" int common_main(int argc, const char* argv[]) { firebase::auth::FederatedOAuthProvider provider; provider.SetProviderData(provider_data); - Future sign_in_future = - auth->current_user()->ReauthenticateWithProvider(&provider); + Future sign_in_future = + auth->current_user().ReauthenticateWithProvider(&provider); WaitForSignInFuture(sign_in_future, "ReauthenticateWithProvider", kAuthErrorNone, auth); if (sign_in_future.error() == kAuthErrorNone && sign_in_future.result() != nullptr) { - LogSignInResult(*sign_in_future.result()); + LogAuthResult(*sign_in_future.result()); } } } @@ -1365,7 +1361,7 @@ extern "C" int common_main(int argc, const char* argv[]) { // Clean up provider-linked user so we can run the test app again // and not get "user with that email already exists" errors. if (auth->current_user()) { - WaitForFuture(auth->current_user()->Delete(), "Delete User", + WaitForFuture(auth->current_user().Delete(), "Delete User", kAuthErrorNone, /*log_error=*/true); } From c44177e940241ccb7aa2f80aa9de6b4250792881 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Wed, 22 May 2024 10:53:42 -0700 Subject: [PATCH 12/33] Update gradle wrapper and minSdk versions. --- admob/testapp/AndroidManifest.xml | 2 +- admob/testapp/build.gradle | 2 +- admob/testapp/gradle/wrapper/gradle-wrapper.properties | 2 +- analytics/testapp/AndroidManifest.xml | 2 +- analytics/testapp/build.gradle | 2 +- analytics/testapp/gradle/wrapper/gradle-wrapper.properties | 2 +- auth/testapp/AndroidManifest.xml | 2 +- auth/testapp/build.gradle | 2 +- auth/testapp/gradle/wrapper/gradle-wrapper.properties | 2 +- database/testapp/AndroidManifest.xml | 2 +- database/testapp/build.gradle | 2 +- database/testapp/gradle/wrapper/gradle-wrapper.properties | 2 +- dynamic_links/testapp/AndroidManifest.xml | 2 +- dynamic_links/testapp/build.gradle | 2 +- dynamic_links/testapp/gradle/wrapper/gradle-wrapper.properties | 2 +- firestore/testapp/AndroidManifest.xml | 2 +- firestore/testapp/build.gradle | 2 +- firestore/testapp/gradle/wrapper/gradle-wrapper.properties | 2 +- functions/testapp/AndroidManifest.xml | 2 +- functions/testapp/build.gradle | 2 +- functions/testapp/gradle/wrapper/gradle-wrapper.properties | 2 +- gma/testapp/AndroidManifest.xml | 2 +- gma/testapp/build.gradle | 2 +- gma/testapp/gradle/wrapper/gradle-wrapper.properties | 2 +- messaging/testapp/AndroidManifest.xml | 2 +- messaging/testapp/build.gradle | 2 +- messaging/testapp/gradle/wrapper/gradle-wrapper.properties | 2 +- remote_config/testapp/AndroidManifest.xml | 2 +- remote_config/testapp/build.gradle | 2 +- remote_config/testapp/gradle/wrapper/gradle-wrapper.properties | 2 +- storage/testapp/AndroidManifest.xml | 2 +- storage/testapp/build.gradle | 2 +- storage/testapp/gradle/wrapper/gradle-wrapper.properties | 2 +- 33 files changed, 33 insertions(+), 33 deletions(-) diff --git a/admob/testapp/AndroidManifest.xml b/admob/testapp/AndroidManifest.xml index e62e6385..aa6b115d 100644 --- a/admob/testapp/AndroidManifest.xml +++ b/admob/testapp/AndroidManifest.xml @@ -6,7 +6,7 @@ - + - + - + - + - + - + - + - + - + diff --git a/messaging/testapp/build.gradle b/messaging/testapp/build.gradle index d0565e63..a2b2a9c6 100644 --- a/messaging/testapp/build.gradle +++ b/messaging/testapp/build.gradle @@ -26,7 +26,7 @@ android { sourceCompatibility 1.8 targetCompatibility 1.8 } - compileSdkVersion 28 + compileSdkVersion 34 buildToolsVersion '28.0.3' sourceSets { diff --git a/messaging/testapp/gradle/wrapper/gradle-wrapper.properties b/messaging/testapp/gradle/wrapper/gradle-wrapper.properties index 9e09cdb6..65340c1b 100644 --- a/messaging/testapp/gradle/wrapper/gradle-wrapper.properties +++ b/messaging/testapp/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https://services.gradle.org/distributions/gradle-5.6.4-all.zip +distributionUrl=https://services.gradle.org/distributions/gradle-6.7.1-all.zip diff --git a/remote_config/testapp/AndroidManifest.xml b/remote_config/testapp/AndroidManifest.xml index e449f7ee..326d45b5 100644 --- a/remote_config/testapp/AndroidManifest.xml +++ b/remote_config/testapp/AndroidManifest.xml @@ -6,7 +6,7 @@ - + - + Date: Wed, 22 May 2024 10:55:39 -0700 Subject: [PATCH 13/33] Update minSdk and targetSdk versions. --- auth/testapp/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/auth/testapp/build.gradle b/auth/testapp/build.gradle index 6dec5bf1..f1f97cfc 100644 --- a/auth/testapp/build.gradle +++ b/auth/testapp/build.gradle @@ -41,8 +41,8 @@ android { defaultConfig { applicationId 'com.google.android.auth.testapp' - minSdkVersion 19 - targetSdkVersion 28 + minSdkVersion 21 + targetSdkVersion 34 versionCode 1 versionName '1.0' externalNativeBuild.cmake { From 49c7b767736b1497461575b7cbf13014cdd8d689 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Wed, 22 May 2024 10:57:03 -0700 Subject: [PATCH 14/33] Add google services files to ignore list. --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..c4f78aab --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +GoogleService-Info.plist +google-services.json From 5b223690c10757aa794cd3d61fb8040e82ff3885 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Wed, 22 May 2024 11:01:10 -0700 Subject: [PATCH 15/33] Update gradle plugin and build tools versions. --- admob/testapp/build.gradle | 4 ++-- analytics/testapp/build.gradle | 4 ++-- auth/testapp/build.gradle | 4 ++-- database/testapp/build.gradle | 4 ++-- dynamic_links/testapp/build.gradle | 4 ++-- firestore/testapp/build.gradle | 4 ++-- functions/testapp/build.gradle | 4 ++-- gma/testapp/build.gradle | 4 ++-- messaging/testapp/build.gradle | 4 ++-- remote_config/testapp/build.gradle | 4 ++-- storage/testapp/build.gradle | 4 ++-- 11 files changed, 22 insertions(+), 22 deletions(-) diff --git a/admob/testapp/build.gradle b/admob/testapp/build.gradle index 4729245e..5c5bc306 100644 --- a/admob/testapp/build.gradle +++ b/admob/testapp/build.gradle @@ -6,7 +6,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.3.3' + classpath 'com.android.tools.build:gradle:4.2.1' classpath 'com.google.gms:google-services:4.0.1' } } @@ -23,7 +23,7 @@ apply plugin: 'com.android.application' android { compileSdkVersion 34 - buildToolsVersion '28.0.3' + buildToolsVersion '30.0.2' sourceSets { main { diff --git a/analytics/testapp/build.gradle b/analytics/testapp/build.gradle index ebb85736..b0096e32 100644 --- a/analytics/testapp/build.gradle +++ b/analytics/testapp/build.gradle @@ -6,7 +6,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.3.3' + classpath 'com.android.tools.build:gradle:4.2.1' classpath 'com.google.gms:google-services:4.0.1' } } @@ -27,7 +27,7 @@ android { targetCompatibility 1.8 } compileSdkVersion 34 - buildToolsVersion '28.0.3' + buildToolsVersion '30.0.2' sourceSets { main { diff --git a/auth/testapp/build.gradle b/auth/testapp/build.gradle index f1f97cfc..084921d3 100644 --- a/auth/testapp/build.gradle +++ b/auth/testapp/build.gradle @@ -6,7 +6,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.3.3' + classpath 'com.android.tools.build:gradle:4.2.1' classpath 'com.google.gms:google-services:4.0.1' } } @@ -28,7 +28,7 @@ android { } compileSdkVersion 34 - buildToolsVersion '28.0.3' + buildToolsVersion '30.0.2' sourceSets { main { diff --git a/database/testapp/build.gradle b/database/testapp/build.gradle index 6943b414..1881f441 100644 --- a/database/testapp/build.gradle +++ b/database/testapp/build.gradle @@ -6,7 +6,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.3.3' + classpath 'com.android.tools.build:gradle:4.2.1' classpath 'com.google.gms:google-services:4.0.1' } } @@ -27,7 +27,7 @@ android { targetCompatibility 1.8 } compileSdkVersion 34 - buildToolsVersion '28.0.3' + buildToolsVersion '30.0.2' sourceSets { main { diff --git a/dynamic_links/testapp/build.gradle b/dynamic_links/testapp/build.gradle index 82962584..742d0e74 100644 --- a/dynamic_links/testapp/build.gradle +++ b/dynamic_links/testapp/build.gradle @@ -6,7 +6,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.3.3' + classpath 'com.android.tools.build:gradle:4.2.1' classpath 'com.google.gms:google-services:4.0.1' } } @@ -27,7 +27,7 @@ android { targetCompatibility 1.8 } compileSdkVersion 34 - buildToolsVersion '28.0.3' + buildToolsVersion '30.0.2' sourceSets { main { diff --git a/firestore/testapp/build.gradle b/firestore/testapp/build.gradle index d8499acf..75525a31 100644 --- a/firestore/testapp/build.gradle +++ b/firestore/testapp/build.gradle @@ -6,7 +6,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.3.3' + classpath 'com.android.tools.build:gradle:4.2.1' classpath 'com.google.gms:google-services:4.0.1' } } @@ -27,7 +27,7 @@ android { targetCompatibility 1.8 } compileSdkVersion 34 - buildToolsVersion "28.0.3" + buildToolsVersion '30.0.2' sourceSets { main { diff --git a/functions/testapp/build.gradle b/functions/testapp/build.gradle index d7d54ee5..83891d53 100644 --- a/functions/testapp/build.gradle +++ b/functions/testapp/build.gradle @@ -6,7 +6,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.3.3' + classpath 'com.android.tools.build:gradle:4.2.1' classpath 'com.google.gms:google-services:4.0.1' } } @@ -27,7 +27,7 @@ android { targetCompatibility 1.8 } compileSdkVersion 34 - buildToolsVersion '28.0.3' + buildToolsVersion '30.0.2' sourceSets { main { diff --git a/gma/testapp/build.gradle b/gma/testapp/build.gradle index d3c1e57b..d878a2df 100644 --- a/gma/testapp/build.gradle +++ b/gma/testapp/build.gradle @@ -7,7 +7,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.3.3' + classpath 'com.android.tools.build:gradle:4.2.1' classpath 'com.google.gms:google-services:4.0.1' } } @@ -28,7 +28,7 @@ android { } compileSdkVersion 34 - buildToolsVersion '28.0.3' + buildToolsVersion '30.0.2' sourceSets { main { diff --git a/messaging/testapp/build.gradle b/messaging/testapp/build.gradle index a2b2a9c6..a21cb697 100644 --- a/messaging/testapp/build.gradle +++ b/messaging/testapp/build.gradle @@ -6,7 +6,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.3.3' + classpath 'com.android.tools.build:gradle:4.2.1' classpath 'com.google.gms:google-services:4.0.1' } } @@ -27,7 +27,7 @@ android { targetCompatibility 1.8 } compileSdkVersion 34 - buildToolsVersion '28.0.3' + buildToolsVersion '30.0.2' sourceSets { main { diff --git a/remote_config/testapp/build.gradle b/remote_config/testapp/build.gradle index b0f051d6..c854c778 100644 --- a/remote_config/testapp/build.gradle +++ b/remote_config/testapp/build.gradle @@ -6,7 +6,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.3.3' + classpath 'com.android.tools.build:gradle:4.2.1' classpath 'com.google.gms:google-services:4.0.1' } } @@ -27,7 +27,7 @@ android { targetCompatibility 1.8 } compileSdkVersion 34 - buildToolsVersion '28.0.3' + buildToolsVersion '30.0.2' sourceSets { main { diff --git a/storage/testapp/build.gradle b/storage/testapp/build.gradle index 78975f74..94355c6e 100644 --- a/storage/testapp/build.gradle +++ b/storage/testapp/build.gradle @@ -6,7 +6,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.3.3' + classpath 'com.android.tools.build:gradle:4.2.1' classpath 'com.google.gms:google-services:4.0.1' } } @@ -27,7 +27,7 @@ android { targetCompatibility 1.8 } compileSdkVersion 34 - buildToolsVersion '28.0.3' + buildToolsVersion '30.0.2' sourceSets { main { From 4349efd9d8b84db2a1ca45eff1973717829554a5 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Wed, 22 May 2024 11:03:11 -0700 Subject: [PATCH 16/33] Add NDK path environment variable. --- admob/testapp/build.gradle | 1 + analytics/testapp/build.gradle | 1 + auth/testapp/build.gradle | 1 + database/testapp/build.gradle | 1 + dynamic_links/testapp/build.gradle | 1 + firestore/testapp/build.gradle | 1 + functions/testapp/build.gradle | 1 + gma/testapp/build.gradle | 1 + messaging/testapp/build.gradle | 1 + remote_config/testapp/build.gradle | 1 + storage/testapp/build.gradle | 1 + 11 files changed, 11 insertions(+) diff --git a/admob/testapp/build.gradle b/admob/testapp/build.gradle index 5c5bc306..570ddee6 100644 --- a/admob/testapp/build.gradle +++ b/admob/testapp/build.gradle @@ -23,6 +23,7 @@ apply plugin: 'com.android.application' android { compileSdkVersion 34 + ndkPath System.getenv('ANDROID_NDK_HOME') buildToolsVersion '30.0.2' sourceSets { diff --git a/analytics/testapp/build.gradle b/analytics/testapp/build.gradle index b0096e32..37c97b80 100644 --- a/analytics/testapp/build.gradle +++ b/analytics/testapp/build.gradle @@ -27,6 +27,7 @@ android { targetCompatibility 1.8 } compileSdkVersion 34 + ndkPath System.getenv('ANDROID_NDK_HOME') buildToolsVersion '30.0.2' sourceSets { diff --git a/auth/testapp/build.gradle b/auth/testapp/build.gradle index 084921d3..10042b88 100644 --- a/auth/testapp/build.gradle +++ b/auth/testapp/build.gradle @@ -28,6 +28,7 @@ android { } compileSdkVersion 34 + ndkPath System.getenv('ANDROID_NDK_HOME') buildToolsVersion '30.0.2' sourceSets { diff --git a/database/testapp/build.gradle b/database/testapp/build.gradle index 1881f441..0f57b575 100644 --- a/database/testapp/build.gradle +++ b/database/testapp/build.gradle @@ -27,6 +27,7 @@ android { targetCompatibility 1.8 } compileSdkVersion 34 + ndkPath System.getenv('ANDROID_NDK_HOME') buildToolsVersion '30.0.2' sourceSets { diff --git a/dynamic_links/testapp/build.gradle b/dynamic_links/testapp/build.gradle index 742d0e74..a4f09573 100644 --- a/dynamic_links/testapp/build.gradle +++ b/dynamic_links/testapp/build.gradle @@ -27,6 +27,7 @@ android { targetCompatibility 1.8 } compileSdkVersion 34 + ndkPath System.getenv('ANDROID_NDK_HOME') buildToolsVersion '30.0.2' sourceSets { diff --git a/firestore/testapp/build.gradle b/firestore/testapp/build.gradle index 75525a31..9195dbf1 100644 --- a/firestore/testapp/build.gradle +++ b/firestore/testapp/build.gradle @@ -27,6 +27,7 @@ android { targetCompatibility 1.8 } compileSdkVersion 34 + ndkPath System.getenv('ANDROID_NDK_HOME') buildToolsVersion '30.0.2' sourceSets { diff --git a/functions/testapp/build.gradle b/functions/testapp/build.gradle index 83891d53..9977f3f7 100644 --- a/functions/testapp/build.gradle +++ b/functions/testapp/build.gradle @@ -27,6 +27,7 @@ android { targetCompatibility 1.8 } compileSdkVersion 34 + ndkPath System.getenv('ANDROID_NDK_HOME') buildToolsVersion '30.0.2' sourceSets { diff --git a/gma/testapp/build.gradle b/gma/testapp/build.gradle index d878a2df..5c8972e4 100644 --- a/gma/testapp/build.gradle +++ b/gma/testapp/build.gradle @@ -28,6 +28,7 @@ android { } compileSdkVersion 34 + ndkPath System.getenv('ANDROID_NDK_HOME') buildToolsVersion '30.0.2' sourceSets { diff --git a/messaging/testapp/build.gradle b/messaging/testapp/build.gradle index a21cb697..38131c52 100644 --- a/messaging/testapp/build.gradle +++ b/messaging/testapp/build.gradle @@ -27,6 +27,7 @@ android { targetCompatibility 1.8 } compileSdkVersion 34 + ndkPath System.getenv('ANDROID_NDK_HOME') buildToolsVersion '30.0.2' sourceSets { diff --git a/remote_config/testapp/build.gradle b/remote_config/testapp/build.gradle index c854c778..97bf0a50 100644 --- a/remote_config/testapp/build.gradle +++ b/remote_config/testapp/build.gradle @@ -27,6 +27,7 @@ android { targetCompatibility 1.8 } compileSdkVersion 34 + ndkPath System.getenv('ANDROID_NDK_HOME') buildToolsVersion '30.0.2' sourceSets { diff --git a/storage/testapp/build.gradle b/storage/testapp/build.gradle index 94355c6e..f128fcf7 100644 --- a/storage/testapp/build.gradle +++ b/storage/testapp/build.gradle @@ -27,6 +27,7 @@ android { targetCompatibility 1.8 } compileSdkVersion 34 + ndkPath System.getenv('ANDROID_NDK_HOME') buildToolsVersion '30.0.2' sourceSets { From 6f245c28cb3fae32acc0502e89fa13259fd43b5d Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Wed, 22 May 2024 11:05:28 -0700 Subject: [PATCH 17/33] Add AndroidX to properties file. --- admob/testapp/gradle.properties | 1 + analytics/testapp/gradle.properties | 1 + auth/testapp/gradle.properties | 1 + database/testapp/gradle.properties | 1 + dynamic_links/testapp/gradle.properties | 1 + firestore/testapp/gradle.properties | 1 + functions/testapp/gradle.properties | 1 + gma/testapp/gradle.properties | 1 + messaging/testapp/gradle.properties | 1 + remote_config/testapp/gradle.properties | 1 + storage/testapp/gradle.properties | 1 + 11 files changed, 11 insertions(+) create mode 100644 admob/testapp/gradle.properties create mode 100644 analytics/testapp/gradle.properties create mode 100644 auth/testapp/gradle.properties create mode 100644 database/testapp/gradle.properties create mode 100644 dynamic_links/testapp/gradle.properties create mode 100644 firestore/testapp/gradle.properties create mode 100644 functions/testapp/gradle.properties create mode 100644 gma/testapp/gradle.properties create mode 100644 messaging/testapp/gradle.properties create mode 100644 remote_config/testapp/gradle.properties create mode 100644 storage/testapp/gradle.properties diff --git a/admob/testapp/gradle.properties b/admob/testapp/gradle.properties new file mode 100644 index 00000000..d7ba8f42 --- /dev/null +++ b/admob/testapp/gradle.properties @@ -0,0 +1 @@ +android.useAndroidX = true diff --git a/analytics/testapp/gradle.properties b/analytics/testapp/gradle.properties new file mode 100644 index 00000000..d7ba8f42 --- /dev/null +++ b/analytics/testapp/gradle.properties @@ -0,0 +1 @@ +android.useAndroidX = true diff --git a/auth/testapp/gradle.properties b/auth/testapp/gradle.properties new file mode 100644 index 00000000..d7ba8f42 --- /dev/null +++ b/auth/testapp/gradle.properties @@ -0,0 +1 @@ +android.useAndroidX = true diff --git a/database/testapp/gradle.properties b/database/testapp/gradle.properties new file mode 100644 index 00000000..d7ba8f42 --- /dev/null +++ b/database/testapp/gradle.properties @@ -0,0 +1 @@ +android.useAndroidX = true diff --git a/dynamic_links/testapp/gradle.properties b/dynamic_links/testapp/gradle.properties new file mode 100644 index 00000000..d7ba8f42 --- /dev/null +++ b/dynamic_links/testapp/gradle.properties @@ -0,0 +1 @@ +android.useAndroidX = true diff --git a/firestore/testapp/gradle.properties b/firestore/testapp/gradle.properties new file mode 100644 index 00000000..d7ba8f42 --- /dev/null +++ b/firestore/testapp/gradle.properties @@ -0,0 +1 @@ +android.useAndroidX = true diff --git a/functions/testapp/gradle.properties b/functions/testapp/gradle.properties new file mode 100644 index 00000000..d7ba8f42 --- /dev/null +++ b/functions/testapp/gradle.properties @@ -0,0 +1 @@ +android.useAndroidX = true diff --git a/gma/testapp/gradle.properties b/gma/testapp/gradle.properties new file mode 100644 index 00000000..d7ba8f42 --- /dev/null +++ b/gma/testapp/gradle.properties @@ -0,0 +1 @@ +android.useAndroidX = true diff --git a/messaging/testapp/gradle.properties b/messaging/testapp/gradle.properties new file mode 100644 index 00000000..d7ba8f42 --- /dev/null +++ b/messaging/testapp/gradle.properties @@ -0,0 +1 @@ +android.useAndroidX = true diff --git a/remote_config/testapp/gradle.properties b/remote_config/testapp/gradle.properties new file mode 100644 index 00000000..d7ba8f42 --- /dev/null +++ b/remote_config/testapp/gradle.properties @@ -0,0 +1 @@ +android.useAndroidX = true diff --git a/storage/testapp/gradle.properties b/storage/testapp/gradle.properties new file mode 100644 index 00000000..d7ba8f42 --- /dev/null +++ b/storage/testapp/gradle.properties @@ -0,0 +1 @@ +android.useAndroidX = true From 6ff962569affc44d8f680343e0064098f93cb769 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Wed, 22 May 2024 11:08:01 -0700 Subject: [PATCH 18/33] Add android:exported to manifest file, update min SDK again. --- admob/testapp/AndroidManifest.xml | 7 ++++--- admob/testapp/build.gradle | 2 +- analytics/testapp/AndroidManifest.xml | 3 ++- analytics/testapp/build.gradle | 2 +- auth/testapp/AndroidManifest.xml | 3 ++- auth/testapp/build.gradle | 2 +- database/testapp/AndroidManifest.xml | 3 ++- database/testapp/build.gradle | 2 +- dynamic_links/testapp/AndroidManifest.xml | 3 ++- dynamic_links/testapp/build.gradle | 2 +- firestore/testapp/AndroidManifest.xml | 3 ++- firestore/testapp/build.gradle | 2 +- functions/testapp/AndroidManifest.xml | 3 ++- functions/testapp/build.gradle | 2 +- gma/testapp/AndroidManifest.xml | 7 ++++--- gma/testapp/build.gradle | 2 +- messaging/testapp/AndroidManifest.xml | 3 ++- messaging/testapp/build.gradle | 2 +- remote_config/testapp/AndroidManifest.xml | 3 ++- remote_config/testapp/build.gradle | 2 +- storage/testapp/AndroidManifest.xml | 3 ++- storage/testapp/build.gradle | 2 +- 22 files changed, 37 insertions(+), 26 deletions(-) diff --git a/admob/testapp/AndroidManifest.xml b/admob/testapp/AndroidManifest.xml index aa6b115d..72ec9ea1 100644 --- a/admob/testapp/AndroidManifest.xml +++ b/admob/testapp/AndroidManifest.xml @@ -6,15 +6,16 @@ - + + android:exported = "true" + android:screenOrientation="portrait" + android:configChanges="orientation|screenSize"> diff --git a/admob/testapp/build.gradle b/admob/testapp/build.gradle index 570ddee6..c5b19b62 100644 --- a/admob/testapp/build.gradle +++ b/admob/testapp/build.gradle @@ -37,7 +37,7 @@ android { defaultConfig { applicationId 'com.google.android.admob.testapp' - minSdkVersion 26 + minSdkVersion 23 targetSdkVersion 28 versionCode 1 versionName '1.0' diff --git a/analytics/testapp/AndroidManifest.xml b/analytics/testapp/AndroidManifest.xml index 2d4a9f35..a0b94e79 100644 --- a/analytics/testapp/AndroidManifest.xml +++ b/analytics/testapp/AndroidManifest.xml @@ -6,9 +6,10 @@ - + - + - + - + - + - + - + + android:exported = "true" + android:screenOrientation="portrait" + android:configChanges="orientation|screenSize"> diff --git a/gma/testapp/build.gradle b/gma/testapp/build.gradle index 5c8972e4..f073766d 100644 --- a/gma/testapp/build.gradle +++ b/gma/testapp/build.gradle @@ -42,7 +42,7 @@ android { defaultConfig { applicationId 'com.google.android.admob.testapp' - minSdkVersion 19 + minSdkVersion 23 targetSdkVersion 28 versionCode 1 versionName '1.0' diff --git a/messaging/testapp/AndroidManifest.xml b/messaging/testapp/AndroidManifest.xml index 80d522d0..2d86d2c0 100644 --- a/messaging/testapp/AndroidManifest.xml +++ b/messaging/testapp/AndroidManifest.xml @@ -3,7 +3,7 @@ package="com.google.android.messaging.testapp" android:versionCode="1" android:versionName="1.0"> - + @@ -19,6 +19,7 @@ work around a known issue when receiving notification data payloads in the background. --> diff --git a/messaging/testapp/build.gradle b/messaging/testapp/build.gradle index 38131c52..89070d57 100644 --- a/messaging/testapp/build.gradle +++ b/messaging/testapp/build.gradle @@ -41,7 +41,7 @@ android { defaultConfig { applicationId 'com.google.android.messaging.testapp' - minSdkVersion 19 + minSdkVersion 23 targetSdkVersion 28 versionCode 1 versionName '1.0' diff --git a/remote_config/testapp/AndroidManifest.xml b/remote_config/testapp/AndroidManifest.xml index 326d45b5..6ba5e57f 100644 --- a/remote_config/testapp/AndroidManifest.xml +++ b/remote_config/testapp/AndroidManifest.xml @@ -6,9 +6,10 @@ - + - + Date: Wed, 22 May 2024 11:13:01 -0700 Subject: [PATCH 19/33] Fix phone auth deprecated methods usage. --- auth/testapp/src/common_main.cc | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/auth/testapp/src/common_main.cc b/auth/testapp/src/common_main.cc index d7be0ca9..f630b41e 100644 --- a/auth/testapp/src/common_main.cc +++ b/auth/testapp/src/common_main.cc @@ -43,6 +43,7 @@ using ::firebase::auth::kAuthErrorInvalidCredential; using ::firebase::auth::kAuthErrorInvalidProviderId; using ::firebase::auth::kAuthErrorNone; using ::firebase::auth::OAuthProvider; +using ::firebase::auth::PhoneAuthOptions; using ::firebase::auth::PhoneAuthProvider; using ::firebase::auth::PhoneAuthCredential; using ::firebase::auth::PlayGamesAuthProvider; @@ -601,8 +602,10 @@ extern "C" int common_main(int argc, const char* argv[]) { "Phone Number", "Please enter your phone number", "+12345678900"); PhoneListener listener; PhoneAuthProvider& phone_provider = PhoneAuthProvider::GetInstance(auth); - phone_provider.VerifyPhoneNumber(phone_number.c_str(), kPhoneAuthTimeoutMs, - nullptr, &listener); + PhoneAuthOptions options; + options.phone_number = phone_number; + options.timeout_milliseconds = kPhoneAuthTimeoutMs; + phone_provider.VerifyPhoneNumber(options, &listener); // Wait for OnCodeSent() callback. int wait_ms = 0; @@ -634,7 +637,7 @@ extern "C" int common_main(int argc, const char* argv[]) { LogMessage("."); } if (listener.num_calls_on_code_auto_retrieval_time_out() > 0) { - const Credential phone_credential = phone_provider.GetCredential( + const PhoneAuthCredential phone_credential = phone_provider.GetCredential( listener.verification_id().c_str(), verification_code.c_str()); Futurephone_future = From 85cd69aec9d170abb8d9227add44431b402962bd Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Wed, 22 May 2024 11:22:46 -0700 Subject: [PATCH 20/33] Fix build error and add coroutines fix and lint ignore. --- admob/testapp/build.gradle | 6 ++++++ analytics/testapp/build.gradle | 6 ++++++ auth/testapp/build.gradle | 6 ++++++ auth/testapp/src/common_main.cc | 2 +- database/testapp/build.gradle | 6 ++++++ dynamic_links/testapp/build.gradle | 6 ++++++ firestore/testapp/build.gradle | 6 ++++++ functions/testapp/build.gradle | 6 ++++++ gma/testapp/build.gradle | 6 ++++++ messaging/testapp/build.gradle | 6 ++++++ remote_config/testapp/build.gradle | 6 ++++++ storage/testapp/build.gradle | 6 ++++++ 12 files changed, 67 insertions(+), 1 deletion(-) diff --git a/admob/testapp/build.gradle b/admob/testapp/build.gradle index c5b19b62..daa94140 100644 --- a/admob/testapp/build.gradle +++ b/admob/testapp/build.gradle @@ -55,6 +55,12 @@ android { proguardFile file('proguard.pro') } } + packagingOptions { + pickFirst 'META-INF/**/coroutines.pro' + } + lintOptions { + abortOnError false + } } apply from: "$gradle.firebase_cpp_sdk_dir/Android/firebase_dependencies.gradle" diff --git a/analytics/testapp/build.gradle b/analytics/testapp/build.gradle index cae14877..90067866 100644 --- a/analytics/testapp/build.gradle +++ b/analytics/testapp/build.gradle @@ -59,6 +59,12 @@ android { proguardFile file('proguard.pro') } } + packagingOptions { + pickFirst 'META-INF/**/coroutines.pro' + } + lintOptions { + abortOnError false + } } apply from: "$gradle.firebase_cpp_sdk_dir/Android/firebase_dependencies.gradle" diff --git a/auth/testapp/build.gradle b/auth/testapp/build.gradle index b519358f..11b622d8 100644 --- a/auth/testapp/build.gradle +++ b/auth/testapp/build.gradle @@ -60,6 +60,12 @@ android { proguardFile file('proguard.pro') } } + packagingOptions { + pickFirst 'META-INF/**/coroutines.pro' + } + lintOptions { + abortOnError false + } } apply from: "$gradle.firebase_cpp_sdk_dir/Android/firebase_dependencies.gradle" diff --git a/auth/testapp/src/common_main.cc b/auth/testapp/src/common_main.cc index f630b41e..f0d12c7b 100644 --- a/auth/testapp/src/common_main.cc +++ b/auth/testapp/src/common_main.cc @@ -647,7 +647,7 @@ extern "C" int common_main(int argc, const char* argv[]) { kAuthErrorNone, auth); if (phone_future.error() == kAuthErrorNone) { User user = *phone_future.result(); - Future update_future = + Future update_future = user.UpdatePhoneNumberCredential(phone_credential); WaitForSignInFuture( update_future, diff --git a/database/testapp/build.gradle b/database/testapp/build.gradle index 62461c4a..5d49d735 100644 --- a/database/testapp/build.gradle +++ b/database/testapp/build.gradle @@ -59,6 +59,12 @@ android { proguardFile file('proguard.pro') } } + packagingOptions { + pickFirst 'META-INF/**/coroutines.pro' + } + lintOptions { + abortOnError false + } } apply from: "$gradle.firebase_cpp_sdk_dir/Android/firebase_dependencies.gradle" diff --git a/dynamic_links/testapp/build.gradle b/dynamic_links/testapp/build.gradle index c195d525..34b3f78c 100644 --- a/dynamic_links/testapp/build.gradle +++ b/dynamic_links/testapp/build.gradle @@ -59,6 +59,12 @@ android { proguardFile file('proguard.pro') } } + packagingOptions { + pickFirst 'META-INF/**/coroutines.pro' + } + lintOptions { + abortOnError false + } } apply from: "$gradle.firebase_cpp_sdk_dir/Android/firebase_dependencies.gradle" diff --git a/firestore/testapp/build.gradle b/firestore/testapp/build.gradle index b12ff5bf..b3cb8b26 100644 --- a/firestore/testapp/build.gradle +++ b/firestore/testapp/build.gradle @@ -60,6 +60,12 @@ android { proguardFile file('proguard.pro') } } + packagingOptions { + pickFirst 'META-INF/**/coroutines.pro' + } + lintOptions { + abortOnError false + } } apply from: "$gradle.firebase_cpp_sdk_dir/Android/firebase_dependencies.gradle" diff --git a/functions/testapp/build.gradle b/functions/testapp/build.gradle index ae4fe14a..bc3a4f63 100644 --- a/functions/testapp/build.gradle +++ b/functions/testapp/build.gradle @@ -59,6 +59,12 @@ android { proguardFile file('proguard.pro') } } + packagingOptions { + pickFirst 'META-INF/**/coroutines.pro' + } + lintOptions { + abortOnError false + } } apply from: "$gradle.firebase_cpp_sdk_dir/Android/firebase_dependencies.gradle" diff --git a/gma/testapp/build.gradle b/gma/testapp/build.gradle index f073766d..10384b95 100644 --- a/gma/testapp/build.gradle +++ b/gma/testapp/build.gradle @@ -60,6 +60,12 @@ android { proguardFile file('proguard.pro') } } + packagingOptions { + pickFirst 'META-INF/**/coroutines.pro' + } + lintOptions { + abortOnError false + } } apply from: "$gradle.firebase_cpp_sdk_dir/Android/firebase_dependencies.gradle" diff --git a/messaging/testapp/build.gradle b/messaging/testapp/build.gradle index 89070d57..de72535c 100644 --- a/messaging/testapp/build.gradle +++ b/messaging/testapp/build.gradle @@ -59,6 +59,12 @@ android { proguardFile file('proguard.pro') } } + packagingOptions { + pickFirst 'META-INF/**/coroutines.pro' + } + lintOptions { + abortOnError false + } } apply from: "$gradle.firebase_cpp_sdk_dir/Android/firebase_dependencies.gradle" diff --git a/remote_config/testapp/build.gradle b/remote_config/testapp/build.gradle index d68b7c63..c6d494cd 100644 --- a/remote_config/testapp/build.gradle +++ b/remote_config/testapp/build.gradle @@ -59,6 +59,12 @@ android { proguardFile file('proguard.pro') } } + packagingOptions { + pickFirst 'META-INF/**/coroutines.pro' + } + lintOptions { + abortOnError false + } } apply from: "$gradle.firebase_cpp_sdk_dir/Android/firebase_dependencies.gradle" diff --git a/storage/testapp/build.gradle b/storage/testapp/build.gradle index 25e33231..daa0f354 100644 --- a/storage/testapp/build.gradle +++ b/storage/testapp/build.gradle @@ -59,6 +59,12 @@ android { proguardFile file('proguard.pro') } } + packagingOptions { + pickFirst 'META-INF/**/coroutines.pro' + } + lintOptions { + abortOnError false + } } apply from: "$gradle.firebase_cpp_sdk_dir/Android/firebase_dependencies.gradle" From 6ad262ebda2c34205f3014db1b0dd8f3d89f766e Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Wed, 22 May 2024 11:33:34 -0700 Subject: [PATCH 21/33] Remove minSdk from manifest (fixing a lint issue) and then turn off the linter for the rest of the lint issues. --- admob/testapp/AndroidManifest.xml | 2 +- admob/testapp/build.gradle | 1 + analytics/testapp/AndroidManifest.xml | 2 +- analytics/testapp/build.gradle | 1 + auth/testapp/AndroidManifest.xml | 2 +- auth/testapp/build.gradle | 1 + database/testapp/AndroidManifest.xml | 2 +- database/testapp/build.gradle | 1 + dynamic_links/testapp/AndroidManifest.xml | 2 +- dynamic_links/testapp/build.gradle | 1 + firestore/testapp/AndroidManifest.xml | 2 +- firestore/testapp/build.gradle | 1 + functions/testapp/AndroidManifest.xml | 2 +- functions/testapp/build.gradle | 1 + gma/testapp/AndroidManifest.xml | 2 +- gma/testapp/build.gradle | 1 + messaging/testapp/AndroidManifest.xml | 2 +- messaging/testapp/build.gradle | 1 + remote_config/testapp/AndroidManifest.xml | 2 +- remote_config/testapp/build.gradle | 1 + storage/testapp/AndroidManifest.xml | 2 +- storage/testapp/build.gradle | 1 + 22 files changed, 22 insertions(+), 11 deletions(-) diff --git a/admob/testapp/AndroidManifest.xml b/admob/testapp/AndroidManifest.xml index 72ec9ea1..a6e324fa 100644 --- a/admob/testapp/AndroidManifest.xml +++ b/admob/testapp/AndroidManifest.xml @@ -6,7 +6,7 @@ - + - + - + - + - + - + - + - + - + diff --git a/messaging/testapp/build.gradle b/messaging/testapp/build.gradle index de72535c..4f607fe3 100644 --- a/messaging/testapp/build.gradle +++ b/messaging/testapp/build.gradle @@ -64,6 +64,7 @@ android { } lintOptions { abortOnError false + checkReleaseBuilds false } } diff --git a/remote_config/testapp/AndroidManifest.xml b/remote_config/testapp/AndroidManifest.xml index 6ba5e57f..a7a904df 100644 --- a/remote_config/testapp/AndroidManifest.xml +++ b/remote_config/testapp/AndroidManifest.xml @@ -6,7 +6,7 @@ - + - + Date: Wed, 22 May 2024 11:37:23 -0700 Subject: [PATCH 22/33] Update iphone deployment target version. --- admob/testapp/testapp.xcodeproj/project.pbxproj | 4 ++-- analytics/testapp/testapp.xcodeproj/project.pbxproj | 4 ++-- auth/testapp/testapp.xcodeproj/project.pbxproj | 4 ++-- database/testapp/testapp.xcodeproj/project.pbxproj | 4 ++-- dynamic_links/testapp/testapp.xcodeproj/project.pbxproj | 4 ++-- firestore/testapp/testapp.xcodeproj/project.pbxproj | 4 ++-- functions/testapp/testapp.xcodeproj/project.pbxproj | 4 ++-- gma/testapp/testapp.xcodeproj/project.pbxproj | 4 ++-- messaging/testapp/testapp.xcodeproj/project.pbxproj | 4 ++-- remote_config/testapp/testapp.xcodeproj/project.pbxproj | 4 ++-- storage/testapp/testapp.xcodeproj/project.pbxproj | 4 ++-- 11 files changed, 22 insertions(+), 22 deletions(-) diff --git a/admob/testapp/testapp.xcodeproj/project.pbxproj b/admob/testapp/testapp.xcodeproj/project.pbxproj index 14a0af7f..3afd2dea 100644 --- a/admob/testapp/testapp.xcodeproj/project.pbxproj +++ b/admob/testapp/testapp.xcodeproj/project.pbxproj @@ -208,7 +208,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.4; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -245,7 +245,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.4; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/analytics/testapp/testapp.xcodeproj/project.pbxproj b/analytics/testapp/testapp.xcodeproj/project.pbxproj index 6c325725..5769362c 100644 --- a/analytics/testapp/testapp.xcodeproj/project.pbxproj +++ b/analytics/testapp/testapp.xcodeproj/project.pbxproj @@ -208,7 +208,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -245,7 +245,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/auth/testapp/testapp.xcodeproj/project.pbxproj b/auth/testapp/testapp.xcodeproj/project.pbxproj index 6c325725..5769362c 100644 --- a/auth/testapp/testapp.xcodeproj/project.pbxproj +++ b/auth/testapp/testapp.xcodeproj/project.pbxproj @@ -208,7 +208,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -245,7 +245,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/database/testapp/testapp.xcodeproj/project.pbxproj b/database/testapp/testapp.xcodeproj/project.pbxproj index 6c325725..5769362c 100644 --- a/database/testapp/testapp.xcodeproj/project.pbxproj +++ b/database/testapp/testapp.xcodeproj/project.pbxproj @@ -208,7 +208,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -245,7 +245,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/dynamic_links/testapp/testapp.xcodeproj/project.pbxproj b/dynamic_links/testapp/testapp.xcodeproj/project.pbxproj index 6c325725..5769362c 100644 --- a/dynamic_links/testapp/testapp.xcodeproj/project.pbxproj +++ b/dynamic_links/testapp/testapp.xcodeproj/project.pbxproj @@ -208,7 +208,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -245,7 +245,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/firestore/testapp/testapp.xcodeproj/project.pbxproj b/firestore/testapp/testapp.xcodeproj/project.pbxproj index 81ed36ca..ed7634b5 100644 --- a/firestore/testapp/testapp.xcodeproj/project.pbxproj +++ b/firestore/testapp/testapp.xcodeproj/project.pbxproj @@ -284,7 +284,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -321,7 +321,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/functions/testapp/testapp.xcodeproj/project.pbxproj b/functions/testapp/testapp.xcodeproj/project.pbxproj index 6c325725..5769362c 100644 --- a/functions/testapp/testapp.xcodeproj/project.pbxproj +++ b/functions/testapp/testapp.xcodeproj/project.pbxproj @@ -208,7 +208,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -245,7 +245,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/gma/testapp/testapp.xcodeproj/project.pbxproj b/gma/testapp/testapp.xcodeproj/project.pbxproj index 14a0af7f..3afd2dea 100644 --- a/gma/testapp/testapp.xcodeproj/project.pbxproj +++ b/gma/testapp/testapp.xcodeproj/project.pbxproj @@ -208,7 +208,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.4; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -245,7 +245,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.4; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/messaging/testapp/testapp.xcodeproj/project.pbxproj b/messaging/testapp/testapp.xcodeproj/project.pbxproj index 096e3825..c5f566b4 100644 --- a/messaging/testapp/testapp.xcodeproj/project.pbxproj +++ b/messaging/testapp/testapp.xcodeproj/project.pbxproj @@ -212,7 +212,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -249,7 +249,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/remote_config/testapp/testapp.xcodeproj/project.pbxproj b/remote_config/testapp/testapp.xcodeproj/project.pbxproj index 6c325725..5769362c 100644 --- a/remote_config/testapp/testapp.xcodeproj/project.pbxproj +++ b/remote_config/testapp/testapp.xcodeproj/project.pbxproj @@ -208,7 +208,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -245,7 +245,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/storage/testapp/testapp.xcodeproj/project.pbxproj b/storage/testapp/testapp.xcodeproj/project.pbxproj index 6c325725..5769362c 100644 --- a/storage/testapp/testapp.xcodeproj/project.pbxproj +++ b/storage/testapp/testapp.xcodeproj/project.pbxproj @@ -208,7 +208,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -245,7 +245,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; From 520e4d712666d9e00fecad9207f764379d597f23 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Wed, 22 May 2024 12:57:08 -0700 Subject: [PATCH 23/33] Update other testapps that use Auth. --- database/testapp/src/common_main.cc | 2 +- firestore/testapp/src/common_main.cc | 4 ++-- functions/testapp/src/common_main.cc | 2 +- storage/testapp/src/common_main.cc | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/database/testapp/src/common_main.cc b/database/testapp/src/common_main.cc index 1d985d7d..3a35179b 100644 --- a/database/testapp/src/common_main.cc +++ b/database/testapp/src/common_main.cc @@ -199,7 +199,7 @@ extern "C" int common_main(int argc, const char* argv[]) { // work as long as your project's Authentication permissions allow anonymous // signin. { - firebase::Future sign_in_future = + firebase::Future sign_in_future = auth->SignInAnonymously(); WaitForCompletion(sign_in_future, "SignInAnonymously"); if (sign_in_future.error() == firebase::auth::kAuthErrorNone) { diff --git a/firestore/testapp/src/common_main.cc b/firestore/testapp/src/common_main.cc index 4322d77d..335ff86b 100644 --- a/firestore/testapp/src/common_main.cc +++ b/firestore/testapp/src/common_main.cc @@ -126,8 +126,8 @@ extern "C" int common_main(int argc, const char* argv[]) { auto login_future = auth->SignInAnonymously(); Await(login_future, "Auth sign-in"); auto* login_result = login_future.result(); - if (login_result && *login_result) { - const firebase::auth::User* user = *login_result; + if (login_result) { + const firebase::auth::AuthResult user = *login_result.user; LogMessage("Signed in as %s user, uid: %s, email: %s.\n", user->is_anonymous() ? "an anonymous" : "a non-anonymous", user->uid().c_str(), user->email().c_str()); diff --git a/functions/testapp/src/common_main.cc b/functions/testapp/src/common_main.cc index 98277ccc..5e5e0019 100644 --- a/functions/testapp/src/common_main.cc +++ b/functions/testapp/src/common_main.cc @@ -97,7 +97,7 @@ extern "C" int common_main(int argc, const char* argv[]) { // Optionally, sign in using Auth before accessing Functions. { - firebase::Future sign_in_future = + firebase::Future sign_in_future = auth->SignInAnonymously(); WaitForCompletion(sign_in_future, "SignInAnonymously"); if (sign_in_future.error() == firebase::auth::kAuthErrorNone) { diff --git a/storage/testapp/src/common_main.cc b/storage/testapp/src/common_main.cc index 9dbade62..a333c4fb 100644 --- a/storage/testapp/src/common_main.cc +++ b/storage/testapp/src/common_main.cc @@ -116,7 +116,7 @@ extern "C" int common_main(int argc, const char* argv[]) { // work as long as your project's Authentication permissions allow anonymous // signin. { - firebase::Future sign_in_future = + firebase::Future sign_in_future = auth->SignInAnonymously(); WaitForCompletion(sign_in_future, "SignInAnonymously"); if (sign_in_future.error() == firebase::auth::kAuthErrorNone) { From 2a6c727f01a9e6dd0c25ff3f01c3917a540bf99d Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Wed, 22 May 2024 12:57:33 -0700 Subject: [PATCH 24/33] Remove Admob build. --- .github/workflows/android.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index d8876f55..d7542560 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -9,7 +9,7 @@ on: inputs: apis: description: 'CSV of apis to build and test' - default: 'admob,analytics,auth,database,dynamic_links,firestore,functions,gma,messaging,remote_config,storage' + default: 'analytics,auth,database,dynamic_links,firestore,functions,gma,messaging,remote_config,storage' required: true env: From 74b4a30068569f9191e194e23a994d0760d5fb0d Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Thu, 23 May 2024 12:04:14 -0700 Subject: [PATCH 25/33] Fix build error. --- firestore/testapp/src/common_main.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firestore/testapp/src/common_main.cc b/firestore/testapp/src/common_main.cc index 335ff86b..d191349d 100644 --- a/firestore/testapp/src/common_main.cc +++ b/firestore/testapp/src/common_main.cc @@ -127,7 +127,7 @@ extern "C" int common_main(int argc, const char* argv[]) { Await(login_future, "Auth sign-in"); auto* login_result = login_future.result(); if (login_result) { - const firebase::auth::AuthResult user = *login_result.user; + const firebase::auth::User user = login_result->user; LogMessage("Signed in as %s user, uid: %s, email: %s.\n", user->is_anonymous() ? "an anonymous" : "a non-anonymous", user->uid().c_str(), user->email().c_str()); From ced418bf1a0aa26f136a5e99c6661374c1666e16 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Thu, 23 May 2024 12:51:45 -0700 Subject: [PATCH 26/33] Fix build issue. --- firestore/testapp/src/common_main.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/firestore/testapp/src/common_main.cc b/firestore/testapp/src/common_main.cc index d191349d..0de5d39b 100644 --- a/firestore/testapp/src/common_main.cc +++ b/firestore/testapp/src/common_main.cc @@ -129,8 +129,8 @@ extern "C" int common_main(int argc, const char* argv[]) { if (login_result) { const firebase::auth::User user = login_result->user; LogMessage("Signed in as %s user, uid: %s, email: %s.\n", - user->is_anonymous() ? "an anonymous" : "a non-anonymous", - user->uid().c_str(), user->email().c_str()); + user.is_anonymous() ? "an anonymous" : "a non-anonymous", + user.uid().c_str(), user.email().c_str()); } else { LogMessage("ERROR: could not sign in"); } From 3397f4976f8aba1f1b6e0df374400f514ed3d56e Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Thu, 23 May 2024 13:47:18 -0700 Subject: [PATCH 27/33] Remove old AdMob quickstart. --- .github/workflows/android.yml | 2 +- admob/testapp/AndroidManifest.xml | 27 -- admob/testapp/CMakeLists.txt | 111 ------ admob/testapp/LICENSE | 202 ---------- admob/testapp/LaunchScreen.storyboard | 7 - admob/testapp/Podfile | 6 - admob/testapp/build.gradle | 72 ---- admob/testapp/gradle.properties | 1 - .../testapp/gradle/wrapper/gradle-wrapper.jar | Bin 49896 -> 0 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 - admob/testapp/gradlew | 164 -------- admob/testapp/gradlew.bat | 90 ----- admob/testapp/proguard.pro | 2 - admob/testapp/readme.md | 200 ---------- admob/testapp/res/values/strings.xml | 4 - admob/testapp/settings.gradle | 36 -- admob/testapp/src/android/android_main.cc | 255 ------------- .../google/firebase/example/LoggingUtils.java | 55 --- admob/testapp/src/common_main.cc | 359 ------------------ admob/testapp/src/desktop/desktop_main.cc | 125 ------ admob/testapp/src/ios/ios_main.mm | 119 ------ admob/testapp/src/main.h | 63 --- .../testapp/testapp.xcodeproj/project.pbxproj | 312 --------------- .../AppIcon.appiconset/Contents.json | 73 ---- .../LaunchImage.launchimage/Contents.json | 36 -- admob/testapp/testapp/Info.plist | 28 -- scripts/build_scripts/build_testapps.json | 13 - .../admob/GoogleService-Info.plist.gpg | Bin 671 -> 0 bytes .../admob/google-services.json.gpg | Bin 1002 -> 0 bytes 29 files changed, 1 insertion(+), 2367 deletions(-) delete mode 100644 admob/testapp/AndroidManifest.xml delete mode 100644 admob/testapp/CMakeLists.txt delete mode 100644 admob/testapp/LICENSE delete mode 100644 admob/testapp/LaunchScreen.storyboard delete mode 100644 admob/testapp/Podfile delete mode 100644 admob/testapp/build.gradle delete mode 100644 admob/testapp/gradle.properties delete mode 100644 admob/testapp/gradle/wrapper/gradle-wrapper.jar delete mode 100644 admob/testapp/gradle/wrapper/gradle-wrapper.properties delete mode 100755 admob/testapp/gradlew delete mode 100644 admob/testapp/gradlew.bat delete mode 100644 admob/testapp/proguard.pro delete mode 100644 admob/testapp/readme.md delete mode 100644 admob/testapp/res/values/strings.xml delete mode 100644 admob/testapp/settings.gradle delete mode 100644 admob/testapp/src/android/android_main.cc delete mode 100644 admob/testapp/src/android/java/com/google/firebase/example/LoggingUtils.java delete mode 100644 admob/testapp/src/common_main.cc delete mode 100644 admob/testapp/src/desktop/desktop_main.cc delete mode 100644 admob/testapp/src/ios/ios_main.mm delete mode 100644 admob/testapp/src/main.h delete mode 100644 admob/testapp/testapp.xcodeproj/project.pbxproj delete mode 100644 admob/testapp/testapp/Images.xcassets/AppIcon.appiconset/Contents.json delete mode 100644 admob/testapp/testapp/Images.xcassets/LaunchImage.launchimage/Contents.json delete mode 100644 admob/testapp/testapp/Info.plist delete mode 100644 scripts/gha-encrypted/admob/GoogleService-Info.plist.gpg delete mode 100644 scripts/gha-encrypted/admob/google-services.json.gpg diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index d7542560..e086d3f9 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -32,7 +32,7 @@ jobs: if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then apis="${{ github.event.inputs.apis }}" else - apis="admob,analytics,auth,database,dynamic_links,firestore,functions,gma,messaging,remote_config,storage" + apis="analytics,auth,database,dynamic_links,firestore,functions,gma,messaging,remote_config,storage" fi echo apis: ${apis} echo "::set-output name=apis::${apis}" diff --git a/admob/testapp/AndroidManifest.xml b/admob/testapp/AndroidManifest.xml deleted file mode 100644 index a6e324fa..00000000 --- a/admob/testapp/AndroidManifest.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/admob/testapp/CMakeLists.txt b/admob/testapp/CMakeLists.txt deleted file mode 100644 index 3a7d02dd..00000000 --- a/admob/testapp/CMakeLists.txt +++ /dev/null @@ -1,111 +0,0 @@ -cmake_minimum_required(VERSION 2.8) - -# User settings for Firebase samples. -# Path to Firebase SDK. -# Try to read the path to the Firebase C++ SDK from an environment variable. -if (NOT "$ENV{FIREBASE_CPP_SDK_DIR}" STREQUAL "") - set(DEFAULT_FIREBASE_CPP_SDK_DIR "$ENV{FIREBASE_CPP_SDK_DIR}") -else() - set(DEFAULT_FIREBASE_CPP_SDK_DIR "firebase_cpp_sdk") -endif() -if ("${FIREBASE_CPP_SDK_DIR}" STREQUAL "") - set(FIREBASE_CPP_SDK_DIR ${DEFAULT_FIREBASE_CPP_SDK_DIR}) -endif() -if(NOT EXISTS ${FIREBASE_CPP_SDK_DIR}) - message(FATAL_ERROR "The Firebase C++ SDK directory does not exist: ${FIREBASE_CPP_SDK_DIR}. See the readme.md for more information") -endif() - -# Sample source files. -set(FIREBASE_SAMPLE_COMMON_SRCS - src/main.h - src/common_main.cc -) - -# The include directory for the testapp. -include_directories(src) - -# Sample uses some features that require C++ 11, such as lambdas. -set (CMAKE_CXX_STANDARD 11) - -if(ANDROID) - # Build an Android application. - - # Source files used for the Android build. - set(FIREBASE_SAMPLE_ANDROID_SRCS - src/android/android_main.cc - ) - - # Build native_app_glue as a static lib - add_library(native_app_glue STATIC - ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c) - - # Export ANativeActivity_onCreate(), - # Refer to: https://github.com/android-ndk/ndk/issues/381. - set(CMAKE_SHARED_LINKER_FLAGS - "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate") - - # Define the target as a shared library, as that is what gradle expects. - set(target_name "android_main") - add_library(${target_name} SHARED - ${FIREBASE_SAMPLE_ANDROID_SRCS} - ${FIREBASE_SAMPLE_COMMON_SRCS} - ) - - target_link_libraries(${target_name} - log android atomic native_app_glue - ) - - target_include_directories(${target_name} PRIVATE - ${ANDROID_NDK}/sources/android/native_app_glue) - - set(ADDITIONAL_LIBS) -else() - # Build a desktop application. - - # Windows runtime mode, either MD or MT depending on whether you are using - # /MD or /MT. For more information see: - # https://msdn.microsoft.com/en-us/library/2kzt1wy3.aspx - set(MSVC_RUNTIME_MODE MD) - - # Platform abstraction layer for the desktop sample. - set(FIREBASE_SAMPLE_DESKTOP_SRCS - src/desktop/desktop_main.cc - ) - - set(target_name "desktop_testapp") - add_executable(${target_name} - ${FIREBASE_SAMPLE_DESKTOP_SRCS} - ${FIREBASE_SAMPLE_COMMON_SRCS} - ) - - if(APPLE) - set(ADDITIONAL_LIBS pthread) - elseif(MSVC) - set(ADDITIONAL_LIBS) - else() - set(ADDITIONAL_LIBS pthread) - endif() - - # If a config file is present, copy it into the binary location so that it's - # possible to create the default Firebase app. - set(FOUND_JSON_FILE FALSE) - foreach(config "google-services-desktop.json" "google-services.json") - if (EXISTS ${config}) - add_custom_command( - TARGET ${target_name} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy - ${config} $) - set(FOUND_JSON_FILE TRUE) - break() - endif() - endforeach() - if(NOT FOUND_JSON_FILE) - message(WARNING "Failed to find either google-services-desktop.json or google-services.json. See the readme.md for more information.") - endif() -endif() - -# Add the Firebase libraries to the target using the function from the SDK. -add_subdirectory(${FIREBASE_CPP_SDK_DIR} bin/ EXCLUDE_FROM_ALL) -# Note that firebase_app needs to be last in the list. -set(firebase_libs firebase_admob firebase_app) -target_link_libraries(${target_name} "${firebase_libs}" ${ADDITIONAL_LIBS}) diff --git a/admob/testapp/LICENSE b/admob/testapp/LICENSE deleted file mode 100644 index d6456956..00000000 --- a/admob/testapp/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/admob/testapp/LaunchScreen.storyboard b/admob/testapp/LaunchScreen.storyboard deleted file mode 100644 index 673e0f7e..00000000 --- a/admob/testapp/LaunchScreen.storyboard +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/admob/testapp/Podfile b/admob/testapp/Podfile deleted file mode 100644 index 9759f528..00000000 --- a/admob/testapp/Podfile +++ /dev/null @@ -1,6 +0,0 @@ -source 'https://github.com/CocoaPods/Specs.git' -platform :ios, '8.0' -# AdMob test application. -target 'testapp' do - pod 'Firebase/AdMob', '7.0.0' -end diff --git a/admob/testapp/build.gradle b/admob/testapp/build.gradle deleted file mode 100644 index 744b80e4..00000000 --- a/admob/testapp/build.gradle +++ /dev/null @@ -1,72 +0,0 @@ -// Top-level build file where you can add configuration options common to all sub-projects/modules. -buildscript { - repositories { - mavenLocal() - maven { url 'https://maven.google.com' } - jcenter() - } - dependencies { - classpath 'com.android.tools.build:gradle:4.2.1' - classpath 'com.google.gms:google-services:4.0.1' - } -} - -allprojects { - repositories { - mavenLocal() - maven { url 'https://maven.google.com' } - jcenter() - } -} - -apply plugin: 'com.android.application' - -android { - compileSdkVersion 34 - ndkPath System.getenv('ANDROID_NDK_HOME') - buildToolsVersion '30.0.2' - - sourceSets { - main { - jniLibs.srcDirs = ['libs'] - manifest.srcFile 'AndroidManifest.xml' - java.srcDirs = ['src/android/java'] - res.srcDirs = ['res'] - } - } - - defaultConfig { - applicationId 'com.google.android.admob.testapp' - minSdkVersion 23 - targetSdkVersion 28 - versionCode 1 - versionName '1.0' - externalNativeBuild.cmake { - arguments "-DFIREBASE_CPP_SDK_DIR=$gradle.firebase_cpp_sdk_dir" - } - } - externalNativeBuild.cmake { - path 'CMakeLists.txt' - } - buildTypes { - release { - minifyEnabled true - proguardFile getDefaultProguardFile('proguard-android.txt') - proguardFile file('proguard.pro') - } - } - packagingOptions { - pickFirst 'META-INF/**/coroutines.pro' - } - lintOptions { - abortOnError false - checkReleaseBuilds false - } -} - -apply from: "$gradle.firebase_cpp_sdk_dir/Android/firebase_dependencies.gradle" -firebaseCpp.dependencies { - admob -} - -apply plugin: 'com.google.gms.google-services' diff --git a/admob/testapp/gradle.properties b/admob/testapp/gradle.properties deleted file mode 100644 index d7ba8f42..00000000 --- a/admob/testapp/gradle.properties +++ /dev/null @@ -1 +0,0 @@ -android.useAndroidX = true diff --git a/admob/testapp/gradle/wrapper/gradle-wrapper.jar b/admob/testapp/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 8c0fb64a8698b08ecc4158d828ca593c4928e9dd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 49896 zcmagFb986H(k`5d^NVfUwr$(C?M#x1ZQHiZiEVpg+jrjgoQrerx!>1o_ul)D>ebz~ zs=Mmxr&>W81QY-S1PKWQ%N-;H^tS;2*XwVA`dej1RRn1z<;3VgfE4~kaG`A%QSPsR z#ovnZe+tS9%1MfeDyz`RirvdjPRK~p(#^q2(^5@O&NM19EHdvN-A&StN>0g6QA^VN z0Gx%Gq#PD$QMRFzmK+utjS^Y1F0e8&u&^=w5K<;4Rz|i3A=o|IKLY+g`iK6vfr9?+ z-`>gmU&i?FGSL5&F?TXFu`&Js6h;15QFkXp2M1H9|Eq~bpov-GU(uz%mH0n55wUl- zv#~ccAz`F5wlQ>e_KlJS3@{)B?^v*EQM=IxLa&76^y51a((wq|2-`qON>+4dLc{Oo z51}}o^Zen(oAjxDK7b++9_Yg`67p$bPo3~BCpGM7uAWmvIhWc5Gi+gQZ|Pwa-Gll@<1xmcPy z|NZmu6m)g5Ftu~BG&Xdxclw7Cij{xbBMBn-LMII#Slp`AElb&2^Hw+w>(3crLH!;I zN+Vk$D+wP1#^!MDCiad@vM>H#6+`Ct#~6VHL4lzmy;lSdk>`z6)=>Wh15Q2)dQtGqvn0vJU@+(B5{MUc*qs4!T+V=q=wy)<6$~ z!G>e_4dN@lGeF_$q9`Ju6Ncb*x?O7=l{anm7Eahuj_6lA{*#Gv*TaJclevPVbbVYu z(NY?5q+xxbO6%g1xF0r@Ix8fJ~u)VRUp`S%&rN$&e!Od`~s+64J z5*)*WSi*i{k%JjMSIN#X;jC{HG$-^iX+5f5BGOIHWAl*%15Z#!xntpk($-EGKCzKa zT7{siZ9;4TICsWQ$pu&wKZQTCvpI$Xvzwxoi+XkkpeE&&kFb!B?h2hi%^YlXt|-@5 zHJ~%AN!g_^tmn1?HSm^|gCE#!GRtK2(L{9pL#hp0xh zME}|DB>(5)`iE7CM)&_+S}-Bslc#@B5W4_+k4Cp$l>iVyg$KP>CN?SVGZ(&02>iZK zB<^HP$g$Lq*L$BWd?2(F?-MUbNWTJVQdW7$#8a|k_30#vHAD1Z{c#p;bETk0VnU5A zBgLe2HFJ3032$G<`m*OB!KM$*sdM20jm)It5OSru@tXpK5LT>#8)N!*skNu1$TpIw zufjjdp#lyH5bZ%|Iuo|iu9vG1HrIVWLH>278xo>aVBkPN3V$~!=KnlXQ4eDqS7%E% zQ!z^$Q$b^6Q)g#cLpwur(|<0gWHo6A6jc;n`t(V9T;LzTAU{IAu*uEQ%Ort1k+Kn+f_N`9|bxYC+~Z1 zCC1UCWv*Orx$_@ydv9mIe(liLfOr7mhbV@tKw{6)q^1DH1nmvZ0cj215R<~&I<4S| zgnr;9Cdjqpz#o8i0CQjtl`}{c*P)aSdH|abxGdrR)-3z+02-eX(k*B)Uqv6~^nh** z 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 \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >&- -APP_HOME="`pwd -P`" -cd "$SAVED" >&- - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=$((i+1)) - done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac -fi - -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") -} -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" - -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/admob/testapp/gradlew.bat b/admob/testapp/gradlew.bat deleted file mode 100644 index 8a0b282a..00000000 --- a/admob/testapp/gradlew.bat +++ /dev/null @@ -1,90 +0,0 @@ -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windowz variants - -if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/admob/testapp/proguard.pro b/admob/testapp/proguard.pro deleted file mode 100644 index 54cd248b..00000000 --- a/admob/testapp/proguard.pro +++ /dev/null @@ -1,2 +0,0 @@ --ignorewarnings --keep,includedescriptorclasses public class com.google.firebase.example.LoggingUtils { *; } diff --git a/admob/testapp/readme.md b/admob/testapp/readme.md deleted file mode 100644 index e8f45f80..00000000 --- a/admob/testapp/readme.md +++ /dev/null @@ -1,200 +0,0 @@ -Firebase AdMob Quickstart -============================== - -The Firebase AdMob Test Application (testapp) demonstrates loading and showing -banners and interstitials using the Firebase AdMob C++ SDK. The application -has no user interface and simply logs actions it's performing to the console -while displaying the ads. - -Introduction ------------- - -- [Read more about Firebase AdMob](https://firebase.google.com/docs/admob) - -Getting Started ---------------- - -### iOS - - Link your iOS app to the Firebase libraries. - - Get CocoaPods version 1 or later by running, - ``` - sudo gem install cocoapods --pre - ``` - - From the testapp directory, install the CocoaPods listed in the Podfile - by running, - ``` - pod install - ``` - - Open the generated Xcode workspace (which now has the CocoaPods), - ``` - open testapp.xcworkspace - ``` - - For further details please refer to the - [general instructions for setting up an iOS app with Firebase](https://firebase.google.com/docs/ios/setup). - - Register your iOS app with Firebase. - - Create a new app on the [Firebase console](https://firebase.google.com/console/), - and attach your iOS app to it. - - You can use "com.google.ios.admob.testapp" as the iOS Bundle ID - while you're testing. You can omit App Store ID while testing. - - Download the Firebase C++ SDK linked from - [https://firebase.google.com/docs/cpp/setup](https://firebase.google.com/docs/cpp/setup) - and unzip it to a directory of your choice. - - Add the following frameworks from the Firebase C++ SDK to the project: - - frameworks/ios/universal/firebase.framework - - frameworks/ios/universal/firebase_admob.framework - - You will need to either, - 1. Check "Copy items if needed" when adding the frameworks, or - 2. Add the framework path in "Framework Search Paths" - - For example, if you downloaded the Firebase C++ SDK to - `/Users/me/firebase_cpp_sdk`, - then you would add the path - `/Users/me/firebase_cpp_sdk/frameworks/ios/universal`. - - To add the path, in Xcode, select your project in the project - navigator, then select your target in the main window. - Select the "Build Settings" tab, and click "All" to see all - the build settings. Scroll down to "Search Paths", and add - your path to "Framework Search Paths". - - Update the AdMob App ID: - - In the `src/common_main.cc`, update `kAdMobAppID` with the app ID for - your iOS app, replacing 'YOUR_IOS_ADMOB_APP_ID'. - - In the `testapp/Info.plist`, update `GADApplicationIdentifier` with the - same app ID, replacing 'YOUR_IOS_ADMOB_APP_ID'. - - For more information, see - [Update your Info.plist](https://developers.google.com/admob/ios/quick-start#manual_download) - - In Xcode, build & run the sample on an iOS device or simulator. - - The testapp displays a banner ad and an interstitial ad. You can dismiss - the interstitial ad to see the banner ad. - - Afterwards, the testapp will display a Rewarded Video test ad. - - The output of the app can be viewed onscreen or via the console. To view - the console in Xcode, select "View --> Debug Area --> Activate Console" - from the menu. - -### Android - - Register your Android app with Firebase. - - Create a new app on the [Firebase console](https://firebase.google.com/console/), - and attach your Android app to it. - - You can use "com.google.android.admob.testapp" as the Package Name - while you're testing. - - To [generate a SHA1](https://developers.google.com/android/guides/client-auth) - run this command on Mac and Linux, - ``` - keytool -exportcert -list -v -alias androiddebugkey -keystore ~/.android/debug.keystore - ``` - or this command on Windows, - ``` - keytool -exportcert -list -v -alias androiddebugkey -keystore %USERPROFILE%\.android\debug.keystore - ``` - - If keytool reports that you do not have a debug.keystore, you can - [create one with](http://developer.android.com/tools/publishing/app-signing.html#signing-manually), - ``` - keytool -genkey -v -keystore ~/.android/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US" - ``` - - Add the `google-services.json` file that you downloaded from Firebase - console to the root directory of testapp. This file identifies your - Android app to the Firebase backend. - - For further details please refer to the - [general instructions for setting up an Android app with Firebase](https://firebase.google.com/docs/android/setup). - - Download the Firebase C++ SDK linked from - [https://firebase.google.com/docs/cpp/setup](https://firebase.google.com/docs/cpp/setup) - and unzip it to a directory of your choice. - - Configure the location of the Firebase C++ SDK by setting the - firebase\_cpp\_sdk.dir Gradle property to the SDK install directory. - For example, in the project directory: - ``` - echo "systemProp.firebase\_cpp\_sdk.dir=/User/$USER/firebase\_cpp\_sdk" >> gradle.properties - ``` - - Ensure the Android SDK and NDK locations are set in Android Studio. - - From the Android Studio launch menu, go to `File/Project Structure...` or - `Configure/Project Defaults/Project Structure...` - (Shortcut: Control + Alt + Shift + S on windows, Command + ";" on a mac) - and download the SDK and NDK if the locations are not yet set. - - Open *build.gradle* in Android Studio. - - From the Android Studio launch menu, "Open an existing Android Studio - project", and select `build.gradle`. - - Install the SDK Platforms that Android Studio reports missing. - - Update the AdMob App ID: - - In the `src/common_main.cc`, update `kAdMobAppID` with the app ID for - your Android app, replacing 'YOUR_ANDROID_ADMOB_APP_ID'. - - In the `AndroidManifest.xml`, update - `com.google.android.gms.ads.APPLICATION_ID` with the same app ID, - replacing 'YOUR_ANDROID_ADMOB_APP_ID'. - - For more information, see - [Update your AndroidManifest.xml](https://developers.google.com/admob/android/quick-start#update_your_androidmanifestxml) - - Build the testapp and run it on an Android device or emulator. - - The testapp will initialize AdMob, then load and display a test banner and - a test interstitial. - - Tapping on an ad to verify the clickthrough process is possible, and the - interstitial will wait to be closed by the user. - - Afterwards, the testapp will display a Rewarded Video test ad. - - While this is happening, information from the device log will be written - to an onscreen TextView. - - Logcat can also be used as normal. - -### Desktop - - Register your app with Firebase. - - Create a new app on the [Firebase console](https://firebase.google.com/console/), - following the above instructions for Android or iOS. - - If you have an Android project, add the `google-services.json` file that - you downloaded from the Firebase console to the root directory of the - testapp. - - If you have an iOS project, and don't wish to use an Android project, - you can use the Python script `generate_xml_from_google_services_json.py --plist`, - located in the Firebase C++ SDK, to convert your `GoogleService-Info.plist` - file into a `google-services-desktop.json` file, which can then be - placed in the root directory of the testapp. - - Download the Firebase C++ SDK linked from - [https://firebase.google.com/docs/cpp/setup](https://firebase.google.com/docs/cpp/setup) - and unzip it to a directory of your choice. - - Configure the testapp with the location of the Firebase C++ SDK. - This can be done a couple different ways: - - When invoking cmake, pass in the location with - -DFIREBASE_CPP_SDK_DIR=/path/to/firebase_cpp_sdk. - - Set an environment variable for FIREBASE_CPP_SDK_DIR to the path to use. - - Edit the CMakeLists.txt file, changing the FIREBASE_CPP_SDK_DIR path - to the appropriate location. - - From the testapp directory, generate the build files by running, - ``` - cmake . - ``` - If you want to use XCode, you can use -G"Xcode" to generate the project. - Similarly, to use Visual Studio, -G"Visual Studio 15 2017". For more - information, see - [CMake generators](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html). - - Build the testapp, by either opening the generated project file based on - the platform, or running, - ``` - cmake --build . - ``` - - Execute the testapp by running, - ``` - ./desktop_testapp - ``` - Note that the executable might be under another directory, such as Debug. - - The testapp has no user interface, but the output can be viewed via the - console. Note that Admob uses a stubbed implementation on desktop, - so functionality is not expected. - -Support -------- - -[https://firebase.google.com/support/](https://firebase.google.com/support/) - -License -------- - -Copyright 2016 Google, Inc. - -Licensed to the Apache Software Foundation (ASF) under one or more contributor -license agreements. See the NOTICE file distributed with this work for -additional information regarding copyright ownership. The ASF licenses this -file to you 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. diff --git a/admob/testapp/res/values/strings.xml b/admob/testapp/res/values/strings.xml deleted file mode 100644 index 8589bd2c..00000000 --- a/admob/testapp/res/values/strings.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - Firebase AdMob Test - diff --git a/admob/testapp/settings.gradle b/admob/testapp/settings.gradle deleted file mode 100644 index 2a543b93..00000000 --- a/admob/testapp/settings.gradle +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2018 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. - -def firebase_cpp_sdk_dir = System.getProperty('firebase_cpp_sdk.dir') -if (firebase_cpp_sdk_dir == null || firebase_cpp_sdk_dir.isEmpty()) { - firebase_cpp_sdk_dir = System.getenv('FIREBASE_CPP_SDK_DIR') - if (firebase_cpp_sdk_dir == null || firebase_cpp_sdk_dir.isEmpty()) { - if ((new File('firebase_cpp_sdk')).exists()) { - firebase_cpp_sdk_dir = 'firebase_cpp_sdk' - } else { - throw new StopActionException( - 'firebase_cpp_sdk.dir property or the FIREBASE_CPP_SDK_DIR ' + - 'environment variable must be set to reference the Firebase C++ ' + - 'SDK install directory. This is used to configure static library ' + - 'and C/C++ include paths for the SDK.') - } - } -} -if (!(new File(firebase_cpp_sdk_dir)).exists()) { - throw new StopActionException( - sprintf('Firebase C++ SDK directory %s does not exist', - firebase_cpp_sdk_dir)) -} -gradle.ext.firebase_cpp_sdk_dir = "$firebase_cpp_sdk_dir" -includeBuild "$firebase_cpp_sdk_dir" \ No newline at end of file diff --git a/admob/testapp/src/android/android_main.cc b/admob/testapp/src/android/android_main.cc deleted file mode 100644 index 73cb30e7..00000000 --- a/admob/testapp/src/android/android_main.cc +++ /dev/null @@ -1,255 +0,0 @@ -// Copyright 2016 Google Inc. All rights reserved. -// -// 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. - -#include -#include - -#include -#include -#include -#include - -#include "main.h" // NOLINT - -// This implementation is derived from http://github.com/google/fplutil - -extern "C" int common_main(int argc, const char* argv[]); - -static struct android_app* g_app_state = nullptr; -static bool g_destroy_requested = false; -static bool g_started = false; -static bool g_restarted = false; -static pthread_mutex_t g_started_mutex; - -// Handle state changes from via native app glue. -static void OnAppCmd(struct android_app* app, int32_t cmd) { - g_destroy_requested |= cmd == APP_CMD_DESTROY; -} - -// Process events pending on the main thread. -// Returns true when the app receives an event requesting exit. -bool ProcessEvents(int msec) { - struct android_poll_source* source = nullptr; - int events; - int looperId = ALooper_pollAll(msec, nullptr, &events, - reinterpret_cast(&source)); - if (looperId >= 0 && source) { - source->process(g_app_state, source); - } - return g_destroy_requested | g_restarted; -} - -// Get the activity. -jobject GetActivity() { return g_app_state->activity->clazz; } - -// Get the window context. For Android, it's a jobject pointing to the Activity. -jobject GetWindowContext() { return g_app_state->activity->clazz; } - -// Find a class, attempting to load the class if it's not found. -jclass FindClass(JNIEnv* env, jobject activity_object, const char* class_name) { - jclass class_object = env->FindClass(class_name); - if (env->ExceptionCheck()) { - env->ExceptionClear(); - // If the class isn't found it's possible NativeActivity is being used by - // the application which means the class path is set to only load system - // classes. The following falls back to loading the class using the - // Activity before retrieving a reference to it. - jclass activity_class = env->FindClass("android/app/Activity"); - jmethodID activity_get_class_loader = env->GetMethodID( - activity_class, "getClassLoader", "()Ljava/lang/ClassLoader;"); - - jobject class_loader_object = - env->CallObjectMethod(activity_object, activity_get_class_loader); - - jclass class_loader_class = env->FindClass("java/lang/ClassLoader"); - jmethodID class_loader_load_class = - env->GetMethodID(class_loader_class, "loadClass", - "(Ljava/lang/String;)Ljava/lang/Class;"); - jstring class_name_object = env->NewStringUTF(class_name); - - class_object = static_cast(env->CallObjectMethod( - class_loader_object, class_loader_load_class, class_name_object)); - - if (env->ExceptionCheck()) { - env->ExceptionClear(); - class_object = nullptr; - } - env->DeleteLocalRef(class_name_object); - env->DeleteLocalRef(class_loader_object); - } - return class_object; -} - -// Vars that we need available for appending text to the log window: -class LoggingUtilsData { - public: - LoggingUtilsData() - : logging_utils_class_(nullptr), - logging_utils_add_log_text_(0), - logging_utils_init_log_window_(0) {} - - ~LoggingUtilsData() { - JNIEnv* env = GetJniEnv(); - assert(env); - if (logging_utils_class_) { - env->DeleteGlobalRef(logging_utils_class_); - } - } - - void Init() { - JNIEnv* env = GetJniEnv(); - assert(env); - - jclass logging_utils_class = FindClass( - env, GetActivity(), "com/google/firebase/example/LoggingUtils"); - assert(logging_utils_class != 0); - - // Need to store as global references so it don't get moved during garbage - // collection. - logging_utils_class_ = - static_cast(env->NewGlobalRef(logging_utils_class)); - env->DeleteLocalRef(logging_utils_class); - - logging_utils_init_log_window_ = env->GetStaticMethodID( - logging_utils_class_, "initLogWindow", "(Landroid/app/Activity;)V"); - logging_utils_add_log_text_ = env->GetStaticMethodID( - logging_utils_class_, "addLogText", "(Ljava/lang/String;)V"); - - env->CallStaticVoidMethod(logging_utils_class_, - logging_utils_init_log_window_, GetActivity()); - } - - void AppendText(const char* text) { - if (logging_utils_class_ == 0) return; // haven't been initted yet - JNIEnv* env = GetJniEnv(); - assert(env); - jstring text_string = env->NewStringUTF(text); - env->CallStaticVoidMethod(logging_utils_class_, logging_utils_add_log_text_, - text_string); - env->DeleteLocalRef(text_string); - } - - private: - jclass logging_utils_class_; - jmethodID logging_utils_add_log_text_; - jmethodID logging_utils_init_log_window_; -}; - -LoggingUtilsData* g_logging_utils_data; - -// Checks if a JNI exception has happened, and if so, logs it to the console. -void CheckJNIException() { - JNIEnv* env = GetJniEnv(); - if (env->ExceptionCheck()) { - // Get the exception text. - jthrowable exception = env->ExceptionOccurred(); - env->ExceptionClear(); - - // Convert the exception to a string. - jclass object_class = env->FindClass("java/lang/Object"); - jmethodID toString = - env->GetMethodID(object_class, "toString", "()Ljava/lang/String;"); - jstring s = (jstring)env->CallObjectMethod(exception, toString); - const char* exception_text = env->GetStringUTFChars(s, nullptr); - - // Log the exception text. - __android_log_print(ANDROID_LOG_INFO, FIREBASE_TESTAPP_NAME, - "-------------------JNI exception:"); - __android_log_print(ANDROID_LOG_INFO, FIREBASE_TESTAPP_NAME, "%s", - exception_text); - __android_log_print(ANDROID_LOG_INFO, FIREBASE_TESTAPP_NAME, - "-------------------"); - - // Also, assert fail. - assert(false); - - // In the event we didn't assert fail, clean up. - env->ReleaseStringUTFChars(s, exception_text); - env->DeleteLocalRef(s); - env->DeleteLocalRef(exception); - } -} - -// Log a message that can be viewed in "adb logcat". -void LogMessage(const char* format, ...) { - static const int kLineBufferSize = 100; - char buffer[kLineBufferSize + 2]; - - va_list list; - va_start(list, format); - int string_len = vsnprintf(buffer, kLineBufferSize, format, list); - string_len = string_len < kLineBufferSize ? string_len : kLineBufferSize; - // append a linebreak to the buffer: - buffer[string_len] = '\n'; - buffer[string_len + 1] = '\0'; - - __android_log_vprint(ANDROID_LOG_INFO, FIREBASE_TESTAPP_NAME, format, list); - g_logging_utils_data->AppendText(buffer); - CheckJNIException(); - va_end(list); -} - -// Get the JNI environment. -JNIEnv* GetJniEnv() { - JavaVM* vm = g_app_state->activity->vm; - JNIEnv* env; - jint result = vm->AttachCurrentThread(&env, nullptr); - return result == JNI_OK ? env : nullptr; -} - -// Execute common_main(), flush pending events and finish the activity. -extern "C" void android_main(struct android_app* state) { - // native_app_glue spawns a new thread, calling android_main() when the - // activity onStart() or onRestart() methods are called. This code handles - // the case where we're re-entering this method on a different thread by - // signalling the existing thread to exit, waiting for it to complete before - // reinitializing the application. - if (g_started) { - g_restarted = true; - // Wait for the existing thread to exit. - pthread_mutex_lock(&g_started_mutex); - pthread_mutex_unlock(&g_started_mutex); - } else { - g_started_mutex = PTHREAD_MUTEX_INITIALIZER; - } - pthread_mutex_lock(&g_started_mutex); - g_started = true; - - // Save native app glue state and setup a callback to track the state. - g_destroy_requested = false; - g_app_state = state; - g_app_state->onAppCmd = OnAppCmd; - - // Create the logging display. - g_logging_utils_data = new LoggingUtilsData(); - g_logging_utils_data->Init(); - - // Execute cross platform entry point. - static const char* argv[] = {FIREBASE_TESTAPP_NAME}; - int return_value = common_main(1, argv); - (void)return_value; // Ignore the return value. - ProcessEvents(10); - - // Clean up logging display. - delete g_logging_utils_data; - g_logging_utils_data = nullptr; - - // Finish the activity. - if (!g_restarted) ANativeActivity_finish(state->activity); - - g_app_state->activity->vm->DetachCurrentThread(); - g_started = false; - g_restarted = false; - pthread_mutex_unlock(&g_started_mutex); -} diff --git a/admob/testapp/src/android/java/com/google/firebase/example/LoggingUtils.java b/admob/testapp/src/android/java/com/google/firebase/example/LoggingUtils.java deleted file mode 100644 index 11d67c5b..00000000 --- a/admob/testapp/src/android/java/com/google/firebase/example/LoggingUtils.java +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2016 Google Inc. All rights reserved. -// -// 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. - -package com.google.firebase.example; - -import android.app.Activity; -import android.os.Handler; -import android.os.Looper; -import android.view.Window; -import android.widget.LinearLayout; -import android.widget.ScrollView; -import android.widget.TextView; - -/** - * A utility class, encapsulating the data and methods required to log arbitrary - * text to the screen, via a non-editable TextView. - */ -public class LoggingUtils { - public static TextView sTextView = null; - - public static void initLogWindow(Activity activity) { - LinearLayout linearLayout = new LinearLayout(activity); - ScrollView scrollView = new ScrollView(activity); - TextView textView = new TextView(activity); - textView.setTag("Logger"); - linearLayout.addView(scrollView); - scrollView.addView(textView); - Window window = activity.getWindow(); - window.takeSurface(null); - window.setContentView(linearLayout); - sTextView = textView; - } - - public static void addLogText(final String text) { - new Handler(Looper.getMainLooper()).post(new Runnable() { - @Override - public void run() { - if (sTextView != null) { - sTextView.append(text); - } - } - }); - } -} diff --git a/admob/testapp/src/common_main.cc b/admob/testapp/src/common_main.cc deleted file mode 100644 index eae4f5c4..00000000 --- a/admob/testapp/src/common_main.cc +++ /dev/null @@ -1,359 +0,0 @@ -// Copyright 2016 Google Inc. All rights reserved. -// -// 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. - -#include "firebase/admob.h" -#include "firebase/admob/banner_view.h" -#include "firebase/admob/interstitial_ad.h" -#include "firebase/admob/rewarded_video.h" -#include "firebase/admob/types.h" -#include "firebase/app.h" -#include "firebase/future.h" - -// Thin OS abstraction layer. -#include "main.h" // NOLINT - -// A simple listener that logs changes to a BannerView. -class LoggingBannerViewListener : public firebase::admob::BannerView::Listener { - public: - LoggingBannerViewListener() {} - void OnPresentationStateChanged( - firebase::admob::BannerView* banner_view, - firebase::admob::BannerView::PresentationState state) override { - ::LogMessage("BannerView PresentationState has changed to %d.", state); - } - void OnBoundingBoxChanged(firebase::admob::BannerView* banner_view, - firebase::admob::BoundingBox box) override { - ::LogMessage( - "BannerView BoundingBox has changed to (x: %d, y: %d, width: %d, " - "height %d).", - box.x, box.y, box.width, box.height); - } -}; - -// A simple listener that logs changes to an InterstitialAd. -class LoggingInterstitialAdListener - : public firebase::admob::InterstitialAd::Listener { - public: - LoggingInterstitialAdListener() {} - void OnPresentationStateChanged( - firebase::admob::InterstitialAd* interstitial_ad, - firebase::admob::InterstitialAd::PresentationState state) override { - ::LogMessage("InterstitialAd PresentationState has changed to %d.", state); - } -}; - -// A simple listener that logs changes to rewarded video state. -class LoggingRewardedVideoListener - : public firebase::admob::rewarded_video::Listener { - public: - LoggingRewardedVideoListener() {} - void OnRewarded(firebase::admob::rewarded_video::RewardItem reward) override { - ::LogMessage("Rewarding user with %f %s.", reward.amount, - reward.reward_type.c_str()); - } - void OnPresentationStateChanged( - firebase::admob::rewarded_video::PresentationState state) override { - ::LogMessage("Rewarded video PresentationState has changed to %d.", state); - } -}; - -// The AdMob app IDs for the test app. -#if defined(__ANDROID__) -// If you change the AdMob app ID for your Android app, make sure to change it -// in AndroidManifest.xml as well. -const char* kAdMobAppID = "YOUR_ANDROID_ADMOB_APP_ID"; -#else -// If you change the AdMob app ID for your iOS app, make sure to change the -// value for "GADApplicationIdentifier" in your Info.plist as well. -const char* kAdMobAppID = "YOUR_IOS_ADMOB_APP_ID"; -#endif - -// These ad units IDs have been created specifically for testing, and will -// always return test ads. -#if defined(__ANDROID__) -const char* kBannerAdUnit = "ca-app-pub-3940256099942544/6300978111"; -const char* kInterstitialAdUnit = "ca-app-pub-3940256099942544/1033173712"; -const char* kRewardedVideoAdUnit = "ca-app-pub-3940256099942544/5224354917"; -#else -const char* kBannerAdUnit = "ca-app-pub-3940256099942544/2934735716"; -const char* kInterstitialAdUnit = "ca-app-pub-3940256099942544/4411468910"; -const char* kRewardedVideoAdUnit = "ca-app-pub-3940256099942544/1712485313"; -#endif - -// Standard mobile banner size is 320x50. -static const int kBannerWidth = 320; -static const int kBannerHeight = 50; - -// Sample keywords to use in making the request. -static const char* kKeywords[] = {"AdMob", "C++", "Fun"}; - -// Sample test device IDs to use in making the request. -static const char* kTestDeviceIDs[] = {"2077ef9a63d2b398840261c8221a0c9b", - "098fe087d987c9a878965454a65654d7"}; - -// Sample birthday value to use in making the request. -static const int kBirthdayDay = 10; -static const int kBirthdayMonth = 11; -static const int kBirthdayYear = 1976; - -static void WaitForFutureCompletion(firebase::FutureBase future) { - while (!ProcessEvents(1000)) { - if (future.status() != firebase::kFutureStatusPending) { - break; - } - } - - if (future.error() != firebase::admob::kAdMobErrorNone) { - LogMessage("ERROR: Action failed with error code %d and message \"%s\".", - future.error(), future.error_message()); - } -} - -// Execute all methods of the C++ admob API. -extern "C" int common_main(int argc, const char* argv[]) { - firebase::App* app; - LogMessage("Initializing the AdMob library."); - -#if defined(__ANDROID__) - app = ::firebase::App::Create(GetJniEnv(), GetActivity()); -#else - app = ::firebase::App::Create(); -#endif // defined(__ANDROID__) - - LogMessage("Created the Firebase App %x.", - static_cast(reinterpret_cast(app))); - - LogMessage("Initializing the AdMob with Firebase API."); - firebase::admob::Initialize(*app, kAdMobAppID); - - firebase::admob::AdRequest request; - // If the app is aware of the user's gender, it can be added to the targeting - // information. Otherwise, "unknown" should be used. - request.gender = firebase::admob::kGenderUnknown; - - // This value allows publishers to specify whether they would like the request - // to be treated as child-directed for purposes of the Children’s Online - // Privacy Protection Act (COPPA). - // See http://business.ftc.gov/privacy-and-security/childrens-privacy. - request.tagged_for_child_directed_treatment = - firebase::admob::kChildDirectedTreatmentStateTagged; - - // The user's birthday, if known. Note that months are indexed from one. - request.birthday_day = kBirthdayDay; - request.birthday_month = kBirthdayMonth; - request.birthday_year = kBirthdayYear; - - // Additional keywords to be used in targeting. - request.keyword_count = sizeof(kKeywords) / sizeof(kKeywords[0]); - request.keywords = kKeywords; - - // "Extra" key value pairs can be added to the request as well. Typically - // these are used when testing new features. - static const firebase::admob::KeyValuePair kRequestExtras[] = { - {"the_name_of_an_extra", "the_value_for_that_extra"}}; - request.extras_count = sizeof(kRequestExtras) / sizeof(kRequestExtras[0]); - request.extras = kRequestExtras; - - // This example uses ad units that are specially configured to return test ads - // for every request. When using your own ad unit IDs, however, it's important - // to register the device IDs associated with any devices that will be used to - // test the app. This ensures that regardless of the ad unit ID, those - // devices will always receive test ads in compliance with AdMob policy. - // - // Device IDs can be obtained by checking the logcat or the Xcode log while - // debugging. They appear as a long string of hex characters. - request.test_device_id_count = - sizeof(kTestDeviceIDs) / sizeof(kTestDeviceIDs[0]); - request.test_device_ids = kTestDeviceIDs; - - // Create an ad size for the BannerView. - firebase::admob::AdSize banner_ad_size; - banner_ad_size.ad_size_type = firebase::admob::kAdSizeStandard; - banner_ad_size.width = kBannerWidth; - banner_ad_size.height = kBannerHeight; - - LogMessage("Creating the BannerView."); - firebase::admob::BannerView* banner = new firebase::admob::BannerView(); - banner->Initialize(GetWindowContext(), kBannerAdUnit, banner_ad_size); - - WaitForFutureCompletion(banner->InitializeLastResult()); - - // Set the listener. - LoggingBannerViewListener banner_listener; - banner->SetListener(&banner_listener); - - // Load the banner ad. - LogMessage("Loading a banner ad."); - banner->LoadAd(request); - - WaitForFutureCompletion(banner->LoadAdLastResult()); - - // Make the BannerView visible. - LogMessage("Showing the banner ad."); - banner->Show(); - - WaitForFutureCompletion(banner->ShowLastResult()); - - // Move to each of the six pre-defined positions. - LogMessage("Moving the banner ad to top-center."); - banner->MoveTo(firebase::admob::BannerView::kPositionTop); - - WaitForFutureCompletion(banner->MoveToLastResult()); - - LogMessage("Moving the banner ad to top-left."); - banner->MoveTo(firebase::admob::BannerView::kPositionTopLeft); - - WaitForFutureCompletion(banner->MoveToLastResult()); - - LogMessage("Moving the banner ad to top-right."); - banner->MoveTo(firebase::admob::BannerView::kPositionTopRight); - - WaitForFutureCompletion(banner->MoveToLastResult()); - - LogMessage("Moving the banner ad to bottom-center."); - banner->MoveTo(firebase::admob::BannerView::kPositionBottom); - - WaitForFutureCompletion(banner->MoveToLastResult()); - - LogMessage("Moving the banner ad to bottom-left."); - banner->MoveTo(firebase::admob::BannerView::kPositionBottomLeft); - - WaitForFutureCompletion(banner->MoveToLastResult()); - - LogMessage("Moving the banner ad to bottom-right."); - banner->MoveTo(firebase::admob::BannerView::kPositionBottomRight); - - WaitForFutureCompletion(banner->MoveToLastResult()); - - // Try some coordinate moves. - LogMessage("Moving the banner ad to (100, 300)."); - banner->MoveTo(100, 300); - - WaitForFutureCompletion(banner->MoveToLastResult()); - - LogMessage("Moving the banner ad to (100, 400)."); - banner->MoveTo(100, 400); - - WaitForFutureCompletion(banner->MoveToLastResult()); - - // Try hiding and showing the BannerView. - LogMessage("Hiding the banner ad."); - banner->Hide(); - - WaitForFutureCompletion(banner->HideLastResult()); - - LogMessage("Showing the banner ad."); - banner->Show(); - - WaitForFutureCompletion(banner->ShowLastResult()); - - // A few last moves after showing it again. - LogMessage("Moving the banner ad to (100, 300)."); - banner->MoveTo(100, 300); - - WaitForFutureCompletion(banner->MoveToLastResult()); - - LogMessage("Moving the banner ad to (100, 400)."); - banner->MoveTo(100, 400); - - WaitForFutureCompletion(banner->MoveToLastResult()); - - LogMessage("Hiding the banner ad now that we're done with it."); - banner->Hide(); - - WaitForFutureCompletion(banner->HideLastResult()); - - // Create and test InterstitialAd. - LogMessage("Creating the InterstitialAd."); - firebase::admob::InterstitialAd* interstitial = - new firebase::admob::InterstitialAd(); - interstitial->Initialize(GetWindowContext(), kInterstitialAdUnit); - - WaitForFutureCompletion(interstitial->InitializeLastResult()); - - // Set the listener. - LoggingInterstitialAdListener interstitial_listener; - interstitial->SetListener(&interstitial_listener); - - // When the InterstitialAd is initialized, load an ad. - LogMessage("Loading an interstitial ad."); - interstitial->LoadAd(request); - - WaitForFutureCompletion(interstitial->LoadAdLastResult()); - - // When the InterstitialAd has loaded an ad, show it. - LogMessage("Showing the interstitial ad."); - interstitial->Show(); - - WaitForFutureCompletion(interstitial->ShowLastResult()); - - // Wait for the user to close the interstitial. - while (interstitial->presentation_state() != - firebase::admob::InterstitialAd::PresentationState:: - kPresentationStateHidden) { - ProcessEvents(1000); - } - - // Start up rewarded video ads and associated mediation adapters. - LogMessage("Initializing rewarded video."); - namespace rewarded_video = firebase::admob::rewarded_video; - rewarded_video::Initialize(); - - WaitForFutureCompletion(rewarded_video::InitializeLastResult()); - - LogMessage("Setting rewarded video listener."); - LoggingRewardedVideoListener rewarded_listener; - rewarded_video::SetListener(&rewarded_listener); - - LogMessage("Loading a rewarded video ad."); - rewarded_video::LoadAd(kRewardedVideoAdUnit, request); - - WaitForFutureCompletion(rewarded_video::LoadAdLastResult()); - - // If an ad has loaded, show it. If the user watches all the way through, the - // LoggingRewardedVideoListener will log a reward! - if (rewarded_video::LoadAdLastResult().error() == - firebase::admob::kAdMobErrorNone) { - LogMessage("Showing a rewarded video ad."); - rewarded_video::Show(GetWindowContext()); - - WaitForFutureCompletion(rewarded_video::ShowLastResult()); - - // Normally Pause and Resume would be called in response to the app pausing - // or losing focus. This is just a test. - LogMessage("Pausing."); - rewarded_video::Pause(); - - WaitForFutureCompletion(rewarded_video::PauseLastResult()); - - LogMessage("Resuming."); - rewarded_video::Resume(); - - WaitForFutureCompletion(rewarded_video::ResumeLastResult()); - } - - LogMessage("Done!"); - - // Wait until the user kills the app. - while (!ProcessEvents(1000)) { - } - - delete banner; - delete interstitial; - rewarded_video::Destroy(); - firebase::admob::Terminate(); - delete app; - - return 0; -} diff --git a/admob/testapp/src/desktop/desktop_main.cc b/admob/testapp/src/desktop/desktop_main.cc deleted file mode 100644 index 0220c688..00000000 --- a/admob/testapp/src/desktop/desktop_main.cc +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright 2016 Google Inc. All rights reserved. -// -// 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. - -#include -#include -#include - -#ifdef _WIN32 -#include -#define chdir _chdir -#else -#include -#endif // _WIN32 - -#ifdef _WIN32 -#include -#endif // _WIN32 - -#include -#include - -#include "main.h" // NOLINT - -// The TO_STRING macro is useful for command line defined strings as the quotes -// get stripped. -#define TO_STRING_EXPAND(X) #X -#define TO_STRING(X) TO_STRING_EXPAND(X) - -// Path to the Firebase config file to load. -#ifdef FIREBASE_CONFIG -#define FIREBASE_CONFIG_STRING TO_STRING(FIREBASE_CONFIG) -#else -#define FIREBASE_CONFIG_STRING "" -#endif // FIREBASE_CONFIG - -extern "C" int common_main(int argc, const char* argv[]); - -static bool quit = false; - -#ifdef _WIN32 -static BOOL WINAPI SignalHandler(DWORD event) { - if (!(event == CTRL_C_EVENT || event == CTRL_BREAK_EVENT)) { - return FALSE; - } - quit = true; - return TRUE; -} -#else -static void SignalHandler(int /* ignored */) { quit = true; } -#endif // _WIN32 - -bool ProcessEvents(int msec) { -#ifdef _WIN32 - Sleep(msec); -#else - usleep(msec * 1000); -#endif // _WIN32 - return quit; -} - -std::string PathForResource() { - return std::string(); -} - -void LogMessage(const char* format, ...) { - va_list list; - va_start(list, format); - vprintf(format, list); - va_end(list); - printf("\n"); - fflush(stdout); -} - -WindowContext GetWindowContext() { return nullptr; } - -// Change the current working directory to the directory containing the -// specified file. -void ChangeToFileDirectory(const char* file_path) { - std::string path(file_path); - std::replace(path.begin(), path.end(), '\\', '/'); - auto slash = path.rfind('/'); - if (slash != std::string::npos) { - std::string directory = path.substr(0, slash); - if (!directory.empty()) chdir(directory.c_str()); - } -} - -int main(int argc, const char* argv[]) { - ChangeToFileDirectory( - FIREBASE_CONFIG_STRING[0] != '\0' ? - FIREBASE_CONFIG_STRING : argv[0]); // NOLINT -#ifdef _WIN32 - SetConsoleCtrlHandler((PHANDLER_ROUTINE)SignalHandler, TRUE); -#else - signal(SIGINT, SignalHandler); -#endif // _WIN32 - return common_main(argc, argv); -} - -#if defined(_WIN32) -// Returns the number of microseconds since the epoch. -int64_t WinGetCurrentTimeInMicroseconds() { - FILETIME file_time; - GetSystemTimeAsFileTime(&file_time); - - ULARGE_INTEGER now; - now.LowPart = file_time.dwLowDateTime; - now.HighPart = file_time.dwHighDateTime; - - // Windows file time is expressed in 100s of nanoseconds. - // To convert to microseconds, multiply x10. - return now.QuadPart * 10LL; -} -#endif diff --git a/admob/testapp/src/ios/ios_main.mm b/admob/testapp/src/ios/ios_main.mm deleted file mode 100644 index 6ccb2de5..00000000 --- a/admob/testapp/src/ios/ios_main.mm +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright 2016 Google Inc. All rights reserved. -// -// 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. - -#import - -#include - -#include "main.h" - -extern "C" int common_main(int argc, const char* argv[]); - -@interface AppDelegate : UIResponder - -@property(nonatomic, strong) UIWindow *window; - -@end - -@interface FTAViewController : UIViewController - -@end - -static int g_exit_status = 0; -static bool g_shutdown = false; -static NSCondition *g_shutdown_complete; -static NSCondition *g_shutdown_signal; -static UITextView *g_text_view; -static UIView *g_parent_view; - -@implementation FTAViewController - -- (void)viewDidLoad { - [super viewDidLoad]; - g_parent_view = self.view; - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - const char *argv[] = {FIREBASE_TESTAPP_NAME}; - [g_shutdown_signal lock]; - g_exit_status = common_main(1, argv); - [g_shutdown_complete signal]; - }); -} - -@end - -bool ProcessEvents(int msec) { - [g_shutdown_signal - waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:static_cast(msec) / 1000.0f]]; - return g_shutdown; -} - -WindowContext GetWindowContext() { - return g_parent_view; -} - -// Log a message that can be viewed in the console. -void LogMessage(const char* format, ...) { - va_list args; - NSString *formatString = @(format); - - va_start(args, format); - NSString *message = [[NSString alloc] initWithFormat:formatString arguments:args]; - va_end(args); - - NSLog(@"%@", message); - message = [message stringByAppendingString:@"\n"]; - - dispatch_async(dispatch_get_main_queue(), ^{ - g_text_view.text = [g_text_view.text stringByAppendingString:message]; - }); -} - -int main(int argc, char* argv[]) { - @autoreleasepool { - UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); - } - return g_exit_status; -} - -@implementation AppDelegate - -- (BOOL)application:(UIApplication*)application - didFinishLaunchingWithOptions:(NSDictionary*)launchOptions { - g_shutdown_complete = [[NSCondition alloc] init]; - g_shutdown_signal = [[NSCondition alloc] init]; - [g_shutdown_complete lock]; - - self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; - FTAViewController *viewController = [[FTAViewController alloc] init]; - self.window.rootViewController = viewController; - [self.window makeKeyAndVisible]; - - g_text_view = [[UITextView alloc] initWithFrame:viewController.view.bounds]; - - g_text_view.editable = NO; - g_text_view.scrollEnabled = YES; - g_text_view.userInteractionEnabled = YES; - - [viewController.view addSubview:g_text_view]; - - return YES; -} - -- (void)applicationWillTerminate:(UIApplication *)application { - g_shutdown = true; - [g_shutdown_signal signal]; - [g_shutdown_complete wait]; -} - -@end diff --git a/admob/testapp/src/main.h b/admob/testapp/src/main.h deleted file mode 100644 index 2eda2c10..00000000 --- a/admob/testapp/src/main.h +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2016 Google Inc. All rights reserved. -// -// 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. - -#ifndef FIREBASE_TESTAPP_MAIN_H_ // NOLINT -#define FIREBASE_TESTAPP_MAIN_H_ // NOLINT - -#if defined(__ANDROID__) -#include -#include -#elif defined(__APPLE__) -extern "C" { -#include -} // extern "C" -#endif // __ANDROID__ - -// Defined using -DANDROID_MAIN_APP_NAME=some_app_name when compiling this -// file. -#ifndef FIREBASE_TESTAPP_NAME -#define FIREBASE_TESTAPP_NAME "android_main" -#endif // FIREBASE_TESTAPP_NAME - -// Cross platform logging method. -// Implemented by android/android_main.cc or ios/ios_main.mm. -extern "C" void LogMessage(const char* format, ...); - -// Platform-independent method to flush pending events for the main thread. -// Returns true when an event requesting program-exit is received. -bool ProcessEvents(int msec); - -// WindowContext represents the handle to the parent window. It's type -// (and usage) vary based on the OS. -#if defined(__ANDROID__) -typedef jobject WindowContext; // A jobject to the Java Activity. -#elif defined(__APPLE__) -typedef id WindowContext; // A pointer to an iOS UIView. -#else -typedef void* WindowContext; // A void* for any other environments. -#endif - -#if defined(__ANDROID__) -// Get the JNI environment. -JNIEnv* GetJniEnv(); -// Get the activity. -jobject GetActivity(); -#endif // defined(__ANDROID__) - -// Returns a variable that describes the window context for the app. On Android -// this will be a jobject pointing to the Activity. On iOS, it's an id pointing -// to the root view of the view controller. -WindowContext GetWindowContext(); - -#endif // FIREBASE_TESTAPP_MAIN_H_ // NOLINT diff --git a/admob/testapp/testapp.xcodeproj/project.pbxproj b/admob/testapp/testapp.xcodeproj/project.pbxproj deleted file mode 100644 index 3afd2dea..00000000 --- a/admob/testapp/testapp.xcodeproj/project.pbxproj +++ /dev/null @@ -1,312 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXBuildFile section */ - 4A7C015A1CEAA2480011C504 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4A7C01591CEAA2480011C504 /* Images.xcassets */; }; - 520BC0391C869159008CFBC3 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 520BC0381C869159008CFBC3 /* GoogleService-Info.plist */; }; - 529226D61C85F68000C89379 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 529226D51C85F68000C89379 /* Foundation.framework */; }; - 529226D81C85F68000C89379 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 529226D71C85F68000C89379 /* CoreGraphics.framework */; }; - 529226DA1C85F68000C89379 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 529226D91C85F68000C89379 /* UIKit.framework */; }; - 529227211C85FB6A00C89379 /* common_main.cc in Sources */ = {isa = PBXBuildFile; fileRef = 5292271F1C85FB6A00C89379 /* common_main.cc */; }; - 529227241C85FB7600C89379 /* ios_main.mm in Sources */ = {isa = PBXBuildFile; fileRef = 529227221C85FB7600C89379 /* ios_main.mm */; }; - D66B16871CE46E8900E5638A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D66B16861CE46E8900E5638A /* LaunchScreen.storyboard */; }; -/* End PBXBuildFile section */ - -/* Begin PBXFileReference section */ - 4A7C01591CEAA2480011C504 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = testapp/Images.xcassets; sourceTree = ""; }; - 520BC0381C869159008CFBC3 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; - 529226D21C85F68000C89379 /* testapp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = testapp.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 529226D51C85F68000C89379 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; - 529226D71C85F68000C89379 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; - 529226D91C85F68000C89379 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; - 529226EE1C85F68000C89379 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; - 5292271F1C85FB6A00C89379 /* common_main.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = common_main.cc; path = src/common_main.cc; sourceTree = ""; }; - 529227201C85FB6A00C89379 /* main.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = main.h; path = src/main.h; sourceTree = ""; }; - 529227221C85FB7600C89379 /* ios_main.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ios_main.mm; path = src/ios/ios_main.mm; sourceTree = ""; }; - 52FD1FF81C85FFA000BC68E3 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = testapp/Info.plist; sourceTree = ""; }; - D66B16861CE46E8900E5638A /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 529226CF1C85F68000C89379 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 529226D81C85F68000C89379 /* CoreGraphics.framework in Frameworks */, - 529226DA1C85F68000C89379 /* UIKit.framework in Frameworks */, - 529226D61C85F68000C89379 /* Foundation.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 529226C91C85F68000C89379 = { - isa = PBXGroup; - children = ( - 4A7C01591CEAA2480011C504 /* Images.xcassets */, - D66B16861CE46E8900E5638A /* LaunchScreen.storyboard */, - 520BC0381C869159008CFBC3 /* GoogleService-Info.plist */, - 52FD1FF81C85FFA000BC68E3 /* Info.plist */, - 5292271D1C85FB5500C89379 /* src */, - 529226D41C85F68000C89379 /* Frameworks */, - 529226D31C85F68000C89379 /* Products */, - ); - sourceTree = ""; - }; - 529226D31C85F68000C89379 /* Products */ = { - isa = PBXGroup; - children = ( - 529226D21C85F68000C89379 /* testapp.app */, - ); - name = Products; - sourceTree = ""; - }; - 529226D41C85F68000C89379 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 529226D51C85F68000C89379 /* Foundation.framework */, - 529226D71C85F68000C89379 /* CoreGraphics.framework */, - 529226D91C85F68000C89379 /* UIKit.framework */, - 529226EE1C85F68000C89379 /* XCTest.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; - 5292271D1C85FB5500C89379 /* src */ = { - isa = PBXGroup; - children = ( - 5292271F1C85FB6A00C89379 /* common_main.cc */, - 529227201C85FB6A00C89379 /* main.h */, - 5292271E1C85FB5B00C89379 /* ios */, - ); - name = src; - sourceTree = ""; - }; - 5292271E1C85FB5B00C89379 /* ios */ = { - isa = PBXGroup; - children = ( - 529227221C85FB7600C89379 /* ios_main.mm */, - ); - name = ios; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 529226D11C85F68000C89379 /* testapp */ = { - isa = PBXNativeTarget; - buildConfigurationList = 529226F91C85F68000C89379 /* Build configuration list for PBXNativeTarget "testapp" */; - buildPhases = ( - 529226CE1C85F68000C89379 /* Sources */, - 529226CF1C85F68000C89379 /* Frameworks */, - 529226D01C85F68000C89379 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = testapp; - productName = testapp; - productReference = 529226D21C85F68000C89379 /* testapp.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 529226CA1C85F68000C89379 /* Project object */ = { - isa = PBXProject; - attributes = { - LastUpgradeCheck = 0640; - ORGANIZATIONNAME = Google; - TargetAttributes = { - 529226D11C85F68000C89379 = { - CreatedOnToolsVersion = 6.4; - }; - }; - }; - buildConfigurationList = 529226CD1C85F68000C89379 /* Build configuration list for PBXProject "testapp" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; - hasScannedForEncodings = 0; - knownRegions = ( - en, - ); - mainGroup = 529226C91C85F68000C89379; - productRefGroup = 529226D31C85F68000C89379 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 529226D11C85F68000C89379 /* testapp */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 529226D01C85F68000C89379 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - D66B16871CE46E8900E5638A /* LaunchScreen.storyboard in Resources */, - 4A7C015A1CEAA2480011C504 /* Images.xcassets in Resources */, - 520BC0391C869159008CFBC3 /* GoogleService-Info.plist in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 529226CE1C85F68000C89379 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 529227241C85FB7600C89379 /* ios_main.mm in Sources */, - 529227211C85FB6A00C89379 /* common_main.cc in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin XCBuildConfiguration section */ - 529226F71C85F68000C89379 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_SYMBOLS_PRIVATE_EXTERN = NO; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 529226F81C85F68000C89379 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - 529226FA1C85F68000C89379 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; - HEADER_SEARCH_PATHS = ( - "$(inherited)", - /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, - "\"$(SRCROOT)/src\"", - ); - INFOPLIST_FILE = testapp/Info.plist; - PRODUCT_NAME = "$(TARGET_NAME)"; - WRAPPER_EXTENSION = app; - }; - name = Debug; - }; - 529226FB1C85F68000C89379 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; - HEADER_SEARCH_PATHS = ( - "$(inherited)", - /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, - "\"$(SRCROOT)/src\"", - ); - INFOPLIST_FILE = testapp/Info.plist; - PRODUCT_NAME = "$(TARGET_NAME)"; - WRAPPER_EXTENSION = app; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 529226CD1C85F68000C89379 /* Build configuration list for PBXProject "testapp" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 529226F71C85F68000C89379 /* Debug */, - 529226F81C85F68000C89379 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 529226F91C85F68000C89379 /* Build configuration list for PBXNativeTarget "testapp" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 529226FA1C85F68000C89379 /* Debug */, - 529226FB1C85F68000C89379 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 529226CA1C85F68000C89379 /* Project object */; -} diff --git a/admob/testapp/testapp/Images.xcassets/AppIcon.appiconset/Contents.json b/admob/testapp/testapp/Images.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index eeea76c2..00000000 --- a/admob/testapp/testapp/Images.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,73 +0,0 @@ -{ - "images" : [ - { - "idiom" : "iphone", - "size" : "29x29", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "29x29", - "scale" : "3x" - }, - { - "idiom" : "iphone", - "size" : "40x40", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "40x40", - "scale" : "3x" - }, - { - "idiom" : "iphone", - "size" : "60x60", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "60x60", - "scale" : "3x" - }, - { - "idiom" : "ipad", - "size" : "29x29", - "scale" : "1x" - }, - { - "idiom" : "ipad", - "size" : "29x29", - "scale" : "2x" - }, - { - "idiom" : "ipad", - "size" : "40x40", - "scale" : "1x" - }, - { - "idiom" : "ipad", - "size" : "40x40", - "scale" : "2x" - }, - { - "idiom" : "ipad", - "size" : "76x76", - "scale" : "1x" - }, - { - "idiom" : "ipad", - "size" : "76x76", - "scale" : "2x" - }, - { - "idiom" : "ipad", - "size" : "83.5x83.5", - "scale" : "2x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/admob/testapp/testapp/Images.xcassets/LaunchImage.launchimage/Contents.json b/admob/testapp/testapp/Images.xcassets/LaunchImage.launchimage/Contents.json deleted file mode 100644 index a0ad363c..00000000 --- a/admob/testapp/testapp/Images.xcassets/LaunchImage.launchimage/Contents.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "images" : [ - { - "orientation" : "portrait", - "idiom" : "ipad", - "extent" : "full-screen", - "minimum-system-version" : "7.0", - "scale" : "1x" - }, - { - "orientation" : "landscape", - "idiom" : "ipad", - "extent" : "full-screen", - "minimum-system-version" : "7.0", - "scale" : "1x" - }, - { - "orientation" : "portrait", - "idiom" : "ipad", - "extent" : "full-screen", - "minimum-system-version" : "7.0", - "scale" : "2x" - }, - { - "orientation" : "landscape", - "idiom" : "ipad", - "extent" : "full-screen", - "minimum-system-version" : "7.0", - "scale" : "2x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/admob/testapp/testapp/Info.plist b/admob/testapp/testapp/Info.plist deleted file mode 100644 index 3f0f944e..00000000 --- a/admob/testapp/testapp/Info.plist +++ /dev/null @@ -1,28 +0,0 @@ - - - - - GADApplicationIdentifier - YOUR_IOS_ADMOB_APP_ID - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - com.google.ios.admob.testapp - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - LSRequiresIPhoneOS - - UILaunchStoryboardName - LaunchScreen - - diff --git a/scripts/build_scripts/build_testapps.json b/scripts/build_scripts/build_testapps.json index 7dc1d078..5b5ef2d1 100644 --- a/scripts/build_scripts/build_testapps.json +++ b/scripts/build_scripts/build_testapps.json @@ -1,18 +1,5 @@ { "apis": [ - { - "name": "admob", - "full_name": "FirebaseAdmob", - "bundle_id": "com.google.ios.admob.testapp", - "ios_target": "testapp", - "tvos_target": "", - "testapp_path": "admob/testapp", - "frameworks": [ - "firebase_admob.xcframework", - "firebase.xcframework" - ], - "provision": "Google_Development.mobileprovision" - }, { "name": "analytics", "full_name": "FirebaseAnalytics", diff --git a/scripts/gha-encrypted/admob/GoogleService-Info.plist.gpg b/scripts/gha-encrypted/admob/GoogleService-Info.plist.gpg deleted file mode 100644 index 7a8dd56b43dacbfaf599257dc3c7228deffe524e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 671 zcmV;Q0$}}&4Fm}T0-Lp(S*XbSzyH$d0ccQ3S6V1c>`Vq{v@eT;(WXE&Mg7Sm$!4&< z@!pq8juOCk>c~ub=-BY`=|$N%sL8OnJhQx3;)u5G_mI7lb7x&|tZJ`xmU6Y|4}M`o-pzpt>cC;cW3n(fm#lMj0q5qrw#iXV@hH3!~0EURh?^Y}bnKHD}=G$)7BL6z=wX z61wI zU^IG4E^8z{RtfF8iGTe6w0ms+K{j{aCnwrF zQ(W`X)ryzv4IjF(R2}7emWWn?aS5FZaWa8+4BoL4^j}#qP{bD(UkyP^~9$knrMTAClQ`y;xhD(q}xeIq+JpqL8h^2E> zH%l>NZN>i!)bm8tWMoH+{9-#))^qYDF~q_RhG(d*n*RRsaoQ+$jVH Fi_{?GSk3?d diff --git a/scripts/gha-encrypted/admob/google-services.json.gpg b/scripts/gha-encrypted/admob/google-services.json.gpg deleted file mode 100644 index b62f63cbd715bac8245378fe906b83c40f168bbe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1002 zcmV|*kk0})|U8{>`vX{A^rB0mUfNRZ3b@0(nBs0uCLT5=f&CS7md#Vj&Q8(kxYI|_37n{ zJ}{jbQu@aHPH&_>z`JK!8@DwEYY^&yKTQ0dvkSeP*eUR(u1Uo5TYJf*^mDeGN9duk z8nii0zNRM>G4S=ub?k1o&K;x!!c%~Je;MAmq2m7_C;TP?FoA#-0aF-v#3`vetun(a%*PI# z-YEo)EDP?8qw7I;P;${whXK)^q5UYT`namLHDbE zv1#5*IDnu2pd4ACmBp^Av6(*@k|^+B^yML1DV_j+UAG*8?ovqQRQ{ z*{e3BDhx2%z*jOvllCRg!51fA0cDg$ymDS0b4BXgU#W)*kg69Ayb`d;C#Wt^5{Oxq z&LG)P>~?QI7BYDLKMlfL)Vfa5RqCvDdHuuNtN_H{y1h}xPGJ`#eM^_A@Ft#VHnP+m z+%l8o45e`Ql2}r~?NdQmIz2Cfvi(t9!^Ne-*o*|f*iIre@_eihE5slG-9_xHNlR54 zd|>Y0(rKpKTGZ%-lt}y({)arrs8Snc4GBf48&t1I`HN5o-?>I%-_i#MU@L9n$_}Lt z%>~;AA;Z13$f5C(fNXk8&Aox%LXL&b0Dp4Cm?d6wEb_1Q`}{2%m43+QSioY9j*9lx zfi&XaUSG~Qp_EOTU31bi0Z)!OzzJP4Qu|9Isy=iG>pi_|m;h2(U0A=U=Zs^MQ0Eig z@>P&AdHOoJG;=J0xZA>?(KL1xCX)wctgo1|yv@!RTFChPvNK(K!bUox+U6aQ5gAXd zipi1IvmVTR+>zj$82f9Y0>mLfFf5C1&+q|X!Fg%ST!JNNvbNXO*b-PoZ63MT5sxiV z&S&PyOhi6OBMXcedg~ut+-5@gSV|E4;bP8(z467TvcX;x`x0hYO{WmPoKO$Z_8Ey* Yboa4XrsEr5lqq?)xW4iT*rtkxy%%)*%m4rY From 233418b2f34111f1f08fc60ab153db7e894d3bd0 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Thu, 23 May 2024 16:41:59 -0700 Subject: [PATCH 28/33] Update Cocoapods to latest. --- analytics/testapp/Podfile | 4 ++-- auth/testapp/Podfile | 4 ++-- database/testapp/Podfile | 6 +++--- dynamic_links/testapp/Podfile | 4 ++-- firestore/testapp/Podfile | 6 +++--- functions/testapp/Podfile | 6 +++--- gma/testapp/Podfile | 6 +++--- messaging/testapp/Podfile | 4 ++-- remote_config/testapp/Podfile | 4 ++-- storage/testapp/Podfile | 6 +++--- 10 files changed, 25 insertions(+), 25 deletions(-) diff --git a/analytics/testapp/Podfile b/analytics/testapp/Podfile index f7888311..9428612e 100644 --- a/analytics/testapp/Podfile +++ b/analytics/testapp/Podfile @@ -1,6 +1,6 @@ source 'https://github.com/CocoaPods/Specs.git' -platform :ios, '8.0' +platform :ios, '13.0' # Analytics test application. target 'testapp' do - pod 'Firebase/Analytics', '7.0.0' + pod 'Firebase/Analytics', '10.25.0' end diff --git a/auth/testapp/Podfile b/auth/testapp/Podfile index d2ddd900..a44a8d32 100644 --- a/auth/testapp/Podfile +++ b/auth/testapp/Podfile @@ -1,6 +1,6 @@ source 'https://github.com/CocoaPods/Specs.git' -platform :ios, '8.0' +platform :ios, '13.0' # Auth test application. target 'testapp' do - pod 'Firebase/Auth', '7.0.0' + pod 'Firebase/Auth', '10.25.0' end diff --git a/database/testapp/Podfile b/database/testapp/Podfile index 966f7b10..f004f70a 100644 --- a/database/testapp/Podfile +++ b/database/testapp/Podfile @@ -1,7 +1,7 @@ source 'https://github.com/CocoaPods/Specs.git' -platform :ios, '8.0' +platform :ios, '13.0' # Firebase Realtime Database test application. target 'testapp' do - pod 'Firebase/Database', '7.0.0' - pod 'Firebase/Auth', '7.0.0' + pod 'Firebase/Database', '10.25.0' + pod 'Firebase/Auth', '10.25.0' end diff --git a/dynamic_links/testapp/Podfile b/dynamic_links/testapp/Podfile index 8af0d756..af8f5566 100644 --- a/dynamic_links/testapp/Podfile +++ b/dynamic_links/testapp/Podfile @@ -1,6 +1,6 @@ source 'https://github.com/CocoaPods/Specs.git' -platform :ios, '8.0' +platform :ios, '13.0' # Dynamic Links test application. target 'testapp' do - pod 'Firebase/DynamicLinks', '7.0.0' + pod 'Firebase/DynamicLinks', '10.25.0' end diff --git a/firestore/testapp/Podfile b/firestore/testapp/Podfile index 31e20c7c..2f14e9d3 100644 --- a/firestore/testapp/Podfile +++ b/firestore/testapp/Podfile @@ -1,7 +1,7 @@ source 'https://github.com/CocoaPods/Specs.git' -platform :ios, '10.0' +platform :ios, '13.0' # Firebase Firestore test application. target 'testapp' do - pod 'Firebase/Firestore', '8.2.0' - pod 'Firebase/Auth', '8.2.0' + pod 'Firebase/Firestore', '10.25.0' + pod 'Firebase/Auth', '10.25.0' end diff --git a/functions/testapp/Podfile b/functions/testapp/Podfile index 51c5b50a..0a698245 100644 --- a/functions/testapp/Podfile +++ b/functions/testapp/Podfile @@ -1,7 +1,7 @@ source 'https://github.com/CocoaPods/Specs.git' -platform :ios, '8.0' +platform :ios, '13.0' # Cloud Functions for Firebase test application. target 'testapp' do - pod 'Firebase/Functions', '7.0.0' - pod 'Firebase/Auth', '7.0.0' + pod 'Firebase/Functions', '10.25.0' + pod 'Firebase/Auth', '10.25.0' end diff --git a/gma/testapp/Podfile b/gma/testapp/Podfile index 51c73e38..aa47dcc4 100644 --- a/gma/testapp/Podfile +++ b/gma/testapp/Podfile @@ -1,7 +1,7 @@ source 'https://github.com/CocoaPods/Specs.git' -platform :ios, '10.0' +platform :ios, '13.0' # GMA test application. target 'testapp' do - pod 'Google-Mobile-Ads-SDK', '8.13.0' - pod 'Firebase/Analytics', '8.10.0' + pod 'Google-Mobile-Ads-SDK', '11.2.0' + pod 'Firebase/Analytics', '10.25.0' end diff --git a/messaging/testapp/Podfile b/messaging/testapp/Podfile index e6ffc8dc..7aa873f4 100644 --- a/messaging/testapp/Podfile +++ b/messaging/testapp/Podfile @@ -1,6 +1,6 @@ source 'https://github.com/CocoaPods/Specs.git' -platform :ios, '8.0' +platform :ios, '13.0' # FCM test application. target 'testapp' do - pod 'Firebase/Messaging', '7.0.0' + pod 'Firebase/Messaging', '10.25.0' end diff --git a/remote_config/testapp/Podfile b/remote_config/testapp/Podfile index 5ce98e6d..274d3540 100644 --- a/remote_config/testapp/Podfile +++ b/remote_config/testapp/Podfile @@ -1,6 +1,6 @@ source 'https://github.com/CocoaPods/Specs.git' -platform :ios, '8.0' +platform :ios, '13.0' # Firebase Remote Config test application. target 'testapp' do - pod 'Firebase/RemoteConfig', '7.0.0' + pod 'Firebase/RemoteConfig', '10.25.0' end diff --git a/storage/testapp/Podfile b/storage/testapp/Podfile index f79f288b..8091b546 100644 --- a/storage/testapp/Podfile +++ b/storage/testapp/Podfile @@ -1,7 +1,7 @@ source 'https://github.com/CocoaPods/Specs.git' -platform :ios, '8.0' +platform :ios, '13.0' # Cloud Storage for Firebase test application. target 'testapp' do - pod 'Firebase/Storage', '7.0.0' - pod 'Firebase/Auth', '7.0.0' + pod 'Firebase/Storage', '10.25.0' + pod 'Firebase/Auth', '10.25.0' end From 9e61685c85b5cbd56eaffcdf7ddd10166eca9560 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Thu, 23 May 2024 17:09:50 -0700 Subject: [PATCH 29/33] Added use_frameworks! flag needed for Swift compatibility. --- analytics/testapp/Podfile | 1 + auth/testapp/Podfile | 1 + .../testapp/testapp.xcodeproj/project.pbxproj | 78 +++++++++++++++++++ database/testapp/Podfile | 1 + dynamic_links/testapp/Podfile | 1 + firestore/testapp/Podfile | 1 + functions/testapp/Podfile | 1 + gma/testapp/Podfile | 1 + messaging/testapp/Podfile | 1 + remote_config/testapp/Podfile | 1 + storage/testapp/Podfile | 1 + 11 files changed, 88 insertions(+) diff --git a/analytics/testapp/Podfile b/analytics/testapp/Podfile index 9428612e..5895e3a3 100644 --- a/analytics/testapp/Podfile +++ b/analytics/testapp/Podfile @@ -1,5 +1,6 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '13.0' +use_frameworks! # Analytics test application. target 'testapp' do pod 'Firebase/Analytics', '10.25.0' diff --git a/auth/testapp/Podfile b/auth/testapp/Podfile index a44a8d32..825abee2 100644 --- a/auth/testapp/Podfile +++ b/auth/testapp/Podfile @@ -1,5 +1,6 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '13.0' +use_frameworks! # Auth test application. target 'testapp' do pod 'Firebase/Auth', '10.25.0' diff --git a/auth/testapp/testapp.xcodeproj/project.pbxproj b/auth/testapp/testapp.xcodeproj/project.pbxproj index 5769362c..65f4be31 100644 --- a/auth/testapp/testapp.xcodeproj/project.pbxproj +++ b/auth/testapp/testapp.xcodeproj/project.pbxproj @@ -15,9 +15,11 @@ 529227241C85FB7600C89379 /* ios_main.mm in Sources */ = {isa = PBXBuildFile; fileRef = 529227221C85FB7600C89379 /* ios_main.mm */; }; 52B71EBB1C8600B600398745 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 52B71EBA1C8600B600398745 /* Images.xcassets */; }; D66B16871CE46E8900E5638A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D66B16861CE46E8900E5638A /* LaunchScreen.storyboard */; }; + FA08030EA3D096D266A6DE7B /* Pods_testapp.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 46C2548FA84411C2A9D067CB /* Pods_testapp.framework */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 46C2548FA84411C2A9D067CB /* Pods_testapp.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_testapp.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 520BC0381C869159008CFBC3 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; 529226D21C85F68000C89379 /* testapp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = testapp.app; sourceTree = BUILT_PRODUCTS_DIR; }; 529226D51C85F68000C89379 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; @@ -29,7 +31,9 @@ 529227221C85FB7600C89379 /* ios_main.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ios_main.mm; path = src/ios/ios_main.mm; sourceTree = ""; }; 52B71EBA1C8600B600398745 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = testapp/Images.xcassets; sourceTree = ""; }; 52FD1FF81C85FFA000BC68E3 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = testapp/Info.plist; sourceTree = ""; }; + B1320FFA7E8D45124716A662 /* Pods-testapp.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-testapp.release.xcconfig"; path = "Target Support Files/Pods-testapp/Pods-testapp.release.xcconfig"; sourceTree = ""; }; D66B16861CE46E8900E5638A /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = ""; }; + FC7EC0D42BD90C2E1CAB03EE /* Pods-testapp.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-testapp.debug.xcconfig"; path = "Target Support Files/Pods-testapp/Pods-testapp.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -40,12 +44,23 @@ 529226D81C85F68000C89379 /* CoreGraphics.framework in Frameworks */, 529226DA1C85F68000C89379 /* UIKit.framework in Frameworks */, 529226D61C85F68000C89379 /* Foundation.framework in Frameworks */, + FA08030EA3D096D266A6DE7B /* Pods_testapp.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 3E2FC86EB58D8B5977042124 /* Pods */ = { + isa = PBXGroup; + children = ( + FC7EC0D42BD90C2E1CAB03EE /* Pods-testapp.debug.xcconfig */, + B1320FFA7E8D45124716A662 /* Pods-testapp.release.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; 529226C91C85F68000C89379 = { isa = PBXGroup; children = ( @@ -56,6 +71,7 @@ 5292271D1C85FB5500C89379 /* src */, 529226D41C85F68000C89379 /* Frameworks */, 529226D31C85F68000C89379 /* Products */, + 3E2FC86EB58D8B5977042124 /* Pods */, ); sourceTree = ""; }; @@ -74,6 +90,7 @@ 529226D71C85F68000C89379 /* CoreGraphics.framework */, 529226D91C85F68000C89379 /* UIKit.framework */, 529226EE1C85F68000C89379 /* XCTest.framework */, + 46C2548FA84411C2A9D067CB /* Pods_testapp.framework */, ); name = Frameworks; sourceTree = ""; @@ -103,9 +120,11 @@ isa = PBXNativeTarget; buildConfigurationList = 529226F91C85F68000C89379 /* Build configuration list for PBXNativeTarget "testapp" */; buildPhases = ( + 17179573A7AC37A3937EFB3C /* [CP] Check Pods Manifest.lock */, 529226CE1C85F68000C89379 /* Sources */, 529226CF1C85F68000C89379 /* Frameworks */, 529226D01C85F68000C89379 /* Resources */, + D4E64C05496F0F15FA3C6510 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -160,6 +179,63 @@ }; /* End PBXResourcesBuildPhase section */ +/* Begin PBXShellScriptBuildPhase section */ + 17179573A7AC37A3937EFB3C /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-testapp-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + D4E64C05496F0F15FA3C6510 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-testapp/Pods-testapp-frameworks.sh", + "${BUILT_PRODUCTS_DIR}/FirebaseAppCheckInterop/FirebaseAppCheckInterop.framework", + "${BUILT_PRODUCTS_DIR}/FirebaseAuth/FirebaseAuth.framework", + "${BUILT_PRODUCTS_DIR}/FirebaseCore/FirebaseCore.framework", + "${BUILT_PRODUCTS_DIR}/FirebaseCoreInternal/FirebaseCoreInternal.framework", + "${BUILT_PRODUCTS_DIR}/GTMSessionFetcher/GTMSessionFetcher.framework", + "${BUILT_PRODUCTS_DIR}/GoogleUtilities/GoogleUtilities.framework", + "${BUILT_PRODUCTS_DIR}/PromisesObjC/FBLPromises.framework", + "${BUILT_PRODUCTS_DIR}/RecaptchaInterop/RecaptchaInterop.framework", + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseAppCheckInterop.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseAuth.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCore.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCoreInternal.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GTMSessionFetcher.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleUtilities.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBLPromises.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RecaptchaInterop.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-testapp/Pods-testapp-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + /* Begin PBXSourcesBuildPhase section */ 529226CE1C85F68000C89379 /* Sources */ = { isa = PBXSourcesBuildPhase; @@ -255,6 +331,7 @@ }; 529226FA1C85F68000C89379 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = FC7EC0D42BD90C2E1CAB03EE /* Pods-testapp.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; @@ -271,6 +348,7 @@ }; 529226FB1C85F68000C89379 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = B1320FFA7E8D45124716A662 /* Pods-testapp.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; diff --git a/database/testapp/Podfile b/database/testapp/Podfile index f004f70a..5832b272 100644 --- a/database/testapp/Podfile +++ b/database/testapp/Podfile @@ -1,5 +1,6 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '13.0' +use_frameworks! # Firebase Realtime Database test application. target 'testapp' do pod 'Firebase/Database', '10.25.0' diff --git a/dynamic_links/testapp/Podfile b/dynamic_links/testapp/Podfile index af8f5566..e983680a 100644 --- a/dynamic_links/testapp/Podfile +++ b/dynamic_links/testapp/Podfile @@ -1,5 +1,6 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '13.0' +use_frameworks! # Dynamic Links test application. target 'testapp' do pod 'Firebase/DynamicLinks', '10.25.0' diff --git a/firestore/testapp/Podfile b/firestore/testapp/Podfile index 2f14e9d3..e0bca1be 100644 --- a/firestore/testapp/Podfile +++ b/firestore/testapp/Podfile @@ -1,5 +1,6 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '13.0' +use_frameworks! # Firebase Firestore test application. target 'testapp' do pod 'Firebase/Firestore', '10.25.0' diff --git a/functions/testapp/Podfile b/functions/testapp/Podfile index 0a698245..7197d817 100644 --- a/functions/testapp/Podfile +++ b/functions/testapp/Podfile @@ -1,5 +1,6 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '13.0' +use_frameworks! # Cloud Functions for Firebase test application. target 'testapp' do pod 'Firebase/Functions', '10.25.0' diff --git a/gma/testapp/Podfile b/gma/testapp/Podfile index aa47dcc4..4683028b 100644 --- a/gma/testapp/Podfile +++ b/gma/testapp/Podfile @@ -1,5 +1,6 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '13.0' +use_frameworks! # GMA test application. target 'testapp' do pod 'Google-Mobile-Ads-SDK', '11.2.0' diff --git a/messaging/testapp/Podfile b/messaging/testapp/Podfile index 7aa873f4..b022880f 100644 --- a/messaging/testapp/Podfile +++ b/messaging/testapp/Podfile @@ -1,5 +1,6 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '13.0' +use_frameworks! # FCM test application. target 'testapp' do pod 'Firebase/Messaging', '10.25.0' diff --git a/remote_config/testapp/Podfile b/remote_config/testapp/Podfile index 274d3540..14a96f7d 100644 --- a/remote_config/testapp/Podfile +++ b/remote_config/testapp/Podfile @@ -1,5 +1,6 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '13.0' +use_frameworks! # Firebase Remote Config test application. target 'testapp' do pod 'Firebase/RemoteConfig', '10.25.0' diff --git a/storage/testapp/Podfile b/storage/testapp/Podfile index 8091b546..7010e7c0 100644 --- a/storage/testapp/Podfile +++ b/storage/testapp/Podfile @@ -1,5 +1,6 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '13.0' +use_frameworks! # Cloud Storage for Firebase test application. target 'testapp' do pod 'Firebase/Storage', '10.25.0' From a1028f445901be3593784425eb78c0a69bc43020 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Tue, 28 May 2024 11:35:31 -0700 Subject: [PATCH 30/33] Remove extraneous pods. --- .../testapp/testapp.xcodeproj/project.pbxproj | 75 ++----------------- 1 file changed, 5 insertions(+), 70 deletions(-) diff --git a/auth/testapp/testapp.xcodeproj/project.pbxproj b/auth/testapp/testapp.xcodeproj/project.pbxproj index 65f4be31..dbad8094 100644 --- a/auth/testapp/testapp.xcodeproj/project.pbxproj +++ b/auth/testapp/testapp.xcodeproj/project.pbxproj @@ -15,11 +15,10 @@ 529227241C85FB7600C89379 /* ios_main.mm in Sources */ = {isa = PBXBuildFile; fileRef = 529227221C85FB7600C89379 /* ios_main.mm */; }; 52B71EBB1C8600B600398745 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 52B71EBA1C8600B600398745 /* Images.xcassets */; }; D66B16871CE46E8900E5638A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D66B16861CE46E8900E5638A /* LaunchScreen.storyboard */; }; - FA08030EA3D096D266A6DE7B /* Pods_testapp.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 46C2548FA84411C2A9D067CB /* Pods_testapp.framework */; }; + D6FC32B32C06595E00E3E028 /* GameKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D6FC32B22C06595E00E3E028 /* GameKit.framework */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ - 46C2548FA84411C2A9D067CB /* Pods_testapp.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_testapp.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 520BC0381C869159008CFBC3 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; 529226D21C85F68000C89379 /* testapp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = testapp.app; sourceTree = BUILT_PRODUCTS_DIR; }; 529226D51C85F68000C89379 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; @@ -31,9 +30,8 @@ 529227221C85FB7600C89379 /* ios_main.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ios_main.mm; path = src/ios/ios_main.mm; sourceTree = ""; }; 52B71EBA1C8600B600398745 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = testapp/Images.xcassets; sourceTree = ""; }; 52FD1FF81C85FFA000BC68E3 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = testapp/Info.plist; sourceTree = ""; }; - B1320FFA7E8D45124716A662 /* Pods-testapp.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-testapp.release.xcconfig"; path = "Target Support Files/Pods-testapp/Pods-testapp.release.xcconfig"; sourceTree = ""; }; D66B16861CE46E8900E5638A /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = ""; }; - FC7EC0D42BD90C2E1CAB03EE /* Pods-testapp.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-testapp.debug.xcconfig"; path = "Target Support Files/Pods-testapp/Pods-testapp.debug.xcconfig"; sourceTree = ""; }; + D6FC32B22C06595E00E3E028 /* GameKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GameKit.framework; path = System/Library/Frameworks/GameKit.framework; sourceTree = SDKROOT; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -42,9 +40,9 @@ buildActionMask = 2147483647; files = ( 529226D81C85F68000C89379 /* CoreGraphics.framework in Frameworks */, + D6FC32B32C06595E00E3E028 /* GameKit.framework in Frameworks */, 529226DA1C85F68000C89379 /* UIKit.framework in Frameworks */, 529226D61C85F68000C89379 /* Foundation.framework in Frameworks */, - FA08030EA3D096D266A6DE7B /* Pods_testapp.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -54,10 +52,7 @@ 3E2FC86EB58D8B5977042124 /* Pods */ = { isa = PBXGroup; children = ( - FC7EC0D42BD90C2E1CAB03EE /* Pods-testapp.debug.xcconfig */, - B1320FFA7E8D45124716A662 /* Pods-testapp.release.xcconfig */, ); - name = Pods; path = Pods; sourceTree = ""; }; @@ -86,11 +81,11 @@ 529226D41C85F68000C89379 /* Frameworks */ = { isa = PBXGroup; children = ( + D6FC32B22C06595E00E3E028 /* GameKit.framework */, 529226D51C85F68000C89379 /* Foundation.framework */, 529226D71C85F68000C89379 /* CoreGraphics.framework */, 529226D91C85F68000C89379 /* UIKit.framework */, 529226EE1C85F68000C89379 /* XCTest.framework */, - 46C2548FA84411C2A9D067CB /* Pods_testapp.framework */, ); name = Frameworks; sourceTree = ""; @@ -120,11 +115,9 @@ isa = PBXNativeTarget; buildConfigurationList = 529226F91C85F68000C89379 /* Build configuration list for PBXNativeTarget "testapp" */; buildPhases = ( - 17179573A7AC37A3937EFB3C /* [CP] Check Pods Manifest.lock */, 529226CE1C85F68000C89379 /* Sources */, 529226CF1C85F68000C89379 /* Frameworks */, 529226D01C85F68000C89379 /* Resources */, - D4E64C05496F0F15FA3C6510 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -154,6 +147,7 @@ developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( + English, en, ); mainGroup = 529226C91C85F68000C89379; @@ -179,63 +173,6 @@ }; /* End PBXResourcesBuildPhase section */ -/* Begin PBXShellScriptBuildPhase section */ - 17179573A7AC37A3937EFB3C /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-testapp-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - D4E64C05496F0F15FA3C6510 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-testapp/Pods-testapp-frameworks.sh", - "${BUILT_PRODUCTS_DIR}/FirebaseAppCheckInterop/FirebaseAppCheckInterop.framework", - "${BUILT_PRODUCTS_DIR}/FirebaseAuth/FirebaseAuth.framework", - "${BUILT_PRODUCTS_DIR}/FirebaseCore/FirebaseCore.framework", - "${BUILT_PRODUCTS_DIR}/FirebaseCoreInternal/FirebaseCoreInternal.framework", - "${BUILT_PRODUCTS_DIR}/GTMSessionFetcher/GTMSessionFetcher.framework", - "${BUILT_PRODUCTS_DIR}/GoogleUtilities/GoogleUtilities.framework", - "${BUILT_PRODUCTS_DIR}/PromisesObjC/FBLPromises.framework", - "${BUILT_PRODUCTS_DIR}/RecaptchaInterop/RecaptchaInterop.framework", - ); - name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseAppCheckInterop.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseAuth.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCore.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCoreInternal.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GTMSessionFetcher.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleUtilities.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBLPromises.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RecaptchaInterop.framework", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-testapp/Pods-testapp-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; -/* End PBXShellScriptBuildPhase section */ - /* Begin PBXSourcesBuildPhase section */ 529226CE1C85F68000C89379 /* Sources */ = { isa = PBXSourcesBuildPhase; @@ -331,7 +268,6 @@ }; 529226FA1C85F68000C89379 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = FC7EC0D42BD90C2E1CAB03EE /* Pods-testapp.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; @@ -348,7 +284,6 @@ }; 529226FB1C85F68000C89379 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = B1320FFA7E8D45124716A662 /* Pods-testapp.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; From ee5157fb350a6c916a8b6e6b64c2ddf2eb2dbc84 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Fri, 21 Jun 2024 13:11:01 -0700 Subject: [PATCH 31/33] Update secret files. --- scripts/gha-encrypted/README | 3 +-- .../analytics/GoogleService-Info.plist.gpg | Bin 683 -> 670 bytes .../analytics/google-services.json.gpg | Bin 958 -> 962 bytes .../auth/GoogleService-Info.plist.gpg | Bin 691 -> 681 bytes .../auth/google-services.json.gpg | Bin 1014 -> 1007 bytes .../database/GoogleService-Info.plist.gpg | Bin 711 -> 700 bytes .../database/google-services.json.gpg | Bin 719 -> 725 bytes .../GoogleService-Info.plist.gpg | Bin 736 -> 649 bytes .../dynamic_links/google-services.json.gpg | Bin 712 -> 711 bytes .../firestore/GoogleService-Info.plist.gpg | Bin 610 -> 597 bytes .../firestore/google-services.json.gpg | Bin 534 -> 523 bytes .../functions/GoogleService-Info.plist.gpg | Bin 688 -> 674 bytes .../functions/google-services.json.gpg | Bin 850 -> 525 bytes .../gma/GoogleService-Info.plist.gpg | Bin 672 -> 663 bytes .../gma/google-services.json.gpg | Bin 1002 -> 953 bytes .../messaging/GoogleService-Info.plist.gpg | Bin 692 -> 681 bytes .../messaging/google-services.json.gpg | Bin 911 -> 609 bytes .../GoogleService-Info.plist.gpg | Bin 720 -> 614 bytes .../remote_config/google-services.json.gpg | Bin 1013 -> 848 bytes .../storage/GoogleService-Info.plist.gpg | Bin 682 -> 641 bytes .../storage/google-services.json.gpg | Bin 816 -> 779 bytes 21 files changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/gha-encrypted/README b/scripts/gha-encrypted/README index eb31d713..e73cd287 100644 --- a/scripts/gha-encrypted/README +++ b/scripts/gha-encrypted/README @@ -1,4 +1,3 @@ See https://help.github.com/en/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets -Googlers: code search firebase/cpp/Secrets/quickstart to find the sources. - +Googlers: code search firebase/cpp/Secrets to find the sources. \ No newline at end of file diff --git a/scripts/gha-encrypted/analytics/GoogleService-Info.plist.gpg b/scripts/gha-encrypted/analytics/GoogleService-Info.plist.gpg index 094a665cff13969b6a82cb9a4ffc017316aae368..a7fad00c750ca5f5dbea9480b71f296ba5c91778 100644 GIT binary patch literal 670 zcmV;P0%84(4Fm}T0;WqzTw6|j&;Qct0i2^tOioR>@Kcp;1g1!uk_aZ3C^`hX5j}}E zKlP6GSt)+Z>(`Fq;Ctxq8p$Obk8d@b6xoPDGq2&ZxSH;?(t7zIp*GCg#AYQ(i}1yH zs+Hhhi9NitnlPzMP99N%+cB)XS`L(H1MYrp-~KV!iYYprqV1o^g@<7^;0i9_R9S0oaJH9VaiuIAN7?_z6gPk(fJONZ3?dY1 zjlR0$hMA!FTj{#6XdQ53vVcm0F&b1Pfx703y{k!N1^I;0tS9FNNVFbXC_5WqXi_wd z46o2Ee$M^i8S0-5opNgjHLtR+qw`}0UGhybjY>-gTm(Mu!kl^p765QDknVHiNE>J7 zfcJcSkyg8t_XR$recyAn68a;(+3NWRAyxr6fQawDB7~>U{E4tWtttdB{l**J=3Tv- z_~KO0(4^8-AZYNLU8dD|KwN&7!n56XzdXLJUH_%;qrcT7LYwX<2%O}pAr!GLovxrf zl!URHVp|eG-vUz>+{ZUx7j_Gm_ex;&V!a79XygiazNsSwbptDSp7TEdBW$dh8Aw=H zyhr_lUmzDvW5!TC@J7SJj3wO;bUzYFt>nSN-u;WNQotl(u|PXrzT`w#x0urz=PN53 zl$QW+EPKH?P9cQPR?Z^^cy7|MyO+mmv8GiYdb$kfjn}L##!+MiVCM(2yO|0i2OGg6KgFaNq@Qa0xP)ug||uQE}`=`2k86 zz`Ol|O@_9$cQym3YRKN#BX!RvOO_DMuG?3Z3GeV|BCO`>=2UkEU_MMw2%BivP=uiV zVm|E-o5EJH+F!8i+@yKeuq-3jP#Z?J_OcIriJ`|?j(M92cGr{iKX5XIThZ|?U`0)_M9FFE0)N13828W(5SyTxb;KeC=PI8jZ6avy? z8%tBEv8W99m)Fy;wNGjei88@E?p7Vx5dXSh7nDnt640O?PG4wy2&7r}=d)L7@d?); z+JHmXHFS<+K}$_SNEts5k(?}#MWu>{NdQpRmJUk?@x8zHpTfG~JLP@FtV<_M7iuon zS7Pw<+b?9!0mqll6c;D$MUl96OpKtPVd1pvSB$uwKK_u@8u&{qkX8gaP{{t6wqFW6 z{Qa#JYc#BT-UDlr`AZy}tBm{BxHANZ6%%eozM%NbU=GklVvM}CYs@vuKt)}940KPx zWuXnGzti=BXdHpj9iP%xvJHOG6!LlZ1efvOX7>qH(~{J9im(b1HZ|T1b-w1ye|5il zH|v5@00QR#0eukrPE9uTDL+S4O_y{GM+-@G-hHX#RX)xlDFR{a%FfYwYKX-pcOPsbPG{u!oPt_P(IcQOUB&{Ml$Hr1=~^m3-C1A z2+!JO<9GG(5BVP;_fvEQeO7H>+9)lpDp8{!_0 RUmC{{rNp@RriZN!T-3M=P}%?h diff --git a/scripts/gha-encrypted/analytics/google-services.json.gpg b/scripts/gha-encrypted/analytics/google-services.json.gpg index 58d461365e9d4a8c78e6ba066a36ddc91e212659..8e2a09d3632c44e795411a6282fe5d901f16d900 100644 GIT binary patch literal 962 zcmV;z13mnV4Fm}T0$!46MC_qDvH#NP0b@M-?2@P-`n)1YV4CrfAMDE)uLPjzzGWdT zw+iU_^=hkl+aFSzKetJrFx5X4K7QuthEGu_t64cD3ft-_3mZtrX5so!oF5CM z5V!WN@AEYeT={zmV>{BI1WSqbKM)aNM!BK5v6Yr!=pvS353fQHJEUSWh(cXF8 z{LI5aGknz#fHbmAN1_hslh<9yhsvGPUYoVVrgP2aK1L6E1E!p3;*=1Fm%He?KdC=n zAo9g(V%L(f2+tGQVSCn4U23x)9l$C5$e_0d0-GD6ts`YobTgGYj?kRb{JRaz>*yBW zV=+U792B(6IE{&wbWkZYYBc%a+F9~pB{^}^9&*v-7)1neHCy#Un{1>!>CBSAvn&{l z2Cw6Fas;OE6)3N!>+=wh6{FF9Kn(@!7e%{kpnyX52plUB-?hTEX(>wVgMI1nj!p;j zf;69TK&A^QkN@XCpQGe+6!$wcw5ld)3HJ&Afh198x%}Z#{|1k-!q(VzR@U&s_o}om zn%}Jo%-Xwq>moXY@3x;X+$-1d_?t-LS>2P?7gb{)vAE=(fTTi=9*;8f9)hu7V8Yl1StQ>U-ZUEduMpf@+UH(zYVJx^-@2j2EdVUQP)0QS3n42ikLtu>+LP-WBe@+k}}TVs~t{NDa2CLa(fs?$b+pVGEcQ9b8#qmb->%<`< ziVe8ihbJRK$;Kl_&w%86%mif#OG_Zp91HsV+_*r)$hFd$-c*N+-gFfR3Ws!iI&^bL z9>GMqL?U8Y)KGnx&baI%nX^}oZ~Rcp-g~ZG`K_O;uM7uGpo)}z5HR9E!GbGLW4VhY zsa^b{*f$%(Lo5CBg_H;3=gczcJYn2mJ8Bduo-jmKU(Y*W-yhZ0fH3|;-n=^TG)h^P zQGa-gWA8oHilnCpvZ-z@mZi=#(C%KBJ;Ofg(Em2B#+6Z|EeV)*!c-(1qZYw{rRk3g z0j92(JQ`$Or1#Wyb#=8Fa^dN1fAx5jRb|Rw%Vx9nen>8l@G}z78Tg-@TXO_Aw!xGe kpw7T=*H!qXWQ&~}5Ao)oW8Q4TJBC;?mCsfBqAi*#&qs^sV*mgE literal 958 zcmV;v13~T!1C6|W5>d-`i}bU6bA{|IG`5R zQK%DNYAwaq57z|kLC#*^RMa>!u&xPi+r9jLD<~bCqhQ^FE{Ese@uZPen3?v>*;>@< z-Ui5zxdFkXoEK`Aui(c47d#?&&E692D*xmcsT`6ddfh#x7A9U&bB!v6&Xv$aPYF!O zqn+K@wwH`7{w@|(*l>P-`+4@v>2|_FyazJ$y@Z?MW{h{dsZ$Jk# zIHn?v1D4efSn%9@xMVYYCugM%(qoutO{_21}S;!0QpC5-UJl2aeo0^?iwlL&f`|4Xi7zOc&Qrh<_vOA0(C_7s1DW zK`@GoM&l@(B6{xY$=Di$7W!7rcRH17{f3T{dlL98uIZaE;^6epw1O!5@kU_x2Ee?&ZA9pwH~v@DzjE{j2IM5M*vreO3))AeAax z&L>NtX;1M#WKBI8-}FywR%b-Y2&ogHBFSD=qg7mp(@d+)`bUzhJwmueo_^=+A zA;DO*eirh=OQ}pjI}8}Qvn>567FBtJH2JbDS@FWWA$Iu!YJ_gn=?i-}W)aJU^+`C{lh!8$qjHNuFGV4L~$9?sg-PKb=(_y7O^ diff --git a/scripts/gha-encrypted/auth/GoogleService-Info.plist.gpg b/scripts/gha-encrypted/auth/GoogleService-Info.plist.gpg index 03490af4d0a7d8e6b73326e72709402670e41656..896aeabf097147db78a02c2621b253c3d4eed7d8 100644 GIT binary patch literal 681 zcmV;a0#^Nu4Fm}T0)^q1sm94UaR1Wj0WgL0MFx$_$<@G-EOi-&t#&2&e0#V7N9dL- zI+}-ZdwGi*In{=Gi6Ba_d;HD54xBEJ%+*j{{(`@Tja_i^Zp4{{U8D^y;^5WeGLTQ4 zgWCZE-|c1EJF6G{v@pfI6hjPVU1pQphbq4cf%*C12E3i$B8e({bU&N1hq`435Z7rM zsp8mYSA8;O1}T9tmuF@+o|6R}vN_j28gk>V(DNu~+1hXO_M=ZQA7s@n9HFw?*n0Du zIb%z-3)g>_4?w*WqA5#|xDN-oXMQPECzjD|e%?qgJ{DNE;~B=#HZ;3Wr`V=@HBPiG zh*qAhnJ;tbcjEryss18FW|NfQK8+`L-4;O6T&9qPm#{)2T&ocN z^F(m*zO*wn^0jH{?X6%Svr5t^g2s{*&(-sh3#Aw*^ILj^M~I& zHds!x@QSmEYRP;}+evop%(krSQZ+F`WHi+0?FsLnC&PHX#(&BvLCP_O5;aOlHm~h_ zNR9+R#V-lM4z}MLe5*-P)?Wur=p>wqTNSecX#yaD&VQi+4xKB4KZ5L>bI`{3=AhmN zUYe3lR0nx_>EB~raV{-eO?nN8|H8IU^ztSMWmDE1c5-Y%W3*B=^!Wva7=pj>fvj>9 zn@fK7f`RDT&WQ}6RPPE~m($`w*xh1UXJh%(w5E#gAJw{8lA^NIH9=E+rqR=l} z>x^Ydg~xpmLg5a3jk9S8RC3Ko$&DMaJ6rxm%>_YFi}Ax&Aw+8;`Ra_<*T%r9|=Pw*{dD9oenq%xPt}x zZohS$^X4lsLS}utirGsEzD{}&#g6scx4+nPx%H@~E(9+2{P+WO6@_*S<5>*)iEYBx zO5PA3mo?c!P5qM;4o6Oq+vAUr<+9V**PGTof#f6z>ydrZqK@rQr#kLfUCfA%Wp3d1P5>y+K?ntnpEzv|3NC~1F^^>%M<@0UXrW@qE*+M@iOP@mT|Gd1F@uR zmtxbpx}2aM7sj_~Q1<&I&`GoTa0e@Yykh7WQmk!U`1+UU#^dENB#Hj*efF|pQ0)Q3 za<0_gkOs+^1g%{!mNXF6h_`M$a(Trf7$c=(%20YmfDE}b#*Criw2=F(uVH|9Z_5Xy z3nMw8ScGIZ7X4YarR~a$O`}<^^36$2)dfZK3~V>FZt&7R2Ceg#d#7?J8?_d4s$<$+ zpg^q;YubOaxOJ)EiUZEQiK)bRAL>8^0{Vo$&&t1M3gsx%3!&4Onz}7GgP%^H$$L5+`QQp;Y4qR$6k)FDa*b1Kal-JuQ%;IOOS+5 ZL01p~douxq(TboFv_+~H0P(i1k1AEvVGRHP diff --git a/scripts/gha-encrypted/auth/google-services.json.gpg b/scripts/gha-encrypted/auth/google-services.json.gpg index dab68319270549a5feae30e2acbcb3299c7ded9c..9f86b0a3f0744d3414c5adb997de69569c43c655 100644 GIT binary patch literal 1007 zcmVM$j)HSj#JfyEq{6y@7%eWBzshrzeHbW(QPUh?1M%oN+ zl<#`oRRC)&L%l+yI5nrM(VO6&>7~!Gt*C_{*YPS$9&f$Au)UpZ8sY&(F@VmKT}mY& z#5C4Y@3zI`^>OS)5L+3UZj95$8X_6L$>?GoMe(2XG2OoLB+n`*0aV14*m|TXO%3vs z`#fR`)PVW-y<wD2UoZA5!4)tv-}w5O7GS@As$FYVSPE)Jy@f) zzgD2`C0KIc2N~IsAiBxlXeR66oCm;sw>(-%ZT6y-nJ&I2;{NXM%MuFqCXA{993tWg zJXMSsI6H}~gLpr59QJK%QjmeZ|I2b_GS7$L%6pJ`NgquEE>D6s2Q+^g>6sgk#{adh zamwNrSy-@NDnAc2umh$X4J%#vS{=W}mGoOJlRYgz`#T$Z?fG`jib)Ww!H(Ai+A|9( zCGD!5y)^2oOz1{DT18uC!5l&V2k3+ZIt^SVCDtNzn^fcR% zkR2H&^gUlwy%tbxBys(V%!sFdjcOsFNop zf}m7$cKvLevhAYfVDjuQ-tD(q8bKb47W{5(xY-&@b{r)RIL2WbXZRjo)d29MxnpSP zIN1VKmTH_zD~4*gDG|#;b9B{es}}-re5);FI5ymotn~{aG3+vxLEJ^C@|)JbgYC{n z&WV8T@)nMWydPPq-XvHsGz<>wG$x%K+zsFgNiCxGG(o6kv_j0$H6-HjK^7)xIS6Nr zwlIg-NLFM`T9I21@pqK`pDO3uyv6vrpI7z9&-HESydH}dUlCY-In(OK=4S%mMneLy deaS|)YNQ1sc8c)S(J0XeiL%r>S?SoKUA+l2_Kg4l literal 1014 zcmVL+WaXk?c zq*3kUynx<@M#=c@bk6dFVqn;6)ZBtB)Wwn5fcn1EvV-XPRFLVvqy4LGEuuYRa$lRv zyBsj80xKu49y!e9E+OW02mJ1BMTzC|*|XXG7v5>Bw`*yd6!(*$Hvf`8$_k4M;ev0- z7awGnaBFSRaVznrM}6yo_L<{-FL*wcOI!O0^Ata#RrFkB|?7LItTZv|eOY7wP+w>={~Jf%C(g21cPg zX|Mvab+=EgQBj+U#vETzaXZLXrr_P-UJJsZe7vBTAJO27 z-BteNGd+X?peYpy?U11xL)A5`LifKSDb@VK1TE;YkeI=2e=B^k2jiw69tbOX)?CAX z?AAp(G9!5}F5CK8Z6^6^Ap6^U;gtz~H?#ktH)!%2^~q+7%JL1^o9$={h@dkn+oV)= zEh6r-s#+gMQ8zXGQe@dE)IZbvVXVz8n zKC03CdF4!zxcskORJJQ!t7Qo{9fmJZmkwMN1kMAaHQD(-?(e)$olg1UB~Kx6<(1sJ kR+oCUDSlOJ$ZZ4zWZ!VAr$Sp_hSGH|$scxe{CmA+ApOk_b4Fm}T0(cJzehBA^?f=s00kcdA;5yS0EZ$;S0;$G?7>n8yJ(K!2&67ZF zWWR%SiD@5z=E^$ia~fPU{lSn$yiaAyWXYK2gUl2K#MZGBU;*&)j$R?VO`@K)^{c5D zDBzRFLGU>DK=rLle1% zQ&7ikKvoe{EWv0&<(fe)<|U`v?6b8WtmPa_Ob+?;0l-rI4Ucc3d&+6r-Ve`8=aO-^ zFaQF+Pp~yVNA&>RPg5W=!iW>Jt1y&Q%T!7+5Cimob<#=#!B2eLAfI}<%jXh4q((c) zC0?3|yN0x4>R@Fs!X)V!0{T%qu`ZyhO=HHQ1~w8~N<}TV9V8Zt^adEK=kT)vH`rKC zlWPa$GP!%l<`H#wt(5L%XJopqq49F3qxLr{ew?j2!J$)#0hJ&9w}KxIdovPA0M&QN zs1WNk(ucwAs5lM8B+3@shTTVdYOws$oGLI9>)!l4`%T!p+nO=L-}nGzH-$^D?ip!9 zhD8i8)crGxVk1ak@l>v9&=sWWh!|eQW(rrJu6332F^v4$pB8dptoVhgNxN#WqAdKm zMm5H@Dlb^_JNw5#O;)GyUA?@Scmo1GD9q??a(D;1Cf--Psmas`y_Cbo@XC(@le18v z@;9nvMxO-LPxF77j~wKAs=J#5O@nNV@-0Rnopzc1rg!fI_G4ARkw_PQ$#Um+vHV#M z@B$p9qJm>+CS~2{gjdU8@kJFV$#eR1z(T(=DS`0*Pl^?lpZnojFe%OMuO2yhGF;tu zh*tB@Fbhl*Blgw%MxDH6%#X<%$QL{?Qe?@S-Z;lt9u9H)K(guImHxx1573}Mhznfu i0ZCbQ9@F%tASpgo6Iv~MWx$&}Bm-LBzWPXW4=kaDAX+B? literal 711 zcmV;&0yzDQ4Fm}T0-KkN* z*AcJ9sh^H-4Y~JV3Ck26vB5BNaqheDd{-KqxxS9G?R<~O&vpSx>P1I(^nw;PczR!k zsFMY7EgTi|*GmA6EXrD)o>OwdCY@iY{h&>B1M^? z;T90c0fXV>`|TfoN)QWJNr+DsAWK8H7G0%&+2c@S5#OyeA>kmb9(alX8ONFnOc&+s z>1n%zgZ$`|4$$H-BQX@}vj*W>$zf~~?Mn7Q(_qE4@ZcL~EX?IzPHK@;Yv7f3(73Yf z3za5ER(%9uG&Va4bwRwIAqi%dBq8?M5+&?v6I@skmF*k*} z?rU{J>>BKpviowaGkUV`)<_S4q^8x^3pkQ+c6LVU7#~ZNF?I-lfLsH8=_HTq8H)TD z;Yr6hYz%`-)R%??jKCW$iMbm3aSM@YdXgqI(}lduq2GRB@Xy2NSk((zni8v$XykUb zw8JLO`AIA52|*#WU15yA+?L$F@bl8zyea!cN`LnHeaHanIDaQ8%mum`UsUx?Nx^sr zKDdv(_*56ScLp?@{(tQEh;J$DY~#4SR%@r z(hek?DkYFR;EPv69{Efcs~{pA#1`Pj%0(?6^g%^C@Fb21BnFF8BlK;}_0XBiO5((1Dx)2khkzu`aON|@;Vh{*jeNDD~ zi}#_Cy_i+!6lM+42$j{L4(c1^$%c?M6~#uzWZ_!((?{@NK%%cr*@Q(+pqJiz z(P-R8_|!Ks|{pQaQeY=ZcX#UL{QAEl+(sGUFi%s zW&WU*l)R6TsJta8Rg3fMG1;yH<%D|)syp+q^GYl|HQd`M`b8td^iMQt9qslQCT$bT zURH?DZj{ovE5Ai6k{V^TT?l+x*9%F;+l+7|O;rBPfMwjK7ea3mu^7~wIHbZw3aD(- zTz9q#dwdwBeETp2Hy4{01?J|LMAI zHp}cU5op)eQ0HlcfHoJG(nxd*imlmmah@FDqs`Nq8rHFEhL~`?FDH`4$@^9^{VN-R z?erSZ&AcyW$sh0{@iamdFaVac$G9H^u!nF-Hh3*fP)&(u)!^vHVzb#x8%hhR_TxmO z50buX>fQX11U1DptB38*Gc3b?Y#SV%_C4FE~&7O)zaVF@o7t@|;>?lb?5a zTVb)Gt&2?<>Q9HDQYX+CT$uBgdq?kkJCy366&N81OfWk3k60AAMQdlW-sC5Rx z_2lb2z&`)n??HLhp-HVDzygf7d9G@CBHD$D!~n~eu{3|CW37X z@)@nH#!9CCd3}X*zk~~4W;4Co=sP#wJ$8J>4zdrBHDDp3oGI&MVZS}eqt^n`hE+t@ z0-fhWA9GF#Pl3oZ3w^8Y!@8?4Nkb~u{zS2+j5x!zHd>Mhsr~#-Cb~tu@DuPr*k1=g zjq7y4sl2NZF{D|03)&1q*uk~;d1NRWLRBy({!v$15YY{jrvmszOdSft-XySWS8YuQ H8CqjunkZxu literal 719 zcmV;=0xqF(c=A$u&ooC)s zbjFHc`gAB*^gXRx9-$n#ZSN5C#eIlgVgwdLqcm*CT1>{H(~wM?-olk~90Sn5!dTho zeOTUEyNv;^raBh_XolvWN$(-yyBIG15u440aPXMWqC5|f9FCXVN|9(wS`hT|;Az>y zs@n;ad+ipmQpP0Gw6Y(Dt+uYwvhJBqQCSFcw4=IT*U7}&y1!$>0wOk?M>GF~kM+)u z@!ME-Kn}_k22-~n1;UrIq6hs`g)e}0p;GDl|1G0-X^Dr?2Pp)?yCiEbtl-0|TzTdk zkZ?b#95c;Hb{UO?Iq(@I{jb7IKvgkux1Q2Q1w7)0gFLHb63S*g&-BOW)4J#DGz1I{ z@4jpWTrg6;qo+7Cdqi@2>y-@^Ahq1_(|#)y-xqV&0p=@jpxcQ4QR+`a6nm`U^*y#L zx#RMkGKkt?aVK3^LqmCjr9Y=?JO%a06tygu@a9;lW}Nl!=1XEkD8KG@Xm>UqJ@&NZ zH1K56y}?TgW4FSK3eHG(y#=7iMyx?zyYB?sSxL%uAp7Q2RU}7H1gU3kn3_|*Jl-|z-TtiD)i~cf_QIF1;C`k6 zE{PCqjgsZ5n&EryV%QAvg37X;v5ektJ4DC%1hb~fnKmKVwXe`bGdw21y7YN)CRam1 z!EG{$hct?=x!w^6PIW%|zV)@!!$%njIkgzgW^%I-4kM=+tvZ(>P9(QA(IEEAQe>Zb BWBC98 diff --git a/scripts/gha-encrypted/dynamic_links/GoogleService-Info.plist.gpg b/scripts/gha-encrypted/dynamic_links/GoogleService-Info.plist.gpg index 871ee6e1b6d0ffed52db74b79cc38f0bb04b760f..7d577bef1b38f967ce4e02edc3e611a517f52944 100644 GIT binary patch literal 649 zcmV;40(Sk34Fm}T0-Dh`Owg>JQ2)~D0aY1%yW!%9C4D+&6AtW4i+d0Z!YipN;u2b4 zGAfd*u_^DP&|{np4Gyof>Fm!*Aptt&AtnYaZ@nIY8OQ&Fljd>$hTeOWs`Q#6i`298 zx(P`77eg-Q8c{Hp!TZe-WnR`)=3;nav5~#l`R!%fT%_=#hP02bPMRkcZOSX`^yKv~ zymiROQy9=q(vPYJk~+9asS-1<#6ZHr-FcM%8Xf0`1(7F?@*&H703^>{>F-RH=G00s zB*sEexU{Y4d1M~yq6k|)VBe061ma4$`E|SQMe2rhMd;2VBV1BM+!?yUa_=5Qa%1M9 zU&DA%8o01d4V-&od>C)B5+juu9l&t1frY>{~?M&Fi1LP}IYsuaMzCKu`{>2vUbkAHFZ^@6mvTDEL`uR-O zCb)(iU7^*?K$@jKtNwusNDD|Djl3()*?V0=x9F8jf;9yn*YRGkz*HGED^XY}Yo_WT zpBy}$3-fV(XqKecyR_Gw;D?3rhzCd~fe;CDmfbOm)>oX}1EP z`3Xq6MEGe&iqFOj%&d>QVVZ@iai82pDti$c@1z_GVGvt(ANtQ&99jd|y!U)lA?)bZ zFHPk%V-U0sqqJ0gQULn-+z*r~OT~&p>@Ip;(%)zYa_Lj8#!a z*bw+An6v=*wjbxj@N0;LkhyiS=}>KZ1NIe(U~?HHQ5DzF@9lQe%zl9qB$q|_#*_CH z=Z(TytDjxU?w=KNC2V}W(9-18*R{~j%hA%7YuoUggxxIuIS3ZbpiieZ(2onuhM_G1KNZf}sb=#`e&piw;sU)05Bee-KkI(5v$+CPlrTt}^D(_dLpmlG+u zdV3^pNsrqQ&QrxwFfNrt-O3;xRyL4ap>#T-ifDIU3G5v$^8Jo;a3$3SlZ-bv4%jHi z*Ub^TE-?}#OHRgFGQNAZ=N%d^5($um1(+jAdpVK-tRUd_pWKcE3Q*FTnNJ85g-($7 zQcWF+C~C=JH_)6Ut6=T9tv7nTE)#`l;^vRbpc;Qnc)59+?lR%aMO%43X^>5m=j-I=QBP(Zg3DA_)3^+wZR#XHsbf% zSR#7b_?aIrl&N@`w=)&`;MwYB{7oPOz|OJ}e=NEQ{Q)ZZIu7y0tRO~Gp%WwTIlP77 z;I)RJ8Xcm0f@E)R;QqY0c$Q&VuU1UJtq<=f#k{O&PcBKtP>Ig)x{0A=?ILn z5R4Jgk$I`2m{oC>tuEukzzy;#*;gS)dqV9R(+;6pu0YlusFY$vXyh3v)xGjT zA^3$ZcQIM!H{W$Q%go9KAB81=8(ay)dsn;cEZ>v&<4&E{LVI3SQX=boIOd#*tMFG) SzZ_K7y2`7uphu8mqCB-8c5L1N diff --git a/scripts/gha-encrypted/dynamic_links/google-services.json.gpg b/scripts/gha-encrypted/dynamic_links/google-services.json.gpg index 846543f60e983f1a137e51e0507045d05d1283ce..0db36dbf672d1e8a0a826260209587442ca2955e 100644 GIT binary patch literal 711 zcmV;&0yzDQ4Fm}T0vVD9ATySnb^p@o0X^G}`r2?o2V$_+XNaYMGyUT(DGdX9b1&0C zT8whjlOBc=B;;vcs)hI@;pOM#F+Pjy`-51l)iY0lTEMRQha6~+zu-ZJRh_R#RR7rQTw4*-;gV#?u_*h=Yp|!UPpM z_Hy_ZMG{*Z1EtwL%j>4wg)WM2-)cR2RggY4gQcBlrYV-^T48dr3{dm<*>WP4{5Yth zajXTX{=PM&DC`uYb#DybKSRHj$^N2*X-;1{ zSj|5AfsJ-bu(-hsTPL({P z@}t;!3mKH-Wh%ZOFU%iYXCQA+O`Iop4BU!lSqTacXQm=cMskq_zhQ$m5Lgn|yo9JR zkmNW&*si=ujCB?KGD7WbwT7_w`JS2`=Xk|F*lk)nR#^!SR)4))}YPv{aV{{=)DLjeUF#+jC>>09SBzkk^)ALBo#EFP0)GObqCkRYE zSVnRoR`>BI4;$0_mU8)iH{{=k|~lmu8vN1O*D47 zgK@Mqx6E`%cBbZIYGaCsP!8q@dx*c@`ZovCtA7zf5j3LLrqJ~JAuoj4DOnF9 z17)4Ip7wJ3k^>@58&?OxBvku;%UfVO8}jI1G8~w<5Ky+a?9MlNf?2iCkr^Qv4~dv`c@hq4?aWM!-=pM`&=r=nQ?BG0_qZ|*1B^$5Fv#BvPoC}>#1}z`l^Ny z3xsc3>rVtnz%vK06b6icE(M$+ON^!nnWdlAD843Yo1o-hAa`4Oo$Oy3ZXeI42K^LF ubm?XGZzX5!($>B!R4x@>1V+ZJtXoIH$qYp!*P95aNyp?RgcLU{(Zi>x1zpJi diff --git a/scripts/gha-encrypted/firestore/GoogleService-Info.plist.gpg b/scripts/gha-encrypted/firestore/GoogleService-Info.plist.gpg index 15ce08669627292013ee908e87fac31acaff9eef..4ae5fb4154aa88e5a0960c8a76b07b0ed930c12d 100644 GIT binary patch literal 597 zcmV-b0;>It4Fm}T0<3j;`}*~etpC#K0dM$E_Klms7qhh{e<*<_@<>z^i2PIsRL>`i-lu_e_^Hl7y4t zw~#KE-$j4xHTgQ5ARZr#QEL^J;NQR$87uzMN>@r;1IpcwSu7wgq`UG^k|AQT7v80}*a9QDczqwM8yOkz4d ze^PIUV{e-He8t$uRK_Y9HaqOH_p3%yQSqzd`{&jJ8Fp>y0??BqX#w~iX{K<)Pf;ww zaT~ika#*TZIiU@H_vl8unSHxBTl!baKuKv3MoAKs8G||*HR!A_f)5SzuL;@_?uoSN zR_GO3)P)zgYNzMC#_^G#LoaKzsdn@*N-O|WoKFt@Yb9a_N<Q!HM!sKL zcj=Ua)ec_!&!8yf4x#T^0@LcL|7Y^6d=5O*7wE8nn0`m-$0zS2n$GyuIr#buX(P56zqFy{+8cj-^nm z7Hr`-^1Ib|&hdg`jqOnMzS{wJLETZv)ILql7fJ4G8Y9sm3Kw#ZXk`z*0p=Gz{^_Q9 zD6XXqLvp3{QB#$}{p)qKRF5EGv)^rRw>Y3U!k43tVFI1<>U=FZ>ZIY#%VNTF)W6TA zMy&V^A7JF3Nt+PflxJa$K)p%xXxfM13>}@#tsyVPUmcW(m*5LBWH}HWK2wMoi~!i< zX%*|jXBuPpnwyYnEv{OZYt*7qD_o;^i+1o4(KPIl`@F!S_hoFWQ*$1>XACe|Q}8e+ zmtW@+Pbu$gS0F4zJi|Ft@1goj7YKAH>34mCp_YNpQ_Rwso$l$=fti$RO9exDwpXZ- zw+Z4>8m5|Kv#N45#nqmb(xNqQd=d-tj7nkf&BzrkB-3CID;WU-PLjstJGpfU5}SaV z;gxqdeulvxNduT_2~RMoEcXPVpu9Nt4t|QVPS?YVbKP9h!_G7~ev%)APM8n6o&Fd0 zfyrWrZYQl4kWTL|qoBhtQ0%<~HckpxK2&C}b?%>)!PE{X(xCeaGpz0V47jqZSlHYr w$ffhj1DuNqC%2YWTH+>#c<2nOvA-Bxn>ei(bn*{vGHYAZx-li$$u9-F3)^EXtpET3 diff --git a/scripts/gha-encrypted/firestore/google-services.json.gpg b/scripts/gha-encrypted/firestore/google-services.json.gpg index 9067b0d5720e88b7e5627be95bb7897e258cf234..9807334757f549f43b8e539c8e3f07272f57526f 100644 GIT binary patch literal 523 zcmV+m0`&ci4Fm}T0{pq`U*2ysTL03)IRW_&7 z*?~T&hUCDY5Zgl%eXy;Zv{&$f;FTYGPu(8%;i_)rSa<`{9@-y=OM06!dMSFZhj+fT z;C*YAzeny)arJMqI)D@$zO}JZJ2OIvte%buc$Gbzuhw4YOR|_jm-VcGX@d!nTS8ik z0Kf*J*DQWK*KkxJ{AV!Qp9K^$Q-z>0j+OWhdND5H8cJPW+E4x@i3mJ-DRHXTkuc4K z@a-^J@O5IwHp`Zu!{|cFLdCo3U8l1+daI`)cWj;iUQE@w{m>8gH3w;O)t4G$${WCA Nvp@Mw`)_THcA!*b0BryO literal 534 zcmV+x0_pvX4Fm}T0{*~q9{iWGv;WfR0s0HDOIVBGy4RwLz=KOkGZl<#eRes!xNxZH zEr9T5qJao+Z+qJD_H3R37DAFN~B8iJrg!)5d;nTvU>cz{{a0p>{2E*r1Ry~k8yXT?!E)gV`d1X za}VXAa$QY6m-fVmlmyK)k?2j_j2?KQH}#N-@~-=wNAMpM9GzBRoBg;PaoxaJG+u)MOH z!+mlgVZ7+2%gSzc?S?Ap%;AhZoP%EO9-8$w(|H~~ieCi2Kwdc~vPMKm({%&y6#3!q zL_g#P==9VHc5J6~FH1|Xu374+o7WTwz}lk~zFUF^yjjUDFz@JP2i6gQ5@7h{Vs&aiG8DBZ9d~uH=*;2YwLFc) zD(7rm1_}~6aooE{YAbeg9}f3A{<86qh*J3Wj4HS8TU>>V#tMAm8#2q|M=da)0dO6& zj?EBG&Y%gUA2%}A)75+h>5J>z?& zTgB+G+KJ@Fv|yeH0|d~rT4Oz=v`_TFm0bi(6*%1Wv9n3j;_|}PvBNU-_|=F~quo|P z!%(`KAZ`??1yTd-hScyLq@=jSg?wNtNScRh5?`R_hjf=1`CR?Scw_xQgRP%-$g0~%zPwCi&s1vG!Xi5;m(F?yjthQt+`-$56C^6zDx6U$Vlz;(n8R3MoPe zkPs$9{{)Q94|aZwwV7)rGhVCgf5oQjYw+%^qF8cq28a@puY52DT@jH%X?z2c*MkQ! z`KywMVlhSG-!3BaKXa0=kcRt`sVCIgmOCHFSfZ4D!B<4XYdzb??HR1lw^?dL7VsQ> zV+j#r{i9)Wim^LQ+arg&{3r_Y=oH+pa{l>qwFJ~8_aYMN@;zn=SaI^$V}0t8C~#z4 zIau9b?1w55xL0;4dvgfO<=GJ%zoc^#$vrpWJb+t(oZ&WP!c(FxdH8wRiyy I+!#PABySN)CGwSuv8y0yJg!xEF!H6d5b&yxvHMdm12%;^qJ?Ajpf+fxN2SCg zZ&-!)Z^`wH7+C@2MsFn#yLG``)2k1~e;V%pRh=dHai9KsMyXNhqZiFkm$-Q3lG+K2 z+^m;&ONh=g6DQ@3GQ{V6II|y)589iPvtn_D<3P1x->V=wPLyt=ALGGkur6H{HO=Ji6AzGA!@h|n;F7Sw&dU^R*x$Ll>dfA+*;))2Y)!E-z%sg$L?t{)enbUV?& z`hSA57Z@*U!wC{Y%%v3%h!zk>YxL7Z3w2-%5gEcEWTUFQh$Xw)VHuZ@6BjZDyyh&$;=UV%*9$kt6 diff --git a/scripts/gha-encrypted/functions/google-services.json.gpg b/scripts/gha-encrypted/functions/google-services.json.gpg index 41eed7b7e5c5ae888e7b53b148e46c2935ae3802..c9edac069d757c27369db388fb9c7c2fc48b1977 100644 GIT binary patch literal 525 zcmV+o0`mQg4Fm}T0pqeG z7XX_S2QE~FouFMOWTJcUPXB)JlC0^R60hrm6*Y_vxqw*D?7{vefHl!k4jl9kAIdfa z&^eWi)4);g{xX_)sIl6&^F`?Q?V~ls8SYT_mL;;&P+;@yw8~be6+IUMR+Y!>gESbu zrP+<0AZo+HIT3IPyj&;uLDTg|xhUX9i~Z(ya(VitBu{;Og^CYY=Ks~@M68xdL#56! zO|!TdV(<*&ynf^__C5-_lJE}^I~`vWZ*15@O(yYO3Mzq$Z2lFp16+aO?=CiD8~9%+ zm^k!ht*5(T@bN(1C-b{b5R&X^YRz4non5hdQLJ~OcNKNWIqo+tSWIFU#%(<1J`}=8 zbY%?n?*z*(aEBryXuzAVo(~kG$-kllOH2#+t8|!b^N!)7j4s!|wgtFu!8apX-Zlc(3_+p;YvBTU9F;X>FP?Z-0LyD#%yd P<^++o`2!*$pGGt{EocN) literal 850 zcmV-Y1Figw4Fm}T0<5<~zsUPs`Tx@C0g9{?M|q!2!(7*n{|}12)Isp^j$OFXkE4=I z5Sz>kwv`%#X)qzKTSVergpNoTNMu*U(|=K30aZ&T>+cv(q;@;8*>lyC=!3`cmuwN^u(~OVmAGK?y({HVgkh>ccYX(j{AShDmYSxu_R_OmaxO!U9=oDD9hf!~lU`^g4cfYn*WZ*k>s-@jTs;-<2e)0rQ6$*9EYj{}dg;ec8X5@t=6 z=r%5ID0`_n@XJTIcM1a*kWhQ=FBrhQHN1&0@<=o{vC5qI1~l_`+2=kGIMN)RS#R3z z$xTBx-~Qp}z<(JMTQl{QKzIWT(O`S2LJ(U^a z*Z$XxwsvZCBn=3%IPbatNFZVoWa^Qfp_b3Q7_Xm5c??~obpZUHoPRJ}ah<;WMDJqn z!x3q2rck+`M3Xiq_?C3~NJg-kAy9vsh8aVs?7}Slp~R(vh2ude;vHR3Dyg%PlxYyY zp@c=X~7{>A-v>tpsvfc(4}wdaA7;5IP)&gyl24ZypZ_f*w@{ zPG(Wdu5Vqm>_ZYJrkV}OPMCvOnM=az9CGeGv5GN-`z_0uhj&A9U0;f+gWxrt7>46p cn&8@9NHxUxA72=_ZY(Z2$lO diff --git a/scripts/gha-encrypted/gma/GoogleService-Info.plist.gpg b/scripts/gha-encrypted/gma/GoogleService-Info.plist.gpg index 58896342a9fe3079458eb8aa63a9e806e139d62d..65b703ce7e8841cd231a8eeb0b355a6631c9f463 100644 GIT binary patch literal 663 zcmV;I0%-k=4Fm}T0-fzaCMf$rivQB-0p|fHWyO^&Ync6x>Efpw?qdT3?Bq zjPBP~k2>%=QT6e(ZG>MNm7#5O(Tt)6z3idw07jl=zmH+QdQwr!bWyx1XVPt(Y69$O zD4AO!?IvhRONC0FbWiJT`zhZmbn27_AYEmd>+nnOdpgf=n_!lIL zK)jN{X+d{4{S4FtlNI9nxr_#!x(Q%I``z*)J2;DCGM_|iX3|-W)CQ5^yN1O75hO)q zOAm}cNOC&k9UUh(06a3A3f}Rm%cuh@98<4}TzGKY(o zb$4E@E=$QI0G*5t)3w8Lpzo)!43vQH3lnr~U&CRUz|*s$yPy~p$lLU7|2?8b{0dPY xG>ERw<<0y28k literal 672 zcmV;R0$=@%4Fm}T0_%(Zwo`_NuK&{M0fc3b`Gu6#?+=rYlG&2v>SbN1s>zvyX}l#I z8j1Vm4I$k9@A8oRrn0q!CqDY?S1kilKw9M(bN+vFy@RA?O(K290W&ez+sgu$mdk=muyM$D6neiHbIBQkqmGzi(akogx z#+3L0oIAG3zErL(tN8;f(XbBq4Fw3zJRk>+BhhHuPFo8a|weImAK9D5a>qRDK!X`y_2xCziolhO?|G zP_C?h%S2(VBU7>ci6Q=!bZkjqQQ?CcVN)2cfWzeyv4!}PRO|>jkKhLu)>M&1cO1}> z9R+cbFf-W#wE!Gk-nheNj;Zb`h@jOLmT3@rTwdXI7~>!{){q+aofdyoKhAYJh7{)& zVa^db&^D=$E#%?8!Gg2a8m<_TM5ALeV>Y#JT4KCxRUCF{5F;%fy=fX>JKd6bwIB GtRThF2}+Rw diff --git a/scripts/gha-encrypted/gma/google-services.json.gpg b/scripts/gha-encrypted/gma/google-services.json.gpg index b118a4cb346e8149418cf8fa86ab5a616d648a3e..c46f596425a5955ec5b958766883ee90eb2ecf91 100644 GIT binary patch literal 953 zcmV;q14jIe4Fm}T0?5jj8X$yf(Erlu0o=SNVA9@O)Q#w6U2MVR(@sDoEKw4ruNO3O zPmJZ;D5%EpUdQb|p#VZcUSnlVF@HZ;Wo}Mq^r98p=hBMmONvOFn$0sn^L@iCqmP0F=UD9qsJblrIkqLol4tEpjp=eNY6-X z3b1{x7dV}KDG(ANS!?UAG=_uE%pR_G)fWWN=y3INqb`x*r;Qh`aW8c@%WR>J#-RSH z_yZ@%%_g%i!9NC5P8?4F*Q|}lID-2sQh`i3YbzJbE9Cv3R^QvR9b;>>avs%oXYmSZ z(s}B$Z%QQ#9*42A>|_%5lapchw(xLZ$O#HE7)Vtk;xn~`iH2pd{n2prmB}cn2+yHM z_KWm!Y7DPfg{`p<;raH;-q*N2tRG0zIdUPVn>knZifwyjUFtzxxP+pI2SIo$vq(7K zH(MHzZk5pMUByZtm?FxuJ5p@_pu|0(kvV5~7acIy|Ek-FrC2v9Xp<3(e<7|m<^>?% zg0PrAonln~Hd;|_%R*5#vo4#5d_|1?ZRSQzeZKDpUbZCyk|Y0PAtCl1xHz#^FFSZ^ z!1yU~KTei^?nVUonbM^qeEz;g^!3Dg#mNS;O`_ULM_J63hHe;7BL&mO>=K zwc<{Q4XsD%oc#^bfC`Biz~;ot-)Ku86zZ3) z-lCqPSZH;ue>aJDQ`eQ3FPi6QjYI@Rf&YQRqoCJu<<)m$PbpSakN-)z+b>_w;MdEA zM-w?Vvsh7EOK4T_iJEF2mPPu~{F~3me5AD}r)>`h)%;#B9;Mn=PX0M3s`>C?WSNK9vak2` zenu+TM-u+s2hgi?MXENJ{8#AIW@jq7BUic?=b7+v3gOs9KYZp literal 1002 zcmVFe&;$LD*U2V6jbw7A0UT^9NN8WQ= zL!01j1k_|^rv{=>;&kSH`%sP)Jy8+TO4R42x+jJ^{Q!FNY^pj@%BNQl3hSE>k?G?o zFuv2|$LYzUe{;GREk-smY>;EG@<+uV{)giF;8tP_h+-w#(wV8RWYhq3$M^RkS68PT zu$KB_Hrp&Y^TP3m+spKMuk_zU#q6_0KMP$R$o zwqE-{!H)SNDumqbBP1ngESQYS3Rgw9gE)*eLl2b~0l%50U*(83nS{7qXGe>Crz4w$ z@=i(ek~StY#h7hX?gL0)y{8=ZxXPflsJMJ&29Y-z7WG4&vI-R6l5n3E{~NaTEcdb! zs|*VCeRf#XKF_L@9B6As!50I`ahtj*yb8U7Ma3xl+gp_@7(NvTv)=~4YIVNnT@)&1 z)<5pNDkgT+yM69s*%Qg5SbcaSA!VWUuO2ZtFV75jjc~lKPM;)uRw|*%0L}5R5J@?%!9@* z{~04Fr5G@3^4mufbyGeaB>+-i@(KBkP*-S;oRSgJ9AijFhJ!$66D)<N6-G#<{<}U;MLm~VQ7gG2cXwSZ$Yc@%Ho6;OE*_DK+q4Xg{xmEBx`>Ug3!?V8 z_&qr0#OkxP4^l!&F*-MFswC*V#eDlIZAa2IpO>Awj&iYgt+K4j+BHKD@JFri)DtJj zcx-VH-Q|!NYo0ebe%T{?@QE>z*y4xFB@yCYQG`B=$8jixSNHWY(ng}IQF?K9K0ngd zW;_sIr8*^J&W|b8@Y`bjXTVS}+TPxGwOZXBMq;L|9qah*s>f(e>@k(+Cl;R;mpkM0 z{%YydGK@kl`8padeC6Ma`@0SG>Yp#+g-s6wp8J#2Y_sLBV;_Q@p4@bi?D{CW20|W$p^>asU7T diff --git a/scripts/gha-encrypted/messaging/GoogleService-Info.plist.gpg b/scripts/gha-encrypted/messaging/GoogleService-Info.plist.gpg index f8581e8822441373c038c20ddd7ce20b58e81c76..cf95c652d8c3f4a8e1ced8e26ee693973b5d74f7 100644 GIT binary patch literal 681 zcmV;a0#^Nu4Fm}T0;FEs)peJ;@&D540h3?tHNL7~)BKD4v@4NWlr=pQilhSDjI(iF z1(VC?G|7B+^l)N*wBnzv=ff#48S7FhKjTGX6qJj#qbWpzs3X`M7jYiGa6YUW#qrs7 z$~{vT7&@}ERtlW)R~RBTQr_}>Fd3Za#vMrx_nM(ue3%zya0#Nue%6;3%2j6GNQE=V zHD4ThD>{GO+iJZ)1$%~CC4+{UT-Mx|Jsuzv^KhjdzCR?}3d+NbQiNe@Irgq6nh`VgjVY8XywJei9c?o_9aaV^(DPu% zS;tSQUS;lZFjZN)pq`-w1A?RUi`_b;FMN%^1&pYg!sZLxZ+9dv-YSA#OYh6vijEk z3E3-Ov+T4V!tI=hXAuO}LUZJcmlX=$oz=4$cL4|HL%Z?7wrgzfah%9k0N z4xJ)O^H!mk?{Mr1y~rnoFSt4b(e-G+rI@BwjhS0%K2IIQnWluOuA!08&@sQV14q?--w70#e3P*g0Fgn{F@t1QMLrUwdX9@RK%s*F<-PoM{#L!|q-MU$lj)v-t?i6NW z16$JzO$57WEn}f0tl2b|7fM!6G1w?4JtfM14uluB{JM`Gclo3CSgJ%`@(i3 z$3R3FD^_52ESBC6U58IY;8aALAu8M}MHdg94Ltq6KRosurqpqw!#B zaI6MxLL+j}M*;wg>+smq8b&YZCAk2Ks4Bx^EL4#A%@Px2l`AoQ;d!qy~V znsI<3LX_-OxBjoQs1j;NcOzFMEUvI172wL$4svH`;_T zC(C|(rC#WaZwHQubwrUw$Mn@ZXeXq<``kE=oAMgUIm9ij@&L;y?FePfLYm%I6u~ZcE9k$avopDHeC+k!Gf0{^@$L&F)@1*Gj2{gIZ{6E@N{={|fS^v`M0X;|)8~MWIq;;LywHFxnc6g{6OU$<8J3d(Z zDG_BIhEH?I&mMS4d8JPoN4MfS%^@c6db%X{O*6G54C(5)nG) z+T~qulRbQr^8EB({5m0xt3*KI+o{bm=+>spRyA1dL=`2Azpf71+M0sz_1zZ&;+2vB zS?s)I^+8WnSZ2OYB-q9;CrtNil=`o{{75)38dwMN%4Gvn2dNPf8b$C*X=2dU2GmHa zm&5MzqXebRAdUf$xLRgbT;@5WzA2JsW`2rX&v(KcD3{yo(77UYy4l9NUqaFX;TJ|9 za8H%NO!Yfl#PmbnufEnInb^7KpY`R)PT{$q0)kzrD#uztDx&@1j1Nef@6M{h5^FEu zL9@_G)ZdKY%Jhhc0~}_kK(`+}GR>1AH((#+?~ap@wt{NA`Q&CzoE?%KNJ=Y-@vbK% z)swGWEC{;@zbbO8Sqn5-Pq=UjaTB?ozA;r}n0pcKF2CU$+AqU0=0B*L46ptlCaBMg vPD~IzIRHz)j-tlSqT&%x5p2?6e(xKAWJfnzBs(C?QMLBN33UZmQW_K?5Pl{+ literal 911 zcmV;A191F|4Fm}T0`YxB4m(c}*8kG!0m7YmV&(C!pAo=|H-F$^7kIYfXLG&g?c~Z3 zEdgp3hDm6lBb71<;)#?`AOLY%9AS|v4v$YS;%Q!*MF@L*MN{$=c55>HY1-LER12MGAZ;_&6AouH}hQRXz(goDsP8S{33 z1BD}81eO)Daae5AmPRUy+6a0`hqO^c&Sif^&C9^-=m1UMfnZ0;=7=ME=SKzHE^&6- zwG~-V*d5_5Wv{PZ6zEvmkOtXsO-=hY1_qcFKt3SPs5>}XKUj@Jf^*d+)BYF8C@6UL+3Dg;j5V$fss|w2>}y@Ca3KIW#&Fi&N)c2UP+pSLc7f- zr;{M(n#C3Jzr1q#qn;a>_|P_6IKVT^y-5W;E9m&|w7Zf4BP*-(biYdQD=B?joPef& z@ItJN^I1Y*{!cMDlPBKSMRpwcn_|z>YaPa0E~Vedr4`cICx98(?Scayu{`(~G7=r=)+GI}i!`gNZiPViR7G0kb}UNA|MOx$QZ zeOnvxg-j(m$vUm(8pvR!YhzEvR2mvacg=XFb(U4sLs^Qe3M$KVR|a8LP+H#}!9SBi zQ0r8NC$8JPw3RIqnApv&r;QuhGP65x9R2dP;~V#G(b9ZZ>Y^8DTOYpadqW}@kOoh@ zA3x%P+4WlIAAqHI6z%#5odl#>Oh>h&cRMu#cDraU)7=hIUP2xG*oW{GQB2U`!=;&odZ=Bl62`TF)_{9k;n(cXE l?=3pxlZ=9HYOa1P=pe1Y+YnnzXD&){J}f3+zbi0-yi6L{yQ}~J diff --git a/scripts/gha-encrypted/remote_config/GoogleService-Info.plist.gpg b/scripts/gha-encrypted/remote_config/GoogleService-Info.plist.gpg index 67535e90eb6aab32f1b9456bff223a7de20d5302..ea9dc946108db17d9abceca676221ca110659982 100644 GIT binary patch literal 614 zcmV-s0-61c4Fm}T0+!@LUVw)oga6X$0fm6UEhysKH8@d}V0ZtUPzYhNql4l=?%x}4 zZmy^lPM-hGQ^`3_SU5V}t?<$7wO#lRYbS#NJbKiC?$Om(>ySGh;2*mM`*0v6w{yfT zFVpiXcM&7eXSbsspzE%VSPYa^+6V~B9h8~|;s~X6i z3D;Fg9=y~7=|%nk81WNAj5>uFWF9~Wq#hWDw5T5(Xu*KJgjIPWU-xWxoaDH@AmeM9hU8mCRDr z$uy6Ya$`mTX;DyDaU1165bob?{>|t5z-v{b|K;zTNj+_GN+mRX1j|G`n`{68)cO0K za`4~>iOmCsTIK99CDNgX)`g*27>jnR8;V3(J@}4d=^OATG zyfY;(0ff)9RO16P==~yQ8CO#~gAgmt;Ej$_RgwxX@1S?5AwG^`7EJv0^?qCo=bSedPEu6uPY=pv zX8-ga0A`RM10x$iH4Fm}T0=^8^*~I=}N&nL60rw$}bMwNO=&7@TbiLg|!F%^?VFL9!Ij|Yv zlId#GD!R4GB(k9ZL087&vLAiP!4VzDRV18~b7ccc`es=y>ptcZeA0wNr*I@@j(?($ zB>qY{E*litsZJOk=saq-vTjjR;P^+1qA#zhAh(1QX56xThw>DKzn5#6q2;)!BQ7iN z8K#x*B>oTkvtes%mUp(L$N+R?%XH@?!r4$rlPdkXqiS+Xdb}vrs03LQp_3CRG^Ub9 z%4R4_v?sde90iAAVVKOSrRyJkACGRuasEoO7PzF?z~3-IGNdMtIXm)H+cg!}6$7vq zu#5J(6b}`D8v}*!`xI)_pf^5<6j1pGWz9yzw#J(P(om_g%`KhtS@{2!Dp7QI+DEIc6`TuCEL zFfcSd={;8v-+^wJ@0T$|Na|7`9j?gRa%!Gi1ey+mb-Yn$cLpHGUU9Q;+Zf}}iO;or z`Sudx@%P>LU8RJnbQ^V+hCFjRar?_G=d`9;CXM)4e-~-)F!U*k&K`73 z#i+^z{_resHsWoY1T6g^T+9x%Z*CnQC-KO3mw(tnk4#^wF;=Za5Vda#IYQ;4 z3IKSrMkKQe5VL%v#oJG7%TmMXfK3+rk^wqFZA(+@k2H2?;4!-u%LgaWE3ENhRc&y6 z+>QmjPCAF7A@s}0jw>%!aAU%ShZEOmQX$i;ESCa{w*~wTi5HpD{`+cA__c7Q>cX}R z6+93?dp#!gelrsBu_NOw=KI^DojPY4HU;yuIEcuf>h zWW{*LZQ6KL3?YM$XO*EC&9_}HOli>A=8fA|`?I{ITbn(r1l!n+K)O-{3IM8yYqHiD z`D3B(KX^u+uNaoBwokDam0Q*2lkgEB--!ov44iN7EytQ!yvlc_+JW6$pdfQ&TX~QY zF+#PhS@RDmKfJ#LW}$H92{NYja>N$oty;18BCSCxh~UuAij(!h?J7|dW)xXGW`&Kq a@DVqWc|5L*rEG4scQP@*HM&^F@@MEq9-0gQ literal 1013 zcmV7zLZ0{H_Y@4tTNp)K%Vi>#K#saZ`(5 zfG52v4akYo4e=E|q>atMcp^y+v09yf&{g8<8DSKx)>B3b4hlGH(dAUJ!?ze9TH*rO zq;J52BtOup$i$JVvw?c?)MST6igt!>A1bdU_F?aH+Rc>r_hezs4V+4=%et0kig@49 z)bjciZ(5@3#}*VA40yO;4$8+}2Z|8dA-P7+FIvz4xJfWco~B zn7;Q{wI2%&|5Vd1g7Q%_lQsYt0Q{uX55z1<7ptSDh*bsP7;5U+bCWN1kL>~Mb0nw2 zN+6oV+s zwCMVhU6tp5opfCTVZaEiR~AD~Z5SEAY?-@1(tgc3A}U@wduJxb`Ay3eg`ltGyu$nU zy`RHPqMm-Q&z$UvM(mW4!6M+=koY*J9)J6q{-!FddT+^ap{WaM$3*!mFi{jiY|#yg z^EUAJi|XE8wLx3-t4BoKBq6um;|aRgXUhs|nH}$nXorEcBF?W`HWtZ%;<7bmcsaT!$ek#vqx0` z|EM@zba;246$iSz3WlTtQ89EFozI+CQ4i7NQg00PZsAezumz~8oT$YHyVS#M+B;Zy zB$JL+ktITHorf|}GLKI&6 zuiZ*29RM;*19IiNE`eBJE@XyHv3y^6408JGL&J)kitFVGj$E-giszDeASqUmTMzFQ zh>&cDFiBj03LP!u$9G@^0#OW!ymA;nd$UNr8dqP5a=uC{VmXC8C7IIpg}nO$Ju=c{ zB~sfm^v)mya=8)D!to|y>^0zyUb-R}8JdBH>?kKWgpSQ<1ODF<2d|-_|9TEO1 diff --git a/scripts/gha-encrypted/storage/GoogleService-Info.plist.gpg b/scripts/gha-encrypted/storage/GoogleService-Info.plist.gpg index de4c9c060ef900c64209cf477719da2b2b6e3847..01d28ec9c68dc2093039e5ef88d1b62e35c50863 100644 GIT binary patch literal 641 zcmV-{0)G9B4Fm}T0@BxCNCa2Mb^p@o0qtuiw?`tmd2RrUYRplF_cg~tu&`l%UZu36 zW0MRzaLxexhJhCfA|3TEiVMjoR0g|Ev<&_Vby6z0Esd`#5J`H{XGDoH?Th*BD?~wl z>&nNarqtokGHhG~MyqIYPd5@{=u^NAX%v;qpGq6@sI=4-~{0Nv73!Qd^ztPq)LTOE)Q+8 z+8rQw@R_hYkjkHRD{j;!jUVyU5~W!JF6xXofC?4?-5m&9adv!qB1 zG)WQ1&8goO)Ix|y2z3Q^8R|P&VCARg$3^&pOcXY{G+Ev6^+Wr7 zYn_il4A8BAPO3TpS-v?AGqn0=9&sg1fwHv3g@n=fL2ASJ$#uXTbB?Ck9Iw%~-Gbf= z{CuV)zbfK>m2pbZ5c=7UZ+uaBN8WID4!>K_X7Dv@ovP-*%J7VrJDQ|%v8D!iJ(H6y zowMHM(~G5nZYfLQ;hY4qMDEYch^&#t|5(0TMrK3;<|wn3kWMIuB9bE#?BN78xN|YY btXortgjczDJQD$D!1QIdm0!5@WZL-o7G^nu literal 682 zcmV;b0#*Ht4Fm}T0v!pkCXcJ+#sAXj0Z@8>?Hh4R@FkB5D3Uypi7`t7BSeCWHt?KO zg8Fy%ocVvr@D@~SMZLWa(L=^0g*WAmz3v$JWvi(defSRf7zRvQ+x;eBLu`JUOPSMw zn*TK-xVS+nChk}-XFq`yMa6?I@{k$$kq zza1!Pd~g&%NCJ-ze#J97&Eq2prlN)5-PT_yq0WO6HJW<-_3zlltL*I^x;?p^Fbv!K zG~Qno?;uL*Yc{`c{-)$ay}~6c4ty}{$kP3-i!2NkwD`~IW(XYILwijf5x9X8GO&$= zQ%&nV%m3c!?1>{`v7FyZ<#AW=DbxwgIUm1IAg|NdsTsih#F|x_jy0_C{TFM93r&mb zeg}TnkolFtEPvZvdPx5#D$qRGSU_5+S=yx$zsDwJ5`yW0lxk?mv(;$FsxsY(vTYR5 z7VPo5y&B%{tN_(W6=m{%xM$y-iE`=aCM5&*3PFm&+lR6K1io;UFD3Sy&V00Xu;M;M z650@GYf*prNbc3Ek1OcQm>(%l?rreoTwM_X0ICasoJUx;K6mf7qh@=Z|ACz`6(RC) z_p>Q-9LF*fV-?81bEkBMLFUX0@szZw)0a8};;D`;Q2|U!>X$$3wDJ?kUKDvumBDNX ze`2dhU6N3Mp9JX6339M;RO4Y?A#Waw6#GN%c5s@e+%O31FNCY8z$6_Va4>D@oBB^r Q@RD4!kXrp;=I5_ED>va;vj6}9 diff --git a/scripts/gha-encrypted/storage/google-services.json.gpg b/scripts/gha-encrypted/storage/google-services.json.gpg index 1393dce3671f22a200dcc706133d0b4f95f23b5a..57ba134aa519fd3e9fd4c6fe3e35bdfa7ab9f86c 100644 GIT binary patch literal 779 zcmV+m1N8ii4Fm}T0v}cjW*B7vvj5WQ0ddkb%Cwn*48_buW^@!uuT2?GV5}3TWRcaf z;2`T3gm||t~4(epM>5nKUnE1@cp zPB2p_6Sp_yGdE||m$yrkQC^U@B;`XBw4Cet#xE?0YMb#6G8d@)6V8x_sIk*A!hSxG zV{PUZD7#3x#tBKMIPUcS1L+?mTpz}NpgT?TVik0fopDg~XHo}Han$mf&N+!=2RR0gg4x(OqNwmUSzJr!dDiKul zx4T?gU)+wP%ZyMcWyP-F1mj{B_r+&$(3!qGN9*s%2Fpx>PdS+CD z9)wXN&TGX3ETIOI33%(0z&K5s*DR9}URZ%=kPuF(MhucM9GUcTZHp$xW)tyCW2|*q zo(#ZVKbkUg8s6MV zTFNY@&bsgi{{4(Hu5z^t51z8LB)Nkhez1gAY|&{QQ7p4;6-}>|E=j}bzt+9+2KA;_ z(?K^!D$*b~S80>8bJe}pa8wUq#4~28*g$H=Jv-D7>>I;SSk_fs;B zOiGw|(LyUVcd2k^$kRI{cifK?&2Cmvoph zVeX}Y2=EdIRM6+7s%-E;SPq~+L|oB#|GH6KI>Z+}JN#ayQovGuJg>igmM8$SD_rAj zy}5T+JM%kx4Ob`Cio}}uNR@u{>WS1rleQk_>GZuRp>2cXkr2#-h+O;E@QYwR5}81< zvfhaN$GE-*ATt67HE4i5L+8PImT7=M9~yX)Dyi}!t7(fumWFTDhJfvMdn;R{77Z3= z%sAFx`Qt zqW2YZ2jy7db)!C&hI3#aJ>%>>*-X~$YL9^fDFDYH^5?WQw#ON=5E=RW$>#XXL})YX z&SrDEOL}R;m0>YEC$iaM@EHt`O@3F|Y$Cp}t$jKtGu&^6Tm?ON%$~5)y;-MpXS~eX^3ptoq^@303CA))iMYnA2!@j`?`p=u#P8R0b)9@a9svQy`21NY6oNM+aUs)mO z{!$JyC$Ze9+$EJ From d099e4d8556b6b51564e604baba813221a252b7e Mon Sep 17 00:00:00 2001 From: a-maurice Date: Tue, 5 Nov 2024 15:51:26 -0800 Subject: [PATCH 32/33] Specify using v1 Cloud Functions --- functions/testapp/functions/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/functions/testapp/functions/index.js b/functions/testapp/functions/index.js index ebafe634..a8d1905d 100644 --- a/functions/testapp/functions/index.js +++ b/functions/testapp/functions/index.js @@ -15,9 +15,9 @@ */ 'use strict'; -const functions = require('firebase-functions'); +const functions = require('firebase-functions/v1'); const admin = require('firebase-admin'); -admin.initializeApp(functions.config().firebase); +admin.initializeApp(); // Adds two numbers to each other. exports.addNumbers = functions.https.onCall((data) => { From cf330de003168a0bb9892b93e6ca990f03433170 Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Wed, 18 Dec 2024 17:04:19 +0000 Subject: [PATCH 33/33] firestore/CMakeLists.txt: add dbghelp and bcrypt libraries to ADDITIONAL_LIBS on windows --- firestore/testapp/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firestore/testapp/CMakeLists.txt b/firestore/testapp/CMakeLists.txt index a4dd2727..1a2c953f 100644 --- a/firestore/testapp/CMakeLists.txt +++ b/firestore/testapp/CMakeLists.txt @@ -106,7 +106,7 @@ else() "-framework SystemConfiguration" ) elseif(MSVC) - set(ADDITIONAL_LIBS advapi32 ws2_32 crypt32 iphlpapi psapi userenv) + set(ADDITIONAL_LIBS advapi32 ws2_32 crypt32 iphlpapi psapi userenv dbghelp bcrypt) else() set(ADDITIONAL_LIBS pthread) endif()