diff --git a/.github/ISSUE_TEMPLATE/issue.md b/.github/ISSUE_TEMPLATE/issue.md
new file mode 100644
index 00000000..94c20889
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/issue.md
@@ -0,0 +1,35 @@
+---
+name: Firebase C++ Sample issue
+about: Report issues with the Firebase C++ Sample.
+labels: new
+---
+
+
+### [READ] For Firebase C++ SDK issues, please report to [Firebase C++ open-source](https://github.com/firebase/firebase-cpp-sdk/issues/new/choose)
+
+Once you've read this section and determined that your issue is appropriate for this repository, please delete this section.
+
+### [REQUIRED] Please fill in the following fields:
+
+ * Which Firebase Sample: _____ (Auth, Database, etc.)
+ * Firebase C++ SDK version: _____
+ * Additional SDKs you are using: _____ (Facebook, AdMob, etc.)
+ * Platform you are using the SDK on: _____ (Mac, Windows, or Linux)
+ * Platform you are targeting: _____ (iOS, Android, and/or desktop)
+
+### [REQUIRED] Please describe the issue here:
+
+(Please list the full steps to reproduce the issue. Include device logs, and stack traces if available.)
+
+#### Steps to reproduce:
+
+What's the issue repro rate? (eg 100%, 1/5 etc)
+
+What happened? How can we make the problem occur?
+This could be a description, log/console output, etc.
+
+If you have a downloadable sample project that reproduces the bug you're reporting, you will
+likely receive a faster response on your issue.
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 d7428213..00000000
--- a/admob/testapp/Podfile
+++ /dev/null
@@ -1,7 +0,0 @@
-source 'https://github.com/CocoaPods/Specs.git'
-platform :ios, '7.0'
-# AdMob test application.
-target 'testapp' do
- pod 'Firebase/AdMob'
- pod 'Firebase/Core'
-end
\ No newline at end of file
diff --git a/admob/testapp/build.gradle b/admob/testapp/build.gradle
deleted file mode 100644
index 53c3a381..00000000
--- a/admob/testapp/build.gradle
+++ /dev/null
@@ -1,117 +0,0 @@
-// Top-level build file where you can add configuration options common to all sub-projects/modules.
-buildscript {
- repositories {
- mavenLocal()
- jcenter()
- }
- dependencies {
- classpath 'com.android.tools.build:gradle:2.1.2'
- classpath 'com.google.gms:google-services:3.0.0'
- }
-}
-
-allprojects {
- repositories {
- mavenLocal()
- jcenter()
- }
-}
-
-apply plugin: 'com.android.application'
-
-// Pre-experimental Gradle plug-in NDK boilerplate below.
-// Right now the Firebase plug-in does not work with the experimental
-// Gradle plug-in so we're using ndk-build for the moment.
-project.ext {
- // Configure the Firebase C++ SDK location.
- firebase_cpp_sdk_dir = System.getProperty('firebase_cpp_sdk.dir')
- if (firebase_cpp_sdk_dir == null || firebase_cpp_sdk_dir.isEmpty()) {
- firebase_cpp_sdk_dir = System.getenv('FIREBASE_CPP_SDK_DIR')
- if (firebase_cpp_sdk_dir == null || firebase_cpp_sdk_dir.isEmpty()) {
- if ((new File('firebase_cpp_sdk')).exists()) {
- firebase_cpp_sdk_dir = 'firebase_cpp_sdk'
- } else {
- throw new StopActionException(
- 'firebase_cpp_sdk.dir property or the FIREBASE_CPP_SDK_DIR ' +
- 'environment variable must be set to reference the Firebase C++ ' +
- 'SDK install directory. This is used to configure static library ' +
- 'and C/C++ include paths for the SDK.')
- }
- }
- }
- if (!(new File(firebase_cpp_sdk_dir)).exists()) {
- throw new StopActionException(
- sprintf('Firebase C++ SDK directory %s does not exist',
- firebase_cpp_sdk_dir))
- }
- // Check the NDK location using the same configuration options as the
- // experimental Gradle plug-in.
- ndk_dir = project.android.ndkDirectory
- if (ndk_dir == null || !ndk_dir.exists()) {
- ndk_dir = System.getenv('ANDROID_NDK_HOME')
- if (ndk_dir == null || ndk_dir.isEmpty()) {
- throw new StopActionException(
- 'Android NDK directory should be specified using the ndk.dir ' +
- 'property or ANDROID_NDK_HOME environment variable.')
- }
- }
-}
-
-android {
- compileSdkVersion 23
- buildToolsVersion '23.0.3'
-
- sourceSets {
- main {
- jniLibs.srcDirs = ['libs']
- manifest.srcFile 'AndroidManifest.xml'
- java.srcDirs = ['src/android/java']
- res.srcDirs = ['res']
- }
- }
-
- defaultConfig {
- applicationId 'com.google.android.admob.testapp'
- minSdkVersion 14
- targetSdkVersion 23
- versionCode 1
- versionName '1.0'
- }
- buildTypes {
- release {
- minifyEnabled true
- proguardFile getDefaultProguardFile('proguard-android.txt')
- proguardFile file(project.ext.firebase_cpp_sdk_dir + "/libs/android/app.pro")
- proguardFile file(project.ext.firebase_cpp_sdk_dir + "/libs/android/admob.pro")
- }
- }
-}
-
-dependencies {
- compile 'com.google.firebase:firebase-ads:10.2.0'
- compile 'com.google.android.gms:play-services-base:10.2.0'
-}
-
-apply plugin: 'com.google.gms.google-services'
-
-task ndkBuildCompile(type:Exec) {
- description 'Use ndk-build to compile the C++ application.'
- commandLine("${project.ext.ndk_dir}${File.separator}ndk-build",
- "FIREBASE_CPP_SDK_DIR=${project.ext.firebase_cpp_sdk_dir}",
- sprintf("APP_PLATFORM=android-%d",
- android.defaultConfig.minSdkVersion.mApiLevel))
-}
-
-task ndkBuildClean(type:Exec) {
- description 'Use ndk-build to clean the C++ application.'
- commandLine("${project.ext.ndk_dir}${File.separator}ndk-build",
- "FIREBASE_CPP_SDK_DIR=${project.ext.firebase_cpp_sdk_dir}",
- "clean")
-}
-
-// Once the Android Gradle plug-in has generated tasks, add dependencies for
-// the ndk-build targets.
-project.afterEvaluate {
- preBuild.dependsOn(ndkBuildCompile)
- clean.dependsOn(ndkBuildClean)
-}
diff --git a/admob/testapp/jni/Android.mk b/admob/testapp/jni/Android.mk
deleted file mode 100644
index 76ef6dc6..00000000
--- a/admob/testapp/jni/Android.mk
+++ /dev/null
@@ -1,57 +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.
-
-LOCAL_PATH:=$(call my-dir)/..
-
-ifeq ($(FIREBASE_CPP_SDK_DIR),)
-$(error FIREBASE_CPP_SDK_DIR must specify the Firebase package location.)
-endif
-
-# With Firebase libraries for the selected build configuration (ABI + STL)
-STL:=$(firstword $(subst _, ,$(APP_STL)))
-FIREBASE_LIBRARY_PATH:=\
-$(FIREBASE_CPP_SDK_DIR)/libs/android/$(TARGET_ARCH_ABI)/$(STL)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE:=firebase_app
-LOCAL_SRC_FILES:=$(FIREBASE_LIBRARY_PATH)/libapp.a
-LOCAL_EXPORT_C_INCLUDES:=$(FIREBASE_CPP_SDK_DIR)/include
-include $(PREBUILT_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE:=firebase_admob
-LOCAL_SRC_FILES:=$(FIREBASE_LIBRARY_PATH)/libadmob.a
-LOCAL_EXPORT_C_INCLUDES:=$(FIREBASE_CPP_SDK_DIR)/include
-include $(PREBUILT_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE:=android_main
-LOCAL_SRC_FILES:=\
- $(LOCAL_PATH)/src/common_main.cc \
- $(LOCAL_PATH)/src/android/android_main.cc
-LOCAL_STATIC_LIBRARIES:=\
- firebase_admob \
- firebase_app
-LOCAL_WHOLE_STATIC_LIBRARIES:=\
- android_native_app_glue
-LOCAL_C_INCLUDES:=\
- $(NDK_ROOT)/sources/android/native_app_glue \
- $(LOCAL_PATH)/src
-LOCAL_LDLIBS:=-llog -landroid -latomic
-LOCAL_ARM_MODE:=arm
-LOCAL_LDFLAGS:=-Wl,-z,defs -Wl,--no-undefined
-include $(BUILD_SHARED_LIBRARY)
-
-$(call import-add-path,$(NDK_ROOT)/sources/android)
-$(call import-module,android/native_app_glue)
diff --git a/admob/testapp/jni/Application.mk b/admob/testapp/jni/Application.mk
deleted file mode 100644
index 53ed56a2..00000000
--- a/admob/testapp/jni/Application.mk
+++ /dev/null
@@ -1,20 +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.
-
-APP_PLATFORM:=android-14
-NDK_TOOLCHAIN_VERSION=clang
-APP_ABI:=armeabi armeabi-v7a arm64-v8a x86 x86_64 mips mips64
-APP_STL:=c++_static
-APP_MODULES:=android_main
-APP_CPPFLAGS+=-std=c++11
diff --git a/admob/testapp/readme.md b/admob/testapp/readme.md
deleted file mode 100644
index 74300c31..00000000
--- a/admob/testapp/readme.md
+++ /dev/null
@@ -1,141 +0,0 @@
-Firebase AdMob Quickstart
-==============================
-
-The Firebase AdMob Test Application (testapp) demonstrates loading and showing
-banners and interstitials using the Firebase AdMob C++ SDK. The application
-has no user interface and simply logs actions it's performing to the console
-while displaying the ads.
-
-Introduction
-------------
-
-- [Read more about Firebase AdMob](https://firebase.google.com/docs/admob)
-
-Getting Started
----------------
-
-### iOS
- - Link your iOS app to the Firebase libraries.
- - Get CocoaPods version 1 or later by running,
- ```
- $ sudo gem install CocoaPods --pre
- ```
- - From the testapp directory, install the CocoaPods listed in the Podfile
- by running,
- ```
- $ pod install
- ```
- - Open the generated Xcode workspace (which now has the CocoaPods),
- ```
- $ open testapp.xcworkspace
- ```
- - For further details please refer to the
- [general instructions for setting up an iOS app with Firebase](https://firebase.google.com/docs/ios/setup).
- - Register your iOS app with Firebase.
- - Create a new app on the [Firebase console](https://firebase.google.com/console/), and attach
- your iOS app to it.
- - You can use "com.google.ios.admob.testapp" as the iOS Bundle ID
- while you're testing. You can omit App Store ID while testing.
- - Download the Firebase C++ SDK linked from
- https://firebase.google.com/docs/cpp/setup and unzip it to a
- directory of your choice.
- - Add the following frameworks from the Firebase C++ SDK to the project:
- - frameworks/ios/universal/firebase.framework
- - frameworks/ios/universal/firebase_admob.framework
- - You will need to either,
- 1. Check "Copy items if needed" when adding the frameworks, or
- 2. Add the framework path in "Framework Search Paths"
- - For example, if you downloaded the Firebase C++ SDK to
- `/Users/me/firebase_cpp_sdk`,
- then you would add the path
- `/Users/me/firebase_cpp_sdk/frameworks/ios/universal`.
- - To add the path, in Xcode, select your project in the project
- navigator, then select your target in the main window.
- Select the "Build Settings" tab, and click "All" to see all
- the build settings. Scroll down to "Search Paths", and add
- your path to "Framework Search Paths".
- - 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. The output of the app can
- be viewed via the console. To view the conscole in Xcode, select
- "View --> Debug Area --> Activate Console" from the menu.
- - For Rewarded Video, you must supply your own ad unit. Learn more in the
- [Rewarded Video C++ guide](http://firebase.google.com/docs/admob/cpp/rewarded-video).
-
-### Android
- - Register your Android app with Firebase.
- - Create a new app on the [Firebase console](https://firebase.google.com/console/), and attach
- your Android app to it.
- - You can use "com.google.android.admob.testapp" as the Package Name
- while you're testing.
- - To [generate a SHA1](https://developers.google.com/android/guides/client-auth)
- run this command on Mac and Linux,
- ```
- keytool -exportcert -list -v -alias androiddebugkey -keystore ~/.android/debug.keystore
- ```
- or this command on Windows,
- ```
- keytool -exportcert -list -v -alias androiddebugkey -keystore %USERPROFILE%\.android\debug.keystore
- ```
- - If keytool reports that you do not have a debug.keystore, you can
- [create one with](http://developer.android.com/tools/publishing/app-signing.html#signing-manually),
- ```
- keytool -genkey -v -keystore ~/.android/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"
- ```
- - Add the `google-services.json` file that you downloaded from Firebase
- console to the root directory of testapp. This file identifies your
- Android app to the Firebase backend.
- - For further details please refer to the
- [general instructions for setting up an Android app with Firebase](https://firebase.google.com/docs/android/setup).
- - Download the Firebase C++ SDK linked from
- https://firebase.google.com/docs/cpp/setup and unzip it to a
- directory of your choice.
- - Configure the location of the Firebase C++ SDK by setting the
- firebase\_cpp\_sdk.dir Gradle property to the SDK install directory.
- For example, in the project directory:
- ```
- > echo "systemProp.firebase\_cpp\_sdk.dir=/User/$USER/firebase\_cpp\_sdk" >> gradle.properties
- ```
- - Ensure the Android SDK and NDK locations are set in Android Studio.
- - From the Android Studio launch menu, go to
- Configure/Project Defaults/Project Structure and download the SDK and NDK if
- the locations are not yet set.
- - Open *build.gradle* in Android Studio.
- - From the Android Studio launch menu, "Open an existing Android Studio
- project", and select `build.gradle`.
- - Install the SDK Platforms that Android Studio reports missing.
- - Build the testapp and run it on an Android device or emulator.
- - The testapp will initialize AdMob, then load and display a test banner and
- a test interstitial.
- - Tapping on an ad to verify the clickthrough process is possible, and the
- interstitial will wait to be closed by the user.
- - While this is happening, information from the device log will be written
- to an onscreen TextView.
- - Logcat can also be used as normal.
- - For Rewarded Video, you must supply your own ad unit. Learn more in the
- [Rewarded Video C++ guide](http://firebase.google.com/docs/admob/cpp/rewarded-video).
-
-Support
--------
-
-https://firebase.google.com/support/
-
-License
--------
-
-Copyright 2016 Google, Inc.
-
-Licensed to the Apache Software Foundation (ASF) under one or more contributor
-license agreements. See the NOTICE file distributed with this work for
-additional information regarding copyright ownership. The ASF licenses this
-file to you under the Apache License, Version 2.0 (the "License"); you may not
-use this file except in compliance with the License. You may obtain a copy of
-the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-License for the specific language governing permissions and limitations under
-the License.
diff --git a/admob/testapp/src/common_main.cc b/admob/testapp/src/common_main.cc
deleted file mode 100644
index abf34c61..00000000
--- a/admob/testapp/src/common_main.cc
+++ /dev/null
@@ -1,495 +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/native_express_ad_view.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 a NativeExpressAdView.
-class LoggingNativeExpressAdViewListener
- : public firebase::admob::NativeExpressAdView::Listener {
- public:
- LoggingNativeExpressAdViewListener() {}
- void OnPresentationStateChanged(
- firebase::admob::NativeExpressAdView* native_express_ad_view,
- firebase::admob::NativeExpressAdView::PresentationState state) override {
- ::LogMessage("NativeExpressAdView PresentationState has changed to %d.",
- state);
- }
- void OnBoundingBoxChanged(
- firebase::admob::NativeExpressAdView* native_express_ad_view,
- firebase::admob::BoundingBox box) override {
- ::LogMessage(
- "NativeExpressAdView 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 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__)
-const char* kAdMobAppID = "ca-app-pub-3940256099942544~3347511713";
-#else
-const char* kAdMobAppID = "ca-app-pub-3940256099942544~1458002511";
-#endif
-
-// These ad units are configured to always serve test ads.
-#if defined(__ANDROID__)
-const char* kBannerAdUnit = "ca-app-pub-3940256099942544/6300978111";
-const char* kInterstitialAdUnit = "ca-app-pub-3940256099942544/1033173712";
-const char* kNativeExpressAdUnit = "ca-app-pub-3940256099942544/1072772517";
-const char* kRewardedVideoAdUnit = "YOUR_REWARDED_VIDEO_AD_UNIT_ID";
-#else
-const char* kBannerAdUnit = "ca-app-pub-3940256099942544/2934735716";
-const char* kInterstitialAdUnit = "ca-app-pub-3940256099942544/4411468910";
-const char* kNativeExpressAdUnit = "ca-app-pub-3940256099942544/2562852117";
-const char* kRewardedVideoAdUnit = "YOUR_REWARDED_VIDEO_AD_UNIT_ID";
-#endif
-
-// Standard mobile banner size is 320x50.
-static const int kBannerWidth = 320;
-static const int kBannerHeight = 50;
-
-// The native express ad's width and height.
-static const int kNativeExpressAdWidth = 320;
-static const int kNativeExpressAdHeight = 220;
-
-// 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);
- }
-
- // Create an ad size for the NativeExpressAdView.
- firebase::admob::AdSize native_express_ad_size;
- native_express_ad_size.ad_size_type = firebase::admob::kAdSizeStandard;
- native_express_ad_size.width = kNativeExpressAdWidth;
- native_express_ad_size.height = kNativeExpressAdHeight;
-
- LogMessage("Creating the NativeExpressAdView.");
- firebase::admob::NativeExpressAdView* native_express_ad =
- new firebase::admob::NativeExpressAdView();
- native_express_ad->Initialize(GetWindowContext(), kNativeExpressAdUnit,
- native_express_ad_size);
-
- WaitForFutureCompletion(native_express_ad->InitializeLastResult());
-
- // Set the listener.
- LoggingNativeExpressAdViewListener native_express_ad_listener;
- native_express_ad->SetListener(&native_express_ad_listener);
-
- // Load the native express ad.
- LogMessage("Loading a native express ad.");
- native_express_ad->LoadAd(request);
-
- WaitForFutureCompletion(native_express_ad->LoadAdLastResult());
-
- // Make the NativeExpressAdView visible.
- LogMessage("Showing the native express ad.");
- native_express_ad->Show();
-
- WaitForFutureCompletion(native_express_ad->ShowLastResult());
-
- // Move to each of the six pre-defined positions.
- LogMessage("Moving the native express ad to top-center.");
- native_express_ad->MoveTo(firebase::admob::NativeExpressAdView::kPositionTop);
-
- WaitForFutureCompletion(native_express_ad->MoveToLastResult());
-
- LogMessage("Moving the native express ad to top-left.");
- native_express_ad->MoveTo(
- firebase::admob::NativeExpressAdView::kPositionTopLeft);
-
- WaitForFutureCompletion(native_express_ad->MoveToLastResult());
-
- LogMessage("Moving the native express ad to top-right.");
- native_express_ad->MoveTo(
- firebase::admob::NativeExpressAdView::kPositionTopRight);
-
- WaitForFutureCompletion(native_express_ad->MoveToLastResult());
-
- LogMessage("Moving the native express ad to bottom-center.");
- native_express_ad->MoveTo(
- firebase::admob::NativeExpressAdView::kPositionBottom);
-
- WaitForFutureCompletion(native_express_ad->MoveToLastResult());
-
- LogMessage("Moving the native express ad to bottom-left.");
- native_express_ad->MoveTo(
- firebase::admob::NativeExpressAdView::kPositionBottomLeft);
-
- WaitForFutureCompletion(native_express_ad->MoveToLastResult());
-
- LogMessage("Moving the native express ad to bottom-right.");
- native_express_ad->MoveTo(
- firebase::admob::NativeExpressAdView::kPositionBottomRight);
-
- WaitForFutureCompletion(native_express_ad->MoveToLastResult());
-
- // Try some coordinate moves.
- LogMessage("Moving the native express ad to (100, 300).");
- native_express_ad->MoveTo(100, 300);
-
- WaitForFutureCompletion(native_express_ad->MoveToLastResult());
-
- LogMessage("Moving the native express ad to (100, 400).");
- native_express_ad->MoveTo(100, 400);
-
- WaitForFutureCompletion(native_express_ad->MoveToLastResult());
-
- // Try hiding and showing the NativeExpressAdView.
- LogMessage("Hiding the native express ad.");
- native_express_ad->Hide();
-
- WaitForFutureCompletion(native_express_ad->HideLastResult());
-
- LogMessage("Showing the native express ad.");
- native_express_ad->Show();
-
- WaitForFutureCompletion(native_express_ad->ShowLastResult());
-
- // A few last moves after showing it again.
- LogMessage("Moving the native express ad to (100, 300).");
- native_express_ad->MoveTo(100, 300);
-
- WaitForFutureCompletion(native_express_ad->MoveToLastResult());
-
- LogMessage("Moving the native express ad to (100, 400).");
- native_express_ad->MoveTo(100, 400);
-
- WaitForFutureCompletion(native_express_ad->MoveToLastResult());
-
- LogMessage("Hiding the native express ad now that we're done with it.");
- native_express_ad->Hide();
-
- WaitForFutureCompletion(native_express_ad->HideLastResult());
-
- // 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());
- } else {
- // Rewarded Video returned an error. This might be because the
- // developer did not put their Rewarded Video ad unit into
- // kRewardedVideoAdUnit above.
- LogMessage("WARNING: Is your Rewarded Video ad unit ID correct?");
- LogMessage(
- "Ensure kRewardedVideoAdUnit is set to your own Rewarded Video ad unit "
- "ID in src/common_main.cc.");
- }
-
- LogMessage("Done!");
-
- // Wait until the user kills the app.
- while (!ProcessEvents(1000)) {
- }
-
- delete banner;
- delete interstitial;
- delete native_express_ad;
- rewarded_video::Destroy();
- firebase::admob::Terminate();
- delete app;
-
- return 0;
-}
diff --git a/analytics/testapp/AndroidManifest.xml b/analytics/testapp/AndroidManifest.xml
index 3a7b2043..b7038a15 100644
--- a/analytics/testapp/AndroidManifest.xml
+++ b/analytics/testapp/AndroidManifest.xml
@@ -6,9 +6,10 @@
-
+
)
+ set(FOUND_JSON_FILE TRUE)
+ break()
+ endif()
+ endforeach()
+ if(NOT FOUND_JSON_FILE)
+ message(WARNING "Failed to find either google-services-desktop.json or google-services.json. See the readme.md for more information.")
+ endif()
+endif()
+
+# Add the Firebase libraries to the target using the function from the SDK.
+add_subdirectory(${FIREBASE_CPP_SDK_DIR} bin/ EXCLUDE_FROM_ALL)
+# Note that firebase_app needs to be last in the list.
+set(firebase_libs firebase_analytics firebase_app)
+target_link_libraries(${target_name} "${firebase_libs}" ${ADDITIONAL_LIBS})
+
diff --git a/analytics/testapp/Podfile b/analytics/testapp/Podfile
index f701ef72..5895e3a3 100644
--- a/analytics/testapp/Podfile
+++ b/analytics/testapp/Podfile
@@ -1,6 +1,7 @@
source 'https://github.com/CocoaPods/Specs.git'
-platform :ios, '8.0'
+platform :ios, '13.0'
+use_frameworks!
# Analytics test application.
target 'testapp' do
- pod 'Firebase/Analytics'
+ pod 'Firebase/Analytics', '10.25.0'
end
diff --git a/analytics/testapp/build.gradle b/analytics/testapp/build.gradle
index ddcfe2c3..42939198 100644
--- a/analytics/testapp/build.gradle
+++ b/analytics/testapp/build.gradle
@@ -2,117 +2,75 @@
buildscript {
repositories {
mavenLocal()
+ maven { url 'https://maven.google.com' }
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:2.1.2'
- classpath 'com.google.gms:google-services:3.0.0'
+ classpath 'com.android.tools.build:gradle:4.2.1'
+ classpath 'com.google.gms:google-services:4.0.1'
}
}
allprojects {
repositories {
mavenLocal()
+ maven { url 'https://maven.google.com' }
jcenter()
}
}
apply plugin: 'com.android.application'
-// Pre-experimental Gradle plug-in NDK boilerplate below.
-// Right now the Firebase plug-in does not work with the experimental
-// Gradle plug-in so we're using ndk-build for the moment.
-project.ext {
- // Configure the Firebase C++ SDK location.
- firebase_cpp_sdk_dir = System.getProperty('firebase_cpp_sdk.dir')
- if (firebase_cpp_sdk_dir == null || firebase_cpp_sdk_dir.isEmpty()) {
- firebase_cpp_sdk_dir = System.getenv('FIREBASE_CPP_SDK_DIR')
- if (firebase_cpp_sdk_dir == null || firebase_cpp_sdk_dir.isEmpty()) {
- if ((new File('firebase_cpp_sdk')).exists()) {
- firebase_cpp_sdk_dir = 'firebase_cpp_sdk'
- } else {
- throw new StopActionException(
- 'firebase_cpp_sdk.dir property or the FIREBASE_CPP_SDK_DIR ' +
- 'environment variable must be set to reference the Firebase C++ ' +
- 'SDK install directory. This is used to configure static library ' +
- 'and C/C++ include paths for the SDK.')
- }
- }
- }
- if (!(new File(firebase_cpp_sdk_dir)).exists()) {
- throw new StopActionException(
- sprintf('Firebase C++ SDK directory %s does not exist',
- firebase_cpp_sdk_dir))
- }
- // Check the NDK location using the same configuration options as the
- // experimental Gradle plug-in.
- ndk_dir = project.android.ndkDirectory
- if (ndk_dir == null || !ndk_dir.exists()) {
- ndk_dir = System.getenv('ANDROID_NDK_HOME')
- if (ndk_dir == null || ndk_dir.isEmpty()) {
- throw new StopActionException(
- 'Android NDK directory should be specified using the ndk.dir ' +
- 'property or ANDROID_NDK_HOME environment variable.')
- }
- }
-}
-
android {
- compileSdkVersion 23
- buildToolsVersion '23.0.3'
+ compileOptions {
+ sourceCompatibility 1.8
+ targetCompatibility 1.8
+ }
+ compileSdkVersion 34
+ ndkPath System.getenv('ANDROID_NDK_HOME')
+ buildToolsVersion '30.0.2'
- sourceSets {
- main {
- jniLibs.srcDirs = ['libs']
- manifest.srcFile 'AndroidManifest.xml'
- java.srcDirs = ['src/android/java']
- res.srcDirs = ['res']
- }
+ sourceSets {
+ main {
+ jniLibs.srcDirs = ['libs']
+ manifest.srcFile 'AndroidManifest.xml'
+ java.srcDirs = ['src/android/java']
+ res.srcDirs = ['res']
}
+ }
- defaultConfig {
- applicationId 'com.google.android.analytics.testapp'
- minSdkVersion 14
- targetSdkVersion 23
- versionCode 1
- versionName '1.0'
+ defaultConfig {
+ applicationId 'com.google.android.analytics.testapp'
+ minSdkVersion 23
+ targetSdkVersion 28
+ versionCode 1
+ versionName '1.0'
+ externalNativeBuild.cmake {
+ arguments "-DFIREBASE_CPP_SDK_DIR=$gradle.firebase_cpp_sdk_dir"
}
- buildTypes {
- release {
- minifyEnabled true
- proguardFile getDefaultProguardFile('proguard-android.txt')
- proguardFile file(project.ext.firebase_cpp_sdk_dir + "/libs/android/app.pro")
- proguardFile file(project.ext.firebase_cpp_sdk_dir + "/libs/android/analytics.pro")
- proguardFile file('proguard.pro')
- }
+ }
+ externalNativeBuild.cmake {
+ path 'CMakeLists.txt'
+ }
+ buildTypes {
+ release {
+ minifyEnabled true
+ proguardFile getDefaultProguardFile('proguard-android.txt')
+ proguardFile file('proguard.pro')
}
+ }
+ packagingOptions {
+ pickFirst 'META-INF/**/coroutines.pro'
+ }
+ lintOptions {
+ abortOnError false
+ checkReleaseBuilds false
+ }
}
-dependencies {
- compile 'com.google.firebase:firebase-analytics:10.2.0'
- compile 'com.google.android.gms:play-services-base:10.2.0'
+apply from: "$gradle.firebase_cpp_sdk_dir/Android/firebase_dependencies.gradle"
+firebaseCpp.dependencies {
+ analytics
}
apply plugin: 'com.google.gms.google-services'
-
-task ndkBuildCompile(type:Exec) {
- description 'Use ndk-build to compile the C++ application.'
- commandLine("${project.ext.ndk_dir}${File.separator}ndk-build",
- "FIREBASE_CPP_SDK_DIR=${project.ext.firebase_cpp_sdk_dir}",
- sprintf("APP_PLATFORM=android-%d",
- android.defaultConfig.minSdkVersion.mApiLevel))
-}
-
-task ndkBuildClean(type:Exec) {
- description 'Use ndk-build to clean the C++ application.'
- commandLine("${project.ext.ndk_dir}${File.separator}ndk-build",
- "FIREBASE_CPP_SDK_DIR=${project.ext.firebase_cpp_sdk_dir}",
- "clean")
-}
-
-// Once the Android Gradle plug-in has generated tasks, add dependencies for
-// the ndk-build targets.
-project.afterEvaluate {
- preBuild.dependsOn(ndkBuildCompile)
- clean.dependsOn(ndkBuildClean)
-}
diff --git a/analytics/testapp/gradle.properties b/analytics/testapp/gradle.properties
new file mode 100644
index 00000000..d7ba8f42
--- /dev/null
+++ b/analytics/testapp/gradle.properties
@@ -0,0 +1 @@
+android.useAndroidX = true
diff --git a/analytics/testapp/gradle/wrapper/gradle-wrapper.properties b/analytics/testapp/gradle/wrapper/gradle-wrapper.properties
index d5705170..65340c1b 100644
--- a/analytics/testapp/gradle/wrapper/gradle-wrapper.properties
+++ b/analytics/testapp/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Wed Apr 10 15:27:10 PDT 2013
+#Mon Nov 27 14:03:45 PST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
+distributionUrl=https://services.gradle.org/distributions/gradle-6.7.1-all.zip
diff --git a/analytics/testapp/jni/Android.mk b/analytics/testapp/jni/Android.mk
deleted file mode 100644
index c48cac71..00000000
--- a/analytics/testapp/jni/Android.mk
+++ /dev/null
@@ -1,57 +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.
-
-LOCAL_PATH:=$(call my-dir)/..
-
-ifeq ($(FIREBASE_CPP_SDK_DIR),)
-$(error FIREBASE_CPP_SDK_DIR must specify the Firebase package location.)
-endif
-
-# With Firebase libraries for the selected build configuration (ABI + STL)
-STL:=$(firstword $(subst _, ,$(APP_STL)))
-FIREBASE_LIBRARY_PATH:=\
-$(FIREBASE_CPP_SDK_DIR)/libs/android/$(TARGET_ARCH_ABI)/$(STL)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE:=firebase_app
-LOCAL_SRC_FILES:=$(FIREBASE_LIBRARY_PATH)/libapp.a
-LOCAL_EXPORT_C_INCLUDES:=$(FIREBASE_CPP_SDK_DIR)/include
-include $(PREBUILT_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE:=firebase_analytics
-LOCAL_SRC_FILES:=$(FIREBASE_LIBRARY_PATH)/libanalytics.a
-LOCAL_EXPORT_C_INCLUDES:=$(FIREBASE_CPP_SDK_DIR)/include
-include $(PREBUILT_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE:=android_main
-LOCAL_SRC_FILES:=\
- $(LOCAL_PATH)/src/common_main.cc \
- $(LOCAL_PATH)/src/android/android_main.cc
-LOCAL_STATIC_LIBRARIES:=\
- firebase_analytics \
- firebase_app
-LOCAL_WHOLE_STATIC_LIBRARIES:=\
- android_native_app_glue
-LOCAL_C_INCLUDES:=\
- $(NDK_ROOT)/sources/android/native_app_glue \
- $(LOCAL_PATH)/src
-LOCAL_LDLIBS:=-llog -landroid -latomic
-LOCAL_ARM_MODE:=arm
-LOCAL_LDFLAGS:=-Wl,-z,defs -Wl,--no-undefined
-include $(BUILD_SHARED_LIBRARY)
-
-$(call import-add-path,$(NDK_ROOT)/sources/android)
-$(call import-module,android/native_app_glue)
diff --git a/analytics/testapp/jni/Application.mk b/analytics/testapp/jni/Application.mk
deleted file mode 100644
index 53ed56a2..00000000
--- a/analytics/testapp/jni/Application.mk
+++ /dev/null
@@ -1,20 +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.
-
-APP_PLATFORM:=android-14
-NDK_TOOLCHAIN_VERSION=clang
-APP_ABI:=armeabi armeabi-v7a arm64-v8a x86 x86_64 mips mips64
-APP_STL:=c++_static
-APP_MODULES:=android_main
-APP_CPPFLAGS+=-std=c++11
diff --git a/analytics/testapp/proguard.pro b/analytics/testapp/proguard.pro
index c7e9278d..54cd248b 100644
--- a/analytics/testapp/proguard.pro
+++ b/analytics/testapp/proguard.pro
@@ -1 +1,2 @@
+-ignorewarnings
-keep,includedescriptorclasses public class com.google.firebase.example.LoggingUtils { *; }
diff --git a/analytics/testapp/readme.md b/analytics/testapp/readme.md
index 0b328d9a..374488a6 100644
--- a/analytics/testapp/readme.md
+++ b/analytics/testapp/readme.md
@@ -17,16 +17,16 @@ Building and Running the testapp
- Link your iOS app to the Firebase libraries.
- Get CocoaPods version 1 or later by running,
```
- $ sudo gem install CocoaPods --pre
+ sudo gem install cocoapods --pre
```
- From the testapp directory, install the CocoaPods listed in the Podfile
by running,
```
- $ pod install
+ pod install
```
- Open the generated Xcode workspace (which now has the CocoaPods),
```
- $ open testapp.xcworkspace
+ open testapp.xcworkspace
```
- For further details please refer to the
[general instructions for setting up an iOS app with Firebase](https://firebase.google.com/docs/ios/setup).
@@ -39,8 +39,8 @@ Building and Running the testapp
console to the testapp root directory. This file identifies your iOS app
to the Firebase backend.
- Download the Firebase C++ SDK linked from
- https://firebase.google.com/docs/cpp/setup and unzip it to a
- directory of your choice.
+ [https://firebase.google.com/docs/cpp/setup](https://firebase.google.com/docs/cpp/setup)
+ and unzip it to a directory of your choice.
- Add the following frameworks from the Firebase C++ SDK to the project:
- frameworks/ios/universal/firebase.framework
- frameworks/ios/universal/firebase\_analytics.framework
@@ -62,7 +62,7 @@ Building and Running the testapp
"View --> Debug Area --> Activate Console" from the menu.
- After 5 hours, data should be visible in the Firebase Console under the
"Analytics" tab accessible from
- https://firebase.google.com/console/.
+ [https://firebase.google.com/console/](https://firebase.google.com/console/).
### Android
- Register your Android app with Firebase.
@@ -90,18 +90,19 @@ Building and Running the testapp
- For further details please refer to the
[general instructions for setting up an Android app with Firebase](https://firebase.google.com/docs/android/setup).
- Download the Firebase C++ SDK linked from
- https://firebase.google.com/docs/cpp/setup and unzip it to a
- directory of your choice.
+ [https://firebase.google.com/docs/cpp/setup](https://firebase.google.com/docs/cpp/setup)
+ and unzip it to a directory of your choice.
- Configure the location of the Firebase C++ SDK by setting the
firebase\_cpp\_sdk.dir Gradle property to the SDK install directory.
For example, in the project directory:
```
- > echo "systemProp.firebase\_cpp\_sdk.dir=/User/$USER/firebase\_cpp\_sdk" >> gradle.properties
+ echo "systemProp.firebase\_cpp\_sdk.dir=/User/$USER/firebase\_cpp\_sdk" >> gradle.properties
```
- Ensure the Android SDK and NDK locations are set in Android Studio.
- - From the Android Studio launch menu, go to
- Configure/Project Defaults/Project Structure and download the SDK and NDK if
- the locations are not yet set.
+ - From the Android Studio launch menu, go to `File/Project Structure...` or
+ `Configure/Project Defaults/Project Structure...`
+ (Shortcut: Control + Alt + Shift + S on windows, Command + ";" on a mac)
+ and download the SDK and NDK if the locations are not yet set.
- Open *build.gradle* in Android Studio.
- From the Android Studio launch menu, "Open an existing Android Studio
project", and select `build.gradle`.
@@ -112,12 +113,56 @@ Building and Running the testapp
the command line.
- After 5 hours, data should be visible in the Firebase Console under the
"Analytics" tab accessible from
- https://firebase.google.com/console/.
+ [https://firebase.google.com/console/](https://firebase.google.com/console/).
+
+### Desktop
+ - Register your app with Firebase.
+ - Create a new app on the [Firebase console](https://firebase.google.com/console/),
+ following the above instructions for Android or iOS.
+ - If you have an Android project, add the `google-services.json` file that
+ you downloaded from the Firebase console to the root directory of the
+ testapp.
+ - If you have an iOS project, and don't wish to use an Android project,
+ you can use the Python script `generate_xml_from_google_services_json.py --plist`,
+ located in the Firebase C++ SDK, to convert your `GoogleService-Info.plist`
+ file into a `google-services-desktop.json` file, which can then be
+ placed in the root directory of the testapp.
+ - Download the Firebase C++ SDK linked from
+ [https://firebase.google.com/docs/cpp/setup](https://firebase.google.com/docs/cpp/setup)
+ and unzip it to a directory of your choice.
+ - Configure the testapp with the location of the Firebase C++ SDK.
+ This can be done a couple different ways (in highest to lowest priority):
+ - When invoking cmake, pass in the location with
+ -DFIREBASE_CPP_SDK_DIR=/path/to/firebase_cpp_sdk.
+ - Set an environment variable for FIREBASE_CPP_SDK_DIR to the path to use.
+ - Edit the CMakeLists.txt file, changing the FIREBASE_CPP_SDK_DIR path
+ to the appropriate location.
+ - From the testapp directory, generate the build files by running,
+ ```
+ cmake .
+ ```
+ If you want to use XCode, you can use -G"Xcode" to generate the project.
+ Similarly, to use Visual Studio, -G"Visual Studio 15 2017". For more
+ information, see
+ [CMake generators](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html).
+ - Build the testapp, by either opening the generated project file based on
+ the platform, or running,
+ ```
+ cmake --build .
+ ```
+ - Execute the testapp by running,
+ ```
+ ./desktop_testapp
+ ```
+ Note that the executable might be under another directory, such as Debug.
+ - The testapp has no user interface, but the output can be viewed via the
+ console. Note that Analytics uses a stubbed implementation on desktop,
+ so functionality is not expected.
Support
-------
-https://firebase.google.com/support/
+[https://firebase.google.com/support/](https://firebase.google.com/support/)
License
-------
@@ -138,4 +183,3 @@ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations under
the License.
-
diff --git a/analytics/testapp/settings.gradle b/analytics/testapp/settings.gradle
new file mode 100644
index 00000000..2a543b93
--- /dev/null
+++ b/analytics/testapp/settings.gradle
@@ -0,0 +1,36 @@
+// Copyright 2018 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+def firebase_cpp_sdk_dir = System.getProperty('firebase_cpp_sdk.dir')
+if (firebase_cpp_sdk_dir == null || firebase_cpp_sdk_dir.isEmpty()) {
+ firebase_cpp_sdk_dir = System.getenv('FIREBASE_CPP_SDK_DIR')
+ if (firebase_cpp_sdk_dir == null || firebase_cpp_sdk_dir.isEmpty()) {
+ if ((new File('firebase_cpp_sdk')).exists()) {
+ firebase_cpp_sdk_dir = 'firebase_cpp_sdk'
+ } else {
+ throw new StopActionException(
+ 'firebase_cpp_sdk.dir property or the FIREBASE_CPP_SDK_DIR ' +
+ 'environment variable must be set to reference the Firebase C++ ' +
+ 'SDK install directory. This is used to configure static library ' +
+ 'and C/C++ include paths for the SDK.')
+ }
+ }
+}
+if (!(new File(firebase_cpp_sdk_dir)).exists()) {
+ throw new StopActionException(
+ sprintf('Firebase C++ SDK directory %s does not exist',
+ firebase_cpp_sdk_dir))
+}
+gradle.ext.firebase_cpp_sdk_dir = "$firebase_cpp_sdk_dir"
+includeBuild "$firebase_cpp_sdk_dir"
\ No newline at end of file
diff --git a/analytics/testapp/src/common_main.cc b/analytics/testapp/src/common_main.cc
index 9b063622..e063e416 100644
--- a/analytics/testapp/src/common_main.cc
+++ b/analytics/testapp/src/common_main.cc
@@ -40,10 +40,22 @@ extern "C" int common_main(int argc, const char* argv[]) {
LogMessage("Enabling data collection.");
analytics::SetAnalyticsCollectionEnabled(true);
- // App needs to be open at least 1s before logging a valid session.
- analytics::SetMinimumSessionDuration(1000);
- // App session times out after 5s.
- analytics::SetSessionTimeoutDuration(5000);
+ // App session times out after 30 minutes.
+ // If the app is placed in the background and returns to the foreground after
+ // the timeout is expired analytics will log a new session.
+ analytics::SetSessionTimeoutDuration(1000 * 60 * 30);
+
+ LogMessage("Get App Instance ID...");
+ auto future_result = analytics::GetAnalyticsInstanceId();
+ while (future_result.status() == firebase::kFutureStatusPending) {
+ if (ProcessEvents(1000)) break;
+ }
+ if (future_result.status() == firebase::kFutureStatusComplete) {
+ LogMessage("Analytics Instance ID %s", future_result.result()->c_str());
+ } else {
+ LogMessage("ERROR: Failed to fetch Analytics Instance ID %s (%d)",
+ future_result.error_message(), future_result.error());
+ }
LogMessage("Set user properties.");
// Set the user's sign up method.
@@ -51,9 +63,9 @@ extern "C" int common_main(int argc, const char* argv[]) {
// Set the user ID.
analytics::SetUserId("uber_user_510");
- LogMessage("Set current screen.");
- // Set the user's current screen.
- analytics::SetCurrentScreen("Firebase Analytics C++ testapp", "testapp");
+ LogMessage("Log current screen.");
+ // Log the user's current screen.
+ analytics::LogEvent(analytics::kEventScreenView, "Firebase Analytics C++ testapp", "testapp" );
// Log an event with no parameters.
LogMessage("Log login event.");
diff --git a/analytics/testapp/src/desktop/desktop_main.cc b/analytics/testapp/src/desktop/desktop_main.cc
index 99ace543..0220c688 100644
--- a/analytics/testapp/src/desktop/desktop_main.cc
+++ b/analytics/testapp/src/desktop/desktop_main.cc
@@ -15,16 +15,35 @@
#include
#include
#include
-#ifndef _WIN32
+
+#ifdef _WIN32
+#include
+#define chdir _chdir
+#else
#include
-#endif // !_WIN32
+#endif // _WIN32
#ifdef _WIN32
#include
#endif // _WIN32
+#include
+#include
+
#include "main.h" // NOLINT
+// The TO_STRING macro is useful for command line defined strings as the quotes
+// get stripped.
+#define TO_STRING_EXPAND(X) #X
+#define TO_STRING(X) TO_STRING_EXPAND(X)
+
+// Path to the Firebase config file to load.
+#ifdef FIREBASE_CONFIG
+#define FIREBASE_CONFIG_STRING TO_STRING(FIREBASE_CONFIG)
+#else
+#define FIREBASE_CONFIG_STRING ""
+#endif // FIREBASE_CONFIG
+
extern "C" int common_main(int argc, const char* argv[]);
static bool quit = false;
@@ -50,6 +69,10 @@ bool ProcessEvents(int msec) {
return quit;
}
+std::string PathForResource() {
+ return std::string();
+}
+
void LogMessage(const char* format, ...) {
va_list list;
va_start(list, format);
@@ -61,7 +84,22 @@ void LogMessage(const char* format, ...) {
WindowContext GetWindowContext() { return nullptr; }
+// Change the current working directory to the directory containing the
+// specified file.
+void ChangeToFileDirectory(const char* file_path) {
+ std::string path(file_path);
+ std::replace(path.begin(), path.end(), '\\', '/');
+ auto slash = path.rfind('/');
+ if (slash != std::string::npos) {
+ std::string directory = path.substr(0, slash);
+ if (!directory.empty()) chdir(directory.c_str());
+ }
+}
+
int main(int argc, const char* argv[]) {
+ ChangeToFileDirectory(
+ FIREBASE_CONFIG_STRING[0] != '\0' ?
+ FIREBASE_CONFIG_STRING : argv[0]); // NOLINT
#ifdef _WIN32
SetConsoleCtrlHandler((PHANDLER_ROUTINE)SignalHandler, TRUE);
#else
@@ -69,3 +107,19 @@ int main(int argc, const char* argv[]) {
#endif // _WIN32
return common_main(argc, argv);
}
+
+#if defined(_WIN32)
+// Returns the number of microseconds since the epoch.
+int64_t WinGetCurrentTimeInMicroseconds() {
+ FILETIME file_time;
+ GetSystemTimeAsFileTime(&file_time);
+
+ ULARGE_INTEGER now;
+ now.LowPart = file_time.dwLowDateTime;
+ now.HighPart = file_time.dwHighDateTime;
+
+ // Windows file time is expressed in 100s of nanoseconds.
+ // To convert to microseconds, multiply x10.
+ return now.QuadPart * 10LL;
+}
+#endif
diff --git a/analytics/testapp/testapp.xcodeproj/project.pbxproj b/analytics/testapp/testapp.xcodeproj/project.pbxproj
index baf45c4c..5769362c 100644
--- a/analytics/testapp/testapp.xcodeproj/project.pbxproj
+++ b/analytics/testapp/testapp.xcodeproj/project.pbxproj
@@ -208,7 +208,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 8.4;
+ IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@@ -245,7 +245,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 8.4;
+ IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
diff --git a/analytics/testapp/testapp/Info.plist b/analytics/testapp/testapp/Info.plist
index d938a81f..4a067783 100644
--- a/analytics/testapp/testapp/Info.plist
+++ b/analytics/testapp/testapp/Info.plist
@@ -27,7 +27,7 @@
google
CFBundleURLSchemes
- com.googleusercontent.apps.255980362477-3a1nf8c4nl0c7hlnlnmc98hbtg2mnbue
+ YOUR_REVERSED_CLIENT_ID
diff --git a/auth/testapp/AndroidManifest.xml b/auth/testapp/AndroidManifest.xml
index a4705fe4..540dec54 100644
--- a/auth/testapp/AndroidManifest.xml
+++ b/auth/testapp/AndroidManifest.xml
@@ -6,9 +6,10 @@
-
+
)
+ set(FOUND_JSON_FILE TRUE)
+ break()
+ endif()
+ endforeach()
+ if(NOT FOUND_JSON_FILE)
+ message(WARNING "Failed to find either google-services-desktop.json or google-services.json. See the readme.md for more information.")
+ endif()
+endif()
+
+# Add the Firebase libraries to the target using the function from the SDK.
+add_subdirectory(${FIREBASE_CPP_SDK_DIR} bin/ EXCLUDE_FROM_ALL)
+# Note that firebase_app needs to be last in the list.
+set(firebase_libs firebase_auth firebase_app)
+target_link_libraries(${target_name} "${firebase_libs}" ${ADDITIONAL_LIBS})
diff --git a/auth/testapp/Podfile b/auth/testapp/Podfile
index ff799f87..825abee2 100644
--- a/auth/testapp/Podfile
+++ b/auth/testapp/Podfile
@@ -1,6 +1,7 @@
source 'https://github.com/CocoaPods/Specs.git'
-platform :ios, '8.0'
+platform :ios, '13.0'
+use_frameworks!
# Auth test application.
target 'testapp' do
- pod 'Firebase/Auth'
+ pod 'Firebase/Auth', '10.25.0'
end
diff --git a/auth/testapp/build.gradle b/auth/testapp/build.gradle
index b35f5d66..eb739709 100644
--- a/auth/testapp/build.gradle
+++ b/auth/testapp/build.gradle
@@ -2,117 +2,76 @@
buildscript {
repositories {
mavenLocal()
+ maven { url 'https://maven.google.com' }
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:2.1.2'
- classpath 'com.google.gms:google-services:3.0.0'
+ classpath 'com.android.tools.build:gradle:4.2.1'
+ classpath 'com.google.gms:google-services:4.0.1'
}
}
allprojects {
repositories {
mavenLocal()
+ maven { url 'https://maven.google.com' }
jcenter()
}
}
apply plugin: 'com.android.application'
-// Pre-experimental Gradle plug-in NDK boilerplate below.
-// Right now the Firebase plug-in does not work with the experimental
-// Gradle plug-in so we're using ndk-build for the moment.
-project.ext {
- // Configure the Firebase C++ SDK location.
- firebase_cpp_sdk_dir = System.getProperty('firebase_cpp_sdk.dir')
- if (firebase_cpp_sdk_dir == null || firebase_cpp_sdk_dir.isEmpty()) {
- firebase_cpp_sdk_dir = System.getenv('FIREBASE_CPP_SDK_DIR')
- if (firebase_cpp_sdk_dir == null || firebase_cpp_sdk_dir.isEmpty()) {
- if ((new File('firebase_cpp_sdk')).exists()) {
- firebase_cpp_sdk_dir = 'firebase_cpp_sdk'
- } else {
- throw new StopActionException(
- 'firebase_cpp_sdk.dir property or the FIREBASE_CPP_SDK_DIR ' +
- 'environment variable must be set to reference the Firebase C++ ' +
- 'SDK install directory. This is used to configure static library ' +
- 'and C/C++ include paths for the SDK.')
- }
- }
- }
- if (!(new File(firebase_cpp_sdk_dir)).exists()) {
- throw new StopActionException(
- sprintf('Firebase C++ SDK directory %s does not exist',
- firebase_cpp_sdk_dir))
- }
- // Check the NDK location using the same configuration options as the
- // experimental Gradle plug-in.
- ndk_dir = project.android.ndkDirectory
- if (ndk_dir == null || !ndk_dir.exists()) {
- ndk_dir = System.getenv('ANDROID_NDK_HOME')
- if (ndk_dir == null || ndk_dir.isEmpty()) {
- throw new StopActionException(
- 'Android NDK directory should be specified using the ndk.dir ' +
- 'property or ANDROID_NDK_HOME environment variable.')
- }
+android {
+ compileOptions {
+ sourceCompatibility 1.8
+ targetCompatibility 1.8
}
-}
-android {
- compileSdkVersion 23
- buildToolsVersion '23.0.3'
+ compileSdkVersion 34
+ ndkPath System.getenv('ANDROID_NDK_HOME')
+ buildToolsVersion '30.0.2'
- sourceSets {
- main {
- jniLibs.srcDirs = ['libs']
- manifest.srcFile 'AndroidManifest.xml'
- java.srcDirs = ['src/android/java']
- res.srcDirs = ['res']
- }
+ sourceSets {
+ main {
+ jniLibs.srcDirs = ['libs']
+ manifest.srcFile 'AndroidManifest.xml'
+ java.srcDirs = ['src/android/java']
+ res.srcDirs = ['res']
}
+ }
- defaultConfig {
- applicationId 'com.google.android.auth.testapp'
- minSdkVersion 14
- targetSdkVersion 23
- versionCode 1
- versionName '1.0'
+ defaultConfig {
+ applicationId 'com.google.android.auth.testapp'
+ minSdkVersion 23
+ targetSdkVersion 34
+ versionCode 1
+ versionName '1.0'
+ externalNativeBuild.cmake {
+ arguments "-DFIREBASE_CPP_SDK_DIR=$gradle.firebase_cpp_sdk_dir"
}
- buildTypes {
- release {
- minifyEnabled true
- proguardFile getDefaultProguardFile('proguard-android.txt')
- proguardFile file(project.ext.firebase_cpp_sdk_dir + "/libs/android/app.pro")
- proguardFile file(project.ext.firebase_cpp_sdk_dir + "/libs/android/auth.pro")
- proguardFile file('proguard.pro')
- }
+ }
+ externalNativeBuild.cmake {
+ path 'CMakeLists.txt'
+ }
+ buildTypes {
+ release {
+ minifyEnabled true
+ proguardFile getDefaultProguardFile('proguard-android.txt')
+ proguardFile file('proguard.pro')
}
+ }
+ packagingOptions {
+ pickFirst 'META-INF/**/coroutines.pro'
+ }
+ lintOptions {
+ abortOnError false
+ checkReleaseBuilds false
+ }
}
-dependencies {
- compile 'com.google.firebase:firebase-auth:10.2.0'
- compile 'com.google.android.gms:play-services-base:10.2.0'
+apply from: "$gradle.firebase_cpp_sdk_dir/Android/firebase_dependencies.gradle"
+firebaseCpp.dependencies {
+ auth
}
apply plugin: 'com.google.gms.google-services'
-
-task ndkBuildCompile(type:Exec) {
- description 'Use ndk-build to compile the C++ application.'
- commandLine("${project.ext.ndk_dir}${File.separator}ndk-build",
- "FIREBASE_CPP_SDK_DIR=${project.ext.firebase_cpp_sdk_dir}",
- sprintf("APP_PLATFORM=android-%d",
- android.defaultConfig.minSdkVersion.mApiLevel))
-}
-
-task ndkBuildClean(type:Exec) {
- description 'Use ndk-build to clean the C++ application.'
- commandLine("${project.ext.ndk_dir}${File.separator}ndk-build",
- "FIREBASE_CPP_SDK_DIR=${project.ext.firebase_cpp_sdk_dir}",
- "clean")
-}
-
-// Once the Android Gradle plug-in has generated tasks, add dependencies for
-// the ndk-build targets.
-project.afterEvaluate {
- preBuild.dependsOn(ndkBuildCompile)
- clean.dependsOn(ndkBuildClean)
-}
diff --git a/auth/testapp/gradle.properties b/auth/testapp/gradle.properties
new file mode 100644
index 00000000..d7ba8f42
--- /dev/null
+++ b/auth/testapp/gradle.properties
@@ -0,0 +1 @@
+android.useAndroidX = true
diff --git a/auth/testapp/gradle/wrapper/gradle-wrapper.properties b/auth/testapp/gradle/wrapper/gradle-wrapper.properties
index d5705170..65340c1b 100644
--- a/auth/testapp/gradle/wrapper/gradle-wrapper.properties
+++ b/auth/testapp/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Wed Apr 10 15:27:10 PDT 2013
+#Mon Nov 27 14:03:45 PST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
+distributionUrl=https://services.gradle.org/distributions/gradle-6.7.1-all.zip
diff --git a/auth/testapp/jni/Android.mk b/auth/testapp/jni/Android.mk
deleted file mode 100644
index 3f8e180f..00000000
--- a/auth/testapp/jni/Android.mk
+++ /dev/null
@@ -1,57 +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.
-
-LOCAL_PATH:=$(call my-dir)/..
-
-ifeq ($(FIREBASE_CPP_SDK_DIR),)
-$(error FIREBASE_CPP_SDK_DIR must specify the Firebase package location.)
-endif
-
-# With Firebase libraries for the selected build configuration (ABI + STL)
-STL:=$(firstword $(subst _, ,$(APP_STL)))
-FIREBASE_LIBRARY_PATH:=\
-$(FIREBASE_CPP_SDK_DIR)/libs/android/$(TARGET_ARCH_ABI)/$(STL)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE:=firebase_app
-LOCAL_SRC_FILES:=$(FIREBASE_LIBRARY_PATH)/libapp.a
-LOCAL_EXPORT_C_INCLUDES:=$(FIREBASE_CPP_SDK_DIR)/include
-include $(PREBUILT_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE:=firebase_auth
-LOCAL_SRC_FILES:=$(FIREBASE_LIBRARY_PATH)/libauth.a
-LOCAL_EXPORT_C_INCLUDES:=$(FIREBASE_CPP_SDK_DIR)/include
-include $(PREBUILT_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE:=android_main
-LOCAL_SRC_FILES:=\
- $(LOCAL_PATH)/src/common_main.cc \
- $(LOCAL_PATH)/src/android/android_main.cc
-LOCAL_STATIC_LIBRARIES:=\
- firebase_auth \
- firebase_app
-LOCAL_WHOLE_STATIC_LIBRARIES:=\
- android_native_app_glue
-LOCAL_C_INCLUDES:=\
- $(NDK_ROOT)/sources/android/native_app_glue \
- $(LOCAL_PATH)/src
-LOCAL_LDLIBS:=-llog -landroid -latomic
-LOCAL_ARM_MODE:=arm
-LOCAL_LDFLAGS:=-Wl,-z,defs -Wl,--no-undefined
-include $(BUILD_SHARED_LIBRARY)
-
-$(call import-add-path,$(NDK_ROOT)/sources/android)
-$(call import-module,android/native_app_glue)
diff --git a/auth/testapp/jni/Application.mk b/auth/testapp/jni/Application.mk
deleted file mode 100644
index 53ed56a2..00000000
--- a/auth/testapp/jni/Application.mk
+++ /dev/null
@@ -1,20 +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.
-
-APP_PLATFORM:=android-14
-NDK_TOOLCHAIN_VERSION=clang
-APP_ABI:=armeabi armeabi-v7a arm64-v8a x86 x86_64 mips mips64
-APP_STL:=c++_static
-APP_MODULES:=android_main
-APP_CPPFLAGS+=-std=c++11
diff --git a/auth/testapp/proguard.pro b/auth/testapp/proguard.pro
index c7e9278d..5edad15e 100644
--- a/auth/testapp/proguard.pro
+++ b/auth/testapp/proguard.pro
@@ -1 +1,3 @@
+-ignorewarnings
-keep,includedescriptorclasses public class com.google.firebase.example.LoggingUtils { *; }
+-keep,includedescriptorclasses public class com.google.firebase.example.TextEntryField { *; }
diff --git a/auth/testapp/readme.md b/auth/testapp/readme.md
index bb9448f2..04dd6fd0 100644
--- a/auth/testapp/readme.md
+++ b/auth/testapp/readme.md
@@ -33,23 +33,23 @@ Building and Running the testapp
- Link your iOS app to the Firebase libraries.
- Get CocoaPods version 1 or later by running,
```
- $ sudo gem install CocoaPods --pre
+ sudo gem install cocoapods --pre
```
- From the testapp directory, install the CocoaPods listed in the Podfile
by running,
```
- $ pod install
+ pod install
```
- Open the generated Xcode workspace (which now has the CocoaPods),
```
- $ open testapp.xcworkspace
+ open testapp.xcworkspace
```
- For further details please refer to the
[general instructions for setting up an iOS app with Firebase](https://firebase.google.com/docs/ios/setup).
- Register your iOS app with Firebase.
- Create a new app on the [Firebase console](https://firebase.google.com/console/), and attach
your iOS app to it.
- - You can use "com.google.ios.auth.testapp" as the iOS Bundle ID
+ - You can use "com.google.FirebaseCppAuthTestApp.dev" as the iOS Bundle ID
while you're testing. You can omit App Store ID while testing.
- Add the GoogleService-Info.plist that you downloaded from Firebase
console to the testapp root directory. This file identifies your iOS app
@@ -58,8 +58,8 @@ Building and Running the testapp
enable "Anonymous". This will allow the testapp to use email accounts and
anonymous sign-in.
- Download the Firebase C++ SDK linked from
- https://firebase.google.com/docs/cpp/setup and unzip it to a
- directory of your choice.
+ [https://firebase.google.com/docs/cpp/setup](https://firebase.google.com/docs/cpp/setup)
+ and unzip it to a directory of your choice.
- Add the following frameworks from the Firebase C++ SDK to the project:
- frameworks/ios/universal/firebase.framework
- frameworks/ios/universal/firebase_auth.framework
@@ -75,7 +75,16 @@ Building and Running the testapp
Select the "Build Settings" tab, and click "All" to see all
the build settings. Scroll down to "Search Paths", and add
your path to "Framework Search Paths".
+ - Configure the XCode project for push messaging.
+ - Select the `Capabilities` tab in the XCode project.
+ - Switch `Push Notifications` to `On`.
- In XCode, build & run the sample on an iOS device or simulator.
+ - Phone authentication needs to launch a webview and return the results to the
+ application. To do this it requires you configure a URL type to handle the
+ callback. In your project's Info tab, under the URL Types section, find
+ the URL Schemes box containing YOUR\_REVERSED\_CLIENT\_ID. Replace this
+ with the value of the REVERSED\_CLIENT\_ID string in
+ GoogleService-Info.plist.
- The testapp has no user interface. The output of the app can be viewed
via the console. In Xcode, select
"View --> Debug Area --> Activate Console" from the menu.
@@ -110,18 +119,19 @@ Building and Running the testapp
- For further details please refer to the
[general instructions for setting up an Android app with Firebase](https://firebase.google.com/docs/android/setup).
- Download the Firebase C++ SDK linked from
- https://firebase.google.com/docs/cpp/setup and unzip it to a
- directory of your choice.
+ [https://firebase.google.com/docs/cpp/setup](https://firebase.google.com/docs/cpp/setup)
+ and unzip it to a directory of your choice.
- Configure the location of the Firebase C++ SDK by setting the
firebase\_cpp\_sdk.dir Gradle property to the SDK install directory.
For example, in the project directory:
```
- > echo "systemProp.firebase\_cpp\_sdk.dir=/User/$USER/firebase\_cpp\_sdk" >> gradle.properties
+ echo "systemProp.firebase\_cpp\_sdk.dir=/User/$USER/firebase\_cpp\_sdk" >> gradle.properties
```
- Ensure the Android SDK and NDK locations are set in Android Studio.
- - From the Android Studio launch menu, go to
- Configure/Project Defaults/Project Structure and download the SDK and NDK if
- the locations are not yet set.
+ - From the Android Studio launch menu, go to `File/Project Structure...` or
+ `Configure/Project Defaults/Project Structure...`
+ (Shortcut: Control + Alt + Shift + S on windows, Command + ";" on a mac)
+ and download the SDK and NDK if the locations are not yet set.
- Open *build.gradle* in Android Studio.
- From the Android Studio launch menu, "Open an existing Android Studio
project", and select `build.gradle`.
@@ -131,38 +141,51 @@ Building and Running the testapp
in the logcat output of Android studio or by running
"adb logcat *:W android_main firebase" from the command line.
-Known issues
-------------
-
- - Auth::SignInAnonymously() and Auth::CreateUserWithEmailAndPassword() are
- temporarily broken. For the moment, they will always return
- kAuthError_GeneralBackendError.
- - User::UpdateUserProfile() currently fails to set the photo URL on both
- Android and iOS, and also fails to set the display name on Android.
- - The iOS testapp generates benign asserts of the form:
- ```
- assertion failed: 15D21 13C75: assertiond + 12188
- [8CF1968D-3466-38B7-B225-3F6F5B64C552]: 0x1
- ```
- - Several API function are pending competion and will return status
- `firebase::kAuthError_Unimplemented` or empty data.
- - `Auth::ConfirmPasswordReset()`
- - `Auth::CheckActionCode()`
- - `Auth::ApplyActionCode()`
- - `User::Reauthenticate()`
- - `User::SendEmailVerification()`
- - `User::Delete()`
- - `User::ProviderData()`
- - Several API functions return different error codes on iOS and Android.
- The disparities will be eliminated in a subsequent release.
- - When given invalid parameters, several API functions return
- kAuthError_GeneralBackendError instead of kAuthError_InvalidEmail or
- kAuthError_EmailNotFound.
+### Desktop
+ - Register your app with Firebase.
+ - Create a new app on the [Firebase console](https://firebase.google.com/console/),
+ following the above instructions for Android or iOS.
+ - If you have an Android project, add the `google-services.json` file that
+ you downloaded from the Firebase console to the root directory of the
+ testapp.
+ - If you have an iOS project, and don't wish to use an Android project,
+ you can use the Python script `generate_xml_from_google_services_json.py --plist`,
+ located in the Firebase C++ SDK, to convert your `GoogleService-Info.plist`
+ file into a `google-services-desktop.json` file, which can then be
+ placed in the root directory of the testapp.
+ - Download the Firebase C++ SDK linked from
+ [https://firebase.google.com/docs/cpp/setup](https://firebase.google.com/docs/cpp/setup)
+ and unzip it to a directory of your choice.
+ - Configure the testapp with the location of the Firebase C++ SDK.
+ This can be done a couple different ways (in highest to lowest priority):
+ - When invoking cmake, pass in the location with
+ -DFIREBASE_CPP_SDK_DIR=/path/to/firebase_cpp_sdk.
+ - Set an environment variable for FIREBASE_CPP_SDK_DIR to the path to use.
+ - Edit the CMakeLists.txt file, changing the FIREBASE_CPP_SDK_DIR path
+ to the appropriate location.
+ - From the testapp directory, generate the build files by running,
+ ```
+ cmake .
+ ```
+ If you want to use XCode, you can use -G"Xcode" to generate the project.
+ Similarly, to use Visual Studio, -G"Visual Studio 15 2017". For more
+ information, see
+ [CMake generators](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html).
+ - Build the testapp, by either opening the generated project file based on the platform, or running,
+ ```
+ cmake --build .
+ ```
+ - Execute the testapp by running,
+ ```
+ ./desktop_testapp
+ ```
+ Note that the executable might be under another directory, such as Debug.
+ - The testapp has no user interface, but the output can be viewed via the console.
Support
-------
-https://firebase.google.com/support/
+[https://firebase.google.com/support/](https://firebase.google.com/support/)
License
-------
@@ -183,4 +206,3 @@ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations under
the License.
-
diff --git a/auth/testapp/settings.gradle b/auth/testapp/settings.gradle
new file mode 100644
index 00000000..2a543b93
--- /dev/null
+++ b/auth/testapp/settings.gradle
@@ -0,0 +1,36 @@
+// Copyright 2018 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+def firebase_cpp_sdk_dir = System.getProperty('firebase_cpp_sdk.dir')
+if (firebase_cpp_sdk_dir == null || firebase_cpp_sdk_dir.isEmpty()) {
+ firebase_cpp_sdk_dir = System.getenv('FIREBASE_CPP_SDK_DIR')
+ if (firebase_cpp_sdk_dir == null || firebase_cpp_sdk_dir.isEmpty()) {
+ if ((new File('firebase_cpp_sdk')).exists()) {
+ firebase_cpp_sdk_dir = 'firebase_cpp_sdk'
+ } else {
+ throw new StopActionException(
+ 'firebase_cpp_sdk.dir property or the FIREBASE_CPP_SDK_DIR ' +
+ 'environment variable must be set to reference the Firebase C++ ' +
+ 'SDK install directory. This is used to configure static library ' +
+ 'and C/C++ include paths for the SDK.')
+ }
+ }
+}
+if (!(new File(firebase_cpp_sdk_dir)).exists()) {
+ throw new StopActionException(
+ sprintf('Firebase C++ SDK directory %s does not exist',
+ firebase_cpp_sdk_dir))
+}
+gradle.ext.firebase_cpp_sdk_dir = "$firebase_cpp_sdk_dir"
+includeBuild "$firebase_cpp_sdk_dir"
\ No newline at end of file
diff --git a/auth/testapp/src/android/android_main.cc b/auth/testapp/src/android/android_main.cc
index 73cb30e7..02ef221d 100644
--- a/auth/testapp/src/android/android_main.cc
+++ b/auth/testapp/src/android/android_main.cc
@@ -148,6 +148,85 @@ class LoggingUtilsData {
LoggingUtilsData* g_logging_utils_data;
+// Vars that we need available for reading text from the user.
+class TextEntryFieldData {
+ public:
+ TextEntryFieldData()
+ : text_entry_field_class_(nullptr), text_entry_field_read_text_(0) {}
+
+ ~TextEntryFieldData() {
+ JNIEnv* env = GetJniEnv();
+ assert(env);
+ if (text_entry_field_class_) {
+ env->DeleteGlobalRef(text_entry_field_class_);
+ }
+ }
+
+ void Init() {
+ JNIEnv* env = GetJniEnv();
+ assert(env);
+
+ jclass text_entry_field_class = FindClass(
+ env, GetActivity(), "com/google/firebase/example/TextEntryField");
+ assert(text_entry_field_class != 0);
+
+ // Need to store as global references so it don't get moved during garbage
+ // collection.
+ text_entry_field_class_ =
+ static_cast(env->NewGlobalRef(text_entry_field_class));
+ env->DeleteLocalRef(text_entry_field_class);
+
+ static const JNINativeMethod kNativeMethods[] = {
+ {"nativeSleep", "(I)Z", reinterpret_cast(ProcessEvents)}};
+ env->RegisterNatives(text_entry_field_class_, kNativeMethods,
+ sizeof(kNativeMethods) / sizeof(kNativeMethods[0]));
+ text_entry_field_read_text_ = env->GetStaticMethodID(
+ text_entry_field_class_, "readText",
+ "(Landroid/app/Activity;Ljava/lang/String;Ljava/lang/String;"
+ "Ljava/lang/String;)Ljava/lang/String;");
+ }
+
+ // Call TextEntryField.readText(), which shows a text entry dialog and spins
+ // until the user enters some text (or cancels). If the user cancels, returns
+ // an empty string.
+ std::string ReadText(const char* title, const char* message,
+ const char* placeholder) {
+ if (text_entry_field_class_ == 0) return ""; // haven't been initted yet
+ JNIEnv* env = GetJniEnv();
+ assert(env);
+ jstring title_string = env->NewStringUTF(title);
+ jstring message_string = env->NewStringUTF(message);
+ jstring placeholder_string = env->NewStringUTF(placeholder);
+ jobject result_string = env->CallStaticObjectMethod(
+ text_entry_field_class_, text_entry_field_read_text_, GetActivity(),
+ title_string, message_string, placeholder_string);
+ env->DeleteLocalRef(title_string);
+ env->DeleteLocalRef(message_string);
+ env->DeleteLocalRef(placeholder_string);
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ }
+ if (result_string == nullptr) {
+ // Check if readText() returned null, which will be the case if an
+ // exception occurred or if TextEntryField returned null for some reason.
+ return "";
+ }
+ const char* result_buffer =
+ env->GetStringUTFChars(static_cast(result_string), 0);
+ std::string result(result_buffer);
+ env->ReleaseStringUTFChars(static_cast(result_string),
+ result_buffer);
+ return result;
+ }
+
+ private:
+ jclass text_entry_field_class_;
+ jmethodID text_entry_field_read_text_;
+};
+
+TextEntryFieldData* g_text_entry_field_data;
+
// Checks if a JNI exception has happened, and if so, logs it to the console.
void CheckJNIException() {
JNIEnv* env = GetJniEnv();
@@ -208,6 +287,15 @@ JNIEnv* GetJniEnv() {
return result == JNI_OK ? env : nullptr;
}
+// Use a Java class, TextEntryField, to prompt the user to enter some text.
+// This function blocks until text was entered or the dialog was canceled.
+// If the user cancels, returns an empty string.
+std::string ReadTextInput(const char* title, const char* message,
+ const char* placeholder) {
+ assert(g_text_entry_field_data);
+ return g_text_entry_field_data->ReadText(title, message, placeholder);
+}
+
// Execute common_main(), flush pending events and finish the activity.
extern "C" void android_main(struct android_app* state) {
// native_app_glue spawns a new thread, calling android_main() when the
@@ -235,6 +323,10 @@ extern "C" void android_main(struct android_app* state) {
g_logging_utils_data = new LoggingUtilsData();
g_logging_utils_data->Init();
+ // Create the text entry dialog.
+ g_text_entry_field_data = new TextEntryFieldData();
+ g_text_entry_field_data->Init();
+
// Execute cross platform entry point.
static const char* argv[] = {FIREBASE_TESTAPP_NAME};
int return_value = common_main(1, argv);
diff --git a/auth/testapp/src/android/java/com/google/firebase/example/TextEntryField.java b/auth/testapp/src/android/java/com/google/firebase/example/TextEntryField.java
new file mode 100644
index 00000000..34c59fa5
--- /dev/null
+++ b/auth/testapp/src/android/java/com/google/firebase/example/TextEntryField.java
@@ -0,0 +1,106 @@
+// Copyright 2016 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.firebase.example;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.widget.EditText;
+
+/**
+ * A utility class, with a method to prompt the user to enter a line of text, and a native method to
+ * sleep for a given number of milliseconds.
+ */
+public class TextEntryField {
+ private static Object lock = new Object();
+ private static String resultText = null;
+
+ /**
+ * Prompt the user with a text field, blocking until the user fills it out, then returns the text
+ * they entered. If the user cancels, returns an empty string.
+ */
+ public static String readText(
+ final Activity activity, final String title, final String message, final String placeholder) {
+ resultText = null;
+ // Show the alert dialog on the main thread.
+ activity.runOnUiThread(
+ new Runnable() {
+ @Override
+ public void run() {
+ AlertDialog.Builder alertBuilder = new AlertDialog.Builder(activity);
+ alertBuilder.setTitle(title);
+ alertBuilder.setMessage(message);
+
+ // Set up and add the text field.
+ final EditText textField = new EditText(activity);
+ textField.setHint(placeholder);
+ alertBuilder.setView(textField);
+
+ alertBuilder.setPositiveButton(
+ "OK",
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int whichButton) {
+ synchronized (lock) {
+ resultText = textField.getText().toString();
+ }
+ }
+ });
+
+ alertBuilder.setNegativeButton(
+ "Cancel",
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int whichButton) {
+ synchronized (lock) {
+ resultText = "";
+ }
+ }
+ });
+
+ alertBuilder.setOnCancelListener(
+ new DialogInterface.OnCancelListener() {
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ synchronized (lock) {
+ resultText = "";
+ }
+ }
+ });
+ alertBuilder.show();
+ }
+ });
+
+ // In our original thread, wait for the dialog to finish, then return its result.
+ while (true) {
+ // Pause a second, waiting for the user to enter text.
+ if (nativeSleep(1000)) {
+ // If this returns true, an exit was requested.
+ return "";
+ }
+ synchronized (lock) {
+ if (resultText != null) {
+ // resultText will be set to non-null when a dialog button is clicked, or the dialog
+ // is canceled.
+ String result = resultText;
+ resultText = null; // Consume the result.
+ return result;
+ }
+ }
+ }
+ }
+
+ private static native boolean nativeSleep(int milliseconds);
+}
diff --git a/auth/testapp/src/common_main.cc b/auth/testapp/src/common_main.cc
index b47f7bb1..f0d12c7b 100644
--- a/auth/testapp/src/common_main.cc
+++ b/auth/testapp/src/common_main.cc
@@ -18,6 +18,7 @@
#include "firebase/app.h"
#include "firebase/auth.h"
+#include "firebase/auth/credential.h"
#include "firebase/util.h"
// Thin OS abstraction layer.
@@ -27,18 +28,33 @@ using ::firebase::App;
using ::firebase::AppOptions;
using ::firebase::Future;
using ::firebase::FutureBase;
+using ::firebase::Variant;
+using ::firebase::auth::AdditionalUserInfo;
using ::firebase::auth::Auth;
-using ::firebase::auth::Credential;
using ::firebase::auth::AuthError;
-using ::firebase::auth::kAuthErrorNone;
-using ::firebase::auth::kAuthErrorFailure;
+using ::firebase::auth::AuthResult;
+using ::firebase::auth::Credential;
using ::firebase::auth::EmailAuthProvider;
using ::firebase::auth::FacebookAuthProvider;
using ::firebase::auth::GitHubAuthProvider;
using ::firebase::auth::GoogleAuthProvider;
+using ::firebase::auth::kAuthErrorFailure;
+using ::firebase::auth::kAuthErrorInvalidCredential;
+using ::firebase::auth::kAuthErrorInvalidProviderId;
+using ::firebase::auth::kAuthErrorNone;
+using ::firebase::auth::OAuthProvider;
+using ::firebase::auth::PhoneAuthOptions;
+using ::firebase::auth::PhoneAuthProvider;
+using ::firebase::auth::PhoneAuthCredential;
+using ::firebase::auth::PlayGamesAuthProvider;
using ::firebase::auth::TwitterAuthProvider;
using ::firebase::auth::User;
using ::firebase::auth::UserInfoInterface;
+using ::firebase::auth::UserMetadata;
+
+#if TARGET_OS_IPHONE
+using ::firebase::auth::GameCenterAuthProvider;
+#endif
// Set this to true, and set the email/password, to test a custom email address.
static const bool kTestCustomEmail = false;
@@ -46,12 +62,20 @@ static const char kCustomEmail[] = "custom.email@example.com";
static const char kCustomPassword[] = "CustomPasswordGoesHere";
// Constants used during tests.
+static const char kTestNonceBad[] = "testBadNonce";
static const char kTestPassword[] = "testEmailPassword123";
static const char kTestEmailBad[] = "bad.test.email@example.com";
static const char kTestPasswordBad[] = "badTestPassword";
static const char kTestIdTokenBad[] = "bad id token for testing";
static const char kTestAccessTokenBad[] = "bad access token for testing";
static const char kTestPasswordUpdated[] = "testpasswordupdated";
+static const char kTestIdProviderIdBad[] = "bad provider id for testing";
+static const char kTestServerAuthCodeBad[] = "bad server auth code";
+
+static const int kWaitIntervalMs = 300;
+static const int kPhoneAuthCodeSendWaitMs = 600000;
+static const int kPhoneAuthCompletionWaitMs = 8000;
+static const int kPhoneAuthTimeoutMs = 0;
static const char kFirebaseProviderId[] =
#if defined(__ANDROID__)
@@ -63,7 +87,7 @@ static const char kFirebaseProviderId[] =
// Don't return until `future` is complete.
// Print a message for whether the result mathes our expectations.
// Returns true if the application should exit.
-static bool WaitForFuture(FutureBase future, const char* fn,
+static bool WaitForFuture(const FutureBase& future, const char* fn,
AuthError expected_error, bool log_error = true) {
// Note if the future has not be started properly.
if (future.status() == ::firebase::kFutureStatusInvalid) {
@@ -96,27 +120,51 @@ static bool WaitForFuture(FutureBase future, const char* fn,
return false;
}
-static bool WaitForSignInFuture(Future sign_in_future, const char* fn,
+static bool WaitForSignInFuture(Future sign_in_future, const char* fn,
AuthError expected_error, Auth* auth) {
if (WaitForFuture(sign_in_future, fn, expected_error)) return true;
- const User* const* sign_in_user_ptr = sign_in_future.result();
- const User* sign_in_user =
- sign_in_user_ptr == nullptr ? nullptr : *sign_in_user_ptr;
- const User* auth_user = auth->current_user();
+ const User sign_in_user = sign_in_future.result() ? *sign_in_future.result() :
+ User();
+ const User auth_user = auth->current_user();
+
+ if (expected_error == ::firebase::auth::kAuthErrorNone &&
+ sign_in_user != auth_user) {
+ LogMessage("ERROR: future's user (%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,
+ const char* fn, AuthError expected_error,
+ Auth* auth) {
+ if (WaitForFuture(sign_in_future, fn, expected_error)) return true;
+
+ 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 (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)));
+ if (expected_error == ::firebase::auth::kAuthErrorNone &&
+ sign_in_user != 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());
}
- const bool should_be_null = expected_error != kAuthErrorNone;
- const bool is_null = sign_in_user == nullptr;
- if (should_be_null != is_null) {
- LogMessage("ERROR: user pointer (%x) is incorrect",
- static_cast(reinterpret_cast(auth_user)));
+ return false;
+}
+
+// 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().is_valid()) {
+ if (ProcessEvents(100)) return true;
}
+ // Wait - hopefully - long enough for listeners to be signalled.
+ ProcessEvents(1000);
return false;
}
@@ -154,11 +202,82 @@ static void ExpectStringsEqual(const char* test, const char* expected,
}
}
+static void LogVariantMap(const std::map& variant_map,
+ int indent);
+
+// Log a vector of variants.
+static void LogVariantVector(const std::vector& variants, int indent) {
+ std::string indent_string(indent * 2, ' ');
+ LogMessage("%s[", indent_string.c_str());
+ for (auto it = variants.begin(); it != variants.end(); ++it) {
+ const Variant& item = *it;
+ if (item.is_fundamental_type()) {
+ const Variant& string_value = item.AsString();
+ LogMessage("%s %s,", indent_string.c_str(), string_value.string_value());
+ } else if (item.is_vector()) {
+ LogVariantVector(item.vector(), indent + 2);
+ } else if (item.is_map()) {
+ LogVariantMap(item.map(), indent + 2);
+ } else {
+ LogMessage("%s ERROR: unknown type %d", indent_string.c_str(),
+ static_cast(item.type()));
+ }
+ }
+ LogMessage("%s]", indent_string.c_str());
+}
+
+// Log a map of variants.
+static void LogVariantMap(const std::map& variant_map,
+ int indent) {
+ std::string indent_string(indent * 2, ' ');
+ for (auto it = variant_map.begin(); it != variant_map.end(); ++it) {
+ const Variant& key_string = it->first.AsString();
+ const Variant& value = it->second;
+ if (value.is_fundamental_type()) {
+ const Variant& string_value = value.AsString();
+ LogMessage("%s%s: %s,", indent_string.c_str(), key_string.string_value(),
+ string_value.string_value());
+ } else {
+ LogMessage("%s%s:", indent_string.c_str(), key_string.string_value());
+ if (value.is_vector()) {
+ LogVariantVector(value.vector(), indent + 1);
+ } else if (value.is_map()) {
+ LogVariantMap(value.map(), indent + 1);
+ } else {
+ LogMessage("%s ERROR: unknown type %d", indent_string.c_str(),
+ static_cast(value.type()));
+ }
+ }
+ }
+}
+
+// Display the sign-in result.
+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.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);
+ UserMetadata metadata = result.user.metadata();
+ LogMessage("* Sign in timestamp %d",
+ static_cast(metadata.last_sign_in_timestamp));
+ LogMessage("* Creation timestamp %d",
+ static_cast(metadata.creation_timestamp));
+}
+
class AuthStateChangeCounter : public firebase::auth::AuthStateListener {
public:
AuthStateChangeCounter() : num_state_changes_(0) {}
- virtual void OnAuthStateChanged(Auth* auth) { num_state_changes_++; }
+ virtual void OnAuthStateChanged(Auth* auth) { // NOLINT
+ 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) {
CompleteTest(test_name, expected_state_changes, expected_state_changes);
@@ -178,6 +297,34 @@ class AuthStateChangeCounter : public firebase::auth::AuthStateListener {
int num_state_changes_;
};
+class IdTokenChangeCounter : public firebase::auth::IdTokenListener {
+ public:
+ IdTokenChangeCounter() : num_token_changes_(0) {}
+
+ virtual void OnIdTokenChanged(Auth* auth) { // NOLINT
+ 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) {
+ CompleteTest(test_name, token_changes, token_changes);
+ }
+
+ void CompleteTest(const char* test_name, int min_token_changes,
+ int max_token_changes) {
+ const bool success = min_token_changes <= num_token_changes_ &&
+ num_token_changes_ <= max_token_changes;
+ LogMessage("%sIdTokenListener called %d time%s on %s.",
+ success ? "" : "ERROR: ", num_token_changes_,
+ num_token_changes_ == 1 ? "" : "s", test_name);
+ num_token_changes_ = 0;
+ }
+
+ private:
+ int num_token_changes_;
+};
+
// Utility class for holding a user's login credentials.
class UserLogin {
public:
@@ -185,7 +332,6 @@ class UserLogin {
: auth_(auth),
email_(email),
password_(password),
- user_(nullptr),
log_errors_(true) {}
explicit UserLogin(Auth* auth) : auth_(auth) {
@@ -194,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; }
@@ -243,10 +389,70 @@ class UserLogin {
Auth* auth_;
std::string email_;
std::string password_;
- User* user_;
+ User user_;
bool log_errors_;
};
+class PhoneListener : public PhoneAuthProvider::Listener {
+ public:
+ PhoneListener()
+ : num_calls_on_verification_complete_(0),
+ num_calls_on_verification_failed_(0),
+ num_calls_on_code_sent_(0),
+ num_calls_on_code_auto_retrieval_time_out_(0) {}
+
+ void OnVerificationCompleted(PhoneAuthCredential /*credential*/) override {
+ LogMessage("PhoneListener: successful automatic verification.");
+ num_calls_on_verification_complete_++;
+ }
+
+ void OnVerificationFailed(const std::string& error) override {
+ LogMessage("ERROR: PhoneListener verification failed with error, %s",
+ error.c_str());
+ num_calls_on_verification_failed_++;
+ }
+
+ void OnCodeSent(const std::string& verification_id,
+ const PhoneAuthProvider::ForceResendingToken&
+ force_resending_token) override {
+ LogMessage("PhoneListener: code sent. verification_id=%s",
+ verification_id.c_str());
+ verification_id_ = verification_id;
+ force_resending_token_ = force_resending_token;
+ num_calls_on_code_sent_++;
+ }
+
+ void OnCodeAutoRetrievalTimeOut(const std::string& verification_id) override {
+ LogMessage("PhoneListener: auto retrieval timeout. verification_id=%s",
+ verification_id.c_str());
+ verification_id_ = verification_id;
+ num_calls_on_code_auto_retrieval_time_out_++;
+ }
+
+ const std::string& verification_id() const { return verification_id_; }
+ const PhoneAuthProvider::ForceResendingToken& force_resending_token() const {
+ return force_resending_token_;
+ }
+ int num_calls_on_verification_complete() const {
+ return num_calls_on_verification_complete_;
+ }
+ int num_calls_on_verification_failed() const {
+ return num_calls_on_verification_failed_;
+ }
+ int num_calls_on_code_sent() const { return num_calls_on_code_sent_; }
+ int num_calls_on_code_auto_retrieval_time_out() const {
+ return num_calls_on_code_auto_retrieval_time_out_;
+ }
+
+ private:
+ std::string verification_id_;
+ PhoneAuthProvider::ForceResendingToken force_resending_token_;
+ int num_calls_on_verification_complete_;
+ int num_calls_on_verification_failed_;
+ int num_calls_on_code_sent_;
+ int num_calls_on_code_auto_retrieval_time_out_;
+};
+
// Execute all methods of the C++ Auth API.
extern "C" int common_main(int argc, const char* argv[]) {
App* app;
@@ -287,11 +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 %s already signed in, so signing them out.",
- auth->current_user()->display_name().c_str());
+ 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->SignOut();
}
@@ -315,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 "
@@ -324,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 NULL after "
+ "ERROR: current_user() returning %s instead of nullptr after "
"SignOut()",
- auth->current_user());
+ auth->current_user().uid().c_str());
}
}
}
@@ -337,50 +545,146 @@ extern "C" int common_main(int argc, const char* argv[]) {
// --- StateChange tests -----------------------------------------------------
{
AuthStateChangeCounter counter;
+ IdTokenChangeCounter token_counter;
// Test notification on registration.
auth->AddAuthStateListener(&counter);
- counter.CompleteTest("registration", 0);
+ auth->AddIdTokenListener(&token_counter);
+ // Expect notification immediately after registration.
+ counter.CompleteTest("registration", 1);
+ token_counter.CompleteTest("registration", 1);
// Test notification on SignOut(), when already signed-out.
auth->SignOut();
counter.CompleteTest("SignOut() when already signed-out", 0);
+ 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
+ // changed.
counter.CompleteTest("SignInAnonymously()", 1, 4);
+ token_counter.CompleteTest("SignInAnonymously()", 1, 5);
+
+ // Refresh the token.
+ 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.
- // TODO(jsanmiya): Change the minimum expected callbacks to 1 once
- // b/32179003 is fixed.
+ LogMessage("Current user %s", auth->current_user().uid().c_str()); // DEBUG
auth->SignOut();
- counter.CompleteTest("SignOut()", 0, 4);
+ // Wait for the sign out to complete.
+ WaitForSignOut(auth);
+ counter.CompleteTest("SignOut()", 1);
+ token_counter.CompleteTest("SignOut()", 1);
+ LogMessage("Current user %s", auth->current_user().uid().c_str()); // DEBUG
auth->RemoveAuthStateListener(&counter);
+ auth->RemoveIdTokenListener(&token_counter);
+ }
+
+ // Phone verification isn't currently implemented on desktop
+#if defined(__ANDROID__) || TARGET_OS_IPHONE
+ // --- PhoneListener tests ---------------------------------------------------
+ {
+ UserLogin user_login(auth); // Generate a random name/password
+ user_login.Register();
+
+ LogMessage("Verifying phone number");
+
+ const std::string phone_number = ReadTextInput(
+ "Phone Number", "Please enter your phone number", "+12345678900");
+ PhoneListener listener;
+ PhoneAuthProvider& phone_provider = PhoneAuthProvider::GetInstance(auth);
+ PhoneAuthOptions options;
+ options.phone_number = phone_number;
+ options.timeout_milliseconds = kPhoneAuthTimeoutMs;
+ phone_provider.VerifyPhoneNumber(options, &listener);
+
+ // Wait for OnCodeSent() callback.
+ int wait_ms = 0;
+ while (listener.num_calls_on_verification_complete() == 0 &&
+ listener.num_calls_on_verification_failed() == 0 &&
+ listener.num_calls_on_code_sent() == 0) {
+ if (wait_ms > kPhoneAuthCodeSendWaitMs) break;
+ ProcessEvents(kWaitIntervalMs);
+ wait_ms += kWaitIntervalMs;
+ LogMessage(".");
+ }
+ if (wait_ms > kPhoneAuthCodeSendWaitMs ||
+ listener.num_calls_on_verification_failed()) {
+ LogMessage("ERROR: SMS with verification code not sent.");
+ } else {
+ LogMessage("SMS verification code sent.");
+
+ const std::string verification_code = ReadTextInput(
+ "Verification Code",
+ "Please enter the verification code sent to you via SMS", "123456");
+
+ // Wait for one of the other callbacks.
+ while (listener.num_calls_on_verification_complete() == 0 &&
+ listener.num_calls_on_verification_failed() == 0 &&
+ listener.num_calls_on_code_auto_retrieval_time_out() == 0) {
+ if (wait_ms > kPhoneAuthCompletionWaitMs) break;
+ ProcessEvents(kWaitIntervalMs);
+ wait_ms += kWaitIntervalMs;
+ LogMessage(".");
+ }
+ if (listener.num_calls_on_code_auto_retrieval_time_out() > 0) {
+ const PhoneAuthCredential phone_credential = phone_provider.GetCredential(
+ listener.verification_id().c_str(), verification_code.c_str());
+
+ 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);
+ WaitForSignInFuture(
+ update_future,
+ "user.UpdatePhoneNumberCredential(phone_credential)",
+ kAuthErrorNone, auth);
+ }
+
+ } else {
+ LogMessage("ERROR: SMS auto-detect time out did not occur.");
+ }
+ }
}
+#endif // defined(__ANDROID__) || TARGET_OS_IPHONE
// --- Auth tests ------------------------------------------------------------
{
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",
+ sign_in_future == auth->SignInAnonymouslyLastResult());
+
// 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 NULL after "
+ "ERROR: current_user() returning valid user %s instead of invalid user after "
"SignOut()",
- auth->current_user());
+ auth->current_user().uid().c_str());
}
}
}
@@ -391,6 +695,10 @@ extern "C" int common_main(int argc, const char* argv[]) {
auth->FetchProvidersForEmail(user_login.email());
WaitForFuture(providers_future, "Auth::FetchProvidersForEmail()",
kAuthErrorNone);
+ ExpectTrue(
+ "FetchProvidersForEmailLastResult matches returned Future",
+ providers_future == auth->FetchProvidersForEmailLastResult());
+
const Auth::FetchProvidersResult* pro = providers_future.result();
if (pro) {
LogMessage(" email %s, num providers %d", user_login.email(),
@@ -405,51 +713,123 @@ 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,
"Auth::SignInWithEmailAndPassword() existing email and password",
kAuthErrorNone, auth);
+ ExpectTrue(
+ "SignInWithEmailAndPasswordLastResult matches returned Future",
+ sign_in_future == auth->SignInWithEmailAndPasswordLastResult());
+
// 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 NULL 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(
+ 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;
+ 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);
+ WaitForFuture(update_profile_future, "User::UpdateUserProfile",
+ kAuthErrorNone);
+ if (update_profile_future.error() == kAuthErrorNone) {
+ ExpectStringsEqual("User::display_name", kDisplayName,
+ user.display_name().c_str());
+ ExpectStringsEqual("User::photo_url", kPhotoUrl,
+ user.photo_url().c_str());
+ }
+ }
+ }
+
+ // 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();
+ WaitForSignInFuture(sign_in_anonymously_future,
+ "Auth::SignInAnonymously", kAuthErrorNone, auth);
+ if (sign_in_anonymously_future.error() == kAuthErrorNone) {
+ 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.LinkWithCredential(credential);
+ WaitForSignInFuture(link_future,
+ "User::LinkAndRetrieveDataWithCredential",
+ kAuthErrorNone, auth);
+ if (link_future.error() == kAuthErrorNone) {
+ LogAuthResult(*link_future.result());
+ Future reauth_future =
+ user.ReauthenticateAndRetrieveData(credential);
+ WaitForSignInFuture(reauth_future,
+ "User::ReauthenticateAndRetrieveData",
+ kAuthErrorNone, auth);
+ if (reauth_future.error() == kAuthErrorNone) {
+ LogAuthResult(*reauth_future.result());
+ }
+ // Unlink email / password from credential.
+ Future unlink_future =
+ user.Unlink(credential.provider().c_str());
+ WaitForSignInFuture(unlink_future, "User::Unlink", kAuthErrorNone,
+ auth);
}
+ auth->SignOut();
}
}
// 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",
- kAuthErrorFailure, auth);
+ ::firebase::auth::kAuthErrorUserNotFound, auth);
}
// 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,
"Auth::SignInWithEmailAndPassword() bad password",
- kAuthErrorFailure, auth);
+ ::firebase::auth::kAuthErrorWrongPassword, auth);
}
// 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,
"Auth::CreateUserWithEmailAndPassword() existing email",
- kAuthErrorFailure, auth);
+ ::firebase::auth::kAuthErrorEmailAlreadyInUse, auth);
+ ExpectTrue(
+ "CreateUserWithEmailAndPasswordLastResult matches returned Future",
+ create_future_bad ==
+ auth->CreateUserWithEmailAndPasswordLastResult());
}
// Test Auth::SignInWithCredential() using email&password.
@@ -457,54 +837,181 @@ 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);
}
+ // Test Auth::SignInAndRetrieveDataWithCredential using email & password.
+ // Use existing email. Should succeed.
+ {
+ Credential email_cred = EmailAuthProvider::GetCredential(
+ user_login.email(), user_login.password());
+ Future sign_in_future =
+ auth->SignInAndRetrieveDataWithCredential(email_cred);
+ WaitForSignInFuture(sign_in_future,
+ "Auth::SignInAndRetrieveDataWithCredential "
+ "existing email",
+ kAuthErrorNone, auth);
+ ExpectTrue(
+ "SignInAndRetrieveDataWithCredentialLastResult matches "
+ "returned Future",
+ sign_in_future ==
+ auth->SignInAndRetrieveDataWithCredentialLastResult());
+ if (sign_in_future.error() == kAuthErrorNone) {
+ const AuthResult* sign_in_result = sign_in_future.result();
+ if (sign_in_result != nullptr && sign_in_result->user.is_valid()) {
+ LogMessage("SignInAndRetrieveDataWithCredential");
+ LogAuthResult(*sign_in_result);
+ } else {
+ LogMessage(
+ "ERROR: SignInAndRetrieveDataWithCredential returned no "
+ "result");
+ }
+ }
+ }
+
// Use bad Facebook credentials. Should fail.
{
Credential facebook_cred_bad =
FacebookAuthProvider::GetCredential(kTestAccessTokenBad);
- Future facebook_bad =
+ Futurefacebook_bad =
auth->SignInWithCredential(facebook_cred_bad);
WaitForSignInFuture(
facebook_bad,
"Auth::SignInWithCredential() bad Facebook credentials",
- kAuthErrorFailure, auth);
+ kAuthErrorInvalidCredential, auth);
}
// Use bad GitHub credentials. Should fail.
{
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",
- kAuthErrorFailure, auth);
+ kAuthErrorInvalidCredential, auth);
}
// Use bad Google credentials. Should fail.
{
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",
- kAuthErrorFailure, auth);
+ kAuthErrorInvalidCredential, auth);
+ }
+
+ // Use bad Google credentials, missing an optional parameter. Should fail.
+ {
+ Credential google_cred_bad =
+ GoogleAuthProvider::GetCredential(kTestIdTokenBad, nullptr);
+ Futuregoogle_bad = auth->SignInWithCredential(google_cred_bad);
+ WaitForSignInFuture(
+ google_bad, "Auth::SignInWithCredential() bad Google credentials",
+ kAuthErrorInvalidCredential, auth);
+ }
+
+#if defined(__ANDROID__)
+ // Use bad Play Games (Android-only) credentials. Should fail.
+ {
+ Credential play_games_cred_bad =
+ PlayGamesAuthProvider::GetCredential(kTestServerAuthCodeBad);
+ Futureplay_games_bad =
+ auth->SignInWithCredential(play_games_cred_bad);
+ WaitForSignInFuture(
+ play_games_bad,
+ "Auth:SignInWithCredential() bad Play Games credentials",
+ kAuthErrorInvalidCredential, auth);
}
+#endif // defined(__ANDROID__)
+#if TARGET_OS_IPHONE
+ // Test Game Center status/login
+ {
+ // Check if the current user is authenticated to GameCenter
+ bool is_authenticated = GameCenterAuthProvider::IsPlayerAuthenticated();
+ if (!is_authenticated) {
+ LogMessage("Not signed into Game Center, skipping test.");
+ } else {
+ LogMessage("Signed in, testing Game Center authentication.");
+
+ // Get the Game Center credential from the device
+ Future game_center_credential_future =
+ GameCenterAuthProvider::GetCredential();
+ WaitForFuture(game_center_credential_future,
+ "GameCenterAuthProvider::GetCredential()",
+ kAuthErrorNone);
+
+ const AuthError credential_error =
+ static_cast(game_center_credential_future.error());
+
+ // Only attempt to sign in if we were able to get a credential.
+ if (credential_error == kAuthErrorNone) {
+ const Credential* gc_credential_ptr =
+ game_center_credential_future.result();
+
+ if (gc_credential_ptr == nullptr) {
+ LogMessage("Failed to retrieve Game Center credential.");
+ } else {
+ Futuregame_center_user =
+ auth->SignInWithCredential(*gc_credential_ptr);
+ WaitForFuture(game_center_user,
+ "Auth::SignInWithCredential() test Game Center "
+ "credential signin",
+ kAuthErrorNone);
+ }
+ }
+ }
+ }
+#endif // TARGET_OS_IPHONE
// Use bad Twitter credentials. Should fail.
{
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",
+ kAuthErrorInvalidCredential, auth);
+ }
+
+ // Construct OAuthCredential with nonce & access token.
+ {
+ Credential nonce_credential_good =
+ OAuthProvider::GetCredential(kTestIdProviderIdBad, kTestIdTokenBad,
+ kTestNonceBad, kTestAccessTokenBad);
+ }
+
+ // Construct OAuthCredential with nonce, null access token.
+ {
+ Credential nonce_credential_good = OAuthProvider::GetCredential(
+ kTestIdProviderIdBad, kTestIdTokenBad, kTestNonceBad,
+ /*access_token=*/nullptr);
+ }
+
+ // Use bad OAuth credentials. Should fail.
+ {
+ Credential oauth_cred_bad = OAuthProvider::GetCredential(
+ kTestIdProviderIdBad, kTestIdTokenBad, kTestAccessTokenBad);
+ Futureoauth_bad = auth->SignInWithCredential(oauth_cred_bad);
+ WaitForSignInFuture(
+ oauth_bad, "Auth::SignInWithCredential() bad OAuth credentials",
+ kAuthErrorFailure, auth);
+ }
+
+ // Use bad OAuth credentials with nonce. Should fail.
+ {
+ Credential oauth_cred_bad =
+ OAuthProvider::GetCredential(kTestIdProviderIdBad, kTestIdTokenBad,
+ kTestNonceBad, kTestAccessTokenBad);
+ Futureoauth_bad = auth->SignInWithCredential(oauth_cred_bad);
+ WaitForSignInFuture(
+ oauth_bad, "Auth::SignInWithCredential() bad OAuth credentials",
kAuthErrorFailure, auth);
}
@@ -516,6 +1023,9 @@ extern "C" int common_main(int argc, const char* argv[]) {
WaitForFuture(send_password_reset_ok,
"Auth::SendPasswordResetEmail() existing email",
kAuthErrorNone);
+ ExpectTrue(
+ "SendPasswordResetEmailLastResult matches returned Future",
+ send_password_reset_ok == auth->SendPasswordResetEmailLastResult());
}
// Use bad email. Should fail.
@@ -524,79 +1034,133 @@ extern "C" int common_main(int argc, const char* argv[]) {
auth->SendPasswordResetEmail(kTestEmailBad);
WaitForFuture(send_password_reset_bad,
"Auth::SendPasswordResetEmail() bad email",
- kAuthErrorFailure);
+ ::firebase::auth::kAuthErrorUserNotFound);
}
}
}
// --- 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());
- ExpectTrue("Anonymous email is_anonymous()",
- anonymous_user->is_anonymous());
- ExpectFalse("Email email is_email_verified()",
- anonymous_user->is_email_verified());
-
- // Test User::LinkWithCredential().
+ anonymous_user.provider_id().c_str());
+ ExpectTrue("Anonymous user is_anonymous()",
+ anonymous_user.is_anonymous());
+ ExpectFalse("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);
+ ExpectTrue("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);
- WaitForSignInFuture(link_future, "User::LinkWithCredential()",
- kAuthErrorNone, auth);
+ {
+ 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);
+ WaitForSignInFuture(link_future, "User::LinkWithCredential() again",
+ ::firebase::auth::kAuthErrorProviderAlreadyLinked,
+ auth);
+ }
+
+ // 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();
+ ExpectTrue("Test precondition requires active user",
+ pre_link_user.is_valid());
+
+ Credential twitter_cred_bad = TwitterAuthProvider::GetCredential(
+ kTestIdTokenBad, kTestAccessTokenBad);
+ Future link_bad_future =
+ anonymous_user.LinkWithCredential(twitter_cred_bad);
+ WaitForFuture(link_bad_future,
+ "User::LinkWithCredential() with bad credential",
+ kAuthErrorInvalidCredential);
+ ExpectTrue("Linking maintains user",
+ auth->current_user() == pre_link_user);
+ }
+
+ // 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();
+ ExpectTrue("Test precondition requires active user",
+ pre_signin_user.is_valid());
+ Credential twitter_cred_bad = TwitterAuthProvider::GetCredential(
+ kTestIdTokenBad, kTestAccessTokenBad);
+ Futuresignin_bad_future =
+ auth->SignInWithCredential(twitter_cred_bad);
+ WaitForFuture(signin_bad_future,
+ "Auth::SignInWithCredential() with bad credential",
+ kAuthErrorInvalidCredential, auth);
+ ExpectTrue("Failed sign in maintains user",
+ auth->current_user() == pre_signin_user);
+ }
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());
- ExpectFalse("Email email is_anonymous()",
- email_user->is_anonymous());
- ExpectFalse("Email email is_email_verified()",
- email_user->is_email_verified());
+ email_user.provider_id().c_str());
+ ExpectFalse("Email user is_anonymous()",
+ email_user.is_anonymous());
+ ExpectFalse("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);
+ ExpectTrue("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",
@@ -606,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",
@@ -615,62 +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::refresh_token().
- const std::string refresh_token = email_user->refresh_token();
- LogMessage("User::refresh_token() = %s", refresh_token.c_str());
-
// Test User::Unlink().
- Future unlink_future = email_user->Unlink("firebase");
+ Future unlink_future = email_user.Unlink("firebase");
WaitForSignInFuture(unlink_future, "User::Unlink()",
- kAuthErrorFailure, auth);
+ ::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);
@@ -678,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);
}
@@ -694,29 +1255,123 @@ 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());
}
+#ifdef INTERNAL_EXPERIMENTAL
+#if defined TARGET_OS_IPHONE || defined(__ANDROID__)
+ // --- FederatedAuthProvider tests ------------------------------------------
+ {
+ { // --- LinkWithProvider ---
+ LogMessage("LinkWithProvider");
+ UserLogin user_login(auth); // Generate a random name/password
+ user_login.Register();
+ if (!user_login.user()) {
+ LogMessage("ERROR: Could not register new user.");
+ } else {
+ LogMessage("Setting up provider data");
+ firebase::auth::FederatedOAuthProviderData provider_data;
+ provider_data.provider_id =
+ firebase::auth::GoogleAuthProvider::kProviderId;
+ provider_data.provider_id = "google.com";
+ provider_data.scopes = {
+ "https://www.googleapis.com/auth/fitness.activity.read"};
+ provider_data.custom_parameters = {{"req_id", "1234"}};
+
+ LogMessage("Configuration oAuthProvider");
+ firebase::auth::FederatedOAuthProvider provider;
+ provider.SetProviderData(provider_data);
+ LogMessage("invoking linkwithprovider");
+ Future sign_in_future =
+ user_login.user().LinkWithProvider(&provider);
+ WaitForSignInFuture(sign_in_future, "LinkWithProvider", kAuthErrorNone,
+ auth);
+ if (sign_in_future.error() == kAuthErrorNone) {
+ const 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");
+ }
+ }
+ }
+
+ {
+ LogMessage("SignInWithProvider");
+ // --- SignInWithProvider ---
+ firebase::auth::FederatedOAuthProviderData provider_data;
+ provider_data.provider_id =
+ firebase::auth::GoogleAuthProvider::kProviderId;
+ provider_data.custom_parameters = {{"req_id", "1234"}};
+
+ firebase::auth::FederatedOAuthProvider provider;
+ provider.SetProviderData(provider_data);
+ LogMessage("SignInWithProvider SETUP COMPLETE");
+ Future sign_in_future = auth->SignInWithProvider(&provider);
+ WaitForSignInFuture(sign_in_future, "SignInWithProvider", kAuthErrorNone,
+ auth);
+ if (sign_in_future.error() == kAuthErrorNone &&
+ sign_in_future.result() != nullptr) {
+ LogAuthResult(*sign_in_future.result());
+ }
+ }
+
+ { // --- ReauthenticateWithProvider ---
+ LogMessage("ReauthethenticateWithProvider");
+ if (!auth->current_user()) {
+ LogMessage("ERROR: Expected User from SignInWithProvider");
+ } else {
+ firebase::auth::FederatedOAuthProviderData provider_data;
+ provider_data.provider_id =
+ firebase::auth::GoogleAuthProvider::kProviderId;
+ provider_data.custom_parameters = {{"req_id", "1234"}};
+
+ firebase::auth::FederatedOAuthProvider provider;
+ provider.SetProviderData(provider_data);
+ Future sign_in_future =
+ auth->current_user().ReauthenticateWithProvider(&provider);
+ WaitForSignInFuture(sign_in_future, "ReauthenticateWithProvider",
+ kAuthErrorNone, auth);
+ if (sign_in_future.error() == kAuthErrorNone &&
+ sign_in_future.result() != nullptr) {
+ LogAuthResult(*sign_in_future.result());
+ }
+ }
+ }
+
+ // Clean up provider-linked user so we can run the test app again
+ // and not get "user with that email already exists" errors.
+ if (auth->current_user()) {
+ WaitForFuture(auth->current_user().Delete(), "Delete User",
+ kAuthErrorNone,
+ /*log_error=*/true);
+ }
+ } // end FederatedAuthProvider
+#endif // TARGET_OS_IPHONE || defined(__ANDROID__)
+#endif // INTERNAL_EXPERIMENTAL
+
LogMessage("Completed Auth tests.");
while (!ProcessEvents(1000)) {
diff --git a/auth/testapp/src/desktop/desktop_main.cc b/auth/testapp/src/desktop/desktop_main.cc
index 99ace543..0220c688 100644
--- a/auth/testapp/src/desktop/desktop_main.cc
+++ b/auth/testapp/src/desktop/desktop_main.cc
@@ -15,16 +15,35 @@
#include
#include
#include
-#ifndef _WIN32
+
+#ifdef _WIN32
+#include
+#define chdir _chdir
+#else
#include
-#endif // !_WIN32
+#endif // _WIN32
#ifdef _WIN32
#include
#endif // _WIN32
+#include
+#include
+
#include "main.h" // NOLINT
+// The TO_STRING macro is useful for command line defined strings as the quotes
+// get stripped.
+#define TO_STRING_EXPAND(X) #X
+#define TO_STRING(X) TO_STRING_EXPAND(X)
+
+// Path to the Firebase config file to load.
+#ifdef FIREBASE_CONFIG
+#define FIREBASE_CONFIG_STRING TO_STRING(FIREBASE_CONFIG)
+#else
+#define FIREBASE_CONFIG_STRING ""
+#endif // FIREBASE_CONFIG
+
extern "C" int common_main(int argc, const char* argv[]);
static bool quit = false;
@@ -50,6 +69,10 @@ bool ProcessEvents(int msec) {
return quit;
}
+std::string PathForResource() {
+ return std::string();
+}
+
void LogMessage(const char* format, ...) {
va_list list;
va_start(list, format);
@@ -61,7 +84,22 @@ void LogMessage(const char* format, ...) {
WindowContext GetWindowContext() { return nullptr; }
+// Change the current working directory to the directory containing the
+// specified file.
+void ChangeToFileDirectory(const char* file_path) {
+ std::string path(file_path);
+ std::replace(path.begin(), path.end(), '\\', '/');
+ auto slash = path.rfind('/');
+ if (slash != std::string::npos) {
+ std::string directory = path.substr(0, slash);
+ if (!directory.empty()) chdir(directory.c_str());
+ }
+}
+
int main(int argc, const char* argv[]) {
+ ChangeToFileDirectory(
+ FIREBASE_CONFIG_STRING[0] != '\0' ?
+ FIREBASE_CONFIG_STRING : argv[0]); // NOLINT
#ifdef _WIN32
SetConsoleCtrlHandler((PHANDLER_ROUTINE)SignalHandler, TRUE);
#else
@@ -69,3 +107,19 @@ int main(int argc, const char* argv[]) {
#endif // _WIN32
return common_main(argc, argv);
}
+
+#if defined(_WIN32)
+// Returns the number of microseconds since the epoch.
+int64_t WinGetCurrentTimeInMicroseconds() {
+ FILETIME file_time;
+ GetSystemTimeAsFileTime(&file_time);
+
+ ULARGE_INTEGER now;
+ now.LowPart = file_time.dwLowDateTime;
+ now.HighPart = file_time.dwHighDateTime;
+
+ // Windows file time is expressed in 100s of nanoseconds.
+ // To convert to microseconds, multiply x10.
+ return now.QuadPart * 10LL;
+}
+#endif
diff --git a/auth/testapp/src/ios/ios_main.mm b/auth/testapp/src/ios/ios_main.mm
index 6ccb2de5..16ede7ef 100644
--- a/auth/testapp/src/ios/ios_main.mm
+++ b/auth/testapp/src/ios/ios_main.mm
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#import
#import
#include
@@ -28,6 +29,8 @@ @interface AppDelegate : UIResponder
@interface FTAViewController : UIViewController
+@property(atomic, strong) NSString *textEntryResult;
+
@end
static int g_exit_status = 0;
@@ -36,6 +39,30 @@ @interface FTAViewController : UIViewController
static NSCondition *g_shutdown_signal;
static UITextView *g_text_view;
static UIView *g_parent_view;
+static FTAViewController *g_view_controller;
+
+void initGameCenter(UIViewController* view_controller) {
+ if (![GKLocalPlayer class])
+ return;
+
+ __weak GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer];
+ localPlayer.authenticateHandler = ^(UIViewController *gcAuthViewController, NSError *error) {
+ if (gcAuthViewController != nil) {
+ // Pause any activities that require user interaction, then present the
+ // gcAuthViewController to the player.
+ [view_controller presentViewController:gcAuthViewController animated:YES completion:nil];
+ } else if (localPlayer.isAuthenticated) {
+ // Player is already logged into Game Center
+ } else {
+ if (error) {
+ LogMessage("Unable to initialize GameCenter: %s", error.localizedDescription);
+ return;
+ } else {
+ LogMessage("Unable to initialize GameCenter: Unknown Error");
+ }
+ }
+ };
+}
@implementation FTAViewController
@@ -45,6 +72,7 @@ - (void)viewDidLoad {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
const char *argv[] = {FIREBASE_TESTAPP_NAME};
[g_shutdown_signal lock];
+ initGameCenter(self);
g_exit_status = common_main(1, argv);
[g_shutdown_complete signal];
});
@@ -86,6 +114,56 @@ int main(int argc, char* argv[]) {
return g_exit_status;
}
+// Create an alert dialog via UIAlertController, and prompt the user to enter a line of text.
+// This function spins until the text has been entered (or the alert dialog was canceled).
+// If the user cancels, returns an empty string.
+std::string ReadTextInput(const char *title, const char *message, const char *placeholder) {
+ assert(g_view_controller);
+ // This should only be called from a background thread, as it blocks, which will mess up the main
+ // thread.
+ assert(![NSThread isMainThread]);
+
+ g_view_controller.textEntryResult = nil;
+
+ dispatch_async(dispatch_get_main_queue(), ^{
+ UIAlertController *alertController =
+ [UIAlertController alertControllerWithTitle:@(title)
+ message:@(message)
+ preferredStyle:UIAlertControllerStyleAlert];
+ [alertController addTextFieldWithConfigurationHandler:^(UITextField *_Nonnull textField) {
+ textField.placeholder = @(placeholder);
+ }];
+ UIAlertAction *confirmAction = [UIAlertAction
+ actionWithTitle:@"OK"
+ style:UIAlertActionStyleDefault
+ handler:^(UIAlertAction *_Nonnull action) {
+ g_view_controller.textEntryResult = alertController.textFields.firstObject.text;
+ }];
+ [alertController addAction:confirmAction];
+ UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"Cancel"
+ style:UIAlertActionStyleCancel
+ handler:^(UIAlertAction *_Nonnull action) {
+ g_view_controller.textEntryResult = @"";
+ }];
+ [alertController addAction:cancelAction];
+ [g_view_controller presentViewController:alertController animated:YES completion:nil];
+ });
+
+ while (true) {
+ // Pause a second, waiting for the user to enter text.
+ if (ProcessEvents(1000)) {
+ // If this returns true, an exit was requested.
+ return "";
+ }
+ if (g_view_controller.textEntryResult != nil) {
+ // textEntryResult will be set to non-nil when a dialog button is clicked.
+ std::string result = g_view_controller.textEntryResult.UTF8String;
+ g_view_controller.textEntryResult = nil; // Consume the result.
+ return result;
+ }
+ }
+}
+
@implementation AppDelegate
- (BOOL)application:(UIApplication*)application
@@ -95,22 +173,23 @@ - (BOOL)application:(UIApplication*)application
[g_shutdown_complete lock];
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
- FTAViewController *viewController = [[FTAViewController alloc] init];
- self.window.rootViewController = viewController;
+ g_view_controller = [[FTAViewController alloc] init];
+ self.window.rootViewController = g_view_controller;
[self.window makeKeyAndVisible];
- g_text_view = [[UITextView alloc] initWithFrame:viewController.view.bounds];
+ g_text_view = [[UITextView alloc] initWithFrame:g_view_controller.view.bounds];
g_text_view.editable = NO;
g_text_view.scrollEnabled = YES;
g_text_view.userInteractionEnabled = YES;
- [viewController.view addSubview:g_text_view];
+ [g_view_controller.view addSubview:g_text_view];
return YES;
}
- (void)applicationWillTerminate:(UIApplication *)application {
+ g_view_controller = nil;
g_shutdown = true;
[g_shutdown_signal signal];
[g_shutdown_complete wait];
diff --git a/auth/testapp/src/main.h b/auth/testapp/src/main.h
index 2eda2c10..130286c1 100644
--- a/auth/testapp/src/main.h
+++ b/auth/testapp/src/main.h
@@ -15,14 +15,17 @@
#ifndef FIREBASE_TESTAPP_MAIN_H_ // NOLINT
#define FIREBASE_TESTAPP_MAIN_H_ // NOLINT
+#include
+
#if defined(__ANDROID__)
#include
#include
#elif defined(__APPLE__)
+#include
extern "C" {
#include
} // extern "C"
-#endif // __ANDROID__
+#endif // __ANDROID__, __APPLE__
// Defined using -DANDROID_MAIN_APP_NAME=some_app_name when compiling this
// file.
@@ -42,7 +45,7 @@ bool ProcessEvents(int msec);
// (and usage) vary based on the OS.
#if defined(__ANDROID__)
typedef jobject WindowContext; // A jobject to the Java Activity.
-#elif defined(__APPLE__)
+#elif TARGET_OS_IPHONE
typedef id WindowContext; // A pointer to an iOS UIView.
#else
typedef void* WindowContext; // A void* for any other environments.
@@ -60,4 +63,11 @@ jobject GetActivity();
// to the root view of the view controller.
WindowContext GetWindowContext();
+// Prompt the user with a dialog box to enter a line of text, blocking
+// until the user enters the text or the dialog box is canceled.
+// Returns the text that was entered, or an empty string if the user
+// canceled.
+std::string ReadTextInput(const char* title, const char* message,
+ const char* placeholder);
+
#endif // FIREBASE_TESTAPP_MAIN_H_ // NOLINT
diff --git a/auth/testapp/testapp.xcodeproj/project.pbxproj b/auth/testapp/testapp.xcodeproj/project.pbxproj
index baf45c4c..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 = 8.4;
+ 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 = 8.4;
+ IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
diff --git a/auth/testapp/testapp/Info.plist b/auth/testapp/testapp/Info.plist
index cd039b8f..3831d56d 100644
--- a/auth/testapp/testapp/Info.plist
+++ b/auth/testapp/testapp/Info.plist
@@ -7,7 +7,7 @@
CFBundleExecutable
$(EXECUTABLE_NAME)
CFBundleIdentifier
- com.google.ios.auth.testapp
+ com.google.FirebaseCppAuthTestApp.dev
CFBundleInfoDictionaryVersion
6.0
CFBundleName
@@ -16,10 +16,18 @@
APPL
CFBundleShortVersionString
1.0
- CFBundleSignature
- ????
CFBundleURLTypes
+
+ CFBundleTypeRole
+ Editor
+ CFBundleURLName
+ com.google.FirebaseCppAuthTestApp.dev
+ CFBundleURLSchemes
+
+ com.google.FirebaseCppAuthTestApp.dev
+
+
CFBundleTypeRole
Editor
@@ -27,19 +35,9 @@
google
CFBundleURLSchemes
- com.googleusercontent.apps.255980362477-3a1nf8c4nl0c7hlnlnmc98hbtg2mnbue
+ YOUR_REVERSED_CLIENT_ID
-
- CFBundleTypeRole
- Editor
- CFBundleURLName
- google
- CFBundleURLSchemes
-
- com.google.ios.auth.testapp
-
-
CFBundleVersion
1
diff --git a/database/testapp/AndroidManifest.xml b/database/testapp/AndroidManifest.xml
index da5edbaa..f94a8ca5 100644
--- a/database/testapp/AndroidManifest.xml
+++ b/database/testapp/AndroidManifest.xml
@@ -6,9 +6,10 @@
-
+
)
+ set(FOUND_JSON_FILE TRUE)
+ break()
+ endif()
+ endforeach()
+ if(NOT FOUND_JSON_FILE)
+ message(WARNING "Failed to find either google-services-desktop.json or google-services.json. See the readme.md for more information.")
+ endif()
+endif()
+
+# Add the Firebase libraries to the target using the function from the SDK.
+add_subdirectory(${FIREBASE_CPP_SDK_DIR} bin/ EXCLUDE_FROM_ALL)
+# Note that firebase_app needs to be last in the list.
+set(firebase_libs firebase_database firebase_auth firebase_app)
+target_link_libraries(${target_name} "${firebase_libs}" ${ADDITIONAL_LIBS})
diff --git a/database/testapp/Podfile b/database/testapp/Podfile
index 53076d26..5832b272 100644
--- a/database/testapp/Podfile
+++ b/database/testapp/Podfile
@@ -1,7 +1,8 @@
source 'https://github.com/CocoaPods/Specs.git'
-platform :ios, '8.0'
+platform :ios, '13.0'
+use_frameworks!
# Firebase Realtime Database test application.
target 'testapp' do
-pod 'Firebase/Database'
-pod 'Firebase/Auth'
+ pod 'Firebase/Database', '10.25.0'
+ pod 'Firebase/Auth', '10.25.0'
end
diff --git a/database/testapp/build.gradle b/database/testapp/build.gradle
index ef9ac466..bfa91283 100644
--- a/database/testapp/build.gradle
+++ b/database/testapp/build.gradle
@@ -2,118 +2,76 @@
buildscript {
repositories {
mavenLocal()
+ maven { url 'https://maven.google.com' }
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:2.1.2'
- classpath 'com.google.gms:google-services:3.0.0'
+ classpath 'com.android.tools.build:gradle:4.2.1'
+ classpath 'com.google.gms:google-services:4.0.1'
}
}
allprojects {
repositories {
mavenLocal()
+ maven { url 'https://maven.google.com' }
jcenter()
}
}
apply plugin: 'com.android.application'
-// Pre-experimental Gradle plug-in NDK boilerplate below.
-// Right now the Firebase plug-in does not work with the experimental
-// Gradle plug-in so we're using ndk-build for the moment.
-project.ext {
- // Configure the Firebase C++ SDK location.
- firebase_cpp_sdk_dir = System.getProperty('firebase_cpp_sdk.dir')
- if (firebase_cpp_sdk_dir == null || firebase_cpp_sdk_dir.isEmpty()) {
- firebase_cpp_sdk_dir = System.getenv('FIREBASE_CPP_SDK_DIR')
- if (firebase_cpp_sdk_dir == null || firebase_cpp_sdk_dir.isEmpty()) {
- if ((new File('firebase_cpp_sdk')).exists()) {
- firebase_cpp_sdk_dir = 'firebase_cpp_sdk'
- } else {
- throw new StopActionException(
- 'firebase_cpp_sdk.dir property or the FIREBASE_CPP_SDK_DIR ' +
- 'environment variable must be set to reference the Firebase C++ ' +
- 'SDK install directory. This is used to configure static library ' +
- 'and C/C++ include paths for the SDK.')
- }
- }
- }
- if (!(new File(firebase_cpp_sdk_dir)).exists()) {
- throw new StopActionException(
- sprintf('Firebase C++ SDK directory %s does not exist',
- firebase_cpp_sdk_dir))
- }
- // Check the NDK location using the same configuration options as the
- // experimental Gradle plug-in.
- ndk_dir = project.android.ndkDirectory
- if (ndk_dir == null || !ndk_dir.exists()) {
- ndk_dir = System.getenv('ANDROID_NDK_HOME')
- if (ndk_dir == null || ndk_dir.isEmpty()) {
- throw new StopActionException(
- 'Android NDK directory should be specified using the ndk.dir ' +
- 'property or ANDROID_NDK_HOME environment variable.')
- }
- }
-}
-
android {
- compileSdkVersion 23
- buildToolsVersion '23.0.3'
+ compileOptions {
+ sourceCompatibility 1.8
+ targetCompatibility 1.8
+ }
+ compileSdkVersion 34
+ ndkPath System.getenv('ANDROID_NDK_HOME')
+ buildToolsVersion '30.0.2'
- sourceSets {
- main {
- jniLibs.srcDirs = ['libs']
- manifest.srcFile 'AndroidManifest.xml'
- java.srcDirs = ['src/android/java']
- res.srcDirs = ['res']
- }
+ sourceSets {
+ main {
+ jniLibs.srcDirs = ['libs']
+ manifest.srcFile 'AndroidManifest.xml'
+ java.srcDirs = ['src/android/java']
+ res.srcDirs = ['res']
}
+ }
- defaultConfig {
- applicationId 'com.google.firebase.cpp.database.testapp'
- minSdkVersion 14
- targetSdkVersion 23
- versionCode 1
- versionName '1.0'
+ defaultConfig {
+ applicationId 'com.google.firebase.cpp.database.testapp'
+ minSdkVersion 23
+ targetSdkVersion 28
+ versionCode 1
+ versionName '1.0'
+ externalNativeBuild.cmake {
+ arguments "-DFIREBASE_CPP_SDK_DIR=$gradle.firebase_cpp_sdk_dir"
}
- buildTypes {
- release {
- minifyEnabled true
- proguardFile getDefaultProguardFile('proguard-android.txt')
- proguardFile file(project.ext.firebase_cpp_sdk_dir + "/libs/android/app.pro")
- proguardFile file(project.ext.firebase_cpp_sdk_dir + "/libs/android/auth.pro")
- proguardFile file(project.ext.firebase_cpp_sdk_dir + "/libs/android/database.pro")
- proguardFile file('proguard.pro')
- }
+ }
+ externalNativeBuild.cmake {
+ path 'CMakeLists.txt'
+ }
+ buildTypes {
+ release {
+ minifyEnabled true
+ proguardFile getDefaultProguardFile('proguard-android.txt')
+ proguardFile file('proguard.pro')
}
+ }
+ packagingOptions {
+ pickFirst 'META-INF/**/coroutines.pro'
+ }
+ lintOptions {
+ abortOnError false
+ checkReleaseBuilds false
+ }
}
-dependencies {
- compile 'com.google.firebase:firebase-auth:10.2.0'
- compile 'com.google.firebase:firebase-database:10.2.0'
+apply from: "$gradle.firebase_cpp_sdk_dir/Android/firebase_dependencies.gradle"
+firebaseCpp.dependencies {
+ auth
+ database
}
apply plugin: 'com.google.gms.google-services'
-
-task ndkBuildCompile(type:Exec) {
- description 'Use ndk-build to compile the C++ application.'
- commandLine("${project.ext.ndk_dir}${File.separator}ndk-build",
- "FIREBASE_CPP_SDK_DIR=${project.ext.firebase_cpp_sdk_dir}",
- sprintf("APP_PLATFORM=android-%d",
- android.defaultConfig.minSdkVersion.mApiLevel))
-}
-
-task ndkBuildClean(type:Exec) {
- description 'Use ndk-build to clean the C++ application.'
- commandLine("${project.ext.ndk_dir}${File.separator}ndk-build",
- "FIREBASE_CPP_SDK_DIR=${project.ext.firebase_cpp_sdk_dir}",
- "clean")
-}
-
-// Once the Android Gradle plug-in has generated tasks, add dependencies for
-// the ndk-build targets.
-project.afterEvaluate {
- preBuild.dependsOn(ndkBuildCompile)
- clean.dependsOn(ndkBuildClean)
-}
diff --git a/database/testapp/gradle.properties b/database/testapp/gradle.properties
new file mode 100644
index 00000000..d7ba8f42
--- /dev/null
+++ b/database/testapp/gradle.properties
@@ -0,0 +1 @@
+android.useAndroidX = true
diff --git a/database/testapp/gradle/wrapper/gradle-wrapper.properties b/database/testapp/gradle/wrapper/gradle-wrapper.properties
index d5705170..65340c1b 100644
--- a/database/testapp/gradle/wrapper/gradle-wrapper.properties
+++ b/database/testapp/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Wed Apr 10 15:27:10 PDT 2013
+#Mon Nov 27 14:03:45 PST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
+distributionUrl=https://services.gradle.org/distributions/gradle-6.7.1-all.zip
diff --git a/database/testapp/jni/Android.mk b/database/testapp/jni/Android.mk
deleted file mode 100644
index 2b62b147..00000000
--- a/database/testapp/jni/Android.mk
+++ /dev/null
@@ -1,64 +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.
-
-LOCAL_PATH:=$(call my-dir)/..
-
-ifeq ($(FIREBASE_CPP_SDK_DIR),)
-$(error FIREBASE_CPP_SDK_DIR must specify the Firebase package location.)
-endif
-
-# With Firebase libraries for the selected build configuration (ABI + STL)
-STL:=$(firstword $(subst _, ,$(APP_STL)))
-FIREBASE_LIBRARY_PATH:=\
-$(FIREBASE_CPP_SDK_DIR)/libs/android/$(TARGET_ARCH_ABI)/$(STL)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE:=firebase_app
-LOCAL_SRC_FILES:=$(FIREBASE_LIBRARY_PATH)/libapp.a
-LOCAL_EXPORT_C_INCLUDES:=$(FIREBASE_CPP_SDK_DIR)/include
-include $(PREBUILT_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE:=firebase_auth
-LOCAL_SRC_FILES:=$(FIREBASE_LIBRARY_PATH)/libauth.a
-LOCAL_EXPORT_C_INCLUDES:=$(FIREBASE_CPP_SDK_DIR)/include
-include $(PREBUILT_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE:=firebase_database
-LOCAL_SRC_FILES:=$(FIREBASE_LIBRARY_PATH)/libdatabase.a
-LOCAL_EXPORT_C_INCLUDES:=$(FIREBASE_CPP_SDK_DIR)/include
-include $(PREBUILT_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE:=android_main
-LOCAL_SRC_FILES:=\
- $(LOCAL_PATH)/src/common_main.cc \
- $(LOCAL_PATH)/src/android/android_main.cc
-LOCAL_STATIC_LIBRARIES:=\
- firebase_database \
- firebase_auth \
- firebase_app
-LOCAL_WHOLE_STATIC_LIBRARIES:=\
- android_native_app_glue
-LOCAL_C_INCLUDES:=\
- $(NDK_ROOT)/sources/android/native_app_glue \
- $(LOCAL_PATH)/src
-LOCAL_LDLIBS:=-llog -landroid -latomic
-LOCAL_ARM_MODE:=arm
-LOCAL_LDFLAGS:=-Wl,-z,defs -Wl,--no-undefined
-include $(BUILD_SHARED_LIBRARY)
-
-$(call import-add-path,$(NDK_ROOT)/sources/android)
-$(call import-module,android/native_app_glue)
diff --git a/database/testapp/jni/Application.mk b/database/testapp/jni/Application.mk
deleted file mode 100644
index 53ed56a2..00000000
--- a/database/testapp/jni/Application.mk
+++ /dev/null
@@ -1,20 +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.
-
-APP_PLATFORM:=android-14
-NDK_TOOLCHAIN_VERSION=clang
-APP_ABI:=armeabi armeabi-v7a arm64-v8a x86 x86_64 mips mips64
-APP_STL:=c++_static
-APP_MODULES:=android_main
-APP_CPPFLAGS+=-std=c++11
diff --git a/database/testapp/proguard.pro b/database/testapp/proguard.pro
index c7e9278d..54cd248b 100644
--- a/database/testapp/proguard.pro
+++ b/database/testapp/proguard.pro
@@ -1 +1,2 @@
+-ignorewarnings
-keep,includedescriptorclasses public class com.google.firebase.example.LoggingUtils { *; }
diff --git a/database/testapp/readme.md b/database/testapp/readme.md
index 809e0dc5..7841f590 100644
--- a/database/testapp/readme.md
+++ b/database/testapp/readme.md
@@ -44,16 +44,16 @@ Building and Running the testapp
- Link your iOS app to the Firebase libraries.
- Get CocoaPods version 1 or later by running,
```
- $ sudo gem install CocoaPods --pre
+ sudo gem install cocoapods --pre
```
- From the testapp directory, install the CocoaPods listed in the Podfile
by running,
```
- $ pod install
+ pod install
```
- Open the generated Xcode workspace (which now has the CocoaPods),
```
- $ open testapp.xcworkspace
+ open testapp.xcworkspace
```
- For further details please refer to the
[general instructions for setting up an iOS app with Firebase](https://firebase.google.com/docs/ios/setup).
@@ -70,8 +70,8 @@ Building and Running the testapp
authenticate with Firebase Database, which requires a signed-in user by
default (an anonymous user will suffice).
- Download the Firebase C++ SDK linked from
- https://firebase.google.com/docs/cpp/setup and unzip it to a
- directory of your choice.
+ [https://firebase.google.com/docs/cpp/setup](https://firebase.google.com/docs/cpp/setup)
+ and unzip it to a directory of your choice.
- Add the following frameworks from the Firebase C++ SDK to the project:
- frameworks/ios/universal/firebase.framework
- frameworks/ios/universal/firebase_auth.framework
@@ -125,18 +125,19 @@ Building and Running the testapp
- For further details please refer to the
[general instructions for setting up an Android app with Firebase](https://firebase.google.com/docs/android/setup).
- Download the Firebase C++ SDK linked from
- https://firebase.google.com/docs/cpp/setup and unzip it to a
- directory of your choice.
+ [https://firebase.google.com/docs/cpp/setup](https://firebase.google.com/docs/cpp/setup)
+ and unzip it to a directory of your choice.
- Configure the location of the Firebase C++ SDK by setting the
firebase\_cpp\_sdk.dir Gradle property to the SDK install directory.
For example, in the project directory:
```
- > echo "systemProp.firebase\_cpp\_sdk.dir=/User/$USER/firebase\_cpp\_sdk" >> gradle.properties
+ echo "systemProp.firebase\_cpp\_sdk.dir=/User/$USER/firebase\_cpp\_sdk" >> gradle.properties
```
- Ensure the Android SDK and NDK locations are set in Android Studio.
- - From the Android Studio launch menu, go to
- Configure/Project Defaults/Project Structure and download the SDK and NDK if
- the locations are not yet set.
+ - From the Android Studio launch menu, go to `File/Project Structure...` or
+ `Configure/Project Defaults/Project Structure...`
+ (Shortcut: Control + Alt + Shift + S on windows, Command + ";" on a mac)
+ and download the SDK and NDK if the locations are not yet set.
- Open *build.gradle* in Android Studio.
- From the Android Studio launch menu, "Open an existing Android Studio
project", and select `build.gradle`.
@@ -146,6 +147,71 @@ Building and Running the testapp
viewed on the device's display, or in the logcat output of Android studio or
by running "adb logcat *:W android_main firebase" from the command line.
+### Desktop
+ - Register your app with Firebase.
+ - Create a new app on the [Firebase console](https://firebase.google.com/console/),
+ following the above instructions for Android or iOS.
+ - The Desktop version of the library requires you to set up any keys you
+ use via Query::OrderByChild() in advance. Add this to your database's
+ rules in the _Rules_ tab of your database settings in Firebase console:
+ ```
+ {
+ "rules": {
+ // Require authentication for writing to the database.
+ ".read": "true",
+ ".write": "auth != null",
+ // Set up entity_list to be indexed by the entity_type child.
+ "test_app_data": {
+ "$dummy": {
+ "ChildListener": {
+ "entity_list": {
+ ".indexOn": "entity_type"
+ }
+ }
+ }
+ }
+ }
+ }
+ ```
+ - If you have an Android project, add the `google-services.json` file that
+ you downloaded from the Firebase console to the root directory of the
+ testapp.
+ - If you have an iOS project, and don't wish to use an Android project,
+ you can use the Python script `generate_xml_from_google_services_json.py --plist`,
+ located in the Firebase C++ SDK, to convert your `GoogleService-Info.plist`
+ file into a `google-services-desktop.json` file, which can then be
+ placed in the root directory of the testapp.
+ - Download the Firebase C++ SDK linked from
+ [https://firebase.google.com/docs/cpp/setup](https://firebase.google.com/docs/cpp/setup)
+ and unzip it to a directory of your choice.
+ - Configure the testapp with the location of the Firebase C++ SDK.
+ This can be done a couple different ways (in highest to lowest priority):
+ - When invoking cmake, pass in the location with
+ -DFIREBASE_CPP_SDK_DIR=/path/to/firebase_cpp_sdk.
+ - Set an environment variable for FIREBASE_CPP_SDK_DIR to the path to use.
+ - Edit the CMakeLists.txt file, changing the FIREBASE_CPP_SDK_DIR path
+ to the appropriate location.
+ - From the testapp directory, generate the build files by running,
+ ```
+ cmake .
+ ```
+ If you want to use XCode, you can use -G"Xcode" to generate the project.
+ Similarly, to use Visual Studio, -G"Visual Studio 15 2017". For more
+ information, see
+ [CMake generators](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html).
+ - Build the testapp, by either opening the generated project file based on
+ the platform, or running,
+ ```
+ cmake --build .
+ ```
+ - Execute the testapp by running,
+ ```
+ ./desktop_testapp
+ ```
+ Note that the executable might be under another directory, such as Debug.
+ - The testapp has no user interface, but the output can be viewed via the
+ console.
+
Known issues
------------
@@ -159,7 +225,7 @@ Known issues
Support
-------
-https://firebase.google.com/support/
+[https://firebase.google.com/support/](https://firebase.google.com/support/)
License
-------
@@ -180,4 +246,3 @@ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations under
the License.
-
diff --git a/database/testapp/settings.gradle b/database/testapp/settings.gradle
new file mode 100644
index 00000000..2a543b93
--- /dev/null
+++ b/database/testapp/settings.gradle
@@ -0,0 +1,36 @@
+// Copyright 2018 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+def firebase_cpp_sdk_dir = System.getProperty('firebase_cpp_sdk.dir')
+if (firebase_cpp_sdk_dir == null || firebase_cpp_sdk_dir.isEmpty()) {
+ firebase_cpp_sdk_dir = System.getenv('FIREBASE_CPP_SDK_DIR')
+ if (firebase_cpp_sdk_dir == null || firebase_cpp_sdk_dir.isEmpty()) {
+ if ((new File('firebase_cpp_sdk')).exists()) {
+ firebase_cpp_sdk_dir = 'firebase_cpp_sdk'
+ } else {
+ throw new StopActionException(
+ 'firebase_cpp_sdk.dir property or the FIREBASE_CPP_SDK_DIR ' +
+ 'environment variable must be set to reference the Firebase C++ ' +
+ 'SDK install directory. This is used to configure static library ' +
+ 'and C/C++ include paths for the SDK.')
+ }
+ }
+}
+if (!(new File(firebase_cpp_sdk_dir)).exists()) {
+ throw new StopActionException(
+ sprintf('Firebase C++ SDK directory %s does not exist',
+ firebase_cpp_sdk_dir))
+}
+gradle.ext.firebase_cpp_sdk_dir = "$firebase_cpp_sdk_dir"
+includeBuild "$firebase_cpp_sdk_dir"
\ No newline at end of file
diff --git a/database/testapp/src/common_main.cc b/database/testapp/src/common_main.cc
index 18c66a68..3a35179b 100644
--- a/database/testapp/src/common_main.cc
+++ b/database/testapp/src/common_main.cc
@@ -52,7 +52,7 @@ class SampleValueListener : public firebase::database::ValueListener {
std::vector seen_values_;
};
-// An example ChildListener class. TODO(jsimantov) implement.
+// An example ChildListener class.
class SampleChildListener : public firebase::database::ChildListener {
public:
void OnChildAdded(const firebase::database::DataSnapshot& snapshot,
@@ -109,6 +109,9 @@ class ExpectValueListener : public firebase::database::ValueListener {
const firebase::database::DataSnapshot& snapshot) override {
if (snapshot.value().AsString() == wait_value_) {
got_value_ = true;
+ } else {
+ LogMessage(
+ "FAILURE: ExpectValueListener did not receive the expected result.");
}
}
void OnCancelled(const firebase::database::Error& error_code,
@@ -189,12 +192,14 @@ extern "C" int common_main(int argc, const char* argv[]) {
}
LogMessage("Successfully initialized Firebase Auth and Firebase Database.");
+ database->set_persistence_enabled(true);
+
// Sign in using Auth before accessing the database.
// The default Database permissions allow anonymous users access. This will
// work as long as your project's Authentication permissions allow anonymous
// signin.
{
- firebase::Future sign_in_future =
+ firebase::Future sign_in_future =
auth->SignInAnonymously();
WaitForCompletion(sign_in_future, "SignInAnonymously");
if (sign_in_future.error() == firebase::auth::kAuthErrorNone) {
@@ -214,8 +219,8 @@ extern "C" int common_main(int argc, const char* argv[]) {
std::string saved_url; // persists across connections
// Create a unique child in the database that we can run our tests in.
- firebase::database::DatabaseReference ref =
- database->GetReference("test_app_data").PushChild();
+ firebase::database::DatabaseReference ref;
+ ref = database->GetReference("test_app_data").PushChild();
saved_url = ref.url();
LogMessage("URL: %s", saved_url.c_str());
@@ -226,6 +231,7 @@ extern "C" int common_main(int argc, const char* argv[]) {
{
const char* kSimpleString = "Some simple string";
const int kSimpleInt = 2;
+ const int kSimplePriority = 100;
const double kSimpleDouble = 3.4;
const bool kSimpleBool = true;
@@ -243,22 +249,30 @@ extern "C" int common_main(int argc, const char* argv[]) {
ref.Child("Simple")
.Child("Timestamp")
.SetValue(firebase::database::ServerTimestamp());
+ firebase::Future f6 =
+ ref.Child("Simple")
+ .Child("IntAndPriority")
+ .SetValueAndPriority(kSimpleInt, kSimplePriority);
WaitForCompletion(f1, "SetSimpleString");
WaitForCompletion(f2, "SetSimpleInt");
WaitForCompletion(f3, "SetSimpleDouble");
WaitForCompletion(f4, "SetSimpleBool");
WaitForCompletion(f5, "SetSimpleTimestamp");
+ WaitForCompletion(f6, "SetSimpleIntAndPriority");
if (f1.error() != firebase::database::kErrorNone ||
f2.error() != firebase::database::kErrorNone ||
f3.error() != firebase::database::kErrorNone ||
f4.error() != firebase::database::kErrorNone ||
- f5.error() != firebase::database::kErrorNone) {
+ f5.error() != firebase::database::kErrorNone ||
+ f6.error() != firebase::database::kErrorNone) {
LogMessage("ERROR: Set simple values failed.");
LogMessage(" String: Error %d: %s", f1.error(), f1.error_message());
LogMessage(" Int: Error %d: %s", f2.error(), f2.error_message());
LogMessage(" Double: Error %d: %s", f3.error(), f3.error_message());
LogMessage(" Bool: Error %d: %s", f4.error(), f4.error_message());
LogMessage(" Timestamp: Error %d: %s", f5.error(), f5.error_message());
+ LogMessage(" Int and Priority: Error %d: %s", f6.error(),
+ f6.error_message());
} else {
LogMessage("SUCCESS: Set simple values.");
}
@@ -277,30 +291,36 @@ extern "C" int common_main(int argc, const char* argv[]) {
ref.Child("Simple").Child("Bool").GetValue();
firebase::Future f5 =
ref.Child("Simple").Child("Timestamp").GetValue();
+ firebase::Future f6 =
+ ref.Child("Simple").Child("IntAndPriority").GetValue();
WaitForCompletion(f1, "GetSimpleString");
WaitForCompletion(f2, "GetSimpleInt");
WaitForCompletion(f3, "GetSimpleDouble");
WaitForCompletion(f4, "GetSimpleBool");
WaitForCompletion(f5, "GetSimpleTimestamp");
+ WaitForCompletion(f6, "GetSimpleIntAndPriority");
if (f1.error() == firebase::database::kErrorNone &&
f2.error() == firebase::database::kErrorNone &&
f3.error() == firebase::database::kErrorNone &&
f4.error() == firebase::database::kErrorNone &&
- f5.error() == firebase::database::kErrorNone) {
+ f5.error() == firebase::database::kErrorNone &&
+ f6.error() == firebase::database::kErrorNone) {
// Get the current time to compare to the Timestamp.
int64_t current_time_milliseconds =
static_cast(time(nullptr)) * 1000L;
int64_t time_difference = f5.result()->value().AsInt64().int64_value() -
current_time_milliseconds;
- // As long as our timestamp is within a day, it's correct enough for our
- // purposes.
- const int64_t kAllowedTimeDifferenceMilliseconds =
- 1000L * 60L * 60L * 24L;
+ // As long as our timestamp is within 15 minutes, it's correct enough
+ // for our purposes.
+ const int64_t kAllowedTimeDifferenceMilliseconds = 1000L * 60L * 15L;
+
if (f1.result()->value().AsString() != kSimpleString ||
f2.result()->value().AsInt64() != kSimpleInt ||
f3.result()->value().AsDouble() != kSimpleDouble ||
f4.result()->value().AsBool() != kSimpleBool ||
+ f6.result()->value().AsInt64() != kSimpleInt ||
+ f6.result()->priority().AsInt64() != kSimplePriority ||
time_difference > kAllowedTimeDifferenceMilliseconds ||
time_difference < -kAllowedTimeDifferenceMilliseconds) {
LogMessage("ERROR: Get simple values failed, values did not match.");
@@ -318,6 +338,12 @@ extern "C" int common_main(int argc, const char* argv[]) {
LogMessage(" Timestamp: Got %lld, expected something near %lld",
f5.result()->value().AsInt64().int64_value(),
current_time_milliseconds);
+ LogMessage(
+ " IntAndPriority: Got {.value:%lld,.priority:%lld}, expected "
+ "{.value:%d,.priority:%d}",
+ f6.result()->value().AsInt64().int64_value(),
+ f6.result()->priority().AsInt64().int64_value(), kSimpleInt,
+ kSimplePriority);
} else {
LogMessage("SUCCESS: Get simple values.");
@@ -344,6 +370,65 @@ extern "C" int common_main(int argc, const char* argv[]) {
}
}
+#if defined(__ANDROID__) || TARGET_OS_IPHONE
+ // Actually shut down the realtime database, and restart it, to make sure
+ // that persistence persists across database object instances.
+ {
+ // Write a value that we can test for.
+ const char* kPersistenceString = "Persistence Test!";
+ WaitForCompletion(ref.Child("PersistenceTest").SetValue(kPersistenceString),
+ "SetPersistenceTestValue");
+
+ LogMessage("Destroying database object.");
+ delete database;
+ LogMessage("Recreating database object.");
+ database = ::firebase::database::Database::GetInstance(app);
+
+ // Offline mode. If persistence works, we should still be able to fetch
+ // our value even though we're offline.
+
+ database->GoOffline();
+ ref = database->GetReferenceFromUrl(saved_url.c_str());
+
+ {
+ LogMessage(
+ "TEST: Fetching the value while offline via AddValueListener.");
+ ExpectValueListener* listener =
+ new ExpectValueListener(kPersistenceString);
+ ref.Child("PersistenceTest").AddValueListener(listener);
+
+ while (!listener->got_value()) {
+ ProcessEvents(100);
+ }
+ delete listener;
+ listener = nullptr;
+ }
+
+ {
+ LogMessage("TEST: Fetching the value while offline via GetValue.");
+ firebase::Future value_future =
+ ref.Child("PersistenceTest").GetValue();
+
+ WaitForCompletion(value_future, "GetValue");
+
+ const firebase::database::DataSnapshot& result = *value_future.result();
+
+ if (value_future.error() == firebase::database::kErrorNone) {
+ if (result.value().AsString() == kPersistenceString) {
+ LogMessage("SUCCESS: GetValue returned the correct value.");
+ } else {
+ LogMessage("FAILURE: GetValue returned an incorrect value.");
+ }
+ } else {
+ LogMessage("FAILURE: GetValue Future returned an error.");
+ }
+ }
+
+ LogMessage("Going back online.");
+ database->GoOnline();
+ }
+#endif // defined(__ANDROID__) || TARGET_OS_IPHONE
+
// Test running a transaction. This will call RunTransaction and set
// some values, including incrementing the player's score.
{
@@ -358,18 +443,26 @@ extern "C" int common_main(int argc, const char* argv[]) {
"SetInitialScoreValue");
// The transaction will set the player's item and class, and increment
// their score by 100 points.
+ int score_delta = 100;
transaction_future =
ref.Child("TransactionResult")
- .RunTransaction([](firebase::database::MutableData* data) {
- LogMessage(" Transaction function executing.");
- data->Child("player_item").set_value("Fire sword");
- data->Child("player_class").set_value("Warrior");
- // Increment the current score by 100.
- int64_t score =
- data->Child("player_score").value().AsInt64().int64_value();
- data->Child("player_score").set_value(score + kAddedScore);
- return firebase::database::kTransactionResultSuccess;
- });
+ .RunTransaction(
+ [](firebase::database::MutableData* data,
+ void* score_delta_void) {
+ LogMessage(" Transaction function executing.");
+ data->Child("player_item").set_value("Fire sword");
+ data->Child("player_class").set_value("Warrior");
+ // Increment the current score by 100.
+ int64_t score = data->Child("player_score")
+ .value()
+ .AsInt64()
+ .int64_value();
+ data->Child("player_score")
+ .set_value(score +
+ *reinterpret_cast(score_delta_void));
+ return firebase::database::kTransactionResultSuccess;
+ },
+ &score_delta);
WaitForCompletion(transaction_future, "RunTransaction");
// Check whether the transaction succeeded, was aborted, or failed with an
@@ -585,17 +678,20 @@ extern "C" int common_main(int argc, const char* argv[]) {
}
// Test a ValueListener, which sits on a Query and listens for changes in
- // the
- // value at that location.
+ // the value at that location.
{
LogMessage("TEST: ValueListener");
SampleValueListener* listener = new SampleValueListener();
- // Set a value before attaching the listener. The listener should not
- // receive this value.
WaitForCompletion(ref.Child("ValueListener").SetValue(0), "SetValueZero");
// Attach the listener, then set 3 values, which will trigger the
// listener.
ref.Child("ValueListener").AddValueListener(listener);
+
+ // The listener's OnChanged callback is triggered once when the listener is
+ // attached and again every time the data, including children, changes.
+ // Wait for here for a moment for the initial values to be received.
+ ProcessEvents(2000);
+
WaitForCompletion(ref.Child("ValueListener").SetValue(1), "SetValueOne");
WaitForCompletion(ref.Child("ValueListener").SetValue(2), "SetValueTwo");
WaitForCompletion(ref.Child("ValueListener").SetValue(3), "SetValueThree");
@@ -614,10 +710,11 @@ extern "C" int common_main(int argc, const char* argv[]) {
// Wait a few more seconds to ensure the listener is not triggered.
ProcessEvents(2000);
- // Ensure that the listener was only triggered 3 times, with the values
- // 1, 2, and 3.
- if (listener->num_seen_values() == 3 && listener->seen_value(1) &&
- listener->seen_value(2) && listener->seen_value(3)) {
+ // Ensure that the listener was only triggered 4 times, with the values
+ // 0 (the initial value), 1, 2, and 3.
+ if (listener->num_seen_values() == 4 && listener->seen_value(0) &&
+ listener->seen_value(1) && listener->seen_value(2) &&
+ listener->seen_value(3)) {
LogMessage("SUCCESS: ValueListener got all values.");
} else {
LogMessage("ERROR: ValueListener did not get all values.");
@@ -625,9 +722,8 @@ extern "C" int common_main(int argc, const char* argv[]) {
delete listener;
}
-
// Test a ChildListener, which sits on a Query and listens for changes in
- // the child heirarchy at the location.
+ // the child hierarchy at the location.
{
LogMessage("TEST: ChildListener");
SampleChildListener* listener = new SampleChildListener();
@@ -639,6 +735,11 @@ extern "C" int common_main(int argc, const char* argv[]) {
.EqualTo("enemy")
.AddChildListener(listener);
+ // The listener's OnChanged callback is triggered once when the listener is
+ // attached and again every time the data, including children, changes.
+ // Wait for here for a moment for the initial values to be received.
+ ProcessEvents(2000);
+
std::map params;
params["entity_name"] = "cobra";
params["entity_type"] = "enemy";
@@ -847,6 +948,7 @@ extern "C" int common_main(int argc, const char* argv[]) {
ref.Child("OnDisconnectTests").GetValue();
WaitForCompletion(future, "ReadOnDisconnectChanges");
bool failed = false;
+
if (future.error() == firebase::database::kErrorNone) {
const firebase::database::DataSnapshot& result = *future.result();
if (!result.HasChild("SetValueTo1") ||
@@ -901,10 +1003,39 @@ extern "C" int common_main(int argc, const char* argv[]) {
future.error(), future.error_message());
}
+ bool test_snapshot_was_valid = false;
+ firebase::database::DataSnapshot* test_snapshot = nullptr;
+ if (future.error() == firebase::database::kErrorNone) {
+ // This is a little convoluted as it's not possible to construct an
+ // empty test snapshot so we copy the result and point at the copy.
+ static firebase::database::DataSnapshot copied_snapshot = // NOLINT
+ *future.result(); // NOLINT
+ test_snapshot = &copied_snapshot;
+ test_snapshot_was_valid = test_snapshot->is_valid();
+ }
+
LogMessage("Shutdown the Database library.");
delete database;
database = nullptr;
+ // Ensure that the ref we had is now invalid.
+ if (!ref.is_valid()) {
+ LogMessage("SUCCESS: Reference was invalidated on library shutdown.");
+ } else {
+ LogMessage("ERROR: Reference is still valid after library shutdown.");
+ }
+
+ if (test_snapshot_was_valid && test_snapshot) {
+ if (!test_snapshot->is_valid()) {
+ LogMessage("SUCCESS: Snapshot was invalidated on library shutdown.");
+ } else {
+ LogMessage("ERROR: Snapshot is still valid after library shutdown.");
+ }
+ } else {
+ LogMessage(
+ "WARNING: Snapshot was already invalid at shutdown, couldn't check.");
+ }
+
LogMessage("Signing out from anonymous account.");
auth->SignOut();
LogMessage("Shutdown the Auth library.");
diff --git a/database/testapp/src/desktop/desktop_main.cc b/database/testapp/src/desktop/desktop_main.cc
index 99ace543..0220c688 100644
--- a/database/testapp/src/desktop/desktop_main.cc
+++ b/database/testapp/src/desktop/desktop_main.cc
@@ -15,16 +15,35 @@
#include
#include
#include
-#ifndef _WIN32
+
+#ifdef _WIN32
+#include
+#define chdir _chdir
+#else
#include
-#endif // !_WIN32
+#endif // _WIN32
#ifdef _WIN32
#include
#endif // _WIN32
+#include
+#include
+
#include "main.h" // NOLINT
+// The TO_STRING macro is useful for command line defined strings as the quotes
+// get stripped.
+#define TO_STRING_EXPAND(X) #X
+#define TO_STRING(X) TO_STRING_EXPAND(X)
+
+// Path to the Firebase config file to load.
+#ifdef FIREBASE_CONFIG
+#define FIREBASE_CONFIG_STRING TO_STRING(FIREBASE_CONFIG)
+#else
+#define FIREBASE_CONFIG_STRING ""
+#endif // FIREBASE_CONFIG
+
extern "C" int common_main(int argc, const char* argv[]);
static bool quit = false;
@@ -50,6 +69,10 @@ bool ProcessEvents(int msec) {
return quit;
}
+std::string PathForResource() {
+ return std::string();
+}
+
void LogMessage(const char* format, ...) {
va_list list;
va_start(list, format);
@@ -61,7 +84,22 @@ void LogMessage(const char* format, ...) {
WindowContext GetWindowContext() { return nullptr; }
+// Change the current working directory to the directory containing the
+// specified file.
+void ChangeToFileDirectory(const char* file_path) {
+ std::string path(file_path);
+ std::replace(path.begin(), path.end(), '\\', '/');
+ auto slash = path.rfind('/');
+ if (slash != std::string::npos) {
+ std::string directory = path.substr(0, slash);
+ if (!directory.empty()) chdir(directory.c_str());
+ }
+}
+
int main(int argc, const char* argv[]) {
+ ChangeToFileDirectory(
+ FIREBASE_CONFIG_STRING[0] != '\0' ?
+ FIREBASE_CONFIG_STRING : argv[0]); // NOLINT
#ifdef _WIN32
SetConsoleCtrlHandler((PHANDLER_ROUTINE)SignalHandler, TRUE);
#else
@@ -69,3 +107,19 @@ int main(int argc, const char* argv[]) {
#endif // _WIN32
return common_main(argc, argv);
}
+
+#if defined(_WIN32)
+// Returns the number of microseconds since the epoch.
+int64_t WinGetCurrentTimeInMicroseconds() {
+ FILETIME file_time;
+ GetSystemTimeAsFileTime(&file_time);
+
+ ULARGE_INTEGER now;
+ now.LowPart = file_time.dwLowDateTime;
+ now.HighPart = file_time.dwHighDateTime;
+
+ // Windows file time is expressed in 100s of nanoseconds.
+ // To convert to microseconds, multiply x10.
+ return now.QuadPart * 10LL;
+}
+#endif
diff --git a/database/testapp/testapp.xcodeproj/project.pbxproj b/database/testapp/testapp.xcodeproj/project.pbxproj
index baf45c4c..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 = 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/database/testapp/testapp/Info.plist b/database/testapp/testapp/Info.plist
index 12137455..095e6672 100644
--- a/database/testapp/testapp/Info.plist
+++ b/database/testapp/testapp/Info.plist
@@ -16,8 +16,6 @@
APPL
CFBundleShortVersionString
1.0
- CFBundleSignature
- ????
CFBundleURLTypes
@@ -27,7 +25,7 @@
google
CFBundleURLSchemes
- com.googleusercontent.apps.255980362477-3a1nf8c4nl0c7hlnlnmc98hbtg2mnbue
+ YOUR_REVERSED_CLIENT_ID
diff --git a/firestore/.clang-format b/firestore/.clang-format
new file mode 100644
index 00000000..e2412319
--- /dev/null
+++ b/firestore/.clang-format
@@ -0,0 +1,9 @@
+BasedOnStyle: Google
+Standard: Cpp11
+ColumnLimit: 80
+BinPackParameters: false
+AllowAllParametersOfDeclarationOnNextLine: true
+SpacesInContainerLiterals: true
+DerivePointerAlignment: false
+PointerAlignment: Left
+IncludeBlocks: Preserve
diff --git a/firestore/testapp/AndroidManifest.xml b/firestore/testapp/AndroidManifest.xml
new file mode 100644
index 00000000..b4f84579
--- /dev/null
+++ b/firestore/testapp/AndroidManifest.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/firestore/testapp/CMakeLists.txt b/firestore/testapp/CMakeLists.txt
new file mode 100644
index 00000000..1a2c953f
--- /dev/null
+++ b/firestore/testapp/CMakeLists.txt
@@ -0,0 +1,136 @@
+cmake_minimum_required(VERSION 2.8)
+
+# User settings for Firebase samples.
+# Path to Firebase SDK.
+# Try to read the path to the Firebase C++ SDK from an environment variable.
+if (NOT "$ENV{FIREBASE_CPP_SDK_DIR}" STREQUAL "")
+ set(DEFAULT_FIREBASE_CPP_SDK_DIR "$ENV{FIREBASE_CPP_SDK_DIR}")
+else()
+ set(DEFAULT_FIREBASE_CPP_SDK_DIR "firebase_cpp_sdk")
+endif()
+if ("${FIREBASE_CPP_SDK_DIR}" STREQUAL "")
+ set(FIREBASE_CPP_SDK_DIR ${DEFAULT_FIREBASE_CPP_SDK_DIR})
+endif()
+if(NOT EXISTS ${FIREBASE_CPP_SDK_DIR})
+ message(FATAL_ERROR "The Firebase C++ SDK directory does not exist: ${FIREBASE_CPP_SDK_DIR}. See the readme.md for more information")
+endif()
+
+# Windows runtime mode, either MD or MT depending on whether you are using
+# /MD or /MT. For more information see:
+# https://msdn.microsoft.com/en-us/library/2kzt1wy3.aspx
+set(MSVC_RUNTIME_MODE MD)
+
+project(firebase_testapp)
+
+# Sample source files.
+set(FIREBASE_SAMPLE_COMMON_SRCS
+ src/main.h
+ src/common_main.cc
+)
+
+# The include directory for the testapp.
+include_directories(src)
+
+# Sample uses some features that require C++ 11, such as lambdas.
+set (CMAKE_CXX_STANDARD 11)
+
+if(ANDROID)
+ # Build an Android application.
+
+ # Source files used for the Android build.
+ set(FIREBASE_SAMPLE_ANDROID_SRCS
+ src/android/android_main.cc
+ )
+
+ # Build native_app_glue as a static lib
+ add_library(native_app_glue STATIC
+ ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c)
+
+ # Export ANativeActivity_onCreate(),
+ # Refer to: https://github.com/android-ndk/ndk/issues/381.
+ # This also does a workaround that prevents numerous errors that occur when
+ # building using Android Studio. The errors look like:
+ # libfirebase_auth.a(auth.o): relocation R_386_GOTOFF against preemptible
+ # symbol cannot be used when making a shared object.
+ # Taken from:
+ # https://github.com/opencv/opencv/issues/10229#issuecomment-359202825
+ set(CMAKE_SHARED_LINKER_FLAGS
+ "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate \
+ -Wl,--exclude-libs,libfirebase_firestore.a \
+ -Wl,--exclude-libs,libfirebase_auth.a \
+ -Wl,--exclude-libs,libfirebase_app.a"
+ )
+
+ # Define the target as a shared library, as that is what gradle expects.
+ set(target_name "android_main")
+ add_library(${target_name} SHARED
+ ${FIREBASE_SAMPLE_ANDROID_SRCS}
+ ${FIREBASE_SAMPLE_COMMON_SRCS}
+ )
+
+ target_link_libraries(${target_name}
+ log android atomic native_app_glue
+ )
+
+ target_include_directories(${target_name} PRIVATE
+ ${ANDROID_NDK}/sources/android/native_app_glue)
+
+ set(ADDITIONAL_LIBS)
+else()
+ # Build a desktop application.
+
+ # Windows runtime mode, either MD or MT depending on whether you are using
+ # /MD or /MT. For more information see:
+ # https://msdn.microsoft.com/en-us/library/2kzt1wy3.aspx
+ set(MSVC_RUNTIME_MODE MD)
+
+ # Platform abstraction layer for the desktop sample.
+ set(FIREBASE_SAMPLE_DESKTOP_SRCS
+ src/desktop/desktop_main.cc
+ )
+
+ set(target_name "desktop_testapp")
+ add_executable(${target_name}
+ ${FIREBASE_SAMPLE_DESKTOP_SRCS}
+ ${FIREBASE_SAMPLE_COMMON_SRCS}
+ )
+
+ if(APPLE)
+ set(ADDITIONAL_LIBS
+ gssapi_krb5
+ pthread
+ "-framework CoreFoundation"
+ "-framework Foundation"
+ "-framework GSS"
+ "-framework Security"
+ "-framework SystemConfiguration"
+ )
+ elseif(MSVC)
+ set(ADDITIONAL_LIBS advapi32 ws2_32 crypt32 iphlpapi psapi userenv dbghelp bcrypt)
+ else()
+ set(ADDITIONAL_LIBS pthread)
+ endif()
+
+ # If a config file is present, copy it into the binary location so that it's
+ # possible to create the default Firebase app.
+ set(FOUND_JSON_FILE FALSE)
+ foreach(config "google-services-desktop.json" "google-services.json")
+ if (EXISTS ${config})
+ add_custom_command(
+ TARGET ${target_name} POST_BUILD
+ COMMAND ${CMAKE_COMMAND} -E copy
+ ${config} $)
+ set(FOUND_JSON_FILE TRUE)
+ break()
+ endif()
+ endforeach()
+ if(NOT FOUND_JSON_FILE)
+ message(WARNING "Failed to find either google-services-desktop.json or google-services.json. See the readme.md for more information.")
+ endif()
+endif()
+
+# Add the Firebase libraries to the target using the function from the SDK.
+add_subdirectory(${FIREBASE_CPP_SDK_DIR} bin/ EXCLUDE_FROM_ALL)
+# Note that firebase_app needs to be last in the list.
+set(firebase_libs firebase_firestore firebase_auth firebase_app)
+target_link_libraries(${target_name} "${firebase_libs}" ${ADDITIONAL_LIBS})
diff --git a/admob/testapp/LaunchScreen.storyboard b/firestore/testapp/LaunchScreen.storyboard
similarity index 100%
rename from admob/testapp/LaunchScreen.storyboard
rename to firestore/testapp/LaunchScreen.storyboard
diff --git a/firestore/testapp/Podfile b/firestore/testapp/Podfile
new file mode 100644
index 00000000..e0bca1be
--- /dev/null
+++ b/firestore/testapp/Podfile
@@ -0,0 +1,8 @@
+source 'https://github.com/CocoaPods/Specs.git'
+platform :ios, '13.0'
+use_frameworks!
+# Firebase Firestore test application.
+target 'testapp' do
+ pod 'Firebase/Firestore', '10.25.0'
+ pod 'Firebase/Auth', '10.25.0'
+end
diff --git a/firestore/testapp/README.md b/firestore/testapp/README.md
new file mode 100644
index 00000000..500c24c6
--- /dev/null
+++ b/firestore/testapp/README.md
@@ -0,0 +1,268 @@
+# Firebase Firestore Quickstart
+
+The Firebase Firestore Test Application (testapp) demonstrates Firebase
+Firestore operations with the Firebase Firestore C++ SDK. The application has no
+user interface and simply logs actions it's performing to the console.
+
+The testapp performs the following:
+
+- Creates a firebase::App in a platform-specific way. The App holds
+ platform-specific context that's used by other Firebase APIs, and is a
+ central point for communication between the Firebase Firestore C++ and
+ Firebase Auth C++ libraries.
+- Gets a pointer to firebase::Auth, and signs in anonymously. This allows the
+ testapp to access a Firebase Firestore instance with authentication rules
+ enabled.
+- Initializes a Firestore instance and sets its logging level to
+ `kLogLevelDebug` in order to see debug messages in the logs.
+- Tests that it can create `Timestamp`, `SnapshotMetadata`, and `GeoPoint`
+ objects.
+- Creates a collection and a document inside that collection.
+- Writes initial data to the document (`Set`), updates the document content
+ (`Update`), reads the document back (`Get`), and checks that the contents
+ match our expectation.
+- Deletes the document.
+- Performs a batch write to two documents.
+- Performs a Transaction containing three operations (`Update`, `Delete`, and
+ `Set`) on three different documents.
+- Queries documents in the collection that match a certain condition. Ensures
+ the documents returned via the query match our expectation.
+
+Introduction
+------------
+
+- [Read more about Firebase Firestore](https://firebase.google.com/docs/firestore/)
+
+Building and Running the testapp
+--------------------------------
+
+### iOS
+
+- Link your iOS app to the Firebase libraries.
+
+ - Get CocoaPods version 1 or later by running,
+
+ ```
+ sudo gem install cocoapods --pre
+ ```
+
+ - Update the pod versions in the Podfile to match the C++ SDK version that you are using.
+ For the latest version of the C++ SDK, you can find the associated pod versions on the
+ [`Add Firebase to your project` page](https://firebase.google.com/docs/cpp/setup?platform=ios#libraries-ios).
+ For instance: if you're using SDK version 8.2.0, use the following in the Podfile
+ (but note that pod versions may not always match the C++ SDK version):
+
+ ```
+ pod 'Firebase/Firestore', '8.2.0'
+ pod 'Firebase/Auth', '8.2.0'
+ ```
+
+ - From the testapp directory, install the CocoaPods listed in the Podfile
+ by running,
+
+ ```
+ pod install
+ ```
+
+ - Open the generated Xcode workspace (which now has the CocoaPods),
+
+ ```
+ open testapp.xcworkspace
+ ```
+
+ - For further details please refer to the
+ [general instructions for setting up an iOS app with Firebase](https://firebase.google.com/docs/ios/setup).
+
+- Register your iOS app with Firebase.
+
+ - Create a new app on the
+ [Firebase console](https://firebase.google.com/console/), and attach
+ your iOS app to it.
+ - You can use "com.google.firebase.cpp.firestore.testapp" as the iOS
+ Bundle ID while you're testing. You can omit App Store ID while
+ testing.
+ - Add the GoogleService-Info.plist that you downloaded from Firebase
+ console to the testapp root directory. This file identifies your iOS app
+ to the Firebase backend.
+ - In the Firebase console for your app, select "Auth", then enable
+ "Anonymous". This will allow the testapp to use anonymous sign-in to
+ authenticate with Firebase Firestore, which requires a signed-in user by
+ default (an anonymous user will suffice).
+
+- Download the Firebase C++ SDK linked from
+ [https://firebase.google.com/docs/cpp/setup](https://firebase.google.com/docs/cpp/setup)
+ and unzip it to a directory of your choice.
+
+- Add the following frameworks from the Firebase C++ SDK to the project:
+
+ - xcframeworks/firebase.xcframework/ios-arm64_armv7/firebase.framework
+ - xcframeworks/firebase_firestore.xcframework/ios-arm64_armv7/firebase_firestore.framework
+ - xcframeworks/firebase_auth.xcframework/ios-arm64_armv7/firebase_auth.framework
+ - You will need to either,
+ 1. Check "Copy items if needed" when adding the frameworks, or
+ 2. Add the framework path in "Framework Search Paths"
+ - e.g. If you downloaded the Firebase C++ SDK to
+ `/Users/me/firebase_cpp_sdk`, then you would add the path
+ `/Users/me/firebase_cpp_sdk/xcframeworks/**`.
+ - To add the path, in XCode, select your project in the project
+ navigator, then select your target in the main window. Select
+ the "Build Settings" tab, and click "All" to see all the build
+ settings. Scroll down to "Search Paths", and add your path to
+ "Framework Search Paths".
+
+- In XCode, build & run the sample on an iOS device or simulator.
+
+- The testapp has no interative interface. The output of the app can be viewed
+ via the console or on the device's display. In Xcode, select "View --> Debug
+ Area --> Activate Console" from the menu to view the console.
+
+### Android
+
+- Register your Android app with Firebase.
+
+ - Create a new app on the
+ [Firebase console](https://firebase.google.com/console/), and attach
+ your Android app to it.
+
+ - You can use "com.google.firebase.cpp.firestore.testapp" as the
+ Package Name while you're testing.
+ - To
+ [generate a SHA1](https://developers.google.com/android/guides/client-auth)
+ run this command on Mac and Linux,
+
+ ```
+ keytool -exportcert -list -v -alias androiddebugkey -keystore ~/.android/debug.keystore
+ ```
+
+ or this command on Windows,
+
+ ```
+ keytool -exportcert -list -v -alias androiddebugkey -keystore %USERPROFILE%\.android\debug.keystore
+ ```
+
+ - If keytool reports that you do not have a debug.keystore, you can
+ [create one with](http://developer.android.com/tools/publishing/app-signing.html#signing-manually),
+
+ ```
+ keytool -genkey -v -keystore ~/.android/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"
+ ```
+
+ - Add the `google-services.json` file that you downloaded from Firebase
+ console to the root directory of testapp. This file identifies your
+ Android app to the Firebase backend.
+
+ - In the Firebase console for your app, select "Auth", then enable
+ "Anonymous". This will allow the testapp to use anonymous sign-in to
+ authenticate with Firebase Firestore, which requires a signed-in user by
+ default (an anonymous user will suffice).
+
+ - For further details please refer to the
+ [general instructions for setting up an Android app with Firebase](https://firebase.google.com/docs/android/setup).
+
+- Download the Firebase C++ SDK linked from
+ [https://firebase.google.com/docs/cpp/setup](https://firebase.google.com/docs/cpp/setup)
+ and unzip it to a directory of your choice.
+
+- Configure the location of the Firebase C++ SDK by setting the
+ firebase\_cpp\_sdk.dir Gradle property to the SDK install directory. For
+ example, in the project directory:
+
+ ```
+ echo "systemProp.firebase\_cpp\_sdk.dir=/User/$USER/firebase\_cpp\_sdk" >> gradle.properties
+ ```
+
+- Ensure the Android SDK and NDK locations are set in Android Studio.
+
+ - From the Android Studio launch menu, go to `File/Project Structure...`
+ or `Configure/Project Defaults/Project Structure...` (Shortcut:
+ Control + Alt + Shift + S on windows, Command + ";" on a mac) and
+ download the SDK and NDK if the locations are not yet set.
+
+- Open *build.gradle* in Android Studio.
+
+ - From the Android Studio launch menu, "Open an existing Android Studio
+ project", and select `build.gradle`.
+
+- Install the SDK Platforms that Android Studio reports missing.
+
+- Build the testapp and run it on an Android device or emulator.
+
+- The testapp has no interactive interface. The output of the app can be
+ viewed on the device's display, or in the logcat output of Android studio or
+ by running "adb logcat *:W android_main firebase" from the command line.
+
+### Desktop
+
+- Register your app with Firebase.
+ - Create a new app on the
+ [Firebase console](https://firebase.google.com/console/), following the
+ above instructions for Android or iOS.
+ - If you have an Android project, add the `google-services.json` file that
+ you downloaded from the Firebase console to the root directory of the
+ testapp.
+ - If you have an iOS project, and don't wish to use an Android project,
+ you can use the Python script `generate_xml_from_google_services_json.py
+ --plist`, located in the Firebase C++ SDK, to convert your
+ `GoogleService-Info.plist` file into a `google-services-desktop.json`
+ file, which can then be placed in the root directory of the testapp.
+- Download the Firebase C++ SDK linked from
+ [https://firebase.google.com/docs/cpp/setup](https://firebase.google.com/docs/cpp/setup)
+ and unzip it to a directory of your choice.
+- Configure the testapp with the location of the Firebase C++ SDK. This can be
+ done a couple different ways (in highest to lowest priority):
+ - When invoking cmake, pass in the location with
+ -DFIREBASE_CPP_SDK_DIR=/path/to/firebase_cpp_sdk.
+ - Set an environment variable for FIREBASE_CPP_SDK_DIR to the path to use.
+ - Edit the CMakeLists.txt file, changing the FIREBASE_CPP_SDK_DIR path to
+ the appropriate location.
+- From the testapp directory, generate the build files by running,
+
+ ```
+ cmake .
+ ```
+
+ If you want to use XCode, you can use -G"Xcode" to generate the project.
+ Similarly, to use Visual Studio, -G"Visual Studio 15 2017". For more
+ information, see
+ [CMake generators](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html).
+
+- Build the testapp, by either opening the generated project file based on the
+ platform, or running,
+
+ ```
+ cmake --build .
+ ```
+
+- Execute the testapp by running,
+
+ ```
+ ./desktop_testapp
+ ```
+
+ Note that the executable might be under another directory, such as Debug.
+
+- The testapp has no user interface, but the output can be viewed via the
+ console.
+
+Support
+-------
+
+[https://firebase.google.com/support/](https://firebase.google.com/support/)
+
+License
+-------
+
+Copyright 2016-2019 Google LLC Licensed to the Apache Software Foundation (ASF)
+under one or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information regarding copyright
+ownership. The ASF licenses this file to you under the Apache License, Version
+2.0 (the "License"); you may not use this file except in compliance with the
+License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+License for the specific language governing permissions and limitations under
+the License.
diff --git a/firestore/testapp/build.gradle b/firestore/testapp/build.gradle
new file mode 100644
index 00000000..6adb3b5a
--- /dev/null
+++ b/firestore/testapp/build.gradle
@@ -0,0 +1,78 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+buildscript {
+ repositories {
+ mavenLocal()
+ maven { url 'https://maven.google.com' }
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:4.2.1'
+ classpath 'com.google.gms:google-services:4.0.1'
+ }
+}
+
+allprojects {
+ repositories {
+ mavenLocal()
+ maven { url 'https://maven.google.com' }
+ jcenter()
+ }
+}
+
+apply plugin: 'com.android.application'
+
+android {
+ compileOptions {
+ sourceCompatibility 1.8
+ targetCompatibility 1.8
+ }
+ compileSdkVersion 34
+ ndkPath System.getenv('ANDROID_NDK_HOME')
+ buildToolsVersion '30.0.2'
+
+ sourceSets {
+ main {
+ jniLibs.srcDirs = ['libs']
+ manifest.srcFile 'AndroidManifest.xml'
+ java.srcDirs = ['src/android/java']
+ res.srcDirs = ['res']
+ }
+ }
+
+ defaultConfig {
+ applicationId 'com.google.firebase.cpp.firestore.testapp'
+ minSdkVersion 23
+ targetSdkVersion 28
+ versionCode 1
+ versionName '1.0'
+ externalNativeBuild.cmake {
+ arguments "-DFIREBASE_CPP_SDK_DIR=$gradle.firebase_cpp_sdk_dir"
+ }
+ multiDexEnabled true
+ }
+ externalNativeBuild.cmake {
+ path 'CMakeLists.txt'
+ }
+ buildTypes {
+ release {
+ minifyEnabled true
+ proguardFile getDefaultProguardFile('proguard-android.txt')
+ proguardFile file('proguard.pro')
+ }
+ }
+ packagingOptions {
+ pickFirst 'META-INF/**/coroutines.pro'
+ }
+ lintOptions {
+ abortOnError false
+ checkReleaseBuilds false
+ }
+}
+
+apply from: "$gradle.firebase_cpp_sdk_dir/Android/firebase_dependencies.gradle"
+firebaseCpp.dependencies {
+ auth
+ firestore
+}
+
+apply plugin: 'com.google.gms.google-services'
diff --git a/firestore/testapp/gradle.properties b/firestore/testapp/gradle.properties
new file mode 100644
index 00000000..d7ba8f42
--- /dev/null
+++ b/firestore/testapp/gradle.properties
@@ -0,0 +1 @@
+android.useAndroidX = true
diff --git a/admob/testapp/gradle/wrapper/gradle-wrapper.jar b/firestore/testapp/gradle/wrapper/gradle-wrapper.jar
similarity index 100%
rename from admob/testapp/gradle/wrapper/gradle-wrapper.jar
rename to firestore/testapp/gradle/wrapper/gradle-wrapper.jar
diff --git a/invites/testapp/gradle/wrapper/gradle-wrapper.properties b/firestore/testapp/gradle/wrapper/gradle-wrapper.properties
similarity index 52%
rename from invites/testapp/gradle/wrapper/gradle-wrapper.properties
rename to firestore/testapp/gradle/wrapper/gradle-wrapper.properties
index d5705170..65340c1b 100644
--- a/invites/testapp/gradle/wrapper/gradle-wrapper.properties
+++ b/firestore/testapp/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Wed Apr 10 15:27:10 PDT 2013
+#Mon Nov 27 14:03:45 PST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
+distributionUrl=https://services.gradle.org/distributions/gradle-6.7.1-all.zip
diff --git a/admob/testapp/gradlew b/firestore/testapp/gradlew
similarity index 100%
rename from admob/testapp/gradlew
rename to firestore/testapp/gradlew
diff --git a/admob/testapp/gradlew.bat b/firestore/testapp/gradlew.bat
similarity index 100%
rename from admob/testapp/gradlew.bat
rename to firestore/testapp/gradlew.bat
diff --git a/invites/testapp/proguard.pro b/firestore/testapp/proguard.pro
similarity index 85%
rename from invites/testapp/proguard.pro
rename to firestore/testapp/proguard.pro
index c7e9278d..54cd248b 100644
--- a/invites/testapp/proguard.pro
+++ b/firestore/testapp/proguard.pro
@@ -1 +1,2 @@
+-ignorewarnings
-keep,includedescriptorclasses public class com.google.firebase.example.LoggingUtils { *; }
diff --git a/invites/testapp/res/layout/main.xml b/firestore/testapp/res/layout/main.xml
similarity index 100%
rename from invites/testapp/res/layout/main.xml
rename to firestore/testapp/res/layout/main.xml
diff --git a/invites/testapp/res/values/strings.xml b/firestore/testapp/res/values/strings.xml
similarity index 52%
rename from invites/testapp/res/values/strings.xml
rename to firestore/testapp/res/values/strings.xml
index 2e7bcd10..25c93dde 100644
--- a/invites/testapp/res/values/strings.xml
+++ b/firestore/testapp/res/values/strings.xml
@@ -1,4 +1,4 @@
- Firebase Invites Test
+ Firebase Firestore Test
diff --git a/firestore/testapp/settings.gradle b/firestore/testapp/settings.gradle
new file mode 100644
index 00000000..5b0e2c37
--- /dev/null
+++ b/firestore/testapp/settings.gradle
@@ -0,0 +1,36 @@
+// Copyright 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+def firebase_cpp_sdk_dir = System.getProperty('firebase_cpp_sdk.dir')
+if (firebase_cpp_sdk_dir == null || firebase_cpp_sdk_dir.isEmpty()) {
+ firebase_cpp_sdk_dir = System.getenv('FIREBASE_CPP_SDK_DIR')
+ if (firebase_cpp_sdk_dir == null || firebase_cpp_sdk_dir.isEmpty()) {
+ if ((new File('firebase_cpp_sdk')).exists()) {
+ firebase_cpp_sdk_dir = 'firebase_cpp_sdk'
+ } else {
+ throw new StopActionException(
+ 'firebase_cpp_sdk.dir property or the FIREBASE_CPP_SDK_DIR ' +
+ 'environment variable must be set to reference the Firebase C++ ' +
+ 'SDK install directory. This is used to configure static library ' +
+ 'and C/C++ include paths for the SDK.')
+ }
+ }
+}
+if (!(new File(firebase_cpp_sdk_dir)).exists()) {
+ throw new StopActionException(
+ sprintf('Firebase C++ SDK directory %s does not exist',
+ firebase_cpp_sdk_dir))
+}
+gradle.ext.firebase_cpp_sdk_dir = "$firebase_cpp_sdk_dir"
+includeBuild "$firebase_cpp_sdk_dir"
\ No newline at end of file
diff --git a/admob/testapp/src/android/android_main.cc b/firestore/testapp/src/android/android_main.cc
similarity index 100%
rename from admob/testapp/src/android/android_main.cc
rename to firestore/testapp/src/android/android_main.cc
diff --git a/admob/testapp/src/android/java/com/google/firebase/example/LoggingUtils.java b/firestore/testapp/src/android/java/com/google/firebase/example/LoggingUtils.java
similarity index 100%
rename from admob/testapp/src/android/java/com/google/firebase/example/LoggingUtils.java
rename to firestore/testapp/src/android/java/com/google/firebase/example/LoggingUtils.java
diff --git a/firestore/testapp/src/common_main.cc b/firestore/testapp/src/common_main.cc
new file mode 100644
index 00000000..0de5d39b
--- /dev/null
+++ b/firestore/testapp/src/common_main.cc
@@ -0,0 +1,341 @@
+// Copyright 2016 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "firebase/auth.h"
+#include "firebase/auth/user.h"
+#include "firebase/firestore.h"
+#include "firebase/util.h"
+
+// Thin OS abstraction layer.
+#include "main.h" // NOLINT
+
+const int kTimeoutMs = 5000;
+const int kSleepMs = 100;
+
+// Waits for a Future to be completed and returns whether the future has
+// completed successfully. If the Future returns an error, it will be logged.
+bool Await(const firebase::FutureBase& future, const char* name) {
+ int remaining_timeout = kTimeoutMs;
+ while (future.status() == firebase::kFutureStatusPending &&
+ remaining_timeout > 0) {
+ remaining_timeout -= kSleepMs;
+ ProcessEvents(kSleepMs);
+ }
+
+ if (future.status() != firebase::kFutureStatusComplete) {
+ LogMessage("ERROR: %s returned an invalid result.", name);
+ return false;
+ } else if (future.error() != 0) {
+ LogMessage("ERROR: %s returned error %d: %s", name, future.error(),
+ future.error_message());
+ return false;
+ }
+ return true;
+}
+
+class Countable {
+ public:
+ int event_count() const { return event_count_; }
+
+ protected:
+ int event_count_ = 0;
+};
+
+template
+class TestEventListener : public Countable {
+ public:
+ explicit TestEventListener(std::string name) : name_(std::move(name)) {}
+
+ void OnEvent(const T& value,
+ const firebase::firestore::Error error_code,
+ const std::string& error_message) {
+ event_count_++;
+ if (error_code != firebase::firestore::kErrorOk) {
+ LogMessage("ERROR: EventListener %s got %d (%s).", name_.c_str(),
+ error_code, error_message.c_str());
+ }
+ }
+
+ template
+ firebase::firestore::ListenerRegistration AttachTo(U* ref) {
+ return ref->AddSnapshotListener(
+ [this](const T& result, firebase::firestore::Error error_code,
+ const std::string& error_message) {
+ OnEvent(result, error_code, error_message);
+ });
+ }
+
+ private:
+ std::string name_;
+};
+
+void Await(const Countable& listener, const char* name) {
+ int remaining_timeout = kTimeoutMs;
+ while (listener.event_count() && remaining_timeout > 0) {
+ remaining_timeout -= kSleepMs;
+ ProcessEvents(kSleepMs);
+ }
+ if (remaining_timeout <= 0) {
+ LogMessage("ERROR: %s listener timed out.", name);
+ }
+}
+
+extern "C" int common_main(int argc, const char* argv[]) {
+ firebase::App* app;
+
+#if defined(__ANDROID__)
+ app = firebase::App::Create(GetJniEnv(), GetActivity());
+#else
+ app = firebase::App::Create();
+#endif // defined(__ANDROID__)
+
+ LogMessage("Initialized Firebase App.");
+
+ LogMessage("Initializing Firebase Auth...");
+ firebase::InitResult result;
+ firebase::auth::Auth* auth = firebase::auth::Auth::GetAuth(app, &result);
+ if (result != firebase::kInitResultSuccess) {
+ LogMessage("Failed to initialize Firebase Auth, error: %d",
+ static_cast(result));
+ return -1;
+ }
+ LogMessage("Initialized Firebase Auth.");
+
+ LogMessage("Signing in...");
+ // Auth caches the previously signed-in user, which can be annoying when
+ // trying to test for sign-in failures.
+ auth->SignOut();
+ auto login_future = auth->SignInAnonymously();
+ Await(login_future, "Auth sign-in");
+ auto* login_result = login_future.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());
+ } else {
+ LogMessage("ERROR: could not sign in");
+ }
+
+ // Note: Auth cannot be deleted while any of the futures issued by it are
+ // still valid.
+ login_future.Release();
+
+ LogMessage("Initialize Firebase Firestore.");
+
+ // Use ModuleInitializer to initialize Database, ensuring no dependencies are
+ // missing.
+ firebase::firestore::Firestore* firestore = nullptr;
+ void* initialize_targets[] = {&firestore};
+
+ const firebase::ModuleInitializer::InitializerFn initializers[] = {
+ [](firebase::App* app, void* data) {
+ LogMessage("Attempt to initialize Firebase Firestore.");
+ void** targets = reinterpret_cast(data);
+ firebase::InitResult result;
+ *reinterpret_cast(targets[0]) =
+ firebase::firestore::Firestore::GetInstance(app, &result);
+ return result;
+ }};
+
+ firebase::ModuleInitializer initializer;
+ initializer.Initialize(app, initialize_targets, initializers,
+ sizeof(initializers) / sizeof(initializers[0]));
+
+ Await(initializer.InitializeLastResult(), "Initialize");
+
+ if (initializer.InitializeLastResult().error() != 0) {
+ LogMessage("Failed to initialize Firebase libraries: %s",
+ initializer.InitializeLastResult().error_message());
+ return -1;
+ }
+ LogMessage("Successfully initialized Firebase Firestore.");
+
+ firestore->set_log_level(firebase::kLogLevelDebug);
+
+ if (firestore->app() != app) {
+ LogMessage("ERROR: failed to get App the Firestore was created with.");
+ }
+
+ firebase::firestore::Settings settings = firestore->settings();
+ firestore->set_settings(settings);
+ LogMessage("Successfully set Firestore settings.");
+
+ LogMessage("Testing non-wrapping types.");
+ const firebase::Timestamp timestamp{1, 2};
+ if (timestamp.seconds() != 1 || timestamp.nanoseconds() != 2) {
+ LogMessage("ERROR: Timestamp creation failed.");
+ }
+ const firebase::firestore::SnapshotMetadata metadata{
+ /*has_pending_writes*/ false, /*is_from_cache*/ true};
+ if (metadata.has_pending_writes() || !metadata.is_from_cache()) {
+ LogMessage("ERROR: SnapshotMetadata creation failed.");
+ }
+ const firebase::firestore::GeoPoint point{1.23, 4.56};
+ if (point.latitude() != 1.23 || point.longitude() != 4.56) {
+ LogMessage("ERROR: GeoPoint creation failed.");
+ }
+ LogMessage("Tested non-wrapping types.");
+
+ LogMessage("Testing collections.");
+ firebase::firestore::CollectionReference collection =
+ firestore->Collection("foo");
+ if (collection.id() != "foo") {
+ LogMessage("ERROR: failed to get collection id.");
+ }
+ if (collection.Document("bar").path() != "foo/bar") {
+ LogMessage("ERROR: failed to get path of a nested document.");
+ }
+ LogMessage("Tested collections.");
+
+ LogMessage("Testing documents.");
+ firebase::firestore::DocumentReference document =
+ firestore->Document("foo/bar");
+ if (document.firestore() != firestore) {
+ LogMessage("ERROR: failed to get Firestore from document.");
+ }
+
+ if (document.path() != "foo/bar") {
+ LogMessage("ERROR: failed to get path string from document.");
+ }
+
+ LogMessage("Testing Set().");
+ Await(document.Set(firebase::firestore::MapFieldValue{
+ {"str", firebase::firestore::FieldValue::String("foo")},
+ {"int", firebase::firestore::FieldValue::Integer(123)}}),
+ "document.Set");
+
+ LogMessage("Testing Update().");
+ Await(document.Update(firebase::firestore::MapFieldValue{
+ {"int", firebase::firestore::FieldValue::Integer(321)}}),
+ "document.Update");
+
+ LogMessage("Testing Get().");
+ auto doc_future = document.Get();
+ if (Await(doc_future, "document.Get")) {
+ const firebase::firestore::DocumentSnapshot* snapshot = doc_future.result();
+ if (snapshot == nullptr) {
+ LogMessage("ERROR: failed to read document.");
+ } else {
+ for (const auto& kv : snapshot->GetData()) {
+ if (kv.second.type() ==
+ firebase::firestore::FieldValue::Type::kString) {
+ LogMessage("key is %s, value is %s", kv.first.c_str(),
+ kv.second.string_value().c_str());
+ } else if (kv.second.type() ==
+ firebase::firestore::FieldValue::Type::kInteger) {
+ LogMessage("key is %s, value is %ld", kv.first.c_str(),
+ kv.second.integer_value());
+ } else {
+ // Log unexpected type for debugging.
+ LogMessage("key is %s, value is neither string nor integer",
+ kv.first.c_str());
+ }
+ }
+ }
+ }
+
+ LogMessage("Testing Delete().");
+ Await(document.Delete(), "document.Delete");
+ LogMessage("Tested document operations.");
+
+ TestEventListener
+ document_event_listener{"for document"};
+ firebase::firestore::ListenerRegistration registration =
+ document_event_listener.AttachTo(&document);
+ Await(document_event_listener, "document.AddSnapshotListener");
+ registration.Remove();
+ LogMessage("Successfully added and removed document snapshot listener.");
+
+ LogMessage("Testing batch write.");
+ firebase::firestore::WriteBatch batch = firestore->batch();
+ batch.Set(collection.Document("one"),
+ firebase::firestore::MapFieldValue{
+ {"str", firebase::firestore::FieldValue::String("foo")}});
+ batch.Set(collection.Document("two"),
+ firebase::firestore::MapFieldValue{
+ {"int", firebase::firestore::FieldValue::Integer(123)}});
+ Await(batch.Commit(), "batch.Commit");
+ LogMessage("Tested batch write.");
+
+ LogMessage("Testing transaction.");
+ Await(firestore->RunTransaction(
+ [collection](firebase::firestore::Transaction& transaction,
+ std::string&) -> firebase::firestore::Error {
+ transaction.Update(
+ collection.Document("one"),
+ firebase::firestore::MapFieldValue{
+ {"int", firebase::firestore::FieldValue::Integer(123)}});
+ transaction.Delete(collection.Document("two"));
+ transaction.Set(
+ collection.Document("three"),
+ firebase::firestore::MapFieldValue{
+ {"int", firebase::firestore::FieldValue::Integer(321)}});
+ return firebase::firestore::kErrorOk;
+ }),
+ "firestore.RunTransaction");
+ LogMessage("Tested transaction.");
+
+ LogMessage("Testing query.");
+ firebase::firestore::Query query =
+ collection
+ .WhereGreaterThan("int",
+ firebase::firestore::FieldValue::Boolean(true))
+ .Limit(3);
+ auto query_future = query.Get();
+ if (Await(query_future, "query.Get")) {
+ const firebase::firestore::QuerySnapshot* snapshot = query_future.result();
+ if (snapshot == nullptr) {
+ LogMessage("ERROR: failed to fetch query result.");
+ } else {
+ for (const auto& doc : snapshot->documents()) {
+ if (doc.id() == "one" || doc.id() == "three") {
+ LogMessage("doc %s is %ld", doc.id().c_str(),
+ doc.Get("int").integer_value());
+ } else {
+ LogMessage("ERROR: unexpected document %s.", doc.id().c_str());
+ }
+ }
+ }
+ } else {
+ LogMessage("ERROR: failed to fetch query result.");
+ }
+ LogMessage("Tested query.");
+
+ LogMessage("Shutdown the Firestore library.");
+ delete firestore;
+ firestore = nullptr;
+
+ LogMessage("Shutdown Auth.");
+ delete auth;
+ LogMessage("Shutdown Firebase App.");
+ delete app;
+
+ // Log this as the last line to ensure all test cases above goes through.
+ // The test harness will check this line appears.
+ LogMessage("Tests PASS.");
+
+ // Wait until the user wants to quit the app.
+ while (!ProcessEvents(1000)) {
+ }
+
+ return 0;
+}
diff --git a/admob/testapp/src/desktop/desktop_main.cc b/firestore/testapp/src/desktop/desktop_main.cc
similarity index 52%
rename from admob/testapp/src/desktop/desktop_main.cc
rename to firestore/testapp/src/desktop/desktop_main.cc
index 99ace543..3a027f82 100644
--- a/admob/testapp/src/desktop/desktop_main.cc
+++ b/firestore/testapp/src/desktop/desktop_main.cc
@@ -15,16 +15,35 @@
#include
#include
#include
-#ifndef _WIN32
+
+#ifdef _WIN32
+#include
+#define chdir _chdir
+#else
#include
-#endif // !_WIN32
+#endif // _WIN32
#ifdef _WIN32
#include
#endif // _WIN32
+#include
+#include
+
#include "main.h" // NOLINT
+// The TO_STRING macro is useful for command line defined strings as the quotes
+// get stripped.
+#define TO_STRING_EXPAND(X) #X
+#define TO_STRING(X) TO_STRING_EXPAND(X)
+
+// Path to the Firebase config file to load.
+#ifdef FIREBASE_CONFIG
+#define FIREBASE_CONFIG_STRING TO_STRING(FIREBASE_CONFIG)
+#else
+#define FIREBASE_CONFIG_STRING ""
+#endif // FIREBASE_CONFIG
+
extern "C" int common_main(int argc, const char* argv[]);
static bool quit = false;
@@ -50,6 +69,8 @@ bool ProcessEvents(int msec) {
return quit;
}
+std::string PathForResource() { return std::string(); }
+
void LogMessage(const char* format, ...) {
va_list list;
va_start(list, format);
@@ -61,7 +82,22 @@ void LogMessage(const char* format, ...) {
WindowContext GetWindowContext() { return nullptr; }
+// Change the current working directory to the directory containing the
+// specified file.
+void ChangeToFileDirectory(const char* file_path) {
+ std::string path(file_path);
+ std::replace(path.begin(), path.end(), '\\', '/');
+ auto slash = path.rfind('/');
+ if (slash != std::string::npos) {
+ std::string directory = path.substr(0, slash);
+ if (!directory.empty()) chdir(directory.c_str());
+ }
+}
+
int main(int argc, const char* argv[]) {
+ ChangeToFileDirectory(FIREBASE_CONFIG_STRING[0] != '\0'
+ ? FIREBASE_CONFIG_STRING
+ : argv[0]); // NOLINT
#ifdef _WIN32
SetConsoleCtrlHandler((PHANDLER_ROUTINE)SignalHandler, TRUE);
#else
@@ -69,3 +105,19 @@ int main(int argc, const char* argv[]) {
#endif // _WIN32
return common_main(argc, argv);
}
+
+#if defined(_WIN32)
+// Returns the number of microseconds since the epoch.
+int64_t WinGetCurrentTimeInMicroseconds() {
+ FILETIME file_time;
+ GetSystemTimeAsFileTime(&file_time);
+
+ ULARGE_INTEGER now;
+ now.LowPart = file_time.dwLowDateTime;
+ now.HighPart = file_time.dwHighDateTime;
+
+ // Windows file time is expressed in 100s of nanoseconds.
+ // To convert to microseconds, multiply x10.
+ return now.QuadPart * 10;
+}
+#endif
diff --git a/invites/testapp/src/ios/ios_main.mm b/firestore/testapp/src/ios/ios_main.mm
old mode 100644
new mode 100755
similarity index 87%
rename from invites/testapp/src/ios/ios_main.mm
rename to firestore/testapp/src/ios/ios_main.mm
index 6ccb2de5..567f7b0e
--- a/invites/testapp/src/ios/ios_main.mm
+++ b/firestore/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;
@@ -58,12 +58,10 @@ bool ProcessEvents(int msec) {
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);
@@ -79,7 +77,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 +86,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];
@@ -101,6 +99,7 @@ - (BOOL)application:(UIApplication*)application
g_text_view = [[UITextView alloc] initWithFrame:viewController.view.bounds];
+ g_text_view.accessibilityIdentifier = @"Logger";
g_text_view.editable = NO;
g_text_view.scrollEnabled = YES;
g_text_view.userInteractionEnabled = YES;
diff --git a/admob/testapp/src/main.h b/firestore/testapp/src/main.h
similarity index 100%
rename from admob/testapp/src/main.h
rename to firestore/testapp/src/main.h
diff --git a/firestore/testapp/testapp.xcodeproj/project.pbxproj b/firestore/testapp/testapp.xcodeproj/project.pbxproj
new file mode 100644
index 00000000..ed7634b5
--- /dev/null
+++ b/firestore/testapp/testapp.xcodeproj/project.pbxproj
@@ -0,0 +1,398 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 46;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 07D2E7EAE71115C7E5A3CD39 /* libPods-testapp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CFB4B133F33186AB751527C6 /* libPods-testapp.a */; };
+ 520BC0391C869159008CFBC3 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 520BC0381C869159008CFBC3 /* GoogleService-Info.plist */; };
+ 529226D61C85F68000C89379 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 529226D51C85F68000C89379 /* Foundation.framework */; };
+ 529226D81C85F68000C89379 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 529226D71C85F68000C89379 /* CoreGraphics.framework */; };
+ 529226DA1C85F68000C89379 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 529226D91C85F68000C89379 /* UIKit.framework */; };
+ 529227211C85FB6A00C89379 /* common_main.cc in Sources */ = {isa = PBXBuildFile; fileRef = 5292271F1C85FB6A00C89379 /* common_main.cc */; };
+ 529227241C85FB7600C89379 /* ios_main.mm in Sources */ = {isa = PBXBuildFile; fileRef = 529227221C85FB7600C89379 /* ios_main.mm */; };
+ 52B71EBB1C8600B600398745 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 52B71EBA1C8600B600398745 /* Images.xcassets */; };
+ B64AAF0C22EBB8570019A5BD /* firebase_auth.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B64AAF0922EBB8560019A5BD /* firebase_auth.framework */; };
+ B64AAF0E22EBB8570019A5BD /* firebase.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B64AAF0B22EBB8560019A5BD /* firebase.framework */; };
+ B64AAF1022EBBAC30019A5BD /* firebase_firestore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B64AAF0F22EBBAC20019A5BD /* firebase_firestore.framework */; };
+ D66B16871CE46E8900E5638A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D66B16861CE46E8900E5638A /* LaunchScreen.storyboard */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+ 520BC0381C869159008CFBC3 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; };
+ 529226D21C85F68000C89379 /* testapp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = testapp.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 529226D51C85F68000C89379 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
+ 529226D71C85F68000C89379 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
+ 529226D91C85F68000C89379 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
+ 529226EE1C85F68000C89379 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };
+ 5292271F1C85FB6A00C89379 /* common_main.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = common_main.cc; path = src/common_main.cc; sourceTree = ""; };
+ 529227201C85FB6A00C89379 /* main.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = main.h; path = src/main.h; sourceTree = ""; };
+ 529227221C85FB7600C89379 /* ios_main.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ios_main.mm; path = src/ios/ios_main.mm; sourceTree = ""; };
+ 52B71EBA1C8600B600398745 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = testapp/Images.xcassets; sourceTree = ""; };
+ 52FD1FF81C85FFA000BC68E3 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = testapp/Info.plist; sourceTree = ""; };
+ 561521B5AB75C63495CBCDE9 /* Pods-testapp.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-testapp.release.xcconfig"; path = "Target Support Files/Pods-testapp/Pods-testapp.release.xcconfig"; sourceTree = ""; };
+ 7B961A65741D8D4C337CD503 /* Pods-testapp.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-testapp.debug.xcconfig"; path = "Target Support Files/Pods-testapp/Pods-testapp.debug.xcconfig"; sourceTree = ""; };
+ B64AAF0922EBB8560019A5BD /* firebase_auth.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = firebase_auth.framework; sourceTree = ""; };
+ B64AAF0A22EBB8560019A5BD /* firebase_database.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = firebase_database.framework; sourceTree = ""; };
+ B64AAF0B22EBB8560019A5BD /* firebase.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = firebase.framework; sourceTree = ""; };
+ B64AAF0F22EBBAC20019A5BD /* firebase_firestore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = firebase_firestore.framework; sourceTree = ""; };
+ CFB4B133F33186AB751527C6 /* libPods-testapp.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-testapp.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+ D66B16861CE46E8900E5638A /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = ""; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 529226CF1C85F68000C89379 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ B64AAF1022EBBAC30019A5BD /* firebase_firestore.framework in Frameworks */,
+ B64AAF0C22EBB8570019A5BD /* firebase_auth.framework in Frameworks */,
+ B64AAF0E22EBB8570019A5BD /* firebase.framework in Frameworks */,
+ 529226D81C85F68000C89379 /* CoreGraphics.framework in Frameworks */,
+ 529226DA1C85F68000C89379 /* UIKit.framework in Frameworks */,
+ 529226D61C85F68000C89379 /* Foundation.framework in Frameworks */,
+ 07D2E7EAE71115C7E5A3CD39 /* libPods-testapp.a in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 529226C91C85F68000C89379 = {
+ isa = PBXGroup;
+ children = (
+ D66B16861CE46E8900E5638A /* LaunchScreen.storyboard */,
+ 520BC0381C869159008CFBC3 /* GoogleService-Info.plist */,
+ 52B71EBA1C8600B600398745 /* Images.xcassets */,
+ 52FD1FF81C85FFA000BC68E3 /* Info.plist */,
+ 5292271D1C85FB5500C89379 /* src */,
+ 529226D41C85F68000C89379 /* Frameworks */,
+ 529226D31C85F68000C89379 /* Products */,
+ 5C0AA7C41D61214BE0B5CB4C /* Pods */,
+ );
+ sourceTree = "";
+ };
+ 529226D31C85F68000C89379 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 529226D21C85F68000C89379 /* testapp.app */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ 529226D41C85F68000C89379 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ B64AAF0F22EBBAC20019A5BD /* firebase_firestore.framework */,
+ B64AAF0922EBB8560019A5BD /* firebase_auth.framework */,
+ B64AAF0A22EBB8560019A5BD /* firebase_database.framework */,
+ B64AAF0B22EBB8560019A5BD /* firebase.framework */,
+ 529226D51C85F68000C89379 /* Foundation.framework */,
+ 529226D71C85F68000C89379 /* CoreGraphics.framework */,
+ 529226D91C85F68000C89379 /* UIKit.framework */,
+ 529226EE1C85F68000C89379 /* XCTest.framework */,
+ CFB4B133F33186AB751527C6 /* libPods-testapp.a */,
+ );
+ name = Frameworks;
+ sourceTree = "";
+ };
+ 5292271D1C85FB5500C89379 /* src */ = {
+ isa = PBXGroup;
+ children = (
+ 5292271F1C85FB6A00C89379 /* common_main.cc */,
+ 529227201C85FB6A00C89379 /* main.h */,
+ 5292271E1C85FB5B00C89379 /* ios */,
+ );
+ name = src;
+ sourceTree = "";
+ };
+ 5292271E1C85FB5B00C89379 /* ios */ = {
+ isa = PBXGroup;
+ children = (
+ 529227221C85FB7600C89379 /* ios_main.mm */,
+ );
+ name = ios;
+ sourceTree = "";
+ };
+ 5C0AA7C41D61214BE0B5CB4C /* Pods */ = {
+ isa = PBXGroup;
+ children = (
+ 7B961A65741D8D4C337CD503 /* Pods-testapp.debug.xcconfig */,
+ 561521B5AB75C63495CBCDE9 /* Pods-testapp.release.xcconfig */,
+ );
+ path = Pods;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 529226D11C85F68000C89379 /* testapp */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 529226F91C85F68000C89379 /* Build configuration list for PBXNativeTarget "testapp" */;
+ buildPhases = (
+ C504BBC3599D8DE4D5CBFBFC /* [CP] Check Pods Manifest.lock */,
+ 529226CE1C85F68000C89379 /* Sources */,
+ 529226CF1C85F68000C89379 /* Frameworks */,
+ 529226D01C85F68000C89379 /* Resources */,
+ 8B4C0569EC39C8FCDFBCA4EB /* [CP] Copy Pods Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = testapp;
+ productName = testapp;
+ productReference = 529226D21C85F68000C89379 /* testapp.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 529226CA1C85F68000C89379 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastUpgradeCheck = 0640;
+ ORGANIZATIONNAME = Google;
+ TargetAttributes = {
+ 529226D11C85F68000C89379 = {
+ CreatedOnToolsVersion = 6.4;
+ };
+ };
+ };
+ buildConfigurationList = 529226CD1C85F68000C89379 /* Build configuration list for PBXProject "testapp" */;
+ compatibilityVersion = "Xcode 3.2";
+ developmentRegion = English;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ English,
+ en,
+ );
+ mainGroup = 529226C91C85F68000C89379;
+ productRefGroup = 529226D31C85F68000C89379 /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 529226D11C85F68000C89379 /* testapp */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 529226D01C85F68000C89379 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ D66B16871CE46E8900E5638A /* LaunchScreen.storyboard in Resources */,
+ 52B71EBB1C8600B600398745 /* Images.xcassets in Resources */,
+ 520BC0391C869159008CFBC3 /* GoogleService-Info.plist in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 8B4C0569EC39C8FCDFBCA4EB /* [CP] Copy Pods Resources */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-testapp/Pods-testapp-resources.sh",
+ "${PODS_CONFIGURATION_BUILD_DIR}/gRPC-C++/gRPCCertificates-Cpp.bundle",
+ );
+ name = "[CP] Copy Pods Resources";
+ outputPaths = (
+ "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/gRPCCertificates-Cpp.bundle",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-testapp/Pods-testapp-resources.sh\"\n";
+ showEnvVarsInLog = 0;
+ };
+ C504BBC3599D8DE4D5CBFBFC /* [CP] Check Pods Manifest.lock */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+ "${PODS_ROOT}/Manifest.lock",
+ );
+ name = "[CP] Check Pods Manifest.lock";
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ "$(DERIVED_FILE_DIR)/Pods-testapp-checkManifestLockResult.txt",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+ showEnvVarsInLog = 0;
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 529226CE1C85F68000C89379 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 529227241C85FB7600C89379 /* ios_main.mm in Sources */,
+ 529227211C85FB6A00C89379 /* common_main.cc in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin XCBuildConfiguration section */
+ 529226F71C85F68000C89379 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_SYMBOLS_PRIVATE_EXTERN = NO;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 13.0;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 529226F81C85F68000C89379 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 13.0;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ 529226FA1C85F68000C89379 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7B961A65741D8D4C337CD503 /* Pods-testapp.debug.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)",
+ );
+ HEADER_SEARCH_PATHS = (
+ "$(inherited)",
+ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
+ "\"$(SRCROOT)/src\"",
+ );
+ INFOPLIST_FILE = testapp/Info.plist;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ WRAPPER_EXTENSION = app;
+ };
+ name = Debug;
+ };
+ 529226FB1C85F68000C89379 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 561521B5AB75C63495CBCDE9 /* Pods-testapp.release.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)",
+ );
+ HEADER_SEARCH_PATHS = (
+ "$(inherited)",
+ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
+ "\"$(SRCROOT)/src\"",
+ );
+ INFOPLIST_FILE = testapp/Info.plist;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ WRAPPER_EXTENSION = app;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 529226CD1C85F68000C89379 /* Build configuration list for PBXProject "testapp" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 529226F71C85F68000C89379 /* Debug */,
+ 529226F81C85F68000C89379 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 529226F91C85F68000C89379 /* Build configuration list for PBXNativeTarget "testapp" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 529226FA1C85F68000C89379 /* Debug */,
+ 529226FB1C85F68000C89379 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 529226CA1C85F68000C89379 /* Project object */;
+}
diff --git a/firestore/testapp/testapp/Images.xcassets/AppIcon.appiconset/Contents.json b/firestore/testapp/testapp/Images.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 00000000..d8db8d65
--- /dev/null
+++ b/firestore/testapp/testapp/Images.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,98 @@
+{
+ "images" : [
+ {
+ "idiom" : "iphone",
+ "size" : "20x20",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "20x20",
+ "scale" : "3x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "29x29",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "29x29",
+ "scale" : "3x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "40x40",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "40x40",
+ "scale" : "3x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "60x60",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "60x60",
+ "scale" : "3x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "20x20",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "20x20",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "29x29",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "29x29",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "40x40",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "40x40",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "76x76",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "76x76",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "83.5x83.5",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "ios-marketing",
+ "size" : "1024x1024",
+ "scale" : "1x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/invites/testapp/testapp/Images.xcassets/LaunchImage.launchimage/Contents.json b/firestore/testapp/testapp/Images.xcassets/LaunchImage.launchimage/Contents.json
similarity index 100%
rename from invites/testapp/testapp/Images.xcassets/LaunchImage.launchimage/Contents.json
rename to firestore/testapp/testapp/Images.xcassets/LaunchImage.launchimage/Contents.json
diff --git a/firestore/testapp/testapp/Info.plist b/firestore/testapp/testapp/Info.plist
new file mode 100644
index 00000000..341ff801
--- /dev/null
+++ b/firestore/testapp/testapp/Info.plist
@@ -0,0 +1,39 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ com.google.firebase.cpp.firestore.testapp
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ 1.0
+ CFBundleURLTypes
+
+
+ CFBundleTypeRole
+ Editor
+ CFBundleURLName
+ google
+ CFBundleURLSchemes
+
+ YOUR_REVERSED_CLIENT_ID
+
+
+
+ CFBundleVersion
+ 1
+ LSRequiresIPhoneOS
+
+ UILaunchStoryboardName
+ LaunchScreen
+
+
diff --git a/admob/testapp/AndroidManifest.xml b/functions/testapp/AndroidManifest.xml
similarity index 76%
rename from admob/testapp/AndroidManifest.xml
rename to functions/testapp/AndroidManifest.xml
index 13530578..0b38246d 100644
--- a/admob/testapp/AndroidManifest.xml
+++ b/functions/testapp/AndroidManifest.xml
@@ -1,16 +1,17 @@
-
+
+ android:exported = "true"
+ android:screenOrientation="portrait"
+ android:configChanges="orientation|screenSize">
diff --git a/functions/testapp/CMakeLists.txt b/functions/testapp/CMakeLists.txt
new file mode 100644
index 00000000..c6eb6903
--- /dev/null
+++ b/functions/testapp/CMakeLists.txt
@@ -0,0 +1,125 @@
+cmake_minimum_required(VERSION 2.8)
+
+# User settings for Firebase samples.
+# Path to Firebase SDK.
+# Try to read the path to the Firebase C++ SDK from an environment variable.
+if (NOT "$ENV{FIREBASE_CPP_SDK_DIR}" STREQUAL "")
+ set(DEFAULT_FIREBASE_CPP_SDK_DIR "$ENV{FIREBASE_CPP_SDK_DIR}")
+else()
+ set(DEFAULT_FIREBASE_CPP_SDK_DIR "firebase_cpp_sdk")
+endif()
+if ("${FIREBASE_CPP_SDK_DIR}" STREQUAL "")
+ set(FIREBASE_CPP_SDK_DIR ${DEFAULT_FIREBASE_CPP_SDK_DIR})
+endif()
+if(NOT EXISTS ${FIREBASE_CPP_SDK_DIR})
+ message(FATAL_ERROR "The Firebase C++ SDK directory does not exist: ${FIREBASE_CPP_SDK_DIR}. See the readme.md for more information")
+endif()
+
+# Windows runtime mode, either MD or MT depending on whether you are using
+# /MD or /MT. For more information see:
+# https://msdn.microsoft.com/en-us/library/2kzt1wy3.aspx
+set(MSVC_RUNTIME_MODE MD)
+
+project(firebase_testapp)
+
+# Sample source files.
+set(FIREBASE_SAMPLE_COMMON_SRCS
+ src/main.h
+ src/common_main.cc
+)
+
+# The include directory for the testapp.
+include_directories(src)
+
+# Sample uses some features that require C++ 11, such as lambdas.
+set (CMAKE_CXX_STANDARD 11)
+
+if(ANDROID)
+ # Build an Android application.
+
+ # Source files used for the Android build.
+ set(FIREBASE_SAMPLE_ANDROID_SRCS
+ src/android/android_main.cc
+ )
+
+ # Build native_app_glue as a static lib
+ add_library(native_app_glue STATIC
+ ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c)
+
+ # Export ANativeActivity_onCreate(),
+ # Refer to: https://github.com/android-ndk/ndk/issues/381.
+ set(CMAKE_SHARED_LINKER_FLAGS
+ "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate")
+
+ # Define the target as a shared library, as that is what gradle expects.
+ set(target_name "android_main")
+ add_library(${target_name} SHARED
+ ${FIREBASE_SAMPLE_ANDROID_SRCS}
+ ${FIREBASE_SAMPLE_COMMON_SRCS}
+ )
+
+ target_link_libraries(${target_name}
+ log android atomic native_app_glue
+ )
+
+ target_include_directories(${target_name} PRIVATE
+ ${ANDROID_NDK}/sources/android/native_app_glue)
+
+ set(ADDITIONAL_LIBS)
+else()
+ # Build a desktop application.
+
+ # Windows runtime mode, either MD or MT depending on whether you are using
+ # /MD or /MT. For more information see:
+ # https://msdn.microsoft.com/en-us/library/2kzt1wy3.aspx
+ set(MSVC_RUNTIME_MODE MD)
+
+ # Platform abstraction layer for the desktop sample.
+ set(FIREBASE_SAMPLE_DESKTOP_SRCS
+ src/desktop/desktop_main.cc
+ )
+
+ set(target_name "desktop_testapp")
+ add_executable(${target_name}
+ ${FIREBASE_SAMPLE_DESKTOP_SRCS}
+ ${FIREBASE_SAMPLE_COMMON_SRCS}
+ )
+
+ if(APPLE)
+ set(ADDITIONAL_LIBS
+ gssapi_krb5
+ pthread
+ "-framework CoreFoundation"
+ "-framework Foundation"
+ "-framework GSS"
+ "-framework Security"
+ )
+ elseif(MSVC)
+ set(ADDITIONAL_LIBS advapi32 ws2_32 crypt32 rpcrt4 ole32)
+ else()
+ set(ADDITIONAL_LIBS pthread)
+ endif()
+
+ # If a config file is present, copy it into the binary location so that it's
+ # possible to create the default Firebase app.
+ set(FOUND_JSON_FILE FALSE)
+ foreach(config "google-services-desktop.json" "google-services.json")
+ if (EXISTS ${config})
+ add_custom_command(
+ TARGET ${target_name} POST_BUILD
+ COMMAND ${CMAKE_COMMAND} -E copy
+ ${config} $)
+ set(FOUND_JSON_FILE TRUE)
+ break()
+ endif()
+ endforeach()
+ if(NOT FOUND_JSON_FILE)
+ message(WARNING "Failed to find either google-services-desktop.json or google-services.json. See the readme.md for more information.")
+ endif()
+endif()
+
+# Add the Firebase libraries to the target using the function from the SDK.
+add_subdirectory(${FIREBASE_CPP_SDK_DIR} bin/ EXCLUDE_FROM_ALL)
+# Note that firebase_app needs to be last in the list.
+set(firebase_libs firebase_functions firebase_auth firebase_app)
+target_link_libraries(${target_name} "${firebase_libs}" ${ADDITIONAL_LIBS})
diff --git a/admob/testapp/LICENSE b/functions/testapp/LICENSE
similarity index 100%
rename from admob/testapp/LICENSE
rename to functions/testapp/LICENSE
diff --git a/invites/testapp/LaunchScreen.storyboard b/functions/testapp/LaunchScreen.storyboard
similarity index 100%
rename from invites/testapp/LaunchScreen.storyboard
rename to functions/testapp/LaunchScreen.storyboard
diff --git a/functions/testapp/Podfile b/functions/testapp/Podfile
new file mode 100644
index 00000000..7197d817
--- /dev/null
+++ b/functions/testapp/Podfile
@@ -0,0 +1,8 @@
+source 'https://github.com/CocoaPods/Specs.git'
+platform :ios, '13.0'
+use_frameworks!
+# Cloud Functions for Firebase test application.
+target 'testapp' do
+ pod 'Firebase/Functions', '10.25.0'
+ pod 'Firebase/Auth', '10.25.0'
+end
diff --git a/functions/testapp/build.gradle b/functions/testapp/build.gradle
new file mode 100644
index 00000000..d47c3488
--- /dev/null
+++ b/functions/testapp/build.gradle
@@ -0,0 +1,77 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+buildscript {
+ repositories {
+ mavenLocal()
+ maven { url 'https://maven.google.com' }
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:4.2.1'
+ classpath 'com.google.gms:google-services:4.0.1'
+ }
+}
+
+allprojects {
+ repositories {
+ mavenLocal()
+ maven { url 'https://maven.google.com' }
+ jcenter()
+ }
+}
+
+apply plugin: 'com.android.application'
+
+android {
+ compileOptions {
+ sourceCompatibility 1.8
+ targetCompatibility 1.8
+ }
+ compileSdkVersion 34
+ ndkPath System.getenv('ANDROID_NDK_HOME')
+ buildToolsVersion '30.0.2'
+
+ sourceSets {
+ main {
+ jniLibs.srcDirs = ['libs']
+ manifest.srcFile 'AndroidManifest.xml'
+ java.srcDirs = ['src/android/java']
+ res.srcDirs = ['res']
+ }
+ }
+
+ defaultConfig {
+ applicationId 'com.google.firebase.cpp.functions.testapp'
+ minSdkVersion 23
+ targetSdkVersion 28
+ versionCode 1
+ versionName '1.0'
+ externalNativeBuild.cmake {
+ arguments "-DFIREBASE_CPP_SDK_DIR=$gradle.firebase_cpp_sdk_dir"
+ }
+ }
+ externalNativeBuild.cmake {
+ path 'CMakeLists.txt'
+ }
+ buildTypes {
+ release {
+ minifyEnabled true
+ proguardFile getDefaultProguardFile('proguard-android.txt')
+ proguardFile file('proguard.pro')
+ }
+ }
+ packagingOptions {
+ pickFirst 'META-INF/**/coroutines.pro'
+ }
+ lintOptions {
+ abortOnError false
+ checkReleaseBuilds false
+ }
+}
+
+apply from: "$gradle.firebase_cpp_sdk_dir/Android/firebase_dependencies.gradle"
+firebaseCpp.dependencies {
+ auth
+ functions
+}
+
+apply plugin: 'com.google.gms.google-services'
diff --git a/functions/testapp/functions/index.js b/functions/testapp/functions/index.js
new file mode 100644
index 00000000..a8d1905d
--- /dev/null
+++ b/functions/testapp/functions/index.js
@@ -0,0 +1,43 @@
+/**
+ * Copyright 2018 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.
+ */
+'use strict';
+
+const functions = require('firebase-functions/v1');
+const admin = require('firebase-admin');
+admin.initializeApp();
+
+// Adds two numbers to each other.
+exports.addNumbers = functions.https.onCall((data) => {
+ // Numbers passed from the client.
+ const firstNumber = data.firstNumber;
+ const secondNumber = data.secondNumber;
+
+ // Checking that attributes are present and are numbers.
+ if (!Number.isFinite(firstNumber) || !Number.isFinite(secondNumber)) {
+ // Throwing an HttpsError so that the client gets the error details.
+ throw new functions.https.HttpsError('invalid-argument', 'The function ' +
+ 'must be called with two arguments "firstNumber" and "secondNumber" ' +
+ 'which must both be numbers.');
+ }
+
+ // returning result.
+ return {
+ firstNumber: firstNumber,
+ secondNumber: secondNumber,
+ operator: '+',
+ operationResult: firstNumber + secondNumber,
+ };
+});
diff --git a/functions/testapp/functions/package.json b/functions/testapp/functions/package.json
new file mode 100644
index 00000000..86b46886
--- /dev/null
+++ b/functions/testapp/functions/package.json
@@ -0,0 +1,9 @@
+{
+ "name": "functions",
+ "description": "Cloud Functions for Firebase C++ Quickstart",
+ "dependencies": {
+ "firebase-admin": "~5.10.0",
+ "firebase-functions": "0.9.0"
+ },
+ "private": true
+}
diff --git a/functions/testapp/gradle.properties b/functions/testapp/gradle.properties
new file mode 100644
index 00000000..d7ba8f42
--- /dev/null
+++ b/functions/testapp/gradle.properties
@@ -0,0 +1 @@
+android.useAndroidX = true
diff --git a/invites/testapp/gradle/wrapper/gradle-wrapper.jar b/functions/testapp/gradle/wrapper/gradle-wrapper.jar
similarity index 100%
rename from invites/testapp/gradle/wrapper/gradle-wrapper.jar
rename to functions/testapp/gradle/wrapper/gradle-wrapper.jar
diff --git a/admob/testapp/gradle/wrapper/gradle-wrapper.properties b/functions/testapp/gradle/wrapper/gradle-wrapper.properties
similarity index 52%
rename from admob/testapp/gradle/wrapper/gradle-wrapper.properties
rename to functions/testapp/gradle/wrapper/gradle-wrapper.properties
index d5705170..65340c1b 100644
--- a/admob/testapp/gradle/wrapper/gradle-wrapper.properties
+++ b/functions/testapp/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Wed Apr 10 15:27:10 PDT 2013
+#Mon Nov 27 14:03:45 PST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
+distributionUrl=https://services.gradle.org/distributions/gradle-6.7.1-all.zip
diff --git a/invites/testapp/gradlew b/functions/testapp/gradlew
similarity index 100%
rename from invites/testapp/gradlew
rename to functions/testapp/gradlew
diff --git a/invites/testapp/gradlew.bat b/functions/testapp/gradlew.bat
similarity index 100%
rename from invites/testapp/gradlew.bat
rename to functions/testapp/gradlew.bat
diff --git a/functions/testapp/proguard.pro b/functions/testapp/proguard.pro
new file mode 100644
index 00000000..2d04b8a9
--- /dev/null
+++ b/functions/testapp/proguard.pro
@@ -0,0 +1,2 @@
+-ignorewarnings
+-keep,includedescriptorclasses public class com.google.firebase.example.LoggingUtils { * ; }
diff --git a/functions/testapp/readme.md b/functions/testapp/readme.md
new file mode 100644
index 00000000..8d1537a6
--- /dev/null
+++ b/functions/testapp/readme.md
@@ -0,0 +1,214 @@
+Cloud Functions for Firebase Quickstart
+========================
+
+The Cloud Functions for Firebase Test Application (testapp) demonstrates
+Cloud Function operations with the Firebase C++ SDK for Cloud Functions.
+The application has no user interface and simply logs actions it's performing
+to the console.
+
+The testapp performs the following:
+ - Creates a firebase::App in a platform-specific way. The App holds
+ platform-specific context that's used by other Firebase APIs, and is a
+ central point for communication between the Cloud Function C++ and
+ Firebase Auth C++ libraries.
+ - Calls various integration test Cloud Functions and verifies their results.
+ - Shuts down the Cloud Functions, Firebase Auth, and Firebase App systems.
+
+Introduction
+------------
+
+- [Read more about Cloud Functions for Firebase](http://go/firebase-functions-api)
+
+Building and Running the testapp
+--------------------------------
+
+### Deploying functions.
+- Install the [Firebase CLI](https://firebase.google.com/docs/cli/).
+- Deploy the provided functions.
+ ```bash
+ # Move to the `functions` subdirectory of quickstart-android
+ cd functions
+
+ # Install all of the dependencies of the cloud functions
+ cd functions
+ npm install
+ cd ../
+
+ # Deploy functions to your Firebase project
+ firebase --project=YOUR_PROJECT_ID deploy --only functions
+ ```
+
+### iOS
+ - Link your iOS app to the Firebase libraries.
+ - Get CocoaPods version 1 or later by running,
+ ```
+ sudo gem install cocoapods --pre
+ ```
+ - From the testapp directory, install the CocoaPods listed in the Podfile
+ by running,
+ ```
+ pod install
+ ```
+ - Open the generated Xcode workspace (which now has the CocoaPods),
+ ```
+ open testapp.xcworkspace
+ ```
+ - For further details please refer to the
+ [general instructions for setting up an iOS app with Firebase](https://firebase.google.com/docs/ios/setup).
+ - Register your iOS app with Firebase.
+ - Create a new app on the [Firebase console](https://firebase.google.com/console/), and attach
+ your iOS app to it.
+ - You can use "com.google.firebase.cpp.functions.testapp" as the iOS Bundle
+ ID while you're testing. You can omit App Store ID while testing.
+ - Add the GoogleService-Info.plist that you downloaded from Firebase
+ console to the testapp root directory. This file identifies your iOS app
+ to the Firebase backend.
+ - In the Firebase console for your app, select "Auth", then enable
+ "Anonymous". This will allow the testapp to use anonymous sign-in to
+ authenticate with Cloud Functions, which requires a signed-in user by
+ default (an anonymous user will suffice).
+ - Download the Firebase C++ SDK linked from
+ [https://firebase.google.com/docs/cpp/setup](https://firebase.google.com/docs/cpp/setup)
+ and unzip it to a directory of your choice.
+ - Add the following frameworks from the Firebase C++ SDK to the project:
+ - frameworks/ios/universal/firebase.framework
+ - frameworks/ios/universal/firebase_auth.framework
+ - frameworks/ios/universal/firebase_functions.framework
+ - You will need to either,
+ 1. Check "Copy items if needed" when adding the frameworks, or
+ 2. Add the framework path in "Framework Search Paths"
+ - e.g. If you downloaded the Firebase C++ SDK to
+ `/Users/me/firebase_cpp_sdk`,
+ then you would add the path
+ `/Users/me/firebase_cpp_sdk/frameworks/ios/universal`.
+ - To add the path, in XCode, select your project in the project
+ navigator, then select your target in the main window.
+ Select the "Build Settings" tab, and click "All" to see all
+ the build settings. Scroll down to "Search Paths", and add
+ your path to "Framework Search Paths".
+ - In XCode, build & run the sample on an iOS device or simulator.
+ - The testapp has no interative interface. The output of the app can be viewed
+ via the console or on the device's display. In Xcode, select
+ "View --> Debug Area --> Activate Console" from the menu to view the console.
+
+### Android
+ - Register your Android app with Firebase.
+ - Create a new app on
+ the [Firebase console](https://firebase.google.com/console/), and attach
+ your Android app to it.
+ - You can use "com.google.firebase.cpp.functions.testapp" as the Package
+ Name while you're testing.
+ - To
+ [generate a SHA1](https://developers.google.com/android/guides/client-auth)
+ run this command on Mac and Linux,
+ ```
+ keytool -exportcert -list -v -alias androiddebugkey -keystore ~/.android/debug.keystore
+ ```
+ or this command on Windows,
+ ```
+ keytool -exportcert -list -v -alias androiddebugkey -keystore %USERPROFILE%\.android\debug.keystore
+ ```
+ - If keytool reports that you do not have a debug.keystore, you can
+ [create one with](http://developer.android.com/tools/publishing/app-signing.html#signing-manually),
+ ```
+ keytool -genkey -v -keystore ~/.android/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"
+ ```
+ - Add the `google-services.json` file that you downloaded from Firebase
+ console to the root directory of testapp. This file identifies your
+ Android app to the Firebase backend.
+ - In the Firebase console for your app, select "Auth", then enable
+ "Anonymous". This will allow the testapp to use anonymous sign-in to
+ authenticate with Cloud Functions.
+ - For further details please refer to the
+ [general instructions for setting up an Android app with Firebase](https://firebase.google.com/docs/android/setup).
+ - Download the Firebase C++ SDK linked from
+ [https://firebase.google.com/docs/cpp/setup](https://firebase.google.com/docs/cpp/setup)
+ and unzip it to a directory of your choice.
+ - Configure the location of the Firebase C++ SDK by setting the
+ firebase\_cpp\_sdk.dir Gradle property to the SDK install directory.
+ For example, in the project directory:
+ ```
+ echo "systemProp.firebase\_cpp\_sdk.dir=/User/$USER/firebase\_cpp\_sdk" >> gradle.properties
+ ```
+ - Ensure the Android SDK and NDK locations are set in Android Studio.
+ - From the Android Studio launch menu, go to `File/Project Structure...` or
+ `Configure/Project Defaults/Project Structure...`
+ (Shortcut: Control + Alt + Shift + S on windows, Command + ";" on a mac)
+ and download the SDK and NDK if the locations are not yet set.
+ - Open *build.gradle* in Android Studio.
+ - From the Android Studio launch menu, "Open an existing Android Studio
+ project", and select `build.gradle`.
+ - Install the SDK Platforms that Android Studio reports missing.
+ - Build the testapp and run it on an Android device or emulator.
+ - The testapp has no interactive interface. The output of the app can be
+ viewed on the device's display, or in the logcat output of Android studio or
+ by running "adb logcat *:W android_main firebase" from the command line.
+
+### Desktop
+ - Register your app with Firebase.
+ - Create a new app on the [Firebase console](https://firebase.google.com/console/),
+ following the above instructions for Android or iOS.
+ - If you have an Android project, add the `google-services.json` file that
+ you downloaded from the Firebase console to the root directory of the
+ testapp.
+ - If you have an iOS project, and don't wish to use an Android project,
+ you can use the Python script `generate_xml_from_google_services_json.py --plist`,
+ located in the Firebase C++ SDK, to convert your `GoogleService-Info.plist`
+ file into a `google-services-desktop.json` file, which can then be
+ placed in the root directory of the testapp.
+ - Download the Firebase C++ SDK linked from
+ [https://firebase.google.com/docs/cpp/setup](https://firebase.google.com/docs/cpp/setup)
+ and unzip it to a directory of your choice.
+ - Configure the testapp with the location of the Firebase C++ SDK.
+ This can be done a couple different ways (in highest to lowest priority):
+ - When invoking cmake, pass in the location with
+ -DFIREBASE_CPP_SDK_DIR=/path/to/firebase_cpp_sdk.
+ - Set an environment variable for FIREBASE_CPP_SDK_DIR to the path to use.
+ - Edit the CMakeLists.txt file, changing the FIREBASE_CPP_SDK_DIR path
+ to the appropriate location.
+ - From the testapp directory, generate the build files by running,
+ ```
+ cmake .
+ ```
+ If you want to use XCode, you can use -G"Xcode" to generate the project.
+ Similarly, to use Visual Studio, -G"Visual Studio 15 2017". For more
+ information, see
+ [CMake generators](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html).
+ - Build the testapp, by either opening the generated project file based on
+ the platform, or running,
+ ```
+ cmake --build .
+ ```
+ - Execute the testapp by running,
+ ```
+ ./desktop_testapp
+ ```
+ Note that the executable might be under another directory, such as Debug.
+ - The testapp has no user interface, but the output can be viewed via the
+ console. Note that Functions uses a stubbed implementation on desktop,
+ so functionality is not expected.
+
+Support
+-------
+
+[https://firebase.google.com/support/](https://firebase.google.com/support/)
+
+License
+-------
+
+Copyright 2016 Google, Inc.
+
+Licensed to the Apache Software Foundation (ASF) under one or more contributor
+license agreements. See the NOTICE file distributed with this work for
+additional information regarding copyright ownership. The ASF licenses this
+file to you under the Apache License, Version 2.0 (the "License"); you may not
+use this file except in compliance with the License. You may obtain a copy of
+the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+License for the specific language governing permissions and limitations under
+the License.
diff --git a/functions/testapp/res/layout/main.xml b/functions/testapp/res/layout/main.xml
new file mode 100644
index 00000000..d3ffb630
--- /dev/null
+++ b/functions/testapp/res/layout/main.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/functions/testapp/res/values/strings.xml b/functions/testapp/res/values/strings.xml
new file mode 100644
index 00000000..2cf3eca3
--- /dev/null
+++ b/functions/testapp/res/values/strings.xml
@@ -0,0 +1,4 @@
+
+
+ Cloud Functions for Firebase Test
+
diff --git a/functions/testapp/settings.gradle b/functions/testapp/settings.gradle
new file mode 100644
index 00000000..2a543b93
--- /dev/null
+++ b/functions/testapp/settings.gradle
@@ -0,0 +1,36 @@
+// Copyright 2018 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+def firebase_cpp_sdk_dir = System.getProperty('firebase_cpp_sdk.dir')
+if (firebase_cpp_sdk_dir == null || firebase_cpp_sdk_dir.isEmpty()) {
+ firebase_cpp_sdk_dir = System.getenv('FIREBASE_CPP_SDK_DIR')
+ if (firebase_cpp_sdk_dir == null || firebase_cpp_sdk_dir.isEmpty()) {
+ if ((new File('firebase_cpp_sdk')).exists()) {
+ firebase_cpp_sdk_dir = 'firebase_cpp_sdk'
+ } else {
+ throw new StopActionException(
+ 'firebase_cpp_sdk.dir property or the FIREBASE_CPP_SDK_DIR ' +
+ 'environment variable must be set to reference the Firebase C++ ' +
+ 'SDK install directory. This is used to configure static library ' +
+ 'and C/C++ include paths for the SDK.')
+ }
+ }
+}
+if (!(new File(firebase_cpp_sdk_dir)).exists()) {
+ throw new StopActionException(
+ sprintf('Firebase C++ SDK directory %s does not exist',
+ firebase_cpp_sdk_dir))
+}
+gradle.ext.firebase_cpp_sdk_dir = "$firebase_cpp_sdk_dir"
+includeBuild "$firebase_cpp_sdk_dir"
\ No newline at end of file
diff --git a/invites/testapp/src/android/android_main.cc b/functions/testapp/src/android/android_main.cc
similarity index 98%
rename from invites/testapp/src/android/android_main.cc
rename to functions/testapp/src/android/android_main.cc
index 73cb30e7..4b86ce30 100644
--- a/invites/testapp/src/android/android_main.cc
+++ b/functions/testapp/src/android/android_main.cc
@@ -50,6 +50,12 @@ bool ProcessEvents(int msec) {
return g_destroy_requested | g_restarted;
}
+std::string PathForResource() {
+ ANativeActivity* nativeActivity = g_app_state->activity;
+ std::string result(nativeActivity->internalDataPath);
+ return result + "/";
+}
+
// Get the activity.
jobject GetActivity() { return g_app_state->activity->clazz; }
diff --git a/functions/testapp/src/android/java/com/google/firebase/example/LoggingUtils.java b/functions/testapp/src/android/java/com/google/firebase/example/LoggingUtils.java
new file mode 100644
index 00000000..351cf3b4
--- /dev/null
+++ b/functions/testapp/src/android/java/com/google/firebase/example/LoggingUtils.java
@@ -0,0 +1,57 @@
+// Copyright 2016 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.firebase.example;
+
+import android.app.Activity;
+import android.os.Handler;
+import android.os.Looper;
+import android.view.Window;
+import android.widget.LinearLayout;
+import android.widget.ScrollView;
+import android.widget.TextView;
+
+/**
+ * A utility class, encapsulating the data and methods required to log arbitrary text to the screen,
+ * via a non-editable TextView.
+ */
+public class LoggingUtils {
+ public static TextView sTextView = null;
+
+ public static void initLogWindow(Activity activity) {
+ LinearLayout linearLayout = new LinearLayout(activity);
+ ScrollView scrollView = new ScrollView(activity);
+ TextView textView = new TextView(activity);
+ textView.setTag("Logger");
+ linearLayout.addView(scrollView);
+ scrollView.addView(textView);
+ Window window = activity.getWindow();
+ window.takeSurface(null);
+ window.setContentView(linearLayout);
+ sTextView = textView;
+ }
+
+ public static void addLogText(final String text) {
+ new Handler(Looper.getMainLooper())
+ .post(
+ new Runnable() {
+ @Override
+ public void run() {
+ if (sTextView != null) {
+ sTextView.append(text);
+ }
+ }
+ });
+ }
+}
diff --git a/functions/testapp/src/common_main.cc b/functions/testapp/src/common_main.cc
new file mode 100644
index 00000000..5e5e0019
--- /dev/null
+++ b/functions/testapp/src/common_main.cc
@@ -0,0 +1,173 @@
+// Copyright 2016 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "firebase/app.h"
+#include "firebase/auth.h"
+#include "firebase/functions.h"
+#include "firebase/future.h"
+#include "firebase/log.h"
+#include "firebase/util.h"
+
+// Thin OS abstraction layer.
+#include "main.h" // NOLINT
+
+// Wait for a Future to be completed. If the Future returns an error, it will
+// be logged.
+void WaitForCompletion(const firebase::FutureBase& future, const char* name) {
+ while (future.status() == firebase::kFutureStatusPending) {
+ ProcessEvents(100);
+ }
+}
+
+
+extern "C" int common_main(int argc, const char* argv[]) {
+ ::firebase::App* app;
+
+#if defined(__ANDROID__)
+ app = ::firebase::App::Create(GetJniEnv(), GetActivity());
+#else
+ app = ::firebase::App::Create();
+#endif // defined(__ANDROID__)
+
+ LogMessage("Initialized Firebase App.");
+
+ LogMessage("Initializing Firebase Auth and Cloud Functions.");
+
+ // Use ModuleInitializer to initialize both Auth and Functions, ensuring no
+ // dependencies are missing.
+ ::firebase::functions::Functions* functions = nullptr;
+ ::firebase::auth::Auth* auth = nullptr;
+ void* initialize_targets[] = {&auth, &functions};
+
+ const firebase::ModuleInitializer::InitializerFn initializers[] = {
+ [](::firebase::App* app, void* data) {
+ LogMessage("Attempt to initialize Firebase Auth.");
+ void** targets = reinterpret_cast(data);
+ ::firebase::InitResult result;
+ *reinterpret_cast<::firebase::auth::Auth**>(targets[0]) =
+ ::firebase::auth::Auth::GetAuth(app, &result);
+ return result;
+ },
+ [](::firebase::App* app, void* data) {
+ LogMessage("Attempt to initialize Cloud Functions.");
+ void** targets = reinterpret_cast(data);
+ ::firebase::InitResult result;
+ *reinterpret_cast<::firebase::functions::Functions**>(targets[1]) =
+ ::firebase::functions::Functions::GetInstance(app, &result);
+ return result;
+ }};
+
+ ::firebase::ModuleInitializer initializer;
+ initializer.Initialize(app, initialize_targets, initializers,
+ sizeof(initializers) / sizeof(initializers[0]));
+
+ WaitForCompletion(initializer.InitializeLastResult(), "Initialize");
+
+ if (initializer.InitializeLastResult().error() != 0) {
+ LogMessage("Failed to initialize Firebase libraries: %s",
+ initializer.InitializeLastResult().error_message());
+ ProcessEvents(2000);
+ return 1;
+ }
+ LogMessage("Successfully initialized Firebase Auth and Cloud Functions.");
+
+ // To test against a local emulator, uncomment this line:
+ // functions->UseFunctionsEmulator("http://localhost:5005");
+ // Or when running in an Android emulator:
+ // functions->UseFunctionsEmulator("http://10.0.2.2:5005");
+
+ // Optionally, sign in using Auth before accessing Functions.
+ {
+ firebase::Future sign_in_future =
+ auth->SignInAnonymously();
+ WaitForCompletion(sign_in_future, "SignInAnonymously");
+ if (sign_in_future.error() == firebase::auth::kAuthErrorNone) {
+ LogMessage("Auth: Signed in anonymously.");
+ } else {
+ LogMessage("ERROR: Could not sign in anonymously. Error %d: %s",
+ sign_in_future.error(), sign_in_future.error_message());
+ LogMessage(
+ " Ensure your application has the Anonymous sign-in provider "
+ "enabled in Firebase Console.");
+ LogMessage(
+ " Attempting to connect to Cloud Functions anyway. This may fail "
+ "depending on the function.");
+ }
+ }
+
+
+ // Create a callable.
+ LogMessage("Calling addNumbers");
+ firebase::functions::HttpsCallableReference addNumbers;
+ addNumbers = functions->GetHttpsCallable("addNumbers");
+
+ firebase::Future future;
+ {
+ std::map data;
+ data["firstNumber"] = firebase::Variant(5);
+ data["secondNumber"] = firebase::Variant(7);
+ future = addNumbers.Call(firebase::Variant(data));
+ }
+ WaitForCompletion(future, "Call");
+ if (future.error() != firebase::functions::kErrorNone) {
+ LogMessage("FAILED!");
+ LogMessage(" Error %d: %s", future.error(), future.error_message());
+ } else {
+ firebase::Variant result = future.result()->data();
+ int op_result =
+ static_cast(result.map()["operationResult"].int64_value());
+ const int expected = 12;
+ if (op_result != expected) {
+ LogMessage("FAILED!");
+ LogMessage(" Expected: %d, Actual: %d", expected, op_result);
+ } else {
+ LogMessage("SUCCESS.");
+ LogMessage(" Got expected result: %d", op_result);
+ }
+ }
+
+ LogMessage("Shutting down the Functions library.");
+ delete functions;
+ functions = nullptr;
+
+ // Ensure that the ref we had is now invalid.
+ if (!addNumbers.is_valid()) {
+ LogMessage("SUCCESS: Reference was invalidated on library shutdown.");
+ } else {
+ LogMessage("ERROR: Reference is still valid after library shutdown.");
+ }
+
+ LogMessage("Signing out from anonymous account.");
+ auth->SignOut();
+ LogMessage("Shutting down the Auth library.");
+ delete auth;
+ auth = nullptr;
+
+ LogMessage("Shutting down Firebase App.");
+ delete app;
+
+ // Wait until the user wants to quit the app.
+ while (!ProcessEvents(1000)) {
+ }
+
+ return 0;
+}
diff --git a/invites/testapp/src/desktop/desktop_main.cc b/functions/testapp/src/desktop/desktop_main.cc
similarity index 52%
rename from invites/testapp/src/desktop/desktop_main.cc
rename to functions/testapp/src/desktop/desktop_main.cc
index 99ace543..0220c688 100644
--- a/invites/testapp/src/desktop/desktop_main.cc
+++ b/functions/testapp/src/desktop/desktop_main.cc
@@ -15,16 +15,35 @@
#include
#include
#include
-#ifndef _WIN32
+
+#ifdef _WIN32
+#include
+#define chdir _chdir
+#else
#include
-#endif // !_WIN32
+#endif // _WIN32
#ifdef _WIN32
#include
#endif // _WIN32
+#include
+#include
+
#include "main.h" // NOLINT
+// The TO_STRING macro is useful for command line defined strings as the quotes
+// get stripped.
+#define TO_STRING_EXPAND(X) #X
+#define TO_STRING(X) TO_STRING_EXPAND(X)
+
+// Path to the Firebase config file to load.
+#ifdef FIREBASE_CONFIG
+#define FIREBASE_CONFIG_STRING TO_STRING(FIREBASE_CONFIG)
+#else
+#define FIREBASE_CONFIG_STRING ""
+#endif // FIREBASE_CONFIG
+
extern "C" int common_main(int argc, const char* argv[]);
static bool quit = false;
@@ -50,6 +69,10 @@ bool ProcessEvents(int msec) {
return quit;
}
+std::string PathForResource() {
+ return std::string();
+}
+
void LogMessage(const char* format, ...) {
va_list list;
va_start(list, format);
@@ -61,7 +84,22 @@ void LogMessage(const char* format, ...) {
WindowContext GetWindowContext() { return nullptr; }
+// Change the current working directory to the directory containing the
+// specified file.
+void ChangeToFileDirectory(const char* file_path) {
+ std::string path(file_path);
+ std::replace(path.begin(), path.end(), '\\', '/');
+ auto slash = path.rfind('/');
+ if (slash != std::string::npos) {
+ std::string directory = path.substr(0, slash);
+ if (!directory.empty()) chdir(directory.c_str());
+ }
+}
+
int main(int argc, const char* argv[]) {
+ ChangeToFileDirectory(
+ FIREBASE_CONFIG_STRING[0] != '\0' ?
+ FIREBASE_CONFIG_STRING : argv[0]); // NOLINT
#ifdef _WIN32
SetConsoleCtrlHandler((PHANDLER_ROUTINE)SignalHandler, TRUE);
#else
@@ -69,3 +107,19 @@ int main(int argc, const char* argv[]) {
#endif // _WIN32
return common_main(argc, argv);
}
+
+#if defined(_WIN32)
+// Returns the number of microseconds since the epoch.
+int64_t WinGetCurrentTimeInMicroseconds() {
+ FILETIME file_time;
+ GetSystemTimeAsFileTime(&file_time);
+
+ ULARGE_INTEGER now;
+ now.LowPart = file_time.dwLowDateTime;
+ now.HighPart = file_time.dwHighDateTime;
+
+ // Windows file time is expressed in 100s of nanoseconds.
+ // To convert to microseconds, multiply x10.
+ return now.QuadPart * 10LL;
+}
+#endif
diff --git a/functions/testapp/src/ios/ios_main.mm b/functions/testapp/src/ios/ios_main.mm
new file mode 100755
index 00000000..ee0c792c
--- /dev/null
+++ b/functions/testapp/src/ios/ios_main.mm
@@ -0,0 +1,127 @@
+// Copyright 2016 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#import
+
+#include
+
+#include "main.h"
+
+extern "C" int common_main(int argc, const char *argv[]);
+
+@interface AppDelegate : UIResponder
+
+@property(nonatomic, strong) UIWindow *window;
+
+@end
+
+@interface FTAViewController : UIViewController
+
+@end
+
+static int g_exit_status = 0;
+static bool g_shutdown = false;
+static NSCondition *g_shutdown_complete; // NOLINT
+static NSCondition *g_shutdown_signal; // NOLINT
+static UITextView *g_text_view; // NOLINT
+static UIView *g_parent_view; // NOLINT
+
+@implementation FTAViewController
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+ g_parent_view = self.view;
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+ const char *argv[] = {FIREBASE_TESTAPP_NAME};
+ [g_shutdown_signal lock];
+ g_exit_status = common_main(1, argv);
+ [g_shutdown_complete signal];
+ });
+}
+
+@end
+
+bool ProcessEvents(int msec) {
+ [g_shutdown_signal
+ waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:static_cast(msec) / 1000.0f]];
+ return g_shutdown;
+}
+
+std::string PathForResource() {
+ NSArray *paths =
+ NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
+ NSString *documentsDirectory = paths.firstObject;
+ // Force a trailing slash by removing any that exists, then appending another.
+ return std::string(
+ [[documentsDirectory stringByStandardizingPath] stringByAppendingString:@"/"].UTF8String);
+}
+
+WindowContext GetWindowContext() { return g_parent_view; }
+
+// Log a message that can be viewed in the console.
+void LogMessage(const char *format, ...) {
+ va_list args;
+ NSString *formatString = @(format);
+
+ va_start(args, format);
+ NSString *message = [[NSString alloc] initWithFormat:formatString arguments:args];
+ va_end(args);
+
+ NSLog(@"%@", message);
+ message = [message stringByAppendingString:@"\n"];
+
+ dispatch_async(dispatch_get_main_queue(), ^{
+ g_text_view.text = [g_text_view.text stringByAppendingString:message];
+ });
+}
+
+int main(int argc, char *argv[]) {
+ @autoreleasepool {
+ UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
+ }
+ return g_exit_status;
+}
+
+@implementation AppDelegate
+
+- (BOOL)application:(UIApplication *)application
+ didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
+ g_shutdown_complete = [[NSCondition alloc] init];
+ g_shutdown_signal = [[NSCondition alloc] init];
+ [g_shutdown_complete lock];
+
+ self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
+ FTAViewController *viewController = [[FTAViewController alloc] init];
+ self.window.rootViewController = viewController;
+ [self.window makeKeyAndVisible];
+
+ g_text_view = [[UITextView alloc] initWithFrame:viewController.view.bounds];
+
+ g_text_view.accessibilityIdentifier = @"Logger";
+ g_text_view.editable = NO;
+ g_text_view.scrollEnabled = YES;
+ g_text_view.userInteractionEnabled = YES;
+
+ [viewController.view addSubview:g_text_view];
+
+ return YES;
+}
+
+- (void)applicationWillTerminate:(UIApplication *)application {
+ g_shutdown = true;
+ [g_shutdown_signal signal];
+ [g_shutdown_complete wait];
+}
+
+@end
diff --git a/invites/testapp/src/main.h b/functions/testapp/src/main.h
similarity index 95%
rename from invites/testapp/src/main.h
rename to functions/testapp/src/main.h
index 2eda2c10..f5980902 100644
--- a/invites/testapp/src/main.h
+++ b/functions/testapp/src/main.h
@@ -15,6 +15,7 @@
#ifndef FIREBASE_TESTAPP_MAIN_H_ // NOLINT
#define FIREBASE_TESTAPP_MAIN_H_ // NOLINT
+#include
#if defined(__ANDROID__)
#include
#include
@@ -38,6 +39,9 @@ extern "C" void LogMessage(const char* format, ...);
// Returns true when an event requesting program-exit is received.
bool ProcessEvents(int msec);
+// Returns a path to a file suitable for the given platform.
+std::string PathForResource();
+
// WindowContext represents the handle to the parent window. It's type
// (and usage) vary based on the OS.
#if defined(__ANDROID__)
diff --git a/invites/testapp/testapp.xcodeproj/project.pbxproj b/functions/testapp/testapp.xcodeproj/project.pbxproj
similarity index 99%
rename from invites/testapp/testapp.xcodeproj/project.pbxproj
rename to functions/testapp/testapp.xcodeproj/project.pbxproj
index baf45c4c..5769362c 100644
--- a/invites/testapp/testapp.xcodeproj/project.pbxproj
+++ b/functions/testapp/testapp.xcodeproj/project.pbxproj
@@ -208,7 +208,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 8.4;
+ IPHONEOS_DEPLOYMENT_TARGET = 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/invites/testapp/testapp/Images.xcassets/AppIcon.appiconset/Contents.json b/functions/testapp/testapp/Images.xcassets/AppIcon.appiconset/Contents.json
similarity index 100%
rename from invites/testapp/testapp/Images.xcassets/AppIcon.appiconset/Contents.json
rename to functions/testapp/testapp/Images.xcassets/AppIcon.appiconset/Contents.json
diff --git a/functions/testapp/testapp/Images.xcassets/LaunchImage.launchimage/Contents.json b/functions/testapp/testapp/Images.xcassets/LaunchImage.launchimage/Contents.json
new file mode 100644
index 00000000..6f870a46
--- /dev/null
+++ b/functions/testapp/testapp/Images.xcassets/LaunchImage.launchimage/Contents.json
@@ -0,0 +1,51 @@
+{
+ "images" : [
+ {
+ "orientation" : "portrait",
+ "idiom" : "iphone",
+ "extent" : "full-screen",
+ "minimum-system-version" : "7.0",
+ "scale" : "2x"
+ },
+ {
+ "orientation" : "portrait",
+ "idiom" : "iphone",
+ "subtype" : "retina4",
+ "extent" : "full-screen",
+ "minimum-system-version" : "7.0",
+ "scale" : "2x"
+ },
+ {
+ "orientation" : "portrait",
+ "idiom" : "ipad",
+ "extent" : "full-screen",
+ "minimum-system-version" : "7.0",
+ "scale" : "1x"
+ },
+ {
+ "orientation" : "landscape",
+ "idiom" : "ipad",
+ "extent" : "full-screen",
+ "minimum-system-version" : "7.0",
+ "scale" : "1x"
+ },
+ {
+ "orientation" : "portrait",
+ "idiom" : "ipad",
+ "extent" : "full-screen",
+ "minimum-system-version" : "7.0",
+ "scale" : "2x"
+ },
+ {
+ "orientation" : "landscape",
+ "idiom" : "ipad",
+ "extent" : "full-screen",
+ "minimum-system-version" : "7.0",
+ "scale" : "2x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/functions/testapp/testapp/Info.plist b/functions/testapp/testapp/Info.plist
new file mode 100644
index 00000000..1b49a974
--- /dev/null
+++ b/functions/testapp/testapp/Info.plist
@@ -0,0 +1,39 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ com.google.firebase.cpp.functions.testapp
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ 1.0
+ CFBundleURLTypes
+
+
+ CFBundleTypeRole
+ Editor
+ CFBundleURLName
+ google
+ CFBundleURLSchemes
+
+ YOUR_REVERSED_CLIENT_ID
+
+
+
+ CFBundleVersion
+ 1
+ LSRequiresIPhoneOS
+
+ UILaunchStoryboardName
+ LaunchScreen
+
+
diff --git a/gma/testapp/AndroidManifest.xml b/gma/testapp/AndroidManifest.xml
new file mode 100644
index 00000000..f443dd14
--- /dev/null
+++ b/gma/testapp/AndroidManifest.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/gma/testapp/CMakeLists.txt b/gma/testapp/CMakeLists.txt
new file mode 100644
index 00000000..2132660c
--- /dev/null
+++ b/gma/testapp/CMakeLists.txt
@@ -0,0 +1,111 @@
+cmake_minimum_required(VERSION 2.8)
+
+# User settings for Firebase samples.
+# Path to Firebase SDK.
+# Try to read the path to the Firebase C++ SDK from an environment variable.
+if (NOT "$ENV{FIREBASE_CPP_SDK_DIR}" STREQUAL "")
+ set(DEFAULT_FIREBASE_CPP_SDK_DIR "$ENV{FIREBASE_CPP_SDK_DIR}")
+else()
+ set(DEFAULT_FIREBASE_CPP_SDK_DIR "firebase_cpp_sdk")
+endif()
+if ("${FIREBASE_CPP_SDK_DIR}" STREQUAL "")
+ set(FIREBASE_CPP_SDK_DIR ${DEFAULT_FIREBASE_CPP_SDK_DIR})
+endif()
+if(NOT EXISTS ${FIREBASE_CPP_SDK_DIR})
+ message(FATAL_ERROR "The Firebase C++ SDK directory does not exist: ${FIREBASE_CPP_SDK_DIR}. See the readme.md for more information")
+endif()
+
+# Sample source files.
+set(FIREBASE_SAMPLE_COMMON_SRCS
+ src/main.h
+ src/common_main.cc
+)
+
+# The include directory for the testapp.
+include_directories(src)
+
+# Sample uses some features that require C++ 11, such as lambdas.
+set (CMAKE_CXX_STANDARD 11)
+
+if(ANDROID)
+ # Build an Android application.
+
+ # Source files used for the Android build.
+ set(FIREBASE_SAMPLE_ANDROID_SRCS
+ src/android/android_main.cc
+ )
+
+ # Build native_app_glue as a static lib
+ add_library(native_app_glue STATIC
+ ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c)
+
+ # Export ANativeActivity_onCreate(),
+ # Refer to: https://github.com/android-ndk/ndk/issues/381.
+ set(CMAKE_SHARED_LINKER_FLAGS
+ "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate")
+
+ # Define the target as a shared library, as that is what gradle expects.
+ set(target_name "android_main")
+ add_library(${target_name} SHARED
+ ${FIREBASE_SAMPLE_ANDROID_SRCS}
+ ${FIREBASE_SAMPLE_COMMON_SRCS}
+ )
+
+ target_link_libraries(${target_name}
+ log android atomic native_app_glue
+ )
+
+ target_include_directories(${target_name} PRIVATE
+ ${ANDROID_NDK}/sources/android/native_app_glue)
+
+ set(ADDITIONAL_LIBS)
+else()
+ # Build a desktop application.
+
+ # Windows runtime mode, either MD or MT depending on whether you are using
+ # /MD or /MT. For more information see:
+ # https://msdn.microsoft.com/en-us/library/2kzt1wy3.aspx
+ set(MSVC_RUNTIME_MODE MD)
+
+ # Platform abstraction layer for the desktop sample.
+ set(FIREBASE_SAMPLE_DESKTOP_SRCS
+ src/desktop/desktop_main.cc
+ )
+
+ set(target_name "desktop_testapp")
+ add_executable(${target_name}
+ ${FIREBASE_SAMPLE_DESKTOP_SRCS}
+ ${FIREBASE_SAMPLE_COMMON_SRCS}
+ )
+
+ if(APPLE)
+ set(ADDITIONAL_LIBS pthread)
+ elseif(MSVC)
+ set(ADDITIONAL_LIBS)
+ else()
+ set(ADDITIONAL_LIBS pthread)
+ endif()
+
+ # If a config file is present, copy it into the binary location so that it's
+ # possible to create the default Firebase app.
+ set(FOUND_JSON_FILE FALSE)
+ foreach(config "google-services-desktop.json" "google-services.json")
+ if (EXISTS ${config})
+ add_custom_command(
+ TARGET ${target_name} POST_BUILD
+ COMMAND ${CMAKE_COMMAND} -E copy
+ ${config} $)
+ set(FOUND_JSON_FILE TRUE)
+ break()
+ endif()
+ endforeach()
+ if(NOT FOUND_JSON_FILE)
+ message(WARNING "Failed to find either google-services-desktop.json or google-services.json. See the readme.md for more information.")
+ endif()
+endif()
+
+# Add the Firebase libraries to the target using the function from the SDK.
+add_subdirectory(${FIREBASE_CPP_SDK_DIR} bin/ EXCLUDE_FROM_ALL)
+# Note that firebase_app needs to be last in the list.
+set(firebase_libs firebase_gma firebase_app)
+target_link_libraries(${target_name} "${firebase_libs}" ${ADDITIONAL_LIBS})
diff --git a/invites/testapp/LICENSE b/gma/testapp/LICENSE
similarity index 99%
rename from invites/testapp/LICENSE
rename to gma/testapp/LICENSE
index 25dd9993..d6456956 100644
--- a/invites/testapp/LICENSE
+++ b/gma/testapp/LICENSE
@@ -187,7 +187,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
- Copyright 2016 Google Inc.
+ Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/gma/testapp/LaunchScreen.storyboard b/gma/testapp/LaunchScreen.storyboard
new file mode 100644
index 00000000..673e0f7e
--- /dev/null
+++ b/gma/testapp/LaunchScreen.storyboard
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/gma/testapp/Podfile b/gma/testapp/Podfile
new file mode 100644
index 00000000..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/gma/testapp/build.gradle b/gma/testapp/build.gradle
new file mode 100644
index 00000000..4cf159a9
--- /dev/null
+++ b/gma/testapp/build.gradle
@@ -0,0 +1,79 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ mavenLocal()
+ maven { url 'https://maven.google.com' }
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:4.2.1'
+ classpath 'com.google.gms:google-services:4.0.1' }
+}
+
+allprojects {
+ repositories {
+ mavenLocal()
+ maven { url 'https://maven.google.com' }
+ jcenter()
+ }
+}
+
+apply plugin: 'com.android.application'
+
+android {
+ compileOptions {
+ sourceCompatibility 1.8
+ targetCompatibility 1.8
+ }
+
+ compileSdkVersion 34
+ ndkPath System.getenv('ANDROID_NDK_HOME')
+ buildToolsVersion '30.0.2'
+
+ sourceSets {
+ main {
+ jniLibs.srcDirs = ['libs']
+ manifest.srcFile 'AndroidManifest.xml'
+ java.srcDirs = ['src/android/java']
+ res.srcDirs = ['res']
+ }
+ }
+
+ defaultConfig {
+ applicationId 'com.google.android.admob.testapp'
+ minSdkVersion 23
+ targetSdkVersion 28
+ versionCode 1
+ versionName '1.0'
+ externalNativeBuild.cmake {
+ arguments "-DFIREBASE_CPP_SDK_DIR=$gradle.firebase_cpp_sdk_dir"
+ }
+ }
+ externalNativeBuild.cmake {
+ path 'CMakeLists.txt'
+ }
+ buildTypes {
+ release {
+ minifyEnabled true
+ proguardFile getDefaultProguardFile('proguard-android.txt')
+ proguardFile file('proguard.pro')
+ }
+ }
+ packagingOptions {
+ pickFirst 'META-INF/**/coroutines.pro'
+ }
+ lintOptions {
+ abortOnError false
+ checkReleaseBuilds false
+ }
+}
+
+apply from: "$gradle.firebase_cpp_sdk_dir/Android/firebase_dependencies.gradle"
+firebaseCpp.dependencies {
+ 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/gma/testapp/gradle/wrapper/gradle-wrapper.jar b/gma/testapp/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 00000000..8c0fb64a
Binary files /dev/null and b/gma/testapp/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gma/testapp/gradle/wrapper/gradle-wrapper.properties b/gma/testapp/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 00000000..65340c1b
--- /dev/null
+++ b/gma/testapp/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Mon Nov 27 14:03:45 PST 2017
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https://services.gradle.org/distributions/gradle-6.7.1-all.zip
diff --git a/gma/testapp/gradlew b/gma/testapp/gradlew
new file mode 100755
index 00000000..91a7e269
--- /dev/null
+++ b/gma/testapp/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+ [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/gma/testapp/gradlew.bat b/gma/testapp/gradlew.bat
new file mode 100644
index 00000000..8a0b282a
--- /dev/null
+++ b/gma/testapp/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/gma/testapp/proguard.pro b/gma/testapp/proguard.pro
new file mode 100644
index 00000000..54cd248b
--- /dev/null
+++ b/gma/testapp/proguard.pro
@@ -0,0 +1,2 @@
+-ignorewarnings
+-keep,includedescriptorclasses public class com.google.firebase.example.LoggingUtils { *; }
diff --git a/gma/testapp/readme.md b/gma/testapp/readme.md
new file mode 100644
index 00000000..4143cb7e
--- /dev/null
+++ b/gma/testapp/readme.md
@@ -0,0 +1,195 @@
+Firebase GMA Quickstart
+==============================
+
+The Firebase Google Mobile Ads Test Application (testapp) demonstrates
+loading and showing AdMob-served banners, interstitials and rewarded ads
+using the Firebase GMA C++ SDK. The application has no user interface and
+simply logs actions it's performing to the console while displaying the ads.
+
+Introduction
+------------
+
+- [Read more about Firebase GMA](https://firebase.google.com/docs/gma)
+
+Getting Started
+---------------
+
+### iOS
+ - Link your iOS app to the Firebase libraries.
+ - Get CocoaPods version 1 or later by running,
+ ```
+ sudo gem install cocoapods --pre
+ ```
+ - From the testapp directory, install the CocoaPods listed in the Podfile
+ by running,
+ ```
+ pod install
+ ```
+ - Open the generated Xcode workspace (which now has the CocoaPods),
+ ```
+ open testapp.xcworkspace
+ ```
+ - For further details please refer to the
+ [general instructions for setting up an iOS app with Firebase](https://firebase.google.com/docs/ios/setup).
+ - Register your iOS app with Firebase.
+ - Create a new app on the [Firebase console](https://firebase.google.com/console/),
+ and attach your iOS app to it.
+ - You can use "com.google.ios.admob.testapp" as the iOS Bundle ID
+ while you're testing. You can omit App Store ID while testing.
+ - Download the Firebase C++ SDK linked from
+ [https://firebase.google.com/docs/cpp/setup](https://firebase.google.com/docs/cpp/setup)
+ and unzip it to a directory of your choice.
+ - Add the following frameworks from the Firebase C++ SDK to the project:
+ - frameworks/ios/universal/firebase.framework
+ - frameworks/ios/universal/firebase_gma.framework
+ - You will need to either,
+ 1. Check "Copy items if needed" when adding the frameworks, or
+ 2. Add the framework path in "Framework Search Paths"
+ - For example, if you downloaded the Firebase C++ SDK to
+ `/Users/me/firebase_cpp_sdk`,
+ then you would add the path
+ `/Users/me/firebase_cpp_sdk/frameworks/ios/universal`.
+ - To add the path, in Xcode, select your project in the project
+ navigator, then select your target in the main window.
+ Select the "Build Settings" tab, and click "All" to see all
+ the build settings. Scroll down to "Search Paths", and add
+ your path to "Framework Search Paths".
+ - Update the AdMob App ID:
+ - In the `testapp/Info.plist`, update `GADApplicationIdentifier` with the
+ app ID for your iOS app, replacing 'YOUR_IOS_ADMOB_APP_ID'.
+ - For more information, see
+ [Update your Info.plist](https://developers.google.com/admob/ios/quick-start#manual_download)
+ - In Xcode, build & run the sample on an iOS device or simulator.
+ - The testapp displays a banner ad, an interstitial ad and a rewarded ad. You must
+ dismiss each ad to see the next.
+ - The output of the app can be viewed onscreen or via the console. To view
+ the console in Xcode, select "View --> Debug Area --> Activate Console"
+ from the menu.
+
+### Android
+ - Register your Android app with Firebase.
+ - Create a new app on the [Firebase console](https://firebase.google.com/console/),
+ and attach your Android app to it.
+ - You can use "com.google.android.admob.testapp" as the Package Name
+ while you're testing.
+ - To [generate a SHA1](https://developers.google.com/android/guides/client-auth)
+ run this command on Mac and Linux,
+ ```
+ keytool -exportcert -list -v -alias androiddebugkey -keystore ~/.android/debug.keystore
+ ```
+ or this command on Windows,
+ ```
+ keytool -exportcert -list -v -alias androiddebugkey -keystore %USERPROFILE%\.android\debug.keystore
+ ```
+ - If keytool reports that you do not have a debug.keystore, you can
+ [create one with](http://developer.android.com/tools/publishing/app-signing.html#signing-manually),
+ ```
+ keytool -genkey -v -keystore ~/.android/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"
+ ```
+ - Add the `google-services.json` file that you downloaded from Firebase
+ console to the root directory of testapp. This file identifies your
+ Android app to the Firebase backend.
+ - For further details please refer to the
+ [general instructions for setting up an Android app with Firebase](https://firebase.google.com/docs/android/setup).
+ - Download the Firebase C++ SDK linked from
+ [https://firebase.google.com/docs/cpp/setup](https://firebase.google.com/docs/cpp/setup)
+ and unzip it to a directory of your choice.
+ - Configure the location of the Firebase C++ SDK by setting the
+ firebase\_cpp\_sdk.dir Gradle property to the SDK install directory.
+ For example, in the project directory:
+ ```
+ echo "systemProp.firebase\_cpp\_sdk.dir=/User/$USER/firebase\_cpp\_sdk" >> gradle.properties
+ ```
+ - Ensure the Android SDK and NDK locations are set in Android Studio.
+ - From the Android Studio launch menu, go to `File/Project Structure...` or
+ `Configure/Project Defaults/Project Structure...`
+ (Shortcut: Control + Alt + Shift + S on windows, Command + ";" on a mac)
+ and download the SDK and NDK if the locations are not yet set.
+ - Open *build.gradle* in Android Studio.
+ - From the Android Studio launch menu, "Open an existing Android Studio
+ project", and select `build.gradle`.
+ - Install the SDK Platforms that Android Studio reports missing.
+ - Update the GMA App ID:
+ - In the `AndroidManifest.xml`, update
+ `com.google.android.gms.ads.APPLICATION_ID` with the same app ID,
+ replacing 'YOUR_ANDROID_ADMOB_APP_ID'.
+ - For more information, see
+ [Update your AndroidManifest.xml](https://developers.google.com/admob/android/quick-start#update_your_androidmanifestxml)
+ - Build the testapp and run it on an Android device or emulator.
+ - The testapp will initialize the GMA SDK, then load and display a test
+ banner ad, interstitial ad and rewarded ad.
+ - Tapping on an ad to verify the clickthrough process is possible, and the
+ test app will wait for each ad to be dismissed.
+ - While this is happening, information from the device log will be written
+ to an onscreen TextView.
+ - Logcat can also be used as normal.
+
+### Desktop
+ - Note: the testapp has no user interface, but the output can be viewed via
+ the console. 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.
+ - If you have an Android project, add the `google-services.json` file that
+ you downloaded from the Firebase console to the root directory of the
+ testapp.
+ - If you have an iOS project, and don't wish to use an Android project,
+ you can use the Python script `generate_xml_from_google_services_json.py --plist`,
+ located in the Firebase C++ SDK, to convert your `GoogleService-Info.plist`
+ file into a `google-services-desktop.json` file, which can then be
+ placed in the root directory of the testapp.
+ - Download the Firebase C++ SDK linked from
+ [https://firebase.google.com/docs/cpp/setup](https://firebase.google.com/docs/cpp/setup)
+ and unzip it to a directory of your choice.
+ - Configure the testapp with the location of the Firebase C++ SDK.
+ This can be done a couple different ways:
+ - When invoking cmake, pass in the location with
+ -DFIREBASE_CPP_SDK_DIR=/path/to/firebase_cpp_sdk.
+ - Set an environment variable for FIREBASE_CPP_SDK_DIR to the path to use.
+ - Edit the CMakeLists.txt file, changing the FIREBASE_CPP_SDK_DIR path
+ to the appropriate location.
+ - From the testapp directory, generate the build files by running,
+ ```
+ cmake .
+ ```
+ If you want to use XCode, you can use -G"Xcode" to generate the project.
+ Similarly, to use Visual Studio, -G"Visual Studio 15 2017". For more
+ information, see
+ [CMake generators](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html).
+ - Build the testapp, by either opening the generated project file based on
+ the platform, or running,
+ ```
+ cmake --build .
+ ```
+ - Execute the testapp by running,
+ ```
+ ./desktop_testapp
+ ```
+ Note that the executable might be under another directory, such as Debug.
+
+Support
+-------
+
+[https://firebase.google.com/support/](https://firebase.google.com/support/)
+
+License
+-------
+
+Copyright 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
+additional information regarding copyright ownership. The ASF licenses this
+file to you under the Apache License, Version 2.0 (the "License"); you may not
+use this file except in compliance with the License. You may obtain a copy of
+the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+License for the specific language governing permissions and limitations under
+the License.
diff --git a/admob/testapp/res/values/strings.xml b/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/gma/testapp/settings.gradle b/gma/testapp/settings.gradle
new file mode 100644
index 00000000..2a543b93
--- /dev/null
+++ b/gma/testapp/settings.gradle
@@ -0,0 +1,36 @@
+// Copyright 2018 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+def firebase_cpp_sdk_dir = System.getProperty('firebase_cpp_sdk.dir')
+if (firebase_cpp_sdk_dir == null || firebase_cpp_sdk_dir.isEmpty()) {
+ firebase_cpp_sdk_dir = System.getenv('FIREBASE_CPP_SDK_DIR')
+ if (firebase_cpp_sdk_dir == null || firebase_cpp_sdk_dir.isEmpty()) {
+ if ((new File('firebase_cpp_sdk')).exists()) {
+ firebase_cpp_sdk_dir = 'firebase_cpp_sdk'
+ } else {
+ throw new StopActionException(
+ 'firebase_cpp_sdk.dir property or the FIREBASE_CPP_SDK_DIR ' +
+ 'environment variable must be set to reference the Firebase C++ ' +
+ 'SDK install directory. This is used to configure static library ' +
+ 'and C/C++ include paths for the SDK.')
+ }
+ }
+}
+if (!(new File(firebase_cpp_sdk_dir)).exists()) {
+ throw new StopActionException(
+ sprintf('Firebase C++ SDK directory %s does not exist',
+ firebase_cpp_sdk_dir))
+}
+gradle.ext.firebase_cpp_sdk_dir = "$firebase_cpp_sdk_dir"
+includeBuild "$firebase_cpp_sdk_dir"
\ No newline at end of file
diff --git a/gma/testapp/src/android/android_main.cc b/gma/testapp/src/android/android_main.cc
new file mode 100644
index 00000000..7a6de455
--- /dev/null
+++ b/gma/testapp/src/android/android_main.cc
@@ -0,0 +1,256 @@
+// Copyright 2016 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#include "main.h" // NOLINT
+
+// This implementation is derived from http://github.com/google/fplutil
+
+extern "C" int common_main(int argc, const char *argv[]);
+
+static struct android_app *g_app_state = nullptr;
+static bool g_destroy_requested = false;
+static bool g_started = false;
+static bool g_restarted = false;
+static pthread_mutex_t g_started_mutex;
+
+// Handle state changes from via native app glue.
+static void OnAppCmd(struct android_app *app, int32_t cmd) {
+ g_destroy_requested |= cmd == APP_CMD_DESTROY;
+}
+
+// Process events pending on the main thread.
+// Returns true when the app receives an event requesting exit.
+bool ProcessEvents(int msec) {
+ struct android_poll_source *source = nullptr;
+ int events;
+ int looperId = ALooper_pollAll(msec, nullptr, &events,
+ reinterpret_cast(&source));
+ if (looperId >= 0 && source) {
+ source->process(g_app_state, source);
+ }
+ return g_destroy_requested | g_restarted;
+}
+
+// Get the activity.
+jobject GetActivity() { return g_app_state->activity->clazz; }
+
+// Get the window context. For Android, it's a jobject pointing to the Activity.
+jobject GetWindowContext() { return g_app_state->activity->clazz; }
+
+// Find a class, attempting to load the class if it's not found.
+jclass FindClass(JNIEnv *env, jobject activity_object, const char *class_name) {
+ jclass class_object = env->FindClass(class_name);
+ if (env->ExceptionCheck()) {
+ env->ExceptionClear();
+ // If the class isn't found it's possible NativeActivity is being used by
+ // the application which means the class path is set to only load system
+ // classes. The following falls back to loading the class using the
+ // Activity before retrieving a reference to it.
+ jclass activity_class = env->FindClass("android/app/Activity");
+ jmethodID activity_get_class_loader = env->GetMethodID(
+ activity_class, "getClassLoader", "()Ljava/lang/ClassLoader;");
+
+ jobject class_loader_object =
+ env->CallObjectMethod(activity_object, activity_get_class_loader);
+
+ jclass class_loader_class = env->FindClass("java/lang/ClassLoader");
+ jmethodID class_loader_load_class =
+ env->GetMethodID(class_loader_class, "loadClass",
+ "(Ljava/lang/String;)Ljava/lang/Class;");
+ jstring class_name_object = env->NewStringUTF(class_name);
+
+ class_object = static_cast(env->CallObjectMethod(
+ class_loader_object, class_loader_load_class, class_name_object));
+
+ if (env->ExceptionCheck()) {
+ env->ExceptionClear();
+ class_object = nullptr;
+ }
+ env->DeleteLocalRef(class_name_object);
+ env->DeleteLocalRef(class_loader_object);
+ }
+ return class_object;
+}
+
+// Vars that we need available for appending text to the log window:
+class LoggingUtilsData {
+public:
+ LoggingUtilsData()
+ : logging_utils_class_(nullptr), logging_utils_add_log_text_(0),
+ logging_utils_init_log_window_(0) {}
+
+ ~LoggingUtilsData() {
+ JNIEnv *env = GetJniEnv();
+ assert(env);
+ if (logging_utils_class_) {
+ env->DeleteGlobalRef(logging_utils_class_);
+ }
+ }
+
+ void Init() {
+ JNIEnv *env = GetJniEnv();
+ assert(env);
+
+ jclass logging_utils_class = FindClass(
+ env, GetActivity(), "com/google/firebase/example/LoggingUtils");
+ assert(logging_utils_class != 0);
+
+ // Need to store as global references so it don't get moved during garbage
+ // collection.
+ logging_utils_class_ =
+ static_cast(env->NewGlobalRef(logging_utils_class));
+ env->DeleteLocalRef(logging_utils_class);
+
+ logging_utils_init_log_window_ = env->GetStaticMethodID(
+ logging_utils_class_, "initLogWindow", "(Landroid/app/Activity;)V");
+ logging_utils_add_log_text_ = env->GetStaticMethodID(
+ logging_utils_class_, "addLogText", "(Ljava/lang/String;)V");
+
+ env->CallStaticVoidMethod(logging_utils_class_,
+ logging_utils_init_log_window_, GetActivity());
+ }
+
+ void AppendText(const char *text) {
+ if (logging_utils_class_ == 0)
+ return; // haven't been initted yet
+ JNIEnv *env = GetJniEnv();
+ assert(env);
+ jstring text_string = env->NewStringUTF(text);
+ env->CallStaticVoidMethod(logging_utils_class_, logging_utils_add_log_text_,
+ text_string);
+ env->DeleteLocalRef(text_string);
+ }
+
+private:
+ jclass logging_utils_class_;
+ jmethodID logging_utils_add_log_text_;
+ jmethodID logging_utils_init_log_window_;
+};
+
+LoggingUtilsData *g_logging_utils_data;
+
+// Checks if a JNI exception has happened, and if so, logs it to the console.
+void CheckJNIException() {
+ JNIEnv *env = GetJniEnv();
+ if (env->ExceptionCheck()) {
+ // Get the exception text.
+ jthrowable exception = env->ExceptionOccurred();
+ env->ExceptionClear();
+
+ // Convert the exception to a string.
+ jclass object_class = env->FindClass("java/lang/Object");
+ jmethodID toString =
+ env->GetMethodID(object_class, "toString", "()Ljava/lang/String;");
+ jstring s = (jstring)env->CallObjectMethod(exception, toString);
+ const char *exception_text = env->GetStringUTFChars(s, nullptr);
+
+ // Log the exception text.
+ __android_log_print(ANDROID_LOG_INFO, FIREBASE_TESTAPP_NAME,
+ "-------------------JNI exception:");
+ __android_log_print(ANDROID_LOG_INFO, FIREBASE_TESTAPP_NAME, "%s",
+ exception_text);
+ __android_log_print(ANDROID_LOG_INFO, FIREBASE_TESTAPP_NAME,
+ "-------------------");
+
+ // Also, assert fail.
+ assert(false);
+
+ // In the event we didn't assert fail, clean up.
+ env->ReleaseStringUTFChars(s, exception_text);
+ env->DeleteLocalRef(s);
+ env->DeleteLocalRef(exception);
+ }
+}
+
+// Log a message that can be viewed in "adb logcat".
+void LogMessage(const char *format, ...) {
+ static const int kLineBufferSize = 100;
+ char buffer[kLineBufferSize + 2];
+
+ va_list list;
+ va_start(list, format);
+ int string_len = vsnprintf(buffer, kLineBufferSize, format, list);
+ string_len = string_len < kLineBufferSize ? string_len : kLineBufferSize;
+ // append a linebreak to the buffer:
+ buffer[string_len] = '\n';
+ buffer[string_len + 1] = '\0';
+
+ __android_log_vprint(ANDROID_LOG_INFO, FIREBASE_TESTAPP_NAME, format, list);
+ g_logging_utils_data->AppendText(buffer);
+ CheckJNIException();
+ va_end(list);
+}
+
+// Get the JNI environment.
+JNIEnv *GetJniEnv() {
+ JavaVM *vm = g_app_state->activity->vm;
+ JNIEnv *env;
+ jint result = vm->AttachCurrentThread(&env, nullptr);
+ return result == JNI_OK ? env : nullptr;
+}
+
+// Execute common_main(), flush pending events and finish the activity.
+extern "C" void android_main(struct android_app *state) {
+ // native_app_glue spawns a new thread, calling android_main() when the
+ // activity onStart() or onRestart() methods are called. This code handles
+ // the case where we're re-entering this method on a different thread by
+ // signalling the existing thread to exit, waiting for it to complete before
+ // reinitializing the application.
+ if (g_started) {
+ g_restarted = true;
+ // Wait for the existing thread to exit.
+ pthread_mutex_lock(&g_started_mutex);
+ pthread_mutex_unlock(&g_started_mutex);
+ } else {
+ g_started_mutex = PTHREAD_MUTEX_INITIALIZER;
+ }
+ pthread_mutex_lock(&g_started_mutex);
+ g_started = true;
+
+ // Save native app glue state and setup a callback to track the state.
+ g_destroy_requested = false;
+ g_app_state = state;
+ g_app_state->onAppCmd = OnAppCmd;
+
+ // Create the logging display.
+ g_logging_utils_data = new LoggingUtilsData();
+ g_logging_utils_data->Init();
+
+ // Execute cross platform entry point.
+ static const char *argv[] = {FIREBASE_TESTAPP_NAME};
+ int return_value = common_main(1, argv);
+ (void)return_value; // Ignore the return value.
+ ProcessEvents(10);
+
+ // Clean up logging display.
+ delete g_logging_utils_data;
+ g_logging_utils_data = nullptr;
+
+ // Finish the activity.
+ if (!g_restarted)
+ ANativeActivity_finish(state->activity);
+
+ g_app_state->activity->vm->DetachCurrentThread();
+ g_started = false;
+ g_restarted = false;
+ pthread_mutex_unlock(&g_started_mutex);
+}
diff --git a/invites/testapp/src/android/java/com/google/firebase/example/LoggingUtils.java b/gma/testapp/src/android/java/com/google/firebase/example/LoggingUtils.java
similarity index 91%
rename from invites/testapp/src/android/java/com/google/firebase/example/LoggingUtils.java
rename to gma/testapp/src/android/java/com/google/firebase/example/LoggingUtils.java
index acbd8d3e..3cb37ecf 100644
--- a/invites/testapp/src/android/java/com/google/firebase/example/LoggingUtils.java
+++ b/gma/testapp/src/android/java/com/google/firebase/example/LoggingUtils.java
@@ -33,6 +33,7 @@ public static void initLogWindow(Activity activity) {
LinearLayout linearLayout = new LinearLayout(activity);
ScrollView scrollView = new ScrollView(activity);
TextView textView = new TextView(activity);
+ textView.setTag("Logger");
linearLayout.addView(scrollView);
scrollView.addView(textView);
Window window = activity.getWindow();
@@ -43,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/gma/testapp/src/desktop/desktop_main.cc b/gma/testapp/src/desktop/desktop_main.cc
new file mode 100644
index 00000000..0a318786
--- /dev/null
+++ b/gma/testapp/src/desktop/desktop_main.cc
@@ -0,0 +1,124 @@
+// Copyright 2016 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include
+#include
+#include
+
+#ifdef _WIN32
+#include