From 19c82f4f081bebd796e9aaee1dbc15a408cd8676 Mon Sep 17 00:00:00 2001 From: FalsinSoft Date: Tue, 16 Mar 2021 10:03:33 +0100 Subject: [PATCH] Add Audio tool --- QtAndroidTools/CMakeLists.txt | 8 ++ QtAndroidTools/QAndroidAdMobBanner.cpp | 1 - QtAndroidTools/QAndroidAudio.cpp | 108 +++++++++++++++++ QtAndroidTools/QAndroidAudio.h | 59 ++++++++++ QtAndroidTools/QtAndroidTools.cpp | 6 + QtAndroidTools/QtAndroidTools.pri | 10 ++ .../qtandroidtools/AndroidAudio.java | 111 ++++++++++++++++++ .../qtandroidtools/AndroidTools.java | 16 +-- QtAndroidToolsDemo/CMakeLists.txt | 2 +- QtAndroidToolsDemo/Main.qml | 3 +- QtAndroidToolsDemo/QtAndroidToolsDemo.pro | 3 +- QtAndroidToolsDemo/Sources.qrc | 1 + QtAndroidToolsDemo/tools/AndroidAudio.qml | 38 ++++++ 13 files changed, 354 insertions(+), 12 deletions(-) create mode 100644 QtAndroidTools/QAndroidAudio.cpp create mode 100644 QtAndroidTools/QAndroidAudio.h create mode 100644 QtAndroidTools/src/com/falsinsoft/qtandroidtools/AndroidAudio.java create mode 100644 QtAndroidToolsDemo/tools/AndroidAudio.qml diff --git a/QtAndroidTools/CMakeLists.txt b/QtAndroidTools/CMakeLists.txt index e3eaeda..bac011e 100644 --- a/QtAndroidTools/CMakeLists.txt +++ b/QtAndroidTools/CMakeLists.txt @@ -171,6 +171,14 @@ if(QTAT_USER_MESSAGING_PLATFORM) list(APPEND QTAT_JAVA_FILES ${QTAT_JAVA_DIR}/AndroidUserMessagingPlatform.java) endif() +option(QTAT_AUDIO "Enable QtAndroidTools Audio.") +if(QTAT_AUDIO) + add_compile_definitions(QTAT_AUDIO) + list(APPEND QTAT_SOURCE_FILES QAndroidAudio.cpp) + list(APPEND QTAT_HEADER_FILES QAndroidAudio.h) + list(APPEND QTAT_JAVA_FILES ${QTAT_JAVA_DIR}/AndroidAudio.java) +endif() + add_library(QtAndroidTools STATIC ${QTAT_SOURCE_FILES} ${QTAT_HEADER_FILES}) diff --git a/QtAndroidTools/QAndroidAdMobBanner.cpp b/QtAndroidTools/QAndroidAdMobBanner.cpp index 9fbf6bb..778cae2 100644 --- a/QtAndroidTools/QAndroidAdMobBanner.cpp +++ b/QtAndroidTools/QAndroidAdMobBanner.cpp @@ -55,7 +55,6 @@ QAndroidAdMobBanner::QAndroidAdMobBanner(QQuickItem *parent) : QQuickItem(parent connect(qGuiApp, &QGuiApplication::applicationStateChanged, this, &QAndroidAdMobBanner::applicationStateChanged); connect(qGuiApp->primaryScreen(), &QScreen::geometryChanged, this, &QAndroidAdMobBanner::screenGeometryChanged); setNewAppState(APP_STATE_CREATE); - } QAndroidAdMobBanner::~QAndroidAdMobBanner() diff --git a/QtAndroidTools/QAndroidAudio.cpp b/QtAndroidTools/QAndroidAudio.cpp new file mode 100644 index 0000000..a01b38a --- /dev/null +++ b/QtAndroidTools/QAndroidAudio.cpp @@ -0,0 +1,108 @@ +/* + * 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 "QAndroidAudio.h" + +QAndroidAudio *QAndroidAudio::m_pInstance = nullptr; + +QAndroidAudio::QAndroidAudio() : m_javaAudio("com/falsinsoft/qtandroidtools/AndroidAudio", + "(Landroid/app/Activity;)V", + QtAndroid::androidActivity().object()), + m_focus(false) +{ + m_pInstance = this; + + if(m_javaAudio.isValid()) + { + const JNINativeMethod jniMethod[] = { + {"focusChanged", "(Z)V", reinterpret_cast(&QAndroidAudio::deviceFocusChanged)}, + }; + QAndroidJniEnvironment jniEnv; + jclass objectClass; + + objectClass = jniEnv->GetObjectClass(m_javaAudio.object()); + jniEnv->RegisterNatives(objectClass, jniMethod, sizeof(jniMethod)/sizeof(JNINativeMethod)); + jniEnv->DeleteLocalRef(objectClass); + } +} + +QAndroidAudio::~QAndroidAudio() +{ +} + +QObject* QAndroidAudio::qmlInstance(QQmlEngine *engine, QJSEngine *scriptEngine) +{ + Q_UNUSED(engine); + Q_UNUSED(scriptEngine); + + return new QAndroidAudio(); +} + +QAndroidAudio* QAndroidAudio::instance() +{ + return m_pInstance; +} + +void QAndroidAudio::deviceFocusChanged(JNIEnv *env, jobject thiz, jboolean focus) +{ + Q_UNUSED(env) + Q_UNUSED(thiz) + + if(m_pInstance != nullptr) + { + Q_EMIT m_pInstance->setFocus(focus); + } +} + +bool QAndroidAudio::hasFocus() +{ + return m_focus; +} + +void QAndroidAudio::setFocus(bool focus) +{ + m_focus = focus; + Q_EMIT focusChanged(); +} + +bool QAndroidAudio::requestFocus() +{ + if(m_javaAudio.isValid()) + { + if(m_javaAudio.callMethod("requestFocus")) + { + setFocus(true); + return true; + } + } + return false; +} + +void QAndroidAudio::abandonFocus() +{ + if(m_javaAudio.isValid()) + { + m_javaAudio.callMethod("abandonAudioFocus"); + setFocus(false); + } +} diff --git a/QtAndroidTools/QAndroidAudio.h b/QtAndroidTools/QAndroidAudio.h new file mode 100644 index 0000000..d0ef80f --- /dev/null +++ b/QtAndroidTools/QAndroidAudio.h @@ -0,0 +1,59 @@ +/* + * 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 + +class QAndroidAudio : public QObject +{ + Q_PROPERTY(int focus READ hasFocus NOTIFY focusChanged) + Q_DISABLE_COPY(QAndroidAudio) + Q_OBJECT + + QAndroidAudio(); + +public: + ~QAndroidAudio(); + + static QObject* qmlInstance(QQmlEngine *engine, QJSEngine *scriptEngine); + static QAndroidAudio* instance(); + + Q_INVOKABLE bool requestFocus(); + Q_INVOKABLE void abandonFocus(); + + bool hasFocus(); + +Q_SIGNALS: + void focusChanged(); + +private: + const QAndroidJniObject m_javaAudio; + static QAndroidAudio *m_pInstance; + bool m_focus; + + void setFocus(bool focus); + + static void deviceFocusChanged(JNIEnv *env, jobject thiz, jboolean focus); +}; diff --git a/QtAndroidTools/QtAndroidTools.cpp b/QtAndroidTools/QtAndroidTools.cpp index 0caa4a3..17e8f3b 100644 --- a/QtAndroidTools/QtAndroidTools.cpp +++ b/QtAndroidTools/QtAndroidTools.cpp @@ -73,6 +73,9 @@ #ifdef QTAT_USER_MESSAGING_PLATFORM #include "QAndroidUserMessagingPlatform.h" #endif +#ifdef QTAT_AUDIO +#include "QAndroidAudio.h" +#endif #include "QtAndroidTools.h" QtAndroidTools *QtAndroidTools::m_pInstance = nullptr; @@ -227,4 +230,7 @@ void QtAndroidTools::initializeQmlTools() #ifdef QTAT_USER_MESSAGING_PLATFORM qmlRegisterSingletonType("QtAndroidTools", 1, 0, "QtAndroidUserMessagingPlatform", &QAndroidUserMessagingPlatform::qmlInstance); #endif +#ifdef QTAT_AUDIO + qmlRegisterSingletonType("QtAndroidTools", 1, 0, "QtAndroidAudio", &QAndroidAudio::qmlInstance); +#endif } diff --git a/QtAndroidTools/QtAndroidTools.pri b/QtAndroidTools/QtAndroidTools.pri index 8df42c6..7425d80 100644 --- a/QtAndroidTools/QtAndroidTools.pri +++ b/QtAndroidTools/QtAndroidTools.pri @@ -219,3 +219,13 @@ contains(DEFINES, QTAT_USER_MESSAGING_PLATFORM) { QMAKE_EXTRA_TARGETS += copy_user_messaging_platform } } +contains(DEFINES, QTAT_AUDIO) { + HEADERS += $$PWD/QAndroidAudio.h + SOURCES += $$PWD/QAndroidAudio.cpp + OTHER_FILES += $$PWD/src/com/falsinsoft/qtandroidtools/AndroidAudio.java + equals(COPY_JAVA_FILE, true) { + copy_audio.commands = $(COPY_FILE) $$shell_path($$PWD/src/com/falsinsoft/qtandroidtools/AndroidAudio.java) $$shell_path($$ANDROID_PACKAGE_SOURCE_DIR/src/com/falsinsoft/qtandroidtools/) + PRE_TARGETDEPS += copy_audio + QMAKE_EXTRA_TARGETS += copy_audio + } +} diff --git a/QtAndroidTools/src/com/falsinsoft/qtandroidtools/AndroidAudio.java b/QtAndroidTools/src/com/falsinsoft/qtandroidtools/AndroidAudio.java new file mode 100644 index 0000000..7e607db --- /dev/null +++ b/QtAndroidTools/src/com/falsinsoft/qtandroidtools/AndroidAudio.java @@ -0,0 +1,111 @@ +/* + * 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 android.content.Context; +import android.app.Activity; +import android.util.Log; +import android.media.AudioManager; +import android.media.AudioFocusRequest; +import android.media.AudioAttributes; +import android.media.AudioManager.OnAudioFocusChangeListener; + +public class AndroidAudio +{ + private static final String TAG = "AndroidAudio"; + private final Activity mActivityInstance; + private final AudioManager mAudioManager; + private AudioFocusChangeListener mAudioFocusChangeListener = null; + + public AndroidAudio(Activity activityInstance) + { + mAudioManager = (AudioManager) activityInstance.getSystemService(Context.AUDIO_SERVICE); + mActivityInstance = activityInstance; + } + + public boolean requestFocus() + { + if(mAudioFocusChangeListener == null) + { + mAudioFocusChangeListener = new AudioFocusChangeListener(); + + if(mAudioManager.requestAudioFocus(createAudioFocusRequest(mAudioFocusChangeListener)) == AudioManager.AUDIOFOCUS_REQUEST_FAILED) + { + mAudioFocusChangeListener = null; + return false; + } + } + + return true; + } + + public void abandonFocus() + { + if(mAudioFocusChangeListener != null) + { + mAudioManager.abandonAudioFocusRequest(createAudioFocusRequest(mAudioFocusChangeListener)); + mAudioFocusChangeListener = null; + } + } + + private AudioFocusRequest createAudioFocusRequest(AudioFocusChangeListener listener) + { + AudioAttributes audioAttributes; + AudioFocusRequest focusRequest; + + audioAttributes = new AudioAttributes.Builder() + .setUsage(AudioAttributes.USAGE_MEDIA) + .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH) + .build(); + focusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN) + .setAudioAttributes(audioAttributes) + .setAcceptsDelayedFocusGain(true) + .setWillPauseWhenDucked(true) + .setOnAudioFocusChangeListener(listener) + .build(); + return focusRequest; + } + + private class AudioFocusChangeListener implements OnAudioFocusChangeListener + { + @Override + public void onAudioFocusChange(int focusChange) + { + switch(focusChange) + { + case AudioManager.AUDIOFOCUS_GAIN: + focusChanged(true); + break; + case AudioManager.AUDIOFOCUS_LOSS: + case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: + case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: + focusChanged(false); + break; + } + } + } + + private static native void focusChanged(boolean focus); +} diff --git a/QtAndroidTools/src/com/falsinsoft/qtandroidtools/AndroidTools.java b/QtAndroidTools/src/com/falsinsoft/qtandroidtools/AndroidTools.java index e1c73d0..92a9b07 100644 --- a/QtAndroidTools/src/com/falsinsoft/qtandroidtools/AndroidTools.java +++ b/QtAndroidTools/src/com/falsinsoft/qtandroidtools/AndroidTools.java @@ -22,16 +22,16 @@ * SOFTWARE. */ - package com.falsinsoft.qtandroidtools; +package com.falsinsoft.qtandroidtools; - import android.content.Context; - import android.app.Activity; - import android.content.Intent; - import android.net.Uri; - import android.util.Log; +import android.content.Context; +import android.app.Activity; +import android.content.Intent; +import android.net.Uri; +import android.util.Log; - public class AndroidTools - { +public class AndroidTools +{ private static final String TAG = "AndroidTools"; private final Activity mActivityInstance; diff --git a/QtAndroidToolsDemo/CMakeLists.txt b/QtAndroidToolsDemo/CMakeLists.txt index 7b13e50..b07f05a 100644 --- a/QtAndroidToolsDemo/CMakeLists.txt +++ b/QtAndroidToolsDemo/CMakeLists.txt @@ -35,7 +35,7 @@ if(ANDROID) set(QTAT_GOOGLE_DRIVE ON) set(QTAT_SHARING ON) set(QTAT_USER_MESSAGING_PLATFORM ON) - set(QTAT_PLAY_STORE ON) + set(QTAT_AUDIO ON) find_package(Qt${QT_VERSION_MAJOR} COMPONENTS AndroidExtras REQUIRED) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../QtAndroidTools build) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../QtAndroidTools) diff --git a/QtAndroidToolsDemo/Main.qml b/QtAndroidToolsDemo/Main.qml index 8bde52d..618ce1a 100644 --- a/QtAndroidToolsDemo/Main.qml +++ b/QtAndroidToolsDemo/Main.qml @@ -107,6 +107,7 @@ ApplicationWindow { ListElement { title: "GoogleDrive"; source: "qrc:/tools/AndroidGoogleDrive.qml" } ListElement { title: "Sharing"; source: "qrc:/tools/AndroidSharing.qml" } ListElement { title: "UserMessagingPlatform"; source: "qrc:/tools/AndroidUserMessagingPlatform.qml" } + ListElement { title: "Audio"; source: "qrc:/tools/AndroidAudio.qml" } ListElement { title: "System"; source: "qrc:/tools/AndroidSystem.qml" } } @@ -131,7 +132,7 @@ ApplicationWindow { } Label { - text: "Small collections of tools for manage some Android features from Qt and QML app" + text: "Small collections of tools to manage some Android features from Qt and QML app" anchors.margins: 20 anchors.top: logo.bottom anchors.left: parent.left diff --git a/QtAndroidToolsDemo/QtAndroidToolsDemo.pro b/QtAndroidToolsDemo/QtAndroidToolsDemo.pro index f045553..60ae538 100644 --- a/QtAndroidToolsDemo/QtAndroidToolsDemo.pro +++ b/QtAndroidToolsDemo/QtAndroidToolsDemo.pro @@ -35,7 +35,8 @@ DEFINES += \ QTAT_GOOGLE_ACCOUNT \ QTAT_GOOGLE_DRIVE \ QTAT_SHARING \ - QTAT_USER_MESSAGING_PLATFORM + QTAT_USER_MESSAGING_PLATFORM \ + QTAT_AUDIO include(../QtAndroidTools/QtAndroidTools.pri) } diff --git a/QtAndroidToolsDemo/Sources.qrc b/QtAndroidToolsDemo/Sources.qrc index 16f7595..614626f 100644 --- a/QtAndroidToolsDemo/Sources.qrc +++ b/QtAndroidToolsDemo/Sources.qrc @@ -19,5 +19,6 @@ tools/AndroidSystem.qml tools/AndroidSharing.qml tools/AndroidUserMessagingPlatform.qml + tools/AndroidAudio.qml diff --git a/QtAndroidToolsDemo/tools/AndroidAudio.qml b/QtAndroidToolsDemo/tools/AndroidAudio.qml new file mode 100644 index 0000000..52751d2 --- /dev/null +++ b/QtAndroidToolsDemo/tools/AndroidAudio.qml @@ -0,0 +1,38 @@ +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Dialogs 1.3 +import QtAndroidTools 1.0 + +Page { + id: page + padding: 40 + + Column { + width: parent.width * 0.9 + height: parent.height * 0.9 + anchors.centerIn: parent + spacing: 15 + + Label { + anchors.horizontalCenter: parent.horizontalCenter + font.pixelSize: 15 + text: "Focus:" + } + Label { + anchors.horizontalCenter: parent.horizontalCenter + font.pixelSize: 15 + text: QtAndroidAudio.focus ? "Yes" : "No" + } + + Button { + anchors.horizontalCenter: parent.horizontalCenter + text: "requestFocus" + onClicked: QtAndroidAudio.requestFocus() + } + Button { + anchors.horizontalCenter: parent.horizontalCenter + text: "abandonFocus" + onClicked: QtAndroidAudio.abandonFocus() + } + } +}