diff --git a/QtAndroidTools/QAndroidAdMobInterstitial.cpp b/QtAndroidTools/QAndroidAdMobInterstitial.cpp new file mode 100644 index 0000000..1761f8c --- /dev/null +++ b/QtAndroidTools/QAndroidAdMobInterstitial.cpp @@ -0,0 +1,160 @@ +/* + * MIT License + * + * Copyright (c) 2018 Fabio Falsini + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include +#include "QAndroidAdMobInterstitial.h" + +QMap QAndroidAdMobInterstitial::m_pInstancesMap; +int QAndroidAdMobInterstitial::m_InstancesCounter = 0; + +QAndroidAdMobInterstitial::QAndroidAdMobInterstitial(QQuickItem *parent) : QQuickItem(parent), + m_JavaAdMobInterstitial("com/falsinsoft/qtandroidtools/AdMobInterstitial", + "(Landroid/app/Activity;)V", + QtAndroid::androidActivity().object()), + m_InstanceIndex(m_InstancesCounter++) +{ + m_pInstancesMap[m_InstanceIndex] = this; + + if(m_InstanceIndex == 0 && m_JavaAdMobInterstitial.isValid()) + { + JNINativeMethod JniMethod[] = {{"interstitialEvent", "(I)V", reinterpret_cast(&QAndroidAdMobInterstitial::InterstitialEvent)}, + {"interstitialError", "(I)V", reinterpret_cast(&QAndroidAdMobInterstitial::InterstitialError)} + }; + QAndroidJniEnvironment JniEnv; + jclass ObjectClass; + + ObjectClass = JniEnv->GetObjectClass(m_JavaAdMobInterstitial.object()); + JniEnv->RegisterNatives(ObjectClass, JniMethod, 2); + JniEnv->DeleteLocalRef(ObjectClass); + } + connect(qGuiApp, &QGuiApplication::applicationStateChanged, this, &QAndroidAdMobInterstitial::ApplicationStateChanged); + SetNewAppState(APP_STATE_CREATE); +} + +QAndroidAdMobInterstitial::~QAndroidAdMobInterstitial() +{ + m_pInstancesMap.remove(m_InstanceIndex); + SetNewAppState(APP_STATE_DESTROY); +} + +const QMap& QAndroidAdMobInterstitial::Instances() +{ + return m_pInstancesMap; +} + +bool QAndroidAdMobInterstitial::show() +{ + if(m_JavaAdMobInterstitial.isValid() && m_UnitId.isEmpty() == false) + { + m_JavaAdMobInterstitial.callMethod("show"); + return true; + } + + return false; +} + +bool QAndroidAdMobInterstitial::load() +{ + if(m_JavaAdMobInterstitial.isValid() && m_UnitId.isEmpty() == false) + { + m_JavaAdMobInterstitial.callMethod("load"); + return true; + } + + return false; +} + +const QString& QAndroidAdMobInterstitial::getUnitId() const +{ + return m_UnitId; +} + +void QAndroidAdMobInterstitial::setUnitId(const QString &UnitId) +{ + if(m_JavaAdMobInterstitial.isValid()) + { + m_JavaAdMobInterstitial.callMethod("setUnitId", + "(Ljava/lang/String;)V", + QAndroidJniObject::fromString(UnitId).object() + ); + m_UnitId = UnitId; + } +} + +void QAndroidAdMobInterstitial::InterstitialEvent(JNIEnv *env, jobject thiz, jint eventId) +{ + QMapIterator Instance(m_pInstancesMap); + + Q_UNUSED(env) + Q_UNUSED(thiz) + + while(Instance.hasNext()) + { + Instance.next(); + switch(eventId) + { + case EVENT_LOADING: + emit Instance.value()->loading(); + break; + case EVENT_LOADED: + emit Instance.value()->loaded(); + break; + case EVENT_CLOSED: + emit Instance.value()->closed(); + break; + case EVENT_CLICKED: + emit Instance.value()->clicked(); + break; + } + } +} + +void QAndroidAdMobInterstitial::InterstitialError(JNIEnv *env, jobject thiz, jint errorId) +{ + QMapIterator Instance(m_pInstancesMap); + + Q_UNUSED(env) + Q_UNUSED(thiz) + + while(Instance.hasNext()) + { + Instance.next(); + emit Instance.value()->loadError(errorId); + } +} + +void QAndroidAdMobInterstitial::ApplicationStateChanged(Qt::ApplicationState State) +{ + SetNewAppState((State == Qt::ApplicationActive) ? APP_STATE_START : APP_STATE_STOP); +} + +void QAndroidAdMobInterstitial::SetNewAppState(APP_STATE NewState) +{ + if(m_JavaAdMobInterstitial.isValid()) + { + m_JavaAdMobInterstitial.callMethod("appStateChanged", + "(I)V", + NewState + ); + } +} diff --git a/QtAndroidTools/QAndroidAdMobInterstitial.h b/QtAndroidTools/QAndroidAdMobInterstitial.h new file mode 100644 index 0000000..b034703 --- /dev/null +++ b/QtAndroidTools/QAndroidAdMobInterstitial.h @@ -0,0 +1,92 @@ +/* + * MIT License + * + * Copyright (c) 2018 Fabio Falsini + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#pragma once + +#include +#include +#include + +class QAndroidAdMobInterstitial : public QQuickItem +{ + Q_PROPERTY(QString unitId READ getUnitId WRITE setUnitId) + Q_ENUMS(ERROR_TYPE) + Q_OBJECT + +public: + QAndroidAdMobInterstitial(QQuickItem *parent = nullptr); + ~QAndroidAdMobInterstitial(); + + enum ERROR_TYPE + { + ERROR_INTERNAL = 0, + ERROR_NETWORK, + ERROR_INVALID_REQUEST, + ERROR_NO_FILL + }; + + Q_INVOKABLE bool show(); + Q_INVOKABLE bool load(); + + const QString& getUnitId() const; + void setUnitId(const QString &UnitId); + + static const QMap& Instances(); + +signals: + void loadError(int errorId); + void loading(); + void loaded(); + void closed(); + void clicked(); + +private slots: + void ApplicationStateChanged(Qt::ApplicationState State); + +private: + const QAndroidJniObject m_JavaAdMobInterstitial; + static QMap m_pInstancesMap; + static int m_InstancesCounter; + const int m_InstanceIndex; + QString m_UnitId; + + enum EVENT_TYPE + { + EVENT_LOADING = 0, + EVENT_LOADED, + EVENT_CLOSED, + EVENT_CLICKED + }; + + static void InterstitialEvent(JNIEnv *env, jobject thiz, jint eventId); + static void InterstitialError(JNIEnv *env, jobject thiz, jint errorId); + + enum APP_STATE + { + APP_STATE_CREATE = 0, + APP_STATE_START, + APP_STATE_STOP, + APP_STATE_DESTROY + }; + void SetNewAppState(APP_STATE NewState); +}; diff --git a/QtAndroidTools/QtAndroidTools.cpp b/QtAndroidTools/QtAndroidTools.cpp index 1712da8..b7f1aab 100644 --- a/QtAndroidTools/QtAndroidTools.cpp +++ b/QtAndroidTools/QtAndroidTools.cpp @@ -28,6 +28,7 @@ #include "QAndroidBatteryState.h" #include "QAndroidSignalStrength.h" #include "QAndroidAdMobBanner.h" +#include "QAndroidAdMobInterstitial.h" #include "QtAndroidTools.h" QtAndroidTools::QtAndroidTools() @@ -42,4 +43,5 @@ void QtAndroidTools::InitializeQmlTools() qmlRegisterSingletonType("QtAndroidTools", 1, 0, "QtAndroidBatteryState", &QAndroidBatteryState::qmlInstance); qmlRegisterSingletonType("QtAndroidTools", 1, 0, "QtAndroidSignalStrength", &QAndroidSignalStrength::qmlInstance); qmlRegisterType("QtAndroidTools", 1, 0, "QtAndroidAdMobBanner"); + qmlRegisterType("QtAndroidTools", 1, 0, "QtAndroidAdMobInterstitial"); } diff --git a/QtAndroidTools/QtAndroidTools.pri b/QtAndroidTools/QtAndroidTools.pri index 370bf8d..816fbae 100644 --- a/QtAndroidTools/QtAndroidTools.pri +++ b/QtAndroidTools/QtAndroidTools.pri @@ -26,6 +26,10 @@ HEADERS += $$PWD/QAndroidAdMobBanner.h SOURCES += $$PWD/QAndroidAdMobBanner.cpp OTHER_FILES += $$PWD/src/com/falsinsoft/qtandroidtools/AdMobBanner.java +HEADERS += $$PWD/QAndroidAdMobInterstitial.h +SOURCES += $$PWD/QAndroidAdMobInterstitial.cpp +OTHER_FILES += $$PWD/src/com/falsinsoft/qtandroidtools/AdMobInterstitial.java + copy_src.commands = $(CHK_DIR_EXISTS) $$shell_path($$ANDROID_PACKAGE_SOURCE_DIR/src/com/falsinsoft/qtandroidtools) $(COPY_DIR) $$shell_path($$PWD/src) $$shell_path($$ANDROID_PACKAGE_SOURCE_DIR/src) copy_aidl.commands = $(CHK_DIR_EXISTS) $$shell_path($$ANDROID_PACKAGE_SOURCE_DIR/aidl/com/android/vending/licensing) $(COPY_DIR) $$shell_path($$PWD/aidl) $$shell_path($$ANDROID_PACKAGE_SOURCE_DIR/aidl) PRE_TARGETDEPS += copy_src copy_aidl diff --git a/QtAndroidTools/src/com/falsinsoft/qtandroidtools/AdMobInterstitial.java b/QtAndroidTools/src/com/falsinsoft/qtandroidtools/AdMobInterstitial.java new file mode 100644 index 0000000..f35fb77 --- /dev/null +++ b/QtAndroidTools/src/com/falsinsoft/qtandroidtools/AdMobInterstitial.java @@ -0,0 +1,216 @@ +/* + * MIT License + * + * Copyright (c) 2018 Fabio Falsini + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.falsinsoft.qtandroidtools; + +import com.google.android.gms.ads.AdRequest; +import com.google.android.gms.ads.AdListener; +import com.google.android.gms.ads.InterstitialAd; +import android.app.Activity; +import android.content.Context; +import android.os.Bundle; +import android.view.View; +import android.view.Window; +import android.view.ViewGroup; +import android.util.Log; +import android.graphics.Rect; +import android.widget.FrameLayout; +import android.graphics.Color; + +public class AdMobInterstitial +{ + private final Activity mActivityInstance; + private final InterstitialListener mInterstitialListener; + + private InterstitialAd mInterstitialAd = null; + + public AdMobInterstitial(Activity ActivityInstance) + { + mInterstitialListener = new InterstitialListener(); + mActivityInstance = ActivityInstance; + } + + public void setUnitId(final String unitId) + { + if(mInterstitialAd == null) + { + return; + } + + SyncRunOnUiThread UiThread = new SyncRunOnUiThread(mActivityInstance, new SyncRunOnUiThread.SyncRunOnUiThreadListener() + { + public void runOnUIThread() + { + mInterstitialAd.setAdUnitId(unitId); + } + }); + UiThread.exec(); + } + + public void load() + { + if(mInterstitialAd == null) + { + return; + } + + mActivityInstance.runOnUiThread(new Runnable() + { + @Override + public void run() + { + AdRequest.Builder InterstitialRequest = new AdRequest.Builder(); + mInterstitialAd.loadAd(InterstitialRequest.build()); + interstitialEvent(EVENT_LOADING); + } + }); + } + + public void show() + { + if(mInterstitialAd == null) + { + return; + } + + mActivityInstance.runOnUiThread(new Runnable() + { + @Override + public void run() + { + mInterstitialAd.show(); + } + }); + } + + public void appStateChanged(int newState) + { + switch(newState) + { + case APP_STATE_CREATE: + createInterstitial(); + break; + case APP_STATE_START: + case APP_STATE_STOP: + break; + case APP_STATE_DESTROY: + destroyInterstitial(); + break; + } + } + + private void createInterstitial() + { + if(mInterstitialAd != null) + { + return; + } + + SyncRunOnUiThread UiThread = new SyncRunOnUiThread(mActivityInstance, new SyncRunOnUiThread.SyncRunOnUiThreadListener() + { + public void runOnUIThread() + { + mInterstitialAd = new InterstitialAd(mActivityInstance); + mInterstitialAd.setAdListener(mInterstitialListener); + } + }); + UiThread.exec(); + } + + private void destroyInterstitial() + { + if(mInterstitialAd == null) + { + return; + } + + SyncRunOnUiThread UiThread = new SyncRunOnUiThread(mActivityInstance, new SyncRunOnUiThread.SyncRunOnUiThreadListener() + { + public void runOnUIThread() + { + mInterstitialAd = null; + } + }); + UiThread.exec(); + } + + private class InterstitialListener extends AdListener + { + public void onAdLoaded() + { + interstitialEvent(EVENT_LOADED); + } + + public void onAdClosed() + { + interstitialEvent(EVENT_CLOSED); + } + + public void onAdLeftApplication() + { + interstitialEvent(EVENT_CLICKED); + } + + public void onAdFailedToLoad(int errorCode) + { + int errorId = 0; + + switch(errorCode) + { + case AdRequest.ERROR_CODE_INTERNAL_ERROR: + errorId = ERROR_INTERNAL; + break; + case AdRequest.ERROR_CODE_NETWORK_ERROR: + errorId = ERROR_NETWORK; + break; + case AdRequest.ERROR_CODE_INVALID_REQUEST: + errorId = ERROR_INVALID_REQUEST; + break; + case AdRequest.ERROR_CODE_NO_FILL: + errorId = ERROR_NO_FILL; + break; + } + + interstitialError(errorId); + } + } + + private static final int ERROR_INTERNAL = 0; + private static final int ERROR_NETWORK = 1; + private static final int ERROR_INVALID_REQUEST = 2; + private static final int ERROR_NO_FILL = 3; + + private static final int EVENT_LOADING = 0; + private static final int EVENT_LOADED = 1; + private static final int EVENT_CLOSED = 2; + private static final int EVENT_CLICKED = 3; + + private static final int APP_STATE_CREATE = 0; + private static final int APP_STATE_START = 1; + private static final int APP_STATE_STOP = 2; + private static final int APP_STATE_DESTROY = 3; + + private static native void interstitialEvent(int eventId); + private static native void interstitialError(int errorId); +} diff --git a/QtAndroidToolsDemo/Main.qml b/QtAndroidToolsDemo/Main.qml index bbef4e9..2a2039d 100644 --- a/QtAndroidToolsDemo/Main.qml +++ b/QtAndroidToolsDemo/Main.qml @@ -89,6 +89,7 @@ ApplicationWindow { ListElement { title: "BatteryState"; source: "qrc:/tools/AndroidBatteryState.qml" } ListElement { title: "SignalStrength"; source: "qrc:/tools/AndroidSignalStrength.qml" } ListElement { title: "AdMobBanner"; source: "qrc:/tools/AndroidAdMobBanner.qml" } + ListElement { title: "AdMobInterstitial"; source: "qrc:/tools/AndroidAdMobInterstitial.qml" } } ScrollIndicator.vertical: ScrollIndicator { } diff --git a/QtAndroidToolsDemo/Sources.qrc b/QtAndroidToolsDemo/Sources.qrc index 467621d..2ebc409 100644 --- a/QtAndroidToolsDemo/Sources.qrc +++ b/QtAndroidToolsDemo/Sources.qrc @@ -8,5 +8,6 @@ tools/AndroidBatteryState.qml tools/AndroidSignalStrength.qml tools/AndroidAdMobBanner.qml + tools/AndroidAdMobInterstitial.qml diff --git a/QtAndroidToolsDemo/tools/AndroidAdMobInterstitial.qml b/QtAndroidToolsDemo/tools/AndroidAdMobInterstitial.qml new file mode 100644 index 0000000..e2ffcfa --- /dev/null +++ b/QtAndroidToolsDemo/tools/AndroidAdMobInterstitial.qml @@ -0,0 +1,50 @@ + +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Dialogs 1.3 +import QtAndroidTools 1.0 + +Page { + id: page + padding: 0 + + QtAndroidAdMobInterstitial { + id: interstitial + unitId: "ca-app-pub-3940256099942544/1033173712" + onLoading: interstitialState.text = "Loading" + onLoaded: interstitialState.text = "Loaded" + onLoadError: interstitialState.text = "Error " + errorId + } + + Column { + width: parent.wdith + height: parent.height * 0.8 + anchors.centerIn: parent + spacing: 20 + + + Label { + anchors.horizontalCenter: parent.horizontalCenter + font.bold: true + font.pixelSize: 15 + text: "Interstitial status" + } + Label { + id: interstitialState + anchors.horizontalCenter: parent.horizontalCenter + font.pixelSize: 13 + text: "Not loaded" + } + + Button { + anchors.horizontalCenter: parent.horizontalCenter + text: "Load interstitial" + onClicked: banner1.load(); + } + Button { + anchors.horizontalCenter: parent.horizontalCenter + text: "Show interstitial" + onClicked: banner1.show(); + } + } +}