diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml
new file mode 100644
index 00000000..e086d3f9
--- /dev/null
+++ b/.github/workflows/android.yml
@@ -0,0 +1,209 @@
+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 to build and test'
+ default: '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:
+ check_and_prepare:
+ runs-on: ubuntu-latest
+ outputs:
+ apis: ${{ steps.set_outputs.outputs.apis }}
+ steps:
+ - id: set_outputs
+ run: |
+ if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
+ apis="${{ github.event.inputs.apis }}"
+ else
+ apis="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/.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
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
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
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/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/analytics/testapp/AndroidManifest.xml b/analytics/testapp/AndroidManifest.xml
index 02165262..b7038a15 100644
--- a/analytics/testapp/AndroidManifest.xml
+++ b/analytics/testapp/AndroidManifest.xml
@@ -6,9 +6,10 @@
-
+
-
+
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 +160,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 +252,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 +275,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 +303,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 +332,6 @@ class UserLogin {
: auth_(auth),
email_(email),
password_(password),
- user_(nullptr),
log_errors_(true) {}
explicit UserLogin(Auth* auth) : auth_(auth) {
@@ -340,48 +340,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 +389,7 @@ class UserLogin {
Auth* auth_;
std::string email_;
std::string password_;
- User* user_;
+ User user_;
bool log_errors_;
};
@@ -401,7 +401,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 +493,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 +523,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 +532,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 +560,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 +569,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);
@@ -602,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;
@@ -635,21 +637,21 @@ 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());
- 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 +666,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 +680,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 +713,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 +726,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 +765,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 +801,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 +810,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 +820,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 +837,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 +849,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 +861,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 +877,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 +889,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 +900,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 +910,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 +921,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 +958,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 +973,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 +998,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 +1009,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 +1041,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 +1091,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 +1109,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 +1126,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 +1170,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 +1179,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 +1239,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 +1255,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 +1304,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 +1329,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 +1350,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 +1364,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);
}
diff --git a/auth/testapp/testapp.xcodeproj/project.pbxproj b/auth/testapp/testapp.xcodeproj/project.pbxproj
index 6c325725..dbad8094 100644
--- a/auth/testapp/testapp.xcodeproj/project.pbxproj
+++ b/auth/testapp/testapp.xcodeproj/project.pbxproj
@@ -15,6 +15,7 @@
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 */; };
+ D6FC32B32C06595E00E3E028 /* GameKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D6FC32B22C06595E00E3E028 /* GameKit.framework */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
@@ -30,6 +31,7 @@
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 = ""; };
D66B16861CE46E8900E5638A /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; 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 */
@@ -38,6 +40,7 @@
buildActionMask = 2147483647;
files = (
529226D81C85F68000C89379 /* CoreGraphics.framework in Frameworks */,
+ D6FC32B32C06595E00E3E028 /* GameKit.framework in Frameworks */,
529226DA1C85F68000C89379 /* UIKit.framework in Frameworks */,
529226D61C85F68000C89379 /* Foundation.framework in Frameworks */,
);
@@ -46,6 +49,13 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
+ 3E2FC86EB58D8B5977042124 /* Pods */ = {
+ isa = PBXGroup;
+ children = (
+ );
+ path = Pods;
+ sourceTree = "";
+ };
529226C91C85F68000C89379 = {
isa = PBXGroup;
children = (
@@ -56,6 +66,7 @@
5292271D1C85FB5500C89379 /* src */,
529226D41C85F68000C89379 /* Frameworks */,
529226D31C85F68000C89379 /* Products */,
+ 3E2FC86EB58D8B5977042124 /* Pods */,
);
sourceTree = "";
};
@@ -70,6 +81,7 @@
529226D41C85F68000C89379 /* Frameworks */ = {
isa = PBXGroup;
children = (
+ D6FC32B22C06595E00E3E028 /* GameKit.framework */,
529226D51C85F68000C89379 /* Foundation.framework */,
529226D71C85F68000C89379 /* CoreGraphics.framework */,
529226D91C85F68000C89379 /* UIKit.framework */,
@@ -135,6 +147,7 @@
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
+ English,
en,
);
mainGroup = 529226C91C85F68000C89379;
@@ -208,7 +221,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 +258,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/AndroidManifest.xml b/database/testapp/AndroidManifest.xml
index a087cd78..f94a8ca5 100644
--- a/database/testapp/AndroidManifest.xml
+++ b/database/testapp/AndroidManifest.xml
@@ -6,9 +6,10 @@
-
+
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/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/AndroidManifest.xml b/dynamic_links/testapp/AndroidManifest.xml
index 62212dc1..2aa60f2d 100644
--- a/dynamic_links/testapp/AndroidManifest.xml
+++ b/dynamic_links/testapp/AndroidManifest.xml
@@ -6,9 +6,10 @@
-
+
[!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.
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/AndroidManifest.xml b/firestore/testapp/AndroidManifest.xml
index 9b97c8d6..b4f84579 100644
--- a/firestore/testapp/AndroidManifest.xml
+++ b/firestore/testapp/AndroidManifest.xml
@@ -6,10 +6,11 @@
-
+
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::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");
}
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/AndroidManifest.xml b/functions/testapp/AndroidManifest.xml
index 98dc4cef..0b38246d 100644
--- a/functions/testapp/AndroidManifest.xml
+++ b/functions/testapp/AndroidManifest.xml
@@ -6,9 +6,10 @@
-
+
{
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/functions/testapp/gradle/wrapper/gradle-wrapper.properties b/functions/testapp/gradle/wrapper/gradle-wrapper.properties
index 35732b09..65340c1b 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-6.7.1-all.zip
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/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/admob/testapp/AndroidManifest.xml b/gma/testapp/AndroidManifest.xml
similarity index 80%
rename from admob/testapp/AndroidManifest.xml
rename to gma/testapp/AndroidManifest.xml
index e62e6385..f443dd14 100644
--- a/admob/testapp/AndroidManifest.xml
+++ b/gma/testapp/AndroidManifest.xml
@@ -6,15 +6,16 @@
-
+
+ android:value="ca-app-pub-3940256099942544~3347511713"/>
+ android:exported = "true"
+ android:screenOrientation="portrait"
+ android:configChanges="orientation|screenSize">
diff --git a/admob/testapp/CMakeLists.txt b/gma/testapp/CMakeLists.txt
similarity index 98%
rename from admob/testapp/CMakeLists.txt
rename to gma/testapp/CMakeLists.txt
index 3a7d02dd..2132660c 100644
--- a/admob/testapp/CMakeLists.txt
+++ b/gma/testapp/CMakeLists.txt
@@ -107,5 +107,5 @@ 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)
+set(firebase_libs firebase_gma firebase_app)
target_link_libraries(${target_name} "${firebase_libs}" ${ADDITIONAL_LIBS})
diff --git a/admob/testapp/LICENSE b/gma/testapp/LICENSE
similarity index 100%
rename from admob/testapp/LICENSE
rename to gma/testapp/LICENSE
diff --git a/admob/testapp/LaunchScreen.storyboard b/gma/testapp/LaunchScreen.storyboard
similarity index 100%
rename from admob/testapp/LaunchScreen.storyboard
rename to gma/testapp/LaunchScreen.storyboard
diff --git a/gma/testapp/Podfile b/gma/testapp/Podfile
new file mode 100644
index 00000000..4683028b
--- /dev/null
+++ b/gma/testapp/Podfile
@@ -0,0 +1,8 @@
+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'
+ pod 'Firebase/Analytics', '10.25.0'
+end
diff --git a/admob/testapp/build.gradle b/gma/testapp/build.gradle
similarity index 69%
rename from admob/testapp/build.gradle
rename to gma/testapp/build.gradle
index 4562ea56..4cf159a9 100644
--- a/admob/testapp/build.gradle
+++ b/gma/testapp/build.gradle
@@ -1,4 +1,5 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
buildscript {
repositories {
mavenLocal()
@@ -6,9 +7,8 @@ buildscript {
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.2.1'
- classpath 'com.google.gms:google-services:4.0.1'
- }
+ classpath 'com.android.tools.build:gradle:4.2.1'
+ classpath 'com.google.gms:google-services:4.0.1' }
}
allprojects {
@@ -22,8 +22,14 @@ allprojects {
apply plugin: 'com.android.application'
android {
- compileSdkVersion 28
- buildToolsVersion '28.0.3'
+ compileOptions {
+ sourceCompatibility 1.8
+ targetCompatibility 1.8
+ }
+
+ compileSdkVersion 34
+ ndkPath System.getenv('ANDROID_NDK_HOME')
+ buildToolsVersion '30.0.2'
sourceSets {
main {
@@ -36,7 +42,7 @@ android {
defaultConfig {
applicationId 'com.google.android.admob.testapp'
- minSdkVersion 16
+ minSdkVersion 23
targetSdkVersion 28
versionCode 1
versionName '1.0'
@@ -54,11 +60,20 @@ android {
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
+ gma
}
apply plugin: 'com.google.gms.google-services'
+
+// com.google.gms.googleservices.GoogleServicesPlugin.config.disableVersionCheck = 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/admob/testapp/gradle/wrapper/gradle-wrapper.jar b/gma/testapp/gradle/wrapper/gradle-wrapper.jar
similarity index 100%
rename from admob/testapp/gradle/wrapper/gradle-wrapper.jar
rename to gma/testapp/gradle/wrapper/gradle-wrapper.jar
diff --git a/admob/testapp/gradle/wrapper/gradle-wrapper.properties b/gma/testapp/gradle/wrapper/gradle-wrapper.properties
similarity index 65%
rename from admob/testapp/gradle/wrapper/gradle-wrapper.properties
rename to gma/testapp/gradle/wrapper/gradle-wrapper.properties
index 35732b09..65340c1b 100644
--- a/admob/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.6-all.zip
+distributionUrl=https://services.gradle.org/distributions/gradle-6.7.1-all.zip
diff --git a/admob/testapp/gradlew b/gma/testapp/gradlew
similarity index 100%
rename from admob/testapp/gradlew
rename to gma/testapp/gradlew
diff --git a/admob/testapp/gradlew.bat b/gma/testapp/gradlew.bat
similarity index 100%
rename from admob/testapp/gradlew.bat
rename to gma/testapp/gradlew.bat
diff --git a/admob/testapp/proguard.pro b/gma/testapp/proguard.pro
similarity index 100%
rename from admob/testapp/proguard.pro
rename to gma/testapp/proguard.pro
diff --git a/admob/testapp/readme.md b/gma/testapp/readme.md
similarity index 85%
rename from admob/testapp/readme.md
rename to gma/testapp/readme.md
index e8f45f80..4143cb7e 100644
--- a/admob/testapp/readme.md
+++ b/gma/testapp/readme.md
@@ -1,15 +1,15 @@
-Firebase AdMob Quickstart
+Firebase GMA 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.
+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 AdMob](https://firebase.google.com/docs/admob)
+- [Read more about Firebase GMA](https://firebase.google.com/docs/gma)
Getting Started
---------------
@@ -41,7 +41,7 @@ Getting Started
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
+ - 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"
@@ -55,16 +55,13 @@ Getting Started
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'.
+ 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 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 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.
@@ -112,25 +109,26 @@ Getting Started
- 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'.
+ - 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 AdMob, then load and display a test banner and
- a test interstitial.
+ - 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
- interstitial will wait to be closed by the user.
- - Afterwards, the testapp will display a Rewarded Video test ad.
+ 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. 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.
@@ -170,9 +168,6 @@ Getting Started
./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
-------
@@ -182,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/admob/testapp/res/values/strings.xml b/gma/testapp/res/values/strings.xml
similarity index 53%
rename from admob/testapp/res/values/strings.xml
rename to gma/testapp/res/values/strings.xml
index 8589bd2c..a9a313a3 100644
--- a/admob/testapp/res/values/strings.xml
+++ b/gma/testapp/res/values/strings.xml
@@ -1,4 +1,4 @@
- Firebase AdMob Test
+ Firebase GMA Test
diff --git a/admob/testapp/settings.gradle b/gma/testapp/settings.gradle
similarity index 100%
rename from admob/testapp/settings.gradle
rename to gma/testapp/settings.gradle
diff --git a/admob/testapp/src/android/android_main.cc b/gma/testapp/src/android/android_main.cc
similarity index 86%
rename from admob/testapp/src/android/android_main.cc
rename to gma/testapp/src/android/android_main.cc
index 73cb30e7..7a6de455 100644
--- a/admob/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/admob/testapp/src/android/java/com/google/firebase/example/LoggingUtils.java b/gma/testapp/src/android/java/com/google/firebase/example/LoggingUtils.java
similarity index 92%
rename from admob/testapp/src/android/java/com/google/firebase/example/LoggingUtils.java
rename to gma/testapp/src/android/java/com/google/firebase/example/LoggingUtils.java
index 11d67c5b..3cb37ecf 100644
--- a/admob/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
new file mode 100644
index 00000000..10a0a181
--- /dev/null
+++ b/gma/testapp/src/common_main.cc
@@ -0,0 +1,473 @@
+// 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/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"
+
+// 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) {
+ 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;
+ 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) {
+ LogMessage("\nLoad and show an interstitial ad:");
+ LogMessage("===");
+ // 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) {
+ LogMessage("\nLoad and show a rewarded ad:");
+ LogMessage("===");
+ // 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;
+}
diff --git a/admob/testapp/src/desktop/desktop_main.cc b/gma/testapp/src/desktop/desktop_main.cc
similarity index 81%
rename from admob/testapp/src/desktop/desktop_main.cc
rename to gma/testapp/src/desktop/desktop_main.cc
index 0220c688..0a318786 100644
--- a/admob/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/admob/testapp/src/ios/ios_main.mm b/gma/testapp/src/ios/ios_main.mm
similarity index 74%
rename from admob/testapp/src/ios/ios_main.mm
rename to gma/testapp/src/ios/ios_main.mm
index 6ccb2de5..4c226d5e 100644
--- a/admob/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/admob/testapp/src/main.h b/gma/testapp/src/main.h
similarity index 76%
rename from admob/testapp/src/main.h
rename to gma/testapp/src/main.h
index 2eda2c10..d41017a7 100644
--- a/admob/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
diff --git a/admob/testapp/testapp.xcodeproj/project.pbxproj b/gma/testapp/testapp.xcodeproj/project.pbxproj
similarity index 99%
rename from admob/testapp/testapp.xcodeproj/project.pbxproj
rename to gma/testapp/testapp.xcodeproj/project.pbxproj
index 14a0af7f..3afd2dea 100644
--- a/admob/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/admob/testapp/testapp/Images.xcassets/AppIcon.appiconset/Contents.json b/gma/testapp/testapp/Images.xcassets/AppIcon.appiconset/Contents.json
similarity index 100%
rename from admob/testapp/testapp/Images.xcassets/AppIcon.appiconset/Contents.json
rename to gma/testapp/testapp/Images.xcassets/AppIcon.appiconset/Contents.json
diff --git a/admob/testapp/testapp/Images.xcassets/LaunchImage.launchimage/Contents.json b/gma/testapp/testapp/Images.xcassets/LaunchImage.launchimage/Contents.json
similarity index 100%
rename from admob/testapp/testapp/Images.xcassets/LaunchImage.launchimage/Contents.json
rename to gma/testapp/testapp/Images.xcassets/LaunchImage.launchimage/Contents.json
diff --git a/admob/testapp/testapp/Info.plist b/gma/testapp/testapp/Info.plist
similarity index 100%
rename from admob/testapp/testapp/Info.plist
rename to gma/testapp/testapp/Info.plist
diff --git a/messaging/testapp/AndroidManifest.xml b/messaging/testapp/AndroidManifest.xml
index f0f9b9c8..c0357769 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/Podfile b/messaging/testapp/Podfile
index e6ffc8dc..b022880f 100644
--- a/messaging/testapp/Podfile
+++ b/messaging/testapp/Podfile
@@ -1,6 +1,7 @@
source 'https://github.com/CocoaPods/Specs.git'
-platform :ios, '8.0'
+platform :ios, '13.0'
+use_frameworks!
# FCM test application.
target 'testapp' do
- pod 'Firebase/Messaging', '7.0.0'
+ pod 'Firebase/Messaging', '10.25.0'
end
diff --git a/messaging/testapp/build.gradle b/messaging/testapp/build.gradle
index 0971c57b..4f607fe3 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:4.2.1'
classpath 'com.google.gms:google-services:4.0.1'
}
}
@@ -22,15 +22,13 @@ allprojects {
apply plugin: 'com.android.application'
android {
- compileSdkVersion 28
- buildToolsVersion '28.0.3'
-
- android {
- compileOptions {
- sourceCompatibility 1.8
- targetCompatibility 1.8
- }
+ compileOptions {
+ sourceCompatibility 1.8
+ targetCompatibility 1.8
}
+ compileSdkVersion 34
+ ndkPath System.getenv('ANDROID_NDK_HOME')
+ buildToolsVersion '30.0.2'
sourceSets {
main {
@@ -43,7 +41,7 @@ android {
defaultConfig {
applicationId 'com.google.android.messaging.testapp'
- minSdkVersion 16
+ minSdkVersion 23
targetSdkVersion 28
versionCode 1
versionName '1.0'
@@ -61,6 +59,13 @@ android {
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"
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/messaging/testapp/gradle/wrapper/gradle-wrapper.properties b/messaging/testapp/gradle/wrapper/gradle-wrapper.properties
index 35732b09..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-4.6-all.zip
+distributionUrl=https://services.gradle.org/distributions/gradle-6.7.1-all.zip
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/AndroidManifest.xml b/remote_config/testapp/AndroidManifest.xml
index e449f7ee..a7a904df 100644
--- a/remote_config/testapp/AndroidManifest.xml
+++ b/remote_config/testapp/AndroidManifest.xml
@@ -6,9 +6,10 @@
-
+
(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/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/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..5b5ef2d1
--- /dev/null
+++ b/scripts/build_scripts/build_testapps.json
@@ -0,0 +1,170 @@
+{
+ "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.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/gha-encrypted/README b/scripts/gha-encrypted/README
new file mode 100644
index 00000000..e73cd287
--- /dev/null
+++ b/scripts/gha-encrypted/README
@@ -0,0 +1,3 @@
+See https://help.github.com/en/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets
+
+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
new file mode 100644
index 00000000..a7fad00c
Binary files /dev/null and b/scripts/gha-encrypted/analytics/GoogleService-Info.plist.gpg differ
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 00000000..8e2a09d3
Binary files /dev/null and b/scripts/gha-encrypted/analytics/google-services.json.gpg differ
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 00000000..896aeabf
--- /dev/null
+++ b/scripts/gha-encrypted/auth/GoogleService-Info.plist.gpg
@@ -0,0 +1,3 @@
+
ᗩ9p0E,uv%|{G+:q{y9Նz J{;.P^]pnę]
-2Oe;0żCf]fۇ*
+"*{t?eigW}2f)1gf69>r(goO0d-z9cK@)Kg~)T'm~H/>X64Oئ{5N-V/sw"Df>%>wNAǽ@\B"\Dp35i` ܨ̺哐ʡvM:K!\ٌ<6XNj|MIv̶R51Bd4 'x(A15JH6{H@/ |IR_M$[i +?s^NTyzc]q.-[Mz
¶O&eSvrlAcR5rK~ΉT
+[AbZgcӴպX5AS|ӧm7l{~*K&) ?8tR狮UL衧q,!7eI}B{iTrHɍ;[EAPV!Dk" 陑dyb"kJՖ
\ No newline at end of file
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 00000000..9f86b0a3
Binary files /dev/null and b/scripts/gha-encrypted/auth/google-services.json.gpg differ
diff --git a/scripts/gha-encrypted/database/GoogleService-Info.plist.gpg b/scripts/gha-encrypted/database/GoogleService-Info.plist.gpg
new file mode 100644
index 00000000..9d7f62a8
Binary files /dev/null and b/scripts/gha-encrypted/database/GoogleService-Info.plist.gpg differ
diff --git a/scripts/gha-encrypted/database/google-services.json.gpg b/scripts/gha-encrypted/database/google-services.json.gpg
new file mode 100644
index 00000000..a9421faa
Binary files /dev/null and b/scripts/gha-encrypted/database/google-services.json.gpg differ
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 00000000..7d577bef
Binary files /dev/null and b/scripts/gha-encrypted/dynamic_links/GoogleService-Info.plist.gpg differ
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 00000000..0db36dbf
--- /dev/null
+++ b/scripts/gha-encrypted/dynamic_links/google-services.json.gpg
@@ -0,0 +1,3 @@
+
3u=ێpAbg3.)
zs/@Zrӓ$i^$1>X3OZh%YM
t?_C:AiclƎ
\m.IMi?9 {S~ZUXt⾘=}ݔV||1M-u_m8cVS]brQ㈃:rE[=ۅ.nj=zU>5h)ZarPr"8qбo>E 3|nF(KUe5(uo?CiN_:X>vJ['g_okL\IZJٕ'@ȲN?
k$-SaCSRqv7
+rlYyRQEx%L蔘%p8ye*/\g oOM'w܊fY
+g"KFra6X18?خIu2Bmx>mZ;V2zy4LuiД;T]̄SIbrZ*R|*'6ȑ~XSMG|!ˇ2֠Butq!3nr@lVu
H&P5KOy}e;eUa">g
\ No newline at end of file
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 00000000..4ae5fb41
Binary files /dev/null and b/scripts/gha-encrypted/firestore/GoogleService-Info.plist.gpg differ
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 00000000..98073347
Binary files /dev/null and b/scripts/gha-encrypted/firestore/google-services.json.gpg differ
diff --git a/scripts/gha-encrypted/functions/GoogleService-Info.plist.gpg b/scripts/gha-encrypted/functions/GoogleService-Info.plist.gpg
new file mode 100644
index 00000000..6ed5b9d8
Binary files /dev/null and b/scripts/gha-encrypted/functions/GoogleService-Info.plist.gpg differ
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 00000000..c9edac06
Binary files /dev/null and b/scripts/gha-encrypted/functions/google-services.json.gpg differ
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 00000000..65b703ce
Binary files /dev/null and b/scripts/gha-encrypted/gma/GoogleService-Info.plist.gpg differ
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 00000000..c46f5964
Binary files /dev/null and b/scripts/gha-encrypted/gma/google-services.json.gpg differ
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 00000000..cf95c652
Binary files /dev/null and b/scripts/gha-encrypted/messaging/GoogleService-Info.plist.gpg differ
diff --git a/scripts/gha-encrypted/messaging/google-services.json.gpg b/scripts/gha-encrypted/messaging/google-services.json.gpg
new file mode 100644
index 00000000..8ad06ccf
Binary files /dev/null and b/scripts/gha-encrypted/messaging/google-services.json.gpg differ
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 00000000..ea9dc946
Binary files /dev/null and b/scripts/gha-encrypted/remote_config/GoogleService-Info.plist.gpg differ
diff --git a/scripts/gha-encrypted/remote_config/google-services.json.gpg b/scripts/gha-encrypted/remote_config/google-services.json.gpg
new file mode 100644
index 00000000..b9dd70a0
Binary files /dev/null and b/scripts/gha-encrypted/remote_config/google-services.json.gpg differ
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 00000000..01d28ec9
Binary files /dev/null and b/scripts/gha-encrypted/storage/GoogleService-Info.plist.gpg differ
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 00000000..57ba134a
Binary files /dev/null and b/scripts/gha-encrypted/storage/google-services.json.gpg differ
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)
diff --git a/storage/testapp/AndroidManifest.xml b/storage/testapp/AndroidManifest.xml
index d5428bbe..253ce8c0 100644
--- a/storage/testapp/AndroidManifest.xml
+++ b/storage/testapp/AndroidManifest.xml
@@ -6,9 +6,10 @@
-
+
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/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";