Skip to content

Feature/rewarded user #31

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion admob/integration_test/src/integration_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -435,13 +435,15 @@ class TestRewardedVideoListener
got_reward_ = true;
reward_type_ = reward.reward_type;
reward_amount_ = reward.amount;
ssv_user_id_ = reward.ssv_user_id;
}
void OnPresentationStateChanged(
firebase::admob::rewarded_video::PresentationState state) override {
presentation_states_.push_back(state);
}
bool got_reward_;
std::string reward_type_;
std::string ssv_user_id_;
float reward_amount_;
std::vector<firebase::admob::rewarded_video::PresentationState>
presentation_states_;
Expand All @@ -457,7 +459,7 @@ TEST_F(FirebaseAdMobTest, TestRewardedVideoAd) {
rewarded_video::SetListener(&rewarded_listener);

firebase::admob::AdRequest request = GetAdRequest();
WaitForCompletion(rewarded_video::LoadAd(kRewardedVideoAdUnit, request),
WaitForCompletion(rewarded_video::LoadAd(kRewardedVideoAdUnit, kSSVUserId, request),
"LoadAd");

std::vector<rewarded_video::PresentationState> expected_presentation_states;
Expand Down Expand Up @@ -489,6 +491,7 @@ TEST_F(FirebaseAdMobTest, TestRewardedVideoAd) {
EXPECT_TRUE(rewarded_listener.got_reward_);
EXPECT_NE(rewarded_listener.reward_type_, "");
EXPECT_NE(rewarded_listener.reward_amount_, 0);
EXPECT_EQ(rewarded_listener.ssv_user_id_, kSSVUserId);
LogDebug("Got reward: %.02f %s", rewarded_listener.reward_amount_,
rewarded_listener.reward_type_.c_str());

Expand Down
7 changes: 6 additions & 1 deletion admob/src/android/admob_android.cc
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,7 @@ Java_com_google_firebase_admob_internal_cpp_RewardedVideoHelper_notifyPresentati
extern "C" JNIEXPORT void JNICALL
Java_com_google_firebase_admob_internal_cpp_RewardedVideoHelper_grantReward(
JNIEnv* env, jclass clazz, jlong data_ptr, jint amount,
jstring rewardType) {
jstring rewardType, jstring juserid) {
if (data_ptr == 0) return; // test call only

firebase::admob::rewarded_video::internal::RewardedVideoInternal* internal =
Expand All @@ -389,6 +389,11 @@ Java_com_google_firebase_admob_internal_cpp_RewardedVideoHelper_grantReward(
const char* chars = env->GetStringUTFChars(rewardType, 0);
reward.reward_type = chars;
env->ReleaseStringUTFChars(rewardType, chars);

const char* cuserid = env->GetStringUTFChars(juserid, 0);
reward.reward_type = cuserid;
env->ReleaseStringUTFChars(juserid, cuserid);

internal->NotifyListenerOfReward(reward);
}

Expand Down
6 changes: 5 additions & 1 deletion admob/src/android/rewarded_video_internal_android.cc
Original file line number Diff line number Diff line change
Expand Up @@ -76,23 +76,27 @@ Future<void> RewardedVideoInternalAndroid::Initialize() {
}

Future<void> RewardedVideoInternalAndroid::LoadAd(const char* ad_unit_id,
const char* user_id,
const AdRequest& request) {
FutureCallbackData* callback_data =
CreateFutureCallbackData(&future_data_, kRewardedVideoFnLoadAd);

JNIEnv* env = ::firebase::admob::GetJNI();

jstring ad_unit_id_str = env->NewStringUTF(ad_unit_id);
jstring user_id_str = NULL;
if (user_id) user_id_str = env->NewStringUTF(user_id);

AdRequestConverter converter(request);
jobject request_ref = converter.GetJavaRequestObject();

env->CallVoidMethod(
helper_,
rewarded_video_helper::GetMethodId(rewarded_video_helper::kLoadAd),
reinterpret_cast<jlong>(callback_data), ad_unit_id_str, request_ref);
reinterpret_cast<jlong>(callback_data), ad_unit_id_str, user_id_str, request_ref);

env->DeleteLocalRef(ad_unit_id_str);
if (user_id) env->DeleteLocalRef(user_id_str);

return GetLastResult(kRewardedVideoFnLoadAd);
}
Expand Down
3 changes: 2 additions & 1 deletion admob/src/android/rewarded_video_internal_android.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ namespace rewarded_video {
X(Resume, "resume", "(J)V"), \
X(Show, "show", "(J)V"), \
X(LoadAd, "loadAd", \
"(JLjava/lang/String;Lcom/google/android/gms/ads/AdRequest;)V"), \
"(JLjava/lang/String;Ljava/lang/String;Lcom/google/android/gms/ads/AdRequest;)V"), \
X(GetPresentationState, "getPresentationState", "()I")
// clang-format on

Expand All @@ -50,6 +50,7 @@ class RewardedVideoInternalAndroid : public RewardedVideoInternal {

Future<void> Initialize() override;
Future<void> LoadAd(const char* ad_unit_id,
const char* user_id,
const AdRequest& request) override;
Future<void> Show(AdParent parent) override;
Future<void> Pause() override;
Expand Down
168 changes: 88 additions & 80 deletions admob/src/common/rewarded_video.cc
Original file line number Diff line number Diff line change
Expand Up @@ -25,156 +25,164 @@
namespace firebase {
namespace admob {
namespace rewarded_video {

// An internal, platform-specific implementation object that this class uses to
// interact with the Google Mobile Ads SDKs for iOS and Android.
static internal::RewardedVideoInternal* g_internal = nullptr;

const char kUninitializedError[] =
"rewarded_video::Initialize() must be called before this method.";
"rewarded_video::Initialize() must be called before this method.";
const char kCannotInitTwiceError[] =
"rewarded_video::Initialize cannot be called twice.";
"rewarded_video::Initialize cannot be called twice.";

PollableRewardListener::PollableRewardListener() : mutex_(new Mutex()) {}

PollableRewardListener::~PollableRewardListener() { delete mutex_; }
PollableRewardListener::~PollableRewardListener() {
delete mutex_;
}

bool PollableRewardListener::PollReward(RewardItem* reward) {
FIREBASE_ASSERT(reward != nullptr);
MutexLock lock(*mutex_);
if (rewards_.empty()) {
return false;
}
reward->amount = rewards_.front().amount;
reward->reward_type = rewards_.front().reward_type;
rewards_.pop();
return true;
FIREBASE_ASSERT(reward != nullptr);
MutexLock lock(*mutex_);

if (rewards_.empty()) {
return false;
}
reward->amount = rewards_.front().amount;
reward->reward_type = rewards_.front().reward_type;
rewards_.pop();
return true;
}

void PollableRewardListener::OnRewarded(RewardItem reward) {
MutexLock lock(*mutex_);
RewardItem new_item;
new_item.amount = reward.amount;
new_item.reward_type = reward.reward_type;
rewards_.push(new_item);
MutexLock lock(*mutex_);
RewardItem new_item;

new_item.amount = reward.amount;
new_item.reward_type = reward.reward_type;
rewards_.push(new_item);
}

void PollableRewardListener::OnPresentationStateChanged(
PresentationState state) {
// Nothing done here. The publisher already has the ability to poll
// rewarded_video::presentation_state for presentation state info.
PresentationState state) {
// Nothing done here. The publisher already has the ability to poll
// rewarded_video::presentation_state for presentation state info.
}

// Initialize must be called before any other methods in the namespace. This
// method asserts that Initialize has been invoked and allowed to complete.
static bool CheckIsInitialized() {
bool initialized =
bool initialized =
g_internal != nullptr &&
g_internal->GetLastResult(internal::kRewardedVideoFnInitialize)
.status() == kFutureStatusComplete;
FIREBASE_ASSERT_MESSAGE_RETURN(false, initialized, kUninitializedError);
return true;
.status() == kFutureStatusComplete;

FIREBASE_ASSERT_MESSAGE_RETURN(false, initialized, kUninitializedError);
return true;
}

Future<void> Initialize() {
FIREBASE_ASSERT_RETURN(Future<void>(), admob::IsInitialized());
// Initialize cannot be called more than once.
FIREBASE_ASSERT_MESSAGE_RETURN(Future<void>(), g_internal == nullptr,
kCannotInitTwiceError);
g_internal = internal::RewardedVideoInternal::CreateInstance();
GetOrCreateCleanupNotifier()->RegisterObject(g_internal, [](void*) {
// Since there is no way to shut down this module after initialization
LogWarning(
"rewardedvideo::Destroy should be called before "
"admob::Terminate.");
Destroy();
});
return g_internal->Initialize();
FIREBASE_ASSERT_RETURN(Future<void>(), admob::IsInitialized());

// Initialize cannot be called more than once.
FIREBASE_ASSERT_MESSAGE_RETURN(Future<void>(), g_internal == nullptr,
kCannotInitTwiceError);
g_internal = internal::RewardedVideoInternal::CreateInstance();
GetOrCreateCleanupNotifier()->RegisterObject(g_internal, [](void*) {
// Since there is no way to shut down this module after initialization
LogWarning(
"rewardedvideo::Destroy should be called before "
"admob::Terminate.");
Destroy();
});
return g_internal->Initialize();
}

Future<void> InitializeLastResult() {
// This result can't be checked unless the RewardedVideoInternal has been
// created, but it must be available to publishers *before* Initialize has
// completed (so they can know when it's done). That's why this result uses a
// different conditional than the others.
FIREBASE_ASSERT_MESSAGE_RETURN(Future<void>(), g_internal != nullptr,
kUninitializedError);
return g_internal->GetLastResult(internal::kRewardedVideoFnInitialize);
// This result can't be checked unless the RewardedVideoInternal has been
// created, but it must be available to publishers *before* Initialize has
// completed (so they can know when it's done). That's why this result uses a
// different conditional than the others.
FIREBASE_ASSERT_MESSAGE_RETURN(Future<void>(), g_internal != nullptr,
kUninitializedError);
return g_internal->GetLastResult(internal::kRewardedVideoFnInitialize);
}

Future<void> LoadAd(const char* ad_unit_id, const AdRequest& request) {
if (!CheckIsInitialized()) return Future<void>();
return g_internal->LoadAd(ad_unit_id, request);
return LoadAd(ad_unit_id, NULL, request);
}

Future<void> LoadAd(const char* ad_unit_id, const char* user_id, const AdRequest& request) {
if (!CheckIsInitialized()) return Future<void>();
return g_internal->LoadAd(ad_unit_id, user_id, request);
}

Future<void> LoadAdLastResult() {
if (!CheckIsInitialized()) return Future<void>();
return g_internal->GetLastResult(internal::kRewardedVideoFnLoadAd);
if (!CheckIsInitialized()) return Future<void>();
return g_internal->GetLastResult(internal::kRewardedVideoFnLoadAd);
}

Future<void> Show(AdParent parent) {
if (!CheckIsInitialized()) return Future<void>();
return g_internal->Show(parent);
if (!CheckIsInitialized()) return Future<void>();
return g_internal->Show(parent);
}

Future<void> ShowLastResult() {
if (!CheckIsInitialized()) return Future<void>();
return g_internal->GetLastResult(internal::kRewardedVideoFnShow);
if (!CheckIsInitialized()) return Future<void>();
return g_internal->GetLastResult(internal::kRewardedVideoFnShow);
}

Future<void> Pause() {
if (!CheckIsInitialized()) return Future<void>();
return g_internal->Pause();
if (!CheckIsInitialized()) return Future<void>();
return g_internal->Pause();
}

Future<void> PauseLastResult() {
if (!CheckIsInitialized()) return Future<void>();
return g_internal->GetLastResult(internal::kRewardedVideoFnPause);
if (!CheckIsInitialized()) return Future<void>();
return g_internal->GetLastResult(internal::kRewardedVideoFnPause);
}

Future<void> Resume() {
if (!CheckIsInitialized()) return Future<void>();
return g_internal->Resume();
if (!CheckIsInitialized()) return Future<void>();
return g_internal->Resume();
}

Future<void> ResumeLastResult() {
if (!CheckIsInitialized()) return Future<void>();
return g_internal->GetLastResult(internal::kRewardedVideoFnResume);
if (!CheckIsInitialized()) return Future<void>();
return g_internal->GetLastResult(internal::kRewardedVideoFnResume);
}

// This method is synchronous. It does not return a future, but instead waits
// until the internal Destroy has completed, so that it's safe to delete
// g_internal.
void Destroy() {
if (!CheckIsInitialized()) return;
if (!CheckIsInitialized()) return;

Mutex mutex(Mutex::kModeNonRecursive);
mutex.Acquire();
Mutex mutex(Mutex::kModeNonRecursive);
mutex.Acquire();

GetOrCreateCleanupNotifier()->UnregisterObject(g_internal);
GetOrCreateCleanupNotifier()->UnregisterObject(g_internal);

g_internal->Destroy().OnCompletion(
g_internal->Destroy().OnCompletion(
[](const Future<void>&, void* mutex) {
(reinterpret_cast<Mutex*>(mutex))->Release();
},
(reinterpret_cast<Mutex*>(mutex))->Release();
},
&mutex);

mutex.Acquire();
mutex.Release();
delete g_internal;
g_internal = nullptr;
mutex.Acquire();
mutex.Release();
delete g_internal;
g_internal = nullptr;
}

PresentationState presentation_state() {
if (!CheckIsInitialized()) return kPresentationStateHidden;
return g_internal->GetPresentationState();
if (!CheckIsInitialized()) return kPresentationStateHidden;
return g_internal->GetPresentationState();
}

void SetListener(Listener* listener) {
if (!CheckIsInitialized()) return;
g_internal->SetListener(listener);
if (!CheckIsInitialized()) return;
g_internal->SetListener(listener);
}

} // namespace rewarded_video
} // namespace admob
} // namespace firebase
} // namespace rewarded_video
} // namespace admob
} // namespace firebase
1 change: 1 addition & 0 deletions admob/src/common/rewarded_video_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ class RewardedVideoInternal {

// Initiates an ad request.
virtual Future<void> LoadAd(const char* ad_unit_id,
const char* user_id,
const AdRequest& request) = 0;

// Displays a rewarded video ad.
Expand Down
3 changes: 3 additions & 0 deletions admob/src/include/firebase/admob/rewarded_video.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,8 @@ struct RewardItem {
float amount;
/// A string description of the type of reward (such as "coins" or "points").
std::string reward_type;
/// A string containing the User ID for Server-Side Verification
std::string ssv_user_id;
};

/// A listener class that developers can extend and pass to @ref SetListener
Expand Down Expand Up @@ -178,6 +180,7 @@ Future<void> InitializeLastResult();
/// @param[in] request An AdRequest struct with information about the request
/// to be made (such as targeting info).
Future<void> LoadAd(const char* ad_unit_id, const AdRequest& request);
Future<void> LoadAd(const char* ad_unit_id, const char* user_id, const AdRequest& request);

/// Returns a @ref Future containing the status of the last call to
/// @ref LoadAd.
Expand Down
1 change: 1 addition & 0 deletions admob/src/ios/rewarded_video_internal_ios.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class RewardedVideoInternalIOS : public RewardedVideoInternal {

Future<void> Initialize() override;
Future<void> LoadAd(const char* ad_unit_id,
const char* user_id,
const AdRequest& request) override;
Future<void> Show(AdParent parent) override;
Future<void> Pause() override;
Expand Down
Loading