From 2d2a4804f7790f41a8b9af373c5274ff73e7c84c Mon Sep 17 00:00:00 2001 From: Gabriel Calero Date: Thu, 6 Sep 2018 17:49:52 -0300 Subject: [PATCH 1/9] Add android settings screen. Add AEC setting --- android/app/src/main/AndroidManifest.xml | 10 +++ android/app/src/main/cpp/native.cpp | 46 ++++++++++++++ .../hifiinterface/InterfaceActivity.java | 7 +++ .../hifiinterface/MainActivity.java | 23 +++++++ .../fragment/SettingsFragment.java | 63 +++++++++++++++++++ .../receiver/HeadsetStateReceiver.java | 20 ++++++ .../app/src/main/res/menu/menu_navigation.xml | 5 ++ android/app/src/main/res/values/strings.xml | 5 ++ android/app/src/main/res/xml/settings.xml | 11 ++++ interface/src/AndroidHelper.cpp | 17 +++++ interface/src/AndroidHelper.h | 1 + libraries/audio-client/src/AudioClient.cpp | 6 +- libraries/audio-client/src/AudioClient.h | 8 +++ 13 files changed, 220 insertions(+), 2 deletions(-) create mode 100644 android/app/src/main/java/io/highfidelity/hifiinterface/fragment/SettingsFragment.java create mode 100644 android/app/src/main/java/io/highfidelity/hifiinterface/receiver/HeadsetStateReceiver.java create mode 100644 android/app/src/main/res/xml/settings.xml diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 7255e1f295..b216819ed0 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -9,6 +9,7 @@ + @@ -75,6 +76,15 @@ android:enabled="true" android:exported="false" android:process=":breakpad_uploader"/> + + + + + + diff --git a/android/app/src/main/cpp/native.cpp b/android/app/src/main/cpp/native.cpp index ce5af01f29..6b44b2dc7a 100644 --- a/android/app/src/main/cpp/native.cpp +++ b/android/app/src/main/cpp/native.cpp @@ -355,5 +355,51 @@ JNIEXPORT void Java_io_highfidelity_hifiinterface_WebViewActivity_nativeProcessU AndroidHelper::instance().processURL(QString::fromUtf8(nativeString)); } +JNIEXPORT void JNICALL +Java_io_highfidelity_hifiinterface_fragment_SettingsFragment_updateHifiSetting(JNIEnv *env, + jobject instance, + jstring group_, + jstring key_, + jboolean value_) { + const char *c_group = env->GetStringUTFChars(group_, 0); + const char *c_key = env->GetStringUTFChars(key_, 0); + + const QString group = QString::fromUtf8(c_group); + const QString key = QString::fromUtf8(c_key); + + env->ReleaseStringUTFChars(group_, c_group); + env->ReleaseStringUTFChars(key_, c_key); + + bool value = value_; + + Setting::Handle setting { QStringList() << group << key , !value }; + setting.set(value); +} + +JNIEXPORT jboolean JNICALL +Java_io_highfidelity_hifiinterface_fragment_SettingsFragment_getHifiSettingBoolean(JNIEnv *env, + jobject instance, + jstring group_, + jstring key_, + jboolean defaultValue) { + const char *c_group = env->GetStringUTFChars(group_, 0); + const char *c_key = env->GetStringUTFChars(key_, 0); + + const QString group = QString::fromUtf8(c_group); + const QString key = QString::fromUtf8(c_key); + + env->ReleaseStringUTFChars(group_, c_group); + env->ReleaseStringUTFChars(key_, c_key); + + Setting::Handle setting { QStringList() << group << key , defaultValue}; + return setting.get(); +} + +JNIEXPORT void JNICALL +Java_io_highfidelity_hifiinterface_receiver_HeadsetStateReceiver_notifyHeadsetOn(JNIEnv *env, + jobject instance, + jboolean pluggedIn) { + AndroidHelper::instance().notifyHeadsetOn(pluggedIn); +} } diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java b/android/app/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java index f161783d6a..08e66a2f42 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java @@ -13,6 +13,7 @@ package io.highfidelity.hifiinterface; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; @@ -40,6 +41,7 @@ import java.util.HashMap; import java.util.List; import io.highfidelity.hifiinterface.fragment.WebViewFragment; +import io.highfidelity.hifiinterface.receiver.HeadsetStateReceiver; /*import com.google.vr.cardboard.DisplaySynchronizer; import com.google.vr.cardboard.DisplayUtils; @@ -55,6 +57,7 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW private static final int NORMAL_DPI = 160; private Vibrator mVibrator; + private HeadsetStateReceiver headsetStateReceiver; //public static native void handleHifiURL(String hifiURLString); private native long nativeOnCreate(InterfaceActivity instance, AssetManager assetManager); @@ -151,6 +154,8 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW layoutParams.resolveLayoutDirection(View.LAYOUT_DIRECTION_RTL); qtLayout.addView(webSlidingDrawer, layoutParams); webSlidingDrawer.setVisibility(View.GONE); + + headsetStateReceiver = new HeadsetStateReceiver(); } @Override @@ -161,6 +166,7 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW } else { nativeEnterBackground(); } + unregisterReceiver(headsetStateReceiver); //gvrApi.pauseTracking(); } @@ -183,6 +189,7 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW nativeEnterForeground(); surfacesWorkaround(); keepInterfaceRunning = false; + registerReceiver(headsetStateReceiver, new IntentFilter(Intent.ACTION_HEADSET_PLUG)); //gvrApi.resumeTracking(); } diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/MainActivity.java b/android/app/src/main/java/io/highfidelity/hifiinterface/MainActivity.java index db6f0fca24..4c6d05a3e8 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/MainActivity.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/MainActivity.java @@ -33,6 +33,7 @@ import io.highfidelity.hifiinterface.fragment.FriendsFragment; import io.highfidelity.hifiinterface.fragment.HomeFragment; import io.highfidelity.hifiinterface.fragment.LoginFragment; import io.highfidelity.hifiinterface.fragment.PolicyFragment; +import io.highfidelity.hifiinterface.fragment.SettingsFragment; import io.highfidelity.hifiinterface.task.DownloadProfileImageTask; public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener, @@ -80,6 +81,8 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On mPeopleMenuItem = mNavigationView.getMenu().findItem(R.id.action_people); + updateDebugMenu(mNavigationView.getMenu()); + Toolbar toolbar = findViewById(R.id.toolbar); toolbar.setTitleTextAppearance(this, R.style.HomeActionBarTitleStyle); setSupportActionBar(toolbar); @@ -108,6 +111,16 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On } } + private void updateDebugMenu(Menu menu) { + if (BuildConfig.DEBUG) { + for (int i=0; i < menu.size(); i++) { + if (menu.getItem(i).getItemId() == R.id.action_debug_settings) { + menu.getItem(i).setVisible(true); + } + } + } + } + private void loadFragment(String fragment) { switch (fragment) { case "Login": @@ -151,6 +164,13 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On loadFragment(fragment, getString(R.string.people), getString(R.string.tagFragmentPeople), true); } + private void loadSettingsFragment() { + SettingsFragment fragment = SettingsFragment.newInstance(); + + loadFragment(fragment, getString(R.string.settings), getString(R.string.tagSettings), true); + } + + private void loadFragment(Fragment fragment, String title, String tag, boolean addToBackStack) { FragmentManager fragmentManager = getFragmentManager(); @@ -241,6 +261,9 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On case R.id.action_people: loadPeopleFragment(); return true; + case R.id.action_debug_settings: + loadSettingsFragment(); + return true; } return false; } diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/SettingsFragment.java b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/SettingsFragment.java new file mode 100644 index 0000000000..cc23665e72 --- /dev/null +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/SettingsFragment.java @@ -0,0 +1,63 @@ +package io.highfidelity.hifiinterface.fragment; + +import android.content.SharedPreferences; +import android.media.audiofx.AcousticEchoCanceler; +import android.os.Bundle; +import android.preference.Preference; +import android.preference.PreferenceFragment; +import android.support.annotation.Nullable; + +import io.highfidelity.hifiinterface.R; + +public class SettingsFragment extends PreferenceFragment implements SharedPreferences.OnSharedPreferenceChangeListener { + + public native void updateHifiSetting(String group, String key, boolean value); + public native boolean getHifiSettingBoolean(String group, String key, boolean defaultValue); + + private final String HIFI_SETTINGS_ANDROID_GROUP = "Android"; + private final String HIFI_SETTINGS_AEC_KEY = "aec"; + private final String PREFERENCE_KEY_AEC = "aec"; + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + addPreferencesFromResource(R.xml.settings); + + if (!AcousticEchoCanceler.isAvailable()) { + getPreferenceScreen().getPreferenceManager().findPreference("aec").setEnabled(false); + } + + getPreferenceScreen().getSharedPreferences().edit().putBoolean(PREFERENCE_KEY_AEC, + getHifiSettingBoolean(HIFI_SETTINGS_ANDROID_GROUP, HIFI_SETTINGS_AEC_KEY, false)); + } + + public static SettingsFragment newInstance() { + SettingsFragment fragment = new SettingsFragment(); + return fragment; + } + + @Override + public void onResume() { + super.onResume(); + getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this); + } + + @Override + public void onPause() { + super.onPause(); + getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this); + + } + + @Override + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { + Preference pref = findPreference(key); + switch (key) { + case "aec": + updateHifiSetting(HIFI_SETTINGS_ANDROID_GROUP, HIFI_SETTINGS_AEC_KEY, sharedPreferences.getBoolean(key, false)); + break; + default: + break; + } + } +} diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/receiver/HeadsetStateReceiver.java b/android/app/src/main/java/io/highfidelity/hifiinterface/receiver/HeadsetStateReceiver.java new file mode 100644 index 0000000000..29bc1c49f2 --- /dev/null +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/receiver/HeadsetStateReceiver.java @@ -0,0 +1,20 @@ +package io.highfidelity.hifiinterface.receiver; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.media.AudioManager; +import android.util.Log; + +public class HeadsetStateReceiver extends BroadcastReceiver { + + private native void notifyHeadsetOn(boolean pluggedIn); + + @Override + public void onReceive(Context context, Intent intent) { + AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); + + Log.d("[HEADSET] " , "BR - Wired headset on:" + audioManager.isWiredHeadsetOn()); + notifyHeadsetOn(audioManager.isWiredHeadsetOn()); + } +} diff --git a/android/app/src/main/res/menu/menu_navigation.xml b/android/app/src/main/res/menu/menu_navigation.xml index 3cce64f9f5..142af5d146 100644 --- a/android/app/src/main/res/menu/menu_navigation.xml +++ b/android/app/src/main/res/menu/menu_navigation.xml @@ -9,4 +9,9 @@ android:id="@+id/action_people" android:title="@string/people" /> + diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index b158aba59d..abde15f484 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -29,4 +29,9 @@ tagFragmentLogin tagFragmentPolicy tagFragmentPeople + tagSettings + Settings + AEC + Acoustic Echo Cancellation + Developer diff --git a/android/app/src/main/res/xml/settings.xml b/android/app/src/main/res/xml/settings.xml new file mode 100644 index 0000000000..5ec47b1aff --- /dev/null +++ b/android/app/src/main/res/xml/settings.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/interface/src/AndroidHelper.cpp b/interface/src/AndroidHelper.cpp index 419382f2cb..35bf094591 100644 --- a/interface/src/AndroidHelper.cpp +++ b/interface/src/AndroidHelper.cpp @@ -10,6 +10,7 @@ // #include "AndroidHelper.h" #include +#include #include "Application.h" #if defined(qApp) @@ -18,6 +19,7 @@ #define qApp (static_cast(QCoreApplication::instance())) AndroidHelper::AndroidHelper() { + qRegisterMetaType("QAudio::Mode"); } AndroidHelper::~AndroidHelper() { @@ -56,3 +58,18 @@ void AndroidHelper::processURL(const QString &url) { qApp->acceptURL(url); } } + +void AndroidHelper::notifyHeadsetOn(bool pluggedIn) { +#if defined (Q_OS_ANDROID) + auto audioClient = DependencyManager::get(); + if (audioClient) { + QAudioDeviceInfo activeDev = audioClient->getActiveAudioDevice(QAudio::AudioInput); + Setting::Handle enableAEC(QStringList() << ANDROID_SETTINGS_GROUP << SETTING_AEC_KEY, false); + if ((pluggedIn || !enableAEC.get()) && !activeDev.isNull() && activeDev.deviceName() != VOICE_RECOGNITION) { + QMetaObject::invokeMethod(audioClient.get(), "switchAudioDevice", Q_ARG(QAudio::Mode, QAudio::AudioInput), Q_ARG(QString, VOICE_RECOGNITION)); + } else if ( (!pluggedIn && enableAEC.get()) && !activeDev.isNull() && activeDev.deviceName() != VOICE_COMMUNICATION) { + QMetaObject::invokeMethod(audioClient.get(), "switchAudioDevice", Q_ARG(QAudio::Mode, QAudio::AudioInput), Q_ARG(QString, VOICE_COMMUNICATION)); + } + } +#endif +} diff --git a/interface/src/AndroidHelper.h b/interface/src/AndroidHelper.h index 03d92f91d9..11b84e4025 100644 --- a/interface/src/AndroidHelper.h +++ b/interface/src/AndroidHelper.h @@ -29,6 +29,7 @@ public: void performHapticFeedback(int duration); void processURL(const QString &url); + void notifyHeadsetOn(bool pluggedIn); AndroidHelper(AndroidHelper const&) = delete; void operator=(AndroidHelper const&) = delete; diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 6a9363f309..e23b8ac3cd 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -53,7 +53,6 @@ #include "AudioHelpers.h" #if defined(Q_OS_ANDROID) -#define VOICE_RECOGNITION "voicerecognition" #include #endif @@ -451,9 +450,12 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) { #if defined (Q_OS_ANDROID) if (mode == QAudio::AudioInput) { + Setting::Handle enableAEC(QStringList() << ANDROID_SETTINGS_GROUP << SETTING_AEC_KEY, false); + bool aecEnabled = enableAEC.get(); auto inputDevices = QAudioDeviceInfo::availableDevices(QAudio::AudioInput); for (auto inputDevice : inputDevices) { - if (inputDevice.deviceName() == VOICE_RECOGNITION) { + if ((aecEnabled && inputDevice.deviceName() == VOICE_COMMUNICATION) || + (!aecEnabled && inputDevice.deviceName() == VOICE_RECOGNITION)) { return inputDevice; } } diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 8599c990a3..fa7ac40a16 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -64,6 +64,14 @@ #pragma warning( pop ) #endif +#if defined (Q_OS_ANDROID) +#define VOICE_RECOGNITION "voicerecognition" +#define VOICE_COMMUNICATION "voicecommunication" + +#define ANDROID_SETTINGS_GROUP "Android" +#define SETTING_AEC_KEY "aec" +#endif + class QAudioInput; class QAudioOutput; class QIODevice; From 2943502c9b5d0d9630c97b76793ffa5b3c205561 Mon Sep 17 00:00:00 2001 From: Gabriel Calero Date: Wed, 12 Sep 2018 16:36:04 -0300 Subject: [PATCH 2/9] Headset plug/unplug detection --- .../receiver/HeadsetStateReceiver.java | 2 -- interface/src/AndroidHelper.cpp | 8 +---- libraries/audio-client/src/AudioClient.cpp | 32 +++++++++++++++++-- libraries/audio-client/src/AudioClient.h | 11 +++++-- 4 files changed, 39 insertions(+), 14 deletions(-) diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/receiver/HeadsetStateReceiver.java b/android/app/src/main/java/io/highfidelity/hifiinterface/receiver/HeadsetStateReceiver.java index 29bc1c49f2..5645912d73 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/receiver/HeadsetStateReceiver.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/receiver/HeadsetStateReceiver.java @@ -13,8 +13,6 @@ public class HeadsetStateReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); - - Log.d("[HEADSET] " , "BR - Wired headset on:" + audioManager.isWiredHeadsetOn()); notifyHeadsetOn(audioManager.isWiredHeadsetOn()); } } diff --git a/interface/src/AndroidHelper.cpp b/interface/src/AndroidHelper.cpp index 35bf094591..400085a62a 100644 --- a/interface/src/AndroidHelper.cpp +++ b/interface/src/AndroidHelper.cpp @@ -63,13 +63,7 @@ void AndroidHelper::notifyHeadsetOn(bool pluggedIn) { #if defined (Q_OS_ANDROID) auto audioClient = DependencyManager::get(); if (audioClient) { - QAudioDeviceInfo activeDev = audioClient->getActiveAudioDevice(QAudio::AudioInput); - Setting::Handle enableAEC(QStringList() << ANDROID_SETTINGS_GROUP << SETTING_AEC_KEY, false); - if ((pluggedIn || !enableAEC.get()) && !activeDev.isNull() && activeDev.deviceName() != VOICE_RECOGNITION) { - QMetaObject::invokeMethod(audioClient.get(), "switchAudioDevice", Q_ARG(QAudio::Mode, QAudio::AudioInput), Q_ARG(QString, VOICE_RECOGNITION)); - } else if ( (!pluggedIn && enableAEC.get()) && !activeDev.isNull() && activeDev.deviceName() != VOICE_COMMUNICATION) { - QMetaObject::invokeMethod(audioClient.get(), "switchAudioDevice", Q_ARG(QAudio::Mode, QAudio::AudioInput), Q_ARG(QString, VOICE_COMMUNICATION)); - } + QMetaObject::invokeMethod(audioClient.data(), "setHeadsetPluggedIn", Q_ARG(bool, pluggedIn)); } #endif } diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index e23b8ac3cd..f0ba0307cc 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -209,6 +209,7 @@ AudioClient::AudioClient() : _positionGetter(DEFAULT_POSITION_GETTER), #if defined(Q_OS_ANDROID) _checkInputTimer(this), + _isHeadsetPluggedIn(false), #endif _orientationGetter(DEFAULT_ORIENTATION_GETTER) { // avoid putting a lock in the device callback @@ -450,12 +451,14 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) { #if defined (Q_OS_ANDROID) if (mode == QAudio::AudioInput) { - Setting::Handle enableAEC(QStringList() << ANDROID_SETTINGS_GROUP << SETTING_AEC_KEY, false); + Setting::Handle enableAEC(SETTING_AEC_KEY, false); bool aecEnabled = enableAEC.get(); + auto audioClient = DependencyManager::get(); + bool headsetOn = audioClient? audioClient->isHeadsetPluggedIn() : false ; auto inputDevices = QAudioDeviceInfo::availableDevices(QAudio::AudioInput); for (auto inputDevice : inputDevices) { - if ((aecEnabled && inputDevice.deviceName() == VOICE_COMMUNICATION) || - (!aecEnabled && inputDevice.deviceName() == VOICE_RECOGNITION)) { + if (((headsetOn || !aecEnabled) && inputDevice.deviceName() == VOICE_RECOGNITION) || + ((!headsetOn && aecEnabled) && inputDevice.deviceName() == VOICE_COMMUNICATION)) { return inputDevice; } } @@ -1632,6 +1635,29 @@ void AudioClient::checkInputTimeout() { #endif } +void AudioClient::setHeadsetPluggedIn(bool pluggedIn) { +#if defined(Q_OS_ANDROID) + if (pluggedIn == !_isHeadsetPluggedIn && !_inputDeviceInfo.isNull()) { + QAndroidJniObject brand = QAndroidJniObject::getStaticObjectField("android/os/Build", "BRAND"); + // some samsung phones needs more time to shutdown the previous input device + if (brand.toString().contains("samsung", Qt::CaseInsensitive)) { + switchInputToAudioDevice(QAudioDeviceInfo(), true); + QThread::msleep(200); + } + + Setting::Handle enableAEC(SETTING_AEC_KEY, false); + bool aecEnabled = enableAEC.get(); + + if ((pluggedIn || !aecEnabled) && _inputDeviceInfo.deviceName() != VOICE_RECOGNITION) { + switchAudioDevice(QAudio::AudioInput, VOICE_RECOGNITION); + } else if (!pluggedIn && aecEnabled && _inputDeviceInfo.deviceName() != VOICE_COMMUNICATION) { + switchAudioDevice(QAudio::AudioInput, VOICE_COMMUNICATION); + } + } + _isHeadsetPluggedIn = pluggedIn; +#endif +} + void AudioClient::outputNotify() { int recentUnfulfilled = _audioOutputIODevice.getRecentUnfulfilledReads(); if (recentUnfulfilled > 0) { diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index fa7ac40a16..b1ccb496b6 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -68,8 +68,7 @@ #define VOICE_RECOGNITION "voicerecognition" #define VOICE_COMMUNICATION "voicecommunication" -#define ANDROID_SETTINGS_GROUP "Android" -#define SETTING_AEC_KEY "aec" +#define SETTING_AEC_KEY "Android/aec" #endif class QAudioInput; @@ -176,6 +175,10 @@ public: static QString getWinDeviceName(wchar_t* guid); #endif +#if defined(Q_OS_ANDROID) + bool isHeadsetPluggedIn() { return _isHeadsetPluggedIn; } +#endif + public slots: void start(); void stop(); @@ -224,6 +227,9 @@ public slots: bool switchAudioDevice(QAudio::Mode mode, const QAudioDeviceInfo& deviceInfo = QAudioDeviceInfo()); bool switchAudioDevice(QAudio::Mode mode, const QString& deviceName); + // Qt opensles plugin is not able to detect when the headset is plugged in + void setHeadsetPluggedIn(bool pluggedIn); + float getInputVolume() const { return (_audioInput) ? (float)_audioInput->volume() : 0.0f; } void setInputVolume(float volume, bool emitSignal = true); void setReverb(bool reverb); @@ -285,6 +291,7 @@ private: #ifdef Q_OS_ANDROID QTimer _checkInputTimer; long _inputReadsSinceLastCheck = 0l; + bool _isHeadsetPluggedIn; #endif class Gate { From 27c7592d986af1424c4e85a7df5d44e2a50b2d7f Mon Sep 17 00:00:00 2001 From: Gabriel Calero Date: Thu, 13 Sep 2018 14:30:42 -0300 Subject: [PATCH 3/9] Patch qt 5.11.1 to support voice_communication opensl es preset --- android/build.gradle | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index a6de0d469c..a5d40b9b43 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -73,16 +73,16 @@ def baseUrl = 'https://hifi-public.s3.amazonaws.com/dependencies/android/' def breakpadDumpSymsDir = new File("${appDir}/build/tmp/breakpadDumpSyms") def qtFile='qt-5.11.1_linux_armv8-libcpp_openssl.tgz' -def qtChecksum='f312c47cd8b8dbca824c32af4eec5e66' -def qtVersionId='nyCGcb91S4QbYeJhUkawO5x1lrLdSNB_' +def qtChecksum='bf9e734d9c4e77c4807a38c93515e25c' +def qtVersionId='Qt3rvI0O7l5gLkacia2vA6KAchTgFkFf' if (Os.isFamily(Os.FAMILY_MAC)) { - qtFile = 'qt-5.11.1_osx_armv8-libcpp_openssl.tgz' - qtChecksum='a0c8b394aec5b0fcd46714ca3a53278a' - qtVersionId='QNa.lwNJaPc0eGuIL.xZ8ebeTuLL7rh8' + qtFile = 'qt-5.11.1_osx_armv8-libcpp_openssl_patched.tgz' + qtChecksum='5f12fd4fb25efe648c2fd161fd44998a' + qtVersionId='XXJ7ii1.xYzgFdWPnU.8mXnCj.q6y0Q5' } else if (Os.isFamily(Os.FAMILY_WINDOWS)) { - qtFile = 'qt-5.11.1_win_armv8-libcpp_openssl.tgz' - qtChecksum='d80aed4233ce9e222aae8376e7a94bf9' - qtVersionId='iDVXu0i3WEXRFIxQCtzcJ2XuKrE8RIqB' + qtFile = 'qt-5.11.1_win_armv8-libcpp_openssl_patched.tgz' + qtChecksum='0582191cc55431aa4f660848a542883e' + qtVersionId='JfWM0P_Mz5Qp0LwpzhrsRwN3fqlLSFeT' } def packages = [ From ec2c0226a5aadcf0ab762d463b10a30bc10c05ca Mon Sep 17 00:00:00 2001 From: Gabriel Calero Date: Thu, 13 Sep 2018 16:42:12 -0300 Subject: [PATCH 4/9] Replace qt build version (osx) --- android/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index a5d40b9b43..8a3f4d75b9 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -77,8 +77,8 @@ def qtChecksum='bf9e734d9c4e77c4807a38c93515e25c' def qtVersionId='Qt3rvI0O7l5gLkacia2vA6KAchTgFkFf' if (Os.isFamily(Os.FAMILY_MAC)) { qtFile = 'qt-5.11.1_osx_armv8-libcpp_openssl_patched.tgz' - qtChecksum='5f12fd4fb25efe648c2fd161fd44998a' - qtVersionId='XXJ7ii1.xYzgFdWPnU.8mXnCj.q6y0Q5' + qtChecksum='c83cc477c08a892e00c71764dca051a0' + qtVersionId='QNa.lwNJaPc0eGuIL.xZ8ebeTuLL7rh8' } else if (Os.isFamily(Os.FAMILY_WINDOWS)) { qtFile = 'qt-5.11.1_win_armv8-libcpp_openssl_patched.tgz' qtChecksum='0582191cc55431aa4f660848a542883e' From 1215ddbe88a6669173c7a635be0f0b8514e36fce Mon Sep 17 00:00:00 2001 From: Gabriel Calero Date: Thu, 13 Sep 2018 18:50:46 -0300 Subject: [PATCH 5/9] Remove debug files from linux qt build. Remove extra space --- android/build.gradle | 6 +++--- libraries/audio-client/src/AudioClient.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index 8a3f4d75b9..14f0779e29 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -72,9 +72,9 @@ def jniFolder = new File(appDir, 'src/main/jniLibs/arm64-v8a') def baseUrl = 'https://hifi-public.s3.amazonaws.com/dependencies/android/' def breakpadDumpSymsDir = new File("${appDir}/build/tmp/breakpadDumpSyms") -def qtFile='qt-5.11.1_linux_armv8-libcpp_openssl.tgz' -def qtChecksum='bf9e734d9c4e77c4807a38c93515e25c' -def qtVersionId='Qt3rvI0O7l5gLkacia2vA6KAchTgFkFf' +def qtFile='qt-5.11.1_linux_armv8-libcpp_openssl_patched.tgz' +def qtChecksum='aa449d4bfa963f3bc9a9dfe558ba29df' +def qtVersionId='3S97HBM5G5Xw9EfE52sikmgdN3t6C2MN' if (Os.isFamily(Os.FAMILY_MAC)) { qtFile = 'qt-5.11.1_osx_armv8-libcpp_openssl_patched.tgz' qtChecksum='c83cc477c08a892e00c71764dca051a0' diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index f0ba0307cc..814cbf87e4 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -454,7 +454,7 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) { Setting::Handle enableAEC(SETTING_AEC_KEY, false); bool aecEnabled = enableAEC.get(); auto audioClient = DependencyManager::get(); - bool headsetOn = audioClient? audioClient->isHeadsetPluggedIn() : false ; + bool headsetOn = audioClient? audioClient->isHeadsetPluggedIn() : false; auto inputDevices = QAudioDeviceInfo::availableDevices(QAudio::AudioInput); for (auto inputDevice : inputDevices) { if (((headsetOn || !aecEnabled) && inputDevice.deviceName() == VOICE_RECOGNITION) || From 5795ef3c6323c9dbfd5782b486d3687d3016e322 Mon Sep 17 00:00:00 2001 From: Gabriel Calero Date: Thu, 13 Sep 2018 19:43:28 -0300 Subject: [PATCH 6/9] Fix qt build (osx) file version --- android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/build.gradle b/android/build.gradle index 14f0779e29..aa7aa399b2 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -78,7 +78,7 @@ def qtVersionId='3S97HBM5G5Xw9EfE52sikmgdN3t6C2MN' if (Os.isFamily(Os.FAMILY_MAC)) { qtFile = 'qt-5.11.1_osx_armv8-libcpp_openssl_patched.tgz' qtChecksum='c83cc477c08a892e00c71764dca051a0' - qtVersionId='QNa.lwNJaPc0eGuIL.xZ8ebeTuLL7rh8' + qtVersionId='OxBD7iKINv1HbyOXmAmDrBb8AF3N.Kup' } else if (Os.isFamily(Os.FAMILY_WINDOWS)) { qtFile = 'qt-5.11.1_win_armv8-libcpp_openssl_patched.tgz' qtChecksum='0582191cc55431aa4f660848a542883e' From 1a44054c494af587aa617e93eeb06d7e2e3bb343 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sat, 15 Sep 2018 09:15:43 -0700 Subject: [PATCH 7/9] attempt fix for fb-17131 --- libraries/entities/src/EntityScriptingInterface.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index d9924cb9fd..8f0fde5c9a 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -132,8 +132,8 @@ EntityItemProperties convertPropertiesToScriptSemantics(const EntityItemProperti EntityItemProperties scriptSideProperties = entitySideProperties; scriptSideProperties.setLocalPosition(entitySideProperties.getPosition()); scriptSideProperties.setLocalRotation(entitySideProperties.getRotation()); - scriptSideProperties.setLocalVelocity(entitySideProperties.getLocalVelocity()); - scriptSideProperties.setLocalAngularVelocity(entitySideProperties.getLocalAngularVelocity()); + scriptSideProperties.setLocalVelocity(entitySideProperties.getVelocity()); + scriptSideProperties.setLocalAngularVelocity(entitySideProperties.getAngularVelocity()); scriptSideProperties.setLocalDimensions(entitySideProperties.getDimensions()); bool success; @@ -181,8 +181,6 @@ EntityItemProperties convertPropertiesFromScriptSemantics(const EntityItemProper EntityItemProperties entitySideProperties = scriptSideProperties; bool success; - // TODO -- handle velocity and angularVelocity - if (scriptSideProperties.localPositionChanged()) { entitySideProperties.setPosition(scriptSideProperties.getLocalPosition()); } else if (scriptSideProperties.positionChanged()) { From f86075b2dda4303f706b65ebc9fb5f467615c302 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sat, 15 Sep 2018 09:21:12 -0700 Subject: [PATCH 8/9] Revert "Fix for angularVelocity zeroing out on duplication or undo" This reverts commit 9c96a10f6cf0c60eb39cab3ea3e7460d0dcda0a5. --- scripts/system/libraries/entitySelectionTool.js | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index 5bca58b1ac..1fc0f0611c 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -29,17 +29,6 @@ Script.include([ SelectionManager = (function() { var that = {}; - /** - * @description Removes known to be broken properties from a properties object - * @param properties - * @return properties - */ - var fixRemoveBrokenProperties = function (properties) { - // Reason: Entity property is always set to 0,0,0 which causes it to override angularVelocity (see MS17131) - delete properties.localAngularVelocity; - return properties; - } - // FUNCTION: SUBSCRIBE TO UPDATE MESSAGES function subscribeToUpdateMessages() { Messages.subscribe("entityToolUpdates"); @@ -130,7 +119,7 @@ SelectionManager = (function() { that.savedProperties = {}; for (var i = 0; i < that.selections.length; i++) { var entityID = that.selections[i]; - that.savedProperties[entityID] = fixRemoveBrokenProperties(Entities.getEntityProperties(entityID)); + that.savedProperties[entityID] = Entities.getEntityProperties(entityID); } }; @@ -258,7 +247,7 @@ SelectionManager = (function() { var originalEntityID = entitiesToDuplicate[i]; var properties = that.savedProperties[originalEntityID]; if (properties === undefined) { - properties = fixRemoveBrokenProperties(Entities.getEntityProperties(originalEntityID)); + properties = Entities.getEntityProperties(originalEntityID); } if (!properties.locked && (!properties.clientOnly || properties.owningAvatarID === MyAvatar.sessionUUID)) { if (nonDynamicEntityIsBeingGrabbedByAvatar(properties)) { From 86e502637e581d6cdd9e5e6cc48cb37215891dd7 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sun, 16 Sep 2018 12:18:48 -0700 Subject: [PATCH 9/9] Make Settings thread safe --- interface/src/avatar/MyAvatar.cpp | 211 +++++++++++------------- interface/src/avatar/MyAvatar.h | 18 ++ libraries/shared/src/SettingManager.cpp | 105 ++++++++++-- libraries/shared/src/SettingManager.h | 21 ++- 4 files changed, 224 insertions(+), 131 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index c47cfdb383..df7ec93b6a 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -91,6 +91,8 @@ const float MIN_SCALE_CHANGED_DELTA = 0.001f; const int MODE_READINGS_RING_BUFFER_SIZE = 500; const float CENTIMETERS_PER_METER = 100.0f; +const QString AVATAR_SETTINGS_GROUP_NAME { "Avatar" }; + MyAvatar::MyAvatar(QThread* thread) : Avatar(thread), _yawSpeed(YAW_SPEED_DEFAULT), @@ -118,7 +120,22 @@ MyAvatar::MyAvatar(QThread* thread) : _goToOrientation(), _prevShouldDrawHead(true), _audioListenerMode(FROM_HEAD), - _hmdAtRestDetector(glm::vec3(0), glm::quat()) + _hmdAtRestDetector(glm::vec3(0), glm::quat()), + _dominantHandSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "dominantHand", DOMINANT_RIGHT_HAND), + _headPitchSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "", 0.0f), + _scaleSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "scale", _targetScale), + _yawSpeedSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "yawSpeed", _yawSpeed), + _pitchSpeedSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "pitchSpeed", _pitchSpeed), + _fullAvatarURLSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "fullAvatarURL", + AvatarData::defaultFullAvatarModelUrl()), + _fullAvatarModelNameSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "fullAvatarModelName", _fullAvatarModelName), + _animGraphURLSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "animGraphURL", QUrl("")), + _displayNameSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "displayName", ""), + _collisionSoundURLSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "collisionSoundURL", QUrl(_collisionSoundURL)), + _useSnapTurnSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "useSnapTurn", _useSnapTurn), + _userHeightSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "userHeight", DEFAULT_AVATAR_HEIGHT), + _flyingHMDSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "flyingHMD", _flyingPrefHMD), + _avatarEntityCountSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "avatarEntityData" << "size", _flyingPrefHMD) { _clientTraitsHandler = std::unique_ptr(new ClientTraitsHandler(this)); @@ -1135,88 +1152,80 @@ void MyAvatar::restoreRoleAnimation(const QString& role) { } void MyAvatar::saveAvatarUrl() { - Settings settings; - settings.beginGroup("Avatar"); - if (qApp->getSaveAvatarOverrideUrl() || !qApp->getAvatarOverrideUrl().isValid() ) { - settings.setValue("fullAvatarURL", - _fullAvatarURLFromPreferences == AvatarData::defaultFullAvatarModelUrl() ? - "" : - _fullAvatarURLFromPreferences.toString()); + if (qApp->getSaveAvatarOverrideUrl() || !qApp->getAvatarOverrideUrl().isValid()) { + _fullAvatarURLSetting.set(_fullAvatarURLFromPreferences == AvatarData::defaultFullAvatarModelUrl() ? + "" : + _fullAvatarURLFromPreferences.toString()); + } +} + +void MyAvatar::resizeAvatarEntitySettingHandles(unsigned int avatarEntityIndex) { + // The original Settings interface saved avatar-entity array data like this: + // Avatar/avatarEntityData/size: 5 + // Avatar/avatarEntityData/1/id: ... + // Avatar/avatarEntityData/1/properties: ... + // ... + // Avatar/avatarEntityData/5/id: ... + // Avatar/avatarEntityData/5/properties: ... + // + // Create Setting::Handles to mimic this. + + while (_avatarEntityIDSettings.size() <= avatarEntityIndex) { + Setting::Handle idHandle(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "avatarEntityData" + << QString::number(avatarEntityIndex + 1) << "id", QUuid()); + _avatarEntityIDSettings.push_back(idHandle); + Setting::Handle dataHandle(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "avatarEntityData" + << QString::number(avatarEntityIndex + 1) << "properties", QByteArray()); + _avatarEntityDataSettings.push_back(dataHandle); } - settings.endGroup(); } void MyAvatar::saveData() { - Settings settings; - settings.beginGroup("Avatar"); - - settings.setValue("dominantHand", _dominantHand); - settings.setValue("headPitch", getHead()->getBasePitch()); - - settings.setValue("scale", _targetScale); - - settings.setValue("yawSpeed", _yawSpeed); - settings.setValue("pitchSpeed", _pitchSpeed); + _dominantHandSetting.set(_dominantHand); + _headPitchSetting.set(getHead()->getBasePitch()); + _scaleSetting.set(_targetScale); + _yawSpeedSetting.set(_yawSpeed); + _pitchSpeedSetting.set(_pitchSpeed); // only save the fullAvatarURL if it has not been overwritten on command line // (so the overrideURL is not valid), or it was overridden _and_ we specified // --replaceAvatarURL (so _saveAvatarOverrideUrl is true) if (qApp->getSaveAvatarOverrideUrl() || !qApp->getAvatarOverrideUrl().isValid() ) { - settings.setValue("fullAvatarURL", - _fullAvatarURLFromPreferences == AvatarData::defaultFullAvatarModelUrl() ? - "" : - _fullAvatarURLFromPreferences.toString()); + _fullAvatarURLSetting.set(_fullAvatarURLFromPreferences == AvatarData::defaultFullAvatarModelUrl() ? + "" : + _fullAvatarURLFromPreferences.toString()); } - settings.setValue("fullAvatarModelName", _fullAvatarModelName); - + _fullAvatarModelNameSetting.set(_fullAvatarModelName); QUrl animGraphUrl = _prefOverrideAnimGraphUrl.get(); - settings.setValue("animGraphURL", animGraphUrl); + _animGraphURLSetting.set(animGraphUrl); + _displayNameSetting.set(_displayName); + _collisionSoundURLSetting.set(_collisionSoundURL); + _useSnapTurnSetting.set(_useSnapTurn); + _userHeightSetting.set(getUserHeight()); + _flyingHMDSetting.set(getFlyingHMDPref()); - settings.beginWriteArray("attachmentData"); - for (int i = 0; i < _attachmentData.size(); i++) { - settings.setArrayIndex(i); - const AttachmentData& attachment = _attachmentData.at(i); - settings.setValue("modelURL", attachment.modelURL); - settings.setValue("jointName", attachment.jointName); - settings.setValue("translation_x", attachment.translation.x); - settings.setValue("translation_y", attachment.translation.y); - settings.setValue("translation_z", attachment.translation.z); - glm::vec3 eulers = safeEulerAngles(attachment.rotation); - settings.setValue("rotation_x", eulers.x); - settings.setValue("rotation_y", eulers.y); - settings.setValue("rotation_z", eulers.z); - settings.setValue("scale", attachment.scale); - settings.setValue("isSoft", attachment.isSoft); - } - settings.endArray(); - - settings.remove("avatarEntityData"); - settings.beginWriteArray("avatarEntityData"); - int avatarEntityIndex = 0; auto hmdInterface = DependencyManager::get(); _avatarEntitiesLock.withReadLock([&] { - for (auto entityID : _avatarEntityData.keys()) { - if (hmdInterface->getCurrentTabletFrameID() == entityID) { - // don't persist the tablet between domains / sessions - continue; - } + QList avatarEntityIDs = _avatarEntityData.keys(); + unsigned int avatarEntityCount = avatarEntityIDs.size(); + unsigned int previousAvatarEntityCount = _avatarEntityCountSetting.get(0); + resizeAvatarEntitySettingHandles(std::max(avatarEntityCount, previousAvatarEntityCount)); + _avatarEntityCountSetting.set(avatarEntityCount); - settings.setArrayIndex(avatarEntityIndex); - settings.setValue("id", entityID); - settings.setValue("properties", _avatarEntityData.value(entityID)); + unsigned int avatarEntityIndex = 0; + for (auto entityID : avatarEntityIDs) { + _avatarEntityIDSettings[avatarEntityIndex].set(entityID); + _avatarEntityDataSettings[avatarEntityIndex].set(_avatarEntityData.value(entityID)); avatarEntityIndex++; } + + // clean up any left-over (due to the list shrinking) slots + for (; avatarEntityIndex < previousAvatarEntityCount; avatarEntityIndex++) { + _avatarEntityIDSettings[avatarEntityIndex].remove(); + _avatarEntityDataSettings[avatarEntityIndex].remove(); + } }); - settings.endArray(); - - settings.setValue("displayName", _displayName); - settings.setValue("collisionSoundURL", _collisionSoundURL); - settings.setValue("useSnapTurn", _useSnapTurn); - settings.setValue("userHeight", getUserHeight()); - settings.setValue("flyingHMD", getFlyingHMDPref()); - - settings.endGroup(); } float loadSetting(Settings& settings, const QString& name, float defaultValue) { @@ -1314,66 +1323,36 @@ void MyAvatar::setEnableInverseKinematics(bool isEnabled) { } void MyAvatar::loadData() { - Settings settings; - settings.beginGroup("Avatar"); + getHead()->setBasePitch(_headPitchSetting.get()); - getHead()->setBasePitch(loadSetting(settings, "headPitch", 0.0f)); + _yawSpeed = _yawSpeedSetting.get(_yawSpeed); + _pitchSpeed = _pitchSpeedSetting.get(_pitchSpeed); - _yawSpeed = loadSetting(settings, "yawSpeed", _yawSpeed); - _pitchSpeed = loadSetting(settings, "pitchSpeed", _pitchSpeed); - - _prefOverrideAnimGraphUrl.set(QUrl(settings.value("animGraphURL", "").toString())); - _fullAvatarURLFromPreferences = settings.value("fullAvatarURL", AvatarData::defaultFullAvatarModelUrl()).toUrl(); - _fullAvatarModelName = settings.value("fullAvatarModelName", DEFAULT_FULL_AVATAR_MODEL_NAME).toString(); + _prefOverrideAnimGraphUrl.set(_prefOverrideAnimGraphUrl.get().toString()); + _fullAvatarURLFromPreferences = _fullAvatarURLSetting.get(QUrl(AvatarData::defaultFullAvatarModelUrl())); + _fullAvatarModelName = _fullAvatarModelNameSetting.get(DEFAULT_FULL_AVATAR_MODEL_NAME).toString(); useFullAvatarURL(_fullAvatarURLFromPreferences, _fullAvatarModelName); - int attachmentCount = settings.beginReadArray("attachmentData"); - for (int i = 0; i < attachmentCount; i++) { - settings.setArrayIndex(i); - AttachmentData attachment; - attachment.modelURL = settings.value("modelURL").toUrl(); - attachment.jointName = settings.value("jointName").toString(); - attachment.translation.x = loadSetting(settings, "translation_x", 0.0f); - attachment.translation.y = loadSetting(settings, "translation_y", 0.0f); - attachment.translation.z = loadSetting(settings, "translation_z", 0.0f); - glm::vec3 eulers; - eulers.x = loadSetting(settings, "rotation_x", 0.0f); - eulers.y = loadSetting(settings, "rotation_y", 0.0f); - eulers.z = loadSetting(settings, "rotation_z", 0.0f); - attachment.rotation = glm::quat(eulers); - attachment.scale = loadSetting(settings, "scale", 1.0f); - attachment.isSoft = settings.value("isSoft").toBool(); - // old attachments are stored and loaded/converted later when rig is ready - _oldAttachmentData.append(attachment); - } - settings.endArray(); - - int avatarEntityCount = settings.beginReadArray("avatarEntityData"); + int avatarEntityCount = _avatarEntityCountSetting.get(0); for (int i = 0; i < avatarEntityCount; i++) { - settings.setArrayIndex(i); - QUuid entityID = settings.value("id").toUuid(); + resizeAvatarEntitySettingHandles(i); // QUuid entityID = QUuid::createUuid(); // generate a new ID - QByteArray properties = settings.value("properties").toByteArray(); + QUuid entityID = _avatarEntityIDSettings[i].get(QUuid()); + QByteArray properties = _avatarEntityDataSettings[i].get(); updateAvatarEntity(entityID, properties); } - settings.endArray(); - if (avatarEntityCount == 0) { - // HACK: manually remove empty 'avatarEntityData' else legacy data may persist in settings file - settings.remove("avatarEntityData"); - } // Flying preferences must be loaded before calling setFlyingEnabled() Setting::Handle firstRunVal { Settings::firstRun, true }; - setFlyingHMDPref(firstRunVal.get() ? false : settings.value("flyingHMD").toBool()); + setFlyingHMDPref(firstRunVal.get() ? false : _flyingHMDSetting.get()); setFlyingEnabled(getFlyingEnabled()); - setDisplayName(settings.value("displayName").toString()); - setCollisionSoundURL(settings.value("collisionSoundURL", DEFAULT_AVATAR_COLLISION_SOUND_URL).toString()); - setSnapTurn(settings.value("useSnapTurn", _useSnapTurn).toBool()); - setDominantHand(settings.value("dominantHand", _dominantHand).toString().toLower()); - setUserHeight(settings.value("userHeight", DEFAULT_AVATAR_HEIGHT).toDouble()); - settings.endGroup(); + setDisplayName(_displayNameSetting.get()); + setCollisionSoundURL(_collisionSoundURLSetting.get(QUrl(DEFAULT_AVATAR_COLLISION_SOUND_URL)).toString()); + setSnapTurn(_useSnapTurnSetting.get()); + setDominantHand(_dominantHandSetting.get(DOMINANT_RIGHT_HAND).toLower()); + setUserHeight(_userHeightSetting.get(DEFAULT_AVATAR_HEIGHT)); setEnableMeshVisible(Menu::getInstance()->isOptionChecked(MenuOption::MeshVisible)); _follow.setToggleHipsFollowing (Menu::getInstance()->isOptionChecked(MenuOption::ToggleHipsFollowing)); @@ -1442,6 +1421,7 @@ AttachmentData MyAvatar::loadAttachmentData(const QUrl& modelURL, const QString& return attachment; } + int MyAvatar::parseDataFromBuffer(const QByteArray& buffer) { qCDebug(interfaceapp) << "Error: ignoring update packet for MyAvatar" << " packetLength = " << buffer.size(); @@ -2899,10 +2879,7 @@ void MyAvatar::restrictScaleFromDomainSettings(const QJsonObject& domainSettings } // Set avatar current scale - Settings settings; - settings.beginGroup("Avatar"); - _targetScale = loadSetting(settings, "scale", 1.0f); - + _targetScale = _scaleSetting.get(); // clamp the desired _targetScale by the domain limits NOW, don't try to gracefully animate. Because // this might cause our avatar to become embedded in the terrain. _targetScale = getDomainLimitedScale(); @@ -2914,7 +2891,6 @@ void MyAvatar::restrictScaleFromDomainSettings(const QJsonObject& domainSettings setModelScale(_targetScale); rebuildCollisionShape(); - settings.endGroup(); _haveReceivedHeightLimitsFromDomain = true; } @@ -2925,10 +2901,7 @@ void MyAvatar::leaveDomain() { } void MyAvatar::saveAvatarScale() { - Settings settings; - settings.beginGroup("Avatar"); - settings.setValue("scale", _targetScale); - settings.endGroup(); + _scaleSetting.set(_targetScale); } void MyAvatar::clearScaleRestriction() { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 139f1f6ea2..1dc0b3cd40 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -550,6 +550,7 @@ public: float getHMDRollControlRate() const { return _hmdRollControlRate; } // get/set avatar data + void resizeAvatarEntitySettingHandles(unsigned int avatarEntityIndex); void saveData(); void loadData(); @@ -1806,6 +1807,23 @@ private: bool _haveReceivedHeightLimitsFromDomain { false }; int _disableHandTouchCount { 0 }; + + Setting::Handle _dominantHandSetting; + Setting::Handle _headPitchSetting; + Setting::Handle _scaleSetting; + Setting::Handle _yawSpeedSetting; + Setting::Handle _pitchSpeedSetting; + Setting::Handle _fullAvatarURLSetting; + Setting::Handle _fullAvatarModelNameSetting; + Setting::Handle _animGraphURLSetting; + Setting::Handle _displayNameSetting; + Setting::Handle _collisionSoundURLSetting; + Setting::Handle _useSnapTurnSetting; + Setting::Handle _userHeightSetting; + Setting::Handle _flyingHMDSetting; + Setting::Handle _avatarEntityCountSetting; + std::vector> _avatarEntityIDSettings; + std::vector> _avatarEntityDataSettings; }; QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode); diff --git a/libraries/shared/src/SettingManager.cpp b/libraries/shared/src/SettingManager.cpp index 2e0850255a..e5920b785e 100644 --- a/libraries/shared/src/SettingManager.cpp +++ b/libraries/shared/src/SettingManager.cpp @@ -52,7 +52,7 @@ namespace Setting { if (_pendingChanges.contains(key) && _pendingChanges[key] != UNSET_VALUE) { loadedValue = _pendingChanges[key]; } else { - loadedValue = value(key); + loadedValue = _qSettings.value(key); } if (loadedValue.isValid()) { handle->setVariant(loadedValue); @@ -92,32 +92,115 @@ namespace Setting { } void Manager::saveAll() { - bool forceSync = false; withWriteLock([&] { + bool forceSync = false; for (auto key : _pendingChanges.keys()) { auto newValue = _pendingChanges[key]; - auto savedValue = value(key, UNSET_VALUE); + auto savedValue = _qSettings.value(key, UNSET_VALUE); if (newValue == savedValue) { continue; } + forceSync = true; if (newValue == UNSET_VALUE || !newValue.isValid()) { - forceSync = true; - remove(key); + _qSettings.remove(key); } else { - forceSync = true; - setValue(key, newValue); + _qSettings.setValue(key, newValue); } } _pendingChanges.clear(); - }); - if (forceSync) { - sync(); - } + if (forceSync) { + _qSettings.sync(); + } + }); // Restart timer if (_saveTimer) { _saveTimer->start(); } } + + QString Manager::fileName() const { + return resultWithReadLock([&] { + return _qSettings.fileName(); + }); + } + + void Manager::remove(const QString &key) { + withWriteLock([&] { + _qSettings.remove(key); + }); + } + + QStringList Manager::childGroups() const { + return resultWithReadLock([&] { + return _qSettings.childGroups(); + }); + } + + QStringList Manager::childKeys() const { + return resultWithReadLock([&] { + return _qSettings.childKeys(); + }); + } + + QStringList Manager::allKeys() const { + return resultWithReadLock([&] { + return _qSettings.allKeys(); + }); + } + + bool Manager::contains(const QString &key) const { + return resultWithReadLock([&] { + return _qSettings.contains(key); + }); + } + + int Manager::beginReadArray(const QString &prefix) { + return resultWithReadLock([&] { + return _qSettings.beginReadArray(prefix); + }); + } + + void Manager::beginGroup(const QString &prefix) { + withWriteLock([&] { + _qSettings.beginGroup(prefix); + }); + } + + void Manager::beginWriteArray(const QString &prefix, int size) { + withWriteLock([&] { + _qSettings.beginWriteArray(prefix, size); + }); + } + + void Manager::endArray() { + withWriteLock([&] { + _qSettings.endArray(); + }); + } + + void Manager::endGroup() { + withWriteLock([&] { + _qSettings.endGroup(); + }); + } + + void Manager::setArrayIndex(int i) { + withWriteLock([&] { + _qSettings.setArrayIndex(i); + }); + } + + void Manager::setValue(const QString &key, const QVariant &value) { + withWriteLock([&] { + _qSettings.setValue(key, value); + }); + } + + QVariant Manager::value(const QString &key, const QVariant &defaultValue) const { + return resultWithReadLock([&] { + return _qSettings.value(key, defaultValue); + }); + } } diff --git a/libraries/shared/src/SettingManager.h b/libraries/shared/src/SettingManager.h index 6696a1ecf4..49f3bece4b 100644 --- a/libraries/shared/src/SettingManager.h +++ b/libraries/shared/src/SettingManager.h @@ -23,12 +23,28 @@ namespace Setting { class Interface; - class Manager : public QSettings, public ReadWriteLockable, public Dependency { + class Manager : public QObject, public ReadWriteLockable, public Dependency { Q_OBJECT public: void customDeleter() override; + // thread-safe proxies into QSettings + QString fileName() const; + void remove(const QString &key); + QStringList childGroups() const; + QStringList childKeys() const; + QStringList allKeys() const; + bool contains(const QString &key) const; + int beginReadArray(const QString &prefix); + void beginGroup(const QString &prefix); + void beginWriteArray(const QString &prefix, int size = -1); + void endArray(); + void endGroup(); + void setArrayIndex(int i); + void setValue(const QString &key, const QVariant &value); + QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const; + protected: ~Manager(); void registerHandle(Interface* handle); @@ -52,6 +68,9 @@ namespace Setting { friend class Interface; friend void cleanupSettingsSaveThread(); friend void setupSettingsSaveThread(); + + + QSettings _qSettings; }; }