From fe6a02bb7114326a8d5fb78a45b35e6e57258182 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Kowalczyk?= Date: Tue, 25 Dec 2018 21:37:13 +0100 Subject: [PATCH 01/65] Fix links in `auth` readme --- auth/testapp/readme.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/auth/testapp/readme.md b/auth/testapp/readme.md index 860a8549..282b932c 100644 --- a/auth/testapp/readme.md +++ b/auth/testapp/readme.md @@ -58,7 +58,7 @@ Building and Running the testapp enable "Anonymous". This will allow the testapp to use email accounts and anonymous sign-in. - Download the Firebase C++ SDK linked from - [https://firebase.google.com/docs/cpp/setup]() and unzip it to a + 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 @@ -119,7 +119,7 @@ Building and Running the testapp - 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]() and unzip it to a + 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. @@ -154,7 +154,7 @@ Building and Running the testapp 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]() and unzip it to a + 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 (in highest to lowest priority): @@ -185,7 +185,7 @@ Building and Running the testapp Support ------- -[https://firebase.google.com/support/]() + License ------- From 875f204f845585a2d2e367342a191261d9e69186 Mon Sep 17 00:00:00 2001 From: Stewart Miles Date: Fri, 16 Aug 2019 13:04:26 -0700 Subject: [PATCH 02/65] Integrate Latest @ 263653506 CL: 263653506 --- admob/testapp/AndroidManifest.xml | 2 +- admob/testapp/Podfile | 3 +- admob/testapp/build.gradle | 4 +- analytics/testapp/AndroidManifest.xml | 2 +- analytics/testapp/Podfile | 2 +- analytics/testapp/build.gradle | 4 +- analytics/testapp/testapp/Info.plist | 2 +- auth/testapp/AndroidManifest.xml | 2 +- auth/testapp/Podfile | 3 +- auth/testapp/build.gradle | 4 +- auth/testapp/src/common_main.cc | 102 ++- database/testapp/AndroidManifest.xml | 2 +- database/testapp/Podfile | 5 +- database/testapp/build.gradle | 4 +- database/testapp/testapp/Info.plist | 2 +- dynamic_links/testapp/AndroidManifest.xml | 2 +- dynamic_links/testapp/Podfile | 3 +- dynamic_links/testapp/build.gradle | 4 +- dynamic_links/testapp/readme.md | 13 +- dynamic_links/testapp/src/common_main.cc | 22 +- functions/testapp/AndroidManifest.xml | 2 +- functions/testapp/Podfile | 5 +- functions/testapp/build.gradle | 4 +- functions/testapp/testapp/Info.plist | 2 +- messaging/testapp/AndroidManifest.xml | 2 +- messaging/testapp/Podfile | 3 +- messaging/testapp/build.gradle | 4 +- messaging/testapp/testapp/Info.plist | 2 +- remote_config/testapp/AndroidManifest.xml | 2 +- remote_config/testapp/Podfile | 3 +- remote_config/testapp/build.gradle | 4 +- remote_config/testapp/testapp/Info.plist | 2 +- storage/testapp/AndroidManifest.xml | 2 +- storage/testapp/AndroidManifestAutomated.xml | 22 + storage/testapp/Podfile | 5 +- storage/testapp/build.gradle | 4 +- storage/testapp/src/android/android_main.cc | 158 ++++- .../google/firebase/example/LoggingUtils.java | 70 ++- storage/testapp/src/common_main.cc | 580 ++---------------- storage/testapp/src/desktop/desktop_main.cc | 72 ++- storage/testapp/src/ios/ios_main.mm | 90 ++- storage/testapp/src/main.h | 20 +- storage/testapp/testapp/Info.plist | 2 +- 43 files changed, 570 insertions(+), 677 deletions(-) create mode 100644 storage/testapp/AndroidManifestAutomated.xml diff --git a/admob/testapp/AndroidManifest.xml b/admob/testapp/AndroidManifest.xml index fa871f18..e62e6385 100644 --- a/admob/testapp/AndroidManifest.xml +++ b/admob/testapp/AndroidManifest.xml @@ -6,7 +6,7 @@ - + - + google CFBundleURLSchemes - com.googleusercontent.apps.255980362477-3a1nf8c4nl0c7hlnlnmc98hbtg2mnbue + YOUR_REVERSED_CLIENT_ID diff --git a/auth/testapp/AndroidManifest.xml b/auth/testapp/AndroidManifest.xml index dfcc8f97..f2c06eba 100644 --- a/auth/testapp/AndroidManifest.xml +++ b/auth/testapp/AndroidManifest.xml @@ -6,7 +6,7 @@ - + game_center_credential_future = GameCenterAuthProvider::GetCredential(); - WaitForFuture( - game_center_credential_future, - "GameCenterAuthProvider::GetCredential()", - kAuthErrorNone); + WaitForFuture(game_center_credential_future, + "GameCenterAuthProvider::GetCredential()", + kAuthErrorNone); const AuthError credential_error = static_cast(game_center_credential_future.error()); @@ -1256,6 +1256,98 @@ extern "C" int common_main(int argc, const char* argv[]) { LogMessage("Anonymous uid(%s)", auth->current_user()->uid().c_str()); } +#ifdef INTERNAL_EXPERIMENTAL +#if defined TARGET_OS_IPHONE + // --- FederatedAuthProvider tests ------------------------------------------ + { + { // --- LinkWithProvider --- + LogMessage("LinkWithProvider"); + UserLogin user_login(auth); // Generate a random name/password + user_login.Register(); + if (!user_login.user()) { + LogMessage("ERROR: Could not register new user."); + } else { + LogMessage("Setting up provider data"); + firebase::auth::FederatedOAuthProviderData provider_data; + provider_data.provider_id = + firebase::auth::GoogleAuthProvider::GetProviderId(); + provider_data.provider_id = "google.com"; + provider_data.scopes = { + "https://www.googleapis.com/auth/fitness.activity.read"}; + provider_data.custom_parameters = {{"req_id", "1234"}}; + + LogMessage("Configuration oAuthProvider"); + firebase::auth::FederatedOAuthProvider provider; + provider.SetProviderData(provider_data); + LogMessage("invoking linkwithprovider"); + 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()); + LogMessage("Additonal user info provider_id: %s", + result_ptr->info.provider_id.c_str()); + LogMessage("LinkWithProviderDone"); + } + } + } + + { + LogMessage("SignInWithProvider"); + // --- SignInWithProvider --- + firebase::auth::FederatedOAuthProviderData provider_data; + provider_data.provider_id = + firebase::auth::GoogleAuthProvider::GetProviderId(); + provider_data.custom_parameters = {{"req_id", "1234"}}; + + firebase::auth::FederatedOAuthProvider provider; + provider.SetProviderData(provider_data); + LogMessage("SignInWithProvider SETUP COMPLETE"); + 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()); + } + } + + { // --- ReauthenticateWithProvider --- + LogMessage("ReauthetnicateWithProvider"); + if (!auth->current_user()) { + LogMessage("ERROR: Expected User from SignInWithProvider"); + } else { + firebase::auth::FederatedOAuthProviderData provider_data; + provider_data.provider_id = + firebase::auth::GoogleAuthProvider::GetProviderId(); + provider_data.custom_parameters = {{"req_id", "1234"}}; + + firebase::auth::FederatedOAuthProvider provider; + provider.SetProviderData(provider_data); + 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()); + } + } + } + + // 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", + kAuthErrorNone, + /*log_error=*/true); + } + } // end FederatedAuthProvider +#endif // TARGET_OS_IPHONE +#endif // INTERNAL_EXPERIMENTAL + LogMessage("Completed Auth tests."); while (!ProcessEvents(1000)) { diff --git a/database/testapp/AndroidManifest.xml b/database/testapp/AndroidManifest.xml index 8788b75a..a087cd78 100644 --- a/database/testapp/AndroidManifest.xml +++ b/database/testapp/AndroidManifest.xml @@ -6,7 +6,7 @@ - + google CFBundleURLSchemes - com.googleusercontent.apps.255980362477-3a1nf8c4nl0c7hlnlnmc98hbtg2mnbue + YOUR_REVERSED_CLIENT_ID diff --git a/dynamic_links/testapp/AndroidManifest.xml b/dynamic_links/testapp/AndroidManifest.xml index 2377e537..62212dc1 100644 --- a/dynamic_links/testapp/AndroidManifest.xml +++ b/dynamic_links/testapp/AndroidManifest.xml @@ -6,7 +6,7 @@ - + link_future = diff --git a/functions/testapp/AndroidManifest.xml b/functions/testapp/AndroidManifest.xml index 36f6bfeb..98dc4cef 100644 --- a/functions/testapp/AndroidManifest.xml +++ b/functions/testapp/AndroidManifest.xml @@ -6,7 +6,7 @@ - + google CFBundleURLSchemes - com.googleusercontent.apps.255980362477-3a1nf8c4nl0c7hlnlnmc98hbtg2mnbue + YOUR_REVERSED_CLIENT_ID diff --git a/messaging/testapp/AndroidManifest.xml b/messaging/testapp/AndroidManifest.xml index 70ddecfe..ae648799 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"> - + diff --git a/messaging/testapp/Podfile b/messaging/testapp/Podfile index b2093dc1..6a3a53c8 100644 --- a/messaging/testapp/Podfile +++ b/messaging/testapp/Podfile @@ -2,6 +2,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # FCM test application. target 'testapp' do - pod 'Firebase/Analytics', '6.1.0' - pod 'Firebase/Messaging', '6.1.0' + pod 'Firebase/Messaging', '6.6.0' end diff --git a/messaging/testapp/build.gradle b/messaging/testapp/build.gradle index 213665dd..2f8eaf46 100644 --- a/messaging/testapp/build.gradle +++ b/messaging/testapp/build.gradle @@ -22,7 +22,7 @@ allprojects { apply plugin: 'com.android.application' android { - compileSdkVersion 26 + compileSdkVersion 28 buildToolsVersion '28.0.3' sourceSets { @@ -37,7 +37,7 @@ android { defaultConfig { applicationId 'com.google.android.messaging.testapp' minSdkVersion 16 - targetSdkVersion 26 + targetSdkVersion 28 versionCode 1 versionName '1.0' externalNativeBuild.cmake { diff --git a/messaging/testapp/testapp/Info.plist b/messaging/testapp/testapp/Info.plist index e898946a..2faa0b0d 100644 --- a/messaging/testapp/testapp/Info.plist +++ b/messaging/testapp/testapp/Info.plist @@ -29,7 +29,7 @@ google CFBundleURLSchemes - com.googleusercontent.apps.255980362477-3a1nf8c4nl0c7hlnlnmc98hbtg2mnbue + YOUR_REVERSED_CLIENT_ID diff --git a/remote_config/testapp/AndroidManifest.xml b/remote_config/testapp/AndroidManifest.xml index 6758b44b..e449f7ee 100644 --- a/remote_config/testapp/AndroidManifest.xml +++ b/remote_config/testapp/AndroidManifest.xml @@ -6,7 +6,7 @@ - + google CFBundleURLSchemes - com.googleusercontent.apps.255980362477-3a1nf8c4nl0c7hlnlnmc98hbtg2mnbue + YOUR_REVERSED_CLIENT_ID diff --git a/storage/testapp/AndroidManifest.xml b/storage/testapp/AndroidManifest.xml index d2510a48..d5428bbe 100644 --- a/storage/testapp/AndroidManifest.xml +++ b/storage/testapp/AndroidManifest.xml @@ -6,7 +6,7 @@ - + + + + + + + + + + + + + + + + diff --git a/storage/testapp/Podfile b/storage/testapp/Podfile index 308e2b99..b44d315a 100644 --- a/storage/testapp/Podfile +++ b/storage/testapp/Podfile @@ -2,7 +2,6 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # Cloud Storage for Firebase test application. target 'testapp' do - pod 'Firebase/Analytics', '6.1.0' - pod 'Firebase/Storage', '6.1.0' - pod 'Firebase/Auth', '6.1.0' + pod 'Firebase/Storage', '6.6.0' + pod 'Firebase/Auth', '6.6.0' end diff --git a/storage/testapp/build.gradle b/storage/testapp/build.gradle index b287fe5e..264b2a34 100644 --- a/storage/testapp/build.gradle +++ b/storage/testapp/build.gradle @@ -22,7 +22,7 @@ allprojects { apply plugin: 'com.android.application' android { - compileSdkVersion 26 + compileSdkVersion 28 buildToolsVersion '28.0.3' sourceSets { @@ -37,7 +37,7 @@ android { defaultConfig { applicationId 'com.google.firebase.cpp.storage.testapp' minSdkVersion 16 - targetSdkVersion 26 + targetSdkVersion 28 versionCode 1 versionName '1.0' externalNativeBuild.cmake { diff --git a/storage/testapp/src/android/android_main.cc b/storage/testapp/src/android/android_main.cc index 4b86ce30..d8e4d4dc 100644 --- a/storage/testapp/src/android/android_main.cc +++ b/storage/testapp/src/android/android_main.cc @@ -12,13 +12,17 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include -#include - #include #include #include +#include + #include +#include +#include +#include +#include +#include #include "main.h" // NOLINT @@ -37,6 +41,8 @@ static void OnAppCmd(struct android_app* app, int32_t cmd) { g_destroy_requested |= cmd == APP_CMD_DESTROY; } +namespace app_framework { + // Process events pending on the main thread. // Returns true when the app receives an event requesting exit. bool ProcessEvents(int msec) { @@ -103,7 +109,8 @@ class LoggingUtilsData { LoggingUtilsData() : logging_utils_class_(nullptr), logging_utils_add_log_text_(0), - logging_utils_init_log_window_(0) {} + logging_utils_init_log_window_(0), + logging_utils_get_did_touch_(0) {} ~LoggingUtilsData() { JNIEnv* env = GetJniEnv(); @@ -131,6 +138,8 @@ class LoggingUtilsData { logging_utils_class_, "initLogWindow", "(Landroid/app/Activity;)V"); logging_utils_add_log_text_ = env->GetStaticMethodID( logging_utils_class_, "addLogText", "(Ljava/lang/String;)V"); + logging_utils_get_did_touch_ = + env->GetStaticMethodID(logging_utils_class_, "getDidTouch", "()Z"); env->CallStaticVoidMethod(logging_utils_class_, logging_utils_init_log_window_, GetActivity()); @@ -146,10 +155,19 @@ class LoggingUtilsData { env->DeleteLocalRef(text_string); } + bool DidTouch() { + if (logging_utils_class_ == 0) return false; // haven't been initted yet + JNIEnv* env = GetJniEnv(); + assert(env); + return env->CallStaticBooleanMethod(logging_utils_class_, + logging_utils_get_did_touch_); + } + private: jclass logging_utils_class_; jmethodID logging_utils_add_log_text_; jmethodID logging_utils_init_log_window_; + jmethodID logging_utils_get_did_touch_; }; LoggingUtilsData* g_logging_utils_data; @@ -170,12 +188,10 @@ void CheckJNIException() { const char* exception_text = env->GetStringUTFChars(s, nullptr); // Log the exception text. - __android_log_print(ANDROID_LOG_INFO, FIREBASE_TESTAPP_NAME, + __android_log_print(ANDROID_LOG_INFO, 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, - "-------------------"); + __android_log_print(ANDROID_LOG_INFO, TESTAPP_NAME, "%s", exception_text); + __android_log_print(ANDROID_LOG_INFO, TESTAPP_NAME, "-------------------"); // Also, assert fail. assert(false); @@ -187,23 +203,33 @@ void CheckJNIException() { } } -// 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); + LogMessageV(format, list); + va_end(list); +} + +// Log a message that can be viewed in "adb logcat". +void LogMessageV(const char* format, va_list list) { + static const int kLineBufferSize = 1024; + char buffer[kLineBufferSize + 2]; + 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); + __android_log_vprint(ANDROID_LOG_INFO, TESTAPP_NAME, format, list); + fputs(buffer, stdout); + fflush(stdout); +} + +// Log a message that can be viewed in the console. +void AddToTextView(const char* str) { + app_framework::g_logging_utils_data->AppendText(str); CheckJNIException(); - va_end(list); } // Get the JNI environment. @@ -214,6 +240,75 @@ JNIEnv* GetJniEnv() { return result == JNI_OK ? env : nullptr; } +// Remove all lines starting with these strings. +static const char* const filter_lines[] = {"referenceTable ", nullptr}; + +bool should_filter(const char* str) { + for (int i = 0; filter_lines[i] != nullptr; ++i) { + if (strncmp(str, filter_lines[i], strlen(filter_lines[i])) == 0) + return true; + } + return false; +} + +void* stdout_logger(void* filedes_ptr) { + int fd = reinterpret_cast(filedes_ptr)[0]; + static std::string buffer; + char bufchar; + while (int n = read(fd, &bufchar, 1)) { + if (bufchar == '\0') { + break; + } + buffer = buffer + bufchar; + if (bufchar == '\n') { + if (!should_filter(buffer.c_str())) { + app_framework::AddToTextView(buffer.c_str()); + } + buffer.clear(); + } + } + JavaVM* jvm; + if (app_framework::GetJniEnv()->GetJavaVM(&jvm) == 0) { + jvm->DetachCurrentThread(); + } + return nullptr; +} + +struct FunctionData { + void* (*func)(void*); + void* data; +}; + +static void* CallFunction(void* bg_void) { + FunctionData* bg = reinterpret_cast(bg_void); + void* (*func)(void*) = bg->func; + void* data = bg->data; + // Clean up the data that was passed to us. + delete bg; + // Call the background function. + void* result = func(data); + // Then clean up the Java thread. + JavaVM* jvm; + if (app_framework::GetJniEnv()->GetJavaVM(&jvm) == 0) { + jvm->DetachCurrentThread(); + } + return result; +} + +void RunOnBackgroundThread(void* (*func)(void*), void* data) { + pthread_t thread; + // Rather than running pthread_create(func, data), we must package them into a + // struct, because the c++ thread needs to clean up the JNI thread after it + // finishes. + FunctionData* bg = new FunctionData; + bg->func = func; + bg->data = data; + pthread_create(&thread, nullptr, CallFunction, bg); + pthread_detach(thread); +} + +} // namespace app_framework + // 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 @@ -238,18 +333,37 @@ extern "C" void android_main(struct android_app* state) { g_app_state->onAppCmd = OnAppCmd; // Create the logging display. - g_logging_utils_data = new LoggingUtilsData(); - g_logging_utils_data->Init(); + app_framework::g_logging_utils_data = new app_framework::LoggingUtilsData(); + app_framework::g_logging_utils_data->Init(); + + // Pipe stdout to AddToTextView so we get the gtest output. + int filedes[2]; + assert(pipe(filedes) != -1); + assert(dup2(filedes[1], STDOUT_FILENO) != -1); + pthread_t thread; + pthread_create(&thread, nullptr, app_framework::stdout_logger, + reinterpret_cast(filedes)); // Execute cross platform entry point. - static const char* argv[] = {FIREBASE_TESTAPP_NAME}; + static const char* argv[] = {TESTAPP_NAME}; int return_value = common_main(1, argv); (void)return_value; // Ignore the return value. - ProcessEvents(10); + + // Signal to stdout_logger to exit. + write(filedes[1], "\0", 1); + pthread_join(thread, nullptr); + close(filedes[0]); + close(filedes[1]); + // Pause a few seconds so you can see the results. If the user touches + // the screen during that time, don't exit until they choose to. + bool should_exit = false; + do { + should_exit = app_framework::ProcessEvents(10000); + } while (app_framework::g_logging_utils_data->DidTouch() && !should_exit); // Clean up logging display. - delete g_logging_utils_data; - g_logging_utils_data = nullptr; + delete app_framework::g_logging_utils_data; + app_framework::g_logging_utils_data = nullptr; // Finish the activity. if (!g_restarted) ANativeActivity_finish(state->activity); diff --git a/storage/testapp/src/android/java/com/google/firebase/example/LoggingUtils.java b/storage/testapp/src/android/java/com/google/firebase/example/LoggingUtils.java index 11d67c5b..a49996a6 100644 --- a/storage/testapp/src/android/java/com/google/firebase/example/LoggingUtils.java +++ b/storage/testapp/src/android/java/com/google/firebase/example/LoggingUtils.java @@ -15,8 +15,13 @@ package com.google.firebase.example; import android.app.Activity; +import android.graphics.Typeface; import android.os.Handler; import android.os.Looper; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.MotionEvent; +import android.view.View; import android.view.Window; import android.widget.LinearLayout; import android.widget.ScrollView; @@ -27,29 +32,70 @@ * text to the screen, via a non-editable TextView. */ public class LoggingUtils { - public static TextView sTextView = null; + static TextView textView = null; + static ScrollView scrollView = null; + // Tracks if the log window was touched at least once since the testapp was started. + static boolean didTouch = false; public static void initLogWindow(Activity activity) { + initLogWindow(activity, true); + } + + public static void initLogWindow(Activity activity, boolean monospace) { LinearLayout linearLayout = new LinearLayout(activity); - ScrollView scrollView = new ScrollView(activity); - TextView textView = new TextView(activity); + scrollView = new ScrollView(activity); + textView = new TextView(activity); textView.setTag("Logger"); + if (monospace) { + textView.setTypeface(Typeface.MONOSPACE); + textView.setTextSize(10); + } linearLayout.addView(scrollView); scrollView.addView(textView); Window window = activity.getWindow(); window.takeSurface(null); window.setContentView(linearLayout); - sTextView = textView; + + // Force the TextView to stay scrolled to the bottom. + textView.addTextChangedListener( + new TextWatcher() { + @Override + public void afterTextChanged(Editable e) { + if (scrollView != null && !didTouch) { + // If the user never interacted with the screen, scroll to bottom. + scrollView.fullScroll(View.FOCUS_DOWN); + } + } + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + + @Override + public void onTextChanged(CharSequence s, int start, int count, int after) {} + }); + textView.setOnTouchListener( + new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + didTouch = true; + return false; + } + }); } public static void addLogText(final String text) { - new Handler(Looper.getMainLooper()).post(new Runnable() { - @Override - public void run() { - if (sTextView != null) { - sTextView.append(text); - } - } - }); + new Handler(Looper.getMainLooper()) + .post( + new Runnable() { + @Override + public void run() { + if (textView != null) { + textView.append(text); + } + } + }); + } + + public static boolean getDidTouch() { + return didTouch; } } diff --git a/storage/testapp/src/common_main.cc b/storage/testapp/src/common_main.cc index 256cd7ab..058db079 100644 --- a/storage/testapp/src/common_main.cc +++ b/storage/testapp/src/common_main.cc @@ -13,11 +13,13 @@ // limitations under the License. #include + #include #include #include #include #include + #include "firebase/app.h" #include "firebase/auth.h" #include "firebase/future.h" @@ -28,6 +30,10 @@ // Thin OS abstraction layer. #include "main.h" // NOLINT +using app_framework::GetCurrentTimeInMicroseconds; +using app_framework::LogMessage; +using app_framework::ProcessEvents; + const char* kPutFileTestFile = "PutFileTest.txt"; const char* kGetFileTestFile = "GetFileTest.txt"; @@ -35,35 +41,6 @@ const char* kGetFileTestFile = "GetFileTest.txt"; // in a specific Cloud Storage bucket. const char* kStorageUrl = nullptr; -class StorageListener : public firebase::storage::Listener { - public: - StorageListener() - : on_paused_was_called_(false), on_progress_was_called_(false) {} - - // Tracks whether OnPaused was ever called and resumes the transfer. - void OnPaused(firebase::storage::Controller* controller) override { - LogMessage("Paused"); - on_paused_was_called_ = true; - LogMessage("INFO: Resuming."); - if (!controller->Resume()) { - LogMessage("ERROR: Resume() failed."); - } - } - - void OnProgress(firebase::storage::Controller* controller) override { - LogMessage("Transferred %lld of %lld", controller->bytes_transferred(), - controller->total_byte_count()); - on_progress_was_called_ = true; - } - - bool on_paused_was_called() const { return on_paused_was_called_; } - bool on_progress_was_called() const { return on_progress_was_called_; } - - public: - bool on_paused_was_called_; - bool on_progress_was_called_; -}; - // Wait for a Future to be completed. If the Future returns an error, it will // be logged. void WaitForCompletion(const firebase::FutureBase& future, const char* name) { @@ -78,29 +55,12 @@ void WaitForCompletion(const firebase::FutureBase& future, const char* name) { } } -void EnsureRefsEqual( - std::initializer_list refs) { - using firebase::storage::StorageReference; - for (StorageReference* ref : refs) { - for (StorageReference* compare_to : refs) { - if (ref->bucket() != compare_to->bucket()) { - LogMessage( - "ERROR: StorageReferences unequal. Buckets differ: '%s' vs '%s'", - ref->bucket().c_str(), compare_to->bucket().c_str()); - } else if (ref->full_path() != compare_to->full_path()) { - LogMessage( - "ERROR: StorageReferences unequal. full_paths differ: '%s' vs '%s'", - ref->full_path().c_str(), compare_to->full_path().c_str()); - } - } - } -} - extern "C" int common_main(int argc, const char* argv[]) { ::firebase::App* app; #if defined(__ANDROID__) - app = ::firebase::App::Create(GetJniEnv(), GetActivity()); + app = ::firebase::App::Create(app_framework::GetJniEnv(), + app_framework::GetActivity()); #else app = ::firebase::App::Create(); #endif // defined(__ANDROID__) @@ -181,21 +141,10 @@ extern "C" int common_main(int argc, const char* argv[]) { static_cast(time_in_microseconds)); // NOLINT std::string saved_url = buffer; - // Create a unique child in the storage that we can run our tests in. - firebase::storage::StorageReference ref, ref2, ref3, ref4; + // Create a unique child in the storage that we can run in. + firebase::storage::StorageReference ref; ref = storage->GetReference("test_app_data").Child(saved_url); - // Create the same reference in a few different manners and ensure they're - // equivalent. - // NOLINTNEXTLINE intentional redundant string conversion - ref2 = storage->GetReference(std::string("test_app_data")).Child(saved_url); - std::string url = storage->url() + "/test_app_data"; - ref3 = storage->GetReferenceFromUrl(url.c_str()).Child(saved_url); - ref4 = storage->GetReferenceFromUrl(url).Child(saved_url); - EnsureRefsEqual({&ref, &ref2, &ref3, &ref4}); - - firebase::storage::Metadata test_metadata; - LogMessage("Storage URL: gs://%s%s", ref.bucket().c_str(), ref.full_path().c_str()); @@ -212,526 +161,67 @@ extern "C" int common_main(int argc, const char* argv[]) { "pariatur. Excepteur sint occaecat cupidatat non proident, sunt in " "culpa qui officia deserunt mollit anim id est laborum."; { - LogMessage("TEST: Write a sample file from byte buffer."); - firebase::Future future = - ref.Child("TestFile") - .Child("File1.txt") - .PutBytes(&kSimpleTestFile[0], kSimpleTestFile.size()); - WaitForCompletion(future, "Write Bytes"); - if (future.error() != firebase::storage::kErrorNone) { - LogMessage("ERROR: Write sample file failed."); - LogMessage(" File1.txt: Error %d: %s", future.error(), - future.error_message()); - } else { - LogMessage("SUCCESS: Wrote file with PutBytes."); - auto metadata = future.result(); - if (metadata->size_bytes() == kSimpleTestFile.size()) { - LogMessage("SUCCESS: Metadata reports correct size."); - } else { - LogMessage("ERROR: Metadata reports incorrect size."); - LogMessage(" Got %i bytes, expected %i bytes.", - metadata->size_bytes(), kSimpleTestFile.size()); - } - } - } - - { - LogMessage("TEST: Write a sample file from byte buffer with metadata."); - std::string content_type = "text/plain"; - firebase::storage::Metadata metadata; - metadata.set_content_type(content_type.c_str()); - firebase::Future future = - ref.Child("TestFile") - .Child("File1-Metadata.txt") - .PutBytes(&kSimpleTestFile[0], kSimpleTestFile.size(), metadata); - WaitForCompletion(future, "Write Bytes"); - if (future.error() != firebase::storage::kErrorNone) { - LogMessage("ERROR: Write sample file failed."); - LogMessage(" File1-Metadata.txt: Error %d: %s", future.error(), - future.error_message()); - } else { - LogMessage("SUCCESS: Wrote file with PutBytes."); - auto metadata = future.result(); - if (metadata->size_bytes() == kSimpleTestFile.size()) { - LogMessage("SUCCESS: Metadata reports correct size."); - } else { - LogMessage("ERROR: Metadata reports incorrect size."); - LogMessage(" Got %i bytes, expected %i bytes.", - metadata->size_bytes(), kSimpleTestFile.size()); - } - if (std::string(metadata->content_type()) == content_type) { - LogMessage("SUCCESS: Metadata reports correct content type."); - } else { - LogMessage("ERROR: Metadata reports incorrect content type."); - LogMessage(" Got %s, expected %s.", metadata->content_type(), - content_type.c_str()); - } - } - } - - { - LogMessage( - "TEST: Write a sample file from byte buffer with custom " - "metadata."); + LogMessage("Write a sample file."); std::string custom_metadata_key = "special/key"; std::string custom_metadata_value = "secret value"; firebase::storage::Metadata metadata; + metadata.set_content_type("test/plain"); (*metadata.custom_metadata())[custom_metadata_key] = custom_metadata_value; firebase::Future future = ref.Child("TestFile") - .Child("File1-Custom-Metadata.txt") + .Child("SampleFile.txt") .PutBytes(&kSimpleTestFile[0], kSimpleTestFile.size(), metadata); - WaitForCompletion(future, "Write Bytes"); - if (future.error() != firebase::storage::kErrorNone) { - LogMessage("ERROR: Write sample file failed."); - LogMessage(" File1-Custom-Metadata.txt: Error %d: %s", future.error(), - future.error_message()); - } else { - LogMessage("SUCCESS: Wrote file with PutBytes."); - auto metadata = future.result(); - if (metadata->size_bytes() == kSimpleTestFile.size()) { - LogMessage("SUCCESS: Metadata reports correct size."); - } else { - LogMessage("ERROR: Metadata reports incorrect size."); - LogMessage(" Got %i bytes, expected %i bytes.", - metadata->size_bytes(), kSimpleTestFile.size()); - } - auto& custom_metadata = *metadata->custom_metadata(); - if (custom_metadata[custom_metadata_key] == custom_metadata_value) { - LogMessage("SUCCESS: Metadata reports correct custom metadata."); - } else { - LogMessage("ERROR: Metadata reports incorrect custom metadata."); - LogMessage(" Got %s, expected %s.", - custom_metadata[custom_metadata_key].c_str(), - custom_metadata_value.c_str()); - } - } - } - - { - LogMessage("TEST: Write a sample file from local file."); - - // Write file that we're going to upload. - std::string path = PathForResource() + kPutFileTestFile; - // Cloud Storage expects file:// in front of local paths. - std::string file_path = "file://" + path; - - FILE* file = fopen(path.c_str(), "w"); - std::fwrite(kSimpleTestFile.c_str(), 1, kSimpleTestFile.size(), file); - fclose(file); - - firebase::storage::Metadata new_metadata; - new_metadata.set_content_type("text/html"); - new_metadata.custom_metadata()->insert(std::make_pair("hello", "world")); - - firebase::Future future = - ref.Child("TestFile") - .Child("File2.txt") - .PutFile(file_path.c_str(), new_metadata); - WaitForCompletion(future, "Write File"); - if (future.error() != firebase::storage::kErrorNone) { - LogMessage("ERROR: Write file failed."); - LogMessage(" File2.txt: Error %d: %s", future.error(), - future.error_message()); - } else { - LogMessage("SUCCESS: Wrote file with PutFile."); - auto metadata = future.result(); - if (metadata->size_bytes() == kSimpleTestFile.size()) { - LogMessage("SUCCESS: Metadata reports correct size."); - } else { - LogMessage("ERROR: Metadata reports incorrect size."); - LogMessage(" Got %i bytes, expected %i bytes.", - metadata->size_bytes(), kSimpleTestFile.size()); - } - if (strcmp(metadata->content_type(), new_metadata.content_type()) == - 0) { - LogMessage( - "SUCCESS: Metadata has correct content type set at upload."); - } else { - LogMessage( - "ERROR: Metadata has incorrect content type set at upload."); - LogMessage(" Got %s, expected %s.", metadata->content_type(), - new_metadata.content_type()); - } - auto pair1 = metadata->custom_metadata()->find("hello"); - if (pair1 != metadata->custom_metadata()->end() && - pair1->second == "world") { - LogMessage( - "SUCCESS: Metadata has correct custom metadata set at upload."); - } else { - LogMessage( - "SUCCESS: Metadata has incorrect custom metadata set at upload."); - } - } - } - - // Get the values that we just set, and confirm that they match what we - // set them to. - { - LogMessage("TEST: Read a sample file with GetBytes."); - - // Read the storage file to byte buffer. - { - const size_t kBufferSize = 1024; - char buffer[kBufferSize]; - - firebase::Future future = ref.Child("TestFile") - .Child("File1.txt") - .GetBytes(buffer, kBufferSize); - WaitForCompletion(future, "Read Bytes"); - - // Check if the file contents is correct. - if (future.error() == firebase::storage::kErrorNone) { - if (*future.result() != kSimpleTestFile.size()) { - LogMessage( - "ERROR: Read file failed, read incorrect number of bytes (read " - "%z, expected %z)", - *future.result(), kSimpleTestFile.size()); - } else if (memcmp(&kSimpleTestFile[0], buffer, - kSimpleTestFile.size()) == 0) { - LogMessage("SUCCESS: Read file succeeded."); - } else { - LogMessage("ERROR: Read file failed, file contents did not match."); - } - } else { - LogMessage("ERROR: Read file failed."); - } - } - - LogMessage("TEST: Read a sample file with GetFile."); - - // Read the storage file to local file. - { - const size_t kBufferSize = 1024; - char buffer[kBufferSize]; - - // Write file that we're going to upload. - std::string path = PathForResource() + kGetFileTestFile; - // Cloud Storage expects file:// in front of local paths. - std::string file_path = "file://" + path; - - firebase::Future future = - ref.Child("TestFile").Child("File2.txt").GetFile(file_path.c_str()); - WaitForCompletion(future, "Read File"); - - FILE* file = fopen(path.c_str(), "r"); - if (file != nullptr) { - std::fread(buffer, 1, kBufferSize, file); - fclose(file); - } - - // Check if the file contents are correct. - if (file != nullptr && - future.error() == firebase::storage::kErrorNone) { - if (*future.result() != kSimpleTestFile.size()) { - LogMessage( - "ERROR: Read file failed, read incorrect number of bytes (read " - "%z, expected %z)", - *future.result(), kSimpleTestFile.size()); - } else if (memcmp(&kSimpleTestFile[0], buffer, - kSimpleTestFile.size()) == 0) { - LogMessage("SUCCESS: Read file succeeded."); - } else { - LogMessage("ERROR: Read file failed, file contents did not match."); - } - } else { - LogMessage("ERROR: Read file failed."); - } - } - - // Check if the timestamp is correct. - { - LogMessage("TEST: Check sample file metadata."); - - firebase::Future future = - ref.Child("TestFile").Child("File1.txt").GetMetadata(); - WaitForCompletion(future, "GetFileMetadata"); - const firebase::storage::Metadata* metadata = future.result(); - if (future.error() == firebase::storage::kErrorNone) { - test_metadata = *metadata; // Save a copy of the metadata for later. - - // Get the current time to compare to the Timestamp. - int64_t current_time_seconds = static_cast(time(nullptr)); - int64_t updated_time_milliseconds = metadata->updated_time(); - int64_t updated_time_seconds = updated_time_milliseconds / 1000; - int64_t time_difference_seconds = - updated_time_seconds - current_time_seconds; - // As long as our timestamp is within a day, it's correct enough for - // our purposes. - const int64_t kAllowedTimeDifferenceSeconds = 60L * 60L * 24L; - if (time_difference_seconds > kAllowedTimeDifferenceSeconds || - time_difference_seconds < -kAllowedTimeDifferenceSeconds) { - LogMessage("ERROR: Incorrect metadata."); - LogMessage(" Timestamp: Got %lld, expected something near %lld", - updated_time_seconds, current_time_seconds); - } else { - LogMessage("SUCCESS: Read file successfully."); - } - } else { - LogMessage("ERROR: Read file failed."); - } - // Check custom metadata field to make sure it is empty. - auto custom_metadata = metadata ? metadata->custom_metadata() : nullptr; - if (custom_metadata && !custom_metadata->empty()) { - LogMessage("ERROR: Metadata reports incorrect custom metadata."); - } else { - LogMessage("SUCCESS: Metadata reports correct custom metadata."); - } - - if (custom_metadata) { - // Add some values to custom metadata, update and then compare. - custom_metadata->insert(std::make_pair("Key", "Value")); - custom_metadata->insert(std::make_pair("Foo", "Bar")); - firebase::Future custom_metadata_future = - ref.Child("TestFile") - .Child("File1.txt") - .UpdateMetadata(*metadata); - WaitForCompletion(custom_metadata_future, "UpdateMetadata"); - if (future.error() != firebase::storage::kErrorNone) { - LogMessage("ERROR: UpdateMetadata failed."); - LogMessage(" File1.txt: Error %d: %s", future.error(), - future.error_message()); - } else { - LogMessage("SUCCESS: Updated Metadata."); - const firebase::storage::Metadata* new_metadata = - custom_metadata_future.result(); - auto new_custom_metadata = new_metadata->custom_metadata(); - auto pair1 = new_custom_metadata->find("Key"); - auto pair2 = new_custom_metadata->find("Foo"); - if (pair1 == new_custom_metadata->end() || - pair1->second != "Value" || - pair2 == new_custom_metadata->end() || pair2->second != "Bar") { - LogMessage( - "ERROR: New metadata reports incorrect custom metadata."); - } else { - LogMessage( - "SUCCESS: New metadata reports correct custom metadata."); - } - } - } - } - - // Check the download URL. - { - LogMessage("TEST: Check for a download URL."); - firebase::Future future = - ref.Child("TestFile").Child("File1.txt").GetDownloadUrl(); - - WaitForCompletion(future, "GetDownloadUrl"); - if (future.error() != firebase::storage::kErrorNone) { - LogMessage("ERROR: Couldn't get download URL."); - LogMessage(" File1.txt: Error %d: %s", future.error(), - future.error_message()); - } else { - LogMessage("SUCCESS: Got URL: "); - const std::string* download_url = future.result(); - LogMessage(" %s", download_url->c_str()); + WaitForCompletion(future, "Write"); + if (future.error() == 0) { + if (future.result()->size_bytes() != kSimpleTestFile.size()) { + LogMessage("ERROR: Incorrect number of bytes uploaded."); } } - - // Try removing the file. - { - // Call delete on file. - LogMessage("TEST: Removing file."); - firebase::Future delete_future = - ref.Child("TestFile").Child("File1.txt").Delete(); - WaitForCompletion(delete_future, "DeleteFile"); - if (delete_future.error() == firebase::storage::kErrorNone) { - LogMessage("SUCCESS: File was removed."); - } else { - LogMessage("ERROR: File was not removed."); - } - - // Verify it can no longer be read. - const size_t kBufferSize = 1024; - char buffer[kBufferSize]; - firebase::Future read_future = - ref.Child("TestFile") - .Child("File1.txt") - .GetBytes(buffer, kBufferSize); - while (read_future.status() == firebase::kFutureStatusPending) { - ProcessEvents(100); - } - if (read_future.error() == firebase::storage::kErrorObjectNotFound) { - LogMessage("SUCCESS: File could not be read, as expected."); - } else { - LogMessage("ERROR: File could be read after removal. Status = %d: %s", - read_future.error(), read_future.error_message()); - } - } - } - } - - { - const size_t kLargeFileSize = 2 * 1024 * 1024; - std::vector large_test_file; - large_test_file.resize(kLargeFileSize); - for (int i = 0; i < kLargeFileSize; ++i) { - // Fill the buffer with the alphabet. - large_test_file[i] = 'a' + (i % 26); } - const std::vector& kLargeTestFile = large_test_file; - bool wrote_file = false; { - LogMessage("TEST: Write a large file, pause, and resume mid-way."); - StorageListener listener; - firebase::storage::Controller controller; - firebase::Future future = - ref.Child("TestFile") - .Child("File3.txt") - .PutBytes(&kLargeTestFile[0], kLargeFileSize, &listener, - &controller); - - // Ensure the Controller is valid now that we have associated it with an - // operation. - if (!controller.is_valid()) { - LogMessage("ERROR: Controller was invalid."); - } + LogMessage("Read back the sample file."); - ProcessEvents(500); - // After waiting a moment for the operation to start, pause the operation - // and verify it was successfully paused. Note that pause might not take - // effect immediately, so we give it a few moments to pause before - // failing. - LogMessage("INFO: Pausing."); - if (!controller.Pause()) { - LogMessage("ERROR: Pause() failed."); - } + const size_t kBufferSize = 1024; + char buffer[kBufferSize]; - WaitForCompletion(future, "WriteLargeFile"); - - // Ensure the progress callback was called. - if (!listener.on_progress_was_called()) { - LogMessage("ERROR: Listener OnProgress callback was not called."); - } - - if (future.error() != firebase::storage::kErrorNone) { - LogMessage("ERROR: Write file failed."); - LogMessage(" TestFile: Error %d: %s", future.error(), - future.error_message()); - } else { - LogMessage("SUCCESS: Wrote large file."); - wrote_file = true; - auto metadata = future.result(); - if (metadata->size_bytes() == kLargeFileSize) { - LogMessage("SUCCESS: Metadata reports correct size."); - } else { - LogMessage("ERROR: Metadata reports incorrect size."); - LogMessage(" Got %i bytes, expected %i bytes.", - metadata->size_bytes(), kLargeFileSize); + firebase::Future future = ref.Child("TestFile") + .Child("SampleFile.txt") + .GetBytes(buffer, kBufferSize); + WaitForCompletion(future, "Read"); + if (future.error() == 0) { + if (*future.result() != kSimpleTestFile.size()) { + LogMessage("ERROR: Incorrect number of bytes uploaded."); } - } - } - - if (wrote_file) { - LogMessage("TEST: Reading previously-uploaded large file."); - - std::vector buffer; - buffer.resize(kLargeFileSize); - StorageListener listener; - firebase::storage::Controller controller; - firebase::Future future = - ref.Child("TestFile") - .Child("File3.txt") - .GetBytes(&buffer[0], buffer.size(), &listener, &controller); - - // Ensure the Controller is valid now that we have associated it with an - // operation. - if (!controller.is_valid()) { - LogMessage("ERROR: Controller was invalid."); - } - - WaitForCompletion(future, "ReadLargeFile"); - - // Ensure the progress callback was called. - if (!listener.on_progress_was_called()) { - LogMessage("ERROR: Listener OnProgress callback was not called."); - } - - // Check if the file contents is correct. - if (future.error() == firebase::storage::kErrorNone) { - if (*future.result() != kLargeFileSize) { - LogMessage( - "ERROR: Read file failed, read incorrect number of bytes (read " - "%d, expected %d)", - static_cast(*future.result()), - static_cast(kLargeFileSize)); - } else if (std::memcmp(&kLargeTestFile[0], &buffer[0], - kLargeFileSize) == 0) { - LogMessage("SUCCESS: Read file succeeded."); - } else { - LogMessage("ERROR: Read file failed, file contents did not match."); + if (memcmp(&kSimpleTestFile[0], buffer, kSimpleTestFile.size()) != 0) { + LogMessage("ERROR: file contents did not match."); } - } else { - LogMessage("ERROR: Read file failed."); } } { - LogMessage("TEST: Write a large file and cancel mid-way."); - firebase::storage::Controller controller; - firebase::Future future = - ref.Child("TestFile") - .Child("File4.txt") - .PutBytes(&kLargeTestFile[0], kLargeFileSize, nullptr, - &controller); - - // Ensure the Controller is valid now that we have associated it with an - // operation. - if (!controller.is_valid()) { - LogMessage("ERROR: Controller was invalid."); - } - - // Cancel the operation and verify it was successfully canceled. - controller.Cancel(); + LogMessage("Delete the sample file."); - while (future.status() == firebase::kFutureStatusPending) { - ProcessEvents(100); - } - if (future.error() != firebase::storage::kErrorCancelled) { - LogMessage("ERROR: Write cancellation failed."); - LogMessage(" TestFile: Error %d: %s", future.error(), - future.error_message()); - } else { - LogMessage("SUCCESS: Canceled file upload."); - } + firebase::Future future = + ref.Child("TestFile").Child("SampleFile.txt").Delete(); + WaitForCompletion(future, "Delete"); } } - // Check if test_metadata started valid and was then invalidated. - bool test_metadata_was_valid = test_metadata.is_valid(); - LogMessage("Shutdown the Storage library."); delete storage; storage = nullptr; - // Ensure that the ref we had is now invalid. - if (!ref.is_valid()) { - LogMessage("SUCCESS: Reference was invalidated on library shutdown."); - } else { - LogMessage("ERROR: Reference is still valid after library shutdown."); - } - - if (test_metadata_was_valid) { - if (!test_metadata.is_valid()) { - LogMessage("SUCCESS: Metadata was invalidated on library shutdown."); - } else { - LogMessage("ERROR: Metadata is still valid after library shutdown."); - } - } else { - LogMessage( - "WARNING: Metadata was already invalid at shutdown, couldn't check."); - } - LogMessage("Signing out from anonymous account."); auth->SignOut(); + LogMessage("Shutdown the Auth library."); delete auth; auth = nullptr; LogMessage("Shutdown Firebase App."); delete app; + app = nullptr; // Wait until the user wants to quit the app. while (!ProcessEvents(1000)) { diff --git a/storage/testapp/src/desktop/desktop_main.cc b/storage/testapp/src/desktop/desktop_main.cc index 0220c688..7f3a95ae 100644 --- a/storage/testapp/src/desktop/desktop_main.cc +++ b/storage/testapp/src/desktop/desktop_main.cc @@ -16,10 +16,14 @@ #include #include +#include // NOLINT + #ifdef _WIN32 #include #define chdir _chdir #else +#include +#include #include #endif // _WIN32 @@ -60,6 +64,8 @@ static BOOL WINAPI SignalHandler(DWORD event) { static void SignalHandler(int /* ignored */) { quit = true; } #endif // _WIN32 +namespace app_framework { + bool ProcessEvents(int msec) { #ifdef _WIN32 Sleep(msec); @@ -70,14 +76,46 @@ bool ProcessEvents(int msec) { } std::string PathForResource() { +#if defined(_WIN32) + // On Windows we should hvae TEST_TMPDIR or TEMP or TMP set. + char buf[MAX_PATH + 1]; + if (GetEnvironmentVariable("TEST_TMPDIR", buf, sizeof(buf)) || + GetEnvironmentVariable("TEMP", buf, sizeof(buf)) || + GetEnvironmentVariable("TMP", buf, sizeof(buf))) { + std::string path(buf); + // Add trailing slash. + if (path[path.size() - 1] != '\\') path += '\\'; + return path; + } +#else + // Linux and OS X should either have the TEST_TMPDIR environment variable set + // or use /tmp. + if (const char* value = getenv("TEST_TMPDIR")) { + std::string path(value); + // Add trailing slash. + if (path[path.size() - 1] != '/') path += '/'; + return path; + } + struct stat s; + if (stat("/tmp", &s) == 0) { + if (s.st_mode & S_IFDIR) { + return std::string("/tmp/"); + } + } +#endif // defined(_WIN32) + // If nothing else, use the current directory. return std::string(); } void LogMessage(const char* format, ...) { va_list list; va_start(list, format); - vprintf(format, list); + LogMessageV(format, list); va_end(list); +} + +void LogMessageV(const char* format, va_list list) { + vprintf(format, list); printf("\n"); fflush(stdout); } @@ -96,18 +134,6 @@ void ChangeToFileDirectory(const char* file_path) { } } -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() { @@ -123,3 +149,23 @@ int64_t WinGetCurrentTimeInMicroseconds() { return now.QuadPart * 10LL; } #endif + +void RunOnBackgroundThread(void* (*func)(void*), void* data) { + // On desktop, use std::thread as Windows doesn't support pthreads. + std::thread thread(func, data); + thread.detach(); +} + +} // namespace app_framework + +int main(int argc, const char* argv[]) { + app_framework::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); +} diff --git a/storage/testapp/src/ios/ios_main.mm b/storage/testapp/src/ios/ios_main.mm index 715d4119..61a8bc36 100755 --- a/storage/testapp/src/ios/ios_main.mm +++ b/storage/testapp/src/ios/ios_main.mm @@ -14,7 +14,15 @@ #import -#include +#include +#include + +#include +#include +#include +#include +#include +#include #include "main.h" @@ -43,7 +51,7 @@ - (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}; + const char *argv[] = {TESTAPP_NAME}; [g_shutdown_signal lock]; g_exit_status = common_main(1, argv); [g_shutdown_complete signal]; @@ -51,6 +59,7 @@ - (void)viewDidLoad { } @end +namespace app_framework { bool ProcessEvents(int msec) { [g_shutdown_signal @@ -71,27 +80,91 @@ WindowContext GetWindowContext() { return g_parent_view; } +void LogMessage(const char *format, ...) { + va_list list; + va_start(list, format); + LogMessageV(format, list); + va_end(list); +} + // Log a message that can be viewed in the console. -void LogMessage(const char* format, ...) { - va_list args; +void LogMessageV(const char *format, va_list list) { NSString *formatString = @(format); - va_start(args, format); - NSString *message = [[NSString alloc] initWithFormat:formatString arguments:args]; - va_end(args); + NSString *message = [[NSString alloc] initWithFormat:formatString arguments:list]; NSLog(@"%@", message); message = [message stringByAppendingString:@"\n"]; + fputs(message.UTF8String, stdout); + fflush(stdout); +} + +// Log a message that can be viewed in the console. +void AddToTextView(const char *str) { + NSString *message = @(str); + dispatch_async(dispatch_get_main_queue(), ^{ g_text_view.text = [g_text_view.text stringByAppendingString:message]; + NSRange range = NSMakeRange(g_text_view.text.length, 0); + [g_text_view scrollRangeToVisible:range]; }); } +// Remove all lines starting with these strings. +static const char *const filter_lines[] = {nullptr}; + +bool should_filter(const char *str) { + for (int i = 0; filter_lines[i] != nullptr; ++i) { + if (strncmp(str, filter_lines[i], strlen(filter_lines[i])) == 0) return true; + } + return false; +} + +void *stdout_logger(void *filedes_ptr) { + int fd = reinterpret_cast(filedes_ptr)[0]; + std::string buffer; + char bufchar; + while (int n = read(fd, &bufchar, 1)) { + if (bufchar == '\0') { + break; + } + buffer = buffer + bufchar; + if (bufchar == '\n') { + if (!should_filter(buffer.c_str())) { + app_framework::AddToTextView(buffer.c_str()); + } + buffer.clear(); + } + } + return nullptr; +} + +void RunOnBackgroundThread(void* (*func)(void*), void* data) { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + func(data); + }); +} + +} // namespace app_framework + int main(int argc, char* argv[]) { + // Pipe stdout to call LogToTextView so we can see the gtest output. + int filedes[2]; + assert(pipe(filedes) != -1); + assert(dup2(filedes[1], STDOUT_FILENO) != -1); + pthread_t thread; + pthread_create(&thread, nullptr, app_framework::stdout_logger, reinterpret_cast(filedes)); @autoreleasepool { UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } + // Signal to stdout_logger to exit. + write(filedes[1], "\0", 1); + pthread_join(thread, nullptr); + close(filedes[0]); + close(filedes[1]); + + NSLog(@"Application Exit"); return g_exit_status; } @@ -114,7 +187,7 @@ - (BOOL)application:(UIApplication*)application g_text_view.editable = NO; g_text_view.scrollEnabled = YES; g_text_view.userInteractionEnabled = YES; - + g_text_view.font = [UIFont fontWithName:@"Courier" size:10]; [viewController.view addSubview:g_text_view]; return YES; @@ -125,5 +198,4 @@ - (void)applicationWillTerminate:(UIApplication *)application { [g_shutdown_signal signal]; [g_shutdown_complete wait]; } - @end diff --git a/storage/testapp/src/main.h b/storage/testapp/src/main.h index d3a58852..2d30c14e 100644 --- a/storage/testapp/src/main.h +++ b/storage/testapp/src/main.h @@ -15,6 +15,8 @@ #ifndef FIREBASE_TESTAPP_MAIN_H_ // NOLINT #define FIREBASE_TESTAPP_MAIN_H_ // NOLINT +#include + #include #if !defined(_WIN32) #include @@ -28,15 +30,18 @@ extern "C" { } // extern "C" #endif // __ANDROID__ -// Defined using -DANDROID_MAIN_APP_NAME=some_app_name when compiling this +// Defined using -DTESTAPP_NAME=some_app_name when compiling this // file. -#ifndef FIREBASE_TESTAPP_NAME -#define FIREBASE_TESTAPP_NAME "android_main" -#endif // FIREBASE_TESTAPP_NAME +#ifndef TESTAPP_NAME +#define TESTAPP_NAME "android_main" +#endif // TESTAPP_NAME + +namespace app_framework { // Cross platform logging method. // Implemented by android/android_main.cc or ios/ios_main.mm. -extern "C" void LogMessage(const char* format, ...); +void LogMessage(const char* format, ...); +void LogMessageV(const char* format, va_list list); // Platform-independent method to flush pending events for the main thread. // Returns true when an event requesting program-exit is received. @@ -83,4 +88,9 @@ jobject GetActivity(); // to the root view of the view controller. WindowContext GetWindowContext(); +// Run the given function on a detached background thread. +void RunOnBackgroundThread(void* (*func)(void* data), void* data); + +} // namespace app_framework + #endif // FIREBASE_TESTAPP_MAIN_H_ // NOLINT diff --git a/storage/testapp/testapp/Info.plist b/storage/testapp/testapp/Info.plist index a407135f..31425994 100644 --- a/storage/testapp/testapp/Info.plist +++ b/storage/testapp/testapp/Info.plist @@ -25,7 +25,7 @@ google CFBundleURLSchemes - com.googleusercontent.apps.255980362477-3a1nf8c4nl0c7hlnlnmc98hbtg2mnbue + YOUR_REVERSED_CLIENT_ID From 80f5b390104d077177744b9b8a0cdf546ca37305 Mon Sep 17 00:00:00 2001 From: Anthony Date: Tue, 27 Aug 2019 13:40:04 -0700 Subject: [PATCH 03/65] Integrate Latest @ 264880670 CL: 264880670 --- admob/testapp/src/common_main.cc | 4 ++-- auth/testapp/src/common_main.cc | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/admob/testapp/src/common_main.cc b/admob/testapp/src/common_main.cc index 4dd4cb96..eae4f5c4 100644 --- a/admob/testapp/src/common_main.cc +++ b/admob/testapp/src/common_main.cc @@ -84,11 +84,11 @@ const char* kAdMobAppID = "YOUR_IOS_ADMOB_APP_ID"; #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/2888167318"; +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/6386090517"; +const char* kRewardedVideoAdUnit = "ca-app-pub-3940256099942544/1712485313"; #endif // Standard mobile banner size is 320x50. diff --git a/auth/testapp/src/common_main.cc b/auth/testapp/src/common_main.cc index 6be2d21b..8f0d2fda 100644 --- a/auth/testapp/src/common_main.cc +++ b/auth/testapp/src/common_main.cc @@ -18,7 +18,7 @@ #include "firebase/app.h" #include "firebase/auth.h" -#include "firebase/auth/client/cpp/src/include/firebase/auth/credential.h" +#include "firebase/auth/credential.h" #include "firebase/util.h" // Thin OS abstraction layer. @@ -1257,7 +1257,7 @@ extern "C" int common_main(int argc, const char* argv[]) { } #ifdef INTERNAL_EXPERIMENTAL -#if defined TARGET_OS_IPHONE +#if defined TARGET_OS_IPHONE || defined(__ANDROID__) // --- FederatedAuthProvider tests ------------------------------------------ { { // --- LinkWithProvider --- @@ -1315,7 +1315,7 @@ extern "C" int common_main(int argc, const char* argv[]) { } { // --- ReauthenticateWithProvider --- - LogMessage("ReauthetnicateWithProvider"); + LogMessage("ReauthethenticateWithProvider"); if (!auth->current_user()) { LogMessage("ERROR: Expected User from SignInWithProvider"); } else { @@ -1345,7 +1345,7 @@ extern "C" int common_main(int argc, const char* argv[]) { /*log_error=*/true); } } // end FederatedAuthProvider -#endif // TARGET_OS_IPHONE +#endif // TARGET_OS_IPHONE || defined(__ANDROID__) #endif // INTERNAL_EXPERIMENTAL LogMessage("Completed Auth tests."); From c870ef2427f4180ca69913bfc709fefd70d51b47 Mon Sep 17 00:00:00 2001 From: cynthiajiang Date: Mon, 30 Sep 2019 16:40:32 -0700 Subject: [PATCH 04/65] Integrate Latest @ 271954739 Update samples to 6.6.0 --- admob/testapp/Podfile | 2 +- analytics/testapp/Podfile | 2 +- auth/testapp/Podfile | 2 +- database/testapp/Podfile | 4 ++-- dynamic_links/testapp/Podfile | 2 +- functions/testapp/Podfile | 4 ++-- messaging/testapp/Podfile | 2 +- remote_config/testapp/Podfile | 2 +- storage/testapp/Podfile | 4 ++-- storage/testapp/src/common_main.cc | 2 +- 10 files changed, 13 insertions(+), 13 deletions(-) diff --git a/admob/testapp/Podfile b/admob/testapp/Podfile index da9841b2..91c73206 100644 --- a/admob/testapp/Podfile +++ b/admob/testapp/Podfile @@ -2,5 +2,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # AdMob test application. target 'testapp' do - pod 'Firebase/AdMob', '6.6.0' + pod 'Firebase/AdMob', '6.9.0' end diff --git a/analytics/testapp/Podfile b/analytics/testapp/Podfile index 42108da0..ce278437 100644 --- a/analytics/testapp/Podfile +++ b/analytics/testapp/Podfile @@ -2,5 +2,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # Analytics test application. target 'testapp' do - pod 'Firebase/Analytics', '6.6.0' + pod 'Firebase/Analytics', '6.9.0' end diff --git a/auth/testapp/Podfile b/auth/testapp/Podfile index a2a049cd..46215214 100644 --- a/auth/testapp/Podfile +++ b/auth/testapp/Podfile @@ -2,5 +2,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # Auth test application. target 'testapp' do - pod 'Firebase/Auth', '6.6.0' + pod 'Firebase/Auth', '6.9.0' end diff --git a/database/testapp/Podfile b/database/testapp/Podfile index cbcb11ca..7e95766b 100644 --- a/database/testapp/Podfile +++ b/database/testapp/Podfile @@ -2,6 +2,6 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # Firebase Realtime Database test application. target 'testapp' do - pod 'Firebase/Database', '6.6.0' - pod 'Firebase/Auth', '6.6.0' + pod 'Firebase/Database', '6.9.0' + pod 'Firebase/Auth', '6.9.0' end diff --git a/dynamic_links/testapp/Podfile b/dynamic_links/testapp/Podfile index 9bff8d48..755d50d1 100644 --- a/dynamic_links/testapp/Podfile +++ b/dynamic_links/testapp/Podfile @@ -2,5 +2,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # Dynamic Links test application. target 'testapp' do - pod 'Firebase/DynamicLinks', '6.6.0' + pod 'Firebase/DynamicLinks', '6.9.0' end diff --git a/functions/testapp/Podfile b/functions/testapp/Podfile index 1bf68df6..e25fdc8a 100644 --- a/functions/testapp/Podfile +++ b/functions/testapp/Podfile @@ -2,6 +2,6 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # Cloud Functions for Firebase test application. target 'testapp' do - pod 'Firebase/Functions', '6.6.0' - pod 'Firebase/Auth', '6.6.0' + pod 'Firebase/Functions', '6.9.0' + pod 'Firebase/Auth', '6.9.0' end diff --git a/messaging/testapp/Podfile b/messaging/testapp/Podfile index 6a3a53c8..22c5b360 100644 --- a/messaging/testapp/Podfile +++ b/messaging/testapp/Podfile @@ -2,5 +2,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # FCM test application. target 'testapp' do - pod 'Firebase/Messaging', '6.6.0' + pod 'Firebase/Messaging', '6.9.0' end diff --git a/remote_config/testapp/Podfile b/remote_config/testapp/Podfile index deef5854..7801e3be 100644 --- a/remote_config/testapp/Podfile +++ b/remote_config/testapp/Podfile @@ -2,5 +2,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # Firebase Remote Config test application. target 'testapp' do - pod 'Firebase/RemoteConfig', '6.6.0' + pod 'Firebase/RemoteConfig', '6.9.0' end diff --git a/storage/testapp/Podfile b/storage/testapp/Podfile index b44d315a..37bbc2d2 100644 --- a/storage/testapp/Podfile +++ b/storage/testapp/Podfile @@ -2,6 +2,6 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # Cloud Storage for Firebase test application. target 'testapp' do - pod 'Firebase/Storage', '6.6.0' - pod 'Firebase/Auth', '6.6.0' + pod 'Firebase/Storage', '6.9.0' + pod 'Firebase/Auth', '6.9.0' end diff --git a/storage/testapp/src/common_main.cc b/storage/testapp/src/common_main.cc index 058db079..9dbade62 100644 --- a/storage/testapp/src/common_main.cc +++ b/storage/testapp/src/common_main.cc @@ -162,7 +162,7 @@ extern "C" int common_main(int argc, const char* argv[]) { "culpa qui officia deserunt mollit anim id est laborum."; { LogMessage("Write a sample file."); - std::string custom_metadata_key = "special/key"; + std::string custom_metadata_key = "specialkey"; std::string custom_metadata_value = "secret value"; firebase::storage::Metadata metadata; metadata.set_content_type("test/plain"); From ab84adb302a3bac820654de4b72b12c364195249 Mon Sep 17 00:00:00 2001 From: chkuang-g <31869252+chkuang-g@users.noreply.github.com> Date: Wed, 23 Oct 2019 11:54:38 -0700 Subject: [PATCH 05/65] Add issue template --- .../firebase-unity-sdk-issue.md | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/firebase-unity-sdk-issue.md diff --git a/.github/ISSUE_TEMPLATE/firebase-unity-sdk-issue.md b/.github/ISSUE_TEMPLATE/firebase-unity-sdk-issue.md new file mode 100644 index 00000000..44897e00 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/firebase-unity-sdk-issue.md @@ -0,0 +1,23 @@ +--- +name: Firebase C++ SDK issue +about: Please use this template to report issues with the Firebase C++ SDK. +title: '' +labels: new +assignees: '' + +--- + +### Please fill in the following fields: +Firebase C++ SDK version: +Firebase plugins in use (Auth, Database, etc.): +Additional SDKs you are using (Facebook, AdMob, etc.): +Platform you are using the SDK on (Mac, Windows, or Linux): +Platform you are targeting (iOS, Android, and/or desktop): + +### Please describe the issue here: +(Please list the full steps to reproduce the issue. Include device logs, and stack traces if available.) + +### Please answer the following, if applicable: +Have you been able to reproduce this issue with just the Firebase C++ quickstarts (this GitHub project)? + +What's the issue repro rate? (eg 100%, 1/5 etc) From 8a19eeeaf8593da81a042b65b084c6c2ce07c1c0 Mon Sep 17 00:00:00 2001 From: chkuang-g <31869252+chkuang-g@users.noreply.github.com> Date: Wed, 23 Oct 2019 14:53:17 -0700 Subject: [PATCH 06/65] Rename issue template to firebase-cpp-sdk-issue.md --- .../{firebase-unity-sdk-issue.md => firebase-cpp-sdk-issue.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/ISSUE_TEMPLATE/{firebase-unity-sdk-issue.md => firebase-cpp-sdk-issue.md} (100%) diff --git a/.github/ISSUE_TEMPLATE/firebase-unity-sdk-issue.md b/.github/ISSUE_TEMPLATE/firebase-cpp-sdk-issue.md similarity index 100% rename from .github/ISSUE_TEMPLATE/firebase-unity-sdk-issue.md rename to .github/ISSUE_TEMPLATE/firebase-cpp-sdk-issue.md From ff6d39d2ed44ba930bc621c449d925d49ea87052 Mon Sep 17 00:00:00 2001 From: Stewart Miles Date: Thu, 19 Dec 2019 13:16:02 -0800 Subject: [PATCH 07/65] Integrate Latest @ 286448036 CL: 286448036 --- admob/testapp/Podfile | 2 +- analytics/testapp/Podfile | 2 +- auth/testapp/Podfile | 2 +- auth/testapp/src/common_main.cc | 45 +++++++++++++++++++++++++-------- database/testapp/Podfile | 4 +-- dynamic_links/testapp/Podfile | 2 +- functions/testapp/Podfile | 4 +-- messaging/testapp/Podfile | 2 +- remote_config/testapp/Podfile | 2 +- storage/testapp/Podfile | 4 +-- 10 files changed, 47 insertions(+), 22 deletions(-) diff --git a/admob/testapp/Podfile b/admob/testapp/Podfile index 91c73206..11523879 100644 --- a/admob/testapp/Podfile +++ b/admob/testapp/Podfile @@ -2,5 +2,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # AdMob test application. target 'testapp' do - pod 'Firebase/AdMob', '6.9.0' + pod 'Firebase/AdMob', '6.14.0' end diff --git a/analytics/testapp/Podfile b/analytics/testapp/Podfile index ce278437..382cf08a 100644 --- a/analytics/testapp/Podfile +++ b/analytics/testapp/Podfile @@ -2,5 +2,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # Analytics test application. target 'testapp' do - pod 'Firebase/Analytics', '6.9.0' + pod 'Firebase/Analytics', '6.14.0' end diff --git a/auth/testapp/Podfile b/auth/testapp/Podfile index 46215214..dc3526c2 100644 --- a/auth/testapp/Podfile +++ b/auth/testapp/Podfile @@ -2,5 +2,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # Auth test application. target 'testapp' do - pod 'Firebase/Auth', '6.9.0' + pod 'Firebase/Auth', '6.14.0' end diff --git a/auth/testapp/src/common_main.cc b/auth/testapp/src/common_main.cc index 8f0d2fda..8907bcc4 100644 --- a/auth/testapp/src/common_main.cc +++ b/auth/testapp/src/common_main.cc @@ -60,6 +60,7 @@ static const char kCustomEmail[] = "custom.email@example.com"; static const char kCustomPassword[] = "CustomPasswordGoesHere"; // Constants used during tests. +static const char kTestNonceBad[] = "testBadNonce"; static const char kTestPassword[] = "testEmailPassword123"; static const char kTestEmailBad[] = "bad.test.email@example.com"; static const char kTestPasswordBad[] = "badTestPassword"; @@ -882,7 +883,7 @@ extern "C" int common_main(int argc, const char* argv[]) { WaitForSignInFuture( facebook_bad, "Auth::SignInWithCredential() bad Facebook credentials", - kAuthErrorInvalidProviderId, auth); + kAuthErrorInvalidCredential, auth); } // Use bad GitHub credentials. Should fail. @@ -893,7 +894,7 @@ extern "C" int common_main(int argc, const char* argv[]) { auth->SignInWithCredential(git_hub_cred_bad); WaitForSignInFuture( git_hub_bad, "Auth::SignInWithCredential() bad GitHub credentials", - kAuthErrorInvalidProviderId, auth); + kAuthErrorInvalidCredential, auth); } // Use bad Google credentials. Should fail. @@ -969,7 +970,6 @@ extern "C" int common_main(int argc, const char* argv[]) { } } #endif // TARGET_OS_IPHONE - // Use bad Twitter credentials. Should fail. { Credential twitter_cred_bad = TwitterAuthProvider::GetCredential( @@ -978,7 +978,21 @@ extern "C" int common_main(int argc, const char* argv[]) { auth->SignInWithCredential(twitter_cred_bad); WaitForSignInFuture( twitter_bad, "Auth::SignInWithCredential() bad Twitter credentials", - kAuthErrorInvalidProviderId, auth); + kAuthErrorInvalidCredential, auth); + } + + // Construct OAuthCredential with nonce & access token. + { + Credential nonce_credential_good = + OAuthProvider::GetCredential(kTestIdProviderIdBad, kTestIdTokenBad, + kTestNonceBad, kTestAccessTokenBad); + } + + // Construct OAuthCredential with nonce, null access token. + { + Credential nonce_credential_good = OAuthProvider::GetCredential( + kTestIdProviderIdBad, kTestIdTokenBad, kTestNonceBad, + /*access_token=*/nullptr); } // Use bad OAuth credentials. Should fail. @@ -988,7 +1002,18 @@ extern "C" int common_main(int argc, const char* argv[]) { Future oauth_bad = auth->SignInWithCredential(oauth_cred_bad); WaitForSignInFuture( oauth_bad, "Auth::SignInWithCredential() bad OAuth credentials", - kAuthErrorInvalidProviderId, auth); + kAuthErrorFailure, auth); + } + + // Use bad OAuth credentials with nonce. Should fail. + { + Credential oauth_cred_bad = + OAuthProvider::GetCredential(kTestIdProviderIdBad, kTestIdTokenBad, + kTestNonceBad, kTestAccessTokenBad); + Future oauth_bad = auth->SignInWithCredential(oauth_cred_bad); + WaitForSignInFuture( + oauth_bad, "Auth::SignInWithCredential() bad OAuth credentials", + kAuthErrorFailure, auth); } // Test Auth::SendPasswordResetEmail(). @@ -1077,7 +1102,7 @@ extern "C" int common_main(int argc, const char* argv[]) { anonymous_user->LinkWithCredential(twitter_cred_bad); WaitForFuture(link_bad_future, "User::LinkWithCredential() with bad credential", - kAuthErrorInvalidProviderId); + kAuthErrorInvalidCredential); ExpectTrue("Linking maintains user", auth->current_user() == pre_link_user); } @@ -1094,7 +1119,7 @@ extern "C" int common_main(int argc, const char* argv[]) { auth->SignInWithCredential(twitter_cred_bad); WaitForFuture(signin_bad_future, "Auth::SignInWithCredential() with bad credential", - kAuthErrorInvalidProviderId, auth); + kAuthErrorInvalidCredential, auth); ExpectTrue("Failed sign in maintains user", auth->current_user() == pre_signin_user); } @@ -1270,7 +1295,7 @@ extern "C" int common_main(int argc, const char* argv[]) { LogMessage("Setting up provider data"); firebase::auth::FederatedOAuthProviderData provider_data; provider_data.provider_id = - firebase::auth::GoogleAuthProvider::GetProviderId(); + firebase::auth::GoogleAuthProvider::kProviderId; provider_data.provider_id = "google.com"; provider_data.scopes = { "https://www.googleapis.com/auth/fitness.activity.read"}; @@ -1299,7 +1324,7 @@ extern "C" int common_main(int argc, const char* argv[]) { // --- SignInWithProvider --- firebase::auth::FederatedOAuthProviderData provider_data; provider_data.provider_id = - firebase::auth::GoogleAuthProvider::GetProviderId(); + firebase::auth::GoogleAuthProvider::kProviderId; provider_data.custom_parameters = {{"req_id", "1234"}}; firebase::auth::FederatedOAuthProvider provider; @@ -1321,7 +1346,7 @@ extern "C" int common_main(int argc, const char* argv[]) { } else { firebase::auth::FederatedOAuthProviderData provider_data; provider_data.provider_id = - firebase::auth::GoogleAuthProvider::GetProviderId(); + firebase::auth::GoogleAuthProvider::kProviderId; provider_data.custom_parameters = {{"req_id", "1234"}}; firebase::auth::FederatedOAuthProvider provider; diff --git a/database/testapp/Podfile b/database/testapp/Podfile index 7e95766b..155d5f68 100644 --- a/database/testapp/Podfile +++ b/database/testapp/Podfile @@ -2,6 +2,6 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # Firebase Realtime Database test application. target 'testapp' do - pod 'Firebase/Database', '6.9.0' - pod 'Firebase/Auth', '6.9.0' + pod 'Firebase/Database', '6.14.0' + pod 'Firebase/Auth', '6.14.0' end diff --git a/dynamic_links/testapp/Podfile b/dynamic_links/testapp/Podfile index 755d50d1..21b5e4bd 100644 --- a/dynamic_links/testapp/Podfile +++ b/dynamic_links/testapp/Podfile @@ -2,5 +2,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # Dynamic Links test application. target 'testapp' do - pod 'Firebase/DynamicLinks', '6.9.0' + pod 'Firebase/DynamicLinks', '6.14.0' end diff --git a/functions/testapp/Podfile b/functions/testapp/Podfile index e25fdc8a..3c741d7b 100644 --- a/functions/testapp/Podfile +++ b/functions/testapp/Podfile @@ -2,6 +2,6 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # Cloud Functions for Firebase test application. target 'testapp' do - pod 'Firebase/Functions', '6.9.0' - pod 'Firebase/Auth', '6.9.0' + pod 'Firebase/Functions', '6.14.0' + pod 'Firebase/Auth', '6.14.0' end diff --git a/messaging/testapp/Podfile b/messaging/testapp/Podfile index 22c5b360..c4832c8f 100644 --- a/messaging/testapp/Podfile +++ b/messaging/testapp/Podfile @@ -2,5 +2,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # FCM test application. target 'testapp' do - pod 'Firebase/Messaging', '6.9.0' + pod 'Firebase/Messaging', '6.14.0' end diff --git a/remote_config/testapp/Podfile b/remote_config/testapp/Podfile index 7801e3be..ed55870e 100644 --- a/remote_config/testapp/Podfile +++ b/remote_config/testapp/Podfile @@ -2,5 +2,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # Firebase Remote Config test application. target 'testapp' do - pod 'Firebase/RemoteConfig', '6.9.0' + pod 'Firebase/RemoteConfig', '6.14.0' end diff --git a/storage/testapp/Podfile b/storage/testapp/Podfile index 37bbc2d2..b311ed99 100644 --- a/storage/testapp/Podfile +++ b/storage/testapp/Podfile @@ -2,6 +2,6 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # Cloud Storage for Firebase test application. target 'testapp' do - pod 'Firebase/Storage', '6.9.0' - pod 'Firebase/Auth', '6.9.0' + pod 'Firebase/Storage', '6.14.0' + pod 'Firebase/Auth', '6.14.0' end From 4c33b9457c1feea188500816fdc2b47c8bd0a915 Mon Sep 17 00:00:00 2001 From: Anthony Date: Tue, 25 Feb 2020 11:50:24 -0800 Subject: [PATCH 08/65] Integrate Latest @ 294839797 CL: 294839797 --- admob/testapp/Podfile | 2 +- analytics/testapp/Podfile | 2 +- auth/testapp/Podfile | 2 +- database/testapp/Podfile | 4 ++-- dynamic_links/testapp/Podfile | 2 +- functions/testapp/Podfile | 4 ++-- messaging/testapp/Podfile | 2 +- remote_config/testapp/Podfile | 2 +- storage/testapp/Podfile | 4 ++-- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/admob/testapp/Podfile b/admob/testapp/Podfile index 11523879..0ffe5ff3 100644 --- a/admob/testapp/Podfile +++ b/admob/testapp/Podfile @@ -2,5 +2,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # AdMob test application. target 'testapp' do - pod 'Firebase/AdMob', '6.14.0' + pod 'Firebase/AdMob', '6.16.0' end diff --git a/analytics/testapp/Podfile b/analytics/testapp/Podfile index 382cf08a..4a921721 100644 --- a/analytics/testapp/Podfile +++ b/analytics/testapp/Podfile @@ -2,5 +2,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # Analytics test application. target 'testapp' do - pod 'Firebase/Analytics', '6.14.0' + pod 'Firebase/Analytics', '6.16.0' end diff --git a/auth/testapp/Podfile b/auth/testapp/Podfile index dc3526c2..c7e0cbb7 100644 --- a/auth/testapp/Podfile +++ b/auth/testapp/Podfile @@ -2,5 +2,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # Auth test application. target 'testapp' do - pod 'Firebase/Auth', '6.14.0' + pod 'Firebase/Auth', '6.16.0' end diff --git a/database/testapp/Podfile b/database/testapp/Podfile index 155d5f68..6be73e5b 100644 --- a/database/testapp/Podfile +++ b/database/testapp/Podfile @@ -2,6 +2,6 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # Firebase Realtime Database test application. target 'testapp' do - pod 'Firebase/Database', '6.14.0' - pod 'Firebase/Auth', '6.14.0' + pod 'Firebase/Database', '6.16.0' + pod 'Firebase/Auth', '6.16.0' end diff --git a/dynamic_links/testapp/Podfile b/dynamic_links/testapp/Podfile index 21b5e4bd..f4155c53 100644 --- a/dynamic_links/testapp/Podfile +++ b/dynamic_links/testapp/Podfile @@ -2,5 +2,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # Dynamic Links test application. target 'testapp' do - pod 'Firebase/DynamicLinks', '6.14.0' + pod 'Firebase/DynamicLinks', '6.16.0' end diff --git a/functions/testapp/Podfile b/functions/testapp/Podfile index 3c741d7b..cc755487 100644 --- a/functions/testapp/Podfile +++ b/functions/testapp/Podfile @@ -2,6 +2,6 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # Cloud Functions for Firebase test application. target 'testapp' do - pod 'Firebase/Functions', '6.14.0' - pod 'Firebase/Auth', '6.14.0' + pod 'Firebase/Functions', '6.16.0' + pod 'Firebase/Auth', '6.16.0' end diff --git a/messaging/testapp/Podfile b/messaging/testapp/Podfile index c4832c8f..fc83e1b3 100644 --- a/messaging/testapp/Podfile +++ b/messaging/testapp/Podfile @@ -2,5 +2,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # FCM test application. target 'testapp' do - pod 'Firebase/Messaging', '6.14.0' + pod 'Firebase/Messaging', '6.16.0' end diff --git a/remote_config/testapp/Podfile b/remote_config/testapp/Podfile index ed55870e..6f965499 100644 --- a/remote_config/testapp/Podfile +++ b/remote_config/testapp/Podfile @@ -2,5 +2,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # Firebase Remote Config test application. target 'testapp' do - pod 'Firebase/RemoteConfig', '6.14.0' + pod 'Firebase/RemoteConfig', '6.16.0' end diff --git a/storage/testapp/Podfile b/storage/testapp/Podfile index b311ed99..ae654938 100644 --- a/storage/testapp/Podfile +++ b/storage/testapp/Podfile @@ -2,6 +2,6 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # Cloud Storage for Firebase test application. target 'testapp' do - pod 'Firebase/Storage', '6.14.0' - pod 'Firebase/Auth', '6.14.0' + pod 'Firebase/Storage', '6.16.0' + pod 'Firebase/Auth', '6.16.0' end From e0cef3cafaa5e8b0a50cd4125ab3a30e0aa91170 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Thu, 12 Mar 2020 11:08:51 -0700 Subject: [PATCH 09/65] Integrate Latest @ 300019442 Add Firestore testapp and update dependency versions. CL: 300019442 --- admob/testapp/Podfile | 2 +- analytics/testapp/Podfile | 2 +- auth/testapp/Podfile | 2 +- auth/testapp/readme.md | 2 +- database/testapp/Podfile | 4 +- dynamic_links/testapp/Podfile | 2 +- firestore/testapp/AndroidManifest.xml | 23 + firestore/testapp/CMakeLists.txt | 136 ++++++ firestore/testapp/LaunchScreen.storyboard | 7 + firestore/testapp/Podfile | 8 + firestore/testapp/README.md | 249 +++++++++++ firestore/testapp/build.gradle | 66 +++ .../testapp/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 49896 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + firestore/testapp/gradlew | 164 ++++++++ firestore/testapp/gradlew.bat | 90 ++++ firestore/testapp/proguard.pro | 2 + firestore/testapp/res/layout/main.xml | 12 + firestore/testapp/res/values/strings.xml | 4 + firestore/testapp/settings.gradle | 36 ++ firestore/testapp/src/android/android_main.cc | 255 +++++++++++ .../google/firebase/example/LoggingUtils.java | 55 +++ firestore/testapp/src/common_main.cc | 364 ++++++++++++++++ firestore/testapp/src/desktop/desktop_main.cc | 123 ++++++ firestore/testapp/src/ios/ios_main.mm | 118 ++++++ firestore/testapp/src/main.h | 63 +++ .../testapp/testapp.xcodeproj/project.pbxproj | 398 ++++++++++++++++++ .../AppIcon.appiconset/Contents.json | 98 +++++ .../LaunchImage.launchimage/Contents.json | 51 +++ firestore/testapp/testapp/Info.plist | 39 ++ functions/testapp/Podfile | 4 +- messaging/testapp/Podfile | 2 +- remote_config/testapp/Podfile | 2 +- storage/testapp/Podfile | 4 +- 34 files changed, 2380 insertions(+), 13 deletions(-) create mode 100644 firestore/testapp/AndroidManifest.xml create mode 100644 firestore/testapp/CMakeLists.txt create mode 100644 firestore/testapp/LaunchScreen.storyboard create mode 100644 firestore/testapp/Podfile create mode 100644 firestore/testapp/README.md create mode 100644 firestore/testapp/build.gradle create mode 100644 firestore/testapp/gradle/wrapper/gradle-wrapper.jar create mode 100644 firestore/testapp/gradle/wrapper/gradle-wrapper.properties create mode 100755 firestore/testapp/gradlew create mode 100644 firestore/testapp/gradlew.bat create mode 100644 firestore/testapp/proguard.pro create mode 100644 firestore/testapp/res/layout/main.xml create mode 100644 firestore/testapp/res/values/strings.xml create mode 100644 firestore/testapp/settings.gradle create mode 100644 firestore/testapp/src/android/android_main.cc create mode 100644 firestore/testapp/src/android/java/com/google/firebase/example/LoggingUtils.java create mode 100644 firestore/testapp/src/common_main.cc create mode 100644 firestore/testapp/src/desktop/desktop_main.cc create mode 100755 firestore/testapp/src/ios/ios_main.mm create mode 100644 firestore/testapp/src/main.h create mode 100644 firestore/testapp/testapp.xcodeproj/project.pbxproj create mode 100644 firestore/testapp/testapp/Images.xcassets/AppIcon.appiconset/Contents.json create mode 100644 firestore/testapp/testapp/Images.xcassets/LaunchImage.launchimage/Contents.json create mode 100644 firestore/testapp/testapp/Info.plist diff --git a/admob/testapp/Podfile b/admob/testapp/Podfile index 0ffe5ff3..724dcaab 100644 --- a/admob/testapp/Podfile +++ b/admob/testapp/Podfile @@ -2,5 +2,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # AdMob test application. target 'testapp' do - pod 'Firebase/AdMob', '6.16.0' + pod 'Firebase/AdMob', '6.17.0' end diff --git a/analytics/testapp/Podfile b/analytics/testapp/Podfile index 4a921721..da8d3b53 100644 --- a/analytics/testapp/Podfile +++ b/analytics/testapp/Podfile @@ -2,5 +2,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # Analytics test application. target 'testapp' do - pod 'Firebase/Analytics', '6.16.0' + pod 'Firebase/Analytics', '6.17.0' end diff --git a/auth/testapp/Podfile b/auth/testapp/Podfile index c7e0cbb7..0f2f4278 100644 --- a/auth/testapp/Podfile +++ b/auth/testapp/Podfile @@ -2,5 +2,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # Auth test application. target 'testapp' do - pod 'Firebase/Auth', '6.16.0' + pod 'Firebase/Auth', '6.17.0' end diff --git a/auth/testapp/readme.md b/auth/testapp/readme.md index 9e4220c5..04dd6fd0 100644 --- a/auth/testapp/readme.md +++ b/auth/testapp/readme.md @@ -185,7 +185,7 @@ Building and Running the testapp Support ------- -[Firebase Support](https://firebase.google.com/support/) +[https://firebase.google.com/support/](https://firebase.google.com/support/) License ------- diff --git a/database/testapp/Podfile b/database/testapp/Podfile index 6be73e5b..ca0a839e 100644 --- a/database/testapp/Podfile +++ b/database/testapp/Podfile @@ -2,6 +2,6 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # Firebase Realtime Database test application. target 'testapp' do - pod 'Firebase/Database', '6.16.0' - pod 'Firebase/Auth', '6.16.0' + pod 'Firebase/Database', '6.17.0' + pod 'Firebase/Auth', '6.17.0' end diff --git a/dynamic_links/testapp/Podfile b/dynamic_links/testapp/Podfile index f4155c53..8d4eb2c1 100644 --- a/dynamic_links/testapp/Podfile +++ b/dynamic_links/testapp/Podfile @@ -2,5 +2,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # Dynamic Links test application. target 'testapp' do - pod 'Firebase/DynamicLinks', '6.16.0' + pod 'Firebase/DynamicLinks', '6.17.0' end diff --git a/firestore/testapp/AndroidManifest.xml b/firestore/testapp/AndroidManifest.xml new file mode 100644 index 00000000..9b97c8d6 --- /dev/null +++ b/firestore/testapp/AndroidManifest.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + diff --git a/firestore/testapp/CMakeLists.txt b/firestore/testapp/CMakeLists.txt new file mode 100644 index 00000000..a4dd2727 --- /dev/null +++ b/firestore/testapp/CMakeLists.txt @@ -0,0 +1,136 @@ +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() + +# 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) + +project(firebase_testapp) + +# 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. + # This also does a workaround that prevents numerous errors that occur when + # building using Android Studio. The errors look like: + # libfirebase_auth.a(auth.o): relocation R_386_GOTOFF against preemptible + # symbol cannot be used when making a shared object. + # Taken from: + # https://github.com/opencv/opencv/issues/10229#issuecomment-359202825 + set(CMAKE_SHARED_LINKER_FLAGS + "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate \ + -Wl,--exclude-libs,libfirebase_firestore.a \ + -Wl,--exclude-libs,libfirebase_auth.a \ + -Wl,--exclude-libs,libfirebase_app.a" + ) + + # 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 + gssapi_krb5 + pthread + "-framework CoreFoundation" + "-framework Foundation" + "-framework GSS" + "-framework Security" + "-framework SystemConfiguration" + ) + elseif(MSVC) + set(ADDITIONAL_LIBS advapi32 ws2_32 crypt32 iphlpapi psapi userenv) + 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_firestore firebase_auth firebase_app) +target_link_libraries(${target_name} "${firebase_libs}" ${ADDITIONAL_LIBS}) diff --git a/firestore/testapp/LaunchScreen.storyboard b/firestore/testapp/LaunchScreen.storyboard new file mode 100644 index 00000000..673e0f7e --- /dev/null +++ b/firestore/testapp/LaunchScreen.storyboard @@ -0,0 +1,7 @@ + + + + + + + diff --git a/firestore/testapp/Podfile b/firestore/testapp/Podfile new file mode 100644 index 00000000..4ded7f10 --- /dev/null +++ b/firestore/testapp/Podfile @@ -0,0 +1,8 @@ +source 'https://github.com/CocoaPods/Specs.git' +platform :ios, '8.0' +# Firebase Firestore test application. +target 'testapp' do + pod 'Firebase/Analytics', '6.18.0' + pod 'Firebase/Firestore', '6.18.0' + pod 'Firebase/Auth', '6.18.0' +end diff --git a/firestore/testapp/README.md b/firestore/testapp/README.md new file mode 100644 index 00000000..90578cad --- /dev/null +++ b/firestore/testapp/README.md @@ -0,0 +1,249 @@ +# Firebase Firestore Quickstart + +The Firebase Firestore Test Application (testapp) demonstrates Firebase +Firestore operations with the Firebase Firestore C++ SDK. The application has no +user interface and simply logs actions it's performing to the console. + +The testapp performs the following: + +- Creates a firebase::App in a platform-specific way. The App holds + platform-specific context that's used by other Firebase APIs, and is a + central point for communication between the Firebase Firestore C++ and + Firebase Auth C++ libraries. +- Gets a pointer to firebase::Auth, and signs in anonymously. This allows the + testapp to access a Firebase Firestore instance with authentication rules + enabled. +- TODO(varconst): describe the Firestore-specific logic + +Introduction +------------ + +- [Read more about Firebase Firestore](https://firebase.google.com/docs/firestore/) + +Building and Running the testapp +-------------------------------- + +### 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.firebase.cpp.firestore.testapp" as the iOS + Bundle ID while you're testing. You can omit App Store ID while + testing. + - Add the GoogleService-Info.plist that you downloaded from Firebase + console to the testapp root directory. This file identifies your iOS app + to the Firebase backend. + - In the Firebase console for your app, select "Auth", then enable + "Anonymous". This will allow the testapp to use anonymous sign-in to + authenticate with Firebase Firestore, which requires a signed-in user by + default (an anonymous user will suffice). + +- 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_auth.framework + - frameworks/ios/universal/firebase_firestore.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" + - e.g. 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". + +- In XCode, build & run the sample on an iOS device or simulator. + +- The testapp has no interative interface. The output of the app can be viewed + via the console or on the device's display. In Xcode, select "View --> Debug + Area --> Activate Console" from the menu to view the console. + +### 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.firebase.cpp.firestore.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. + + - In the Firebase console for your app, select "Auth", then enable + "Anonymous". This will allow the testapp to use anonymous sign-in to + authenticate with Firebase Firestore, which requires a signed-in user by + default (an anonymous user will suffice). + + - 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. + +- Build the testapp and run it on an Android device or emulator. + +- The testapp has no interactive interface. The output of the app can be + viewed on the device's display, or in the logcat output of Android studio or + by running "adb logcat *:W android_main firebase" from the command line. + +### 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 (in highest to lowest priority): + - 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. + +Known issues +------------ + +TODO(varconst) + +Support +------- + +[https://firebase.google.com/support/](https://firebase.google.com/support/) + +License +------- + +Copyright 2016-2019 Google LLC 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/firestore/testapp/build.gradle b/firestore/testapp/build.gradle new file mode 100644 index 00000000..37ed5818 --- /dev/null +++ b/firestore/testapp/build.gradle @@ -0,0 +1,66 @@ +// 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.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 29 + buildToolsVersion "29.0.0" + + sourceSets { + main { + jniLibs.srcDirs = ['libs'] + manifest.srcFile 'AndroidManifest.xml' + java.srcDirs = ['src/android/java'] + res.srcDirs = ['res'] + } + } + + defaultConfig { + applicationId 'com.google.firebase.cpp.firestore.testapp' + minSdkVersion 16 + targetSdkVersion 29 + versionCode 1 + versionName '1.0' + externalNativeBuild.cmake { + arguments "-DFIREBASE_CPP_SDK_DIR=$gradle.firebase_cpp_sdk_dir" + } + multiDexEnabled true + } + 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 { + auth + firestore +} + +apply plugin: 'com.google.gms.google-services' diff --git a/firestore/testapp/gradle/wrapper/gradle-wrapper.jar b/firestore/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/firestore/testapp/gradlew.bat b/firestore/testapp/gradlew.bat new file mode 100644 index 00000000..8a0b282a --- /dev/null +++ b/firestore/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/firestore/testapp/proguard.pro b/firestore/testapp/proguard.pro new file mode 100644 index 00000000..54cd248b --- /dev/null +++ b/firestore/testapp/proguard.pro @@ -0,0 +1,2 @@ +-ignorewarnings +-keep,includedescriptorclasses public class com.google.firebase.example.LoggingUtils { *; } diff --git a/firestore/testapp/res/layout/main.xml b/firestore/testapp/res/layout/main.xml new file mode 100644 index 00000000..d3ffb630 --- /dev/null +++ b/firestore/testapp/res/layout/main.xml @@ -0,0 +1,12 @@ + + + + diff --git a/firestore/testapp/res/values/strings.xml b/firestore/testapp/res/values/strings.xml new file mode 100644 index 00000000..25c93dde --- /dev/null +++ b/firestore/testapp/res/values/strings.xml @@ -0,0 +1,4 @@ + + + Firebase Firestore Test + diff --git a/firestore/testapp/settings.gradle b/firestore/testapp/settings.gradle new file mode 100644 index 00000000..5b0e2c37 --- /dev/null +++ b/firestore/testapp/settings.gradle @@ -0,0 +1,36 @@ +// Copyright 2019 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/firestore/testapp/src/android/android_main.cc b/firestore/testapp/src/android/android_main.cc new file mode 100644 index 00000000..73cb30e7 --- /dev/null +++ b/firestore/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/firestore/testapp/src/android/java/com/google/firebase/example/LoggingUtils.java b/firestore/testapp/src/android/java/com/google/firebase/example/LoggingUtils.java new file mode 100644 index 00000000..11d67c5b --- /dev/null +++ b/firestore/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/firestore/testapp/src/common_main.cc b/firestore/testapp/src/common_main.cc new file mode 100644 index 00000000..e13abe14 --- /dev/null +++ b/firestore/testapp/src/common_main.cc @@ -0,0 +1,364 @@ +// 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 "firebase/auth.h" +#include "firebase/auth/user.h" +#include "firebase/firestore.h" +#include "firebase/util.h" + +// Thin OS abstraction layer. +#include "main.h" // NOLINT + +const int kTimeoutMs = 5000; +const int kSleepMs = 100; + +// Wait for a Future to be completed. If the Future returns an error, it will +// be logged. +void Await(const firebase::FutureBase& future, const char* name) { + int remaining_timeout = kTimeoutMs; + while (future.status() == firebase::kFutureStatusPending && + remaining_timeout > 0) { + remaining_timeout -= kSleepMs; + ProcessEvents(kSleepMs); + } + + if (future.status() != firebase::kFutureStatusComplete) { + LogMessage("ERROR: %s returned an invalid result.", name); + } else if (future.error() != 0) { + LogMessage("ERROR: %s returned error %d: %s", name, future.error(), + future.error_message()); + } +} + +class Countable { + public: + int event_count() const { return event_count_; } + + protected: + int event_count_ = 0; +}; + +template +class TestEventListener : public Countable, + public firebase::firestore::EventListener { + public: + explicit TestEventListener(std::string name) : name_(std::move(name)) {} + + void OnEvent(const T& value, + const firebase::firestore::Error error) override { + event_count_++; + if (error != firebase::firestore::Ok) { + LogMessage("ERROR: EventListener %s got %d.", name_.c_str(), error); + } + } + + // Hides the STLPort-related quirk that `AddSnapshotListener` has different + // signatures depending on whether `std::function` is available. + template + firebase::firestore::ListenerRegistration AttachTo(U* ref) { +#if !defined(STLPORT) + return ref->AddSnapshotListener( + [this](const T& result, firebase::firestore::Error error) { + OnEvent(result, error); + }); +#else + return ref->AddSnapshotListener(this); +#endif + } + + private: + std::string name_; +}; + +void Await(const Countable& listener, const char* name) { + int remaining_timeout = kTimeoutMs; + while (listener.event_count() && remaining_timeout > 0) { + remaining_timeout -= kSleepMs; + ProcessEvents(kSleepMs); + } + if (remaining_timeout <= 0) { + LogMessage("ERROR: %s listener timed out.", name); + } +} + +extern "C" int common_main(int argc, const char* argv[]) { + firebase::App* app; + +#if defined(__ANDROID__) + app = firebase::App::Create(GetJniEnv(), GetActivity()); +#else + app = firebase::App::Create(); +#endif // defined(__ANDROID__) + + LogMessage("Initialized Firebase App."); + + LogMessage("Initializing Firebase Auth..."); + firebase::InitResult result; + firebase::auth::Auth* auth = firebase::auth::Auth::GetAuth(app, &result); + if (result != firebase::kInitResultSuccess) { + LogMessage("Failed to initialize Firebase Auth, error: %d", + static_cast(result)); + return -1; + } + LogMessage("Initialized Firebase Auth."); + + LogMessage("Signing in..."); + // Auth caches the previously signed-in user, which can be annoying when + // trying to test for sign-in failures. + auth->SignOut(); + auto future_login = auth->SignInAnonymously(); + Await(future_login, "Auth sign-in"); + auto* future_login_result = future_login.result(); + if (future_login_result && *future_login_result) { + const firebase::auth::User* user = *future_login_result; + 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()); + } else { + LogMessage("ERROR: could not sign in"); + } + + // Note: Auth cannot be deleted while any of the futures issued by it are + // still valid. + future_login.Release(); + + LogMessage("Initialize Firebase Firestore."); + + // Use ModuleInitializer to initialize Database, ensuring no dependencies are + // missing. + firebase::firestore::Firestore* firestore = nullptr; + void* initialize_targets[] = {&firestore}; + + const firebase::ModuleInitializer::InitializerFn initializers[] = { + [](firebase::App* app, void* data) { + LogMessage("Attempt to initialize Firebase Firestore."); + void** targets = reinterpret_cast(data); + firebase::InitResult result; + *reinterpret_cast(targets[0]) = + firebase::firestore::Firestore::GetInstance(app, &result); + return result; + }}; + + firebase::ModuleInitializer initializer; + initializer.Initialize(app, initialize_targets, initializers, + sizeof(initializers) / sizeof(initializers[0])); + + Await(initializer.InitializeLastResult(), "Initialize"); + + if (initializer.InitializeLastResult().error() != 0) { + LogMessage("Failed to initialize Firebase libraries: %s", + initializer.InitializeLastResult().error_message()); + return -1; + } + LogMessage("Successfully initialized Firebase Firestore."); + + firestore->set_logging_enabled(true); + + if (firestore->app() != app) { + LogMessage("ERROR: failed to get App the Firestore was created with."); + } + + firebase::firestore::Settings settings = firestore->settings(); + firestore->set_settings(settings); + LogMessage("Successfully set Firestore settings."); + + LogMessage("Testing non-wrapping types."); + const firebase::Timestamp timestamp{1, 2}; + if (timestamp.seconds() != 1 || timestamp.nanoseconds() != 2) { + LogMessage("ERROR: Timestamp creation failed."); + } + const firebase::firestore::SnapshotMetadata metadata{ + /*has_pending_writes*/ false, /*is_from_cache*/ true}; + if (metadata.has_pending_writes() || !metadata.is_from_cache()) { + LogMessage("ERROR: SnapshotMetadata creation failed."); + } + const firebase::firestore::GeoPoint point{1.23, 4.56}; + if (point.latitude() != 1.23 || point.longitude() != 4.56) { + LogMessage("ERROR: GeoPoint creation failed."); + } + LogMessage("Tested non-wrapping types."); + + LogMessage("Testing collections."); + firebase::firestore::CollectionReference collection = + firestore->Collection("foo"); + if (collection.id() != "foo") { + LogMessage("ERROR: failed to get collection id."); + } + if (collection.Document("bar").path() != "foo/bar") { + LogMessage("ERROR: failed to get path of a nested document."); + } + LogMessage("Tested collections."); + + LogMessage("Testing documents."); + firebase::firestore::DocumentReference document = + firestore->Document("foo/bar"); + if (document.firestore() != firestore) { + LogMessage("ERROR: failed to get Firestore from document."); + } + + if (document.path() != "foo/bar") { + LogMessage("ERROR: failed to get path string from document."); + } + + LogMessage("Testing Set()."); + document.Set(firebase::firestore::MapFieldValue{ + {"str", firebase::firestore::FieldValue::FromString("foo")}, + {"int", firebase::firestore::FieldValue::FromInteger(123LL)}}); + Await(document.SetLastResult(), "document.Set"); + if (document.SetLastResult().status() != firebase::kFutureStatusComplete) { + LogMessage("ERROR: failed to write document."); + } + + LogMessage("Testing Update()."); + document.Update(firebase::firestore::MapFieldValue{ + {"int", firebase::firestore::FieldValue::FromInteger(321LL)}}); + Await(document.UpdateLastResult(), "document.Update"); + if (document.UpdateLastResult().status() != firebase::kFutureStatusComplete) { + LogMessage("ERROR: failed to write document."); + } + + LogMessage("Testing Get()."); + document.Get(); + Await(document.GetLastResult(), "document.Get"); + if (document.GetLastResult().status() == firebase::kFutureStatusComplete) { + const firebase::firestore::DocumentSnapshot* snapshot = + document.GetLastResult().result(); + if (snapshot == nullptr) { + LogMessage("ERROR: failed to read document."); + } else { + for (const auto& kv : snapshot->GetData()) { + if (kv.second.type() == + firebase::firestore::FieldValue::Type::kString) { + LogMessage("key is %s, value is %s", kv.first.c_str(), + kv.second.string_value().c_str()); + } else if (kv.second.type() == + firebase::firestore::FieldValue::Type::kInteger) { + LogMessage("key is %s, value is %ld", kv.first.c_str(), + kv.second.integer_value()); + } else { + // Log unexpected type for debugging. + LogMessage("key is %s, value is neither string nor integer", + kv.first.c_str()); + } + } + } + } + + LogMessage("Testing Delete()."); + document.Delete(); + Await(document.DeleteLastResult(), "document.Delete"); + if (document.DeleteLastResult().status() != firebase::kFutureStatusComplete) { + LogMessage("ERROR: failed to delete document."); + } + LogMessage("Tested document operations."); + + TestEventListener + document_event_listener{"for document"}; + firebase::firestore::ListenerRegistration registration = + document_event_listener.AttachTo(&document); + Await(document_event_listener, "document.AddSnapshotListener"); + registration.Remove(); + LogMessage("Successfully added and removed document snapshot listener."); + + LogMessage("Testing batch write."); + firebase::firestore::WriteBatch batch = firestore->batch(); + batch.Set(collection.Document("one"), + firebase::firestore::MapFieldValue{ + {"str", firebase::firestore::FieldValue::FromString("foo")}}); + batch.Set(collection.Document("two"), + firebase::firestore::MapFieldValue{ + {"int", firebase::firestore::FieldValue::FromInteger(123LL)}}); + batch.Commit(); + Await(batch.CommitLastResult(), "batch.Commit"); + if (batch.CommitLastResult().status() != firebase::kFutureStatusComplete) { + LogMessage("ERROR: failed to write batch."); + } + LogMessage("Tested batch write."); + + LogMessage("Testing transaction."); + firestore->RunTransaction( + [collection](firebase::firestore::Transaction* transaction, + std::string* error_message) -> firebase::firestore::Error { + transaction->Update( + collection.Document("one"), + firebase::firestore::MapFieldValue{ + {"int", firebase::firestore::FieldValue::FromInteger(123LL)}}); + transaction->Delete(collection.Document("two")); + transaction->Set( + collection.Document("three"), + firebase::firestore::MapFieldValue{ + {"int", firebase::firestore::FieldValue::FromInteger(321LL)}}); + return firebase::firestore::Ok; + }); + Await(firestore->RunTransactionLastResult(), "firestore.RunTransaction"); + if (firestore->RunTransactionLastResult().status() != + firebase::kFutureStatusComplete) { + LogMessage("ERROR: failed to run transaction."); + } + LogMessage("Tested transaction."); + + LogMessage("Testing query."); + firebase::firestore::Query query = + collection + .WhereGreaterThan("int", + firebase::firestore::FieldValue::FromBoolean(true)) + .Limit(3); + query.Get(); + Await(query.GetLastResult(), "query.Get"); + if (query.GetLastResult().status() == firebase::kFutureStatusComplete) { + const firebase::firestore::QuerySnapshot* snapshot = + query.GetLastResult().result(); + if (snapshot == nullptr) { + LogMessage("ERROR: failed to fetch query result."); + } else { + for (const auto& doc : snapshot->documents()) { + if (doc.id() == "one" || doc.id() == "three") { + LogMessage("doc %s is %ld", doc.id().c_str(), + doc.Get("int").integer_value()); + } else { + LogMessage("ERROR: unexpected document %s.", doc.id().c_str()); + } + } + } + } else { + LogMessage("ERROR: failed to fetch query result."); + } + LogMessage("Tested query."); + + LogMessage("Shutdown the Firestore library."); + delete firestore; + firestore = nullptr; + + LogMessage("Shutdown Auth."); + delete auth; + LogMessage("Shutdown Firebase App."); + delete app; + + // Log this as the last line to ensure all test cases above goes through. + // The test harness will check this line appears. + LogMessage("Tests PASS."); + + // Wait until the user wants to quit the app. + while (!ProcessEvents(1000)) { + } + + return 0; +} diff --git a/firestore/testapp/src/desktop/desktop_main.cc b/firestore/testapp/src/desktop/desktop_main.cc new file mode 100644 index 00000000..74f03896 --- /dev/null +++ b/firestore/testapp/src/desktop/desktop_main.cc @@ -0,0 +1,123 @@ +// 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/firestore/testapp/src/ios/ios_main.mm b/firestore/testapp/src/ios/ios_main.mm new file mode 100755 index 00000000..567f7b0e --- /dev/null +++ b/firestore/testapp/src/ios/ios_main.mm @@ -0,0 +1,118 @@ +// 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.accessibilityIdentifier = @"Logger"; + 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/firestore/testapp/src/main.h b/firestore/testapp/src/main.h new file mode 100644 index 00000000..2eda2c10 --- /dev/null +++ b/firestore/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/firestore/testapp/testapp.xcodeproj/project.pbxproj b/firestore/testapp/testapp.xcodeproj/project.pbxproj new file mode 100644 index 00000000..cabe181e --- /dev/null +++ b/firestore/testapp/testapp.xcodeproj/project.pbxproj @@ -0,0 +1,398 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 07D2E7EAE71115C7E5A3CD39 /* libPods-testapp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CFB4B133F33186AB751527C6 /* libPods-testapp.a */; }; + 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 */; }; + 52B71EBB1C8600B600398745 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 52B71EBA1C8600B600398745 /* Images.xcassets */; }; + B64AAF0C22EBB8570019A5BD /* firebase_auth.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B64AAF0922EBB8560019A5BD /* firebase_auth.framework */; }; + B64AAF0E22EBB8570019A5BD /* firebase.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B64AAF0B22EBB8560019A5BD /* firebase.framework */; }; + B64AAF1022EBBAC30019A5BD /* firebase_firestore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B64AAF0F22EBBAC20019A5BD /* firebase_firestore.framework */; }; + D66B16871CE46E8900E5638A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D66B16861CE46E8900E5638A /* LaunchScreen.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 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 = ""; }; + 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 = ""; }; + 561521B5AB75C63495CBCDE9 /* 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 = ""; }; + 7B961A65741D8D4C337CD503 /* 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 = ""; }; + B64AAF0922EBB8560019A5BD /* firebase_auth.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = firebase_auth.framework; sourceTree = ""; }; + B64AAF0A22EBB8560019A5BD /* firebase_database.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = firebase_database.framework; sourceTree = ""; }; + B64AAF0B22EBB8560019A5BD /* firebase.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = firebase.framework; sourceTree = ""; }; + B64AAF0F22EBBAC20019A5BD /* firebase_firestore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = firebase_firestore.framework; sourceTree = ""; }; + CFB4B133F33186AB751527C6 /* libPods-testapp.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-testapp.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 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 = ( + B64AAF1022EBBAC30019A5BD /* firebase_firestore.framework in Frameworks */, + B64AAF0C22EBB8570019A5BD /* firebase_auth.framework in Frameworks */, + B64AAF0E22EBB8570019A5BD /* firebase.framework in Frameworks */, + 529226D81C85F68000C89379 /* CoreGraphics.framework in Frameworks */, + 529226DA1C85F68000C89379 /* UIKit.framework in Frameworks */, + 529226D61C85F68000C89379 /* Foundation.framework in Frameworks */, + 07D2E7EAE71115C7E5A3CD39 /* libPods-testapp.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 529226C91C85F68000C89379 = { + isa = PBXGroup; + children = ( + D66B16861CE46E8900E5638A /* LaunchScreen.storyboard */, + 520BC0381C869159008CFBC3 /* GoogleService-Info.plist */, + 52B71EBA1C8600B600398745 /* Images.xcassets */, + 52FD1FF81C85FFA000BC68E3 /* Info.plist */, + 5292271D1C85FB5500C89379 /* src */, + 529226D41C85F68000C89379 /* Frameworks */, + 529226D31C85F68000C89379 /* Products */, + 5C0AA7C41D61214BE0B5CB4C /* Pods */, + ); + sourceTree = ""; + }; + 529226D31C85F68000C89379 /* Products */ = { + isa = PBXGroup; + children = ( + 529226D21C85F68000C89379 /* testapp.app */, + ); + name = Products; + sourceTree = ""; + }; + 529226D41C85F68000C89379 /* Frameworks */ = { + isa = PBXGroup; + children = ( + B64AAF0F22EBBAC20019A5BD /* firebase_firestore.framework */, + B64AAF0922EBB8560019A5BD /* firebase_auth.framework */, + B64AAF0A22EBB8560019A5BD /* firebase_database.framework */, + B64AAF0B22EBB8560019A5BD /* firebase.framework */, + 529226D51C85F68000C89379 /* Foundation.framework */, + 529226D71C85F68000C89379 /* CoreGraphics.framework */, + 529226D91C85F68000C89379 /* UIKit.framework */, + 529226EE1C85F68000C89379 /* XCTest.framework */, + CFB4B133F33186AB751527C6 /* libPods-testapp.a */, + ); + 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 = ""; + }; + 5C0AA7C41D61214BE0B5CB4C /* Pods */ = { + isa = PBXGroup; + children = ( + 7B961A65741D8D4C337CD503 /* Pods-testapp.debug.xcconfig */, + 561521B5AB75C63495CBCDE9 /* Pods-testapp.release.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 529226D11C85F68000C89379 /* testapp */ = { + isa = PBXNativeTarget; + buildConfigurationList = 529226F91C85F68000C89379 /* Build configuration list for PBXNativeTarget "testapp" */; + buildPhases = ( + C504BBC3599D8DE4D5CBFBFC /* [CP] Check Pods Manifest.lock */, + 529226CE1C85F68000C89379 /* Sources */, + 529226CF1C85F68000C89379 /* Frameworks */, + 529226D01C85F68000C89379 /* Resources */, + 8B4C0569EC39C8FCDFBCA4EB /* [CP] Copy Pods 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 = ( + English, + 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 */, + 52B71EBB1C8600B600398745 /* Images.xcassets in Resources */, + 520BC0391C869159008CFBC3 /* GoogleService-Info.plist in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 8B4C0569EC39C8FCDFBCA4EB /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-testapp/Pods-testapp-resources.sh", + "${PODS_CONFIGURATION_BUILD_DIR}/gRPC-C++/gRPCCertificates-Cpp.bundle", + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/gRPCCertificates-Cpp.bundle", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-testapp/Pods-testapp-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + C504BBC3599D8DE4D5CBFBFC /* [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; + }; +/* End PBXShellScriptBuildPhase 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; + baseConfigurationReference = 7B961A65741D8D4C337CD503 /* Pods-testapp.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); + 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; + baseConfigurationReference = 561521B5AB75C63495CBCDE9 /* Pods-testapp.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); + 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/firestore/testapp/testapp/Images.xcassets/AppIcon.appiconset/Contents.json b/firestore/testapp/testapp/Images.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..d8db8d65 --- /dev/null +++ b/firestore/testapp/testapp/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, + { + "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" : "20x20", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "2x" + }, + { + "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" + }, + { + "idiom" : "ios-marketing", + "size" : "1024x1024", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/firestore/testapp/testapp/Images.xcassets/LaunchImage.launchimage/Contents.json b/firestore/testapp/testapp/Images.xcassets/LaunchImage.launchimage/Contents.json new file mode 100644 index 00000000..6f870a46 --- /dev/null +++ b/firestore/testapp/testapp/Images.xcassets/LaunchImage.launchimage/Contents.json @@ -0,0 +1,51 @@ +{ + "images" : [ + { + "orientation" : "portrait", + "idiom" : "iphone", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "portrait", + "idiom" : "iphone", + "subtype" : "retina4", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "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/firestore/testapp/testapp/Info.plist b/firestore/testapp/testapp/Info.plist new file mode 100644 index 00000000..341ff801 --- /dev/null +++ b/firestore/testapp/testapp/Info.plist @@ -0,0 +1,39 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + com.google.firebase.cpp.firestore.testapp + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLName + google + CFBundleURLSchemes + + YOUR_REVERSED_CLIENT_ID + + + + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + + diff --git a/functions/testapp/Podfile b/functions/testapp/Podfile index cc755487..5c0c906a 100644 --- a/functions/testapp/Podfile +++ b/functions/testapp/Podfile @@ -2,6 +2,6 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # Cloud Functions for Firebase test application. target 'testapp' do - pod 'Firebase/Functions', '6.16.0' - pod 'Firebase/Auth', '6.16.0' + pod 'Firebase/Functions', '6.17.0' + pod 'Firebase/Auth', '6.17.0' end diff --git a/messaging/testapp/Podfile b/messaging/testapp/Podfile index fc83e1b3..425161a9 100644 --- a/messaging/testapp/Podfile +++ b/messaging/testapp/Podfile @@ -2,5 +2,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # FCM test application. target 'testapp' do - pod 'Firebase/Messaging', '6.16.0' + pod 'Firebase/Messaging', '6.17.0' end diff --git a/remote_config/testapp/Podfile b/remote_config/testapp/Podfile index 6f965499..14afba59 100644 --- a/remote_config/testapp/Podfile +++ b/remote_config/testapp/Podfile @@ -2,5 +2,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # Firebase Remote Config test application. target 'testapp' do - pod 'Firebase/RemoteConfig', '6.16.0' + pod 'Firebase/RemoteConfig', '6.17.0' end diff --git a/storage/testapp/Podfile b/storage/testapp/Podfile index ae654938..d7be90c8 100644 --- a/storage/testapp/Podfile +++ b/storage/testapp/Podfile @@ -2,6 +2,6 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # Cloud Storage for Firebase test application. target 'testapp' do - pod 'Firebase/Storage', '6.16.0' - pod 'Firebase/Auth', '6.16.0' + pod 'Firebase/Storage', '6.17.0' + pod 'Firebase/Auth', '6.17.0' end From af2b16ee756cb7475e2ed49e49f8813603a23e6c Mon Sep 17 00:00:00 2001 From: Mark Grimes Date: Wed, 3 Jun 2020 16:23:52 +0100 Subject: [PATCH 10/65] Update for API changes --- firestore/testapp/src/common_main.cc | 78 ++++++++++++++-------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/firestore/testapp/src/common_main.cc b/firestore/testapp/src/common_main.cc index e13abe14..b0dd3d37 100644 --- a/firestore/testapp/src/common_main.cc +++ b/firestore/testapp/src/common_main.cc @@ -65,7 +65,7 @@ class TestEventListener : public Countable, void OnEvent(const T& value, const firebase::firestore::Error error) override { event_count_++; - if (error != firebase::firestore::Ok) { + if (error != firebase::firestore::kOk) { LogMessage("ERROR: EventListener %s got %d.", name_.c_str(), error); } } @@ -170,7 +170,7 @@ extern "C" int common_main(int argc, const char* argv[]) { } LogMessage("Successfully initialized Firebase Firestore."); - firestore->set_logging_enabled(true); + firestore->set_log_level(firebase::LogLevel::kLogLevelVerbose); if (firestore->app() != app) { LogMessage("ERROR: failed to get App the Firestore was created with."); @@ -219,28 +219,28 @@ extern "C" int common_main(int argc, const char* argv[]) { } LogMessage("Testing Set()."); - document.Set(firebase::firestore::MapFieldValue{ - {"str", firebase::firestore::FieldValue::FromString("foo")}, - {"int", firebase::firestore::FieldValue::FromInteger(123LL)}}); - Await(document.SetLastResult(), "document.Set"); - if (document.SetLastResult().status() != firebase::kFutureStatusComplete) { + auto setResult=document.Set(firebase::firestore::MapFieldValue{ + {"str", firebase::firestore::FieldValue::String("foo")}, + {"int", firebase::firestore::FieldValue::Integer(123LL)}}); + Await(setResult, "document.Set"); + if (setResult.status() != firebase::kFutureStatusComplete) { LogMessage("ERROR: failed to write document."); } LogMessage("Testing Update()."); - document.Update(firebase::firestore::MapFieldValue{ - {"int", firebase::firestore::FieldValue::FromInteger(321LL)}}); - Await(document.UpdateLastResult(), "document.Update"); - if (document.UpdateLastResult().status() != firebase::kFutureStatusComplete) { + auto updateResult=document.Update(firebase::firestore::MapFieldValue{ + {"int", firebase::firestore::FieldValue::Integer(321LL)}}); + Await(updateResult, "document.Update"); + if (updateResult.status() != firebase::kFutureStatusComplete) { LogMessage("ERROR: failed to write document."); } LogMessage("Testing Get()."); - document.Get(); - Await(document.GetLastResult(), "document.Get"); - if (document.GetLastResult().status() == firebase::kFutureStatusComplete) { + auto getDocumentResult=document.Get(); + Await(getDocumentResult, "document.Get"); + if (getDocumentResult.status() == firebase::kFutureStatusComplete) { const firebase::firestore::DocumentSnapshot* snapshot = - document.GetLastResult().result(); + getDocumentResult.result(); if (snapshot == nullptr) { LogMessage("ERROR: failed to read document."); } else { @@ -263,9 +263,9 @@ extern "C" int common_main(int argc, const char* argv[]) { } LogMessage("Testing Delete()."); - document.Delete(); - Await(document.DeleteLastResult(), "document.Delete"); - if (document.DeleteLastResult().status() != firebase::kFutureStatusComplete) { + auto deleteResult=document.Delete(); + Await(deleteResult, "document.Delete"); + if (deleteResult.status() != firebase::kFutureStatusComplete) { LogMessage("ERROR: failed to delete document."); } LogMessage("Tested document operations."); @@ -282,34 +282,34 @@ extern "C" int common_main(int argc, const char* argv[]) { firebase::firestore::WriteBatch batch = firestore->batch(); batch.Set(collection.Document("one"), firebase::firestore::MapFieldValue{ - {"str", firebase::firestore::FieldValue::FromString("foo")}}); + {"str", firebase::firestore::FieldValue::String("foo")}}); batch.Set(collection.Document("two"), firebase::firestore::MapFieldValue{ - {"int", firebase::firestore::FieldValue::FromInteger(123LL)}}); - batch.Commit(); - Await(batch.CommitLastResult(), "batch.Commit"); - if (batch.CommitLastResult().status() != firebase::kFutureStatusComplete) { + {"int", firebase::firestore::FieldValue::Integer(123LL)}}); + auto commitResult=batch.Commit(); + Await(commitResult, "batch.Commit"); + if (commitResult.status() != firebase::kFutureStatusComplete) { LogMessage("ERROR: failed to write batch."); } LogMessage("Tested batch write."); LogMessage("Testing transaction."); - firestore->RunTransaction( - [collection](firebase::firestore::Transaction* transaction, - std::string* error_message) -> firebase::firestore::Error { - transaction->Update( + auto runTransactionResult=firestore->RunTransaction( + [collection](firebase::firestore::Transaction& transaction, + std::string& error_message) -> firebase::firestore::Error { + transaction.Update( collection.Document("one"), firebase::firestore::MapFieldValue{ - {"int", firebase::firestore::FieldValue::FromInteger(123LL)}}); - transaction->Delete(collection.Document("two")); - transaction->Set( + {"int", firebase::firestore::FieldValue::Integer(123LL)}}); + transaction.Delete(collection.Document("two")); + transaction.Set( collection.Document("three"), firebase::firestore::MapFieldValue{ - {"int", firebase::firestore::FieldValue::FromInteger(321LL)}}); - return firebase::firestore::Ok; + {"int", firebase::firestore::FieldValue::Integer(321LL)}}); + return firebase::firestore::kOk; }); - Await(firestore->RunTransactionLastResult(), "firestore.RunTransaction"); - if (firestore->RunTransactionLastResult().status() != + Await(runTransactionResult, "firestore.RunTransaction"); + if (runTransactionResult.status() != firebase::kFutureStatusComplete) { LogMessage("ERROR: failed to run transaction."); } @@ -319,13 +319,13 @@ extern "C" int common_main(int argc, const char* argv[]) { firebase::firestore::Query query = collection .WhereGreaterThan("int", - firebase::firestore::FieldValue::FromBoolean(true)) + firebase::firestore::FieldValue::Boolean(true)) .Limit(3); - query.Get(); - Await(query.GetLastResult(), "query.Get"); - if (query.GetLastResult().status() == firebase::kFutureStatusComplete) { + auto getQueryResult=query.Get(); + Await(getQueryResult, "query.Get"); + if (getQueryResult.status() == firebase::kFutureStatusComplete) { const firebase::firestore::QuerySnapshot* snapshot = - query.GetLastResult().result(); + getQueryResult.result(); if (snapshot == nullptr) { LogMessage("ERROR: failed to fetch query result."); } else { From 7445988c263f5e1701cc7a37ea934dfbe48f78b3 Mon Sep 17 00:00:00 2001 From: Mark Grimes Date: Wed, 3 Jun 2020 18:01:36 +0100 Subject: [PATCH 11/65] Change logging from verbose to warning Otherwise test results get lost in the crowd. --- 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 b0dd3d37..a613d007 100644 --- a/firestore/testapp/src/common_main.cc +++ b/firestore/testapp/src/common_main.cc @@ -170,7 +170,7 @@ extern "C" int common_main(int argc, const char* argv[]) { } LogMessage("Successfully initialized Firebase Firestore."); - firestore->set_log_level(firebase::LogLevel::kLogLevelVerbose); + firestore->set_log_level(firebase::LogLevel::kLogLevelWarning); if (firestore->app() != app) { LogMessage("ERROR: failed to get App the Firestore was created with."); From c33541f32938977118047e0b03123dc702d75c26 Mon Sep 17 00:00:00 2001 From: Mark Grimes Date: Wed, 3 Jun 2020 20:17:56 +0100 Subject: [PATCH 12/65] Add spaces around equals signs --- firestore/testapp/src/common_main.cc | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/firestore/testapp/src/common_main.cc b/firestore/testapp/src/common_main.cc index a613d007..516614c4 100644 --- a/firestore/testapp/src/common_main.cc +++ b/firestore/testapp/src/common_main.cc @@ -219,7 +219,7 @@ extern "C" int common_main(int argc, const char* argv[]) { } LogMessage("Testing Set()."); - auto setResult=document.Set(firebase::firestore::MapFieldValue{ + auto setResult = document.Set(firebase::firestore::MapFieldValue{ {"str", firebase::firestore::FieldValue::String("foo")}, {"int", firebase::firestore::FieldValue::Integer(123LL)}}); Await(setResult, "document.Set"); @@ -228,7 +228,7 @@ extern "C" int common_main(int argc, const char* argv[]) { } LogMessage("Testing Update()."); - auto updateResult=document.Update(firebase::firestore::MapFieldValue{ + auto updateResult = document.Update(firebase::firestore::MapFieldValue{ {"int", firebase::firestore::FieldValue::Integer(321LL)}}); Await(updateResult, "document.Update"); if (updateResult.status() != firebase::kFutureStatusComplete) { @@ -236,7 +236,7 @@ extern "C" int common_main(int argc, const char* argv[]) { } LogMessage("Testing Get()."); - auto getDocumentResult=document.Get(); + auto getDocumentResult = document.Get(); Await(getDocumentResult, "document.Get"); if (getDocumentResult.status() == firebase::kFutureStatusComplete) { const firebase::firestore::DocumentSnapshot* snapshot = @@ -263,7 +263,7 @@ extern "C" int common_main(int argc, const char* argv[]) { } LogMessage("Testing Delete()."); - auto deleteResult=document.Delete(); + auto deleteResult = document.Delete(); Await(deleteResult, "document.Delete"); if (deleteResult.status() != firebase::kFutureStatusComplete) { LogMessage("ERROR: failed to delete document."); @@ -286,7 +286,7 @@ extern "C" int common_main(int argc, const char* argv[]) { batch.Set(collection.Document("two"), firebase::firestore::MapFieldValue{ {"int", firebase::firestore::FieldValue::Integer(123LL)}}); - auto commitResult=batch.Commit(); + auto commitResult = batch.Commit(); Await(commitResult, "batch.Commit"); if (commitResult.status() != firebase::kFutureStatusComplete) { LogMessage("ERROR: failed to write batch."); @@ -294,7 +294,7 @@ extern "C" int common_main(int argc, const char* argv[]) { LogMessage("Tested batch write."); LogMessage("Testing transaction."); - auto runTransactionResult=firestore->RunTransaction( + auto runTransactionResult = firestore->RunTransaction( [collection](firebase::firestore::Transaction& transaction, std::string& error_message) -> firebase::firestore::Error { transaction.Update( @@ -321,7 +321,7 @@ extern "C" int common_main(int argc, const char* argv[]) { .WhereGreaterThan("int", firebase::firestore::FieldValue::Boolean(true)) .Limit(3); - auto getQueryResult=query.Get(); + auto getQueryResult = query.Get(); Await(getQueryResult, "query.Get"); if (getQueryResult.status() == firebase::kFutureStatusComplete) { const firebase::firestore::QuerySnapshot* snapshot = From b02c6a36d67b41a6bfa02ecbe644b2560d9ccec7 Mon Sep 17 00:00:00 2001 From: Anthony Date: Mon, 8 Jun 2020 13:59:55 -0700 Subject: [PATCH 13/65] Integrate Latest @ 314842997 CL: 314842997 --- admob/testapp/Podfile | 2 +- analytics/testapp/Podfile | 2 +- auth/testapp/Podfile | 2 +- database/testapp/CMakeLists.txt | 2 +- database/testapp/Podfile | 4 +- dynamic_links/testapp/Podfile | 2 +- firestore/testapp/Podfile | 6 +- firestore/testapp/src/common_main.cc | 108 ++++++++---------- firestore/testapp/src/desktop/desktop_main.cc | 2 +- functions/testapp/Podfile | 4 +- messaging/testapp/Podfile | 2 +- remote_config/testapp/Podfile | 2 +- storage/testapp/Podfile | 4 +- 13 files changed, 62 insertions(+), 80 deletions(-) diff --git a/admob/testapp/Podfile b/admob/testapp/Podfile index 724dcaab..269cc6be 100644 --- a/admob/testapp/Podfile +++ b/admob/testapp/Podfile @@ -2,5 +2,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # AdMob test application. target 'testapp' do - pod 'Firebase/AdMob', '6.17.0' + pod 'Firebase/AdMob', '6.24.0' end diff --git a/analytics/testapp/Podfile b/analytics/testapp/Podfile index da8d3b53..63db963c 100644 --- a/analytics/testapp/Podfile +++ b/analytics/testapp/Podfile @@ -2,5 +2,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # Analytics test application. target 'testapp' do - pod 'Firebase/Analytics', '6.17.0' + pod 'Firebase/Analytics', '6.24.0' end diff --git a/auth/testapp/Podfile b/auth/testapp/Podfile index 0f2f4278..a71424d3 100644 --- a/auth/testapp/Podfile +++ b/auth/testapp/Podfile @@ -2,5 +2,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # Auth test application. target 'testapp' do - pod 'Firebase/Auth', '6.17.0' + pod 'Firebase/Auth', '6.24.0' end diff --git a/database/testapp/CMakeLists.txt b/database/testapp/CMakeLists.txt index e2aa427b..d505b0c9 100644 --- a/database/testapp/CMakeLists.txt +++ b/database/testapp/CMakeLists.txt @@ -95,7 +95,7 @@ else() "-framework Security" ) elseif(MSVC) - set(ADDITIONAL_LIBS advapi32 ws2_32 crypt32 iphlpapi psapi userenv) + set(ADDITIONAL_LIBS advapi32 ws2_32 crypt32 iphlpapi psapi userenv shell32) else() set(ADDITIONAL_LIBS pthread) endif() diff --git a/database/testapp/Podfile b/database/testapp/Podfile index ca0a839e..3509b28c 100644 --- a/database/testapp/Podfile +++ b/database/testapp/Podfile @@ -2,6 +2,6 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # Firebase Realtime Database test application. target 'testapp' do - pod 'Firebase/Database', '6.17.0' - pod 'Firebase/Auth', '6.17.0' + pod 'Firebase/Database', '6.24.0' + pod 'Firebase/Auth', '6.24.0' end diff --git a/dynamic_links/testapp/Podfile b/dynamic_links/testapp/Podfile index 8d4eb2c1..be1c85ab 100644 --- a/dynamic_links/testapp/Podfile +++ b/dynamic_links/testapp/Podfile @@ -2,5 +2,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # Dynamic Links test application. target 'testapp' do - pod 'Firebase/DynamicLinks', '6.17.0' + pod 'Firebase/DynamicLinks', '6.24.0' end diff --git a/firestore/testapp/Podfile b/firestore/testapp/Podfile index 4ded7f10..eb725652 100644 --- a/firestore/testapp/Podfile +++ b/firestore/testapp/Podfile @@ -2,7 +2,7 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # Firebase Firestore test application. target 'testapp' do - pod 'Firebase/Analytics', '6.18.0' - pod 'Firebase/Firestore', '6.18.0' - pod 'Firebase/Auth', '6.18.0' + pod 'Firebase/Analytics', '6.24.0' + pod 'Firebase/Firestore', '6.24.0' + pod 'Firebase/Auth', '6.24.0' end diff --git a/firestore/testapp/src/common_main.cc b/firestore/testapp/src/common_main.cc index 516614c4..3fd6757e 100644 --- a/firestore/testapp/src/common_main.cc +++ b/firestore/testapp/src/common_main.cc @@ -30,9 +30,9 @@ const int kTimeoutMs = 5000; const int kSleepMs = 100; -// Wait for a Future to be completed. If the Future returns an error, it will -// be logged. -void Await(const firebase::FutureBase& future, const char* name) { +// Waits for a Future to be completed and returns whether the future has +// completed successfully. If the Future returns an error, it will be logged. +bool Await(const firebase::FutureBase& future, const char* name) { int remaining_timeout = kTimeoutMs; while (future.status() == firebase::kFutureStatusPending && remaining_timeout > 0) { @@ -42,10 +42,13 @@ void Await(const firebase::FutureBase& future, const char* name) { if (future.status() != firebase::kFutureStatusComplete) { LogMessage("ERROR: %s returned an invalid result.", name); + return false; } else if (future.error() != 0) { LogMessage("ERROR: %s returned error %d: %s", name, future.error(), future.error_message()); + return false; } + return true; } class Countable { @@ -124,11 +127,11 @@ extern "C" int common_main(int argc, const char* argv[]) { // Auth caches the previously signed-in user, which can be annoying when // trying to test for sign-in failures. auth->SignOut(); - auto future_login = auth->SignInAnonymously(); - Await(future_login, "Auth sign-in"); - auto* future_login_result = future_login.result(); - if (future_login_result && *future_login_result) { - const firebase::auth::User* user = *future_login_result; + 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; 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()); @@ -138,7 +141,7 @@ extern "C" int common_main(int argc, const char* argv[]) { // Note: Auth cannot be deleted while any of the futures issued by it are // still valid. - future_login.Release(); + login_future.Release(); LogMessage("Initialize Firebase Firestore."); @@ -170,7 +173,7 @@ extern "C" int common_main(int argc, const char* argv[]) { } LogMessage("Successfully initialized Firebase Firestore."); - firestore->set_log_level(firebase::LogLevel::kLogLevelWarning); + firestore->set_log_level(firebase::kLogLevelDebug); if (firestore->app() != app) { LogMessage("ERROR: failed to get App the Firestore was created with."); @@ -219,28 +222,20 @@ extern "C" int common_main(int argc, const char* argv[]) { } LogMessage("Testing Set()."); - auto setResult = document.Set(firebase::firestore::MapFieldValue{ - {"str", firebase::firestore::FieldValue::String("foo")}, - {"int", firebase::firestore::FieldValue::Integer(123LL)}}); - Await(setResult, "document.Set"); - if (setResult.status() != firebase::kFutureStatusComplete) { - LogMessage("ERROR: failed to write document."); - } + Await(document.Set(firebase::firestore::MapFieldValue{ + {"str", firebase::firestore::FieldValue::String("foo")}, + {"int", firebase::firestore::FieldValue::Integer(123)}}), + "document.Set"); LogMessage("Testing Update()."); - auto updateResult = document.Update(firebase::firestore::MapFieldValue{ - {"int", firebase::firestore::FieldValue::Integer(321LL)}}); - Await(updateResult, "document.Update"); - if (updateResult.status() != firebase::kFutureStatusComplete) { - LogMessage("ERROR: failed to write document."); - } + Await(document.Update(firebase::firestore::MapFieldValue{ + {"int", firebase::firestore::FieldValue::Integer(321)}}), + "document.Update"); LogMessage("Testing Get()."); - auto getDocumentResult = document.Get(); - Await(getDocumentResult, "document.Get"); - if (getDocumentResult.status() == firebase::kFutureStatusComplete) { - const firebase::firestore::DocumentSnapshot* snapshot = - getDocumentResult.result(); + auto doc_future = document.Get(); + if (Await(doc_future, "document.Get")) { + const firebase::firestore::DocumentSnapshot* snapshot = doc_future.result(); if (snapshot == nullptr) { LogMessage("ERROR: failed to read document."); } else { @@ -263,11 +258,7 @@ extern "C" int common_main(int argc, const char* argv[]) { } LogMessage("Testing Delete()."); - auto deleteResult = document.Delete(); - Await(deleteResult, "document.Delete"); - if (deleteResult.status() != firebase::kFutureStatusComplete) { - LogMessage("ERROR: failed to delete document."); - } + Await(document.Delete(), "document.Delete"); LogMessage("Tested document operations."); TestEventListener @@ -285,34 +276,27 @@ extern "C" int common_main(int argc, const char* argv[]) { {"str", firebase::firestore::FieldValue::String("foo")}}); batch.Set(collection.Document("two"), firebase::firestore::MapFieldValue{ - {"int", firebase::firestore::FieldValue::Integer(123LL)}}); - auto commitResult = batch.Commit(); - Await(commitResult, "batch.Commit"); - if (commitResult.status() != firebase::kFutureStatusComplete) { - LogMessage("ERROR: failed to write batch."); - } + {"int", firebase::firestore::FieldValue::Integer(123)}}); + Await(batch.Commit(), "batch.Commit"); LogMessage("Tested batch write."); LogMessage("Testing transaction."); - auto runTransactionResult = firestore->RunTransaction( - [collection](firebase::firestore::Transaction& transaction, - std::string& error_message) -> firebase::firestore::Error { - transaction.Update( - collection.Document("one"), - firebase::firestore::MapFieldValue{ - {"int", firebase::firestore::FieldValue::Integer(123LL)}}); - transaction.Delete(collection.Document("two")); - transaction.Set( - collection.Document("three"), - firebase::firestore::MapFieldValue{ - {"int", firebase::firestore::FieldValue::Integer(321LL)}}); - return firebase::firestore::kOk; - }); - Await(runTransactionResult, "firestore.RunTransaction"); - if (runTransactionResult.status() != - firebase::kFutureStatusComplete) { - LogMessage("ERROR: failed to run transaction."); - } + Await( + firestore->RunTransaction( + [collection](firebase::firestore::Transaction& transaction, + std::string&) -> firebase::firestore::Error { + transaction.Update( + collection.Document("one"), + firebase::firestore::MapFieldValue{ + {"int", firebase::firestore::FieldValue::Integer(123)}}); + transaction.Delete(collection.Document("two")); + transaction.Set( + collection.Document("three"), + firebase::firestore::MapFieldValue{ + {"int", firebase::firestore::FieldValue::Integer(321)}}); + return firebase::firestore::kOk; + }), + "firestore.RunTransaction"); LogMessage("Tested transaction."); LogMessage("Testing query."); @@ -321,11 +305,9 @@ extern "C" int common_main(int argc, const char* argv[]) { .WhereGreaterThan("int", firebase::firestore::FieldValue::Boolean(true)) .Limit(3); - auto getQueryResult = query.Get(); - Await(getQueryResult, "query.Get"); - if (getQueryResult.status() == firebase::kFutureStatusComplete) { - const firebase::firestore::QuerySnapshot* snapshot = - getQueryResult.result(); + auto query_future = query.Get(); + if (Await(query_future, "query.Get")) { + const firebase::firestore::QuerySnapshot* snapshot = query_future.result(); if (snapshot == nullptr) { LogMessage("ERROR: failed to fetch query result."); } else { diff --git a/firestore/testapp/src/desktop/desktop_main.cc b/firestore/testapp/src/desktop/desktop_main.cc index 74f03896..3a027f82 100644 --- a/firestore/testapp/src/desktop/desktop_main.cc +++ b/firestore/testapp/src/desktop/desktop_main.cc @@ -118,6 +118,6 @@ int64_t WinGetCurrentTimeInMicroseconds() { // Windows file time is expressed in 100s of nanoseconds. // To convert to microseconds, multiply x10. - return now.QuadPart * 10LL; + return now.QuadPart * 10; } #endif diff --git a/functions/testapp/Podfile b/functions/testapp/Podfile index 5c0c906a..eb5c0fc8 100644 --- a/functions/testapp/Podfile +++ b/functions/testapp/Podfile @@ -2,6 +2,6 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # Cloud Functions for Firebase test application. target 'testapp' do - pod 'Firebase/Functions', '6.17.0' - pod 'Firebase/Auth', '6.17.0' + pod 'Firebase/Functions', '6.24.0' + pod 'Firebase/Auth', '6.24.0' end diff --git a/messaging/testapp/Podfile b/messaging/testapp/Podfile index 425161a9..b0f6333b 100644 --- a/messaging/testapp/Podfile +++ b/messaging/testapp/Podfile @@ -2,5 +2,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # FCM test application. target 'testapp' do - pod 'Firebase/Messaging', '6.17.0' + pod 'Firebase/Messaging', '6.24.0' end diff --git a/remote_config/testapp/Podfile b/remote_config/testapp/Podfile index 14afba59..d44ec81f 100644 --- a/remote_config/testapp/Podfile +++ b/remote_config/testapp/Podfile @@ -2,5 +2,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # Firebase Remote Config test application. target 'testapp' do - pod 'Firebase/RemoteConfig', '6.17.0' + pod 'Firebase/RemoteConfig', '6.24.0' end diff --git a/storage/testapp/Podfile b/storage/testapp/Podfile index d7be90c8..5dca5c0b 100644 --- a/storage/testapp/Podfile +++ b/storage/testapp/Podfile @@ -2,6 +2,6 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # Cloud Storage for Firebase test application. target 'testapp' do - pod 'Firebase/Storage', '6.17.0' - pod 'Firebase/Auth', '6.17.0' + pod 'Firebase/Storage', '6.24.0' + pod 'Firebase/Auth', '6.24.0' end From a1b328198f45579d193ced4fdadf38187c9cf7a4 Mon Sep 17 00:00:00 2001 From: chkuang-g <31869252+chkuang-g@users.noreply.github.com> Date: Wed, 2 Sep 2020 16:36:47 -0700 Subject: [PATCH 14/65] Update firebase-cpp-sdk-issue.md --- .github/ISSUE_TEMPLATE/firebase-cpp-sdk-issue.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/firebase-cpp-sdk-issue.md b/.github/ISSUE_TEMPLATE/firebase-cpp-sdk-issue.md index 44897e00..2507996b 100644 --- a/.github/ISSUE_TEMPLATE/firebase-cpp-sdk-issue.md +++ b/.github/ISSUE_TEMPLATE/firebase-cpp-sdk-issue.md @@ -7,6 +7,8 @@ assignees: '' --- +## For Firebase C++ SDK issues, please report to [Firebase C++ open-source](https://github.com/firebase/firebase-cpp-sdk/issues) + ### Please fill in the following fields: Firebase C++ SDK version: Firebase plugins in use (Auth, Database, etc.): @@ -18,6 +20,4 @@ Platform you are targeting (iOS, Android, and/or desktop): (Please list the full steps to reproduce the issue. Include device logs, and stack traces if available.) ### Please answer the following, if applicable: -Have you been able to reproduce this issue with just the Firebase C++ quickstarts (this GitHub project)? - What's the issue repro rate? (eg 100%, 1/5 etc) From 7a3ccac30a22bc88941f9802cd45b1e46cc131f6 Mon Sep 17 00:00:00 2001 From: chkuang-g <31869252+chkuang-g@users.noreply.github.com> Date: Tue, 8 Sep 2020 16:32:28 -0700 Subject: [PATCH 15/65] Update and rename firebase-cpp-sdk-issue.md to issue.md --- .../ISSUE_TEMPLATE/firebase-cpp-sdk-issue.md | 23 ------------ .github/ISSUE_TEMPLATE/issue.md | 35 +++++++++++++++++++ 2 files changed, 35 insertions(+), 23 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/firebase-cpp-sdk-issue.md create mode 100644 .github/ISSUE_TEMPLATE/issue.md diff --git a/.github/ISSUE_TEMPLATE/firebase-cpp-sdk-issue.md b/.github/ISSUE_TEMPLATE/firebase-cpp-sdk-issue.md deleted file mode 100644 index 2507996b..00000000 --- a/.github/ISSUE_TEMPLATE/firebase-cpp-sdk-issue.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -name: Firebase C++ SDK issue -about: Please use this template to report issues with the Firebase C++ SDK. -title: '' -labels: new -assignees: '' - ---- - -## For Firebase C++ SDK issues, please report to [Firebase C++ open-source](https://github.com/firebase/firebase-cpp-sdk/issues) - -### Please fill in the following fields: -Firebase C++ SDK version: -Firebase plugins in use (Auth, Database, etc.): -Additional SDKs you are using (Facebook, AdMob, etc.): -Platform you are using the SDK on (Mac, Windows, or Linux): -Platform you are targeting (iOS, Android, and/or desktop): - -### Please describe the issue here: -(Please list the full steps to reproduce the issue. Include device logs, and stack traces if available.) - -### Please answer the following, if applicable: -What's the issue repro rate? (eg 100%, 1/5 etc) diff --git a/.github/ISSUE_TEMPLATE/issue.md b/.github/ISSUE_TEMPLATE/issue.md new file mode 100644 index 00000000..40cdd78e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/issue.md @@ -0,0 +1,35 @@ +--- +name: Firebase C++ Sample issue +about: Report issues with the Firebase C++ Sample. +labels: new +--- + + +### [READ] For Firebase C++ SDK issues, please report to [Firebase C++ open-source](https://github.com/firebase/firebase-cpp-sdk/issues) + +Once you've read this section and determined that your issue is appropriate for this repository, please delete this section. + +### [REQUIRED] Please fill in the following fields: + + * Which Firebase Sample? (Auth, Database, etc.): _____ + * Firebase C++ SDK version: _____ + * Additional SDKs you are using (Facebook, AdMob, etc.): _____ + * Platform you are using the SDK on (Mac, Windows, or Linux): _____ + * Platform you are targeting (iOS, Android, and/or desktop): _____ + +### [REQUIRED] Please describe the issue here: + +(Please list the full steps to reproduce the issue. Include device logs, and stack traces if available.) + +#### Steps to reproduce: + +What's the issue repro rate? (eg 100%, 1/5 etc) + +What happened? How can we make the problem occur? +This could be a description, log/console output, etc. + +If you have a downloadable sample project that reproduces the bug you're reporting, you will +likely receive a faster response on your issue. From 1f94c2c5c37cd1ca4464b9ed6a4dbf49361bd559 Mon Sep 17 00:00:00 2001 From: chkuang-g <31869252+chkuang-g@users.noreply.github.com> Date: Tue, 8 Sep 2020 17:02:24 -0700 Subject: [PATCH 16/65] Update issue.md --- .github/ISSUE_TEMPLATE/issue.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/issue.md b/.github/ISSUE_TEMPLATE/issue.md index 40cdd78e..10edc34a 100644 --- a/.github/ISSUE_TEMPLATE/issue.md +++ b/.github/ISSUE_TEMPLATE/issue.md @@ -8,7 +8,7 @@ validate_template=true template_path=.github/ISSUE_TEMPLATE/issue.md --> -### [READ] For Firebase C++ SDK issues, please report to [Firebase C++ open-source](https://github.com/firebase/firebase-cpp-sdk/issues) +### [READ] For Firebase C++ SDK issues, please report to [Firebase C++ open-source](https://github.com/firebase/firebase-cpp-sdk/issues/new/choose) Once you've read this section and determined that your issue is appropriate for this repository, please delete this section. From ba7d41a6a97812d1a82bf6f0e248370b63a38d34 Mon Sep 17 00:00:00 2001 From: chkuang-g <31869252+chkuang-g@users.noreply.github.com> Date: Tue, 8 Sep 2020 17:51:09 -0700 Subject: [PATCH 17/65] Update issue.md --- .github/ISSUE_TEMPLATE/issue.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/issue.md b/.github/ISSUE_TEMPLATE/issue.md index 10edc34a..94c20889 100644 --- a/.github/ISSUE_TEMPLATE/issue.md +++ b/.github/ISSUE_TEMPLATE/issue.md @@ -14,11 +14,11 @@ Once you've read this section and determined that your issue is appropriate for ### [REQUIRED] Please fill in the following fields: - * Which Firebase Sample? (Auth, Database, etc.): _____ + * Which Firebase Sample: _____ (Auth, Database, etc.) * Firebase C++ SDK version: _____ - * Additional SDKs you are using (Facebook, AdMob, etc.): _____ - * Platform you are using the SDK on (Mac, Windows, or Linux): _____ - * Platform you are targeting (iOS, Android, and/or desktop): _____ + * Additional SDKs you are using: _____ (Facebook, AdMob, etc.) + * Platform you are using the SDK on: _____ (Mac, Windows, or Linux) + * Platform you are targeting: _____ (iOS, Android, and/or desktop) ### [REQUIRED] Please describe the issue here: From f4d8254d8efdd3be1fc007a5dc476fce6a5bef7b Mon Sep 17 00:00:00 2001 From: Vimanyu Date: Wed, 25 Nov 2020 14:05:36 -0800 Subject: [PATCH 18/65] adding noop workflow to main. Only if you have a workflow in main, can you trigger it manually via the Github UI. Pushing a dummy workflow so that we can develop in other feature branches and test things by manually activating the workflows. --- .github/workflows/desktop.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .github/workflows/desktop.yml diff --git a/.github/workflows/desktop.yml b/.github/workflows/desktop.yml new file mode 100644 index 00000000..b606cea0 --- /dev/null +++ b/.github/workflows/desktop.yml @@ -0,0 +1,14 @@ +name: Desktop 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 33605f4e2aaed7bfb08fde97b3b8d1934aa17c50 Mon Sep 17 00:00:00 2001 From: DellaBitta Date: Tue, 23 Feb 2021 15:23:06 -0500 Subject: [PATCH 19/65] Updated dependencies to 7.0.0 --- admob/testapp/Podfile | 2 +- analytics/testapp/Podfile | 2 +- .../testapp/testapp.xcodeproj/project.pbxproj | 4 ++-- auth/testapp/Podfile | 2 +- auth/testapp/testapp.xcodeproj/project.pbxproj | 4 ++-- database/testapp/Podfile | 4 ++-- .../testapp/testapp.xcodeproj/project.pbxproj | 4 ++-- dynamic_links/testapp/Podfile | 2 +- .../testapp/testapp.xcodeproj/project.pbxproj | 4 ++-- firestore/testapp/Podfile | 6 +++--- firestore/testapp/src/common_main.cc | 16 +++++++++------- .../testapp/testapp.xcodeproj/project.pbxproj | 4 ++-- functions/testapp/Podfile | 4 ++-- .../testapp/testapp.xcodeproj/project.pbxproj | 4 ++-- messaging/testapp/Podfile | 2 +- .../testapp/testapp.xcodeproj/project.pbxproj | 4 ++-- readme.md | 7 +++++++ remote_config/testapp/Podfile | 2 +- .../testapp/testapp.xcodeproj/project.pbxproj | 4 ++-- storage/testapp/Podfile | 4 ++-- .../testapp/testapp.xcodeproj/project.pbxproj | 4 ++-- 21 files changed, 49 insertions(+), 40 deletions(-) diff --git a/admob/testapp/Podfile b/admob/testapp/Podfile index 269cc6be..9759f528 100644 --- a/admob/testapp/Podfile +++ b/admob/testapp/Podfile @@ -2,5 +2,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # AdMob test application. target 'testapp' do - pod 'Firebase/AdMob', '6.24.0' + pod 'Firebase/AdMob', '7.0.0' end diff --git a/analytics/testapp/Podfile b/analytics/testapp/Podfile index 63db963c..f7888311 100644 --- a/analytics/testapp/Podfile +++ b/analytics/testapp/Podfile @@ -2,5 +2,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # Analytics test application. target 'testapp' do - pod 'Firebase/Analytics', '6.24.0' + pod 'Firebase/Analytics', '7.0.0' end diff --git a/analytics/testapp/testapp.xcodeproj/project.pbxproj b/analytics/testapp/testapp.xcodeproj/project.pbxproj index baf45c4c..6c325725 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 = 8.4; + IPHONEOS_DEPLOYMENT_TARGET = 10.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 = 10.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/auth/testapp/Podfile b/auth/testapp/Podfile index a71424d3..d2ddd900 100644 --- a/auth/testapp/Podfile +++ b/auth/testapp/Podfile @@ -2,5 +2,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # Auth test application. target 'testapp' do - pod 'Firebase/Auth', '6.24.0' + pod 'Firebase/Auth', '7.0.0' end diff --git a/auth/testapp/testapp.xcodeproj/project.pbxproj b/auth/testapp/testapp.xcodeproj/project.pbxproj index baf45c4c..6c325725 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 = 8.4; + IPHONEOS_DEPLOYMENT_TARGET = 10.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 = 10.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/database/testapp/Podfile b/database/testapp/Podfile index 3509b28c..966f7b10 100644 --- a/database/testapp/Podfile +++ b/database/testapp/Podfile @@ -2,6 +2,6 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # Firebase Realtime Database test application. target 'testapp' do - pod 'Firebase/Database', '6.24.0' - pod 'Firebase/Auth', '6.24.0' + pod 'Firebase/Database', '7.0.0' + pod 'Firebase/Auth', '7.0.0' end diff --git a/database/testapp/testapp.xcodeproj/project.pbxproj b/database/testapp/testapp.xcodeproj/project.pbxproj index baf45c4c..6c325725 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 = 8.4; + IPHONEOS_DEPLOYMENT_TARGET = 10.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 = 10.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/dynamic_links/testapp/Podfile b/dynamic_links/testapp/Podfile index be1c85ab..8af0d756 100644 --- a/dynamic_links/testapp/Podfile +++ b/dynamic_links/testapp/Podfile @@ -2,5 +2,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # Dynamic Links test application. target 'testapp' do - pod 'Firebase/DynamicLinks', '6.24.0' + pod 'Firebase/DynamicLinks', '7.0.0' end diff --git a/dynamic_links/testapp/testapp.xcodeproj/project.pbxproj b/dynamic_links/testapp/testapp.xcodeproj/project.pbxproj index baf45c4c..6c325725 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 = 8.4; + IPHONEOS_DEPLOYMENT_TARGET = 10.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 = 10.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/firestore/testapp/Podfile b/firestore/testapp/Podfile index eb725652..b4fb49d8 100644 --- a/firestore/testapp/Podfile +++ b/firestore/testapp/Podfile @@ -2,7 +2,7 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # Firebase Firestore test application. target 'testapp' do - pod 'Firebase/Analytics', '6.24.0' - pod 'Firebase/Firestore', '6.24.0' - pod 'Firebase/Auth', '6.24.0' + pod 'Firebase/Analytics', '7.0.0' + pod 'Firebase/Firestore', '7.0.0' + pod 'Firebase/Auth', '7.0.0' end diff --git a/firestore/testapp/src/common_main.cc b/firestore/testapp/src/common_main.cc index 3fd6757e..3f2d05ad 100644 --- a/firestore/testapp/src/common_main.cc +++ b/firestore/testapp/src/common_main.cc @@ -65,11 +65,12 @@ class TestEventListener : public Countable, public: explicit TestEventListener(std::string name) : name_(std::move(name)) {} - void OnEvent(const T& value, - const firebase::firestore::Error error) override { + void OnEvent(const T& value, const firebase::firestore::Error error_code, + const std::string& error_message) override { event_count_++; - if (error != firebase::firestore::kOk) { - LogMessage("ERROR: EventListener %s got %d.", name_.c_str(), error); + if (error_code != firebase::firestore::kErrorOk) { + LogMessage("ERROR: EventListener %s got %d (%s).", name_.c_str(), + error_code, error_message.c_str()); } } @@ -79,8 +80,9 @@ class TestEventListener : public Countable, firebase::firestore::ListenerRegistration AttachTo(U* ref) { #if !defined(STLPORT) return ref->AddSnapshotListener( - [this](const T& result, firebase::firestore::Error error) { - OnEvent(result, error); + [this](const T& result, firebase::firestore::Error error_code, + const std::string& error_message) { + OnEvent(result, error_code, error_message); }); #else return ref->AddSnapshotListener(this); @@ -294,7 +296,7 @@ extern "C" int common_main(int argc, const char* argv[]) { collection.Document("three"), firebase::firestore::MapFieldValue{ {"int", firebase::firestore::FieldValue::Integer(321)}}); - return firebase::firestore::kOk; + return firebase::firestore::kErrorOk; }), "firestore.RunTransaction"); LogMessage("Tested transaction."); diff --git a/firestore/testapp/testapp.xcodeproj/project.pbxproj b/firestore/testapp/testapp.xcodeproj/project.pbxproj index cabe181e..81ed36ca 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 = 8.4; + IPHONEOS_DEPLOYMENT_TARGET = 10.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 = 8.4; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/functions/testapp/Podfile b/functions/testapp/Podfile index eb5c0fc8..51c5b50a 100644 --- a/functions/testapp/Podfile +++ b/functions/testapp/Podfile @@ -2,6 +2,6 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # Cloud Functions for Firebase test application. target 'testapp' do - pod 'Firebase/Functions', '6.24.0' - pod 'Firebase/Auth', '6.24.0' + pod 'Firebase/Functions', '7.0.0' + pod 'Firebase/Auth', '7.0.0' end diff --git a/functions/testapp/testapp.xcodeproj/project.pbxproj b/functions/testapp/testapp.xcodeproj/project.pbxproj index baf45c4c..6c325725 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 = 8.4; + IPHONEOS_DEPLOYMENT_TARGET = 10.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 = 10.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/messaging/testapp/Podfile b/messaging/testapp/Podfile index b0f6333b..e6ffc8dc 100644 --- a/messaging/testapp/Podfile +++ b/messaging/testapp/Podfile @@ -2,5 +2,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # FCM test application. target 'testapp' do - pod 'Firebase/Messaging', '6.24.0' + pod 'Firebase/Messaging', '7.0.0' end diff --git a/messaging/testapp/testapp.xcodeproj/project.pbxproj b/messaging/testapp/testapp.xcodeproj/project.pbxproj index da19cde8..096e3825 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 = 8.4; + IPHONEOS_DEPLOYMENT_TARGET = 10.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 = 8.4; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/readme.md b/readme.md index 5c7e288c..53fededc 100644 --- a/readme.md +++ b/readme.md @@ -6,6 +6,13 @@ iOS and Android samples for the For more information, see [firebase.google.com](https://firebase.google.com). +## Issue reporting + +This repo no longer accepts issue reporting. For Firebase C++ SDK related +issues, please report to +[Firebase C++ Open-source](https://github.com/firebase/firebase-cpp-sdk/issues). +Please fill in the template to expedite the support. + ## How to make contributions? Please read and follow the steps in [CONTRIBUTING.md](CONTRIBUTING.md). diff --git a/remote_config/testapp/Podfile b/remote_config/testapp/Podfile index d44ec81f..5ce98e6d 100644 --- a/remote_config/testapp/Podfile +++ b/remote_config/testapp/Podfile @@ -2,5 +2,5 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # Firebase Remote Config test application. target 'testapp' do - pod 'Firebase/RemoteConfig', '6.24.0' + pod 'Firebase/RemoteConfig', '7.0.0' end diff --git a/remote_config/testapp/testapp.xcodeproj/project.pbxproj b/remote_config/testapp/testapp.xcodeproj/project.pbxproj index baf45c4c..6c325725 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 = 8.4; + IPHONEOS_DEPLOYMENT_TARGET = 10.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 = 10.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/storage/testapp/Podfile b/storage/testapp/Podfile index 5dca5c0b..f79f288b 100644 --- a/storage/testapp/Podfile +++ b/storage/testapp/Podfile @@ -2,6 +2,6 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' # Cloud Storage for Firebase test application. target 'testapp' do - pod 'Firebase/Storage', '6.24.0' - pod 'Firebase/Auth', '6.24.0' + pod 'Firebase/Storage', '7.0.0' + pod 'Firebase/Auth', '7.0.0' end diff --git a/storage/testapp/testapp.xcodeproj/project.pbxproj b/storage/testapp/testapp.xcodeproj/project.pbxproj index baf45c4c..6c325725 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 = 8.4; + IPHONEOS_DEPLOYMENT_TARGET = 10.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 = 10.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; From 9bb7bde5fd1a3a127997a25f9c237e1fb919eb0e Mon Sep 17 00:00:00 2001 From: Ehsan Nasiri Date: Wed, 21 Jul 2021 14:05:02 -0500 Subject: [PATCH 20/65] Apply clang-format (updated). --- firestore/testapp/src/common_main.cc | 34 ++++++++++++++-------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/firestore/testapp/src/common_main.cc b/firestore/testapp/src/common_main.cc index 3f2d05ad..ad77bfd4 100644 --- a/firestore/testapp/src/common_main.cc +++ b/firestore/testapp/src/common_main.cc @@ -65,7 +65,8 @@ class TestEventListener : public Countable, public: explicit TestEventListener(std::string name) : name_(std::move(name)) {} - void OnEvent(const T& value, const firebase::firestore::Error error_code, + void OnEvent(const T& value, + const firebase::firestore::Error error_code, const std::string& error_message) override { event_count_++; if (error_code != firebase::firestore::kErrorOk) { @@ -283,22 +284,21 @@ extern "C" int common_main(int argc, const char* argv[]) { LogMessage("Tested batch write."); LogMessage("Testing transaction."); - Await( - firestore->RunTransaction( - [collection](firebase::firestore::Transaction& transaction, - std::string&) -> firebase::firestore::Error { - transaction.Update( - collection.Document("one"), - firebase::firestore::MapFieldValue{ - {"int", firebase::firestore::FieldValue::Integer(123)}}); - transaction.Delete(collection.Document("two")); - transaction.Set( - collection.Document("three"), - firebase::firestore::MapFieldValue{ - {"int", firebase::firestore::FieldValue::Integer(321)}}); - return firebase::firestore::kErrorOk; - }), - "firestore.RunTransaction"); + Await(firestore->RunTransaction( + [collection](firebase::firestore::Transaction& transaction, + std::string&) -> firebase::firestore::Error { + transaction.Update( + collection.Document("one"), + firebase::firestore::MapFieldValue{ + {"int", firebase::firestore::FieldValue::Integer(123)}}); + transaction.Delete(collection.Document("two")); + transaction.Set( + collection.Document("three"), + firebase::firestore::MapFieldValue{ + {"int", firebase::firestore::FieldValue::Integer(321)}}); + return firebase::firestore::kErrorOk; + }), + "firestore.RunTransaction"); LogMessage("Tested transaction."); LogMessage("Testing query."); From 9ee9260614dc45186cbc413b1155752f0e478162 Mon Sep 17 00:00:00 2001 From: Ehsan Nasiri Date: Wed, 21 Jul 2021 14:05:34 -0500 Subject: [PATCH 21/65] Don't use the deprecated EventListener class. --- firestore/testapp/src/common_main.cc | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/firestore/testapp/src/common_main.cc b/firestore/testapp/src/common_main.cc index ad77bfd4..4322d77d 100644 --- a/firestore/testapp/src/common_main.cc +++ b/firestore/testapp/src/common_main.cc @@ -60,14 +60,13 @@ class Countable { }; template -class TestEventListener : public Countable, - public firebase::firestore::EventListener { +class TestEventListener : public Countable { public: explicit TestEventListener(std::string name) : name_(std::move(name)) {} void OnEvent(const T& value, const firebase::firestore::Error error_code, - const std::string& error_message) override { + const std::string& error_message) { event_count_++; if (error_code != firebase::firestore::kErrorOk) { LogMessage("ERROR: EventListener %s got %d (%s).", name_.c_str(), @@ -75,19 +74,13 @@ class TestEventListener : public Countable, } } - // Hides the STLPort-related quirk that `AddSnapshotListener` has different - // signatures depending on whether `std::function` is available. template firebase::firestore::ListenerRegistration AttachTo(U* ref) { -#if !defined(STLPORT) return ref->AddSnapshotListener( [this](const T& result, firebase::firestore::Error error_code, const std::string& error_message) { OnEvent(result, error_code, error_message); }); -#else - return ref->AddSnapshotListener(this); -#endif } private: From 0b490c0ba3700696b45f623fc0d3acfc5f84ab1c Mon Sep 17 00:00:00 2001 From: Ehsan Nasiri Date: Wed, 21 Jul 2021 14:57:55 -0500 Subject: [PATCH 22/65] Add .clang-format. --- firestore/.clang-format | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 firestore/.clang-format diff --git a/firestore/.clang-format b/firestore/.clang-format new file mode 100644 index 00000000..e2412319 --- /dev/null +++ b/firestore/.clang-format @@ -0,0 +1,9 @@ +BasedOnStyle: Google +Standard: Cpp11 +ColumnLimit: 80 +BinPackParameters: false +AllowAllParametersOfDeclarationOnNextLine: true +SpacesInContainerLiterals: true +DerivePointerAlignment: false +PointerAlignment: Left +IncludeBlocks: Preserve From 1d220911b5a94d90101ad5812b0acbff35b92cec Mon Sep 17 00:00:00 2001 From: Ehsan Nasiri Date: Wed, 21 Jul 2021 15:42:45 -0500 Subject: [PATCH 23/65] Update README to reflect changes in the frameworks. --- firestore/testapp/README.md | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/firestore/testapp/README.md b/firestore/testapp/README.md index 90578cad..5594b6f4 100644 --- a/firestore/testapp/README.md +++ b/firestore/testapp/README.md @@ -33,6 +33,14 @@ Building and Running the testapp sudo gem install cocoapods --pre ``` + - Update the pod versions in the Podfile to match the C++ SDK version that you are using. + For instance: if you're using SDK version 8.2.0, use the following in the Podfile: + + ``` + pod 'Firebase/Firestore', '8.2.0' + pod 'Firebase/Auth', '8.2.0' + ``` + - From the testapp directory, install the CocoaPods listed in the Podfile by running, @@ -71,15 +79,15 @@ Building and Running the testapp - Add the following frameworks from the Firebase C++ SDK to the project: - - frameworks/ios/universal/firebase.framework - - frameworks/ios/universal/firebase_auth.framework - - frameworks/ios/universal/firebase_firestore.framework + - xcframeworks/firebase.xcframework/ios-arm64_armv7/firebase.framework + - xcframeworks/firebase_firestore.xcframework/ios-arm64_armv7/firebase_firestore.framework + - xcframeworks/firebase_auth.xcframework/ios-arm64_armv7/firebase_auth.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" - e.g. 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`. + `/Users/me/firebase_cpp_sdk/xcframeworks/**`. - 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 From 7193c581568b18622499d9082026c1308a6eb88f Mon Sep 17 00:00:00 2001 From: Ehsan Nasiri Date: Wed, 21 Jul 2021 15:50:12 -0500 Subject: [PATCH 24/65] Update the Podfile. - The minimum target had to be bumped up. - Firebase/Analytics is no longer needed for this app to work. --- firestore/testapp/Podfile | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/firestore/testapp/Podfile b/firestore/testapp/Podfile index b4fb49d8..31e20c7c 100644 --- a/firestore/testapp/Podfile +++ b/firestore/testapp/Podfile @@ -1,8 +1,7 @@ source 'https://github.com/CocoaPods/Specs.git' -platform :ios, '8.0' +platform :ios, '10.0' # Firebase Firestore test application. target 'testapp' do - pod 'Firebase/Analytics', '7.0.0' - pod 'Firebase/Firestore', '7.0.0' - pod 'Firebase/Auth', '7.0.0' + pod 'Firebase/Firestore', '8.2.0' + pod 'Firebase/Auth', '8.2.0' end From 7576b1f27a4b974bcf2111395613fad9ceca34ce Mon Sep 17 00:00:00 2001 From: Ehsan Nasiri Date: Thu, 22 Jul 2021 10:18:09 -0500 Subject: [PATCH 25/65] Add more info to README. --- firestore/testapp/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/firestore/testapp/README.md b/firestore/testapp/README.md index 5594b6f4..2254c47e 100644 --- a/firestore/testapp/README.md +++ b/firestore/testapp/README.md @@ -34,6 +34,8 @@ Building and Running the testapp ``` - Update the pod versions in the Podfile to match the C++ SDK version that you are using. + For the latest version of the C++ SDK, you can find the associated pod versions on the + [`Add Firebase to your project` page](https://firebase.google.com/docs/cpp/setup?platform=ios#libraries-ios). For instance: if you're using SDK version 8.2.0, use the following in the Podfile: ``` From 78ae4919c1a624337f924585f063454734d25d92 Mon Sep 17 00:00:00 2001 From: Ehsan Nasiri Date: Thu, 22 Jul 2021 11:27:29 -0500 Subject: [PATCH 26/65] Update the TODOs in the README. --- firestore/testapp/README.md | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/firestore/testapp/README.md b/firestore/testapp/README.md index 90578cad..e258a51c 100644 --- a/firestore/testapp/README.md +++ b/firestore/testapp/README.md @@ -13,7 +13,19 @@ The testapp performs the following: - Gets a pointer to firebase::Auth, and signs in anonymously. This allows the testapp to access a Firebase Firestore instance with authentication rules enabled. -- TODO(varconst): describe the Firestore-specific logic +- Initializes a Firestore instance, and sets its logging level to + `kLogLevelDebug` in order to see debug messages in the logs. +- Tests that it can create Timestamp, SnapshotMetadata, and GeoPoint objects. +- Creates a collection, and a document inside that collection. +- Writes initial data to the document (`Set`), updates the document content + (`Update`), reads the document back (`Get`), and checks that the contents + match our expectation. +- Deletes the document. +- Performs a batch write to two documents. +- Performs a Transaction containing three operations (`Update`, `Delete`, and + `Set`) on three different documents. +- Queries documents in the collection that match a certain condition. Ensures + the documents returned via the query match our expectation. Introduction ------------ @@ -220,11 +232,6 @@ Building and Running the testapp - The testapp has no user interface, but the output can be viewed via the console. -Known issues ------------- - -TODO(varconst) - Support ------- From b71ac8f39ed51803e4b3d7ea8addf1a68d4aed41 Mon Sep 17 00:00:00 2001 From: Ehsan Nasiri Date: Thu, 22 Jul 2021 13:59:16 -0500 Subject: [PATCH 27/65] Address code review --- firestore/testapp/README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/firestore/testapp/README.md b/firestore/testapp/README.md index e258a51c..beb93221 100644 --- a/firestore/testapp/README.md +++ b/firestore/testapp/README.md @@ -13,10 +13,11 @@ The testapp performs the following: - Gets a pointer to firebase::Auth, and signs in anonymously. This allows the testapp to access a Firebase Firestore instance with authentication rules enabled. -- Initializes a Firestore instance, and sets its logging level to +- Initializes a Firestore instance and sets its logging level to `kLogLevelDebug` in order to see debug messages in the logs. -- Tests that it can create Timestamp, SnapshotMetadata, and GeoPoint objects. -- Creates a collection, and a document inside that collection. +- Tests that it can create `Timestamp`, `SnapshotMetadata`, and `GeoPoint` + objects. +- Creates a collection and a document inside that collection. - Writes initial data to the document (`Set`), updates the document content (`Update`), reads the document back (`Get`), and checks that the contents match our expectation. From bf3b0298a44a71890fad7e4ba93376d4070c300b Mon Sep 17 00:00:00 2001 From: Ehsan Nasiri Date: Wed, 28 Jul 2021 10:53:40 -0500 Subject: [PATCH 28/65] Address comments. --- firestore/testapp/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/firestore/testapp/README.md b/firestore/testapp/README.md index 2254c47e..87de3f2c 100644 --- a/firestore/testapp/README.md +++ b/firestore/testapp/README.md @@ -37,6 +37,7 @@ Building and Running the testapp For the latest version of the C++ SDK, you can find the associated pod versions on the [`Add Firebase to your project` page](https://firebase.google.com/docs/cpp/setup?platform=ios#libraries-ios). For instance: if you're using SDK version 8.2.0, use the following in the Podfile: + (but note that pod versions may not always match the C++ SDK version) ``` pod 'Firebase/Firestore', '8.2.0' From a94a30df2cf31dce2b8f99947486ec640b142c7a Mon Sep 17 00:00:00 2001 From: Ehsan Nasiri Date: Wed, 28 Jul 2021 14:30:01 -0500 Subject: [PATCH 29/65] Address comments. --- firestore/testapp/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/firestore/testapp/README.md b/firestore/testapp/README.md index 87de3f2c..266a52bb 100644 --- a/firestore/testapp/README.md +++ b/firestore/testapp/README.md @@ -36,8 +36,8 @@ Building and Running the testapp - Update the pod versions in the Podfile to match the C++ SDK version that you are using. For the latest version of the C++ SDK, you can find the associated pod versions on the [`Add Firebase to your project` page](https://firebase.google.com/docs/cpp/setup?platform=ios#libraries-ios). - For instance: if you're using SDK version 8.2.0, use the following in the Podfile: - (but note that pod versions may not always match the C++ SDK version) + For instance: if you're using SDK version 8.2.0, use the following in the Podfile + (but note that pod versions may not always match the C++ SDK version): ``` pod 'Firebase/Firestore', '8.2.0' From d8f77e2f2fd1f6ce5f19371bbde54fa502b77ffb Mon Sep 17 00:00:00 2001 From: Vimanyu Date: Thu, 5 Aug 2021 10:14:18 -0700 Subject: [PATCH 30/65] update to use job intent service --- messaging/testapp/AndroidManifest.xml | 1 + .../java/com/google/firebase/example/TestappNativeActivity.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/messaging/testapp/AndroidManifest.xml b/messaging/testapp/AndroidManifest.xml index ae648799..b1cc6a55 100644 --- a/messaging/testapp/AndroidManifest.xml +++ b/messaging/testapp/AndroidManifest.xml @@ -52,6 +52,7 @@ android:exported="false" > diff --git a/messaging/testapp/src/android/java/com/google/firebase/example/TestappNativeActivity.java b/messaging/testapp/src/android/java/com/google/firebase/example/TestappNativeActivity.java index 290a030e..e504595a 100644 --- a/messaging/testapp/src/android/java/com/google/firebase/example/TestappNativeActivity.java +++ b/messaging/testapp/src/android/java/com/google/firebase/example/TestappNativeActivity.java @@ -54,7 +54,7 @@ protected void onNewIntent(Intent intent) { message.setAction(MessageForwardingService.ACTION_REMOTE_INTENT); message.putExtras(intent); message.setData(intent.getData()); - startService(message); + MessageForwardingService.enqueueWork(this, message); } setIntent(intent); } From 05d7b6a1f56e736e0edeae1c2110283b5054f549 Mon Sep 17 00:00:00 2001 From: Vimanyu Date: Thu, 5 Aug 2021 10:14:51 -0700 Subject: [PATCH 31/65] add compatibility with java8 bytecode --- messaging/testapp/build.gradle | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/messaging/testapp/build.gradle b/messaging/testapp/build.gradle index 2f8eaf46..0971c57b 100644 --- a/messaging/testapp/build.gradle +++ b/messaging/testapp/build.gradle @@ -25,6 +25,13 @@ android { compileSdkVersion 28 buildToolsVersion '28.0.3' + android { + compileOptions { + sourceCompatibility 1.8 + targetCompatibility 1.8 + } + } + sourceSets { main { jniLibs.srcDirs = ['libs'] From 9cb1268630c5e745735e45b0c63c239662b428c5 Mon Sep 17 00:00:00 2001 From: Vimanyu Date: Tue, 10 Aug 2021 10:14:15 -0700 Subject: [PATCH 32/65] for consistency with firebase-cpp-sdk android manifest Didn't see any difference on adding this in quickstart but it is good for consistency sake. --- messaging/testapp/AndroidManifest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/messaging/testapp/AndroidManifest.xml b/messaging/testapp/AndroidManifest.xml index b1cc6a55..f0f9b9c8 100644 --- a/messaging/testapp/AndroidManifest.xml +++ b/messaging/testapp/AndroidManifest.xml @@ -49,6 +49,7 @@ Date: Tue, 5 Apr 2022 10:11:57 -0400 Subject: [PATCH 33/65] 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 zGh0A%o~bd$iYvP!egRY{hObDIvy_vXAOkeTgl5o!33m!l4VLm@<-FwT0+k|yl~vUh z@RFcL4=b(QQQmwQ;>FS_e96dyIU`jmR%&&Amxcb8^&?wvpK{_V_IbmqHh);$hBa~S z;^ph!k~noKv{`Ix7Hi&;Hq%y3wpqUsYO%HhI3Oe~HPmjnSTEasoU;Q_UfYbzd?Vv@ zD6ztDG|W|%xq)xqSx%bU1f>fF#;p9g=Hnjph>Pp$ZHaHS@-DkHw#H&vb1gARf4A*zm3Z75QQ6l( z=-MPMjish$J$0I49EEg^Ykw8IqSY`XkCP&TC?!7zmO`ILgJ9R{56s-ZY$f> zU9GwXt`(^0LGOD9@WoNFK0owGKDC1)QACY_r#@IuE2<`tep4B#I^(PRQ_-Fw(5nws zpkX=rVeVXzR;+%UzoNa;jjx<&@ABmU5X926KsQsz40o*{@47S2 z)p9z@lt=9?A2~!G*QqJWYT5z^CTeckRwhSWiC3h8PQ0M9R}_#QC+lz>`?kgy2DZio zz&2Ozo=yTXVf-?&E;_t`qY{Oy>?+7+I= zWl!tZM_YCLmGXY1nKbIHc;*Mag{Nzx-#yA{ zTATrWj;Nn;NWm6_1#0zy9SQiQV=38f(`DRgD|RxwggL(!^`}lcDTuL4RtLB2F5)lt z=mNMJN|1gcui=?#{NfL{r^nQY+_|N|6Gp5L^vRgt5&tZjSRIk{_*y<3^NrX6PTkze zD|*8!08ZVN)-72TA4Wo3B=+Rg1sc>SX9*X>a!rR~ntLVYeWF5MrLl zA&1L8oli@9ERY|geFokJq^O$2hEpVpIW8G>PPH0;=|7|#AQChL2Hz)4XtpAk zNrN2@Ju^8y&42HCvGddK3)r8FM?oM!3oeQ??bjoYjl$2^3|T7~s}_^835Q(&b>~3} z2kybqM_%CIKk1KSOuXDo@Y=OG2o!SL{Eb4H0-QCc+BwE8x6{rq9j$6EQUYK5a7JL! z`#NqLkDC^u0$R1Wh@%&;yj?39HRipTeiy6#+?5OF%pWyN{0+dVIf*7@T&}{v%_aC8 zCCD1xJ+^*uRsDT%lLxEUuiFqSnBZu`0yIFSv*ajhO^DNoi35o1**16bg1JB z{jl8@msjlAn3`qW{1^SIklxN^q#w|#gqFgkAZ4xtaoJN*u z{YUf|`W)RJfq)@6F&LfUxoMQz%@3SuEJHU;-YXb7a$%W=2RWu5;j44cMjC0oYy|1! zed@H>VQ!7=f~DVYkWT0nfQfAp*<@FZh{^;wmhr|K(D)i?fq9r2FEIatP=^0(s{f8GBn<8T zVz_@sKhbLE&d91L-?o`13zv6PNeK}O5dv>f{-`!ms#4U+JtPV=fgQ5;iNPl9Hf&9( zsJSm5iXIqN7|;I5M08MjUJ{J2@M3 zYN9ft?xIjx&{$K_>S%;Wfwf9N>#|ArVF^shFb9vS)v9Gm00m_%^wcLxe;gIx$7^xR zz$-JDB|>2tnGG@Rrt@R>O40AreXSU|kB3Bm)NILHlrcQ&jak^+~b`)2;otjI(n8A_X~kvp4N$+4|{8IIIv zw*(i}tt+)Kife9&xo-TyoPffGYe;D0a%!Uk(Nd^m?SvaF-gdAz4~-DTm3|Qzf%Pfd zC&tA;D2b4F@d23KV)Csxg6fyOD2>pLy#n+rU&KaQU*txfUj&D3aryVj!Lnz*;xHvl zzo}=X>kl0mBeSRXoZ^SeF94hlCU*cg+b}8p#>JZvWj8gh#66A0ODJ`AX>rubFqbBw z-WR3Z5`33S;7D5J8nq%Z^JqvZj^l)wZUX#7^q&*R+XVPln{wtnJ~;_WQzO{BIFV55 zLRuAKXu+A|7*2L*<_P${>0VdVjlC|n^@lRi}r?wnzQQm z3&h~C3!4C`w<92{?Dpea@5nLP2RJrxvCCBh%Tjobl2FupWZfayq_U$Q@L%$uEB6#X zrm_1TZA8FEtkd`tg)a_jaqnv3BC_O*AUq-*RNLOT)$>2D!r>FZdH&$x5G_FiAPaw4 zgK*7>(qd6R?+M3s@h>Z|H%7eGPxJWn_U$w`fb(Mp+_IK2Kj37YT#Xe5e6KS-_~mW} z`NXEovDJh7n!#q4b+=ne<7uB7Y2(TAR<3@PS&o3P$h#cZ-xF$~JiH6_gsv9v(#ehK zhSB_#AI%lF#+!MB5DMUN+Zhf}=t~{B|Fn{rGM?dOaSvX!D{oGXfS*%~g`W84JJAy4 zMdS?9Bb$vx?`91$J`pD-MGCTHNxU+SxLg&QY+*b_pk0R=A`F}jw$pN*BNM8`6Y=cm zgRh#vab$N$0=XjH6vMyTHQg*+1~gwOO9yhnzZx#e!1H#|Mr<`jJGetsM;$TnciSPJ z5I-R0)$)0r8ABy-2y&`2$33xx#%1mp+@1Vr|q_e=#t7YjjWXH#3F|Fu<G#+-tE2K7 zOJkYxNa74@UT_K4CyJ%mR9Yfa$l=z}lB(6)tZ1Ksp2bv$^OUn3Oed@=Q0M}imYTwX zQoO^_H7SKzf_#kPgKcs%r4BFUyAK9MzfYReHCd=l)YJEgPKq-^z3C%4lq%{&8c{2CGQ3jo!iD|wSEhZ# zjJoH87Rt{4*M_1GdBnBU3trC*hn@KCFABd=Zu`hK;@!TW`hp~;4Aac@24m|GI)Ula z4y%}ClnEu;AL4XVQ6^*!()W#P>BYC@K5mw7c4X|Hk^(mS9ZtfMsVLoPIiwI?w_X0- z#vyiV5q9(xq~fS`_FiUZw->8Awktga>2SrWyvZ|h@LVFtnY#T z%OX30{yiSov4!43kFd(8)cPRMyrN z={af_ONd;m=`^wc7lL|b7V!;zmCI}&8qz=?-6t=uOV;X>G{8pAwf9UJ`Hm=ubIbgR zs6bw3pFeQHL`1P1m5fP~fL*s?rX_|8%tB`Phrij^Nkj{o0oCo*g|ELexQU+2gt66=7}w5A+Qr}mHXC%)(ODT# zK#XTuzqOmMsO~*wgoYjDcy)P7G`5x7mYVB?DOXV^D3nN89P#?cp?A~c%c$#;+|10O z8z(C>mwk#A*LDlpv2~JXY_y_OLZ*Mt)>@gqKf-Ym+cZ{8d%+!1xNm3_xMygTp-!A5 zUTpYFd=!lz&4IFq)Ni7kxLYWhd0o2)ngenV-QP@VCu;147_Lo9f~=+=Nw$6=xyZzp zn7zAe41Sac>O60(dgwPd5a^umFVSH;<7vN>o;}YlMYhBZFZ}-sz`P^3oAI>SCZy&zUtwKSewH;CYysPQN7H>&m215&e2J? zY}>5N-LhaDeRF~C0cB>M z7@y&xh9q??*EIKnh*;1)n-WuSl6HkrI?OUiS^lx$Sr2C-jUm6zhd{nd(>#O8k9*kF zPom7-%w1NjFpj7WP=^!>Vx^6SG^r`r+M&s7V(uh~!T7aE;_ubqNSy)<5(Vi)-^Mp9 zEH@8Vs-+FEeJK%M0z3FzqjkXz$n~BzrtjQv`LagAMo>=?dO8-(af?k@UpL5J#;18~ zHCnWuB(m6G6a2gDq2s`^^5km@A3Rqg-oHZ68v5NqVc zHX_Iw!OOMhzS=gfR7k;K1gkEwuFs|MYTeNhc0js>Wo#^=wX4T<`p zR2$8p6%A9ZTac;OvA4u#Oe3(OUep%&QgqpR8-&{0gjRE()!Ikc?ClygFmGa(7Z^9X zWzmV0$<8Uh)#qaH1`2YCV4Zu6@~*c*bhtHXw~1I6q4I>{92Eq+ZS@_nSQU43bZyidk@hd$j-_iL=^^2CwPcaXnBP;s;b zA4C!k+~rg4U)}=bZ2q*)c4BZ#a&o!uJo*6hK3JRBhOOUQ6fQI;dU#3v>_#yi62&Sp z-%9JJxwIfQ`@w(_qH0J0z~(lbh`P zHoyp2?Oppx^WXwD<~20v!lYm~n53G1w*Ej z9^B*j@lrd>XGW43ff)F;5k|HnGGRu=wmZG9c~#%vDWQHlOIA9(;&TBr#yza{(?k0> zcGF&nOI}JhuPl`kLViBEd)~p2nY9QLdX42u9C~EUWsl-@CE;05y@^V1^wM$ z&zemD1oZd$Z))kEw9)_Mf+X#nT?}n({(+aXHK2S@j$MDsdrw-iLb?#r{?Vud?I5+I zVQ8U?LXsQ}8-)JBGaoawyOsTTK_f8~gFFJ&lhDLs8@Rw$ey-wr&eqSEU^~1jtHmz6 z!D2g4Yh?3VE*W8=*r&G`?u?M~AdO;uTRPfE(@=Gkg z7gh=EGu!6VJJ?S_>|5ZwY?dGFBp3B9m4J1=7u=HcGjsCW+y6`W?OWxfH?S#X8&Zk& zvz6tWcnaS1@~3FTH}q_*$)AjYA_j;yl0H0{I(CW7Rq|;5Q2>Ngd(tmJDp+~qHe_8y zPU_fiCrn!SJ3x&>o6;WDnjUVEt`2fhc9+uLI>99(l$(>Tzwpbh>O775OA5i`jaBdp zXnCwUgomyF3K$0tXzgQhSAc!6nhyRh_$fP}Rd$|*Y7?ah(JrN=I7+)+Hp4BLJJ2P~ zFD!)H^uR2*m7GQZpLUVS#R3^?2wCd}(gcFcz!u5KN9ldNJdh@%onf06z9m~T0n;dqg6@?>G@S|rPO*Kj>{su+R|7bH>osA&uD4eqxtr**k($ii`uO? z7-&VkiL4Rp3S&e+T}2Z#;NtWHZco(v8O3QMvN0g7l8GV|U2>x-DbamkZo5)bjaSFR zr~Y9(EvF9{o*@|nBPj+e5o$_K`%TH1hD=|its}|qS^o6EQu_gOuDUH=Dtzik;P7G$ zq%_T<>9O}bGIB?;IQ*H`BJ5NWF6+XLv@G7aZwcy(&BoepG~u`aIcG>y+;J7+L=wTZ zB=%n@O}=+mjBO%1lMo6C0@1*+mhBqqY((%QMUBhyeC~r*5WVqzisOXFncr*5Lr0q6 zyPU&NOV}Vt2jl>&yig4I6j93?D>Ft=keRh=Y;3*^Z-I26nkZ#Jj5OJ89_?@#9lNjp z#gfAO6i937)~I|98P%xAWxwmk(F&@lTMx63*FZ~2b{NHU+}EV8+kMAB0bM*Zn#&7ubt98!PT^ZcMOfwMgkYz6+;?CKbvV zQ}Z@s_3JcMPhF&y1?}9uZFIBiPR3g7lf=+XEr9Bl%zRfGcaKb*ZQq5b35ZkR@=JEw zP#iqgh2^#@VA-h)>r`7R-$1_ddGr&oWWV$rx;pkG0Yohp9p@In_p)hKvMo@qIv zcN2t{23&^Nj=Y&gX;*vJ;kjM zHE2`jtjVRRn;=WqVAY&m$z=IoKa{>DgJ;To@OPqNbh=#jiS$WE+O4TZIOv?niWs47 zQfRBG&WGmU~>2O{}h17wXGEnigSIhCkg%N~|e?hG8a- zG!Wv&NMu5z!*80>;c^G9h3n#e>SBt5JpCm0o-03o2u=@v^n+#6Q^r#96J5Q=Dd=>s z(n0{v%yj)=j_Je2`DoyT#yykulwTB+@ejCB{dA7VUnG>4`oE?GFV4sx$5;%9&}yxfz<-wWk|IlA|g&! zN_Emw#w*2GT=f95(%Y1#Viop;Yro3SqUrW~2`Fl?Ten{jAt==a>hx$0$zXN`^7>V_ zG*o7iqeZV)txtHUU2#SDTyU#@paP;_yxp!SAG##cB= zr@LoQg4f~Uy5QM++W`WlbNrDa*U;54`3$T;^YVNSHX4?%z|`B~i7W+kl0wBB`8|(l zAyI6dXL&-Sei0=f#P^m`z=JJ`=W;PPX18HF;5AaB%Zlze`#pz;t#7Bzq0;k8IyvdK=R zBW+4GhjOv+oNq^~#!5(+pDz)Ku{u60bVjyym8Or8L;iqR|qTcxEKTRm^Y%QjFYU=ab+^a|!{!hYc+= z%Qc02=prKpzD+jiiOwzyb(dELO|-iyWzizeLugO!<1(j|3cbR!8Ty1$C|l@cWoi?v zLe<5+(Z-eH++=fX**O-I8^ceYZgiA!!dH+7zfoP-Q+@$>;ab&~cLFg!uOUX7h0r== z`@*QP9tnV1cu1!9pHc43C!{3?-GUBJEzI(&#~vY9MEUcRNR*61)mo!RG>_Yb^rNN7 zR9^bI45V?3Lq`^^BMD!GONuO4NH#v9OP3@s%6*Ha3#S*;f z6JEi)qW#Iq#5BtIXT9Gby|H?NJG}DN#Li82kZ_Rt1=T0Z@U6OAdyf}4OD|Sk^2%-1 zzgvqZ@b6~kL!^sZLO$r{s!3fQ5bHW}8r$uTVS*iw1u8^9{YlPp_^Xm5IN zF|@)ZOReX zB*#tEbWEX~@f)ST|s$oUKS@drycE1tYtdJ9b*(uFTxNZ{n3BI*kF7wXgT6+@PI@vwH7iQS{1T!Nauk>fm8gOLe`->Pi~ z8)3=UL_$OLl2n7QZlHt846nkYFu4V};3LpYA%5VaF#a2#d2g0&ZO~3WA%1XlerVpg zCAlM;(9OqH@`(>Tha{*@R%twB!}1ng4V=^+R`Q{#fkRk)C|suozf-uCXrkIH2SC^C z6wlxR`yS;-U#uu#`OnD%U<41%C4mp>LYLPIbgVO~WsT1if)Y)T*8nUB`2*(B;U_ha1NWv2`GqrZ z3MWWpT3tZ!*N@d*!j3=@K4>X*gX4A^@QPAz24?7u90AXaLiFq=Z$|5p$Ok2|YCX_Z zFgNPiY2r_Bg2BQE!0z=_N*G?%0cNITmAru*!Mws=F+F&Qw!&1?DBN{vSy%IvGRV@1 zS->PARgL^XS!-aZj zi@`~LhWfD!H-L0kNv=Jil9zR0>jZLqu)cLq?$yXVyk%EteKcWbe^qh#spHJPa#?92 za(N(Kw0se^$7nQUQZBet;C_Dj5(2_?TdrXFYwmebq}YGQbN5Ex7M zGSCX~Ey;5AqAzEDNr%p^!cuG?&wIeY&Bm5guVg>8F=!nT%7QZTGR(uGM&IZuMw0V_ zhPiIFWm?H?aw*(v6#uVT@NEzi2h5I$cZ-n0~m$tmwdMTjG*of^Y%1 zW?Y%o*-_iMqEJhXo^!Qo?tGFUn1Mb|urN4_;a)9bila2}5rBS#hZ5wV+t1xbyF1TW zj+~cdjbcMgY$zTOq6;ODaxzNA@PZIXX(-=cT8DBd;9ihfqqtbDr9#gXGtK24BPxjZ z9+Xp>W1(s)->-}VX~BoQv$I|-CBdO`gULrvNL>;@*HvTdh@wyNf}~IB5mFnTitX2i z;>W>tlQyc2)T4Mq+f!(i3#KuK-I8Kj3Wm(UYx?KWWt8DEPR_Jdb9CE~Fjc7Rkh#gh zowNv()KRO@##-C+ig0l!^*ol!Bj%d32_N*~d!|&>{t!k3lc?6VrdlCCb1?qyoR42m zv;4KdwCgvMT*{?tJKa(T?cl|b;k4P>c&O@~g71K5@}ys$)?}WSxD;<5%4wEz7h=+q ztLumn6>leWdDk#*@{=v9p)MsvuJMyf_VEs;pJh?i3z7_W@Q|3p$a}P@MQ-NpMtDUBgH!h4Ia#L&POr4Qw0Tqdw^}gCmQAB z8Dgkzn?V!_@04(cx0~-pqJOpeP1_}@Ml3pCb45EJoghLows9ET13J8kt0;m$6-jO( z4F|p+JFD1NT%4bpn4?&)d+~<360$z5on`eS6{H`S>t`VS$>(D`#mC*XK6zULj1Da# zpV$gw$2Ui{07NiYJQQNK;rOepRxA>soNK~B2;>z;{Ovx`k}(dlOHHuNHfeR}7tmIp zcM}q4*Fq8vSNJYi@4-;}`@bC?nrUy`3jR%HXhs79qWI5;hyTpH5%n-NcKu&j(aGwT z1~{geeq?Jd>>HL+?2`0K8dB2pvTS=LO~tb~vx_<=iN8^rW!y@~lBTAaxHmvVQJSeJ z!cb9ffMdP1lgI=>QJN{XpM4{reRrdIt|v|0-8!p}M*Qw^uV1@Ho-YsNd0!a(os$F* zT0tGHA#0%u0j*%S>kL*73@~7|iP;;!JbWSTA@`#VHv_l_%Z7CgX@>dhg_ zgn0|U)SY~U-E5{QiT@(uPp#1jaz!(_3^Cbz2 z4ZgWWz=PdGCiGznk{^4TBfx_;ZjAHQ>dB4YI}zfEnTbf60lR%=@VWt0yc=fd38Ig* z)Q38#e9^+tA7K}IDG5Z~>JE?J+n%0_-|i2{E*$jb4h?|_^$HRHjVkiyX6@Y+)0C2a zA+eegpT1dUpqQFIwx;!ayQcWQBQTj1n5&h<%Lggt@&tE19Rm~Rijtqw6nmYip_xg0 zO_IYpU304embcWP+**H|Z5~%R*mqq+y{KbTVqugkb)JFSgjVljsR{-c>u+{?moCCl zTL)?85;LXk0HIDC3v*|bB-r_z%zvL6Dp__L*A~Z*o?$rm>cYux&)W=6#+Cb}TF&Kd zdCgz3(ZrNA>-V>$C{a^Y^2F!l_%3lFe$s(IOfLBLEJ4Mcd!y&Ah9r)7q?oc z5L(+S8{AhZ)@3bw0*8(}Xw{94Vmz6FrK&VFrJN;xB96QmqYEibFz|yHgUluA-=+yS}I-+#_Pk zN67-#8W(R^e7f!;i0tXbJgMmJZH%yEwn*-}5ew13D<_FYWnt?{Mv1+MI~u;FN~?~m z{hUnlD1|RkN}c1HQ6l@^WYbHAXPJ^m0te1woe;LDJ}XEJqh1tPf=sD0%b+OuR1aCoP>I>GBn4C24Zu$D)qg=gq;D??5 zUSj%;-Hvk_ffj-+SI{ZCp`gZcNu=L@_N}kCcs?TyMr-37fhy$?a<7lt1`fZw<%$8@B6(Wgo!#!z9z{ab|x`+&;kP!(gfdY}A-GP&4Cbh-S< z1(kmgnMyB2z3ipEj5;4<{(=&<7a>A_Jl`ujUKYV@%k(oD=cD7W@8~5O=R*zdjM_y; zXwme~0wo0aDa~9rDnjF=B}Bbj|DHRQjN|?@(F^=bVFdr!#mwr|c0843k>%~5J|7|v zSY=T)iPU6rEAwrM(xTZwPio%D4y9Z4kL0bMLKvu4yd)0ZJA3<;>a2q~rEfcREn}~1 zCJ~3c?Afvx?3^@+!lnf(kB6YwfsJ*u^y7kZA?VmM%nBmaMspWu?WXq4)jQsq`9EbT zlF2zJ)wXuAF*2u|yd5hNrG>~|i}R&ZyeetTQ!?Hz6xGZZb3W6|vR>Hq=}*m=V=Lsp zUOMxh;ZfP4za~C{Ppn^%rhitvpnu^G{Z#o-r?TdEgSbtK_+~_iD49xM;$}X*mJF02|WBL{SDqK9}p4N!G$3m=x#@T+4QcapM{4j|Q zwO!(hldpuSW#by!zHEP@tzIC|KdD z%BJzQ7Ho1(HemWm`Z8m_D#*`PZ-(R%sZmPrS$aHS#WPjH3EDitxN|DY+ zYC|3S?PQ3NNYau$Qk8f>{w}~xCX;;CE=7;Kp4^xXR8#&^L+y-jep7oO^wnQ840tg1 zuN17QKsfdqZPlB8OzwF+)q#IsmenEmIbRAJHJ$JjxzawKpk8^sBm3iy=*kB%LppNb zhSdk`^n?01FKQ;=iU+McN7Mk0^`KE>mMe1CQ2a_R26_}^$bogFm=2vqJake7x)KN( zYz;gRPL+r4*KD>1U+DU+1jh{mT8#P#(z9^(aDljpeN{mRmx{AZX&hXKXNuxj3x*RrpjvOaZ#`1EqK!$+8=0yv8}=;>f=E?5tGbRUd4%?QL zy$kq6mZeF%k6E1&8nwAYMd!-lRkhQTob$7s`*XqcHs;l~mHV}fx&0I&i!CHaPVSM{ zHdRh7a>hP)t@YTrWm9y zl-ENWSVzlKVvTdWK>)enmGCEw(WYS=FtY{srdE{Z(3~4svwd)ct;`6Y{^qiW+9E@A ztzd?lj5F#k`=E1U-n*1JJc0{x{0q!_tkD<_S6bGsW)^RxGu%Rj^Mvw|R0WP1SqvAI zs(MiAd@Y5x!UKu376&|quQNxir;{Iz(+}3k-GNb29HaQh?K30u=6sXpIc?j0hF{VY zM$Do*>pN)eRljAOgpx7fMfSrnZ7>fi@@>Jh;qxj1#-Vj}JC3E^GCbC(r55_AG>6cq z4ru34FtVuBt)bkX4>ZFWjToyu)VA>IE6hXc+^(3ruUaKRqHnx3z)(GXetm;^0D95s zQ&drwfjhM4*|q=;i5Io0eDf?I{p}qo@7i7abHX5qLu~VDwYf4bmV~-^M_U?DL(+cG z{AyE^a|*73Ft)o5k-p)+GLXj#q01VlJ9#ZJkf|+c%6qfRgVp&6NsU3~F?!uh}HJm73xq>v$h zYoW3wJE6n9P|;{8U<^%UE2wjR4x^G_Nc$J(i)!>;g4`CCh2z^Dth#ah#<`#axDR?F z4>~hnN2%B2ZUuU6j>m1Qjj~5jQSdA&Q#7hOky#=Ue)}7LPJ!8nbZO_0Sw{G>>M7&E zb1dy|0Zi$(ubk`4^XkVI%4WIpe?Bh!D~IjvZs14yHw=aQ8-`N-=P*?Kzi&eRGZ_6Z zT>eis`!Dy3eT3=vt#Lbc+;}i5XJf7zM3QneL{t?w=U<1rk7+z2Cu^|~=~54tAeSYF zsXHsU;nM0dpK>+71yo(NFLV-^Lf7%U?Q$*q{^j04Gl71ya2)^j`nmJ$cmI9eFMjp+ z#)jKmi4lZc<;l>!={@jTm%?!5jS;6;c*Ml55~r6Y?22B^K3bPhKQ(ICc&z%w<4W1= zjTTtz_}IA$%kCqU)h#$!Yq>>2mVG}qYL}!avmCWYV}x4!YEeq)pgTp| zR;+skHuc7YXRLrcbYXt>?@pa{l^2pL>RrZ!22zMmi1ZR?nkaWF*`@XFK4jGh&Em3vn(l z3~^Q9&tM^eV=f^lccCUc9v02z%^n5VV6s$~k0uq5B#Ipd6`M1Kptg^v<2jiNdlAWQ z_MmtNEaeYIHaiuaFQdG&df7miiB5lZkSbg&kxY*Eh|KTW`Tk~VwKC~+-GoYE+pvwc{+nIEizq6!xP>7ZQ(S2%48l$Y98L zvs7s<&0ArXqOb*GdLH0>Yq-f!{I~e~Z@FUIPm?jzqFZvz9VeZLYNGO}>Vh<=!Er7W zS!X6RF^et7)IM1pq57z*^hP5w7HKSDd8jHX!*gkKrGc-GssrNu5H%7-cNE{h$!aEQK3g*qy;= z)}pxO8;}nLVYm_24@iEs8)R7i;Th0n4->&$8m6(LKCRd(yn7KY%QHu_f=*#e`H^U( z{u!`9JaRD?Z?23fEXrjx>A@+a!y-_oaDB)o@2s{2%A97-ctFfrN0cXQ@6aGH`X~Nr z144?qk;MzDU-cgQOLfT3-ZR#hKmYtKG*iGf4ZJ`|`9!^SkBDUUSJCba)>mM!)k~(z zdjUqB`)~!UObMHB1b$UItM$<0kwlqHH;c z=)+~bkOcIT7vI0Iy(wD)vsg9|oi##%Rgrq`Ek;pN)}lbpz`iv{F4K*{ZZ?Zjixxxr zY|SPl2NsXH+5pimj+MvbZ_+HrfvdC13|9Zs)Y=nW$z<0mhl}%irBSm5T3ZrN#2AhY z_ZrTmS(L`U#y}VZ@~QL9wUS6AnU*7LWS02Xyz`b>%rTml#Wb0yr>@c(Ym*40g;P{V zjV1XSHdU>oY!&Jh7MzhzUV8(9E+yl5UJYga>=0Ldjwtc`5!1>LxaB-kVW;IlSPs+0 zUBx=m8OKVp<`frNvMK>WMO(iKY%PuvqD+PK*vP6f?_o!O)MCW5Ic zv(%f5PLHyOJ2h@Yn_to@54Yq;fdoy40&sbe3A$4uUXHsHP_~K}h#)p&TyOx(~JE?y(IBAQKl}~VQjVC-c6oZwmESL;`Xth?2)-b6ImNcJi z;w|`Q*k?`L(+Dp}t(FocvzWB(%~9$EAB6_J6CrA}hMj-Vy*6iA$FdV}!lvk%6}M)4 zTf<)EbXr9^hveAav1yA?>O0aNEpv0&rju{(Gt|dP=AP%)uQm~OE7@+wEhILrRLt&E zoEsF^nz>4yK1|EOU*kM+9317S;+bb7?TJM2UUpc!%sDp}7!<`i=W!ot8*C&fpj>mk#qt~GCeqcy)?W6sl>eUnR%yCBR&Ow-rc|q;lhnI+f-%`6Xf)% zIYZru;27%vA{Qi2=J`PQC<28;tFx(V^sgXf>)8WNxxQwT14M9I6- z+V0@tiCiDkv`7r-06sJS8@s|Lf>mV+8h}SPT4ZGPSMaFK7_SMXH$3KN7b2V?iV-jA zh1!Z>2tv^HVbHnNUAf-wQW#zMV(h8=3x2Swd|-%AczEIWLcm~EAu7rc3s%56b;7ME zj}$pe#fc^314Mb9i)xH^_#({)tTD4hsoz!7XcHUh9*G|}?k=D?9LBkTm2?fgaIG(%%$DL#}a-_990rQBU+M;jrf zCcvgM`+oyZmsUqc?lly9axZfO)02l$TMS#I+jHYY`Uk!gtDv|@GBQ||uaG^n*QR3Q z@tV?D;R;KmkxSDQh<2DkDC1?m?jTvf2i^T;+}aYhzL?ymNZmdns2e)}2V>tDCRw{= zTV3q3ZQDkdZQHi3?y{@8Y@1!SZQHi(y7|qSx$~Vl=iX<2`@y3eSYpsBV zI`Q-6;)B=p(ZbX55C*pu1C&yqS|@Pytis3$VDux0kxKK}2tO&GC;cH~759o?W2V)2 z)`;U(nCHBE!-maQz%z#zoRNpJR+GmJ!3N^@cA>0EGg?OtgM_h|j1X=!4N%!`g~%hdI3%yz&wq4rYChPIGnSg{H%i>96! z-(@qsCOfnz7ozXoUXzfzDmr>gg$5Z1DK$z#;wn9nnfJhy6T5-oi9fT^_CY%VrL?l} zGvnrMZP_P|XC$*}{V}b^|Hc38YaZQESOWqA1|tiXKtIxxiQ%Zthz?_wfx@<8I{XUW z+LH%eO9RxR_)8gia6-1>ZjZB2(=`?uuX|MkX082Dz*=ep%hMwK$TVTyr2*|gDy&QOWu zorR#*(SDS{S|DzOU$<-I#JTKxj#@0(__e&GRz4NuZZLUS8}$w+$QBgWMMaKge*2-) zrm62RUyB?YSUCWTiP_j-thgG>#(ZEN+~bMuqT~i3;Ri`l${s0OCvCM>sqtIX?Cy`8 zm)MRz-s^YOw>9`aR#J^tJz6$S-et%elmR2iuSqMd(gr6a#gA_+=N(I6%Cc+-mg$?_1>PlK zbgD2`hLZ?z4S~uhJf=rraLBL?H#c$cXyqt{u^?#2vX2sFb z^EU-9jmp{IZ~^ii@+7ogf!n_QawvItcLiC}w^$~vgEi(mX79UwDdBg`IlF42E5lWE zbSibqoIx*0>WWMT{Z_NadHkSg8{YW4*mZ@6!>VP>ey}2PuGwo%>W7FwVv7R!OD32n zW6ArEJX8g_aIxkbBl^YeTy5mhl1kFGI#n>%3hI>b(^`1uh}2+>kKJh0NUC|1&(l)D zh3Barl&yHRG+Le2#~u>KoY-#GSF>v)>xsEp%zgpq4;V6upzm3>V&yk^AD}uIF{vIn zRN-^d4(Sk6ioqcK@EObsAi#Z-u&Hh#kZdv1rjm4u=$2QF<6$mgJ4BE0yefFI zT7HWn?f668n!;x>!CrbdA~lDfjX?)315k1fMR~lG)|X_o()w|NX&iYUTKxI2TLl|r z{&TWcBxP>*;|XSZ1GkL&lSg?XL9rR4Ub&4&03kf};+6$F)%2rsI%9W_i_P|P%Z^b@ zDHH2LV*jB@Izq0~E4F^j04+C|SFiV8{!bth%bz(KfCg42^ zGz5P7xor$)I4VX}Cf6|DqZ$-hG7(}91tg#AknfMLFozF1-R~KS3&5I0GNb`P1+hIB z?OPmW8md3RB6v#N{4S5jm@$WTT{Sg{rVEs*)vA^CQLx?XrMKM@*gcB3mk@j#l0(~2 z9I=(Xh8)bcR(@8=&9sl1C?1}w(z+FA2`Z^NXw1t(!rpYH3(gf7&m=mm3+-sls8vRq z#E(Os4ZNSDdxRo&`NiRpo)Ai|7^GziBL6s@;1DZqlN@P_rfv4Ce1={V2BI~@(;N`A zMqjHDayBZ);7{j>)-eo~ZwBHz0eMGRu`43F`@I0g!%s~ANs>Vum~RicKT1sUXnL=gOG zDR`d=#>s?m+Af1fiaxYxSx{c5@u%@gvoHf#s6g>u57#@#a2~fNvb%uTYPfBoT_$~a^w96(}#d;-wELAoaiZCbM zxY4fKlS6-l1!b1!yra|`LOQoJB))=CxUAYqFcTDThhA?d}6FD$gYlk**!# zD=!KW>>tg1EtmSejwz{usaTPgyQm~o+NDg`MvNo)*2eWX*qAQ)4_I?Pl__?+UL>zU zvoT(dQ)pe9z1y}qa^fi-NawtuXXM>*o6Al~8~$6e>l*vX)3pB_2NFKR#2f&zqbDp7 z5aGX%gMYRH3R1Q3LS91k6-#2tzadzwbwGd{Z~z+fBD5iJ6bz4o1Rj#7cBL|x8k%jO z{cW0%iYUcCODdCIB(++gAsK(^OkY5tbWY;)>IeTp{{d~Y#hpaDa-5r#&Ha?+G{tn~ zb(#A1=WG1~q1*ReXb4CcR7gFcFK*I6Lr8bXLt9>9IybMR&%ZK15Pg4p_(v5Sya_70 ziuUYG@EBKKbKYLWbDZ)|jXpJJZ&bB|>%8bcJ7>l2>hXuf-h5Bm+ zHZ55e9(Sg>G@8a`P@3e2(YWbpKayoLQ}ar?bOh2hs89=v+ifONL~;q(d^X$7qfw=; zENCt`J*+G;dV_85dL3Tm5qz2K4m$dvUXh>H*6A@*)DSZ2og!!0GMoCPTbcd!h z@fRl3f;{F%##~e|?vw6>4VLOJXrgF2O{)k7={TiDIE=(Dq*Qy@oTM*zDr{&ElSiYM zp<=R4r36J69aTWU+R9Hfd$H5gWmJ?V){KU3!FGyE(^@i!wFjeZHzi@5dLM387u=ld zDuI1Y9aR$wW>s#I{2!yLDaVkbP0&*0Rw%6bi(LtieJQ4(1V!z!ec zxPd)Ro0iU%RP#L|_l?KE=8&DRHK>jyVOYvhGeH+Dg_E%lgA(HtS6e$v%D7I;JSA2x zJyAuin-tvpN9g7>R_VAk2y;z??3BAp?u`h-AVDA;hP#m+Ie`7qbROGh%_UTW#R8yfGp<`u zT0}L)#f%(XEE)^iXVkO8^cvjflS zqgCxM310)JQde*o>fUl#>ZVeKsgO|j#uKGi)nF_ur&_f+8#C0&TfHnfsLOL|l(2qn zzdv^wdTi|o>$q(G;+tkTKrC4rE)BY?U`NHrct*gVx&Fq2&`!3htkZEOfODxftr4Te zoseFuag=IL1Nmq45nu|G#!^@0vYG5IueVyabw#q#aMxI9byjs99WGL*y)AKSaV(zx z_`(}GNM*1y<}4H9wYYSFJyg9J)H?v((!TfFaWx(sU*fU823wPgN}sS|an>&UvI;9B(IW(V)zPBm!iHD} z#^w74Lpmu7Q-GzlVS%*T-z*?q9;ZE1rs0ART4jnba~>D}G#opcQ=0H)af6HcoRn+b z<2rB{evcd1C9+1D2J<8wZ*NxIgjZtv5GLmCgt?t)h#_#ke{c+R6mv6))J@*}Y25ef z&~LoA&qL-#o=tcfhjH{wqDJ;~-TG^?2bCf~s0k4Rr!xwz%Aef_LeAklxE=Yzv|3jf zgD0G~)e9wr@)BCjlY84wz?$NS8KC9I$wf(T&+79JjF#n?BTI)Oub%4wiOcqw+R`R_q<`dcuoF z%~hKeL&tDFFYqCY)LkC&5y(k7TTrD>35rIAx}tH4k!g9bwYVJ>Vdir4F$T*wC@$08 z9Vo*Q0>*RcvK##h>MGUhA9xix+?c1wc6xJhn)^9;@BE6i*Rl8VQdstnLOP1mq$2;!bfASHmiW7|=fA{k$rs^-8n{D6_ z!O0=_K}HvcZJLSOC6z-L^pl3Gg>8-rU#Sp1VHMqgXPE@9x&IHe;K3;!^SQLDP1Gk&szPtk| z!gP;D7|#y~yVQ?sOFiT*V(Z-}5w1H6Q_U5JM#iW16yZiFRP1Re z6d4#47#NzEm};1qRP9}1;S?AECZC5?6r)p;GIW%UGW3$tBN7WTlOy|7R1?%A<1!8Z zWcm5P6(|@=;*K&3_$9aiP>2C|H*~SEHl}qnF*32RcmCVYu#s!C?PGvhf1vgQ({MEQ z0-#j>--RMe{&5&$0wkE87$5Ic5_O3gm&0wuE-r3wCp?G1zA70H{;-u#8CM~=RwB~( zn~C`<6feUh$bdO1%&N3!qbu6nGRd5`MM1E_qrbKh-8UYp5Bn)+3H>W^BhAn;{BMii zQ6h=TvFrK)^wKK>Ii6gKj}shWFYof%+9iCj?ME4sR7F+EI)n8FL{{PKEFvB65==*@ ztYjjVTJCuAFf8I~yB-pN_PJtqH&j$`#<<`CruB zL=_u3WB~-;t3q)iNn0eU(mFTih<4nOAb>1#WtBpLi(I)^zeYIHtkMGXCMx+I zxn4BT0V=+JPzPeY=!gAL9H~Iu%!rH0-S@IcG%~=tB#6 z3?WE7GAfJ{>GE{?Cn3T!QE}GK9b*EdSJ02&x@t|}JrL{^wrM@w^&})o;&q816M5`} zv)GB;AU7`haa1_vGQ}a$!m-zkV(+M>q!vI0Swo18{;<>GYZw7-V-`G#FZ z;+`vsBihuCk1RFz1IPbPX8$W|nDk6yiU8Si40!zy{^nmv_P1=2H*j<^as01|W>BQS zU)H`NU*-*((5?rqp;kgu@+hDpJ;?p8CA1d65)bxtJikJal(bvzdGGk}O*hXz+<}J? zLcR+L2OeA7Hg4Ngrc@8htV!xzT1}8!;I6q4U&S$O9SdTrot<`XEF=(`1{T&NmQ>K7 zMhGtK9(g1p@`t)<)=eZjN8=Kn#0pC2gzXjXcadjHMc_pfV(@^3541)LC1fY~k2zn&2PdaW`RPEHoKW^(p_b=LxpW&kF?v&nzb z1`@60=JZj9zNXk(E6D5D}(@k4Oi@$e2^M%grhlEuRwVGjDDay$Qpj z`_X-Y_!4e-Y*GVgF==F0ow5MlTTAsnKR;h#b0TF>AyJe`6r|%==oiwd6xDy5ky6qQ z)}Rd0f)8xoNo)1jj59p;ChIv4Eo7z*{m2yXq6)lJrnziw9jn%Ez|A-2Xg4@1)ET2u zIX8`u5M4m=+-6?`S;?VDFJkEMf+=q?0D7?rRv)mH=gptBFJGuQo21rlIyP>%ymGWk z=PsJ>>q~i>EN~{zO0TklBIe(8i>xkd=+U@;C{SdQ`E03*KXmWm4v#DEJi_-F+3lrR z;0al0yXA&axWr)U%1VZ@(83WozZbaogIoGYpl!5vz@Tz5?u36m;N=*f0UY$ssXR!q zWj~U)qW9Q9Fg9UW?|XPnelikeqa9R^Gk77PgEyEqW$1j=P@L z*ndO!fwPeq_7J_H1Sx>#L$EO_;MfYj{lKuD8ZrUtgQLUUEhvaXA$)-<61v`C=qUhI zioV&KR#l50fn!-2VT`aMv|LycLOFPT{rRSRGTBMc)A`Cl%K&4KIgMf}G%Qpb2@cB* zw8obt-BI3q8Lab!O<#zeaz{P-lI2l`2@qrjD+Qy)^VKks5&SeT(I)i?&Kf59{F`Rw zuh7Q>SQNwqLO%cu2lzcJ7eR*3!g}U)9=EQ}js-q{d%h!wl6X3%H0Z2^8f&^H;yqti4z6TNWc& zDUU8YV(ZHA*34HHaj#C43PFZq7a>=PMmj4+?C4&l=Y-W1D#1VYvJ1~K%$&g-o*-heAgLXXIGRhU zufonwl1R<@Kc8dPKkb`i5P9VFT_NOiRA=#tM0WX2Zut)_ zLjAlJS1&nnrL8x8!o$G+*z|kmgv4DMjvfnvH)7s$X=-nQC3(eU!ioQwIkaXrl+58 z@v)uj$7>i`^#+Xu%21!F#AuX|6lD-uelN9ggShOX&ZIN+G#y5T0q+RL*(T(EP)(nP744-ML= z+Rs3|2`L4I;b=WHwvKX_AD56GU+z92_Q9D*P|HjPYa$yW0o|NO{>4B1Uvq!T;g_N- zAbNf%J0QBo1cL@iahigvWJ9~A4-glDJEK?>9*+GI6)I~UIWi>7ybj#%Po}yT6d6Li z^AGh(W{NJwz#a~Qs!IvGKjqYir%cY1+8(5lFgGvl(nhFHc7H2^A(P}yeOa_;%+bh` zcql{#E$kdu?yhRNS$iE@F8!9E5NISAlyeuOhRD)&xMf0gz^J927u5aK|P- z>B%*9vSHy?L_q)OD>4+P;^tz4T>d(rqGI7Qp@@@EQ-v9w-;n;7N05{)V4c7}&Y^!`kH3}Q z4RtMV6gAARY~y$hG7uSbU|4hRMn97Dv0$Le@1jDIq&DKy{D$FOjqw{NruxivljBGw zP4iM(4Nrz^^~;{QBD7TVrb6PB=B$<-e9!0QeE8lcZLdDeb?Gv$ePllO2jgy&FSbW* zSDjDUV^=`S(Oo0;k(Idvzh}aXkfO)F6AqB?wWqYJw-1wOn5!{-ghaHb^v|B^92LmQ9QZj zHA&X)fd%B$^+TQaM@FPXM$$DdW|Vl)4bM-#?Slb^qUX1`$Yh6Lhc4>9J$I4ba->f3 z9CeGO>T!W3w(){M{OJ+?9!MK68KovK#k9TSX#R?++W4A+N>W8nnk**6AB)e;rev=$ zN_+(?(YEX;vsZ{EkEGw%J#iJYgR8A}p+iW;c@V>Z1&K->wI>!x-+!0*pn|{f=XA7J zfjw88LeeJgs4YI?&dHkBL|PRX`ULOIZlnniTUgo-k`2O2RXx4FC76;K^|ZC6WOAEw zz~V0bZ29xe=!#Xk?*b{sjw+^8l0Koy+e7HjWXgmPa4sITz+$VP!YlJ$eyfi3^6gGx6jZLpbUzX;!Z6K}aoc!1CRi zB6Lhwt%-GMcUW;Yiy6Y7hX(2oksbsi;Z6k*=;y;1!taBcCNBXkhuVPTi+1N*z*}bf z`R=&hH*Ck5oWz>FR~>MO$3dbDSJ!y|wrff-H$y(5KadrA_PR|rR>jS=*9&J*ykWLr z-1Z^QOxE=!6I z%Bozo)mW7#2Hd$-`hzg=F@6*cNz^$#BbGlIf${ZV1ADc}sNl=B72g`41|F7JtZ^BT z+y}nqn3Ug`2scS_{MjykPW2~*k$i6PhvvxJCW;n!SK5B8Rpm41fCEdy=ea-4F`rN5 zF>ClKp#4?}pI7eR#6U|}t`DA!GQJB7nT$HVV*{qPjIRU1Ou3W;I^pCt54o|ZHvWaH zooFx9L%#yv)!P;^er5LCU$5@qXMhJ-*T5Ah8|}byGNU5oMp3V)yR;hWJKojJEregX z<1UPt%&~=5OuP(|B{ty);vLdoe7o^?`tkQa7zoXKAW6D@lc+FTzucotaOfJ!(Bm zHE8f8j@6||lH`y2<&hP}Q1wr(=6ze0D6NRL{7QaE1=nTAzqjIeD}Be&@#_d*dyurz z&L7xo-D9!dS`i>^GaIPArR@r=N#-ppIh!UBcb!N*?nLUO+*%C>_dCF1IH)q>5oT(t zjQo{AoDB;mWL;3&;vTt?;bvJSj>^Gq4Jrh}S}D>G)+b!>oRDWI?c_d77$kF5ms{Gx zak*>~*5AvaB-Xl)IgdZ^Cupv6HxQ0 zM(KPaDpPsPOd)e)aFw}|=tfzg@J1P8oJx2ZBY=g4>_G(Hkgld(u&~jN((eJ}5@b1} zI(P7j443AZj*I@%q!$JQ2?DZV47U!|Tt6_;tlb`mSP3 z74DE4#|1FMDqwYbT4P6#wSI%s?*wDc>)MR$4z9ZtJg04+CTUds>1JSDwI}=vpRoRR zLqx(Tvf34CvkTMOPkoH~$CG~fSZb;(2S4Q6Vpe9G83V={hwQ>acu+MCX)@0i>Vd`% z4I8Ye+7&Kcbh(*bN1etKmrpN)v|=eI+$oD=zzii6nP&w|kn2Y-f!(v<aE zKmOz#{6PZB(8zD={il`RO6D}v(@mN_66KXUAEefgg|;VmBfP?UrfB$&zaRw7oanna zkNmVGz4Vhd!vZSnp1(&_5^t;eSv6O771BloJAHi=Pnn+aa6y(e2iiE97uZ{evzQ^8 z*lN@ZYx<-hLXP^IuYLGf<01O*>nDp0fo;;Iyt`JADrxt7-jEF(vv_btyp6CT8=@5t zm`I0lW+2+_xj2CRL|40kcYysuyYeiGihGe&a)yilqP}5h+^)m8$=mzrUe`$(?BIY> zfF7-V10Gu0CkWF)wz04&hhI>es0NS7d`cnT`4y8K!wUAKv$H09fa>KeNQvwUNDT1zn}_*RHykC$CD%*h7vRCQ&Z z4&N-!L>(@8i?K$l5)13n0%VPPV`iG7Q$2{1T3JypLSvN%1kX73goBIOEmg=Uf$9e? zm}g>JFu}EQKH>|K!)m9teoCmTc`y2Ll}msZYyy0Pkqjeid66>DP_?C{KCw94lHvLW z-+X!2YSm70s833lH0o+|A%Xwsw`@8lE3ia0n_Dve;LC7@I+i~@%$lD|3fNf&R6ob6 z@iGfx^OC4s`$|vO!0jTWwVpX;X^EqJF{i324I>N=f@u+rTN+xJGGR0LsCQc;iFD=F zbZJrgOpS;04o^wP7HF5QBaJ$KJgS2V4u02ViWD=6+7rcu`uc&MOoyf%ZBU|gQZkUg z<}ax>*Fo?d*77Ia)+{(`X45{a8>Bi$u-0BWSteyp#GJnTs?&k&<0NeHA$Qb3;SAJK zl}H*~eyD-0qHI3SEcn`_7d zq@YRsFdBig+k490BZSQwW)j}~GvM7x>2ymO4zakaHZ!q6C2{fz^NvvD8+e%7?BQBH z-}%B{oROo2+|6g%#+XmyyIJrK_(uEbg%MHlBn3^!&hWi+9c0iqM69enep#5FvV_^r z?Yr(k*5FbG{==#CGI1zU0Wk{V?UGhBBfv9HP9A-AmcJmL^f4S zY3E2$WQa&n#WRQ5DOqty_Pu z-NWQGCR^Hnu^Vo2rm`-M>zzf|uMCUd1X0{wISJL2Pp=AO5 zF@(50!g|SYw3n<_VP0T~`WUjtY**6Npphr5bD%i3#*p7h8$#;XTLJAt5J-x~O1~`z z`2C~P4%XSI(JbrEmVMEwqdsa^aqXWg;A6KBn^jDxTl!}Q!^WhprL$kb(Iqq zUS`i$tIPs#hdE-zAaMGoxcG?Z;RO2L0Y|gcjV_)FFo|e)MtTl`msLTwq>po$`H6_U zhdWK97~M>idl9GE_WgobQkK_P85H_0jN?s3O)+m&68B`_;FnbZ3W*Qm++ghSs7|T4b7m~VVV%j0gl`Iw!?+-9#Lsb!j3O%fSTVuK z37V>qM81D+Atl};23`TqEAfEkQDpz$-1$e__>X2jN>xh@Sq)I6sj@< ziJ^66GSmW9c%F7eu6&_t$UaLXF4KweZecS1ZiHPWy-$e_7`jVk74OS*!z=l#(CQ^K zW-ke|g^&0o=hn+4uh-8lUh0>!VIXXnQXwKr>`94+2~<;+`k z$|}QZ>#pm2g}8k*;)`@EnM~ZQtci%_$ink9t6`HP{gn}P1==;WDAld3JX?k%^GcTU za>m|CH|UsyFhyJBwG5=`6562hkVRMQ=_ron-Vlm$4bG^GFz|Jh5mM{J1`!!hAr~8F^w> z^YhQ=c|bFn_6~9X$v(30v$5IX;#Nl-XXRPgs{g_~RS*znH^6Vhe}8>T?aMA|qfnWO zQpf(wr^PfygfM+m2u!9}F|frrZPBQ!dh(varsYo!tCV)WA(Wn^_t=WR_G7cQU`AGx zrK^B6<}9+$w;$vra)QWMKf_Tnqg93AMVZ6Qd=q6rdB{;ZhsoT zWy9QhnpEnc@Dauz4!8gq zqDanAX#$^vf-4~ZqUJtSe?SO+Hmb?)l2#}v(8}2+P{ZZuhlib0$3G0|a5?JR>QgUUP$HTE5hb`h>imq#7P+Y*-UVLm@9km|V# zoigziFt$bxgQMwqKKhd!c--&ciywIED>faY3zHLrA{V#IA)!mq!FXxf?1coGK~N(b zjwu*@2B1^(bzFVBJO`4EJ$=it!a0kbgUvPL;Er(0io{W4G7Bkqh)=g)uS|l0YfD}f zaCJwY7vR-D=P9M68`cmtmQ^!F-$lt@0S|9G7cHgT13A0xMv)HmH#Z<4{~iYo_VOD{ z5!kU+>mUOvHouw+-y?*cNlUlDwD#;6ZvAIc$YcwG&qKZFh>EtM(Eda+w)E$HcfZyB zG*$<*ae_ApE%gxWx%O^~XMnRSNLv!y`g99F(J_m)spJAc95P|_joOIoru%atbw z9PYgkcE*8x#)-W{>96KDl&74iW<#wrK)1s zxzU{`rW5af+dT6Z@_1dG<}CtDMT`EGVEXSL_5D9)Z;6UJe-TW7)M?bY%E;8G?Yc!$ zic;F5=#dba^P~7f#qvC}Nd#XEo2r_UlgfR_`B2^W0QjXU?RAi$>f&{G_Lu8Fp0qDp z?vAdm%z#3kcZmaJ@afooB=A@>8_N~O9Yzu=ZCEikM>UgU+{%>pPvmSNzGk@*jnc5~ z(Z#H4OL^gw>)gqZ!9X|3i4LAdp9vo)?F9QCR3##{BHoZ73Uk^Ha={2rc*TBijfKH- z=$cZQdc<5%*$kVo|{+bL3 zEoU&tq*YPR)^y-SISeQNQ)YZ9v>Hm4O=J)lf(y=Yu1ao&zj#5GVGxyj%V%vl9}dw< zO;@NRd4qe@Et}E@Q;SChBR2QPKll1{*5*jT*<$$5TywvC77vt=1=0xZ46>_17YzbiBoDffH(1_qFP7v2SVhZmA_7JDB50t#C39 z8V<9(E?bVWI<7d6MzcS^w!XmZ**{AO!~DZNU)pgr=yY1 zT@!AapE;yg&hmj*g{I3vd## zx+d%^O?d%%?Dba|l~X6ZOW|>FPsrjPjn-h4swysH!RNJUWofC?K(^0uHrBPrH5#W> zMn8^@USzjUucqo%+5&))Dnnw`5l1mp>roaA99Nkk4keZl2wAF7oa(!x?@8uGWzc5Q zM}g`}zf-D@B6lVFYWmmJ8a+_%z8g$C7Ww~PD9&jki08NY!b!fK288R;E?e3Z+Pk{is%HxQU`xu9+y5 zq?DWJD7kKp(B2J$t5Ij8-)?g!T9_n<&0L8F5-D0dp>9!Qnl#E{eDtkNo#lw6rMJG$ z9Gz_Z&a_6ie?;F1Y^6I$Mg9_sml@-z6t!YLr=ml<6{^U~UIbZUUa_zy>fBtR3Rpig zc1kLSJj!rEJILzL^uE1mQ}hjMCkA|ZlWVC9T-#=~ip%McP%6QscEGlYLuUxDUC=aX zCK@}@!_@~@z;70I+Hp5#Tq4h#d4r!$Np1KhXkAGlY$ap7IZ9DY})&(xoTyle8^dBXbQUhPE6ehWHrfMh&0=d<)E2+pxvWo=@`^ zIk@;-$}a4zJmK;rnaC)^a1_a_ie7OE*|hYEq1<6EG>r}!XI9+(j>oe!fVBG%7d}?U z#ja?T@`XO(;q~fe2CfFm-g8FbVD;O7y9c;J)k0>#q7z-%oMy4l+ zW>V~Y?s`NoXkBeHlXg&u*8B7)B%alfYcCriYwFQWeZ6Qre!4timF`d$=YN~_fPM5Kc8P;B-WIDrg^-j=|{Szq6(TC)oa!V7y zLmMFN1&0lM`+TC$7}on;!51{d^&M`UW ztI$U4S&}_R?G;2sI)g4)uS-t}sbnRoXVwM!&vi3GfYsU?fSI5Hn2GCOJ5IpPZ%Y#+ z=l@;;{XiY_r#^RJSr?s1) z4b@ve?p5(@YTD-<%79-%w)Iv@!Nf+6F4F1`&t~S{b4!B3fl-!~58a~Uj~d4-xRt`k zsmGHs$D~Wr&+DWK$cy07NH@_z(Ku8gdSN989efXqpreBSw$I%17RdxoE<5C^N&9sk!s2b9*#}#v@O@Hgm z2|U7Gs*@hu1JO$H(Mk)%buh~*>paY&Z|_AKf-?cz6jlT-v6 zF>l9?C6EBRpV2&c1~{1$VeSA|G7T(VqyzZr&G>vm87oBq2S%H0D+RbZm}Z`t5Hf$C zFn7X*;R_D^ z#Ug0tYczRP$s!6w<27;5Mw0QT3uNO5xY($|*-DoR1cq8H9l}_^O(=g5jLnbU5*SLx zGpjfy(NPyjL`^Oln_$uI6(aEh(iS4G=$%0;n39C(iw79RlXG>W&8;R1h;oVaODw2nw^v{~`j(1K8$ z5pHKrj2wJhMfw0Sos}kyOS48Dw_~=ka$0ZPb!9=_FhfOx9NpMxd80!a-$dKOmOGDW zi$G74Sd(-u8c!%35lL|GkyxZdlYUCML{V-Ovq{g}SXea9t`pYM^ioot&1_(85oVZ6 zUhCw#HkfCg7mRT3|>99{swr3FlA@_$RnE?714^o;vps4j4}u=PfUAd zMmV3j;Rogci^f!ms$Z;gqiy7>soQwo7clLNJ4=JAyrz;=*Yhe8q7*$Du970BXW89Xyq92M4GSkNS-6uVN~Y4r7iG>{OyW=R?@DmRoi9GS^QtbP zFy2DB`|uZTv8|ow|Jcz6?C=10U$*_l2oWiacRwyoLafS!EO%Lv8N-*U8V+2<_~eEA zgPG-klSM19k%(%;3YM|>F||hE4>7GMA(GaOvZBrE{$t|Hvg(C2^PEsi4+)w#P4jE2XDi2SBm1?6NiSkOp-IT<|r}L9)4tLI_KJ*GKhv16IV}An+Jyx z=Mk`vCXkt-qg|ah5=GD;g5gZQugsv!#)$@ zkE=6=6W9u9VWiGjr|MgyF<&XcKX&S3oN{c{jt-*1HHaQgY({yjZiWW97rha^TxZy< z2%-5X;0EBP>(Y9|x*603*Pz-eMF5*#4M;F`QjTBH>rrO$r3iz5 z?_nHysyjnizhZQMXo1gz7b{p`yZ8Q78^ zFJ3&CzM9fzAqb6ac}@00d*zjW`)TBzL=s$M`X*0{z8$pkd2@#4CGyKEhzqQR!7*Lo@mhw`yNEE6~+nF3p;Qp;x#-C)N5qQD)z#rmZ#)g*~Nk z)#HPdF_V$0wlJ4f3HFy&fTB#7Iq|HwGdd#P3k=p3dcpfCfn$O)C7;y;;J4Za_;+DEH%|8nKwnWcD zBgHX)JrDRqtn(hC+?fV5QVpv1^3=t2!q~AVwMBXohuW@6p`!h>>C58%sth4+Baw|u zh&>N1`t(FHKv(P+@nT$Mvcl){&d%Y5dx|&jkUxjpUO3ii1*^l$zCE*>59`AvAja%`Bfry-`?(Oo?5wY|b4YM0lC?*o7_G$QC~QwKslQTWac z#;%`sWIt8-mVa1|2KH=u!^ukn-3xyQcm4@|+Ra&~nNBi0F81BZT$XgH@$2h2wk2W% znpo1OZuQ1N>bX52II+lsnQ`WVUxmZ?4fR_f0243_m`mbc3`?iy*HBJI)p2 z`GQ{`uS;@;e1COn-vgE2D!>EheLBCF-+ok-x5X8Cu>4H}98dH^O(VlqQwE>jlLcs> zNG`aSgDNHnH8zWw?h!tye^aN|%>@k;h`Z_H6*py3hHO^6PE1-GSbkhG%wg;+vVo&dc)3~9&` zPtZtJyCqCdrFUIEt%Gs_?J``ycD16pKm^bZn>4xq3i>9{b`Ri6yH|K>kfC; zI5l&P)4NHPR)*R0DUcyB4!|2cir(Y1&Bsn3X8v4D(#QW8Dtv@D)CCO zadQC85Zy=Rkrhm9&csynbm>B_nwMTFah9ETdNcLU@J{haekA|9*DA2pY&A|FS*L!*O+>@Q$00FeL+2lg2NWLITxH5 z0l;yj=vQWI@q~jVn~+5MG!mV@Y`gE958tV#UcO#56hn>b69 zM;lq+P@MW=cIvIXkQmKS$*7l|}AW%6zETA2b`qD*cL z(=k4-4=t6FzQo#uMXVwF{4HvE%%tGbiOlO)Q3Y6D<5W$ z9pm>%TBUI99MC`N9S$crpOCr4sWJHP)$Zg#NXa~j?WeVo03P3}_w%##A@F|Bjo-nNxJZX%lbcyQtG8sO zWKHes>38e-!hu1$6VvY+W-z?<942r=i&i<88UGWdQHuMQjWC-rs$7xE<_-PNgC z_aIqBfG^4puRkogKc%I-rLIVF=M8jCh?C4!M|Q=_kO&3gwwjv$ay{FUDs?k7xr%jD zHreor1+#e1_;6|2wGPtz$``x}nzWQFj8V&Wm8Tu#oaqM<$BLh+Xis=Tt+bzEpC}w) z_c&qJ6u&eWHDb<>p;%F_>|`0p6kXYpw0B_3sIT@!=fWHH`M{FYdkF}*CxT|`v%pvx z#F#^4tdS0|O9M1#db%MF(5Opy;i( zL(Pc2aM4*f_Bme@o{xMrsO=)&>YKQw+)P-`FwEHR4vjU>#9~X7ElQ#sRMjR^Cd)wl zg^67Bgn9CK=WP%Ar>T4J!}DcLDe z=ehSmTp##KyQ78cmArL=IjOD6+n@jHCbOatm)#4l$t5YV?q-J86T&;>lEyK&9(XLh zr{kPuX+P8LN%rd%8&&Ia)iKX_%=j`Mr*)c)cO1`-B$XBvoT3yQCDKA>8F0KL$GpHL zPe?6dkE&T+VX=uJOjXyrq$BQ`a8H@wN1%0nw4qBI$2zBx)ID^6;Ux+? zu{?X$_1hoz9d^jkDJpT-N6+HDNo%^MQ2~yqsSBJj4@5;|1@w+BE04#@Jo4I63<~?O?ok%g%vQakTJKpMsk&oeVES1>cnaF7ZkFpqN6lx` zzD+YhR%wq2DP0fJCNC}CXK`g{AA6*}!O}%#0!Tdho4ooh&a5&{xtcFmjO4%Kj$f(1 zTk||{u|*?tAT{{<)?PmD_$JVA;dw;UF+x~|!q-EE*Oy?gFIlB*^``@ob2VL?rogtP z0M34@?2$;}n;^OAV2?o|zHg`+@Adk+&@Syd!rS zWvW$e5w{onua4sp+jHuJ&olMz#V53Z5y-FkcJDz>Wk%_J>COk5<0ya*aZLZl9LH}A zJhJ`Q-n9K+c8=0`FWE^x^xn4Fa7PDUc;v2+us(dSaoIUR4D#QQh91R!${|j{)=Zy1 zG;hqgdhSklM-VKL6HNC3&B(p1B)2Nshe7)F=-HBe=8o%OhK1MN*Gq6dBuPvqDRVJ{ z;zVNY?wSB%W0s^OMR_HL(Ws)va7eWGF*MWx<1wG7hZ}o=B62D?i|&0b14_7UG287YDr%?aYMMpeCkY1i`b+H!J9sqrvKc#Y6c8At@QiLSwj)@ifz~Z|c$lOMA@?cPqFRmZ%_>bz2X4(B=`^3;MDjsEeAO=? zSoD&+L>A|fGt7+6kF2@LqhL06sD%|~YsIe=EcWqy{e_61N_D(*CacnMvyXMjP87HI z4PT6!$fzxx{}=>jeqzkkoN+!r9e|@lZUN4pn(T28v`k=_vIhTn^i9O3qTqd)-%!QQ zYB6*6B@&b(!#X4C~59SLZuorNU_wWZA36{>O%iX)VS5NNZh49C_ppI>?)wwml}_0MLzOXT>lmo#&Ew6d?mu8~~I_^4VGBQtCAke;RQa5DL` z1PFDPsKb3CS$v;RhlQ1J@AHa1VRuuxp}NOIvrC>4$$A0Ix0VpAc0lfG%8{mR{TRQ( zbXM#1Tci3H*Wt>cVuMta^6^z`=^B@j+YhJqq9?>zZPxyg2U(wvod=uwJs{8gtpyab zXHQX<0FOGW6+dw&%c_qMUOI^+Rnb?&HB7Fee|33p4#8i>%_ev(aTm7N1f#6lV%28O zQ`tQh$VDjy8x(Lh#$rg1Kco$Bw%gULq+lc4$&HFGvLMO30QBSDvZ#*~hEHVZ`5=Kw z3y^9D512@P%d~s{x!lrHeL4!TzL`9(ITC97`Cwnn8PSdxPG@0_v{No|kfu3DbtF}K zuoP+88j4dP+Bn7hlGwU$BJy+LN6g&d3HJWMAd1P9xCXG-_P)raipYg5R{KQO$j;I9 z1y1cw#13K|&kfsRZ@qQC<>j=|OC?*v1|VrY$s=2!{}e33aQcZghqc@YsHKq^)kpkg z>B;CWNX+K=u|y#N)O>n5YuyvPl5cO6B^scmG?J zC8ix)E1PlhNaw8FpD+b|D$z`Id^4)rJe78MNiBga?Z- z0$L&MRTieSB1_E#KaN*H#Ns1}?zOA%Ybr{G+Sn3moXTVZj=L`nt?D&-MjOMz-Yq&@ z$P3h23d_F8Dcf*?txX7}p>nM*s+65t z1il8bHHsBynUK|aEXSjzY6sz1nZ%|%XeWTcGLRyRl@q4YAR)JovbdTTY&7u>@}28A zgV^Npp?}I!?3K7IXu9ml-Lw;w@9m zBYTeU+Seh8uJ-w?4e_6byq0f7>O3xm(hO}Y=fgU5^vW|>0yQ^0+?}LT55ei$i zzlU-iRbd8TRX9Ept%h%ariV=%u%F@@FA>U*XdAalcH%>#5_a&w)g`uW%3}m?vP- zc5}DkuF6ruKDwEYj+2YTSQ9=rkp19U5P@(zRm(nLod(sG9{~nw1BUoS2OFDXa{xfw zZ~UaZLFUZxfQ*9?_X?*~`d;nn-BbaefLJ`DT13KF6?T5Mnt;v5d>H}s)aAIzJcs#B z|CuXPJKww}hWBKsUfks#Kh$)ptp?5U1b@ttXFRbe_BZ&_R9XC6CA4WhWhMUE9Y2H4 z{w#CBCR<)Fd1M;mx*m?Z=L-^1kv1WKtqG(BjMiR4M^5yN4rlFM6oGUS2Wf~7Z@e*- ze84Vr`Bmi!(a1y}-m^HHMpbAiKPVEv|(7=|}D#Ihfk+-S5Hlkfch02z&$(zS3vrYz2g*ic{xBy~*gIp(eG}^gMc7 zPu2Eivnp@BH3SOgx!aJXttx*()!=2)%Bf$Gs^4cCs@)=(PJNxhH5lVY&qSZYaa?A^LhZW`B9(N?fx<^gCb(VE%3QpA*_Pohgp6vCB36iVaq zc1TI%L2Le?kuv?6Dq`H+W>AqnjyEzUBK948|DB|)U0_4DzWF#7L{agwo%y$hC>->r z4|_g_6ZC!n2=GF4RqVh6$$reQ(bG0K)i9(oC1t6kY)R@DNxicxGxejwL2sB<>l#w4 zE$QkyFI^(kZ#eE5srv*JDRIqRp2Totc8I%{jWhC$GrPWVc&gE1(8#?k!xDEQ)Tu~e zdU@aD8enALmN@%1FmWUz;4p}41)@c>Fg}1vv~q>xD}KC#sF|L&FU);^Ye|Q;1#^ps z)WmmdQI2;%?S%6i86-GD88>r|(nJackvJ#50vG6fm$1GWf*f6>oBiDKG0Kkwb17KPnS%7CKb zB7$V58cTd8x*NXg=uEX8Man_cDu;)4+P}BuCvYH6P|`x-#CMOp;%u$e z&BZNHgXz-KlbLp;j)si^~BI{!yNLWs5fK+!##G;yVWq|<>7TlosfaWN-;C@oag~V`3rZM_HN`kpF`u1p# ztNTl4`j*Lf>>3NIoiu{ZrM9&E5H~ozq-Qz@Lkbp-xdm>FbHQ2KCc8WD7kt?=R*kG# z!rQ178&ZoU(~U<;lsg@n216Ze3rB2FwqjbZ=u|J?nN%<4J9(Bl(90xevE|7ejUYm9 zg@E_xX}u2d%O1mpA2XzjRwWinvSeg)gHABeMH(2!A^g@~4l%8e0WWAkBvv60Cr>TR zQB1%EQ zUoZeUdqjh+1gFo6h~C~z#A57mf5ibmq$y_uVtA_kWv8X)CzfVEooDaY!#P?5$Y zGPKXbE<75nc%D-|w4OrP#;87oL@2^4+sxKah;a-5&z_&SUf~-z(1}bP=tM^GYtR3a z!x4zjSa^)KWG6jxfUI#{<26g$iAI;o_+B{LXY@WfWEdEl6%#8s3@b`?&Tm#aSK!~| z^%DdrXnijW`d!ajWuKApw&{L+WCPpFialo&^dZ9jC7A%BO`2ZF&YUDe;Yu|zFuv`2 z)BE*7Lkay)M7uohJ)446X``0x0%PzPTWY92`1Oq4a2D_7V0wypPnXFR)WM0IlFgg@ zqz#hv2xJEQL8eu}O;e(w4rSA?5|eZHbS6jENytJBq59?bOf>Wrl8ySZH36H(6fGR#vHM6q zn}!7!I@4$*+LFXs{x?|=q2*QtYT%Lw3+5(8uc0j8o3}TrG(zSV#>4wo6~)u|R+Yx# z?0$AspZDjv{dfv417~C17Oy%Fal{%+B6H(NX`$Bl>II-L3N3 zZc+sKZbqewU*&_Xt;9k=%4*aVYBvE1n&JZS7Uqjd%n8nOQmzh^x#vWK{;In~=QO)g zT-n3OU(1@3QfL|$g1d2xeBb@O15Rl01+hmpup2De7p%Yrd$E7(In!*R+;IJZh}v!svi z;7N~pq8KZDXXap0qd_D=Y^B)rz4S0^SF=&v6YYTAV$ad43#x!+n~-6< zK{8*vWoAdW(gGGt&URD}@g6tMoY(+Lw=vvxhfIIK9AjvNF_(W}1Rxn(mp;tJfDV<0 zbJN0t(@Xb8UeO{&T{$$uDrs7)j$}=?WsuDl+T2N5Y<4TMHGOMcocPr$%~(yvtKv(n z`U96d!D0cb9>Dx2zz$m&lAhazs%UeR^K*gb>d8CPs+?qlpfA;t{InXa)^2ryC(FU(Zc6Xbnnh`lg`K&g^JeS>}^c0MJKUCfV+~ zV(EN0Z5ztoN;hqcj!8V+VRbSltJ<~|y`U+9#wv|~H zNE!j9uXa=dec@JQSgJ6N6@Il&tzCBJv9#ldR`Lm*<)YwH4tdlAlG0Fl8Nfa(J~c%DQ2AA-}x8D=p(l#n1+hgx;N;1Aq?lq@{Lt9FKu89CjnnHD1G_@p;%Lp`+b@ttb33!E_Xt;QUD9~nRQl&xAro9-{+&6^ljK2f-d>&qy&d#0xwH z@slNv@ULKp!Cf*JHuS@#4c?F->WjPc)yiuSargAIEg>muRxzY?Hzdq@G5CS)U1*Et zE2SLh=@DI1J(guiy2Igq(?(xI9WL%g^f@{5Hmr|!Qz4`vn|LjrtO=b~I6~5EU5Fxy z;-#<)6w#w=DkpSthAu+E;OL?!?6C9Mwt*o(@68(Jhvs-eX4V z=d=>HI|`3J%H5X|gSrC8KH^IL?h5=3ID6svwHH@(wRbSG`Zsor^q4`3PCn#-(YX?< z_q8+T)51$E0xyKR{L!LN(G=+9K6$3#PDT^IAe|Igkx=!4#rqKWoXiZdh`&ocjp=Ok zemJe6*{it~>;sr(B0fSmp(S#*y5I0)OOz~Oe6Im+($S}e3tyx7Y6pA8vKCBmSEQDa zLfkm*;uMbTLpcR0)tF_v-lbK%`5>POyI2E(!)2=Rj0p;WKi=|UNt6HsQv0xR3QIK9 zsew(AFyzH!7Azxum{%VC^`cqhGdGbABGQ4cYdNBPTx+XpJ=NUEDeP^e^w^AOE1pQI zP{Us-sk!v$gj}@684E!uWjzvpoF|%v-6hwnitN1sCSg@(>RDCVgU8Ile_-xX`hL6u zzI4*Q)AVu(-ef8{#~P9STQ5t|qIMRoh&S?7Oq+cL6vxG?{NUr@k(~7^%w)P6nPbDa~4Jw}*p-|cT4p1?)!c0FoB(^DNJ+FDg+LoP6=RgB7Or673WD5MG&C!4< zerd6q$ODkBvFoy*%cpHGKSt z3uDC6Sc=xvv@kDzRD)aIO`x}BaWLycA%(w-D`Pd+uL*rL|etagQ;U&xt_9?7#}=}5HI)cU-0 z%pMA`>Xb7s)|Y)4HKSZOu;{lg=KjeIyXb0{@EM`FTDkLRH`!W%z*lQJ74P%Ka76)H zblrSIzf+dMWbO`g;=(b@{pS)zUcO&GrIFe%&?YeX4r8B2bBArB%-5ZrQ+vonr%AYy z1+u0*K{UVUmV>h5vD!F;6}a%KdMZQLs04oGkpiaC)zI( zT2U9qta5o|6Y+It1)sE8>u&0)W~l$NX@ZQ8UZfB=`($EW6?FT%{EoRhOrb9)z@3r8y?Z99FNLDE;7V=Q zotj&igu*Rh^VQn3MQKBq!T{yTwGhn1YL6k*?j?{_ek5xe8#i#GG4S-a_Re2lssG!} z`Y-d0BcOdB@!m?4y&hMN68}#0-IIlm_xO)d#}ugX{q^OZe{-@LeJyv`cY&ze4t2~! zKb{qX-j;kt{?gC(vW%}X4pm@1F?~LH{^Q8d@X$dy@5ff~p!J3zmA>H`A)y+6RB_h* zZfIO+bd=*LiymRw{asW%xxaVl33_xtdVrrqIPn zc@y8oMJvNtgcO~4i0`f)GCFkWY8EF?4duLVjHTdb6oYLnO9}Q-pe{CKQJL)hV8)JI z$mVA0Dq&7Z1TbYdSC(WbJ+IBjXngZTu&I+vHF|>Zo$757{8lL;8Zr-Exkf?3jzN5k z_d9I>{>^J?!l)< zNd$7E9FVrta}3qy3L7Ys$^fRWNuu^hs^{*eXvazd&+Q*?lTfc>2+EdP(o0P_Z05HX zVKsfFAQ{t^CRu~Dw(CuJ>tvx*p$5@flA>QRl455b&{*U?xU8`)nF2T$uu_(l8VNtq z?pBiRQIckGzk8W&SFSB=g6eG`ZC;6v9w`?eF*S}3E@N`2ropeHP)E}o?qJkyVEI;K$!)bWY zt9>4WmDVJh7U~m$|K`T#hF!v|znj^=M;69uXrFys#51XT;DbMr4H)>7UQ1e2(cuQf z4kr~Tt1tpBB2GaJ(|j~lHgW40EgMMVqR6eJoJig1SBg|2=$~4I3P0eP$q%_`sS&4~ z26=&a&tLjQbch1`cVXa-2fTl1y8}->|Nqu?uVrNTov!=VKh)g89wUPTgAzkSKZ57_ zr=B^mcldE3K04t4{;RaG53&9yovq;@aR#VHx+R1^^*kr-vEEd!uea68Z<{R%_DD6fn&T4 zu;fDj07L-(_fLSJGdkeh&c&7A(ZLj`7iwnkAcqUexU;WjUkqeg1m1-IUZTIZA(4dtr2Gr`e{BIejlCgS<33MB=1!8?a74!F%=Uo7N`F@k} ze+1C_eU4Y_$mvdjci zwEtCIphA2PBzBhng5=M#e4r%)RW5rVD|_`PvY$7BK`}w~d>%0O9sY#*LUAq=^OjMF^PY5m<7!=s5jyRfosCQAo#hL`h5vN-M}6Q z0Li}){5?wi8)GVHNkF|U9*8V5ej)nhb^TLw1KqiPK(@{P1^L&P=`ZNt?_+}&0(8Uh zfyyZFPgMV7ECt;Jdw|`|{}b$w4&x77VxR>8wUs|GQ5FBf1UlvasqX$qfk5rI4>Wfr zztH>y`=daAef**C12yJ7;LDf&3;h3X+5@dGPy@vS(RSs3CWimbTp=g \(.*\)$'` + 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 34/65] 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 35/65] 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 36/65] 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 37/65] 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 38/65] 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 39/65] 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 40/65] 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 41/65] 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 42/65] 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 43/65] 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 44/65] 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 45/65] 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 46/65] 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 47/65] 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 48/65] 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 49/65] 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 50/65] 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 51/65] 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 52/65] 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 53/65] 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 54/65] 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 55/65] 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 56/65] 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 57/65] 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 58/65] 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 59/65] 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 60/65] 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 61/65] 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 62/65] 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 63/65] 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 64/65] 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 65/65] 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()