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 1b4457fc..00000000 --- a/admob/testapp/build.gradle +++ /dev/null @@ -1,120 +0,0 @@ -// Top-level build file where you can add configuration options common to all sub-projects/modules. -buildscript { - repositories { - mavenLocal() - maven { url 'https://maven.google.com' } - jcenter() - } - dependencies { - classpath 'com.android.tools.build:gradle:2.1.2' - classpath 'com.google.gms:google-services:3.0.0' - } -} - -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' - - 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") - proguardFile file('proguard.pro') - } - } -} - -dependencies { - compile 'com.google.firebase:firebase-ads:11.2.0' - compile 'com.google.android.gms:play-services-base:11.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/src/common_main.cc b/admob/testapp/src/common_main.cc deleted file mode 100644 index ce7af9c0..00000000 --- a/admob/testapp/src/common_main.cc +++ /dev/null @@ -1,488 +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 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* kNativeExpressAdUnit = "ca-app-pub-3940256099942544/1072772517"; -const char* kRewardedVideoAdUnit = "ca-app-pub-3940256099942544/2888167318"; -#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 = "ca-app-pub-3940256099942544/6386090517"; -#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()); - } - - 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 6b7f7c57..42939198 100644 --- a/analytics/testapp/build.gradle +++ b/analytics/testapp/build.gradle @@ -6,8 +6,8 @@ buildscript { 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' } } @@ -21,100 +21,56 @@ allprojects { 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:11.2.0' - compile 'com.google.android.gms:play-services-base:11.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/readme.md b/analytics/testapp/readme.md index 98d127d1..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,13 +90,13 @@ 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 `File/Project Structure...` or @@ -113,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 ------- 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 909baae4..eb739709 100644 --- a/auth/testapp/build.gradle +++ b/auth/testapp/build.gradle @@ -6,8 +6,8 @@ buildscript { 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' } } @@ -21,100 +21,57 @@ allprojects { 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:11.2.0' - compile 'com.google.android.gms:play-services-base:11.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/readme.md b/auth/testapp/readme.md index de8084dd..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,13 +119,13 @@ 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 `File/Project Structure...` or @@ -132,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 ------- 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/common_main.cc b/auth/testapp/src/common_main.cc index 79f0227e..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,20 +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; @@ -48,6 +62,7 @@ static const char kCustomEmail[] = "custom.email@example.com"; static const char kCustomPassword[] = "CustomPasswordGoesHere"; // Constants used during tests. +static const char kTestNonceBad[] = "testBadNonce"; static const char kTestPassword[] = "testEmailPassword123"; static const char kTestEmailBad[] = "bad.test.email@example.com"; static const char kTestPasswordBad[] = "badTestPassword"; @@ -55,9 +70,10 @@ 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 = 6000; +static const int kPhoneAuthCodeSendWaitMs = 600000; static const int kPhoneAuthCompletionWaitMs = 8000; static const int kPhoneAuthTimeoutMs = 0; @@ -71,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) { @@ -104,19 +120,38 @@ 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()); } return false; @@ -125,7 +160,7 @@ static bool WaitForSignInFuture(Future sign_in_future, const char* fn, // Wait for the current user to sign out. Typically you should use the // state listener to determine whether the user has signed out. static bool WaitForSignOut(firebase::auth::Auth* auth) { - while (auth->current_user() != nullptr) { + while (!auth->current_user().is_valid()) { if (ProcessEvents(100)) return true; } // Wait - hopefully - long enough for listeners to be signalled. @@ -167,14 +202,81 @@ 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) { // NOLINT num_state_changes_++; - LogMessage("OnAuthStateChanged User %p (state changes %d)", - auth->current_user(), num_state_changes_); + LogMessage("OnAuthStateChanged User %s (state changes %d)", + auth->current_user().uid().c_str(), num_state_changes_); } void CompleteTest(const char* test_name, int expected_state_changes) { @@ -201,8 +303,8 @@ class IdTokenChangeCounter : public firebase::auth::IdTokenListener { virtual void OnIdTokenChanged(Auth* auth) { // NOLINT num_token_changes_++; - LogMessage("OnIdTokenChanged User %p (token changes %d)", - auth->current_user(), num_token_changes_); + LogMessage("OnIdTokenChanged User %s (token changes %d)", + auth->current_user().uid().c_str(), num_token_changes_); } void CompleteTest(const char* test_name, int token_changes) { @@ -230,7 +332,6 @@ class UserLogin { : auth_(auth), email_(email), password_(password), - user_(nullptr), log_errors_(true) {} explicit UserLogin(Auth* auth) : auth_(auth) { @@ -239,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; } @@ -288,7 +389,7 @@ class UserLogin { Auth* auth_; std::string email_; std::string password_; - User* user_; + User user_; bool log_errors_; }; @@ -300,7 +401,7 @@ class PhoneListener : public PhoneAuthProvider::Listener { num_calls_on_code_sent_(0), num_calls_on_code_auto_retrieval_time_out_(0) {} - void OnVerificationCompleted(Credential /*credential*/) override { + void OnVerificationCompleted(PhoneAuthCredential /*credential*/) override { LogMessage("PhoneListener: successful automatic verification."); num_calls_on_verification_complete_++; } @@ -392,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(); } @@ -420,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 " @@ -429,11 +532,11 @@ extern "C" int common_main(int argc, const char* argv[]) { // Test SignOut() after signed in with email and password. if (sign_in_future.status() == ::firebase::kFutureStatusComplete) { auth->SignOut(); - if (auth->current_user() != nullptr) { + if (auth->current_user().is_valid()) { LogMessage( - "ERROR: current_user() returning %x instead of nullptr after " + "ERROR: current_user() returning %s instead of nullptr after " "SignOut()", - auth->current_user()); + auth->current_user().uid().c_str()); } } } @@ -447,8 +550,9 @@ extern "C" int common_main(int argc, const char* argv[]) { // Test notification on registration. auth->AddAuthStateListener(&counter); auth->AddIdTokenListener(&token_counter); - counter.CompleteTest("registration", 0); - token_counter.CompleteTest("registration", 0); + // Expect notification immediately after registration. + counter.CompleteTest("registration", 1); + token_counter.CompleteTest("registration", 1); // Test notification on SignOut(), when already signed-out. auth->SignOut(); @@ -456,35 +560,37 @@ extern "C" int common_main(int argc, const char* argv[]) { token_counter.CompleteTest("SignOut() when already signed-out", 0); // Test notification on SignIn(). - Future sign_in_future = auth->SignInAnonymously(); + Future sign_in_future = auth->SignInAnonymously(); WaitForSignInFuture(sign_in_future, "Auth::SignInAnonymously()", kAuthErrorNone, auth); // Notified when the user is about to change and after the user has // changed. - counter.CompleteTest("SignInAnonymously()", 2, 4); - token_counter.CompleteTest("SignInAnonymously()", 2, 5); + counter.CompleteTest("SignInAnonymously()", 1, 4); + token_counter.CompleteTest("SignInAnonymously()", 1, 5); // Refresh the token. - if (auth->current_user() != nullptr) { - Future token_future = auth->current_user()->GetToken(true); + if (auth->current_user().is_valid()) { + Future token_future = auth->current_user().GetToken(true); WaitForFuture(token_future, "GetToken()", kAuthErrorNone); counter.CompleteTest("GetToken()", 0); token_counter.CompleteTest("GetToken()", 1); } // Test notification on SignOut(), when signed-in. - LogMessage("Current user %p", auth->current_user()); // DEBUG + LogMessage("Current user %s", auth->current_user().uid().c_str()); // DEBUG auth->SignOut(); // Wait for the sign out to complete. WaitForSignOut(auth); counter.CompleteTest("SignOut()", 1); token_counter.CompleteTest("SignOut()", 1); - LogMessage("Current user %p", auth->current_user()); // DEBUG + LogMessage("Current user %s", auth->current_user().uid().c_str()); // DEBUG auth->RemoveAuthStateListener(&counter); auth->RemoveIdTokenListener(&token_counter); } + // 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 @@ -496,8 +602,10 @@ extern "C" int common_main(int argc, const char* argv[]) { "Phone Number", "Please enter your phone number", "+12345678900"); PhoneListener listener; PhoneAuthProvider& phone_provider = PhoneAuthProvider::GetInstance(auth); - phone_provider.VerifyPhoneNumber(phone_number.c_str(), kPhoneAuthTimeoutMs, - nullptr, &listener); + PhoneAuthOptions options; + options.phone_number = phone_number; + options.timeout_milliseconds = kPhoneAuthTimeoutMs; + phone_provider.VerifyPhoneNumber(options, &listener); // Wait for OnCodeSent() callback. int wait_ms = 0; @@ -509,7 +617,8 @@ extern "C" int common_main(int argc, const char* argv[]) { wait_ms += kWaitIntervalMs; LogMessage("."); } - if (wait_ms > kPhoneAuthCodeSendWaitMs) { + if (wait_ms > kPhoneAuthCodeSendWaitMs || + listener.num_calls_on_verification_failed()) { LogMessage("ERROR: SMS with verification code not sent."); } else { LogMessage("SMS verification code sent."); @@ -528,30 +637,41 @@ extern "C" int common_main(int argc, const char* argv[]) { LogMessage("."); } if (listener.num_calls_on_code_auto_retrieval_time_out() > 0) { - const Credential phone_credential = phone_provider.GetCredential( + const PhoneAuthCredential phone_credential = phone_provider.GetCredential( listener.verification_id().c_str(), verification_code.c_str()); - Future phone_future = + Futurephone_future = auth->SignInWithCredential(phone_credential); WaitForSignInFuture(phone_future, "Auth::SignInWithCredential() phone credential", kAuthErrorNone, auth); + if (phone_future.error() == kAuthErrorNone) { + User user = *phone_future.result(); + Future update_future = + user.UpdatePhoneNumberCredential(phone_credential); + 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", @@ -560,11 +680,11 @@ extern "C" int common_main(int argc, const char* argv[]) { // Test SignOut() after signed in anonymously. if (sign_in_future.status() == ::firebase::kFutureStatusComplete) { auth->SignOut(); - if (auth->current_user() != nullptr) { + if (auth->current_user().is_valid()) { LogMessage( - "ERROR: current_user() returning %x instead of nullptr after " + "ERROR: current_user() returning valid user %s instead of invalid user after " "SignOut()", - auth->current_user()); + auth->current_user().uid().c_str()); } } } @@ -593,7 +713,7 @@ extern "C" int common_main(int argc, const char* argv[]) { // Test Auth::SignInWithEmailAndPassword(). // Sign in with email and password that have already been registered. { - Future sign_in_future = auth->SignInWithEmailAndPassword( + Future sign_in_future = auth->SignInWithEmailAndPassword( user_login.email(), user_login.password()); WaitForSignInFuture( sign_in_future, @@ -606,42 +726,106 @@ extern "C" int common_main(int argc, const char* argv[]) { // Test SignOut() after signed in with email and password. if (sign_in_future.status() == ::firebase::kFutureStatusComplete) { auth->SignOut(); - if (auth->current_user() != nullptr) { + if (auth->current_user().is_valid()) { LogMessage( - "ERROR: current_user() returning %x instead of nullptr after " - "SignOut()", - auth->current_user()); + "ERROR: current_user() returning valid user %s instead of invalid user after " + "SignOut()", auth->current_user().uid().c_str()); } } } + // Test User::UpdateUserProfile + { + Future sign_in_future = auth->SignInWithEmailAndPassword( + 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 == @@ -653,74 +837,179 @@ extern "C" int common_main(int argc, const char* argv[]) { { Credential email_cred_ok = EmailAuthProvider::GetCredential( user_login.email(), user_login.password()); - Future sign_in_cred_ok = + Futuresign_in_cred_ok = auth->SignInWithCredential(email_cred_ok); WaitForSignInFuture(sign_in_cred_ok, "Auth::SignInWithCredential() existing email", kAuthErrorNone, auth); - ExpectTrue("SignInWithCredentialLastResult matches returned Future", - sign_in_cred_ok == auth->SignInWithCredentialLastResult()); + } + + // Test Auth::SignInAndRetrieveDataWithCredential using email & password. + // 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); - 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); + } + +#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", - kAuthErrorFailure, auth); + kAuthErrorInvalidCredential, auth); + } + + // Construct OAuthCredential with nonce & access token. + { + Credential nonce_credential_good = + OAuthProvider::GetCredential(kTestIdProviderIdBad, kTestIdTokenBad, + kTestNonceBad, kTestAccessTokenBad); + } + + // Construct OAuthCredential with nonce, null access token. + { + Credential nonce_credential_good = OAuthProvider::GetCredential( + kTestIdProviderIdBad, kTestIdTokenBad, kTestNonceBad, + /*access_token=*/nullptr); } // Use bad OAuth credentials. Should fail. { Credential oauth_cred_bad = OAuthProvider::GetCredential( kTestIdProviderIdBad, kTestIdTokenBad, kTestAccessTokenBad); - Future oauth_bad = auth->SignInWithCredential(oauth_cred_bad); + Futureoauth_bad = auth->SignInWithCredential(oauth_cred_bad); + WaitForSignInFuture( + oauth_bad, "Auth::SignInWithCredential() bad OAuth credentials", + kAuthErrorFailure, auth); + } + + // 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); @@ -745,69 +1034,74 @@ 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()); + 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); + Future link_future = + anonymous_user.LinkWithCredential(user_cred); WaitForSignInFuture(link_future, "User::LinkWithCredential()", kAuthErrorNone, auth); } // Test User::LinkWithCredential(), linking with same email & password. { - Future link_future = - anonymous_user->LinkWithCredential(user_cred); + Future link_future = + anonymous_user.LinkWithCredential(user_cred); WaitForSignInFuture(link_future, "User::LinkWithCredential() again", - kAuthErrorNone, auth); + ::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(); + const User pre_link_user = auth->current_user(); ExpectTrue("Test precondition requires active user", - pre_link_user != nullptr); + pre_link_user.is_valid()); Credential twitter_cred_bad = TwitterAuthProvider::GetCredential( kTestIdTokenBad, kTestAccessTokenBad); - Future link_bad_future = - anonymous_user->LinkWithCredential(twitter_cred_bad); + Future link_bad_future = + anonymous_user.LinkWithCredential(twitter_cred_bad); WaitForFuture(link_bad_future, "User::LinkWithCredential() with bad credential", - kAuthErrorFailure); + kAuthErrorInvalidCredential); ExpectTrue("Linking maintains user", auth->current_user() == pre_link_user); } @@ -815,16 +1109,16 @@ extern "C" int common_main(int argc, const char* argv[]) { // Test Auth::SignInWithCredential(), signing in with bad credential. // Call should fail, and Auth's current user should be maintained. { - const User* pre_signin_user = auth->current_user(); + const User pre_signin_user = auth->current_user(); ExpectTrue("Test precondition requires active user", - pre_signin_user != nullptr); + pre_signin_user.is_valid()); Credential twitter_cred_bad = TwitterAuthProvider::GetCredential( kTestIdTokenBad, kTestAccessTokenBad); - Future signin_bad_future = + Futuresignin_bad_future = auth->SignInWithCredential(twitter_cred_bad); WaitForFuture(signin_bad_future, "Auth::SignInWithCredential() with bad credential", - kAuthErrorFailure, auth); + kAuthErrorInvalidCredential, auth); ExpectTrue("Failed sign in maintains user", auth->current_user() == pre_signin_user); } @@ -832,37 +1126,41 @@ extern "C" int common_main(int argc, const char* argv[]) { UserLogin user_login(auth); user_login.Register(); - if (!user_login.user()) { + if (!user_login.user().is_valid()) { LogMessage("Error - Could not create new user."); } else { // Test email user info strings. - Future email_sign_in_for_user = + Future email_sign_in_for_user = auth->SignInWithEmailAndPassword(user_login.email(), user_login.password()); WaitForSignInFuture(email_sign_in_for_user, "Auth::SignInWithEmailAndPassword() for User", kAuthErrorNone, auth); - User* email_user = email_sign_in_for_user.result() - ? *email_sign_in_for_user.result() - : nullptr; - if (email_user != nullptr) { - LogMessage("Email uid is %s", email_user->uid().c_str()); + User email_user = email_sign_in_for_user.result() + ? email_sign_in_for_user.result()->user + : User(); + if (email_user.is_valid()) { + LogMessage("Email uid is %s", email_user.uid().c_str()); ExpectStringsEqual("Email user email", user_login.email(), - email_user->email().c_str()); + email_user.email().c_str()); ExpectStringsEqual("Email user display_name", "", - email_user->display_name().c_str()); + email_user.display_name().c_str()); ExpectStringsEqual("Email user photo_url", "", - email_user->photo_url().c_str()); + email_user.photo_url().c_str()); ExpectStringsEqual("Email user provider_id", kFirebaseProviderId, - email_user->provider_id().c_str()); - 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", @@ -872,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", @@ -881,58 +1179,59 @@ extern "C" int common_main(int argc, const char* argv[]) { : ""); // Test Reload(). - Future reload_future = email_user->Reload(); + Future reload_future = email_user.Reload(); WaitForFuture(reload_future, "User::Reload()", kAuthErrorNone); // Test User::Unlink(). - Future unlink_future = email_user->Unlink("firebase"); + Future unlink_future = email_user.Unlink("firebase"); WaitForSignInFuture(unlink_future, "User::Unlink()", - 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); @@ -940,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); } @@ -956,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 187e0db9..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,21 +84,22 @@ void LogMessage(const char* format, ...) { WindowContext GetWindowContext() { return nullptr; } -std::string ReadTextInput(const char* title, const char* message, - const char* placeholder) { - char buf[64]; - printf("%s\n%s (for example: %s) ", title, message, placeholder); - fgets(buf, sizeof(buf), stdin); - // Remove trailing CR/LF. - int end = static_cast(strlen(buf)) - 1; // index of the last character - while (end >= 0 && (buf[end] == '\r' || buf[end] == '\n')) { - buf[end] = '\0'; - end--; +// 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()); } - return std::string(buf); } 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 @@ -83,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 ca4ceac8..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 @@ -40,6 +41,29 @@ @interface FTAViewController : UIViewController 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 - (void)viewDidLoad { @@ -48,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]; }); diff --git a/auth/testapp/src/main.h b/auth/testapp/src/main.h index d4b554df..130286c1 100644 --- a/auth/testapp/src/main.h +++ b/auth/testapp/src/main.h @@ -21,10 +21,11 @@ #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. @@ -44,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. 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 4c4d8bef..bfa91283 100644 --- a/database/testapp/build.gradle +++ b/database/testapp/build.gradle @@ -6,8 +6,8 @@ buildscript { 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' } } @@ -21,101 +21,57 @@ allprojects { 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:11.2.0' - compile 'com.google.firebase:firebase-database:11.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/readme.md b/database/testapp/readme.md index 01d70593..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,13 +125,13 @@ 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 `File/Project Structure...` or @@ -147,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 ------------ @@ -160,7 +225,7 @@ Known issues Support ------- -[https://firebase.google.com/support/]() +[https://firebase.google.com/support/](https://firebase.google.com/support/) 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 ec6fda16..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) { @@ -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. { @@ -593,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"); @@ -622,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."); @@ -633,7 +722,6 @@ 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 hierarchy at the location. { @@ -647,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"; @@ -910,8 +1003,16 @@ extern "C" int common_main(int argc, const char* argv[]) { future.error(), future.error_message()); } - firebase::database::DataSnapshot test_snapshot = *future.result(); - bool test_snapshot_was_valid = test_snapshot.is_valid(); + 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; @@ -924,8 +1025,8 @@ extern "C" int common_main(int argc, const char* argv[]) { LogMessage("ERROR: Reference is still valid after library shutdown."); } - if (test_snapshot_was_valid) { - if (!test_snapshot.is_valid()) { + 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."); 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/dynamic_links/testapp/Podfile b/dynamic_links/testapp/Podfile deleted file mode 100644 index 69a6f8aa..00000000 --- a/dynamic_links/testapp/Podfile +++ /dev/null @@ -1,6 +0,0 @@ -source 'https://github.com/CocoaPods/Specs.git' -platform :ios, '8.0' -# Dynamic Links test application. -target 'testapp' do - pod 'Firebase/DynamicLinks' -end diff --git a/dynamic_links/testapp/build.gradle b/dynamic_links/testapp/build.gradle deleted file mode 100644 index 6720717c..00000000 --- a/dynamic_links/testapp/build.gradle +++ /dev/null @@ -1,120 +0,0 @@ -// Top-level build file where you can add configuration options common to all sub-projects/modules. -buildscript { - repositories { - mavenLocal() - maven { url 'https://maven.google.com' } - jcenter() - } - dependencies { - classpath 'com.android.tools.build:gradle:2.1.2' - classpath 'com.google.gms:google-services:3.0.0' - } -} - -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' - - sourceSets { - main { - jniLibs.srcDirs = ['libs'] - manifest.srcFile 'AndroidManifest.xml' - java.srcDirs = ['src/android/java'] - res.srcDirs = ['res'] - } - } - - defaultConfig { - applicationId 'com.google.android.dynamiclinks.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/dynamic_links.pro") - proguardFile file('proguard.pro') - } - } -} - -dependencies { - compile 'com.google.firebase:firebase-invites:11.2.0' - compile 'com.google.android.gms:play-services-base:11.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/dynamic_links/testapp/jni/Android.mk b/dynamic_links/testapp/jni/Android.mk deleted file mode 100644 index 0918d73e..00000000 --- a/dynamic_links/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_dynamic_links -LOCAL_SRC_FILES:=$(FIREBASE_LIBRARY_PATH)/libdynamic_links.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_dynamic_links \ - 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/dynamic_links/testapp/jni/Application.mk b/dynamic_links/testapp/jni/Application.mk deleted file mode 100644 index 53ed56a2..00000000 --- a/dynamic_links/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/dynamic_links/testapp/readme.md b/dynamic_links/testapp/readme.md deleted file mode 100644 index f19248f8..00000000 --- a/dynamic_links/testapp/readme.md +++ /dev/null @@ -1,165 +0,0 @@ -Firebase Dynamic Links Quickstart -================================= - -The Firebase Dynamic Links Quickstart demonstrates logging a range -of different events using the Firebase Dynamic Links C++ SDK. The application -has no user interface and simply logs actions it's performing to the console. - -Introduction ------------- - -- [Read more about Firebase Dynamic Links](https://firebase.google.com/docs/dynamic-links/) - -Building and Running the testapp --------------------------------- - -### iOS - - Link your iOS app to the Firebase libraries. - - Get CocoaPods version 1 or later by running, - ``` - $ sudo gem install CocoaPods --pre - ``` - - From the testapp directory, install the CocoaPods listed in the Podfile - by running, - ``` - $ pod install - ``` - - Open the generated Xcode workspace (which now has the CocoaPods), - ``` - $ open testapp.xcworkspace - ``` - - For further details please refer to the - [general instructions for setting up an iOS app with Firebase](https://firebase.google.com/docs/ios/setup). - - Register your iOS app with Firebase. - - Create a new app on the [Firebase console](https://firebase.google.com/console/), and attach - your iOS app to it. - - You can use "com.google.ios.dynamiclinks.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. - - Configure the app to handle dynamic links / app invites. - - In your project's Info tab, under the URL Types section, find the URL - Schemes box and add your app's bundle ID (the default scheme used - by dynamic links). - - Copy the dynamic links domain for your project under the Dynamic Links - tab of the [Firebase console](https://firebase.google.com/console/) - Then, in your project's Capabilities tab: - - Enable the Associated Domains capability. - - Add applinks:YOUR_DYNAMIC_LINKS_DOMAIN - For example "applinks:xyz.app.goo.gl". - - Copy the dynamic links domain for your project under the Dynamic Links - tab of the [Firebase console](https://firebase.google.com/console/) - e.g xyz.app.goo.gl and assign to the string kDynamicLinksDomain in - src/common_main.cc . - - 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\_dynamic_links.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 user interface. The output of the app can be viewed - via the console. In Xcode, select - "View --> Debug Area --> Activate Console" from the menu. - - When running the application you should see: - - The dynamic link - if any - recevied by the application on startup. - - A dynamically generated long link. - - A dynamically generated short link. - - Leaving the application and opening a link (e.g via an email) for the app - should reopen the app and display the dynamic link. - -### 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.dynamiclinks.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). - - Copy the dynamic links domain for your project under the Dynamic Links - tab of the [Firebase console](https://firebase.google.com/console/) - e.g xyz.app.goo.gl and assign to the string kDynamicLinksDomain in - src/common_main.cc . - - 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 `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 user interface. The output of the app can be viewed - in the logcat output of Android studio or by running "adb logcat" from - the command line. - - When running the application you should see: - - The dynamic link - if any - recevied by the application on startup. - - A dynamically generated long link. - - A dynamically generated short link. - - Leaving the application and opening a link (e.g via an email) for the app - should reopen the app and display the dynamic link. - -Support -------- - -[https://firebase.google.com/support/]() - -License -------- - -Copyright 2017 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/dynamic_links/testapp/res/values/strings.xml b/dynamic_links/testapp/res/values/strings.xml deleted file mode 100644 index 33fe18b2..00000000 --- a/dynamic_links/testapp/res/values/strings.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - Firebase Dynamic Links Test - diff --git a/dynamic_links/testapp/src/common_main.cc b/dynamic_links/testapp/src/common_main.cc deleted file mode 100644 index 2bc5cdfc..00000000 --- a/dynamic_links/testapp/src/common_main.cc +++ /dev/null @@ -1,202 +0,0 @@ -// Copyright 2016 Google Inc. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include - -#include "firebase/app.h" -#include "firebase/dynamic_links.h" -#include "firebase/dynamic_links/components.h" -#include "firebase/future.h" -#include "firebase/util.h" -// Thin OS abstraction layer. -#include "main.h" // NOLINT - -// Invalid domain, used to make sure the user sets a valid domain. -#define INVALID_DYNAMIC_LINKS_DOMAIN "THIS_IS_AN_INVALID_DOMAIN" - -static const char* kDynamicLinksDomainInvalidError = - "kDynamicLinksDomain is not valid, link shortening will fail.\n" - "To resolve this:\n" - "* Goto the Firebase console https://firebase.google.com/console/\n" - "* Click on the Dynamic Links tab\n" - "* Copy the domain e.g x20yz.app.goo.gl\n" - "* Replace the value of kDynamicLinksDomain with the copied domain.\n"; - -// IMPORTANT: You need to set this to a valid domain from the Firebase -// console (see kDynamicLinksDomainInvalidError for the details). -static const char* kDynamicLinksDomain = INVALID_DYNAMIC_LINKS_DOMAIN; - -// Displays a received dynamic link. -class Listener : public firebase::dynamic_links::Listener { - public: - // Called on the client when a dynamic link arrives. - void OnDynamicLinkReceived( - const firebase::dynamic_links::DynamicLink* dynamic_link) override { - LogMessage("Received link: %s", dynamic_link->url.c_str()); - } -}; - -void WaitForCompletion(const firebase::FutureBase& future, const char* name) { - while (future.status() == firebase::kFutureStatusPending) { - ProcessEvents(100); - } - if (future.status() != firebase::kFutureStatusComplete) { - LogMessage("ERROR: %s returned an invalid result.", name); - } else if (future.error() != 0) { - LogMessage("ERROR: %s returned error %d: %s", name, future.error(), - future.error_message()); - } -} - -// Show a generated link. -void ShowGeneratedLink( - const firebase::dynamic_links::GeneratedDynamicLink& generated_link, - const char* operation_description) { - if (!generated_link.warnings.empty()) { - LogMessage("%s generated warnings:", operation_description); - for (auto it = generated_link.warnings.begin(); - it != generated_link.warnings.end(); ++it) { - LogMessage(" %s", it->c_str()); - } - } - LogMessage("url: %s", generated_link.url.c_str()); -} - -// Wait for dynamic link generation to complete, logging the result. -void WaitForAndShowGeneratedLink( - const firebase::Future& - generated_dynamic_link_future, - const char* operation_description) { - LogMessage("%s...", operation_description); - WaitForCompletion(generated_dynamic_link_future, operation_description); - if (generated_dynamic_link_future.error() != 0) { - LogMessage("ERROR: %s failed with error %d: %s", operation_description, - generated_dynamic_link_future.error(), - generated_dynamic_link_future.error_message()); - return; - } - ShowGeneratedLink(*generated_dynamic_link_future.result(), - operation_description); -} - -// Execute all methods of the C++ Dynamic Links API. -extern "C" int common_main(int argc, const char* argv[]) { - namespace dynamic_links = ::firebase::dynamic_links; - ::firebase::App* app; - Listener* link_listener = new Listener; - - LogMessage("Initialize the Firebase Dynamic Links 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))); - - ::firebase::ModuleInitializer initializer; - initializer.Initialize(app, link_listener, - [](::firebase::App* app, void* listener) { - LogMessage("Try to initialize Dynamic Links"); - return ::firebase::dynamic_links::Initialize( - *app, reinterpret_cast(listener)); - }); - while (initializer.InitializeLastResult().status() != - firebase::kFutureStatusComplete) { - if (ProcessEvents(100)) return 1; // exit if requested - } - if (initializer.InitializeLastResult().error() != 0) { - LogMessage("Failed to initialize Firebase Dynamic Links: %s", - initializer.InitializeLastResult().error_message()); - ProcessEvents(2000); - return 1; - } - - LogMessage("Initialized the Firebase Dynamic Links API"); - - firebase::dynamic_links::GoogleAnalyticsParameters analytics_parameters; - analytics_parameters.source = "mysource"; - analytics_parameters.medium = "mymedium"; - analytics_parameters.campaign = "mycampaign"; - analytics_parameters.term = "myterm"; - analytics_parameters.content = "mycontent"; - - firebase::dynamic_links::IOSParameters ios_parameters("com.myapp.bundleid"); - ios_parameters.fallback_url = "https://mysite/fallback"; - ios_parameters.custom_scheme = "mycustomscheme"; - ios_parameters.minimum_version = "1.2.3"; - ios_parameters.ipad_bundle_id = "com.myapp.bundleid.ipad"; - ios_parameters.ipad_fallback_url = "https://mysite/fallbackipad"; - - firebase::dynamic_links::ITunesConnectAnalyticsParameters - app_store_parameters; - app_store_parameters.affiliate_token = "abcdefg"; - app_store_parameters.campaign_token = "hijklmno"; - app_store_parameters.provider_token = "pq-rstuv"; - - firebase::dynamic_links::AndroidParameters android_parameters( - "com.myapp.packageid"); - android_parameters.fallback_url = "https://mysite/fallback"; - android_parameters.minimum_version = 12; - - firebase::dynamic_links::SocialMetaTagParameters social_parameters; - social_parameters.title = "My App!"; - social_parameters.description = "My app is awesome!"; - social_parameters.image_url = "https://mysite.com/someimage.jpg"; - - firebase::dynamic_links::DynamicLinkComponents components( - "https://google.com/abc", kDynamicLinksDomain); - components.google_analytics_parameters = &analytics_parameters; - components.ios_parameters = &ios_parameters; - components.itunes_connect_analytics_parameters = &app_store_parameters; - components.android_parameters = &android_parameters; - components.social_meta_tag_parameters = &social_parameters; - - dynamic_links::GeneratedDynamicLink long_link; - { - const char* description = "Generate long link from components"; - long_link = dynamic_links::GetLongLink(components); - LogMessage("%s...", description); - ShowGeneratedLink(long_link, description); - } - - if (strcmp(kDynamicLinksDomain, INVALID_DYNAMIC_LINKS_DOMAIN) == 0) { - LogMessage(kDynamicLinksDomainInvalidError); - } else { - { - firebase::Future link_future = - dynamic_links::GetShortLink(components); - WaitForAndShowGeneratedLink(link_future, - "Generate short link from components"); - } - if (!long_link.url.empty()) { - dynamic_links::DynamicLinkOptions options; - options.path_length = firebase::dynamic_links::kPathLengthShort; - firebase::Future link_future = - dynamic_links::GetShortLink(long_link.url.c_str(), options); - WaitForAndShowGeneratedLink(link_future, "Generate short from long link"); - } - } - - // Wait until the user wants to quit the app. - while (!ProcessEvents(1000)) { - } - - dynamic_links::Terminate(); - delete link_listener; - delete app; - - return 0; -} diff --git a/dynamic_links/testapp/src/desktop/desktop_main.cc b/dynamic_links/testapp/src/desktop/desktop_main.cc deleted file mode 100644 index 99ace543..00000000 --- a/dynamic_links/testapp/src/desktop/desktop_main.cc +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2016 Google Inc. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include -#include -#ifndef _WIN32 -#include -#endif // !_WIN32 - -#ifdef _WIN32 -#include -#endif // _WIN32 - -#include "main.h" // NOLINT - -extern "C" int common_main(int argc, const char* argv[]); - -static bool quit = false; - -#ifdef _WIN32 -static BOOL WINAPI SignalHandler(DWORD event) { - if (!(event == CTRL_C_EVENT || event == CTRL_BREAK_EVENT)) { - return FALSE; - } - quit = true; - return TRUE; -} -#else -static void SignalHandler(int /* ignored */) { quit = true; } -#endif // _WIN32 - -bool ProcessEvents(int msec) { -#ifdef _WIN32 - Sleep(msec); -#else - usleep(msec * 1000); -#endif // _WIN32 - return quit; -} - -void LogMessage(const char* format, ...) { - va_list list; - va_start(list, format); - vprintf(format, list); - va_end(list); - printf("\n"); - fflush(stdout); -} - -WindowContext GetWindowContext() { return nullptr; } - -int main(int argc, const char* argv[]) { -#ifdef _WIN32 - SetConsoleCtrlHandler((PHANDLER_ROUTINE)SignalHandler, TRUE); -#else - signal(SIGINT, SignalHandler); -#endif // _WIN32 - return common_main(argc, argv); -} diff --git a/dynamic_links/testapp/testapp/Info.plist b/dynamic_links/testapp/testapp/Info.plist deleted file mode 100644 index 4b9b8241..00000000 --- a/dynamic_links/testapp/testapp/Info.plist +++ /dev/null @@ -1,57 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - com.google.ios.dynamiclinks.testapp - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleURLSchemes - - com.google.ios.dynamiclinks.testapp - - CFBundleURLTypes - - - CFBundleTypeRole - Editor - CFBundleURLName - com.google.ios.dynamiclinks.testapp - CFBundleURLSchemes - - com.google.ios.dynamiclinks.testapp - - - - CFBundleTypeRole - Editor - CFBundleURLName - google - CFBundleURLSchemes - - YOUR_REVERSED_CLIENT_ID - - - - CFBundleVersion - 1 - LSRequiresIPhoneOS - - UILaunchStoryboardName - LaunchScreen - NSContactsUsageDescription - Invite others to use the app. - - 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/invites/testapp/AndroidManifest.xml b/firestore/testapp/AndroidManifest.xml similarity index 64% rename from invites/testapp/AndroidManifest.xml rename to firestore/testapp/AndroidManifest.xml index d94a6af8..b4f84579 100644 --- a/invites/testapp/AndroidManifest.xml +++ b/firestore/testapp/AndroidManifest.xml @@ -1,12 +1,16 @@ - - + + + + 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/dynamic_links/testapp/gradle/wrapper/gradle-wrapper.properties b/firestore/testapp/gradle/wrapper/gradle-wrapper.properties similarity index 52% rename from dynamic_links/testapp/gradle/wrapper/gradle-wrapper.properties rename to firestore/testapp/gradle/wrapper/gradle-wrapper.properties index d5705170..65340c1b 100644 --- a/dynamic_links/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/admob/testapp/proguard.pro b/firestore/testapp/proguard.pro similarity index 100% rename from admob/testapp/proguard.pro rename to firestore/testapp/proguard.pro diff --git a/dynamic_links/testapp/res/layout/main.xml b/firestore/testapp/res/layout/main.xml similarity index 100% rename from dynamic_links/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/dynamic_links/testapp/src/ios/ios_main.mm b/firestore/testapp/src/ios/ios_main.mm old mode 100644 new mode 100755 similarity index 88% rename from dynamic_links/testapp/src/ios/ios_main.mm rename to firestore/testapp/src/ios/ios_main.mm index 2adcac9c..567f7b0e --- a/dynamic_links/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]; 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/invites/testapp/testapp.xcodeproj/project.pbxproj b/firestore/testapp/testapp.xcodeproj/project.pbxproj similarity index 70% rename from invites/testapp/testapp.xcodeproj/project.pbxproj rename to firestore/testapp/testapp.xcodeproj/project.pbxproj index baf45c4c..ed7634b5 100644 --- a/invites/testapp/testapp.xcodeproj/project.pbxproj +++ b/firestore/testapp/testapp.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ 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 */; }; @@ -14,6 +15,9 @@ 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 */ @@ -29,6 +33,13 @@ 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 */ @@ -37,9 +48,13 @@ 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; }; @@ -56,6 +71,7 @@ 5292271D1C85FB5500C89379 /* src */, 529226D41C85F68000C89379 /* Frameworks */, 529226D31C85F68000C89379 /* Products */, + 5C0AA7C41D61214BE0B5CB4C /* Pods */, ); sourceTree = ""; }; @@ -70,10 +86,15 @@ 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 = ""; @@ -96,6 +117,15 @@ 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 */ @@ -103,9 +133,11 @@ 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 = ( ); @@ -135,6 +167,7 @@ developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( + English, en, ); mainGroup = 529226C91C85F68000C89379; @@ -160,6 +193,49 @@ }; /* 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; @@ -208,7 +284,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.4; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -245,7 +321,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.4; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; @@ -255,9 +331,14 @@ }; 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, @@ -271,9 +352,14 @@ }; 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, diff --git a/invites/testapp/testapp/Images.xcassets/AppIcon.appiconset/Contents.json b/firestore/testapp/testapp/Images.xcassets/AppIcon.appiconset/Contents.json similarity index 57% rename from invites/testapp/testapp/Images.xcassets/AppIcon.appiconset/Contents.json rename to firestore/testapp/testapp/Images.xcassets/AppIcon.appiconset/Contents.json index b7f3352e..d8db8d65 100644 --- a/invites/testapp/testapp/Images.xcassets/AppIcon.appiconset/Contents.json +++ b/firestore/testapp/testapp/Images.xcassets/AppIcon.appiconset/Contents.json @@ -1,15 +1,35 @@ { "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", @@ -20,6 +40,16 @@ "size" : "60x60", "scale" : "3x" }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "2x" + }, { "idiom" : "ipad", "size" : "29x29", @@ -49,6 +79,16 @@ "idiom" : "ipad", "size" : "76x76", "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "83.5x83.5", + "scale" : "2x" + }, + { + "idiom" : "ios-marketing", + "size" : "1024x1024", + "scale" : "1x" } ], "info" : { diff --git a/dynamic_links/testapp/testapp/Images.xcassets/LaunchImage.launchimage/Contents.json b/firestore/testapp/testapp/Images.xcassets/LaunchImage.launchimage/Contents.json similarity index 100% rename from dynamic_links/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/dynamic_links/testapp/AndroidManifest.xml b/functions/testapp/AndroidManifest.xml similarity index 86% rename from dynamic_links/testapp/AndroidManifest.xml rename to functions/testapp/AndroidManifest.xml index 09799b01..0b38246d 100644 --- a/dynamic_links/testapp/AndroidManifest.xml +++ b/functions/testapp/AndroidManifest.xml @@ -1,14 +1,15 @@ - + ) + 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/dynamic_links/testapp/LaunchScreen.storyboard b/functions/testapp/LaunchScreen.storyboard similarity index 100% rename from dynamic_links/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/dynamic_links/testapp/gradle/wrapper/gradle-wrapper.jar b/functions/testapp/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from dynamic_links/testapp/gradle/wrapper/gradle-wrapper.jar rename to functions/testapp/gradle/wrapper/gradle-wrapper.jar diff --git a/invites/testapp/gradle/wrapper/gradle-wrapper.properties b/functions/testapp/gradle/wrapper/gradle-wrapper.properties similarity index 52% rename from invites/testapp/gradle/wrapper/gradle-wrapper.properties rename to functions/testapp/gradle/wrapper/gradle-wrapper.properties index d5705170..65340c1b 100644 --- a/invites/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/dynamic_links/testapp/gradlew b/functions/testapp/gradlew similarity index 100% rename from dynamic_links/testapp/gradlew rename to functions/testapp/gradlew diff --git a/dynamic_links/testapp/gradlew.bat b/functions/testapp/gradlew.bat similarity index 100% rename from dynamic_links/testapp/gradlew.bat rename to functions/testapp/gradlew.bat diff --git a/invites/testapp/proguard.pro b/functions/testapp/proguard.pro similarity index 73% rename from invites/testapp/proguard.pro rename to functions/testapp/proguard.pro index 54cd248b..2d04b8a9 100644 --- a/invites/testapp/proguard.pro +++ b/functions/testapp/proguard.pro @@ -1,2 +1,2 @@ -ignorewarnings --keep,includedescriptorclasses public class com.google.firebase.example.LoggingUtils { *; } +-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/invites/testapp/res/layout/main.xml b/functions/testapp/res/layout/main.xml similarity index 100% rename from invites/testapp/res/layout/main.xml rename to functions/testapp/res/layout/main.xml 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/invites/testapp/src/android/java/com/google/firebase/example/LoggingUtils.java b/functions/testapp/src/android/java/com/google/firebase/example/LoggingUtils.java similarity index 79% rename from invites/testapp/src/android/java/com/google/firebase/example/LoggingUtils.java rename to functions/testapp/src/android/java/com/google/firebase/example/LoggingUtils.java index acbd8d3e..351cf3b4 100644 --- a/invites/testapp/src/android/java/com/google/firebase/example/LoggingUtils.java +++ b/functions/testapp/src/android/java/com/google/firebase/example/LoggingUtils.java @@ -23,8 +23,8 @@ 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. + * 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; @@ -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(); @@ -42,13 +43,15 @@ 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); - } - } - }); + 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/admob/testapp/src/ios/ios_main.mm b/functions/testapp/src/ios/ios_main.mm old mode 100644 new mode 100755 similarity index 74% rename from admob/testapp/src/ios/ios_main.mm rename to functions/testapp/src/ios/ios_main.mm index 6ccb2de5..ee0c792c --- a/admob/testapp/src/ios/ios_main.mm +++ b/functions/testapp/src/ios/ios_main.mm @@ -18,7 +18,7 @@ #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 @@ -32,10 +32,10 @@ @interface FTAViewController : UIViewController static int g_exit_status = 0; static bool g_shutdown = false; -static NSCondition *g_shutdown_complete; -static NSCondition *g_shutdown_signal; -static UITextView *g_text_view; -static UIView *g_parent_view; +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 @@ -58,12 +58,19 @@ bool ProcessEvents(int msec) { return g_shutdown; } -WindowContext GetWindowContext() { - return g_parent_view; +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, ...) { +void LogMessage(const char *format, ...) { va_list args; NSString *formatString = @(format); @@ -79,7 +86,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 +95,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 +108,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/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/dynamic_links/testapp/testapp.xcodeproj/project.pbxproj b/functions/testapp/testapp.xcodeproj/project.pbxproj similarity index 99% rename from dynamic_links/testapp/testapp.xcodeproj/project.pbxproj rename to functions/testapp/testapp.xcodeproj/project.pbxproj index baf45c4c..5769362c 100644 --- a/dynamic_links/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/dynamic_links/testapp/testapp/Images.xcassets/AppIcon.appiconset/Contents.json b/functions/testapp/testapp/Images.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from dynamic_links/testapp/testapp/Images.xcassets/AppIcon.appiconset/Contents.json rename to functions/testapp/testapp/Images.xcassets/AppIcon.appiconset/Contents.json diff --git a/invites/testapp/testapp/Images.xcassets/LaunchImage.launchimage/Contents.json b/functions/testapp/testapp/Images.xcassets/LaunchImage.launchimage/Contents.json similarity index 100% rename from invites/testapp/testapp/Images.xcassets/LaunchImage.launchimage/Contents.json rename to functions/testapp/testapp/Images.xcassets/LaunchImage.launchimage/Contents.json 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/dynamic_links/testapp/LICENSE b/gma/testapp/LICENSE similarity index 100% rename from dynamic_links/testapp/LICENSE rename to gma/testapp/LICENSE diff --git a/invites/testapp/LaunchScreen.storyboard b/gma/testapp/LaunchScreen.storyboard similarity index 100% rename from invites/testapp/LaunchScreen.storyboard rename to gma/testapp/LaunchScreen.storyboard diff --git a/gma/testapp/Podfile b/gma/testapp/Podfile new file mode 100644 index 00000000..4683028b --- /dev/null +++ b/gma/testapp/Podfile @@ -0,0 +1,8 @@ +source 'https://github.com/CocoaPods/Specs.git' +platform :ios, '13.0' +use_frameworks! +# GMA test application. +target 'testapp' do + pod 'Google-Mobile-Ads-SDK', '11.2.0' + pod 'Firebase/Analytics', '10.25.0' +end diff --git a/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/invites/testapp/gradle/wrapper/gradle-wrapper.jar b/gma/testapp/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from invites/testapp/gradle/wrapper/gradle-wrapper.jar rename to gma/testapp/gradle/wrapper/gradle-wrapper.jar diff --git a/admob/testapp/gradle/wrapper/gradle-wrapper.properties b/gma/testapp/gradle/wrapper/gradle-wrapper.properties similarity index 52% rename from admob/testapp/gradle/wrapper/gradle-wrapper.properties rename to gma/testapp/gradle/wrapper/gradle-wrapper.properties index d5705170..65340c1b 100644 --- a/admob/testapp/gradle/wrapper/gradle-wrapper.properties +++ b/gma/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/gma/testapp/gradlew similarity index 100% rename from invites/testapp/gradlew rename to gma/testapp/gradlew diff --git a/invites/testapp/gradlew.bat b/gma/testapp/gradlew.bat similarity index 100% rename from invites/testapp/gradlew.bat rename to gma/testapp/gradlew.bat diff --git a/dynamic_links/testapp/proguard.pro b/gma/testapp/proguard.pro similarity index 100% rename from dynamic_links/testapp/proguard.pro rename to gma/testapp/proguard.pro diff --git a/admob/testapp/readme.md b/gma/testapp/readme.md similarity index 55% rename from admob/testapp/readme.md rename to gma/testapp/readme.md index f6d0d4fa..4143cb7e 100644 --- a/admob/testapp/readme.md +++ b/gma/testapp/readme.md @@ -1,15 +1,15 @@ -Firebase AdMob Quickstart +Firebase GMA Quickstart ============================== -The Firebase AdMob Test Application (testapp) demonstrates loading and showing -banners and interstitials using the Firebase AdMob C++ SDK. The application -has no user interface and simply logs actions it's performing to the console -while displaying the ads. +The Firebase Google Mobile Ads Test Application (testapp) demonstrates +loading and showing AdMob-served banners, interstitials and rewarded ads +using the Firebase GMA C++ SDK. The application has no user interface and +simply logs actions it's performing to the console while displaying the ads. Introduction ------------ -- [Read more about Firebase AdMob](https://firebase.google.com/docs/admob) +- [Read more about Firebase GMA](https://firebase.google.com/docs/gma) Getting Started --------------- @@ -18,16 +18,16 @@ Getting Started - 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). @@ -37,11 +37,11 @@ Getting Started - 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. + [https://firebase.google.com/docs/cpp/setup](https://firebase.google.com/docs/cpp/setup) + and unzip it to a directory of your choice. - Add the following frameworks from the Firebase C++ SDK to the project: - frameworks/ios/universal/firebase.framework - - frameworks/ios/universal/firebase_admob.framework + - 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" @@ -54,11 +54,14 @@ Getting Started 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 and an interstitial ad. You can dismiss - the interstitial ad to see the banner ad. - - Afterwards, the testapp will display a Native Express test ad and a - Rewarded Video test ad. + - The testapp displays a banner ad, an interstitial ad and a rewarded ad. You must + dismiss each ad to see the next. - The output of the app can be viewed onscreen or via the console. To view the console in Xcode, select "View --> Debug Area --> Activate Console" from the menu. @@ -89,13 +92,13 @@ Getting Started - 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 `File/Project Structure...` or @@ -106,26 +109,75 @@ Getting Started - From the Android Studio launch menu, "Open an existing Android Studio project", and select `build.gradle`. - Install the SDK Platforms that Android Studio reports missing. + - Update the GMA App ID: + - In the `AndroidManifest.xml`, update + `com.google.android.gms.ads.APPLICATION_ID` with the same app ID, + replacing 'YOUR_ANDROID_ADMOB_APP_ID'. + - For more information, see + [Update your AndroidManifest.xml](https://developers.google.com/admob/android/quick-start#update_your_androidmanifestxml) - Build the testapp and run it on an Android device or emulator. - - The testapp will initialize AdMob, then load and display a test banner and - a test interstitial. + - The testapp will initialize the GMA SDK, then load and display a test + banner ad, interstitial ad and rewarded ad. - Tapping on an ad to verify the clickthrough process is possible, and the - interstitial will wait to be closed by the user. - - Afterwards, the testapp will display a Native Express test ad and a - Rewarded Video test ad. + test app will wait for each ad to be dismissed. - While this is happening, information from the device log will be written to an onscreen TextView. - Logcat can also be used as normal. +### Desktop + - Note: the testapp has no user interface, but the output can be viewed via + the console. The GMA SDK uses a stubbed implementation on desktop, so + functionality is not expected, and the app will end waiting for the interstitial + ad to be dismissed. + - Register your app with Firebase. + - Create a new app on the [Firebase console](https://firebase.google.com/console/), + following the above instructions for Android or iOS. + - 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/](https://firebase.google.com/support/) License ------- -Copyright 2016 Google, Inc. +Copyright 2022 Google, Inc. Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for diff --git a/admob/testapp/res/values/strings.xml b/gma/testapp/res/values/strings.xml similarity index 53% rename from admob/testapp/res/values/strings.xml rename to gma/testapp/res/values/strings.xml index 8589bd2c..a9a313a3 100644 --- a/admob/testapp/res/values/strings.xml +++ b/gma/testapp/res/values/strings.xml @@ -1,4 +1,4 @@ - Firebase AdMob Test + Firebase GMA Test diff --git a/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/dynamic_links/testapp/src/android/android_main.cc b/gma/testapp/src/android/android_main.cc similarity index 86% rename from dynamic_links/testapp/src/android/android_main.cc rename to gma/testapp/src/android/android_main.cc index 4e033e1c..7a6de455 100644 --- a/dynamic_links/testapp/src/android/android_main.cc +++ b/gma/testapp/src/android/android_main.cc @@ -17,33 +17,33 @@ #include #include -#include #include +#include -#include "main.h" // NOLINT +#include "main.h" // NOLINT // This implementation is derived from http://github.com/google/fplutil -extern "C" int common_main(int argc, const char* argv[]); +extern "C" int common_main(int argc, const char *argv[]); -static struct android_app* g_app_state = nullptr; +static struct android_app *g_app_state = nullptr; static bool g_destroy_requested = false; static bool g_started = false; static bool g_restarted = false; static pthread_mutex_t g_started_mutex; // Handle state changes from via native app glue. -static void OnAppCmd(struct android_app* app, int32_t cmd) { +static void OnAppCmd(struct android_app *app, int32_t cmd) { g_destroy_requested |= cmd == APP_CMD_DESTROY; } // Process events pending on the main thread. // Returns true when the app receives an event requesting exit. bool ProcessEvents(int msec) { - struct android_poll_source* source = nullptr; + struct android_poll_source *source = nullptr; int events; int looperId = ALooper_pollAll(msec, nullptr, &events, - reinterpret_cast(&source)); + reinterpret_cast(&source)); if (looperId >= 0 && source) { source->process(g_app_state, source); } @@ -57,7 +57,7 @@ jobject GetActivity() { return g_app_state->activity->clazz; } jobject GetWindowContext() { return g_app_state->activity->clazz; } // Find a class, attempting to load the class if it's not found. -jclass FindClass(JNIEnv* env, jobject activity_object, const char* class_name) { +jclass FindClass(JNIEnv *env, jobject activity_object, const char *class_name) { jclass class_object = env->FindClass(class_name); if (env->ExceptionCheck()) { env->ExceptionClear(); @@ -93,14 +93,13 @@ jclass FindClass(JNIEnv* env, jobject activity_object, const char* class_name) { // Vars that we need available for appending text to the log window: class LoggingUtilsData { - public: +public: LoggingUtilsData() - : logging_utils_class_(nullptr), - logging_utils_add_log_text_(0), + : logging_utils_class_(nullptr), logging_utils_add_log_text_(0), logging_utils_init_log_window_(0) {} ~LoggingUtilsData() { - JNIEnv* env = GetJniEnv(); + JNIEnv *env = GetJniEnv(); assert(env); if (logging_utils_class_) { env->DeleteGlobalRef(logging_utils_class_); @@ -108,7 +107,7 @@ class LoggingUtilsData { } void Init() { - JNIEnv* env = GetJniEnv(); + JNIEnv *env = GetJniEnv(); assert(env); jclass logging_utils_class = FindClass( @@ -130,9 +129,10 @@ class LoggingUtilsData { logging_utils_init_log_window_, GetActivity()); } - void AppendText(const char* text) { - if (logging_utils_class_ == 0) return; // haven't been initted yet - JNIEnv* env = GetJniEnv(); + void AppendText(const char *text) { + if (logging_utils_class_ == 0) + return; // haven't been initted yet + JNIEnv *env = GetJniEnv(); assert(env); jstring text_string = env->NewStringUTF(text); env->CallStaticVoidMethod(logging_utils_class_, logging_utils_add_log_text_, @@ -140,17 +140,17 @@ class LoggingUtilsData { env->DeleteLocalRef(text_string); } - private: +private: jclass logging_utils_class_; jmethodID logging_utils_add_log_text_; jmethodID logging_utils_init_log_window_; }; -LoggingUtilsData* g_logging_utils_data; +LoggingUtilsData *g_logging_utils_data; // Checks if a JNI exception has happened, and if so, logs it to the console. void CheckJNIException() { - JNIEnv* env = GetJniEnv(); + JNIEnv *env = GetJniEnv(); if (env->ExceptionCheck()) { // Get the exception text. jthrowable exception = env->ExceptionOccurred(); @@ -161,7 +161,7 @@ void CheckJNIException() { jmethodID toString = env->GetMethodID(object_class, "toString", "()Ljava/lang/String;"); jstring s = (jstring)env->CallObjectMethod(exception, toString); - const char* exception_text = env->GetStringUTFChars(s, nullptr); + const char *exception_text = env->GetStringUTFChars(s, nullptr); // Log the exception text. __android_log_print(ANDROID_LOG_INFO, FIREBASE_TESTAPP_NAME, @@ -182,8 +182,8 @@ void CheckJNIException() { } // Log a message that can be viewed in "adb logcat". -void LogMessage(const char* format, ...) { - static const int kLineBufferSize = 1000; +void LogMessage(const char *format, ...) { + static const int kLineBufferSize = 100; char buffer[kLineBufferSize + 2]; va_list list; @@ -201,15 +201,15 @@ void LogMessage(const char* format, ...) { } // Get the JNI environment. -JNIEnv* GetJniEnv() { - JavaVM* vm = g_app_state->activity->vm; - JNIEnv* env; +JNIEnv *GetJniEnv() { + JavaVM *vm = g_app_state->activity->vm; + JNIEnv *env; jint result = vm->AttachCurrentThread(&env, nullptr); return result == JNI_OK ? env : nullptr; } // Execute common_main(), flush pending events and finish the activity. -extern "C" void android_main(struct android_app* state) { +extern "C" void android_main(struct android_app *state) { // native_app_glue spawns a new thread, calling android_main() when the // activity onStart() or onRestart() methods are called. This code handles // the case where we're re-entering this method on a different thread by @@ -236,9 +236,9 @@ extern "C" void android_main(struct android_app* state) { g_logging_utils_data->Init(); // Execute cross platform entry point. - static const char* argv[] = {FIREBASE_TESTAPP_NAME}; + static const char *argv[] = {FIREBASE_TESTAPP_NAME}; int return_value = common_main(1, argv); - (void)return_value; // Ignore the return value. + (void)return_value; // Ignore the return value. ProcessEvents(10); // Clean up logging display. @@ -246,7 +246,8 @@ extern "C" void android_main(struct android_app* state) { g_logging_utils_data = nullptr; // Finish the activity. - if (!g_restarted) ANativeActivity_finish(state->activity); + if (!g_restarted) + ANativeActivity_finish(state->activity); g_app_state->activity->vm->DetachCurrentThread(); g_started = false; diff --git a/dynamic_links/testapp/src/android/java/com/google/firebase/example/LoggingUtils.java b/gma/testapp/src/android/java/com/google/firebase/example/LoggingUtils.java similarity index 92% rename from dynamic_links/testapp/src/android/java/com/google/firebase/example/LoggingUtils.java rename to gma/testapp/src/android/java/com/google/firebase/example/LoggingUtils.java index 11d67c5b..3cb37ecf 100644 --- a/dynamic_links/testapp/src/android/java/com/google/firebase/example/LoggingUtils.java +++ b/gma/testapp/src/android/java/com/google/firebase/example/LoggingUtils.java @@ -44,12 +44,12 @@ public static void initLogWindow(Activity activity) { public static void addLogText(final String text) { new Handler(Looper.getMainLooper()).post(new Runnable() { - @Override - public void run() { - if (sTextView != null) { - sTextView.append(text); - } + @Override + public void run() { + if (sTextView != null) { + sTextView.append(text); } - }); + } + }); } } diff --git a/gma/testapp/src/common_main.cc b/gma/testapp/src/common_main.cc new file mode 100644 index 00000000..10a0a181 --- /dev/null +++ b/gma/testapp/src/common_main.cc @@ -0,0 +1,473 @@ +// Copyright 2022 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "firebase/app.h" +#include "firebase/future.h" +#include "firebase/gma.h" +#include "firebase/gma/ad_view.h" +#include "firebase/gma/interstitial_ad.h" +#include "firebase/gma/rewarded_ad.h" +#include "firebase/gma/types.h" + +// Thin OS abstraction layer. +#include "main.h" // NOLINT + +// A simple listener that logs changes to an AdView. +class LoggingAdViewListener : public firebase::gma::AdListener { +public: + LoggingAdViewListener() {} + + void OnAdClicked() override { ::LogMessage("AdView ad clicked."); } + + void OnAdClosed() override { ::LogMessage("AdView ad closed."); } + + void OnAdImpression() override { ::LogMessage("AdView ad impression."); } + + void OnAdOpened() override { ::LogMessage("AdView ad opened."); } +}; + +// A simple listener that logs changes to an AdView's bounding box. +class LoggingAdViewBoundedBoxListener + : public firebase::gma::AdViewBoundingBoxListener { +public: + void OnBoundingBoxChanged(firebase::gma::AdView *ad_view, + firebase::gma::BoundingBox box) override { + ::LogMessage("AdView bounding box update x: %d y: %d " + "width: %d height: %d", + box.x, box.y, box.width, box.height); + } +}; + +// A simple listener track FullScreen content changes. +class LoggingFullScreenContentListener + : public firebase::gma::FullScreenContentListener { +public: + LoggingFullScreenContentListener() : num_ad_dismissed_(0) {} + + void OnAdClicked() override { ::LogMessage("FullScreenContent ad clicked."); } + + void OnAdDismissedFullScreenContent() override { + ::LogMessage("FullScreenContent ad dismissed."); + num_ad_dismissed_++; + } + + void OnAdFailedToShowFullScreenContent( + const firebase::gma::AdError &ad_error) override { + ::LogMessage("FullScreenContent ad failed to show full screen content," + " AdErrorCode: %d", + ad_error.code()); + } + + void OnAdImpression() override { + ::LogMessage("FullScreenContent ad impression."); + } + + void OnAdShowedFullScreenContent() override { + ::LogMessage("FullScreenContent ad showed content."); + } + + uint32_t num_ad_dismissed() const { return num_ad_dismissed_; } + +private: + uint32_t num_ad_dismissed_; +}; + +// A simple listener track UserEarnedReward events. +class LoggingUserEarnedRewardListener + : public firebase::gma::UserEarnedRewardListener { +public: + LoggingUserEarnedRewardListener() {} + + void OnUserEarnedReward(const firebase::gma::AdReward &reward) override { + ::LogMessage("User earned reward amount: %d type: %s", reward.amount(), + reward.type().c_str()); + } +}; + +// A simple listener track ad pay events. +class LoggingPaidEventListener : public firebase::gma::PaidEventListener { +public: + LoggingPaidEventListener() {} + + void OnPaidEvent(const firebase::gma::AdValue &value) override { + ::LogMessage("PaidEvent value: %lld currency_code: %s", + value.value_micros(), value.currency_code().c_str()); + } +}; + +void LoadAndShowAdView(const firebase::gma::AdRequest &ad_request); +void LoadAndShowInterstitialAd(const firebase::gma::AdRequest &ad_request); +void LoadAndShowRewardedAd(const firebase::gma::AdRequest &ad_request); + +// These ad units IDs have been created specifically for testing, and will +// always return test ads. +#if defined(__ANDROID__) +const char *kBannerAdUnit = "ca-app-pub-3940256099942544/6300978111"; +const char *kInterstitialAdUnit = "ca-app-pub-3940256099942544/1033173712"; +const char *kRewardedAdUnit = "ca-app-pub-3940256099942544/5224354917"; +#else +const char *kBannerAdUnit = "ca-app-pub-3940256099942544/2934735716"; +const char *kInterstitialAdUnit = "ca-app-pub-3940256099942544/4411468910"; +const char *kRewardedAdUnit = "ca-app-pub-3940256099942544/1712485313"; +#endif + +// Sample keywords to use in making the request. +static const std::vector kKeywords({"GMA", "C++", "Fun"}); + +// Sample test device IDs to use in making the request. Add your own here. +const std::vector kTestDeviceIDs = { + "2077ef9a63d2b398840261c8221a0c9b", "098fe087d987c9a878965454a65654d7"}; + +#if defined(ANDROID) +static const char *kAdNetworkExtrasClassName = + "com/google/ads/mediation/admob/AdMobAdapter"; +#else +static const char *kAdNetworkExtrasClassName = "GADExtras"; +#endif + +// Function to wait for the completion of a future, and log the error +// if one is encountered. +static void WaitForFutureCompletion(firebase::FutureBase future) { + while (!ProcessEvents(1000)) { + if (future.status() != firebase::kFutureStatusPending) { + break; + } + } + + if (future.error() != firebase::gma::kAdErrorCodeNone) { + LogMessage("ERROR: Action failed with error code %d and message \"%s\".", + future.error(), future.error_message()); + } +} + +// Inittialize GMA, load a Banner, Interstitial and Rewarded Ad. +extern "C" int common_main(int argc, const char *argv[]) { + firebase::App *app; + LogMessage("Initializing Firebase App."); + +#if defined(__ANDROID__) + app = ::firebase::App::Create(GetJniEnv(), GetActivity()); +#else + app = ::firebase::App::Create(); +#endif // defined(__ANDROID__) + + LogMessage("Created the Firebase App %x.", + static_cast(reinterpret_cast(app))); + + LogMessage("Initializing the GMA with Firebase API."); + firebase::gma::Initialize(*app); + + WaitForFutureCompletion(firebase::gma::InitializeLastResult()); + if (firebase::gma::InitializeLastResult().error() != + firebase::gma::kAdErrorCodeNone) { + // Initialization Failure. The error was already logged in + // WaitForFutureCompletion, so simply exit here. + return -1; + } + + // Log mediation adapter initialization status. + for (auto adapter_status : + firebase::gma::GetInitializationStatus().GetAdapterStatusMap()) { + LogMessage( + "GMA Mediation Adapter '%s' %s (latency %d ms): %s", + adapter_status.first.c_str(), + (adapter_status.second.is_initialized() ? "loaded" : "NOT loaded"), + adapter_status.second.latency(), + adapter_status.second.description().c_str()); + } + + // Configure test device ids before loading ads. + // + // This example uses ad units that are specially configured to return test ads + // for every request. When using your own ad unit IDs, however, it's important + // to register the device IDs associated with any devices that will be used to + // test the app. This ensures that regardless of the ad unit ID, those + // devices will always receive test ads in compliance with AdMob policy. + // + // Device IDs can be obtained by checking the logcat or the Xcode log while + // debugging. They appear as a long string of hex characters. + firebase::gma::RequestConfiguration request_configuration; + request_configuration.test_device_ids = kTestDeviceIDs; + firebase::gma::SetRequestConfiguration(request_configuration); + + // + // Load and Display a Banner Ad using AdView. + // + + // Create an AdRequest. + firebase::gma::AdRequest ad_request; + + // Configure additional keywords to be used in targeting. + for (auto keyword_iter = kKeywords.begin(); keyword_iter != kKeywords.end(); + ++keyword_iter) { + ad_request.add_keyword((*keyword_iter).c_str()); + } + + // "Extra" key value pairs can be added to the request as well. Typically + // these are used when testing new features. + ad_request.add_extra(kAdNetworkExtrasClassName, "the_name_of_an_extra", + "the_value_for_that_extra"); + + LoadAndShowAdView(ad_request); + LoadAndShowInterstitialAd(ad_request); + LoadAndShowRewardedAd(ad_request); + + LogMessage("\nAll ad operations complete, terminating GMA"); + + firebase::gma::Terminate(); + delete app; + + // Wait until the user kills the app. + while (!ProcessEvents(1000)) { + } + + return 0; +} + +void LoadAndShowAdView(const firebase::gma::AdRequest &ad_request) { + LogMessage("\nLoad and show a banner ad in an AdView:"); + LogMessage("==="); + // Initialize an AdView. + firebase::gma::AdView *ad_view = new firebase::gma::AdView(); + const firebase::gma::AdSize banner_ad_size = firebase::gma::AdSize::kBanner; + ad_view->Initialize(GetWindowContext(), kBannerAdUnit, banner_ad_size); + + // Block until the ad view completes initialization. + WaitForFutureCompletion(ad_view->InitializeLastResult()); + + // Check for errors. + if (ad_view->InitializeLastResult().error() != + firebase::gma::kAdErrorCodeNone) { + LogMessage("AdView initalization failed, error code: %d", + ad_view->InitializeLastResult().error()); + delete ad_view; + ad_view = nullptr; + return; + } + + // Setup the AdView's listeners. + LoggingAdViewListener ad_view_listener; + ad_view->SetAdListener(&ad_view_listener); + LoggingPaidEventListener paid_event_listener; + ad_view->SetPaidEventListener(&paid_event_listener); + LoggingAdViewBoundedBoxListener bounding_box_listener; + ad_view->SetBoundingBoxListener(&bounding_box_listener); + + // Load an ad. + ad_view->LoadAd(ad_request); + WaitForFutureCompletion(ad_view->LoadAdLastResult()); + + // Check for errors. + if (ad_view->LoadAdLastResult().error() != firebase::gma::kAdErrorCodeNone) { + // Log information as to why the loadAd request failed. + const firebase::gma::AdResult *result_ptr = + ad_view->LoadAdLastResult().result(); + if (result_ptr != nullptr) { + LogMessage("AdView::loadAd Failure - Code: %d Message: %s Domain: %s", + result_ptr->ad_error().code(), + result_ptr->ad_error().message().c_str(), + result_ptr->ad_error().domain().c_str()); + } + WaitForFutureCompletion(ad_view->Destroy()); + delete ad_view; + ad_view = nullptr; + return; + } + + // Log the loaded ad's dimensions. + const firebase::gma::AdSize ad_size = ad_view->ad_size(); + LogMessage("AdView loaded ad width: %d height: %d", ad_size.width(), + ad_size.height()); + + // Show the ad. + LogMessage("Showing the banner ad."); + WaitForFutureCompletion(ad_view->Show()); + + // Move to each of the six pre-defined positions. + LogMessage("Moving the banner ad to top-center."); + ad_view->SetPosition(firebase::gma::AdView::kPositionTop); + WaitForFutureCompletion(ad_view->SetPositionLastResult()); + + LogMessage("Moving the banner ad to top-left."); + ad_view->SetPosition(firebase::gma::AdView::kPositionTopLeft); + WaitForFutureCompletion(ad_view->SetPositionLastResult()); + + LogMessage("Moving the banner ad to top-right."); + ad_view->SetPosition(firebase::gma::AdView::kPositionTopRight); + WaitForFutureCompletion(ad_view->SetPositionLastResult()); + + LogMessage("Moving the banner ad to bottom-center."); + ad_view->SetPosition(firebase::gma::AdView::kPositionBottom); + WaitForFutureCompletion(ad_view->SetPositionLastResult()); + + LogMessage("Moving the banner ad to bottom-left."); + ad_view->SetPosition(firebase::gma::AdView::kPositionBottomLeft); + WaitForFutureCompletion(ad_view->SetPositionLastResult()); + + LogMessage("Moving the banner ad to bottom-right."); + ad_view->SetPosition(firebase::gma::AdView::kPositionBottomRight); + WaitForFutureCompletion(ad_view->SetPositionLastResult()); + + // Try some coordinate moves. + LogMessage("Moving the banner ad to (100, 300)."); + ad_view->SetPosition(100, 300); + WaitForFutureCompletion(ad_view->SetPositionLastResult()); + + LogMessage("Moving the banner ad to (100, 400)."); + ad_view->SetPosition(100, 400); + WaitForFutureCompletion(ad_view->SetPositionLastResult()); + + // Try hiding and showing the BannerView. + LogMessage("Hiding the banner ad."); + ad_view->Hide(); + WaitForFutureCompletion(ad_view->HideLastResult()); + + LogMessage("Showing the banner ad."); + ad_view->Show(); + WaitForFutureCompletion(ad_view->ShowLastResult()); + + LogMessage("Hiding the banner ad again now that we're done with it."); + ad_view->Hide(); + WaitForFutureCompletion(ad_view->HideLastResult()); + + // Clean up the ad view. + ad_view->Destroy(); + WaitForFutureCompletion(ad_view->DestroyLastResult()); + delete ad_view; + ad_view = nullptr; +} + +void LoadAndShowInterstitialAd(const firebase::gma::AdRequest &ad_request) { + LogMessage("\nLoad and show an interstitial ad:"); + LogMessage("==="); + // Initialize an InterstitialAd. + firebase::gma::InterstitialAd *interstitial_ad = + new firebase::gma::InterstitialAd(); + interstitial_ad->Initialize(GetWindowContext()); + + // Block until the interstitial ad completes initialization. + WaitForFutureCompletion(interstitial_ad->InitializeLastResult()); + + // Check for errors. + if (interstitial_ad->InitializeLastResult().error() != + firebase::gma::kAdErrorCodeNone) { + delete interstitial_ad; + interstitial_ad = nullptr; + return; + } + + // Setup the interstitial ad's listeners. + LoggingFullScreenContentListener fullscreen_content_listener; + interstitial_ad->SetFullScreenContentListener(&fullscreen_content_listener); + LoggingPaidEventListener paid_event_listener; + interstitial_ad->SetPaidEventListener(&paid_event_listener); + + // Load an ad. + interstitial_ad->LoadAd(kInterstitialAdUnit, ad_request); + WaitForFutureCompletion(interstitial_ad->LoadAdLastResult()); + + // Check for errors. + if (interstitial_ad->LoadAdLastResult().error() != + firebase::gma::kAdErrorCodeNone) { + // Log information as to why the loadAd request failed. + const firebase::gma::AdResult *result_ptr = + interstitial_ad->LoadAdLastResult().result(); + if (result_ptr != nullptr) { + LogMessage( + "InterstitialAd::loadAd Failure - Code: %d Message: %s Domain: %s", + result_ptr->ad_error().code(), + result_ptr->ad_error().message().c_str(), + result_ptr->ad_error().domain().c_str()); + } + delete interstitial_ad; + interstitial_ad = nullptr; + return; + } + + // Show the ad. + LogMessage("Showing the interstitial ad."); + interstitial_ad->Show(); + WaitForFutureCompletion(interstitial_ad->ShowLastResult()); + + // Wait for the user to close the interstitial. + while (fullscreen_content_listener.num_ad_dismissed() == 0) { + ProcessEvents(1000); + } + + // Clean up the interstitial ad. + delete interstitial_ad; + interstitial_ad = nullptr; +} + +// WIP +void LoadAndShowRewardedAd(const firebase::gma::AdRequest &ad_request) { + LogMessage("\nLoad and show a rewarded ad:"); + LogMessage("==="); + // Initialize a RewardedAd. + firebase::gma::RewardedAd *rewarded_ad = new firebase::gma::RewardedAd(); + rewarded_ad->Initialize(GetWindowContext()); + + // Block until the interstitial ad completes initialization. + WaitForFutureCompletion(rewarded_ad->InitializeLastResult()); + + // Check for errors. + if (rewarded_ad->InitializeLastResult().error() != + firebase::gma::kAdErrorCodeNone) { + delete rewarded_ad; + rewarded_ad = nullptr; + return; + } + + // Setup the rewarded ad's lifecycle listeners. + LoggingFullScreenContentListener fullscreen_content_listener; + rewarded_ad->SetFullScreenContentListener(&fullscreen_content_listener); + LoggingPaidEventListener paid_event_listener; + rewarded_ad->SetPaidEventListener(&paid_event_listener); + + // Load an ad. + rewarded_ad->LoadAd(kRewardedAdUnit, ad_request); + WaitForFutureCompletion(rewarded_ad->LoadAdLastResult()); + + // Check for errors. + if (rewarded_ad->LoadAdLastResult().error() != + firebase::gma::kAdErrorCodeNone) { + // Log information as to why the loadAd request failed. + const firebase::gma::AdResult *result_ptr = + rewarded_ad->LoadAdLastResult().result(); + if (result_ptr != nullptr) { + LogMessage("RewardedAd::loadAd Failure - Code: %d Message: %s Domain: %s", + result_ptr->ad_error().code(), + result_ptr->ad_error().message().c_str(), + result_ptr->ad_error().domain().c_str()); + } + delete rewarded_ad; + rewarded_ad = nullptr; + return; + } + + // Show the ad. + LogMessage("Showing the rewarded ad."); + LoggingUserEarnedRewardListener user_earned_reward_listener; + rewarded_ad->Show(&user_earned_reward_listener); + WaitForFutureCompletion(rewarded_ad->ShowLastResult()); + + // Wait for the user to close the interstitial. + while (fullscreen_content_listener.num_ad_dismissed() == 0) { + ProcessEvents(1000); + } + + // Clean up the interstitial ad. + delete rewarded_ad; + rewarded_ad = nullptr; +} diff --git a/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 +#define chdir _chdir +#else +#include +#endif // _WIN32 + +#ifdef _WIN32 +#include +#endif // _WIN32 + +#include +#include + +#include "main.h" // NOLINT + +// The TO_STRING macro is useful for command line defined strings as the quotes +// get stripped. +#define TO_STRING_EXPAND(X) #X +#define TO_STRING(X) TO_STRING_EXPAND(X) + +// Path to the Firebase config file to load. +#ifdef FIREBASE_CONFIG +#define FIREBASE_CONFIG_STRING TO_STRING(FIREBASE_CONFIG) +#else +#define FIREBASE_CONFIG_STRING "" +#endif // FIREBASE_CONFIG + +extern "C" int common_main(int argc, const char *argv[]); + +static bool quit = false; + +#ifdef _WIN32 +static BOOL WINAPI SignalHandler(DWORD event) { + if (!(event == CTRL_C_EVENT || event == CTRL_BREAK_EVENT)) { + return FALSE; + } + quit = true; + return TRUE; +} +#else +static void SignalHandler(int /* ignored */) { quit = true; } +#endif // _WIN32 + +bool ProcessEvents(int msec) { +#ifdef _WIN32 + Sleep(msec); +#else + usleep(msec * 1000); +#endif // _WIN32 + return quit; +} + +std::string PathForResource() { return std::string(); } + +void LogMessage(const char *format, ...) { + va_list list; + va_start(list, format); + vprintf(format, list); + va_end(list); + printf("\n"); + fflush(stdout); +} + +WindowContext GetWindowContext() { return nullptr; } + +// Change the current working directory to the directory containing the +// specified file. +void ChangeToFileDirectory(const char *file_path) { + std::string path(file_path); + std::replace(path.begin(), path.end(), '\\', '/'); + auto slash = path.rfind('/'); + if (slash != std::string::npos) { + std::string directory = path.substr(0, slash); + if (!directory.empty()) + chdir(directory.c_str()); + } +} + +int main(int argc, const char *argv[]) { + ChangeToFileDirectory(FIREBASE_CONFIG_STRING[0] != '\0' + ? FIREBASE_CONFIG_STRING + : argv[0]); // NOLINT +#ifdef _WIN32 + SetConsoleCtrlHandler((PHANDLER_ROUTINE)SignalHandler, TRUE); +#else + signal(SIGINT, SignalHandler); +#endif // _WIN32 + return common_main(argc, argv); +} + +#if defined(_WIN32) +// Returns the number of microseconds since the epoch. +int64_t WinGetCurrentTimeInMicroseconds() { + FILETIME file_time; + GetSystemTimeAsFileTime(&file_time); + + ULARGE_INTEGER now; + now.LowPart = file_time.dwLowDateTime; + now.HighPart = file_time.dwHighDateTime; + + // Windows file time is expressed in 100s of nanoseconds. + // To convert to microseconds, multiply x10. + return now.QuadPart * 10LL; +} +#endif diff --git a/invites/testapp/src/ios/ios_main.mm b/gma/testapp/src/ios/ios_main.mm similarity index 74% rename from invites/testapp/src/ios/ios_main.mm rename to gma/testapp/src/ios/ios_main.mm index 6ccb2de5..4c226d5e 100644 --- a/invites/testapp/src/ios/ios_main.mm +++ b/gma/testapp/src/ios/ios_main.mm @@ -18,9 +18,9 @@ #include "main.h" -extern "C" int common_main(int argc, const char* argv[]); +extern "C" int common_main(int argc, const char *argv[]); -@interface AppDelegate : UIResponder +@interface AppDelegate : UIResponder @property(nonatomic, strong) UIWindow *window; @@ -42,33 +42,35 @@ @implementation FTAViewController - (void)viewDidLoad { [super viewDidLoad]; g_parent_view = self.view; - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - const char *argv[] = {FIREBASE_TESTAPP_NAME}; - [g_shutdown_signal lock]; - g_exit_status = common_main(1, argv); - [g_shutdown_complete signal]; - }); + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), + ^{ + const char *argv[] = {FIREBASE_TESTAPP_NAME}; + [g_shutdown_signal lock]; + g_exit_status = common_main(1, argv); + [g_shutdown_complete signal]; + }); } @end bool ProcessEvents(int msec) { [g_shutdown_signal - waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:static_cast(msec) / 1000.0f]]; + waitUntilDate:[NSDate + dateWithTimeIntervalSinceNow:static_cast(msec) / + 1000.0f]]; return g_shutdown; } -WindowContext GetWindowContext() { - return g_parent_view; -} +WindowContext GetWindowContext() { return g_parent_view; } // Log a message that can be viewed in the console. -void LogMessage(const char* format, ...) { +void LogMessage(const char *format, ...) { va_list args; NSString *formatString = @(format); va_start(args, format); - NSString *message = [[NSString alloc] initWithFormat:formatString arguments:args]; + NSString *message = [[NSString alloc] initWithFormat:formatString + arguments:args]; va_end(args); NSLog(@"%@", message); @@ -79,7 +81,7 @@ void LogMessage(const char* format, ...) { }); } -int main(int argc, char* argv[]) { +int main(int argc, char *argv[]) { @autoreleasepool { UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } @@ -88,8 +90,8 @@ int main(int argc, char* argv[]) { @implementation AppDelegate -- (BOOL)application:(UIApplication*)application - didFinishLaunchingWithOptions:(NSDictionary*)launchOptions { +- (BOOL)application:(UIApplication *)application + didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { g_shutdown_complete = [[NSCondition alloc] init]; g_shutdown_signal = [[NSCondition alloc] init]; [g_shutdown_complete lock]; diff --git a/dynamic_links/testapp/src/main.h b/gma/testapp/src/main.h similarity index 76% rename from dynamic_links/testapp/src/main.h rename to gma/testapp/src/main.h index 2eda2c10..d41017a7 100644 --- a/dynamic_links/testapp/src/main.h +++ b/gma/testapp/src/main.h @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef FIREBASE_TESTAPP_MAIN_H_ // NOLINT -#define FIREBASE_TESTAPP_MAIN_H_ // NOLINT +#ifndef FIREBASE_TESTAPP_MAIN_H_ // NOLINT +#define FIREBASE_TESTAPP_MAIN_H_ // NOLINT #if defined(__ANDROID__) #include @@ -21,18 +21,18 @@ #elif defined(__APPLE__) extern "C" { #include -} // extern "C" -#endif // __ANDROID__ +} // extern "C" +#endif // __ANDROID__ // Defined using -DANDROID_MAIN_APP_NAME=some_app_name when compiling this // file. #ifndef FIREBASE_TESTAPP_NAME #define FIREBASE_TESTAPP_NAME "android_main" -#endif // FIREBASE_TESTAPP_NAME +#endif // FIREBASE_TESTAPP_NAME // Cross platform logging method. // Implemented by android/android_main.cc or ios/ios_main.mm. -extern "C" void LogMessage(const char* format, ...); +extern "C" void LogMessage(const char *format, ...); // Platform-independent method to flush pending events for the main thread. // Returns true when an event requesting program-exit is received. @@ -41,23 +41,23 @@ bool ProcessEvents(int msec); // WindowContext represents the handle to the parent window. It's type // (and usage) vary based on the OS. #if defined(__ANDROID__) -typedef jobject WindowContext; // A jobject to the Java Activity. +typedef jobject WindowContext; // A jobject to the Java Activity. #elif defined(__APPLE__) -typedef id WindowContext; // A pointer to an iOS UIView. +typedef id WindowContext; // A pointer to an iOS UIView. #else -typedef void* WindowContext; // A void* for any other environments. +typedef void *WindowContext; // A void* for any other environments. #endif #if defined(__ANDROID__) // Get the JNI environment. -JNIEnv* GetJniEnv(); +JNIEnv *GetJniEnv(); // Get the activity. jobject GetActivity(); -#endif // defined(__ANDROID__) +#endif // defined(__ANDROID__) // Returns a variable that describes the window context for the app. On Android // this will be a jobject pointing to the Activity. On iOS, it's an id pointing // to the root view of the view controller. WindowContext GetWindowContext(); -#endif // FIREBASE_TESTAPP_MAIN_H_ // NOLINT +#endif // FIREBASE_TESTAPP_MAIN_H_ // NOLINT diff --git a/admob/testapp/testapp.xcodeproj/project.pbxproj b/gma/testapp/testapp.xcodeproj/project.pbxproj similarity index 99% rename from admob/testapp/testapp.xcodeproj/project.pbxproj rename to gma/testapp/testapp.xcodeproj/project.pbxproj index 14a0af7f..3afd2dea 100644 --- a/admob/testapp/testapp.xcodeproj/project.pbxproj +++ b/gma/testapp/testapp.xcodeproj/project.pbxproj @@ -208,7 +208,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.4; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -245,7 +245,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.4; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/admob/testapp/testapp/Images.xcassets/AppIcon.appiconset/Contents.json b/gma/testapp/testapp/Images.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from admob/testapp/testapp/Images.xcassets/AppIcon.appiconset/Contents.json rename to gma/testapp/testapp/Images.xcassets/AppIcon.appiconset/Contents.json diff --git a/admob/testapp/testapp/Images.xcassets/LaunchImage.launchimage/Contents.json b/gma/testapp/testapp/Images.xcassets/LaunchImage.launchimage/Contents.json similarity index 100% rename from admob/testapp/testapp/Images.xcassets/LaunchImage.launchimage/Contents.json rename to gma/testapp/testapp/Images.xcassets/LaunchImage.launchimage/Contents.json diff --git a/admob/testapp/testapp/Info.plist b/gma/testapp/testapp/Info.plist similarity index 91% rename from admob/testapp/testapp/Info.plist rename to gma/testapp/testapp/Info.plist index 4744d9dc..3f0f944e 100644 --- a/admob/testapp/testapp/Info.plist +++ b/gma/testapp/testapp/Info.plist @@ -2,6 +2,8 @@ + GADApplicationIdentifier + YOUR_IOS_ADMOB_APP_ID CFBundleDevelopmentRegion en CFBundleExecutable @@ -16,8 +18,6 @@ APPL CFBundleShortVersionString 1.0 - CFBundleSignature - ???? CFBundleVersion 1 LSRequiresIPhoneOS diff --git a/invites/testapp/LICENSE b/invites/testapp/LICENSE deleted file mode 100644 index 25dd9993..00000000 --- a/invites/testapp/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2016 Google Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/invites/testapp/Podfile b/invites/testapp/Podfile deleted file mode 100644 index ee5d54c6..00000000 --- a/invites/testapp/Podfile +++ /dev/null @@ -1,6 +0,0 @@ -source 'https://github.com/CocoaPods/Specs.git' -platform :ios, '8.0' -# Invites test application. -target 'testapp' do - pod 'Firebase/Invites' -end diff --git a/invites/testapp/build.gradle b/invites/testapp/build.gradle deleted file mode 100644 index fc6cae94..00000000 --- a/invites/testapp/build.gradle +++ /dev/null @@ -1,121 +0,0 @@ -// Top-level build file where you can add configuration options common to all sub-projects/modules. -buildscript { - repositories { - mavenLocal() - maven { url 'https://maven.google.com' } - jcenter() - } - dependencies { - classpath 'com.android.tools.build:gradle:2.1.2' - classpath 'com.google.gms:google-services:3.0.0' - } -} - -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' - - sourceSets { - main { - jniLibs.srcDirs = ['libs'] - manifest.srcFile 'AndroidManifest.xml' - java.srcDirs = ['src/android/java'] - res.srcDirs = ['res'] - } - } - - defaultConfig { - applicationId 'com.google.android.invites.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/invites.pro") - proguardFile file('proguard.pro') - } - } -} - -dependencies { - compile 'com.google.firebase:firebase-invites:11.2.0' - compile 'com.google.android.gms:play-services-base:11.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/invites/testapp/jni/Android.mk b/invites/testapp/jni/Android.mk deleted file mode 100644 index 7cf2588a..00000000 --- a/invites/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_invites -LOCAL_SRC_FILES:=$(FIREBASE_LIBRARY_PATH)/libinvites.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_invites \ - 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/invites/testapp/jni/Application.mk b/invites/testapp/jni/Application.mk deleted file mode 100644 index 53ed56a2..00000000 --- a/invites/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/invites/testapp/readme.md b/invites/testapp/readme.md deleted file mode 100644 index 030a29fc..00000000 --- a/invites/testapp/readme.md +++ /dev/null @@ -1,188 +0,0 @@ -Firebase Invites Quickstart -============================== - -The Firebase Invites Test Application (testapp) demonstrates both -sending and receiving Firebase Invites using the Firebase Invites C++ -SDK. This application has no user interface and simply logs actions -it's performing to the console. - -Introduction ------------- - -- [Read more about Firebase Invites](https://firebase.google.com/docs/invites/) -- [Read more about Firebase Dynamic Links](https://firebase.google.com/docs/dynamic-links/) - -Building and Running the testapp --------------------------------- - -### iOS - - Link your iOS app to the Firebase libraries. - - Get CocoaPods version 1 or later by running, - ``` - $ sudo gem install CocoaPods --pre - ``` - - From the testapp directory, install the CocoaPods listed in the Podfile - by running, - ``` - $ pod install - ``` - - Open the generated Xcode workspace (which now has the CocoaPods), - ``` - $ open testapp.xcworkspace - ``` - - For further details please refer to the - [general instructions for setting up an iOS app with Firebase](https://firebase.google.com/docs/ios/setup). - - Register your iOS app with Firebase. - - Create a new app on - [firebase.google.com/console](https://firebase.google.com/console/), - and attach your iOS app to it. - - For Invites, you will need an App Store ID. Use something random such - as 12345678." - - You can use "com.google.ios.invites.testapp" as the iOS Bundle ID - while you're 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. - - Firebase Invites uses Google Sign-In to send invites needs to be - configured. - - Enable the Keychain Sharing capability on iOS 10 or above. - You can enable this capability on your project in Xcode 8 by going to - your project's settings, Capabilities, and turning on Keychain Sharing. - - Configure a URL type to handle the Google Sign-In 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. - - Configure the app to handle dynamic links / app invites. - - In your project's Info tab, under the URL Types section, find the URL - Schemes box and add your app's bundle ID (the default scheme used - by dynamic links). - - Copy the dynamic links domain for your project under the Dynamic Links - tab of the [Firebase console](https://firebase.google.com/console/) - Then, in your project's Capabilities tab: - - Enable the Associated Domains capability. - - Add applinks:YOUR_DYNAMIC_LINKS_DOMAIN - For example "applinks:xyz.app.goo.gl". - - - 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_invites.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". - - _Known Issue_: There is an issue where bundles from the FirebaseInvites - Cocoapod are not properly copied into the Xcode project. To work around the - issue, you can manually copy the bundles into your project. Navigate to the - Pods/FirebaseInvites/Frameworks/frameworks/FirebaseInvites.framework/Resources - subdirectory of your project's folder, and drag the - GINInviteResources.bundle and GPPACLPickerResources.bundle folders into your - Xcode project, selecting "Copy items if needed" when adding them. - - In Xcode, build & run the sample on an iOS device or simulator. - - 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. - -### Android - - Register your Android app with Firebase. - - Create a new app on - [developers.google.com](https://firebase.google.com/console/), - and attach your Android app to it. - - You can use "com.google.android.invites.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 `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. - - See [below](#using_the_test_app) for usage instructions. - -# Using the Test App - -- Install and run the test app on your iOS or Android device or emulator. -- The application has minimal user interface. The output of the app can be viewed - via the console: - - __iOS__: Open select "View --> Debug Area --> Activate Console" from the menu - in Xcode. - - __Android__: View the logcat output in Android studio or by running - "adb logcat" from the command line. - -- When you first run the app, it will check for an incoming dynamic link or - invitation, and report whether it was able to fetch an invite. -- Afterwards, it will open a screen that allows you to send an invite for the - current app via e-mail or SMS. - - You may have to log in to Google first. -- To simulate receiving an invitation from a friend, you can send yourself an - invite, uninstall the test app, then click the link in your e-mail. - - This would normally send you to the Play Store or App Store to download the - app. Because this is a test app, it will link to a nonexistent store page. -- After clicking the invite link, re-install and run the app on your device or - emulator, and see the invitation fetched on the receiving side. - -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/invites/testapp/src/common_main.cc b/invites/testapp/src/common_main.cc deleted file mode 100644 index c585142e..00000000 --- a/invites/testapp/src/common_main.cc +++ /dev/null @@ -1,149 +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/app.h" -#include "firebase/future.h" -#include "firebase/invites.h" -#include "firebase/util.h" - -// Thin OS abstraction layer. -#include "main.h" // NOLINT - -void ConversionFinished(const firebase::Future& future_result, - void* user_data) { - if (future_result.status() == firebase::kFutureStatusInvalid) { - LogMessage("ConvertInvitation: Invalid, sorry!"); - } else if (future_result.status() == firebase::kFutureStatusComplete) { - LogMessage("ConvertInvitation: Complete!"); - if (future_result.error() != 0) { - LogMessage("ConvertInvitation: Error %d: %s", future_result.error(), - future_result.error_message()); - } else { - LogMessage("ConvertInvitation: Successfully converted invitation"); - } - } -} - -class InviteListener : public firebase::invites::Listener { - public: - void OnInviteReceived(const char* invitation_id, const char* deep_link, - bool is_strong_match) override { // NOLINT - if (invitation_id != nullptr) { - LogMessage("InviteReceived: Got invitation ID: %s", invitation_id); - - // We got an invitation ID, so let's try and convert it. - LogMessage("ConvertInvitation: Converting invitation %s", invitation_id); - - ::firebase::invites::ConvertInvitation(invitation_id) - .OnCompletion(ConversionFinished, nullptr); - } - if (deep_link != nullptr) { - LogMessage("InviteReceived: Got deep link: %s", deep_link); - } - } - - void OnInviteNotReceived() override { - LogMessage("InviteReceived: No invitation ID or deep link, confirmed."); - } - - void OnErrorReceived(int error_code, const char* error_message) override { - LogMessage("Error (%d) on received invite: %s", error_code, error_message); - } -}; - -InviteListener g_listener; - -// Execute all methods of the C++ Invites API. -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))); - - ::firebase::ModuleInitializer initializer; - initializer.Initialize(app, nullptr, [](::firebase::App* app, void*) { - LogMessage("Try to initialize Invites"); - return ::firebase::invites::Initialize(*app); - }); - while (initializer.InitializeLastResult().status() != - firebase::kFutureStatusComplete) { - if (ProcessEvents(100)) return 1; // exit if requested - } - if (initializer.InitializeLastResult().error() != 0) { - LogMessage("Failed to initialize Firebase Invites: %s", - initializer.InitializeLastResult().error_message()); - ProcessEvents(2000); - return 1; - } - LogMessage("Initialized Firebase Invites."); - - // First, try sending an Invite. - { - LogMessage("SendInvite: Sending an invitation..."); - ::firebase::invites::Invite invite; - invite.title_text = "Invites Test App"; - invite.message_text = "Please try my app! It's awesome."; - invite.call_to_action_text = "Download it for FREE"; - invite.deep_link_url = "http://google.com/abc"; - auto future_result = ::firebase::invites::SendInvite(invite); - while (future_result.status() == firebase::kFutureStatusPending) { - if (ProcessEvents(10)) break; - } - - if (future_result.status() == firebase::kFutureStatusInvalid) { - LogMessage("SendInvite: Invalid, sorry!"); - } else if (future_result.status() == firebase::kFutureStatusComplete) { - LogMessage("SendInvite: Complete!"); - if (future_result.error() != 0) { - LogMessage("SendInvite: Error %d: %s", future_result.error(), - future_result.error_message()); - } else { - auto result = *future_result.result(); - // error == 0 - if (result.invitation_ids.size() == 0) { - LogMessage("SendInvite: Nothing sent, user must have canceled."); - } else { - LogMessage("SendInvite: %d invites sent successfully.", - result.invitation_ids.size()); - for (int i = 0; i < result.invitation_ids.size(); i++) { - LogMessage("SendInvite: Invite code: %s", - result.invitation_ids[i].c_str()); - } - } - } - } - } - - // Then, set the listener, which will check for any invitations. - ::firebase::invites::SetListener(&g_listener); - - LogMessage("Listener set, main loop finished."); - - while (!ProcessEvents(1000)) { - } - - ::firebase::invites::Terminate(); - delete app; - app = nullptr; - - return 0; -} diff --git a/invites/testapp/testapp/Info.plist b/invites/testapp/testapp/Info.plist deleted file mode 100644 index 1590f803..00000000 --- a/invites/testapp/testapp/Info.plist +++ /dev/null @@ -1,57 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - com.google.ios.invites.testapp - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleURLSchemes - - com.google.ios.invites.testapp - - CFBundleURLTypes - - - CFBundleTypeRole - Editor - CFBundleURLName - com.google.ios.invites.testapp - CFBundleURLSchemes - - com.google.ios.invites.testapp - - - - CFBundleTypeRole - Editor - CFBundleURLName - google - CFBundleURLSchemes - - YOUR_REVERSED_CLIENT_ID - - - - CFBundleVersion - 1 - LSRequiresIPhoneOS - - UILaunchStoryboardName - LaunchScreen - NSContactsUsageDescription - Invite others to use the app. - - diff --git a/messaging/testapp/AndroidManifest.xml b/messaging/testapp/AndroidManifest.xml index 44c2159d..c0357769 100644 --- a/messaging/testapp/AndroidManifest.xml +++ b/messaging/testapp/AndroidManifest.xml @@ -3,7 +3,7 @@ package="com.google.android.messaging.testapp" android:versionCode="1" android:versionName="1.0"> - + @@ -19,6 +19,7 @@ work around a known issue when receiving notification data payloads in the background. --> @@ -28,26 +29,32 @@ - - + - + + + + + - + - - + diff --git a/messaging/testapp/CMakeLists.txt b/messaging/testapp/CMakeLists.txt new file mode 100644 index 00000000..03bd733e --- /dev/null +++ b/messaging/testapp/CMakeLists.txt @@ -0,0 +1,118 @@ +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 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_messaging firebase_app) +target_link_libraries(${target_name} "${firebase_libs}" ${ADDITIONAL_LIBS}) diff --git a/messaging/testapp/Podfile b/messaging/testapp/Podfile index ce69f3b8..b022880f 100644 --- a/messaging/testapp/Podfile +++ b/messaging/testapp/Podfile @@ -1,6 +1,7 @@ source 'https://github.com/CocoaPods/Specs.git' -platform :ios, '8.0' +platform :ios, '13.0' +use_frameworks! # FCM test application. target 'testapp' do - pod 'Firebase/Messaging' + pod 'Firebase/Messaging', '10.25.0' end diff --git a/messaging/testapp/build.gradle b/messaging/testapp/build.gradle index 47a311c9..4f607fe3 100644 --- a/messaging/testapp/build.gradle +++ b/messaging/testapp/build.gradle @@ -6,8 +6,8 @@ buildscript { 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' } } @@ -21,107 +21,56 @@ allprojects { 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.messaging.testapp' - minSdkVersion 14 - targetSdkVersion 23 - versionCode 1 - versionName '1.0' + defaultConfig { + applicationId 'com.google.android.messaging.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/messaging.pro") - proguardFile file('proguard.pro') - } + } + externalNativeBuild.cmake { + path 'CMakeLists.txt' + } + buildTypes { + release { + minifyEnabled true + proguardFile getDefaultProguardFile('proguard-android.txt') + proguardFile file('proguard.pro') } -} - -repositories { - flatDir { - dirs project.ext.firebase_cpp_sdk_dir + "/libs/android" + } + packagingOptions { + pickFirst 'META-INF/**/coroutines.pro' + } + lintOptions { + abortOnError false + checkReleaseBuilds false } } -dependencies { - compile 'com.google.firebase.messaging.cpp:firebase_messaging_cpp@aar' - compile 'com.google.firebase:firebase-messaging:11.2.0' - compile 'com.google.android.gms:play-services-base:11.2.0' +apply from: "$gradle.firebase_cpp_sdk_dir/Android/firebase_dependencies.gradle" +firebaseCpp.dependencies { + messaging } 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/messaging/testapp/gradle.properties b/messaging/testapp/gradle.properties new file mode 100644 index 00000000..d7ba8f42 --- /dev/null +++ b/messaging/testapp/gradle.properties @@ -0,0 +1 @@ +android.useAndroidX = true diff --git a/messaging/testapp/gradle/wrapper/gradle-wrapper.properties b/messaging/testapp/gradle/wrapper/gradle-wrapper.properties index d5705170..65340c1b 100644 --- a/messaging/testapp/gradle/wrapper/gradle-wrapper.properties +++ b/messaging/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/messaging/testapp/jni/Android.mk b/messaging/testapp/jni/Android.mk deleted file mode 100644 index 15ae2801..00000000 --- a/messaging/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_messaging -LOCAL_SRC_FILES:=$(FIREBASE_LIBRARY_PATH)/libmessaging.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_messaging \ - 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/messaging/testapp/jni/Application.mk b/messaging/testapp/jni/Application.mk deleted file mode 100644 index 53ed56a2..00000000 --- a/messaging/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/messaging/testapp/readme.md b/messaging/testapp/readme.md index f3b8bc19..c5a59969 100644 --- a/messaging/testapp/readme.md +++ b/messaging/testapp/readme.md @@ -1,168 +1,48 @@ Firebase Cloud Messaging Quickstart =================================== -The Firebase Cloud Messaging Test Application (testapp) demonstrates receiving -Firebase Cloud Messages using the Firebase Cloud Messaging C++ SDK. This +The Firebase Messaging C++ Sample (hereafter the 'test app') demonstrates receiving messages from [Firebase Cloud Messaging](https://firebase.google.com/docs/cloud-messaging/) +using the [Firebase C++ SDK](https://firebase.google.com/docs/cpp/setup). This application has no user interface and simply logs actions it's performing to the console. -Introduction ------------- +## Set up, build and run the test app -- [Read more about Firebase Cloud Messaging](https://firebase.google.com/docs/cloud-messaging/) +> **_NOTE:_** Do not implement the code changes of the subsequent linked instructions as this codebase already has those functionalities. -Building and Running the testapp --------------------------------- -### iOS - - Link your iOS app to the Firebase libraries. - - Get CocoaPods version 1 or later by running, +1. Complete [Add Firebase to your C++ project](https://firebase.google.com/docs/cpp/setup) for your selected build platforms. +1. [Set up a Firebase Cloud Messaging client app with C++](https://firebase.google.com/docs/cloud-messaging/cpp/client). - ``` - $ 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 - [firebase.google.com/console](https://firebase.google.com/console/) - , and attach your iOS app to it. - - For Messaging, you will need an App Store ID. Use something random such - as 12345678." - - You can use "com.google.ios.messaging.testapp" as the iOS Bundle ID - while you're 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. - - 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_messaging.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". - - You need a valid - [APNs](https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/ApplePushService.html) - certificate. If you don't already have one, see - [Provisioning APNs SSL Certificates.](https://firebase.google.com/docs/cloud-messaging/ios/certs) - - In XCode, build & run the sample on an iOS device or simulator. - - 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. - -### Android -**Register your Android app with Firebase.** - -- Create a new app on -[firebase.google.com/console](https://firebase.google.com/console/) -, and attach your Android app to it. -- You can use "com.google.android.messaging.testapp" as the Package Name -while you're testing. -- To [generate a SHA1](https://developers.google.com/android/guides/client-auth) -run this command (_Note: the default password is 'android'_): - * Mac and Linux: - - ``` - keytool -exportcert -list -v -alias androiddebugkey -keystore ~/.android/debug.keystore - ``` - * 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](http://developer.android.com/tools/publishing/app-signing.html#signing-manually) -with: - - ``` - 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 your SDK paths** - -- Configure the location of the Firebase C++ SDK by setting the -`firebase_cpp_sdk.dir` Gradle property to the SDK install directory. - * Run this command in the project directory, and modify the path to match your - local installation path: - - ``` - > 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. - * Android Studio will write these paths to `local.properties`. - -**Build & Run** - -- 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. -- See [below](#using_the_test_app) for usage instructions. - -Using the Test App ------------------- +### Running the test app +---------------------- - Install and run the test app on your iOS or Android device or emulator. - The application has minimal user interface. The output of the app can be viewed via the console: - * **iOS**: Open select "View --> Debug Area --> Activate Console" from the + * **iOS**: Open select "View -> Debug Area -> Activate Console" from the menu in Xcode. * **Android**: View the logcat output in Android studio or by running - "adb logcat" from the command line. + `adb logcat` from the command line. - When you first run the app, it will print: -`Recieved Registration Token: `. Copy this code to a text editor. -- Copy the ServerKey from the firebase console: - - Open your project in the - [Firebase Console](https://firebase.google.com/console/). - - Click the gear icon then `Project settings` in the menu on the left - - Select the `Cloud Messaging` tab. - - Copy the `Server Key` -- Replace `` and `` in this command and run it -from the command line. -``` -curl --header "Authorization: key=" --header "Content-Type: application/json" https://android.googleapis.com/gcm/send -d '{"notification":{"title":"Hi","body":"Hello from the Cloud"},"data":{"score":"lots"},"to":""}' -``` -- Observe the command received in the app, via the console output. +`Received Registration Token: `. Copy this code to a text editor. -Support -------- +### Send a message from your server environment +----------------------------------------------- + +1. Navigate to the FCM REST API Docs for [Method: projects.messages.send](https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages/send) +1. Look for the "Try this method" panel + 1. This might require you to expand your browser window. +1. Follow the API doc to understand what IDs and fields to fill in for your particular use case. + 1. Your project number is available from [Project settings](https://console.firebase.google.com/project/_/settings/general) +1. Fill in the `Request body` referencing the [FCM REST API docs](https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages). +1. Execute. +1. Observe the command received in the app, via the console output. + +Reminder, while this process is currently done from our website, it uses the FCM v1 Send API directly and provides the simplest starting point to switch to using the v1 Send API or the Firebase Admin SDK in production. -[https://firebase.google.com/support/]() +## Troubleshooting and Support +- [Firebase Cloud Messaging troubleshooting & FAQ](https://firebase.google.com/docs/cloud-messaging/troubleshooting) +- [Firebase Support](https://firebase.google.com/support/) License ------- diff --git a/messaging/testapp/settings.gradle b/messaging/testapp/settings.gradle new file mode 100644 index 00000000..2a543b93 --- /dev/null +++ b/messaging/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/messaging/testapp/src/android/java/com/google/firebase/example/TestappNativeActivity.java b/messaging/testapp/src/android/java/com/google/firebase/example/TestappNativeActivity.java index 37047eee..e504595a 100644 --- a/messaging/testapp/src/android/java/com/google/firebase/example/TestappNativeActivity.java +++ b/messaging/testapp/src/android/java/com/google/firebase/example/TestappNativeActivity.java @@ -53,7 +53,8 @@ protected void onNewIntent(Intent intent) { Intent message = new Intent(this, MessageForwardingService.class); message.setAction(MessageForwardingService.ACTION_REMOTE_INTENT); message.putExtras(intent); - startService(message); + message.setData(intent.getData()); + MessageForwardingService.enqueueWork(this, message); } setIntent(intent); } diff --git a/messaging/testapp/src/common_main.cc b/messaging/testapp/src/common_main.cc index f4a7cbdb..45a4fca7 100644 --- a/messaging/testapp/src/common_main.cc +++ b/messaging/testapp/src/common_main.cc @@ -13,12 +13,51 @@ // limitations under the License. #include "firebase/app.h" +#include "firebase/future.h" #include "firebase/messaging.h" #include "firebase/util.h" // Thin OS abstraction layer. #include "main.h" // NOLINT +// 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(const ::firebase::FutureBase& future, const char* fn, + ::firebase::messaging::Error expected_error, + bool log_error = true) { + // Note if the future has not be started properly. + if (future.status() == ::firebase::kFutureStatusInvalid) { + LogMessage("ERROR: Future for %s is invalid", fn); + return false; + } + + // Wait for future to complete. + LogMessage(" %s...", fn); + while (future.status() == ::firebase::kFutureStatusPending) { + if (ProcessEvents(100)) return true; + } + + // Log error result. + if (log_error) { + const ::firebase::messaging::Error error = + static_cast<::firebase::messaging::Error>(future.error()); + if (error == expected_error) { + const char* error_message = future.error_message(); + if (error_message) { + LogMessage("%s completed as expected", fn); + } else { + LogMessage("%s completed as expected, error: %d '%s'", fn, error, + error_message); + } + } else { + LogMessage("ERROR: %s completed with error: %d, `%s`", fn, error, + future.error_message()); + } + } + return false; +} + // Execute all methods of the C++ Firebase Cloud Messaging API. extern "C" int common_main(int argc, const char* argv[]) { ::firebase::App* app; @@ -40,7 +79,14 @@ extern "C" int common_main(int argc, const char* argv[]) { LogMessage("Try to initialize Firebase Messaging"); ::firebase::messaging::PollableListener* listener = static_cast<::firebase::messaging::PollableListener*>(userdata); - return ::firebase::messaging::Initialize(*app, listener); + firebase::messaging::MessagingOptions options; + // Prevent the app from requesting permission to show notifications + // immediately upon starting up. Since it the prompt is being + // suppressed, we must manually display it with a call to + // RequestPermission() elsewhere. + options.suppress_notification_permission_prompt = true; + + return ::firebase::messaging::Initialize(*app, listener, options); }); while (initializer.InitializeLastResult().status() != @@ -56,19 +102,40 @@ extern "C" int common_main(int argc, const char* argv[]) { LogMessage("Initialized Firebase Cloud Messaging."); - ::firebase::messaging::Subscribe("TestTopic"); - LogMessage("Subscribed to TestTopic"); + // This will display the prompt to request permission to receive notifications + // if the prompt has not already been displayed before. (If the user already + // responded to the prompt, their decision is cached by the OS and can be + // changed in the OS settings). + ::firebase::Future result = ::firebase::messaging::RequestPermission(); + LogMessage("Display permission prompt if necessary."); + while (result.status() == ::firebase::kFutureStatusPending) { + ProcessEvents(100); + } + if (result.error() == + ::firebase::messaging::kErrorFailedToRegisterForRemoteNotifications) { + LogMessage("Error registering for remote notifications."); + } else { + LogMessage("Finished checking for permission."); + } + + // Subscribe to topics. + WaitForFuture(::firebase::messaging::Subscribe("TestTopic"), + "::firebase::messaging::Subscribe(\"TestTopic\")", + ::firebase::messaging::kErrorNone); + WaitForFuture(::firebase::messaging::Subscribe("!@#$%^&*()"), + "::firebase::messaging::Subscribe(\"!@#$%^&*()\")", + ::firebase::messaging::kErrorInvalidTopicName); bool done = false; while (!done) { std::string token; if (listener.PollRegistrationToken(&token)) { - LogMessage("Recieved Registration Token: %s", token.c_str()); + LogMessage("Received Registration Token: %s", token.c_str()); } ::firebase::messaging::Message message; while (listener.PollMessage(&message)) { - LogMessage("Recieved a new message"); + LogMessage("Received a new message"); LogMessage("This message was %s by the user", message.notification_opened ? "opened" : "not opened"); if (!message.from.empty()) LogMessage("from: %s", message.from.c_str()); @@ -77,6 +144,9 @@ extern "C" int common_main(int argc, const char* argv[]) { if (!message.message_id.empty()) { LogMessage("message_id: %s", message.message_id.c_str()); } + if (!message.link.empty()) { + LogMessage(" link: %s", message.link.c_str()); + } if (!message.data.empty()) { LogMessage("data:"); for (const auto& field : message.data) { @@ -85,6 +155,11 @@ extern "C" int common_main(int argc, const char* argv[]) { } if (message.notification) { LogMessage("notification:"); + if (message.notification->android) { + LogMessage(" android:"); + LogMessage(" channel_id: %s", + message.notification->android->channel_id.c_str()); + } if (!message.notification->title.empty()) { LogMessage(" title: %s", message.notification->title.c_str()); } diff --git a/messaging/testapp/src/desktop/desktop_main.cc b/messaging/testapp/src/desktop/desktop_main.cc index 99ace543..0220c688 100644 --- a/messaging/testapp/src/desktop/desktop_main.cc +++ b/messaging/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/messaging/testapp/testapp.xcodeproj/project.pbxproj b/messaging/testapp/testapp.xcodeproj/project.pbxproj index da19cde8..c5f566b4 100644 --- a/messaging/testapp/testapp.xcodeproj/project.pbxproj +++ b/messaging/testapp/testapp.xcodeproj/project.pbxproj @@ -212,7 +212,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.4; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -249,7 +249,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.4; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/messaging/testapp/testapp/Info.plist b/messaging/testapp/testapp/Info.plist index 2fedb79c..2faa0b0d 100644 --- a/messaging/testapp/testapp/Info.plist +++ b/messaging/testapp/testapp/Info.plist @@ -7,7 +7,7 @@ CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier - com.google.ios.messaging.testapp + com.google.FirebaseCppMessagingTestApp.dev CFBundleInfoDictionaryVersion 6.0 CFBundleName @@ -16,8 +16,6 @@ APPL CFBundleShortVersionString 1.0 - CFBundleSignature - ???? UIBackgroundModes remote-notification @@ -31,7 +29,7 @@ google CFBundleURLSchemes - com.googleusercontent.apps.255980362477-3a1nf8c4nl0c7hlnlnmc98hbtg2mnbue + YOUR_REVERSED_CLIENT_ID diff --git a/readme.md b/readme.md index 5c7e288c..53fededc 100644 --- a/readme.md +++ b/readme.md @@ -6,6 +6,13 @@ iOS and Android samples for the For more information, see [firebase.google.com](https://firebase.google.com). +## Issue reporting + +This repo no longer accepts issue reporting. For Firebase C++ SDK related +issues, please report to +[Firebase C++ Open-source](https://github.com/firebase/firebase-cpp-sdk/issues). +Please fill in the template to expedite the support. + ## How to make contributions? Please read and follow the steps in [CONTRIBUTING.md](CONTRIBUTING.md). diff --git a/remote_config/testapp/AndroidManifest.xml b/remote_config/testapp/AndroidManifest.xml index ef0b8708..a7a904df 100644 --- a/remote_config/testapp/AndroidManifest.xml +++ b/remote_config/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_remote_config firebase_app) +target_link_libraries(${target_name} "${firebase_libs}" ${ADDITIONAL_LIBS}) diff --git a/remote_config/testapp/Podfile b/remote_config/testapp/Podfile index 6e80e7af..14a96f7d 100644 --- a/remote_config/testapp/Podfile +++ b/remote_config/testapp/Podfile @@ -1,6 +1,7 @@ source 'https://github.com/CocoaPods/Specs.git' -platform :ios, '8.0' +platform :ios, '13.0' +use_frameworks! # Firebase Remote Config test application. target 'testapp' do - pod 'Firebase/RemoteConfig' + pod 'Firebase/RemoteConfig', '10.25.0' end diff --git a/remote_config/testapp/build.gradle b/remote_config/testapp/build.gradle index 5b87d35e..4802ecbf 100644 --- a/remote_config/testapp/build.gradle +++ b/remote_config/testapp/build.gradle @@ -6,8 +6,8 @@ buildscript { 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' } } @@ -21,100 +21,56 @@ allprojects { 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.remoteconfig.testapp' - minSdkVersion 14 - targetSdkVersion 23 - versionCode 1 - versionName '1.0' + defaultConfig { + applicationId 'com.google.android.remoteconfig.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/remote_config.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-config:11.2.0' - compile 'com.google.android.gms:play-services-base:11.2.0' +apply from: "$gradle.firebase_cpp_sdk_dir/Android/firebase_dependencies.gradle" +firebaseCpp.dependencies { + remoteConfig } 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/remote_config/testapp/gradle.properties b/remote_config/testapp/gradle.properties new file mode 100644 index 00000000..d7ba8f42 --- /dev/null +++ b/remote_config/testapp/gradle.properties @@ -0,0 +1 @@ +android.useAndroidX = true diff --git a/remote_config/testapp/gradle/wrapper/gradle-wrapper.properties b/remote_config/testapp/gradle/wrapper/gradle-wrapper.properties index d5705170..65340c1b 100644 --- a/remote_config/testapp/gradle/wrapper/gradle-wrapper.properties +++ b/remote_config/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/remote_config/testapp/jni/Android.mk b/remote_config/testapp/jni/Android.mk deleted file mode 100644 index 6a8ff00a..00000000 --- a/remote_config/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_remote_config -LOCAL_SRC_FILES:=$(FIREBASE_LIBRARY_PATH)/libremote_config.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_remote_config \ - 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/remote_config/testapp/jni/Application.mk b/remote_config/testapp/jni/Application.mk deleted file mode 100644 index 53ed56a2..00000000 --- a/remote_config/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/remote_config/testapp/readme.md b/remote_config/testapp/readme.md index 87192c18..81242895 100644 --- a/remote_config/testapp/readme.md +++ b/remote_config/testapp/readme.md @@ -18,16 +18,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). @@ -41,7 +41,7 @@ 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 + [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 @@ -89,13 +89,13 @@ 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 `File/Project Structure...` or @@ -111,6 +111,49 @@ Building and Running the testapp in the logcat output of Android studio or by running "adb logcat" 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. + Using the Test App ------------------ - In the Firebase Console, under "Remote Config", you can define parameters. @@ -128,7 +171,7 @@ keys that begin with "TestD". Support ------- -[https://firebase.google.com/support/]() +[https://firebase.google.com/support/](https://firebase.google.com/support/) License ------- diff --git a/remote_config/testapp/settings.gradle b/remote_config/testapp/settings.gradle new file mode 100644 index 00000000..2a543b93 --- /dev/null +++ b/remote_config/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/remote_config/testapp/src/common_main.cc b/remote_config/testapp/src/common_main.cc index 37adbec3..3bc35e2c 100644 --- a/remote_config/testapp/src/common_main.cc +++ b/remote_config/testapp/src/common_main.cc @@ -19,32 +19,56 @@ #include "firebase/util.h" // Thin OS abstraction layer. -#include "main.h" // NOLINT +#include "main.h" // NOLINT + +using firebase::remote_config::RemoteConfig; + +// Convert remote_config::ValueSource to a string. +const char *ValueSourceToString(firebase::remote_config::ValueSource source) { + static const char *kSourceToString[] = { + "Static", // kValueSourceStaticValue + "Remote", // kValueSourceRemoteValue + "Default", // kValueSourceDefaultValue + }; + return kSourceToString[source]; +} + +RemoteConfig *rc_ = nullptr; // Execute all methods of the C++ Remote Config API. -extern "C" int common_main(int argc, const char* argv[]) { +extern "C" int common_main(int argc, const char *argv[]) { namespace remote_config = ::firebase::remote_config; - ::firebase::App* app; + ::firebase::App *app; + + // Initialization LogMessage("Initialize the Firebase Remote Config library"); #if defined(__ANDROID__) app = ::firebase::App::Create(GetJniEnv(), GetActivity()); #else app = ::firebase::App::Create(); -#endif // defined(__ANDROID__) +#endif // defined(__ANDROID__) LogMessage("Created the Firebase app %x", static_cast(reinterpret_cast(app))); ::firebase::ModuleInitializer initializer; - initializer.Initialize(app, nullptr, [](::firebase::App* app, void*) { - LogMessage("Try to initialize Remote Config"); - return ::firebase::remote_config::Initialize(*app); + + void *ptr = nullptr; + ptr = &rc_; + initializer.Initialize(app, ptr, [](::firebase::App *app, void *target) { + LogMessage("Try to initialize Firebase RemoteConfig"); + RemoteConfig **rc_ptr = reinterpret_cast(target); + *rc_ptr = RemoteConfig::GetInstance(app); + return firebase::kInitResultSuccess; }); + while (initializer.InitializeLastResult().status() != firebase::kFutureStatusComplete) { - if (ProcessEvents(100)) return 1; // exit if requested + if (ProcessEvents(100)) + return 1; // exit if requested } + if (initializer.InitializeLastResult().error() != 0) { LogMessage("Failed to initialize Firebase Remote Config: %s", initializer.InitializeLastResult().error_message()); @@ -54,6 +78,8 @@ extern "C" int common_main(int argc, const char* argv[]) { LogMessage("Initialized the Firebase Remote Config API"); + // Initialization Complete + // Set Defaults, and test them static const unsigned char kBinaryDefaults[] = {6, 0, 0, 6, 7, 3}; static const remote_config::ConfigKeyValueVariant defaults[] = { @@ -65,63 +91,67 @@ extern "C" int common_main(int argc, const char* argv[]) { sizeof(kBinaryDefaults))}, {"TestDefaultOnly", "Default value that won't be overridden"}}; size_t default_count = sizeof(defaults) / sizeof(defaults[0]); - remote_config::SetDefaults(defaults, default_count); + rc_->SetDefaults(defaults, default_count); // The return values may not be the set defaults, if a fetch was previously // completed for the app that set them. + remote_config::ValueInfo value_info; { - bool result = remote_config::GetBoolean("TestBoolean"); - LogMessage("Get TestBoolean %d", result ? 1 : 0); + bool result = rc_->GetBoolean("TestBoolean", &value_info); + LogMessage("Get TestBoolean %d %s", result ? 1 : 0, + ValueSourceToString(value_info.source)); } { - int64_t result = remote_config::GetLong("TestLong"); - LogMessage("Get TestLong %lld", result); + int64_t result = rc_->GetLong("TestLong", &value_info); + LogMessage("Get TestLong %lld %s", result, + ValueSourceToString(value_info.source)); } { - double result = remote_config::GetDouble("TestDouble"); - LogMessage("Get TestDouble %f", result); + double result = rc_->GetDouble("TestDouble", &value_info); + LogMessage("Get TestDouble %f %s", result, + ValueSourceToString(value_info.source)); } { - std::string result = remote_config::GetString("TestString"); - LogMessage("Get TestString %s", result.c_str()); + std::string result = rc_->GetString("TestString", &value_info); + LogMessage("Get TestString \"%s\" %s", result.c_str(), + ValueSourceToString(value_info.source)); } { - std::vector result = remote_config::GetData("TestData"); + std::vector result = rc_->GetData("TestData"); for (size_t i = 0; i < result.size(); ++i) { const unsigned char value = result[i]; LogMessage("TestData[%d] = 0x%02x", i, value); } } { - std::string result = remote_config::GetString("TestDefaultOnly"); - LogMessage("Get TestDefaultOnly %s", result.c_str()); + std::string result = rc_->GetString("TestDefaultOnly", &value_info); + LogMessage("Get TestDefaultOnly \"%s\" %s", result.c_str(), + ValueSourceToString(value_info.source)); + } + { + std::string result = rc_->GetString("TestNotSet", &value_info); + LogMessage("Get TestNotSet \"%s\" %s", result.c_str(), + ValueSourceToString(value_info.source)); } + // Test the existence of the keys by name. { // Print out the keys with default values. - std::vector keys = remote_config::GetKeys(); + std::vector keys = rc_->GetKeys(); LogMessage("GetKeys:"); for (auto s = keys.begin(); s != keys.end(); ++s) { LogMessage(" %s", s->c_str()); } - keys = remote_config::GetKeysByPrefix("TestD"); + keys = rc_->GetKeysByPrefix("TestD"); LogMessage("GetKeysByPrefix(\"TestD\"):"); for (auto s = keys.begin(); s != keys.end(); ++s) { LogMessage(" %s", s->c_str()); } } - // Enable developer mode and verified it's enabled. - // NOTE: Developer mode should not be enabled in production applications. - remote_config::SetConfigSetting(remote_config::kConfigSettingDeveloperMode, - "1"); - if ((*remote_config::GetConfigSetting( - remote_config::kConfigSettingDeveloperMode) - .c_str()) != '1') { - LogMessage("Failed to enable developer mode"); - } + // Test Fetch... LogMessage("Fetch..."); - auto future_result = remote_config::Fetch(0); + auto future_result = rc_->Fetch(0); while (future_result.status() == firebase::kFutureStatusPending) { if (ProcessEvents(1000)) { break; @@ -130,60 +160,78 @@ extern "C" int common_main(int argc, const char* argv[]) { if (future_result.status() == firebase::kFutureStatusComplete) { LogMessage("Fetch Complete"); - bool activate_result = remote_config::ActivateFetched(); - LogMessage("ActivateFetched %s", activate_result ? "succeeded" : "failed"); - - const remote_config::ConfigInfo& info = remote_config::GetInfo(); - LogMessage( - "Info last_fetch_time_ms=%d (year=%.2f) fetch_status=%d " - "failure_reason=%d throttled_end_time=%d", - static_cast(info.fetch_time), - 1970.0f + static_cast(info.fetch_time) / - (1000.0f * 60.0f * 60.0f * 24.0f * 365.0f), - info.last_fetch_status, info.last_fetch_failure_reason, - info.throttled_end_time); + auto activate_future_result = rc_->Activate(); + while (future_result.status() == firebase::kFutureStatusPending) { + if (ProcessEvents(1000)) { + break; + } + } + + bool activate_result = activate_future_result.result(); + LogMessage("Activate %s", activate_result ? "succeeded" : "failed"); + + const remote_config::ConfigInfo &info = rc_->GetInfo(); + LogMessage("Info last_fetch_time_ms=%d (year=%.2f) fetch_status=%d " + "failure_reason=%d throttled_end_time=%d", + static_cast(info.fetch_time), + 1970.0f + static_cast(info.fetch_time) / + (1000.0f * 60.0f * 60.0f * 24.0f * 365.0f), + info.last_fetch_status, info.last_fetch_failure_reason, + info.throttled_end_time); // Print out the new values, which may be updated from the Fetch. { - bool result = remote_config::GetBoolean("TestBoolean"); - LogMessage("Updated TestBoolean %d", result ? 1 : 0); + bool result = rc_->GetBoolean("TestBoolean", &value_info); + LogMessage("Updated TestBoolean %d %s", result ? 1 : 0, + ValueSourceToString(value_info.source)); } { - int64_t result = remote_config::GetLong("TestLong"); - LogMessage("Updated TestLong %lld", result); + int64_t result = rc_->GetLong("TestLong", &value_info); + LogMessage("Updated TestLong %lld %s", result, + ValueSourceToString(value_info.source)); } { - double result = remote_config::GetDouble("TestDouble"); - LogMessage("Updated TestDouble %f", result); + double result = rc_->GetDouble("TestDouble", &value_info); + LogMessage("Updated TestDouble %f %s", result, + ValueSourceToString(value_info.source)); } { - std::string result = remote_config::GetString("TestString"); - LogMessage("Updated TestString %s", result.c_str()); + std::string result = rc_->GetString("TestString", &value_info); + LogMessage("Updated TestString \"%s\" %s", result.c_str(), + ValueSourceToString(value_info.source)); } { - std::vector result = remote_config::GetData("TestData"); + std::vector result = rc_->GetData("TestData"); for (size_t i = 0; i < result.size(); ++i) { const unsigned char value = result[i]; LogMessage("TestData[%d] = 0x%02x", i, value); } } { - std::string result = remote_config::GetString("TestDefaultOnly"); - LogMessage("Get TestDefaultOnly %s", result.c_str()); + std::string result = rc_->GetString("TestDefaultOnly", &value_info); + LogMessage("Get TestDefaultOnly \"%s\" %s", result.c_str(), + ValueSourceToString(value_info.source)); + } + { + std::string result = rc_->GetString("TestNotSet", &value_info); + LogMessage("Get TestNotSet \"%s\" %s", result.c_str(), + ValueSourceToString(value_info.source)); } { // Print out the keys that are now tied to data - std::vector keys = remote_config::GetKeys(); + std::vector keys = rc_->GetKeys(); LogMessage("GetKeys:"); for (auto s = keys.begin(); s != keys.end(); ++s) { LogMessage(" %s", s->c_str()); } - keys = remote_config::GetKeysByPrefix("TestD"); + keys = rc_->GetKeysByPrefix("TestD"); LogMessage("GetKeysByPrefix(\"TestD\"):"); for (auto s = keys.begin(); s != keys.end(); ++s) { LogMessage(" %s", s->c_str()); } } + } else { + LogMessage("Fetch Incomplete"); } // Release a handle to the future so we can shutdown the Remote Config API // when exiting the app. Alternatively we could have placed future_result @@ -194,7 +242,8 @@ extern "C" int common_main(int argc, const char* argv[]) { while (!ProcessEvents(1000)) { } - remote_config::Terminate(); + delete rc_; + rc_ = nullptr; delete app; return 0; diff --git a/remote_config/testapp/src/desktop/desktop_main.cc b/remote_config/testapp/src/desktop/desktop_main.cc index 99ace543..0220c688 100644 --- a/remote_config/testapp/src/desktop/desktop_main.cc +++ b/remote_config/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/remote_config/testapp/testapp.xcodeproj/project.pbxproj b/remote_config/testapp/testapp.xcodeproj/project.pbxproj index baf45c4c..5769362c 100644 --- a/remote_config/testapp/testapp.xcodeproj/project.pbxproj +++ b/remote_config/testapp/testapp.xcodeproj/project.pbxproj @@ -208,7 +208,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 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/remote_config/testapp/testapp/Info.plist b/remote_config/testapp/testapp/Info.plist index 01887a39..907e8a65 100644 --- a/remote_config/testapp/testapp/Info.plist +++ b/remote_config/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/storage/testapp/AndroidManifest.xml b/storage/testapp/AndroidManifest.xml index 3a4a60fd..253ce8c0 100644 --- a/storage/testapp/AndroidManifest.xml +++ b/storage/testapp/AndroidManifest.xml @@ -6,9 +6,10 @@ - + - + + android:screenOrientation="portrait" + android:configChanges="orientation|screenSize"> + android:value="android_main_automated" /> diff --git a/storage/testapp/CMakeLists.txt b/storage/testapp/CMakeLists.txt new file mode 100644 index 00000000..f885aa89 --- /dev/null +++ b/storage/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) + 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_storage firebase_auth firebase_app) +target_link_libraries(${target_name} "${firebase_libs}" ${ADDITIONAL_LIBS}) diff --git a/storage/testapp/Podfile b/storage/testapp/Podfile index 20875f1e..7010e7c0 100644 --- a/storage/testapp/Podfile +++ b/storage/testapp/Podfile @@ -1,7 +1,8 @@ source 'https://github.com/CocoaPods/Specs.git' -platform :ios, '8.0' +platform :ios, '13.0' +use_frameworks! # Cloud Storage for Firebase test application. target 'testapp' do -pod 'Firebase/Storage' -pod 'Firebase/Auth' + pod 'Firebase/Storage', '10.25.0' + pod 'Firebase/Auth', '10.25.0' end diff --git a/storage/testapp/build.gradle b/storage/testapp/build.gradle index 08cfba90..aa20418d 100644 --- a/storage/testapp/build.gradle +++ b/storage/testapp/build.gradle @@ -6,8 +6,8 @@ buildscript { 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' } } @@ -21,101 +21,57 @@ allprojects { 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.storage.testapp' - minSdkVersion 14 - targetSdkVersion 23 - versionCode 1 - versionName '1.0' + defaultConfig { + applicationId 'com.google.firebase.cpp.storage.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/storage.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:11.2.0' - compile 'com.google.firebase:firebase-storage:11.2.0' +apply from: "$gradle.firebase_cpp_sdk_dir/Android/firebase_dependencies.gradle" +firebaseCpp.dependencies { + auth + storage } 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/storage/testapp/gradle.properties b/storage/testapp/gradle.properties new file mode 100644 index 00000000..d7ba8f42 --- /dev/null +++ b/storage/testapp/gradle.properties @@ -0,0 +1 @@ +android.useAndroidX = true diff --git a/storage/testapp/gradle/wrapper/gradle-wrapper.properties b/storage/testapp/gradle/wrapper/gradle-wrapper.properties index d5705170..65340c1b 100644 --- a/storage/testapp/gradle/wrapper/gradle-wrapper.properties +++ b/storage/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/storage/testapp/jni/Android.mk b/storage/testapp/jni/Android.mk deleted file mode 100644 index 847a7783..00000000 --- a/storage/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_storage -LOCAL_SRC_FILES:=$(FIREBASE_LIBRARY_PATH)/libstorage.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_storage \ - 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/storage/testapp/jni/Application.mk b/storage/testapp/jni/Application.mk deleted file mode 100644 index 53ed56a2..00000000 --- a/storage/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/storage/testapp/readme.md b/storage/testapp/readme.md index ac9dec8c..96cdcaae 100644 --- a/storage/testapp/readme.md +++ b/storage/testapp/readme.md @@ -38,16 +38,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). @@ -64,8 +64,8 @@ Building and Running the testapp authenticate with Cloud Storage, 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 @@ -119,13 +119,13 @@ 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 `File/Project Structure...` or @@ -141,10 +141,53 @@ 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. + - 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 ------- diff --git a/storage/testapp/settings.gradle b/storage/testapp/settings.gradle new file mode 100644 index 00000000..2a543b93 --- /dev/null +++ b/storage/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/storage/testapp/src/android/android_main.cc b/storage/testapp/src/android/android_main.cc index 4b86ce30..d8e4d4dc 100644 --- a/storage/testapp/src/android/android_main.cc +++ b/storage/testapp/src/android/android_main.cc @@ -12,13 +12,17 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include -#include - #include #include #include +#include + #include +#include +#include +#include +#include +#include #include "main.h" // NOLINT @@ -37,6 +41,8 @@ static void OnAppCmd(struct android_app* app, int32_t cmd) { g_destroy_requested |= cmd == APP_CMD_DESTROY; } +namespace app_framework { + // Process events pending on the main thread. // Returns true when the app receives an event requesting exit. bool ProcessEvents(int msec) { @@ -103,7 +109,8 @@ class LoggingUtilsData { LoggingUtilsData() : logging_utils_class_(nullptr), logging_utils_add_log_text_(0), - logging_utils_init_log_window_(0) {} + logging_utils_init_log_window_(0), + logging_utils_get_did_touch_(0) {} ~LoggingUtilsData() { JNIEnv* env = GetJniEnv(); @@ -131,6 +138,8 @@ class LoggingUtilsData { logging_utils_class_, "initLogWindow", "(Landroid/app/Activity;)V"); logging_utils_add_log_text_ = env->GetStaticMethodID( logging_utils_class_, "addLogText", "(Ljava/lang/String;)V"); + logging_utils_get_did_touch_ = + env->GetStaticMethodID(logging_utils_class_, "getDidTouch", "()Z"); env->CallStaticVoidMethod(logging_utils_class_, logging_utils_init_log_window_, GetActivity()); @@ -146,10 +155,19 @@ class LoggingUtilsData { env->DeleteLocalRef(text_string); } + bool DidTouch() { + if (logging_utils_class_ == 0) return false; // haven't been initted yet + JNIEnv* env = GetJniEnv(); + assert(env); + return env->CallStaticBooleanMethod(logging_utils_class_, + logging_utils_get_did_touch_); + } + private: jclass logging_utils_class_; jmethodID logging_utils_add_log_text_; jmethodID logging_utils_init_log_window_; + jmethodID logging_utils_get_did_touch_; }; LoggingUtilsData* g_logging_utils_data; @@ -170,12 +188,10 @@ void CheckJNIException() { const char* exception_text = env->GetStringUTFChars(s, nullptr); // Log the exception text. - __android_log_print(ANDROID_LOG_INFO, FIREBASE_TESTAPP_NAME, + __android_log_print(ANDROID_LOG_INFO, TESTAPP_NAME, "-------------------JNI exception:"); - __android_log_print(ANDROID_LOG_INFO, FIREBASE_TESTAPP_NAME, "%s", - exception_text); - __android_log_print(ANDROID_LOG_INFO, FIREBASE_TESTAPP_NAME, - "-------------------"); + __android_log_print(ANDROID_LOG_INFO, TESTAPP_NAME, "%s", exception_text); + __android_log_print(ANDROID_LOG_INFO, TESTAPP_NAME, "-------------------"); // Also, assert fail. assert(false); @@ -187,23 +203,33 @@ void CheckJNIException() { } } -// Log a message that can be viewed in "adb logcat". void LogMessage(const char* format, ...) { - static const int kLineBufferSize = 100; - char buffer[kLineBufferSize + 2]; - va_list list; va_start(list, format); + LogMessageV(format, list); + va_end(list); +} + +// Log a message that can be viewed in "adb logcat". +void LogMessageV(const char* format, va_list list) { + static const int kLineBufferSize = 1024; + char buffer[kLineBufferSize + 2]; + int string_len = vsnprintf(buffer, kLineBufferSize, format, list); string_len = string_len < kLineBufferSize ? string_len : kLineBufferSize; // append a linebreak to the buffer: buffer[string_len] = '\n'; buffer[string_len + 1] = '\0'; - __android_log_vprint(ANDROID_LOG_INFO, FIREBASE_TESTAPP_NAME, format, list); - g_logging_utils_data->AppendText(buffer); + __android_log_vprint(ANDROID_LOG_INFO, TESTAPP_NAME, format, list); + fputs(buffer, stdout); + fflush(stdout); +} + +// Log a message that can be viewed in the console. +void AddToTextView(const char* str) { + app_framework::g_logging_utils_data->AppendText(str); CheckJNIException(); - va_end(list); } // Get the JNI environment. @@ -214,6 +240,75 @@ JNIEnv* GetJniEnv() { return result == JNI_OK ? env : nullptr; } +// Remove all lines starting with these strings. +static const char* const filter_lines[] = {"referenceTable ", nullptr}; + +bool should_filter(const char* str) { + for (int i = 0; filter_lines[i] != nullptr; ++i) { + if (strncmp(str, filter_lines[i], strlen(filter_lines[i])) == 0) + return true; + } + return false; +} + +void* stdout_logger(void* filedes_ptr) { + int fd = reinterpret_cast(filedes_ptr)[0]; + static std::string buffer; + char bufchar; + while (int n = read(fd, &bufchar, 1)) { + if (bufchar == '\0') { + break; + } + buffer = buffer + bufchar; + if (bufchar == '\n') { + if (!should_filter(buffer.c_str())) { + app_framework::AddToTextView(buffer.c_str()); + } + buffer.clear(); + } + } + JavaVM* jvm; + if (app_framework::GetJniEnv()->GetJavaVM(&jvm) == 0) { + jvm->DetachCurrentThread(); + } + return nullptr; +} + +struct FunctionData { + void* (*func)(void*); + void* data; +}; + +static void* CallFunction(void* bg_void) { + FunctionData* bg = reinterpret_cast(bg_void); + void* (*func)(void*) = bg->func; + void* data = bg->data; + // Clean up the data that was passed to us. + delete bg; + // Call the background function. + void* result = func(data); + // Then clean up the Java thread. + JavaVM* jvm; + if (app_framework::GetJniEnv()->GetJavaVM(&jvm) == 0) { + jvm->DetachCurrentThread(); + } + return result; +} + +void RunOnBackgroundThread(void* (*func)(void*), void* data) { + pthread_t thread; + // Rather than running pthread_create(func, data), we must package them into a + // struct, because the c++ thread needs to clean up the JNI thread after it + // finishes. + FunctionData* bg = new FunctionData; + bg->func = func; + bg->data = data; + pthread_create(&thread, nullptr, CallFunction, bg); + pthread_detach(thread); +} + +} // namespace app_framework + // Execute common_main(), flush pending events and finish the activity. extern "C" void android_main(struct android_app* state) { // native_app_glue spawns a new thread, calling android_main() when the @@ -238,18 +333,37 @@ extern "C" void android_main(struct android_app* state) { g_app_state->onAppCmd = OnAppCmd; // Create the logging display. - g_logging_utils_data = new LoggingUtilsData(); - g_logging_utils_data->Init(); + app_framework::g_logging_utils_data = new app_framework::LoggingUtilsData(); + app_framework::g_logging_utils_data->Init(); + + // Pipe stdout to AddToTextView so we get the gtest output. + int filedes[2]; + assert(pipe(filedes) != -1); + assert(dup2(filedes[1], STDOUT_FILENO) != -1); + pthread_t thread; + pthread_create(&thread, nullptr, app_framework::stdout_logger, + reinterpret_cast(filedes)); // Execute cross platform entry point. - static const char* argv[] = {FIREBASE_TESTAPP_NAME}; + static const char* argv[] = {TESTAPP_NAME}; int return_value = common_main(1, argv); (void)return_value; // Ignore the return value. - ProcessEvents(10); + + // Signal to stdout_logger to exit. + write(filedes[1], "\0", 1); + pthread_join(thread, nullptr); + close(filedes[0]); + close(filedes[1]); + // Pause a few seconds so you can see the results. If the user touches + // the screen during that time, don't exit until they choose to. + bool should_exit = false; + do { + should_exit = app_framework::ProcessEvents(10000); + } while (app_framework::g_logging_utils_data->DidTouch() && !should_exit); // Clean up logging display. - delete g_logging_utils_data; - g_logging_utils_data = nullptr; + delete app_framework::g_logging_utils_data; + app_framework::g_logging_utils_data = nullptr; // Finish the activity. if (!g_restarted) ANativeActivity_finish(state->activity); diff --git a/storage/testapp/src/android/java/com/google/firebase/example/LoggingUtils.java b/storage/testapp/src/android/java/com/google/firebase/example/LoggingUtils.java index 11d67c5b..a49996a6 100644 --- a/storage/testapp/src/android/java/com/google/firebase/example/LoggingUtils.java +++ b/storage/testapp/src/android/java/com/google/firebase/example/LoggingUtils.java @@ -15,8 +15,13 @@ package com.google.firebase.example; import android.app.Activity; +import android.graphics.Typeface; import android.os.Handler; import android.os.Looper; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.MotionEvent; +import android.view.View; import android.view.Window; import android.widget.LinearLayout; import android.widget.ScrollView; @@ -27,29 +32,70 @@ * text to the screen, via a non-editable TextView. */ public class LoggingUtils { - public static TextView sTextView = null; + static TextView textView = null; + static ScrollView scrollView = null; + // Tracks if the log window was touched at least once since the testapp was started. + static boolean didTouch = false; public static void initLogWindow(Activity activity) { + initLogWindow(activity, true); + } + + public static void initLogWindow(Activity activity, boolean monospace) { LinearLayout linearLayout = new LinearLayout(activity); - ScrollView scrollView = new ScrollView(activity); - TextView textView = new TextView(activity); + scrollView = new ScrollView(activity); + textView = new TextView(activity); textView.setTag("Logger"); + if (monospace) { + textView.setTypeface(Typeface.MONOSPACE); + textView.setTextSize(10); + } linearLayout.addView(scrollView); scrollView.addView(textView); Window window = activity.getWindow(); window.takeSurface(null); window.setContentView(linearLayout); - sTextView = textView; + + // Force the TextView to stay scrolled to the bottom. + textView.addTextChangedListener( + new TextWatcher() { + @Override + public void afterTextChanged(Editable e) { + if (scrollView != null && !didTouch) { + // If the user never interacted with the screen, scroll to bottom. + scrollView.fullScroll(View.FOCUS_DOWN); + } + } + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + + @Override + public void onTextChanged(CharSequence s, int start, int count, int after) {} + }); + textView.setOnTouchListener( + new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + didTouch = true; + return false; + } + }); } public static void addLogText(final String text) { - new Handler(Looper.getMainLooper()).post(new Runnable() { - @Override - public void run() { - if (sTextView != null) { - sTextView.append(text); - } - } - }); + new Handler(Looper.getMainLooper()) + .post( + new Runnable() { + @Override + public void run() { + if (textView != null) { + textView.append(text); + } + } + }); + } + + public static boolean getDidTouch() { + return didTouch; } } diff --git a/storage/testapp/src/common_main.cc b/storage/testapp/src/common_main.cc index 0cb025fe..a333c4fb 100644 --- a/storage/testapp/src/common_main.cc +++ b/storage/testapp/src/common_main.cc @@ -13,12 +13,13 @@ // limitations under the License. #include -#include + #include #include #include #include #include + #include "firebase/app.h" #include "firebase/auth.h" #include "firebase/future.h" @@ -29,6 +30,10 @@ // Thin OS abstraction layer. #include "main.h" // NOLINT +using app_framework::GetCurrentTimeInMicroseconds; +using app_framework::LogMessage; +using app_framework::ProcessEvents; + const char* kPutFileTestFile = "PutFileTest.txt"; const char* kGetFileTestFile = "GetFileTest.txt"; @@ -36,30 +41,6 @@ const char* kGetFileTestFile = "GetFileTest.txt"; // in a specific Cloud Storage bucket. const char* kStorageUrl = nullptr; -class StorageListener : public firebase::storage::Listener { - public: - StorageListener() - : on_paused_was_called_(false), on_progress_was_called_(false) {} - - // Tracks whether OnPaused was ever called - void OnPaused(firebase::storage::Controller* controller) override { - (void)controller; - on_paused_was_called_ = true; - } - - void OnProgress(firebase::storage::Controller* controller) override { - (void)controller; - on_progress_was_called_ = true; - } - - bool on_paused_was_called() const { return on_paused_was_called_; } - bool on_progress_was_called() const { return on_progress_was_called_; } - - public: - bool on_paused_was_called_; - bool on_progress_was_called_; -}; - // Wait for a Future to be completed. If the Future returns an error, it will // be logged. void WaitForCompletion(const firebase::FutureBase& future, const char* name) { @@ -78,7 +59,8 @@ extern "C" int common_main(int argc, const char* argv[]) { ::firebase::App* app; #if defined(__ANDROID__) - app = ::firebase::App::Create(GetJniEnv(), GetActivity()); + app = ::firebase::App::Create(app_framework::GetJniEnv(), + app_framework::GetActivity()); #else app = ::firebase::App::Create(); #endif // defined(__ANDROID__) @@ -106,9 +88,12 @@ extern "C" int common_main(int argc, const char* argv[]) { LogMessage("Attempt to initialize Cloud Storage."); void** targets = reinterpret_cast(data); ::firebase::InitResult result; - *reinterpret_cast<::firebase::storage::Storage**>(targets[1]) = - ::firebase::storage::Storage::GetInstance(app, kStorageUrl, - &result); + firebase::storage::Storage* storage = + firebase::storage::Storage::GetInstance(app, kStorageUrl, &result); + *reinterpret_cast<::firebase::storage::Storage**>(targets[1]) = storage; + LogMessage("Initialized storage with URL %s, %s", + kStorageUrl ? kStorageUrl : "(null)", + storage->url().c_str()); return result; }}; @@ -131,7 +116,7 @@ extern "C" int common_main(int argc, const char* argv[]) { // work as long as your project's Authentication permissions allow anonymous // signin. { - firebase::Future sign_in_future = + firebase::Future sign_in_future = auth->SignInAnonymously(); WaitForCompletion(sign_in_future, "SignInAnonymously"); if (sign_in_future.error() == firebase::auth::kAuthErrorNone) { @@ -149,21 +134,18 @@ extern "C" int common_main(int argc, const char* argv[]) { } // Generate a folder for the test data based on the time in milliseconds. - struct timeval now; - gettimeofday(&now, nullptr); - int64_t time_in_microseconds = now.tv_sec * 1000000LL + now.tv_usec; + int64_t time_in_microseconds = GetCurrentTimeInMicroseconds(); + char buffer[21] = {0}; snprintf(buffer, sizeof(buffer), "%lld", static_cast(time_in_microseconds)); // NOLINT std::string saved_url = buffer; - // Create a unique child in the storage that we can run our tests in. + // Create a unique child in the storage that we can run in. firebase::storage::StorageReference ref; ref = storage->GetReference("test_app_data").Child(saved_url); - firebase::storage::Metadata test_metadata; - - LogMessage("Storage URL: gs://%s/%s", ref.bucket().c_str(), + LogMessage("Storage URL: gs://%s%s", ref.bucket().c_str(), ref.full_path().c_str()); // Read and write from memory. This will save a small file and then read it @@ -179,466 +161,67 @@ extern "C" int common_main(int argc, const char* argv[]) { "pariatur. Excepteur sint occaecat cupidatat non proident, sunt in " "culpa qui officia deserunt mollit anim id est laborum."; { - LogMessage("TEST: Write a sample file from byte buffer."); + LogMessage("Write a sample file."); + std::string custom_metadata_key = "specialkey"; + std::string custom_metadata_value = "secret value"; + firebase::storage::Metadata metadata; + metadata.set_content_type("test/plain"); + (*metadata.custom_metadata())[custom_metadata_key] = + custom_metadata_value; firebase::Future future = ref.Child("TestFile") - .Child("File1.txt") - .PutBytes(&kSimpleTestFile[0], kSimpleTestFile.size()); - WaitForCompletion(future, "Write Bytes"); - if (future.error() != firebase::storage::kErrorNone) { - LogMessage("ERROR: Write sample file failed."); - LogMessage(" File1.txt: Error %d: %s", future.error(), - future.error_message()); - } else { - LogMessage("SUCCESS: Wrote file with PutBytes."); - auto metadata = future.result(); - if (metadata->size_bytes() == kSimpleTestFile.size()) { - LogMessage("SUCCESS: Metadata reports correct size."); - } else { - LogMessage("ERROR: Metadata reports incorrect size."); - LogMessage(" Got %i bytes, expected %i bytes.", - metadata->size_bytes(), kSimpleTestFile.size()); + .Child("SampleFile.txt") + .PutBytes(&kSimpleTestFile[0], kSimpleTestFile.size(), metadata); + WaitForCompletion(future, "Write"); + if (future.error() == 0) { + if (future.result()->size_bytes() != kSimpleTestFile.size()) { + LogMessage("ERROR: Incorrect number of bytes uploaded."); } } } { - LogMessage("TEST: Write a sample file from local file."); + LogMessage("Read back the sample file."); - // Write file that we're going to upload. - std::string path = PathForResource() + kPutFileTestFile; - // Cloud Storage expects file:// in front of local paths. - std::string file_path = "file://" + path; + const size_t kBufferSize = 1024; + char buffer[kBufferSize]; - FILE* file = fopen(path.c_str(), "w"); - std::fwrite(kSimpleTestFile.c_str(), 1, kSimpleTestFile.size(), file); - fclose(file); - - firebase::storage::Metadata new_metadata; - new_metadata.set_content_type("text/html"); - new_metadata.custom_metadata()->insert(std::make_pair("hello", "world")); - - firebase::Future future = - ref.Child("TestFile") - .Child("File2.txt") - .PutFile(file_path.c_str(), new_metadata); - WaitForCompletion(future, "Write File"); - if (future.error() != firebase::storage::kErrorNone) { - LogMessage("ERROR: Write file failed."); - LogMessage(" File2.txt: Error %d: %s", future.error(), - future.error_message()); - } else { - LogMessage("SUCCESS: Wrote file with PutFile."); - auto metadata = future.result(); - if (metadata->size_bytes() == kSimpleTestFile.size()) { - LogMessage("SUCCESS: Metadata reports correct size."); - } else { - LogMessage("ERROR: Metadata reports incorrect size."); - LogMessage(" Got %i bytes, expected %i bytes.", - metadata->size_bytes(), kSimpleTestFile.size()); - } - if (strcmp(metadata->content_type(), new_metadata.content_type()) == - 0) { - LogMessage( - "SUCCESS: Metadata has correct content type set at upload."); - } else { - LogMessage( - "ERROR: Metadata has incorrect content type set at upload."); - LogMessage(" Got %s, expected %s.", metadata->content_type(), - new_metadata.content_type()); + firebase::Future future = ref.Child("TestFile") + .Child("SampleFile.txt") + .GetBytes(buffer, kBufferSize); + WaitForCompletion(future, "Read"); + if (future.error() == 0) { + if (*future.result() != kSimpleTestFile.size()) { + LogMessage("ERROR: Incorrect number of bytes uploaded."); } - auto pair1 = metadata->custom_metadata()->find("hello"); - if (pair1 != metadata->custom_metadata()->end() && - pair1->second == "world") { - LogMessage( - "SUCCESS: Metadata has correct custom metadata set at upload."); - } else { - LogMessage( - "SUCCESS: Metadata has incorrect custom metadata set at upload."); + if (memcmp(&kSimpleTestFile[0], buffer, kSimpleTestFile.size()) != 0) { + LogMessage("ERROR: file contents did not match."); } } } - - // Get the values that we just set, and confirm that they match what we - // set them to. { - LogMessage("TEST: Read a sample file with GetBytes."); - - // Read the storage file to byte buffer. - { - const size_t kBufferSize = 1024; - char buffer[kBufferSize]; - - firebase::Future future = ref.Child("TestFile") - .Child("File1.txt") - .GetBytes(buffer, kBufferSize); - WaitForCompletion(future, "Read Bytes"); - - // Check if the file contents is correct. - if (future.error() == firebase::storage::kErrorNone) { - if (*future.result() != kSimpleTestFile.size()) { - LogMessage( - "ERROR: Read file failed, read incorrect number of bytes (read " - "%z, expected %z)", - *future.result(), kSimpleTestFile.size()); - } else if (memcmp(&kSimpleTestFile[0], buffer, - kSimpleTestFile.size()) == 0) { - LogMessage("SUCCESS: Read file succeeded."); - } else { - LogMessage("ERROR: Read file failed, file contents did not match."); - } - } else { - LogMessage("ERROR: Read file failed."); - } - } - - LogMessage("TEST: Read a sample file with GetFile."); - - // Read the storage file to local file. - { - const size_t kBufferSize = 1024; - char buffer[kBufferSize]; - - // Write file that we're going to upload. - std::string path = PathForResource() + kGetFileTestFile; - // Cloud Storage expects file:// in front of local paths. - std::string file_path = "file://" + path; - - firebase::Future future = - ref.Child("TestFile").Child("File2.txt").GetFile(file_path.c_str()); - WaitForCompletion(future, "Read File"); - - FILE* file = fopen(path.c_str(), "r"); - std::fread(buffer, 1, kBufferSize, file); - fclose(file); - - // Check if the file contents is correct. - if (future.error() == firebase::storage::kErrorNone) { - if (*future.result() != kSimpleTestFile.size()) { - LogMessage( - "ERROR: Read file failed, read incorrect number of bytes (read " - "%z, expected %z)", - *future.result(), kSimpleTestFile.size()); - } else if (memcmp(&kSimpleTestFile[0], buffer, - kSimpleTestFile.size()) == 0) { - LogMessage("SUCCESS: Read file succeeded."); - } else { - LogMessage("ERROR: Read file failed, file contents did not match."); - } - } else { - LogMessage("ERROR: Read file failed."); - } - } + LogMessage("Delete the sample file."); - // Check if the timestamp is correct. - { - LogMessage("TEST: Check sample file metadata."); - - firebase::Future future = - ref.Child("TestFile").Child("File1.txt").GetMetadata(); - WaitForCompletion(future, "GetFileMetadata"); - const firebase::storage::Metadata* metadata = future.result(); - if (future.error() == firebase::storage::kErrorNone) { - test_metadata = *metadata; // Save a copy of the metadata for later. - - // Get the current time to compare to the Timestamp. - int64_t current_time_seconds = static_cast(time(nullptr)); - int64_t updated_time = metadata->updated_time(); - int64_t time_difference = updated_time - current_time_seconds; - // As long as our timestamp is within a day, it's correct enough for - // our purposes. - const int64_t kAllowedTimeDifferenceSeconds = 60L * 60L * 24L; - if (time_difference > kAllowedTimeDifferenceSeconds || - time_difference < -kAllowedTimeDifferenceSeconds) { - LogMessage("ERROR: Incorrect metadata."); - LogMessage(" Timestamp: Got %lld, expected something near %lld", - updated_time, current_time_seconds); - } else { - LogMessage("SUCCESS: Read file successfully."); - } - } else { - LogMessage("ERROR: Read file failed."); - } - // Check custom metadata field to make sure it is empty. - auto custom_metadata = metadata->custom_metadata(); - if (!custom_metadata->empty()) { - LogMessage("ERROR: Metadata reports incorrect custom metadata."); - } else { - LogMessage("SUCCESS: Metadata reports correct custom metadata."); - } - - // Add some values to custom metadata, update and then compare. - custom_metadata->insert(std::make_pair("Key", "Value")); - custom_metadata->insert(std::make_pair("Foo", "Bar")); - firebase::Future custom_metadata_future = - ref.Child("TestFile").Child("File1.txt").UpdateMetadata(*metadata); - WaitForCompletion(custom_metadata_future, "UpdateMetadata"); - if (future.error() != firebase::storage::kErrorNone) { - LogMessage("ERROR: UpdateMetadata failed."); - LogMessage(" File1.txt: Error %d: %s", future.error(), - future.error_message()); - } else { - LogMessage("SUCCESS: Updated Metadata."); - const firebase::storage::Metadata* new_metadata = - custom_metadata_future.result(); - auto new_custom_metadata = new_metadata->custom_metadata(); - auto pair1 = new_custom_metadata->find("Key"); - auto pair2 = new_custom_metadata->find("Foo"); - if (pair1 == new_custom_metadata->end() || pair1->second != "Value" || - pair2 == new_custom_metadata->end() || pair2->second != "Bar") { - LogMessage( - "ERROR: New metadata reports incorrect custom metadata."); - } else { - LogMessage( - "SUCCESS: New metadata reports correct custom metadata."); - } - } - } - - // Check the download URL. - { - LogMessage("TEST: Check for a download URL."); - firebase::Future future = - ref.Child("TestFile").Child("File1.txt").GetDownloadUrl(); - - WaitForCompletion(future, "GetDownloadUrl"); - if (future.error() != firebase::storage::kErrorNone) { - LogMessage("ERROR: Couldn't get download URL."); - LogMessage(" File1.txt: Error %d: %s", future.error(), - future.error_message()); - } else { - LogMessage("SUCCESS: Got URL: "); - const std::string* download_url = future.result(); - LogMessage(" %s", download_url->c_str()); - } - } - { - firebase::Future future = - ref.Child("TestFile").Child("File1.txt").GetMetadata(); - WaitForCompletion(future, "GetFileMetadataForDownloadUrl"); - if (future.error() == firebase::storage::kErrorNone) { - if (future.result()->download_url() != nullptr) { - LogMessage("SUCCESS: Got URL in metadata: %s", - future.result()->download_url()); - } else { - LogMessage("ERROR: No download URL listed in metadata."); - } - } else { - LogMessage("ERROR: Couldn't read metadata to check download URL."); - } - } - - // Try removing the file. - { - // Call delete on file. - LogMessage("TEST: Removing file."); - firebase::Future delete_future = - ref.Child("TestFile").Child("File1.txt").Delete(); - WaitForCompletion(delete_future, "DeleteFile"); - if (delete_future.error() == firebase::storage::kErrorNone) { - LogMessage("SUCCESS: File was removed."); - } else { - LogMessage("ERROR: File was not removed."); - } - - // Verify it can no longer be read. - const size_t kBufferSize = 1024; - char buffer[kBufferSize]; - firebase::Future read_future = - ref.Child("TestFile") - .Child("File1.txt") - .GetBytes(buffer, kBufferSize); - while (read_future.status() == firebase::kFutureStatusPending) { - ProcessEvents(100); - } - if (read_future.error() == firebase::storage::kErrorObjectNotFound) { - LogMessage("SUCCESS: File could not be read, as expected."); - } else { - LogMessage("ERROR: File could be read after removal. Status = %d: %s", - read_future.error(), read_future.error_message()); - } - } + firebase::Future future = + ref.Child("TestFile").Child("SampleFile.txt").Delete(); + WaitForCompletion(future, "Delete"); } } - { - const size_t kLargeFileSize = 2 * 1024 * 1024; - std::vector large_test_file; - large_test_file.resize(kLargeFileSize); - for (int i = 0; i < kLargeFileSize; ++i) { - // Fill the buffer with the alphabet. - large_test_file[i] = 'a' + (i % 26); - } - const std::vector& kLargeTestFile = large_test_file; - bool wrote_file = false; - - { - LogMessage("TEST: Write a large file, pause, and resume mid-way."); - StorageListener listener; - firebase::storage::Controller controller; - firebase::Future future = - ref.Child("TestFile") - .Child("File3.txt") - .PutBytes(&kLargeTestFile[0], kLargeFileSize, &listener, - &controller); - - // Ensure the Controller is valid now that we have associated it with an - // operation. - if (!controller.is_valid()) { - LogMessage("ERROR: Controller was invalid."); - } - - ProcessEvents(500); - // After waiting a moment for the operation to start, pause the operation - // and verify it was successfully paused. Note that pause might not take - // effect immediately, so we give it a few moments to pause before - // failing. - LogMessage("INFO: Pausing."); - if (controller.Pause()) { - ProcessEvents(5000); - if (!listener.on_paused_was_called()) { - LogMessage("ERROR: Listener OnPaused callback was not called."); - } - LogMessage("INFO: Resuming."); - if (!controller.Resume()) { - LogMessage("ERROR: Resume() failed."); - } - } else { - LogMessage("ERROR: Pause() failed."); - } - - WaitForCompletion(future, "WriteLargeFile"); - - // Ensure the progress callback was called. - if (!listener.on_progress_was_called()) { - LogMessage("ERROR: Listener OnProgress callback was not called."); - } - - if (future.error() != firebase::storage::kErrorNone) { - LogMessage("ERROR: Write file failed."); - LogMessage(" TestFile: Error %d: %s", future.error(), - future.error_message()); - } else { - LogMessage("SUCCESS: Wrote large file."); - wrote_file = true; - auto metadata = future.result(); - if (metadata->size_bytes() == kLargeFileSize) { - LogMessage("SUCCESS: Metadata reports correct size."); - } else { - LogMessage("ERROR: Metadata reports incorrect size."); - LogMessage(" Got %i bytes, expected %i bytes.", - metadata->size_bytes(), kLargeFileSize); - } - } - } - - if (wrote_file) { - LogMessage("TEST: Reading previously-uploaded large file."); - - std::vector buffer; - buffer.resize(kLargeFileSize); - StorageListener listener; - firebase::storage::Controller controller; - firebase::Future future = - ref.Child("TestFile") - .Child("File3.txt") - .GetBytes(&buffer[0], buffer.size(), &listener, &controller); - - // Ensure the Controller is valid now that we have associated it with an - // operation. - if (!controller.is_valid()) { - LogMessage("ERROR: Controller was invalid."); - } - - WaitForCompletion(future, "ReadLargeFile"); - - // Ensure the progress callback was called. - if (!listener.on_progress_was_called()) { - LogMessage("ERROR: Listener OnProgress callback was not called."); - } - - // Check if the file contents is correct. - if (future.error() == firebase::storage::kErrorNone) { - if (*future.result() != kLargeFileSize) { - LogMessage( - "ERROR: Read file failed, read incorrect number of bytes (read " - "%z, expected %z)", - *future.result(), kLargeFileSize); - } else if (std::memcmp(&kLargeTestFile[0], &buffer[0], - kLargeFileSize) == 0) { - LogMessage("SUCCESS: Read file succeeded."); - } else { - LogMessage("ERROR: Read file failed, file contents did not match."); - } - } else { - LogMessage("ERROR: Read file failed."); - } - } - - { - LogMessage("TEST: Write a large file and cancel mid-way."); - firebase::storage::Controller controller; - firebase::Future future = - ref.Child("TestFile") - .Child("File4.txt") - .PutBytes(&kLargeTestFile[0], kLargeFileSize, nullptr, - &controller); - - // Ensure the Controller is valid now that we have associated it with an - // operation. - if (!controller.is_valid()) { - LogMessage("ERROR: Controller was invalid."); - } - - // Cancel the operation and verify it was successfully canceled. - controller.Cancel(); - - while (future.status() == firebase::kFutureStatusPending) { - ProcessEvents(100); - } - if (future.error() != firebase::storage::kErrorCancelled) { - LogMessage("ERROR: Write cancellation failed."); - LogMessage(" TestFile: Error %d: %s", future.error(), - future.error_message()); - } else { - LogMessage("SUCCESS: Canceled file upload."); - } - } - } - - // Check if test_metadata started valid and was then invalidated. - bool test_metadata_was_valid = test_metadata.is_valid(); - LogMessage("Shutdown the Storage library."); delete storage; storage = nullptr; - // Ensure that the ref we had is now invalid. - if (!ref.is_valid()) { - LogMessage("SUCCESS: Reference was invalidated on library shutdown."); - } else { - LogMessage("ERROR: Reference is still valid after library shutdown."); - } - - if (test_metadata_was_valid) { - if (!test_metadata.is_valid()) { - LogMessage("SUCCESS: Metadata was invalidated on library shutdown."); - } else { - LogMessage("ERROR: Metadata is still valid after library shutdown."); - } - } else { - LogMessage( - "WARNING: Metadata was already invalid at shutdown, couldn't check."); - } - LogMessage("Signing out from anonymous account."); auth->SignOut(); + LogMessage("Shutdown the Auth library."); delete auth; auth = nullptr; LogMessage("Shutdown Firebase App."); delete app; + app = nullptr; // Wait until the user wants to quit the app. while (!ProcessEvents(1000)) { diff --git a/storage/testapp/src/desktop/desktop_main.cc b/storage/testapp/src/desktop/desktop_main.cc index 5632a8f9..7f3a95ae 100644 --- a/storage/testapp/src/desktop/desktop_main.cc +++ b/storage/testapp/src/desktop/desktop_main.cc @@ -15,16 +15,39 @@ #include #include #include -#ifndef _WIN32 + +#include // NOLINT + +#ifdef _WIN32 +#include +#define chdir _chdir +#else +#include +#include #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; @@ -41,6 +64,8 @@ static BOOL WINAPI SignalHandler(DWORD event) { static void SignalHandler(int /* ignored */) { quit = true; } #endif // _WIN32 +namespace app_framework { + bool ProcessEvents(int msec) { #ifdef _WIN32 Sleep(msec); @@ -51,21 +76,92 @@ bool ProcessEvents(int msec) { } std::string PathForResource() { +#if defined(_WIN32) + // On Windows we should hvae TEST_TMPDIR or TEMP or TMP set. + char buf[MAX_PATH + 1]; + if (GetEnvironmentVariable("TEST_TMPDIR", buf, sizeof(buf)) || + GetEnvironmentVariable("TEMP", buf, sizeof(buf)) || + GetEnvironmentVariable("TMP", buf, sizeof(buf))) { + std::string path(buf); + // Add trailing slash. + if (path[path.size() - 1] != '\\') path += '\\'; + return path; + } +#else + // Linux and OS X should either have the TEST_TMPDIR environment variable set + // or use /tmp. + if (const char* value = getenv("TEST_TMPDIR")) { + std::string path(value); + // Add trailing slash. + if (path[path.size() - 1] != '/') path += '/'; + return path; + } + struct stat s; + if (stat("/tmp", &s) == 0) { + if (s.st_mode & S_IFDIR) { + return std::string("/tmp/"); + } + } +#endif // defined(_WIN32) + // If nothing else, use the current directory. return std::string(); } void LogMessage(const char* format, ...) { va_list list; va_start(list, format); - vprintf(format, list); + LogMessageV(format, list); va_end(list); +} + +void LogMessageV(const char* format, va_list list) { + vprintf(format, list); printf("\n"); fflush(stdout); } 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()); + } +} + +#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 + +void RunOnBackgroundThread(void* (*func)(void*), void* data) { + // On desktop, use std::thread as Windows doesn't support pthreads. + std::thread thread(func, data); + thread.detach(); +} + +} // namespace app_framework + int main(int argc, const char* argv[]) { + app_framework::ChangeToFileDirectory(FIREBASE_CONFIG_STRING[0] != '\0' + ? FIREBASE_CONFIG_STRING + : argv[0]); // NOLINT #ifdef _WIN32 SetConsoleCtrlHandler((PHANDLER_ROUTINE)SignalHandler, TRUE); #else diff --git a/storage/testapp/src/ios/ios_main.mm b/storage/testapp/src/ios/ios_main.mm index 715d4119..61a8bc36 100755 --- a/storage/testapp/src/ios/ios_main.mm +++ b/storage/testapp/src/ios/ios_main.mm @@ -14,7 +14,15 @@ #import -#include +#include +#include + +#include +#include +#include +#include +#include +#include #include "main.h" @@ -43,7 +51,7 @@ - (void)viewDidLoad { [super viewDidLoad]; g_parent_view = self.view; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - const char *argv[] = {FIREBASE_TESTAPP_NAME}; + const char *argv[] = {TESTAPP_NAME}; [g_shutdown_signal lock]; g_exit_status = common_main(1, argv); [g_shutdown_complete signal]; @@ -51,6 +59,7 @@ - (void)viewDidLoad { } @end +namespace app_framework { bool ProcessEvents(int msec) { [g_shutdown_signal @@ -71,27 +80,91 @@ WindowContext GetWindowContext() { return g_parent_view; } +void LogMessage(const char *format, ...) { + va_list list; + va_start(list, format); + LogMessageV(format, list); + va_end(list); +} + // Log a message that can be viewed in the console. -void LogMessage(const char* format, ...) { - va_list args; +void LogMessageV(const char *format, va_list list) { NSString *formatString = @(format); - va_start(args, format); - NSString *message = [[NSString alloc] initWithFormat:formatString arguments:args]; - va_end(args); + NSString *message = [[NSString alloc] initWithFormat:formatString arguments:list]; NSLog(@"%@", message); message = [message stringByAppendingString:@"\n"]; + fputs(message.UTF8String, stdout); + fflush(stdout); +} + +// Log a message that can be viewed in the console. +void AddToTextView(const char *str) { + NSString *message = @(str); + dispatch_async(dispatch_get_main_queue(), ^{ g_text_view.text = [g_text_view.text stringByAppendingString:message]; + NSRange range = NSMakeRange(g_text_view.text.length, 0); + [g_text_view scrollRangeToVisible:range]; }); } +// Remove all lines starting with these strings. +static const char *const filter_lines[] = {nullptr}; + +bool should_filter(const char *str) { + for (int i = 0; filter_lines[i] != nullptr; ++i) { + if (strncmp(str, filter_lines[i], strlen(filter_lines[i])) == 0) return true; + } + return false; +} + +void *stdout_logger(void *filedes_ptr) { + int fd = reinterpret_cast(filedes_ptr)[0]; + std::string buffer; + char bufchar; + while (int n = read(fd, &bufchar, 1)) { + if (bufchar == '\0') { + break; + } + buffer = buffer + bufchar; + if (bufchar == '\n') { + if (!should_filter(buffer.c_str())) { + app_framework::AddToTextView(buffer.c_str()); + } + buffer.clear(); + } + } + return nullptr; +} + +void RunOnBackgroundThread(void* (*func)(void*), void* data) { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + func(data); + }); +} + +} // namespace app_framework + int main(int argc, char* argv[]) { + // Pipe stdout to call LogToTextView so we can see the gtest output. + int filedes[2]; + assert(pipe(filedes) != -1); + assert(dup2(filedes[1], STDOUT_FILENO) != -1); + pthread_t thread; + pthread_create(&thread, nullptr, app_framework::stdout_logger, reinterpret_cast(filedes)); @autoreleasepool { UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } + // Signal to stdout_logger to exit. + write(filedes[1], "\0", 1); + pthread_join(thread, nullptr); + close(filedes[0]); + close(filedes[1]); + + NSLog(@"Application Exit"); return g_exit_status; } @@ -114,7 +187,7 @@ - (BOOL)application:(UIApplication*)application g_text_view.editable = NO; g_text_view.scrollEnabled = YES; g_text_view.userInteractionEnabled = YES; - + g_text_view.font = [UIFont fontWithName:@"Courier" size:10]; [viewController.view addSubview:g_text_view]; return YES; @@ -125,5 +198,4 @@ - (void)applicationWillTerminate:(UIApplication *)application { [g_shutdown_signal signal]; [g_shutdown_complete wait]; } - @end diff --git a/storage/testapp/src/main.h b/storage/testapp/src/main.h index f5980902..2d30c14e 100644 --- a/storage/testapp/src/main.h +++ b/storage/testapp/src/main.h @@ -15,7 +15,12 @@ #ifndef FIREBASE_TESTAPP_MAIN_H_ // NOLINT #define FIREBASE_TESTAPP_MAIN_H_ // NOLINT +#include + #include +#if !defined(_WIN32) +#include +#endif #if defined(__ANDROID__) #include #include @@ -25,15 +30,18 @@ extern "C" { } // extern "C" #endif // __ANDROID__ -// Defined using -DANDROID_MAIN_APP_NAME=some_app_name when compiling this +// Defined using -DTESTAPP_NAME=some_app_name when compiling this // file. -#ifndef FIREBASE_TESTAPP_NAME -#define FIREBASE_TESTAPP_NAME "android_main" -#endif // FIREBASE_TESTAPP_NAME +#ifndef TESTAPP_NAME +#define TESTAPP_NAME "android_main" +#endif // TESTAPP_NAME + +namespace app_framework { // Cross platform logging method. // Implemented by android/android_main.cc or ios/ios_main.mm. -extern "C" void LogMessage(const char* format, ...); +void LogMessage(const char* format, ...); +void LogMessageV(const char* format, va_list list); // Platform-independent method to flush pending events for the main thread. // Returns true when an event requesting program-exit is received. @@ -42,6 +50,22 @@ bool ProcessEvents(int msec); // Returns a path to a file suitable for the given platform. std::string PathForResource(); +#if defined(_WIN32) +// Windows requires its own version of time-handling code. +int64_t WinGetCurrentTimeInMicroseconds(); +#endif + +// Returns the number of microseconds since the epoch. +static int64_t GetCurrentTimeInMicroseconds() { +#if !defined(_WIN32) + struct timeval now; + gettimeofday(&now, nullptr); + return now.tv_sec * 1000000LL + now.tv_usec; +#else + return WinGetCurrentTimeInMicroseconds(); +#endif +} + // WindowContext represents the handle to the parent window. It's type // (and usage) vary based on the OS. #if defined(__ANDROID__) @@ -64,4 +88,9 @@ jobject GetActivity(); // to the root view of the view controller. WindowContext GetWindowContext(); +// Run the given function on a detached background thread. +void RunOnBackgroundThread(void* (*func)(void* data), void* data); + +} // namespace app_framework + #endif // FIREBASE_TESTAPP_MAIN_H_ // NOLINT diff --git a/storage/testapp/testapp.xcodeproj/project.pbxproj b/storage/testapp/testapp.xcodeproj/project.pbxproj index baf45c4c..5769362c 100644 --- a/storage/testapp/testapp.xcodeproj/project.pbxproj +++ b/storage/testapp/testapp.xcodeproj/project.pbxproj @@ -208,7 +208,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 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/storage/testapp/testapp/Info.plist b/storage/testapp/testapp/Info.plist index dc1a95f9..31425994 100644 --- a/storage/testapp/testapp/Info.plist +++ b/storage/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