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..5645912d73
--- /dev/null
+++ b/android/app/src/main/java/io/highfidelity/hifiinterface/receiver/HeadsetStateReceiver.java
@@ -0,0 +1,18 @@
+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);
+ 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/android/build.gradle b/android/build.gradle
index a6de0d469c..aa7aa399b2 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -72,17 +72,17 @@ 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='f312c47cd8b8dbca824c32af4eec5e66'
-def qtVersionId='nyCGcb91S4QbYeJhUkawO5x1lrLdSNB_'
+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.tgz'
- qtChecksum='a0c8b394aec5b0fcd46714ca3a53278a'
- qtVersionId='QNa.lwNJaPc0eGuIL.xZ8ebeTuLL7rh8'
+ qtFile = 'qt-5.11.1_osx_armv8-libcpp_openssl_patched.tgz'
+ qtChecksum='c83cc477c08a892e00c71764dca051a0'
+ qtVersionId='OxBD7iKINv1HbyOXmAmDrBb8AF3N.Kup'
} 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 = [
diff --git a/interface/resources/qml/AnimStats.qml b/interface/resources/qml/AnimStats.qml
index 35ed3799a6..b1900cf0a7 100644
--- a/interface/resources/qml/AnimStats.qml
+++ b/interface/resources/qml/AnimStats.qml
@@ -48,35 +48,11 @@ Item {
spacing: 4; x: 4; y: 4;
StatText {
- text: "State Machines:---------------------------------------------------------------------------"
+ text: root.positionText
}
- ListView {
- width: firstCol.width
- height: root.animStateMachines.length * 15
- visible: root.animStateMchines.length > 0;
- model: root.animStateMachines
- delegate: StatText {
- text: {
- return modelData;
- }
- }
- }
- }
- }
-
- Rectangle {
- width: secondCol.width + 8
- height: secondCol.height + 8
- color: root.bgColor;
-
- Column {
- id: secondCol
- spacing: 4; x: 4; y: 4;
-
StatText {
text: "Anim Vars:--------------------------------------------------------------------------------"
}
-
ListView {
width: secondCol.width
height: root.animVars.length * 15
@@ -104,6 +80,36 @@ Item {
}
}
+ Rectangle {
+ width: secondCol.width + 8
+ height: secondCol.height + 8
+ color: root.bgColor;
+
+ Column {
+ id: secondCol
+ spacing: 4; x: 4; y: 4;
+
+ StatText {
+ text: root.rotationText
+ }
+ StatText {
+ text: "State Machines:---------------------------------------------------------------------------"
+ }
+ ListView {
+ width: firstCol.width
+ height: root.animStateMachines.length * 15
+ visible: root.animStateMachines.length > 0;
+ model: root.animStateMachines
+ delegate: StatText {
+ text: {
+ return modelData;
+ }
+ }
+ }
+
+ }
+ }
+
Rectangle {
width: thirdCol.width + 8
height: thirdCol.height + 8
@@ -113,10 +119,12 @@ Item {
id: thirdCol
spacing: 4; x: 4; y: 4;
+ StatText {
+ text: root.velocityText
+ }
StatText {
text: "Alpha Values:--------------------------------------------------------------------------"
}
-
ListView {
width: thirdCol.width
height: root.animAlphaValues.length * 15
diff --git a/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml b/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml
new file mode 100644
index 0000000000..8f391f24c0
--- /dev/null
+++ b/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml
@@ -0,0 +1,290 @@
+//
+// marketplaceItemTester
+// qml/hifi/commerce/marketplaceItemTester
+//
+// Load items not in the marketplace for testing purposes
+//
+// Created by Zach Fox on 2018-09-05
+// Copyright 2018 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+import QtQuick 2.5
+import QtQuick.Controls 1.4
+import QtQuick.Controls.Styles 1.4
+import QtQuick.Dialogs 1.0
+import QtQuick.Layouts 1.1
+import Hifi 1.0 as Hifi
+import "../../../styles-uit" as HifiStylesUit
+import "../../../controls-uit" as HifiControlsUit
+
+
+
+Rectangle {
+ id: root
+
+ property string installedApps
+ property var nextResourceObjectId: 0
+ signal sendToScript(var message)
+
+ HifiStylesUit.HifiConstants { id: hifi }
+ ListModel { id: resourceListModel }
+
+ color: hifi.colors.white
+
+ AnimatedImage {
+ id: spinner;
+ source: "spinner.gif"
+ width: 74;
+ height: width;
+ anchors.verticalCenter: parent.verticalCenter;
+ anchors.horizontalCenter: parent.horizontalCenter;
+ }
+
+ function fromScript(message) {
+ switch (message.method) {
+ case "newResourceObjectInTest":
+ var resourceObject = message.resourceObject;
+ resourceListModel.append(resourceObject);
+ spinner.visible = false;
+ break;
+ case "nextObjectIdInTest":
+ nextResourceObjectId = message.id;
+ spinner.visible = false;
+ break;
+ }
+ }
+
+ function buildResourceObj(resource) {
+ resource = resource.trim();
+ var assetType = (resource.match(/\.app\.json$/) ? "application" :
+ resource.match(/\.fst$/) ? "avatar" :
+ resource.match(/\.json\.gz$/) ? "content set" :
+ resource.match(/\.json$/) ? "entity or wearable" :
+ "unknown");
+ return { "id": nextResourceObjectId++,
+ "resource": resource,
+ "assetType": assetType };
+ }
+
+ function installResourceObj(resourceObj) {
+ if ("application" === resourceObj.assetType) {
+ Commerce.installApp(resourceObj.resource);
+ }
+ }
+
+ function addAllInstalledAppsToList() {
+ var i, apps = Commerce.getInstalledApps().split(","), len = apps.length;
+ for(i = 0; i < len - 1; ++i) {
+ if (i in apps) {
+ resourceListModel.append(buildResourceObj(apps[i]));
+ }
+ }
+ }
+
+ function toUrl(resource) {
+ var httpPattern = /^http/i;
+ return httpPattern.test(resource) ? resource : "file:///" + resource;
+ }
+
+ function rezEntity(resource, entityType) {
+ sendToScript({
+ method: 'tester_rezClicked',
+ itemHref: toUrl(resource),
+ itemType: entityType});
+ }
+
+ ListView {
+ anchors.fill: parent
+ anchors.leftMargin: 12
+ anchors.bottomMargin: 40
+ anchors.rightMargin: 12
+ model: resourceListModel
+ spacing: 5
+ interactive: false
+
+ delegate: RowLayout {
+ anchors.left: parent.left
+ width: parent.width
+ spacing: 5
+
+ property var actions: {
+ "forward": function(resource, assetType){
+ switch(assetType) {
+ case "application":
+ Commerce.openApp(resource);
+ break;
+ case "avatar":
+ MyAvatar.useFullAvatarURL(resource);
+ break;
+ case "content set":
+ urlHandler.handleUrl("hifi://localhost/0,0,0");
+ Commerce.replaceContentSet(toUrl(resource), "");
+ break;
+ case "entity":
+ case "wearable":
+ rezEntity(resource, assetType);
+ break;
+ default:
+ print("Marketplace item tester unsupported assetType " + assetType);
+ }
+ },
+ "trash": function(){
+ if ("application" === assetType) {
+ Commerce.uninstallApp(resource);
+ }
+ sendToScript({
+ method: "tester_deleteResourceObject",
+ objectId: resourceListModel.get(index).id});
+ resourceListModel.remove(index);
+ }
+ }
+
+ Column {
+ Layout.preferredWidth: root.width * .6
+ spacing: 5
+ Text {
+ text: {
+ var match = resource.match(/\/([^/]*)$/);
+ return match ? match[1] : resource;
+ }
+ font.pointSize: 12
+ horizontalAlignment: Text.AlignBottom
+ }
+ Text {
+ text: resource
+ font.pointSize: 8
+ width: root.width * .6
+ horizontalAlignment: Text.AlignBottom
+ wrapMode: Text.WrapAnywhere
+ }
+ }
+
+ ComboBox {
+ id: comboBox
+
+ Layout.preferredWidth: root.width * .2
+
+ model: [
+ "application",
+ "avatar",
+ "content set",
+ "entity",
+ "wearable",
+ "unknown"
+ ]
+
+ currentIndex: (("entity or wearable" === assetType) ?
+ model.indexOf("unknown") : model.indexOf(assetType))
+
+ Component.onCompleted: {
+ onCurrentIndexChanged.connect(function() {
+ assetType = model[currentIndex];
+ sendToScript({
+ method: "tester_updateResourceObjectAssetType",
+ objectId: resourceListModel.get(index)["id"],
+ assetType: assetType });
+ });
+ }
+ }
+
+ Repeater {
+ model: [ "forward", "trash" ]
+
+ HifiStylesUit.HiFiGlyphs {
+ property var glyphs: {
+ "application": hifi.glyphs.install,
+ "avatar": hifi.glyphs.avatar,
+ "content set": hifi.glyphs.globe,
+ "entity": hifi.glyphs.wand,
+ "trash": hifi.glyphs.trash,
+ "unknown": hifi.glyphs.circleSlash,
+ "wearable": hifi.glyphs.hat,
+ }
+ text: (("trash" === modelData) ?
+ glyphs.trash :
+ glyphs[comboBox.model[comboBox.currentIndex]])
+ size: ("trash" === modelData) ? 22 : 30
+ color: hifi.colors.black
+ horizontalAlignment: Text.AlignHCenter
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ actions[modelData](resource, comboBox.currentText);
+ }
+ }
+ }
+ }
+ }
+
+ headerPositioning: ListView.OverlayHeader
+ header: HifiStylesUit.RalewayRegular {
+ id: rootHeader
+ text: "Marketplace Item Tester"
+ height: 80
+ width: paintedWidth
+ size: 22
+ color: hifi.colors.black
+ anchors.left: parent.left
+ anchors.leftMargin: 12
+ }
+
+ footerPositioning: ListView.OverlayFooter
+ footer: Row {
+ id: rootActions
+ spacing: 20
+ anchors.horizontalCenter: parent.horizontalCenter
+
+ property string currentAction
+ property var actions: {
+ "Load File": function(){
+ rootActions.currentAction = "load file";
+ Window.browseChanged.connect(onResourceSelected);
+ Window.browseAsync("Please select a file (*.app.json *.json *.fst *.json.gz)", "", "Assets (*.app.json *.json *.fst *.json.gz)");
+ },
+ "Load URL": function(){
+ rootActions.currentAction = "load url";
+ Window.promptTextChanged.connect(onResourceSelected);
+ Window.promptAsync("Please enter a URL", "");
+ }
+ }
+
+ function onResourceSelected(resource) {
+ // It is possible that we received the present signal
+ // from something other than our browserAsync window.
+ // Alas, there is nothing we can do about that so charge
+ // ahead as though we are sure the present signal is one
+ // we expect.
+ switch(currentAction) {
+ case "load file":
+ Window.browseChanged.disconnect(onResourceSelected);
+ break
+ case "load url":
+ Window.promptTextChanged.disconnect(onResourceSelected);
+ break;
+ }
+ if (resource) {
+ var resourceObj = buildResourceObj(resource);
+ installResourceObj(resourceObj);
+ sendToScript({
+ method: 'tester_newResourceObject',
+ resourceObject: resourceObj });
+ }
+ }
+
+ Repeater {
+ model: [ "Load File", "Load URL" ]
+ HifiControlsUit.Button {
+ color: hifi.buttons.blue
+ fontSize: 20
+ text: modelData
+ width: root.width / 3
+ height: 40
+ onClicked: actions[text]()
+ }
+ }
+ }
+ }
+}
diff --git a/interface/resources/qml/hifi/commerce/marketplaceItemTester/spinner.gif b/interface/resources/qml/hifi/commerce/marketplaceItemTester/spinner.gif
new file mode 100644
index 0000000000..00f75ae62f
Binary files /dev/null and b/interface/resources/qml/hifi/commerce/marketplaceItemTester/spinner.gif differ
diff --git a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml
index 032d9b0199..eeb9ac3c54 100644
--- a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml
+++ b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml
@@ -59,7 +59,7 @@ Item {
Connections {
target: Commerce;
-
+
onContentSetChanged: {
if (contentSetHref === root.itemHref) {
showConfirmation = true;
@@ -135,7 +135,7 @@ Item {
anchors.topMargin: 8;
width: 30;
height: width;
-
+
HiFiGlyphs {
id: closeContextMenuGlyph;
text: hifi.glyphs.close;
@@ -376,7 +376,7 @@ Item {
}
}
}
-
+
transform: Rotation {
id: rotation;
origin.x: flipable.width/2;
@@ -509,7 +509,7 @@ Item {
}
verticalAlignment: Text.AlignTop;
}
-
+
HiFiGlyphs {
id: statusIcon;
text: {
@@ -588,7 +588,7 @@ Item {
border.width: 1;
border.color: "#E2334D";
}
-
+
HiFiGlyphs {
id: contextMenuGlyph;
text: hifi.glyphs.verticalEllipsis;
@@ -615,7 +615,7 @@ Item {
}
}
}
-
+
Rectangle {
id: rezzedNotifContainer;
z: 998;
@@ -663,13 +663,13 @@ Item {
Tablet.playSound(TabletEnums.ButtonHover);
}
}
-
+
onFocusChanged: {
if (focus) {
Tablet.playSound(TabletEnums.ButtonHover);
}
}
-
+
onClicked: {
Tablet.playSound(TabletEnums.ButtonClick);
if (root.itemType === "contentSet") {
@@ -775,7 +775,7 @@ Item {
// Style
color: hifi.colors.redAccent;
horizontalAlignment: Text.AlignRight;
-
+
MouseArea {
anchors.fill: parent;
hoverEnabled: true;
diff --git a/interface/src/AndroidHelper.cpp b/interface/src/AndroidHelper.cpp
index 419382f2cb..400085a62a 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,12 @@ 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) {
+ QMetaObject::invokeMethod(audioClient.data(), "setHeadsetPluggedIn", Q_ARG(bool, pluggedIn));
+ }
+#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/interface/src/Application.cpp b/interface/src/Application.cpp
index 46cebc1661..3a34c87ef1 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -1691,21 +1691,21 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
return DependencyManager::get()->navigationFocused() ? 1 : 0;
});
_applicationStateDevice->setInputVariant(STATE_PLATFORM_WINDOWS, []() -> float {
-#if defined(Q_OS_WIN)
+#if defined(Q_OS_WIN)
return 1;
#else
return 0;
#endif
});
_applicationStateDevice->setInputVariant(STATE_PLATFORM_MAC, []() -> float {
-#if defined(Q_OS_MAC)
+#if defined(Q_OS_MAC)
return 1;
#else
return 0;
#endif
});
_applicationStateDevice->setInputVariant(STATE_PLATFORM_ANDROID, []() -> float {
-#if defined(Q_OS_ANDROID)
+#if defined(Q_OS_ANDROID)
return 1;
#else
return 0;
@@ -1759,10 +1759,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
// Make sure we don't time out during slow operations at startup
updateHeartbeat();
-
QTimer* settingsTimer = new QTimer();
moveToNewNamedThread(settingsTimer, "Settings Thread", [this, settingsTimer]{
- connect(qApp, &Application::beforeAboutToQuit, [this, settingsTimer]{
+ // This needs to run on the settings thread, so we need to pass the `settingsTimer` as the
+ // receiver object, otherwise it will run on the application thread and trigger a warning
+ // about trying to kill the timer on the main thread.
+ connect(qApp, &Application::beforeAboutToQuit, settingsTimer, [this, settingsTimer]{
// Disconnect the signal from the save settings
QObject::disconnect(settingsTimer, &QTimer::timeout, this, &Application::saveSettings);
// Stop the settings timer
@@ -2881,9 +2883,10 @@ void Application::initializeUi() {
QUrl{ "hifi/commerce/common/CommerceLightbox.qml" },
QUrl{ "hifi/commerce/common/EmulatedMarketplaceHeader.qml" },
QUrl{ "hifi/commerce/common/FirstUseTutorial.qml" },
- QUrl{ "hifi/commerce/common/SortableListModel.qml" },
QUrl{ "hifi/commerce/common/sendAsset/SendAsset.qml" },
+ QUrl{ "hifi/commerce/common/SortableListModel.qml" },
QUrl{ "hifi/commerce/inspectionCertificate/InspectionCertificate.qml" },
+ QUrl{ "hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml"},
QUrl{ "hifi/commerce/purchases/PurchasedItem.qml" },
QUrl{ "hifi/commerce/purchases/Purchases.qml" },
QUrl{ "hifi/commerce/wallet/Help.qml" },
@@ -3498,13 +3501,14 @@ bool Application::isServerlessMode() const {
}
void Application::setIsInterstitialMode(bool interstitialMode) {
- Settings settings;
- bool enableInterstitial = settings.value("enableIntersitialMode", false).toBool();
- if (_interstitialMode != interstitialMode && enableInterstitial) {
- _interstitialMode = interstitialMode;
+ bool enableInterstitial = DependencyManager::get()->getDomainHandler().getInterstitialModeEnabled();
+ if (enableInterstitial) {
+ if (_interstitialMode != interstitialMode) {
+ _interstitialMode = interstitialMode;
- DependencyManager::get()->setAudioPaused(_interstitialMode);
- DependencyManager::get()->setMyAvatarDataPacketsPaused(_interstitialMode);
+ DependencyManager::get()->setAudioPaused(_interstitialMode);
+ DependencyManager::get()->setMyAvatarDataPacketsPaused(_interstitialMode);
+ }
}
}
diff --git a/interface/src/Application.h b/interface/src/Application.h
index 3bebc60480..eedbdb7622 100644
--- a/interface/src/Application.h
+++ b/interface/src/Application.h
@@ -432,7 +432,7 @@ public slots:
void setIsServerlessMode(bool serverlessDomain);
void loadServerlessDomain(QUrl domainURL, bool errorDomain = false);
- void setIsInterstitialMode(bool interstialMode);
+ void setIsInterstitialMode(bool interstitialMode);
void updateVerboseLogging();
diff --git a/interface/src/ConnectionMonitor.cpp b/interface/src/ConnectionMonitor.cpp
index 3c85cfb339..e86061b090 100644
--- a/interface/src/ConnectionMonitor.cpp
+++ b/interface/src/ConnectionMonitor.cpp
@@ -41,9 +41,15 @@ void ConnectionMonitor::init() {
}
connect(&_timer, &QTimer::timeout, this, [this]() {
- qDebug() << "ConnectionMonitor: Redirecting to 404 error domain";
// set in a timeout error
- emit setRedirectErrorState(REDIRECT_HIFI_ADDRESS, 5);
+ bool enableInterstitial = DependencyManager::get()->getDomainHandler().getInterstitialModeEnabled();
+ if (enableInterstitial) {
+ qDebug() << "ConnectionMonitor: Redirecting to 404 error domain";
+ emit setRedirectErrorState(REDIRECT_HIFI_ADDRESS, "", 5);
+ } else {
+ qDebug() << "ConnectionMonitor: Showing connection failure window";
+ DependencyManager::get()->setDomainConnectionFailureVisibility(true);
+ }
});
}
@@ -53,4 +59,8 @@ void ConnectionMonitor::startTimer() {
void ConnectionMonitor::stopTimer() {
_timer.stop();
+ bool enableInterstitial = DependencyManager::get()->getDomainHandler().getInterstitialModeEnabled();
+ if (!enableInterstitial) {
+ DependencyManager::get()->setDomainConnectionFailureVisibility(false);
+ }
}
diff --git a/interface/src/ConnectionMonitor.h b/interface/src/ConnectionMonitor.h
index 5e75e2618b..2fda6ef7cd 100644
--- a/interface/src/ConnectionMonitor.h
+++ b/interface/src/ConnectionMonitor.h
@@ -24,7 +24,7 @@ public:
void init();
signals:
- void setRedirectErrorState(QUrl errorURL, int reasonCode);
+ void setRedirectErrorState(QUrl errorURL, QString reasonMessage = "", int reasonCode = -1, const QString& extraInfo = "");
private slots:
void startTimer();
@@ -34,4 +34,4 @@ private:
QTimer _timer;
};
-#endif // hifi_ConnectionMonitor_h
\ No newline at end of file
+#endif // hifi_ConnectionMonitor_h
diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp
index 1faf17ea9a..0fdd246d7b 100644
--- a/interface/src/avatar/AvatarManager.cpp
+++ b/interface/src/avatar/AvatarManager.cpp
@@ -456,31 +456,37 @@ void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar
}
void AvatarManager::clearOtherAvatars() {
- // Remove other avatars from the world but don't actually remove them from _avatarHash
- // each will either be removed on timeout or will re-added to the world on receipt of update.
- const render::ScenePointer& scene = qApp->getMain3DScene();
- render::Transaction transaction;
-
- QReadLocker locker(&_hashLock);
- AvatarHash::iterator avatarIterator = _avatarHash.begin();
- while (avatarIterator != _avatarHash.end()) {
- auto avatar = std::static_pointer_cast(avatarIterator.value());
- if (avatar != _myAvatar) {
- handleRemovedAvatar(avatar);
- avatarIterator = _avatarHash.erase(avatarIterator);
- } else {
- ++avatarIterator;
- }
- }
- assert(scene);
- scene->enqueueTransaction(transaction);
_myAvatar->clearLookAtTargetAvatar();
+
+ // setup a vector of removed avatars outside the scope of the hash lock
+ std::vector removedAvatars;
+
+ {
+ QWriteLocker locker(&_hashLock);
+
+ removedAvatars.reserve(_avatarHash.size());
+
+ auto avatarIterator = _avatarHash.begin();
+ while (avatarIterator != _avatarHash.end()) {
+ auto avatar = std::static_pointer_cast(avatarIterator.value());
+ if (avatar != _myAvatar) {
+ removedAvatars.push_back(avatar);
+ avatarIterator = _avatarHash.erase(avatarIterator);
+ } else {
+ ++avatarIterator;
+ }
+ }
+ }
+
+ for (auto& av : removedAvatars) {
+ handleRemovedAvatar(av);
+ }
}
void AvatarManager::deleteAllAvatars() {
assert(_avatarsToChangeInPhysics.empty());
- QReadLocker locker(&_hashLock);
+ QWriteLocker locker(&_hashLock);
AvatarHash::iterator avatarIterator = _avatarHash.begin();
while (avatarIterator != _avatarHash.end()) {
auto avatar = std::static_pointer_cast(avatarIterator.value());
diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h
index 306ba6f39b..9c4287728d 100644
--- a/interface/src/avatar/AvatarManager.h
+++ b/interface/src/avatar/AvatarManager.h
@@ -204,7 +204,12 @@ private:
void simulateAvatarFades(float deltaTime);
AvatarSharedPointer newSharedAvatar() override;
- void handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason = KillAvatarReason::NoReason) override;
+
+ // called only from the AvatarHashMap thread - cannot be called while this thread holds the
+ // hash lock, since handleRemovedAvatar needs a write lock on the entity tree and the entity tree
+ // frequently grabs a read lock on the hash to get a given avatar by ID
+ void handleRemovedAvatar(const AvatarSharedPointer& removedAvatar,
+ KillAvatarReason removalReason = KillAvatarReason::NoReason) override;
QVector _avatarsToFade;
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/interface/src/commerce/QmlCommerce.cpp b/interface/src/commerce/QmlCommerce.cpp
index 06da18148f..7c5df0f3e3 100644
--- a/interface/src/commerce/QmlCommerce.cpp
+++ b/interface/src/commerce/QmlCommerce.cpp
@@ -199,12 +199,18 @@ void QmlCommerce::transferAssetToUsername(const QString& username,
}
void QmlCommerce::replaceContentSet(const QString& itemHref, const QString& certificateID) {
- auto ledger = DependencyManager::get();
- ledger->updateLocation(certificateID, DependencyManager::get()->getPlaceName(), true);
+ if (!certificateID.isEmpty()) {
+ auto ledger = DependencyManager::get();
+ ledger->updateLocation(
+ certificateID,
+ DependencyManager::get()->getPlaceName(),
+ true);
+ }
qApp->replaceDomainContent(itemHref);
- QJsonObject messageProperties = { { "status", "SuccessfulRequestToReplaceContent" }, { "content_set_url", itemHref } };
+ QJsonObject messageProperties = {
+ { "status", "SuccessfulRequestToReplaceContent" },
+ { "content_set_url", itemHref } };
UserActivityLogger::getInstance().logAction("replace_domain_content", messageProperties);
-
emit contentSetChanged(itemHref);
}
@@ -228,6 +234,7 @@ QString QmlCommerce::getInstalledApps(const QString& justInstalledAppID) {
// Thus, we protect against deleting the .app.json from the user's disk (below)
// by skipping that check for the app we just installed.
if ((justInstalledAppID != "") && ((justInstalledAppID + ".app.json") == appFileName)) {
+ installedAppsFromMarketplace += appFileName + ",";
continue;
}
diff --git a/interface/src/raypick/LaserPointer.cpp b/interface/src/raypick/LaserPointer.cpp
index 5fbe3a90b5..3c66923b4e 100644
--- a/interface/src/raypick/LaserPointer.cpp
+++ b/interface/src/raypick/LaserPointer.cpp
@@ -35,6 +35,14 @@ void LaserPointer::editRenderStatePath(const std::string& state, const QVariant&
}
}
+PickResultPointer LaserPointer::getPickResultCopy(const PickResultPointer& pickResult) const {
+ auto rayPickResult = std::dynamic_pointer_cast(pickResult);
+ if (!rayPickResult) {
+ return std::make_shared();
+ }
+ return std::make_shared(*rayPickResult.get());
+}
+
QVariantMap LaserPointer::toVariantMap() const {
QVariantMap qVariantMap;
diff --git a/interface/src/raypick/LaserPointer.h b/interface/src/raypick/LaserPointer.h
index c0ac3259d9..b391f60f85 100644
--- a/interface/src/raypick/LaserPointer.h
+++ b/interface/src/raypick/LaserPointer.h
@@ -47,6 +47,8 @@ public:
static std::shared_ptr buildRenderState(const QVariantMap& propMap);
protected:
+ PickResultPointer getPickResultCopy(const PickResultPointer& pickResult) const override;
+
void editRenderStatePath(const std::string& state, const QVariant& pathProps) override;
glm::vec3 getPickOrigin(const PickResultPointer& pickResult) const override;
diff --git a/interface/src/raypick/ParabolaPointer.cpp b/interface/src/raypick/ParabolaPointer.cpp
index ad698c409b..33fa8738d9 100644
--- a/interface/src/raypick/ParabolaPointer.cpp
+++ b/interface/src/raypick/ParabolaPointer.cpp
@@ -30,6 +30,14 @@ ParabolaPointer::ParabolaPointer(const QVariant& rayProps, const RenderStateMap&
{
}
+PickResultPointer ParabolaPointer::getPickResultCopy(const PickResultPointer& pickResult) const {
+ auto parabolaPickResult = std::dynamic_pointer_cast(pickResult);
+ if (!parabolaPickResult) {
+ return std::make_shared();
+ }
+ return std::make_shared(*parabolaPickResult.get());
+}
+
void ParabolaPointer::editRenderStatePath(const std::string& state, const QVariant& pathProps) {
auto renderState = std::static_pointer_cast(_renderStates[state]);
if (renderState) {
diff --git a/interface/src/raypick/ParabolaPointer.h b/interface/src/raypick/ParabolaPointer.h
index 526abe3b0d..8fb864c07b 100644
--- a/interface/src/raypick/ParabolaPointer.h
+++ b/interface/src/raypick/ParabolaPointer.h
@@ -102,6 +102,8 @@ public:
static std::shared_ptr buildRenderState(const QVariantMap& propMap);
protected:
+ virtual PickResultPointer getPickResultCopy(const PickResultPointer& pickResult) const override;
+
void editRenderStatePath(const std::string& state, const QVariant& pathProps) override;
glm::vec3 getPickOrigin(const PickResultPointer& pickResult) const override;
diff --git a/interface/src/raypick/StylusPointer.cpp b/interface/src/raypick/StylusPointer.cpp
index 06e3e52d21..b648e125bf 100644
--- a/interface/src/raypick/StylusPointer.cpp
+++ b/interface/src/raypick/StylusPointer.cpp
@@ -147,6 +147,14 @@ bool StylusPointer::shouldTrigger(const PickResultPointer& pickResult) {
return false;
}
+PickResultPointer StylusPointer::getPickResultCopy(const PickResultPointer& pickResult) const {
+ auto stylusPickResult = std::dynamic_pointer_cast(pickResult);
+ if (!stylusPickResult) {
+ return std::make_shared();
+ }
+ return std::make_shared(*stylusPickResult.get());
+}
+
Pointer::PickedObject StylusPointer::getHoveredObject(const PickResultPointer& pickResult) {
auto stylusPickResult = std::static_pointer_cast(pickResult);
if (!stylusPickResult) {
diff --git a/interface/src/raypick/StylusPointer.h b/interface/src/raypick/StylusPointer.h
index 4095acb529..ff60fd78e5 100644
--- a/interface/src/raypick/StylusPointer.h
+++ b/interface/src/raypick/StylusPointer.h
@@ -42,6 +42,7 @@ protected:
Buttons getPressedButtons(const PickResultPointer& pickResult) override;
bool shouldHover(const PickResultPointer& pickResult) override;
bool shouldTrigger(const PickResultPointer& pickResult) override;
+ virtual PickResultPointer getPickResultCopy(const PickResultPointer& pickResult) const override;
PointerEvent buildPointerEvent(const PickedObject& target, const PickResultPointer& pickResult, const std::string& button = "", bool hover = true) override;
diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp
index e3ae65aee1..d4eb37e0aa 100644
--- a/interface/src/scripting/WindowScriptingInterface.cpp
+++ b/interface/src/scripting/WindowScriptingInterface.cpp
@@ -180,6 +180,14 @@ void WindowScriptingInterface::setPreviousBrowseAssetLocation(const QString& loc
Setting::Handle(LAST_BROWSE_ASSETS_LOCATION_SETTING).set(location);
}
+bool WindowScriptingInterface::getInterstitialModeEnabled() const {
+ return DependencyManager::get()->getDomainHandler().getInterstitialModeEnabled();
+}
+
+void WindowScriptingInterface::setInterstitialModeEnabled(bool enableInterstitialMode) {
+ DependencyManager::get()->getDomainHandler().setInterstitialModeEnabled(enableInterstitialMode);
+}
+
bool WindowScriptingInterface::isPointOnDesktopWindow(QVariant point) {
auto offscreenUi = DependencyManager::get();
return offscreenUi->isPointOnDesktopWindow(point);
diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h
index 3827406729..ddd7159f23 100644
--- a/interface/src/scripting/WindowScriptingInterface.h
+++ b/interface/src/scripting/WindowScriptingInterface.h
@@ -49,6 +49,7 @@ class WindowScriptingInterface : public QObject, public Dependency {
Q_PROPERTY(int innerHeight READ getInnerHeight)
Q_PROPERTY(int x READ getX)
Q_PROPERTY(int y READ getY)
+ Q_PROPERTY(bool interstitialModeEnabled READ getInterstitialModeEnabled WRITE setInterstitialModeEnabled)
public:
WindowScriptingInterface();
@@ -758,6 +759,9 @@ private:
QString getPreviousBrowseAssetLocation() const;
void setPreviousBrowseAssetLocation(const QString& location);
+ bool getInterstitialModeEnabled() const;
+ void setInterstitialModeEnabled(bool enableInterstitialMode);
+
void ensureReticleVisible() const;
int createMessageBox(QString title, QString text, int buttons, int defaultButton);
diff --git a/interface/src/ui/AnimStats.cpp b/interface/src/ui/AnimStats.cpp
index 7b4778e365..e3579fa2dc 100644
--- a/interface/src/ui/AnimStats.cpp
+++ b/interface/src/ui/AnimStats.cpp
@@ -42,6 +42,29 @@ void AnimStats::updateStats(bool force) {
auto myAvatar = avatarManager->getMyAvatar();
auto debugAlphaMap = myAvatar->getSkeletonModel()->getRig().getDebugAlphaMap();
+ glm::vec3 position = myAvatar->getWorldPosition();
+ glm::quat rotation = myAvatar->getWorldOrientation();
+ glm::vec3 velocity = myAvatar->getWorldVelocity();
+
+ _positionText = QString("Position: (%1, %2, %3)").
+ arg(QString::number(position.x, 'f', 2)).
+ arg(QString::number(position.y, 'f', 2)).
+ arg(QString::number(position.z, 'f', 2));
+ emit positionTextChanged();
+
+ glm::vec3 eulerRotation = safeEulerAngles(rotation);
+ _rotationText = QString("Heading: %1").
+ arg(QString::number(glm::degrees(eulerRotation.y), 'f', 2));
+ emit rotationTextChanged();
+
+ // transform velocity into rig coordinate frame. z forward.
+ glm::vec3 localVelocity = Quaternions::Y_180 * glm::inverse(rotation) * velocity;
+ _velocityText = QString("Local Vel: (%1, %2, %3)").
+ arg(QString::number(localVelocity.x, 'f', 2)).
+ arg(QString::number(localVelocity.y, 'f', 2)).
+ arg(QString::number(localVelocity.z, 'f', 2));
+ emit velocityTextChanged();
+
// update animation debug alpha values
QStringList newAnimAlphaValues;
qint64 now = usecTimestampNow();
diff --git a/interface/src/ui/AnimStats.h b/interface/src/ui/AnimStats.h
index 1a6795b498..7b6aaf7b54 100644
--- a/interface/src/ui/AnimStats.h
+++ b/interface/src/ui/AnimStats.h
@@ -19,6 +19,9 @@ class AnimStats : public QQuickItem {
Q_PROPERTY(QStringList animAlphaValues READ animAlphaValues NOTIFY animAlphaValuesChanged)
Q_PROPERTY(QStringList animVars READ animVars NOTIFY animVarsChanged)
Q_PROPERTY(QStringList animStateMachines READ animStateMachines NOTIFY animStateMachinesChanged)
+ Q_PROPERTY(QString positionText READ positionText NOTIFY positionTextChanged)
+ Q_PROPERTY(QString rotationText READ rotationText NOTIFY rotationTextChanged)
+ Q_PROPERTY(QString velocityText READ velocityText NOTIFY velocityTextChanged)
public:
static AnimStats* getInstance();
@@ -27,9 +30,13 @@ public:
void updateStats(bool force = false);
- QStringList animAlphaValues() { return _animAlphaValues; }
- QStringList animVars() { return _animVarsList; }
- QStringList animStateMachines() { return _animStateMachines; }
+ QStringList animAlphaValues() const { return _animAlphaValues; }
+ QStringList animVars() const { return _animVarsList; }
+ QStringList animStateMachines() const { return _animStateMachines; }
+
+ QString positionText() const { return _positionText; }
+ QString rotationText() const { return _rotationText; }
+ QString velocityText() const { return _velocityText; }
public slots:
void forceUpdateStats() { updateStats(true); }
@@ -39,6 +46,9 @@ signals:
void animAlphaValuesChanged();
void animVarsChanged();
void animStateMachinesChanged();
+ void positionTextChanged();
+ void rotationTextChanged();
+ void velocityTextChanged();
private:
QStringList _animAlphaValues;
@@ -50,6 +60,10 @@ private:
std::map _animVarChangedTimers; // last time animVar value has changed.
QStringList _animStateMachines;
+
+ QString _positionText;
+ QString _rotationText;
+ QString _velocityText;
};
#endif // hifi_AnimStats_h
diff --git a/libraries/animation/src/AnimStateMachine.cpp b/libraries/animation/src/AnimStateMachine.cpp
index 7f46cd614a..fb13b8e71c 100644
--- a/libraries/animation/src/AnimStateMachine.cpp
+++ b/libraries/animation/src/AnimStateMachine.cpp
@@ -88,6 +88,10 @@ const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, co
processOutputJoints(triggersOut);
context.addStateMachineInfo(_id, _currentState->getID(), _previousState->getID(), _duringInterp, _alpha);
+ if (_duringInterp) {
+ // hack: add previoius state to debug alpha map, with parens around it's name.
+ context.setDebugAlpha(QString("(%1)").arg(_previousState->getID()), 1.0f - _alpha, AnimNodeType::Clip);
+ }
return _poses;
}
diff --git a/libraries/animation/src/AnimVariant.cpp b/libraries/animation/src/AnimVariant.cpp
index 509462984a..21fe234f7b 100644
--- a/libraries/animation/src/AnimVariant.cpp
+++ b/libraries/animation/src/AnimVariant.cpp
@@ -140,14 +140,19 @@ std::map AnimVariantMap::toDebugMap() const {
result[pair.first] = QString::number(pair.second.getFloat(), 'f', 3);
break;
case AnimVariant::Type::Vec3: {
+ // To prevent filling up debug stats, don't show vec3 values
+ /*
glm::vec3 value = pair.second.getVec3();
result[pair.first] = QString("(%1, %2, %3)").
arg(QString::number(value.x, 'f', 3)).
arg(QString::number(value.y, 'f', 3)).
arg(QString::number(value.z, 'f', 3));
+ */
break;
}
case AnimVariant::Type::Quat: {
+ // To prevent filling up the anim stats, don't show quat values
+ /*
glm::quat value = pair.second.getQuat();
result[pair.first] = QString("(%1, %2, %3, %4)").
arg(QString::number(value.x, 'f', 3)).
@@ -155,10 +160,14 @@ std::map AnimVariantMap::toDebugMap() const {
arg(QString::number(value.z, 'f', 3)).
arg(QString::number(value.w, 'f', 3));
break;
+ */
}
case AnimVariant::Type::String:
+ // To prevent filling up anim stats, don't show string values
+ /*
result[pair.first] = pair.second.getString();
break;
+ */
default:
assert(("invalid AnimVariant::Type", false));
}
diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp
index 3a33eccc8a..d00bc29054 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
@@ -101,6 +100,13 @@ QList getAvailableDevices(QAudio::Mode mode) {
// now called from a background thread, to keep blocking operations off the audio thread
void AudioClient::checkDevices() {
+ // Make sure we're not shutting down
+ Lock timerMutex(_checkDevicesMutex);
+ // If we HAVE shut down after we were queued, but prior to execution, early exit
+ if (nullptr == _checkDevicesTimer) {
+ return;
+ }
+
auto inputDevices = getAvailableDevices(QAudio::AudioInput);
auto outputDevices = getAvailableDevices(QAudio::AudioOutput);
@@ -210,6 +216,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
@@ -278,9 +285,6 @@ void AudioClient::customDeleter() {
_shouldRestartInputSetup = false;
#endif
stop();
- _checkDevicesTimer->stop();
- _checkPeakValuesTimer->stop();
-
deleteLater();
}
@@ -461,9 +465,14 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) {
#if defined (Q_OS_ANDROID)
if (mode == QAudio::AudioInput) {
+ 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 (inputDevice.deviceName() == VOICE_RECOGNITION) {
+ if (((headsetOn || !aecEnabled) && inputDevice.deviceName() == VOICE_RECOGNITION) ||
+ ((!headsetOn && aecEnabled) && inputDevice.deviceName() == VOICE_COMMUNICATION)) {
return inputDevice;
}
}
@@ -648,12 +657,26 @@ void AudioClient::start() {
}
void AudioClient::stop() {
-
qCDebug(audioclient) << "AudioClient::stop(), requesting switchInputToAudioDevice() to shut down";
switchInputToAudioDevice(QAudioDeviceInfo(), true);
qCDebug(audioclient) << "AudioClient::stop(), requesting switchOutputToAudioDevice() to shut down";
switchOutputToAudioDevice(QAudioDeviceInfo(), true);
+
+ // Stop triggering the checks
+ QObject::disconnect(_checkPeakValuesTimer, &QTimer::timeout, nullptr, nullptr);
+ QObject::disconnect(_checkDevicesTimer, &QTimer::timeout, nullptr, nullptr);
+
+ // Destruction of the pointers will occur when the parent object (this) is destroyed)
+ {
+ Lock lock(_checkDevicesMutex);
+ _checkDevicesTimer = nullptr;
+ }
+ {
+ Lock lock(_checkPeakValuesMutex);
+ _checkPeakValuesTimer = nullptr;
+ }
+
#if defined(Q_OS_ANDROID)
_checkInputTimer.stop();
disconnect(&_checkInputTimer, &QTimer::timeout, 0, 0);
@@ -1640,6 +1663,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 4640d7c045..5e7f1fb8a0 100644
--- a/libraries/audio-client/src/AudioClient.h
+++ b/libraries/audio-client/src/AudioClient.h
@@ -64,6 +64,13 @@
#pragma warning( pop )
#endif
+#if defined (Q_OS_ANDROID)
+#define VOICE_RECOGNITION "voicerecognition"
+#define VOICE_COMMUNICATION "voicecommunication"
+
+#define SETTING_AEC_KEY "Android/aec"
+#endif
+
class QAudioInput;
class QAudioOutput;
class QIODevice;
@@ -169,6 +176,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();
@@ -217,6 +228,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);
@@ -278,6 +292,7 @@ private:
#ifdef Q_OS_ANDROID
QTimer _checkInputTimer;
long _inputReadsSinceLastCheck = 0l;
+ bool _isHeadsetPluggedIn;
#endif
class Gate {
@@ -432,7 +447,9 @@ private:
bool _shouldRestartInputSetup { true }; // Should we restart the input device because of an unintended stop?
#endif
+ Mutex _checkDevicesMutex;
QTimer* _checkDevicesTimer { nullptr };
+ Mutex _checkPeakValuesMutex;
QTimer* _checkPeakValuesTimer { nullptr };
bool _isRecording { false };
diff --git a/libraries/audio-client/src/AudioPeakValues.cpp b/libraries/audio-client/src/AudioPeakValues.cpp
index 0b8921a117..a50567da7f 100644
--- a/libraries/audio-client/src/AudioPeakValues.cpp
+++ b/libraries/audio-client/src/AudioPeakValues.cpp
@@ -40,6 +40,12 @@ void release(IAudioClient* audioClient) {
}
void AudioClient::checkPeakValues() {
+ // Guard against running during shutdown
+ Lock timerMutex(_checkPeakValuesMutex);
+ if (nullptr == _checkPeakValuesTimer) {
+ return;
+ }
+
// prepare the windows environment
CoInitialize(NULL);
diff --git a/libraries/audio/src/Sound.cpp b/libraries/audio/src/Sound.cpp
index 67f9952771..da284f19a3 100644
--- a/libraries/audio/src/Sound.cpp
+++ b/libraries/audio/src/Sound.cpp
@@ -43,8 +43,11 @@ void soundSharedPointerFromScriptValue(const QScriptValue& object, SharedSoundPo
}
}
-SoundScriptingInterface::SoundScriptingInterface(SharedSoundPointer sound) : _sound(sound) {
- QObject::connect(sound.data(), &Sound::ready, this, &SoundScriptingInterface::ready);
+SoundScriptingInterface::SoundScriptingInterface(const SharedSoundPointer& sound) : _sound(sound) {
+ // During shutdown we can sometimes get an empty sound pointer back
+ if (_sound) {
+ QObject::connect(_sound.data(), &Sound::ready, this, &SoundScriptingInterface::ready);
+ }
}
Sound::Sound(const QUrl& url, bool isStereo, bool isAmbisonic) :
diff --git a/libraries/audio/src/Sound.h b/libraries/audio/src/Sound.h
index 348600e4ae..a0544870d0 100644
--- a/libraries/audio/src/Sound.h
+++ b/libraries/audio/src/Sound.h
@@ -105,11 +105,11 @@ class SoundScriptingInterface : public QObject {
Q_PROPERTY(float duration READ getDuration)
public:
- SoundScriptingInterface(SharedSoundPointer sound);
- SharedSoundPointer getSound() { return _sound; }
+ SoundScriptingInterface(const SharedSoundPointer& sound);
+ const SharedSoundPointer& getSound() { return _sound; }
- bool isReady() const { return _sound->isReady(); }
- float getDuration() { return _sound->getDuration(); }
+ bool isReady() const { return _sound ? _sound->isReady() : false; }
+ float getDuration() { return _sound ? _sound->getDuration() : 0.0f; }
/**jsdoc
* Triggered when the sound has been downloaded and is ready to be played.
diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp
index d205a915f8..01557e307e 100644
--- a/libraries/avatars/src/AvatarHashMap.cpp
+++ b/libraries/avatars/src/AvatarHashMap.cpp
@@ -66,6 +66,22 @@ void AvatarReplicas::removeReplicas(const QUuid& parentID) {
}
}
+std::vector AvatarReplicas::takeReplicas(const QUuid& parentID) {
+ std::vector replicas;
+
+ auto it = _replicasMap.find(parentID);
+
+ if (it != _replicasMap.end()) {
+ // take a copy of the replica shared pointers for this parent
+ replicas.swap(it->second);
+
+ // erase the replicas for this parent from our map
+ _replicasMap.erase(it);
+ }
+
+ return replicas;
+}
+
void AvatarReplicas::processAvatarIdentity(const QUuid& parentID, const QByteArray& identityData, bool& identityChanged, bool& displayNameChanged) {
if (_replicasMap.find(parentID) != _replicasMap.end()) {
auto &replicas = _replicasMap[parentID];
@@ -386,24 +402,31 @@ void AvatarHashMap::processKillAvatar(QSharedPointer message, S
}
void AvatarHashMap::removeAvatar(const QUuid& sessionUUID, KillAvatarReason removalReason) {
- QWriteLocker locker(&_hashLock);
+ std::vector removedAvatars;
- auto replicaIDs = _replicas.getReplicaIDs(sessionUUID);
- _replicas.removeReplicas(sessionUUID);
- for (auto id : replicaIDs) {
- auto removedReplica = _avatarHash.take(id);
- if (removedReplica) {
- handleRemovedAvatar(removedReplica, removalReason);
+ {
+ QWriteLocker locker(&_hashLock);
+
+ auto replicas = _replicas.takeReplicas(sessionUUID);
+
+ for (auto& replica : replicas) {
+ auto removedReplica = _avatarHash.take(replica->getID());
+ if (removedReplica) {
+ removedAvatars.push_back(removedReplica);
+ }
+ }
+
+ _pendingAvatars.remove(sessionUUID);
+ auto removedAvatar = _avatarHash.take(sessionUUID);
+
+ if (removedAvatar) {
+ removedAvatars.push_back(removedAvatar);
}
}
- _pendingAvatars.remove(sessionUUID);
- auto removedAvatar = _avatarHash.take(sessionUUID);
-
- if (removedAvatar) {
+ for (auto& removedAvatar: removedAvatars) {
handleRemovedAvatar(removedAvatar, removalReason);
}
-
}
void AvatarHashMap::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason) {
@@ -421,11 +444,18 @@ void AvatarHashMap::sessionUUIDChanged(const QUuid& sessionUUID, const QUuid& ol
}
void AvatarHashMap::clearOtherAvatars() {
- QWriteLocker locker(&_hashLock);
+ QList removedAvatars;
- for (auto& av : _avatarHash) {
- handleRemovedAvatar(av);
+ {
+ QWriteLocker locker(&_hashLock);
+
+ // grab a copy of the current avatars so we can call handleRemoveAvatar for them
+ removedAvatars = _avatarHash.values();
+
+ _avatarHash.clear();
}
- _avatarHash.clear();
+ for (auto& av : removedAvatars) {
+ handleRemovedAvatar(av);
+ }
}
diff --git a/libraries/avatars/src/AvatarHashMap.h b/libraries/avatars/src/AvatarHashMap.h
index 70d7f8c04d..c2cb448e52 100644
--- a/libraries/avatars/src/AvatarHashMap.h
+++ b/libraries/avatars/src/AvatarHashMap.h
@@ -49,6 +49,7 @@ public:
void parseDataFromBuffer(const QUuid& parentID, const QByteArray& buffer);
void processAvatarIdentity(const QUuid& parentID, const QByteArray& identityData, bool& identityChanged, bool& displayNameChanged);
void removeReplicas(const QUuid& parentID);
+ std::vector takeReplicas(const QUuid& parentID);
void processTrait(const QUuid& parentID, AvatarTraits::TraitType traitType, QByteArray traitBinaryData);
void processDeletedTraitInstance(const QUuid& parentID, AvatarTraits::TraitType traitType, AvatarTraits::TraitInstanceID instanceID);
void processTraitInstance(const QUuid& parentID, AvatarTraits::TraitType traitType,
@@ -179,7 +180,7 @@ protected:
bool& isNew);
virtual AvatarSharedPointer findAvatar(const QUuid& sessionUUID) const; // uses a QReadLocker on the hashLock
virtual void removeAvatar(const QUuid& sessionUUID, KillAvatarReason removalReason = KillAvatarReason::NoReason);
-
+
virtual void handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason = KillAvatarReason::NoReason);
AvatarHash _avatarHash;
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()) {
diff --git a/libraries/networking/src/AddressManager.h b/libraries/networking/src/AddressManager.h
index c7cdf8f4ea..17041a5fd7 100644
--- a/libraries/networking/src/AddressManager.h
+++ b/libraries/networking/src/AddressManager.h
@@ -140,8 +140,7 @@ public:
*
* @typedef {number} location.LookupTrigger
*/
- enum LookupTrigger
- {
+ enum LookupTrigger {
UserInput,
Back,
Forward,
@@ -207,9 +206,8 @@ public slots:
// functions and signals that should be exposed are moved to a scripting interface class.
//
// we currently expect this to be called from NodeList once handleLookupString has been called with a path
- bool goToViewpointForPath(const QString& viewpointString, const QString& pathString) {
- return handleViewpoint(viewpointString, false, DomainPathResponse, false, pathString);
- }
+ bool goToViewpointForPath(const QString& viewpointString, const QString& pathString)
+ { return handleViewpoint(viewpointString, false, DomainPathResponse, false, pathString); }
/**jsdoc
* Go back to the previous location in your navigation history, if there is one.
@@ -231,8 +229,7 @@ public slots:
* location history is correctly maintained.
*/
void goToLocalSandbox(QString path = "", LookupTrigger trigger = LookupTrigger::StartupFromSettings) {
- handleUrl(SANDBOX_HIFI_ADDRESS + path, trigger);
- }
+ handleUrl(SANDBOX_HIFI_ADDRESS + path, trigger); }
/**jsdoc
* Go to the default "welcome" metaverse address.
@@ -364,8 +361,7 @@ signals:
* location.locationChangeRequired.connect(onLocationChangeRequired);
*/
void locationChangeRequired(const glm::vec3& newPosition,
- bool hasOrientationChange,
- const glm::quat& newOrientation,
+ bool hasOrientationChange, const glm::quat& newOrientation,
bool shouldFaceLocation);
/**jsdoc
@@ -448,11 +444,8 @@ private:
bool handleNetworkAddress(const QString& lookupString, LookupTrigger trigger, bool& hostChanged);
void handlePath(const QString& path, LookupTrigger trigger, bool wasPathOnly = false);
- bool handleViewpoint(const QString& viewpointString,
- bool shouldFace,
- LookupTrigger trigger,
- bool definitelyPathOnly = false,
- const QString& pathString = QString());
+ bool handleViewpoint(const QString& viewpointString, bool shouldFace, LookupTrigger trigger,
+ bool definitelyPathOnly = false, const QString& pathString = QString());
bool handleUsername(const QString& lookupString);
bool handleDomainID(const QString& host);
diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp
index f34a93de96..df34a1fb59 100644
--- a/libraries/networking/src/DomainHandler.cpp
+++ b/libraries/networking/src/DomainHandler.cpp
@@ -15,6 +15,10 @@
#include
+#include
+
+#include
+
#include
#include
@@ -134,6 +138,18 @@ void DomainHandler::hardReset() {
_pendingPath.clear();
}
+bool DomainHandler::getInterstitialModeEnabled() const {
+ return _interstitialModeSettingLock.resultWithReadLock([&] {
+ return _enableInterstitialMode.get();
+ });
+}
+
+void DomainHandler::setInterstitialModeEnabled(bool enableInterstitialMode) {
+ _interstitialModeSettingLock.withWriteLock([&] {
+ _enableInterstitialMode.set(enableInterstitialMode);
+ });
+}
+
void DomainHandler::setErrorDomainURL(const QUrl& url) {
_errorDomainURL = url;
return;
@@ -340,11 +356,15 @@ void DomainHandler::loadedErrorDomain(std::map namedPaths) {
DependencyManager::get()->goToViewpointForPath(viewpoint, QString());
}
-void DomainHandler::setRedirectErrorState(QUrl errorUrl, int reasonCode) {
- _errorDomainURL = errorUrl;
+void DomainHandler::setRedirectErrorState(QUrl errorUrl, QString reasonMessage, int reasonCode, const QString& extraInfo) {
_lastDomainConnectionError = reasonCode;
- _isInErrorState = true;
- emit redirectToErrorDomainURL(_errorDomainURL);
+ if (getInterstitialModeEnabled()) {
+ _errorDomainURL = errorUrl;
+ _isInErrorState = true;
+ emit redirectToErrorDomainURL(_errorDomainURL);
+ } else {
+ emit domainConnectionRefused(reasonMessage, reasonCode, extraInfo);
+ }
}
void DomainHandler::requestDomainSettings() {
@@ -485,13 +505,9 @@ void DomainHandler::processDomainServerConnectionDeniedPacket(QSharedPointer
#include
+#include
+#include
+
#include "HifiSockAddr.h"
#include "NetworkPeer.h"
#include "NLPacket.h"
@@ -83,6 +86,8 @@ public:
bool isConnected() const { return _isConnected; }
void setIsConnected(bool isConnected);
bool isServerless() const { return _domainURL.scheme() != URL_SCHEME_HIFI; }
+ bool getInterstitialModeEnabled() const;
+ void setInterstitialModeEnabled(bool enableInterstitialMode);
void connectedToServerless(std::map namedPaths);
@@ -171,7 +176,7 @@ public slots:
void processDomainServerConnectionDeniedPacket(QSharedPointer message);
// sets domain handler in error state.
- void setRedirectErrorState(QUrl errorUrl, int reasonCode);
+ void setRedirectErrorState(QUrl errorUrl, QString reasonMessage = "", int reason = -1, const QString& extraInfo = "");
bool isInErrorState() { return _isInErrorState; }
@@ -224,6 +229,8 @@ private:
QJsonObject _settingsObject;
QString _pendingPath;
QTimer _settingsTimer;
+ mutable ReadWriteLockable _interstitialModeSettingLock;
+ Setting::Handle _enableInterstitialMode{ "enableInterstitialMode", false };
QSet _domainConnectionRefusals;
bool _hasCheckedForAccessToken { false };
diff --git a/libraries/pointers/src/Pointer.cpp b/libraries/pointers/src/Pointer.cpp
index 031baece5f..26460cbdd7 100644
--- a/libraries/pointers/src/Pointer.cpp
+++ b/libraries/pointers/src/Pointer.cpp
@@ -68,7 +68,8 @@ void Pointer::update(unsigned int pointerID) {
// This only needs to be a read lock because update won't change any of the properties that can be modified from scripts
withReadLock([&] {
auto pickResult = getPrevPickResult();
- auto visualPickResult = getVisualPickResult(pickResult);
+ // Pointer needs its own PickResult object so it doesn't modify the cached pick result
+ auto visualPickResult = getVisualPickResult(getPickResultCopy(pickResult));
updateVisuals(visualPickResult);
generatePointerEvents(pointerID, visualPickResult);
});
diff --git a/libraries/pointers/src/Pointer.h b/libraries/pointers/src/Pointer.h
index 4264a60079..173163374f 100644
--- a/libraries/pointers/src/Pointer.h
+++ b/libraries/pointers/src/Pointer.h
@@ -91,6 +91,7 @@ protected:
virtual bool shouldHover(const PickResultPointer& pickResult) { return true; }
virtual bool shouldTrigger(const PickResultPointer& pickResult) { return true; }
+ virtual PickResultPointer getPickResultCopy(const PickResultPointer& pickResult) const = 0;
virtual PickResultPointer getVisualPickResult(const PickResultPointer& pickResult) { return pickResult; };
static const float POINTER_MOVE_DELAY;
diff --git a/libraries/render-utils/src/HighlightEffect.cpp b/libraries/render-utils/src/HighlightEffect.cpp
index 11326b1120..bcac31dd5a 100644
--- a/libraries/render-utils/src/HighlightEffect.cpp
+++ b/libraries/render-utils/src/HighlightEffect.cpp
@@ -37,6 +37,8 @@ namespace gr {
#define OUTLINE_STENCIL_MASK 1
+extern void initZPassPipelines(ShapePlumber& plumber, gpu::StatePointer state);
+
HighlightRessources::HighlightRessources() {
}
@@ -180,6 +182,7 @@ void DrawHighlightMask::run(const render::RenderContextPointer& renderContext, c
auto maskPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder);
auto maskSkinnedPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder.withSkinned());
+ auto maskSkinnedDQPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder.withSkinned().withDualQuatSkinned());
// Setup camera, projection and viewport for all items
batch.setViewportTransform(args->_viewport);
@@ -187,14 +190,17 @@ void DrawHighlightMask::run(const render::RenderContextPointer& renderContext, c
batch.setProjectionJitter(jitter.x, jitter.y);
batch.setViewTransform(viewMat);
- std::vector skinnedShapeKeys{};
+ std::vector skinnedShapeKeys;
+ std::vector skinnedDQShapeKeys;
// Iterate through all inShapes and render the unskinned
args->_shapePipeline = maskPipeline;
batch.setPipeline(maskPipeline->pipeline);
for (const auto& items : inShapes) {
itemBounds.insert(itemBounds.end(), items.second.begin(), items.second.end());
- if (items.first.isSkinned()) {
+ if (items.first.isSkinned() && items.first.isDualQuatSkinned()) {
+ skinnedDQShapeKeys.push_back(items.first);
+ } else if (items.first.isSkinned()) {
skinnedShapeKeys.push_back(items.first);
} else {
renderItems(renderContext, items.second);
@@ -202,10 +208,21 @@ void DrawHighlightMask::run(const render::RenderContextPointer& renderContext, c
}
// Reiterate to render the skinned
- args->_shapePipeline = maskSkinnedPipeline;
- batch.setPipeline(maskSkinnedPipeline->pipeline);
- for (const auto& key : skinnedShapeKeys) {
- renderItems(renderContext, inShapes.at(key));
+ if (skinnedShapeKeys.size() > 0) {
+ args->_shapePipeline = maskSkinnedPipeline;
+ batch.setPipeline(maskSkinnedPipeline->pipeline);
+ for (const auto& key : skinnedShapeKeys) {
+ renderItems(renderContext, inShapes.at(key));
+ }
+ }
+
+ // Reiterate to render the DQ skinned
+ if (skinnedDQShapeKeys.size() > 0) {
+ args->_shapePipeline = maskSkinnedDQPipeline;
+ batch.setPipeline(maskSkinnedDQPipeline->pipeline);
+ for (const auto& key : skinnedDQShapeKeys) {
+ renderItems(renderContext, inShapes.at(key));
+ }
}
args->_shapePipeline = nullptr;
@@ -488,7 +505,7 @@ void DrawHighlightTask::build(JobModel& task, const render::Varying& inputs, ren
state->setDepthTest(true, true, gpu::LESS_EQUAL);
state->setColorWriteMask(false, false, false, false);
- initMaskPipelines(*shapePlumber, state);
+ initZPassPipelines(*shapePlumber, state);
}
auto sharedParameters = std::make_shared();
@@ -548,16 +565,4 @@ const render::Varying DrawHighlightTask::addSelectItemJobs(JobModel& task, const
const auto selectedMetasAndOpaques = task.addJob("OpaqueSelection", selectMetaAndOpaqueInput);
const auto selectItemInput = SelectItems::Inputs(transparents, selectedMetasAndOpaques, selectionName).asVarying();
return task.addJob("TransparentSelection", selectItemInput);
-}
-
-void DrawHighlightTask::initMaskPipelines(render::ShapePlumber& shapePlumber, gpu::StatePointer state) {
- gpu::ShaderPointer modelProgram = gpu::Shader::createProgram(shader::render_utils::program::model_shadow);
- shapePlumber.addPipeline(
- ShapeKey::Filter::Builder().withoutSkinned(),
- modelProgram, state);
-
- gpu::ShaderPointer skinProgram = gpu::Shader::createProgram(shader::render_utils::program::skin_model_shadow);
- shapePlumber.addPipeline(
- ShapeKey::Filter::Builder().withSkinned(),
- skinProgram, state);
-}
+}
\ No newline at end of file
diff --git a/libraries/render-utils/src/HighlightEffect.h b/libraries/render-utils/src/HighlightEffect.h
index 64a97a549e..32668c1ab6 100644
--- a/libraries/render-utils/src/HighlightEffect.h
+++ b/libraries/render-utils/src/HighlightEffect.h
@@ -208,8 +208,6 @@ public:
void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs);
private:
-
- static void initMaskPipelines(render::ShapePlumber& plumber, gpu::StatePointer state);
static const render::Varying addSelectItemJobs(JobModel& task, const render::Varying& selectionName, const RenderFetchCullSortTask::BucketList& items);
};
diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp
index b9ed43c339..ab6507b29c 100644
--- a/libraries/render-utils/src/Model.cpp
+++ b/libraries/render-utils/src/Model.cpp
@@ -1553,14 +1553,13 @@ void Model::setBlendedVertices(int blendNumber, const QVector& vertic
for (int i = 0; i < fbxGeometry.meshes.size(); i++) {
const FBXMesh& mesh = fbxGeometry.meshes.at(i);
auto meshNormalsAndTangents = _normalsAndTangents.find(i);
- if (mesh.blendshapes.isEmpty() || meshNormalsAndTangents == _normalsAndTangents.end()) {
+ const auto& buffer = _blendedVertexBuffers.find(i);
+ if (mesh.blendshapes.isEmpty() || meshNormalsAndTangents == _normalsAndTangents.end() || buffer == _blendedVertexBuffers.end()) {
continue;
}
const auto vertexCount = mesh.vertices.size();
const auto verticesSize = vertexCount * sizeof(glm::vec3);
- const auto& buffer = _blendedVertexBuffers.find(i);
- assert(buffer != _blendedVertexBuffers.end());
buffer->second->resize(mesh.vertices.size() * sizeof(glm::vec3) + meshNormalsAndTangents->second.size() * sizeof(NormalType));
buffer->second->setSubData(0, verticesSize, (gpu::Byte*) vertices.constData() + index * sizeof(glm::vec3));
buffer->second->setSubData(verticesSize, meshNormalsAndTangents->second.size() * sizeof(NormalType), (const gpu::Byte*) normalsAndTangents.data() + normalAndTangentIndex * sizeof(NormalType));
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;
};
}
diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp
index bb22a1e753..012e7aa1f5 100644
--- a/libraries/shared/src/SharedUtil.cpp
+++ b/libraries/shared/src/SharedUtil.cpp
@@ -19,8 +19,8 @@
#include
#include
#include
-#include
#include
+#include
#include
@@ -127,82 +127,10 @@ void usecTimestampNowForceClockSkew(qint64 clockSkew) {
::usecTimestampNowAdjust = clockSkew;
}
-static std::atomic TIME_REFERENCE { 0 }; // in usec
-static std::once_flag usecTimestampNowIsInitialized;
-static QElapsedTimer timestampTimer;
-
quint64 usecTimestampNow(bool wantDebug) {
- std::call_once(usecTimestampNowIsInitialized, [&] {
- TIME_REFERENCE = QDateTime::currentMSecsSinceEpoch() * USECS_PER_MSEC; // ms to usec
- timestampTimer.start();
- });
-
- quint64 now;
- quint64 nsecsElapsed = timestampTimer.nsecsElapsed();
- quint64 usecsElapsed = nsecsElapsed / NSECS_PER_USEC; // nsec to usec
-
- // QElapsedTimer may not advance if the CPU has gone to sleep. In which case it
- // will begin to deviate from real time. We detect that here, and reset if necessary
- quint64 msecsCurrentTime = QDateTime::currentMSecsSinceEpoch();
- quint64 msecsEstimate = (TIME_REFERENCE + usecsElapsed) / USECS_PER_MSEC; // usecs to msecs
- int possibleSkew = msecsEstimate - msecsCurrentTime;
- const int TOLERANCE = 10 * MSECS_PER_SECOND; // up to 10 seconds of skew is tolerated
- if (abs(possibleSkew) > TOLERANCE) {
- // reset our TIME_REFERENCE and timer
- TIME_REFERENCE = QDateTime::currentMSecsSinceEpoch() * USECS_PER_MSEC; // ms to usec
- timestampTimer.restart();
- now = TIME_REFERENCE + ::usecTimestampNowAdjust;
-
- if (wantDebug) {
- qCDebug(shared) << "usecTimestampNow() - resetting QElapsedTimer. ";
- qCDebug(shared) << " msecsCurrentTime:" << msecsCurrentTime;
- qCDebug(shared) << " msecsEstimate:" << msecsEstimate;
- qCDebug(shared) << " possibleSkew:" << possibleSkew;
- qCDebug(shared) << " TOLERANCE:" << TOLERANCE;
-
- qCDebug(shared) << " nsecsElapsed:" << nsecsElapsed;
- qCDebug(shared) << " usecsElapsed:" << usecsElapsed;
-
- QDateTime currentLocalTime = QDateTime::currentDateTime();
-
- quint64 msecsNow = now / 1000; // usecs to msecs
- QDateTime nowAsString;
- nowAsString.setMSecsSinceEpoch(msecsNow);
-
- qCDebug(shared) << " now:" << now;
- qCDebug(shared) << " msecsNow:" << msecsNow;
-
- qCDebug(shared) << " nowAsString:" << nowAsString.toString("yyyy-MM-dd hh:mm:ss.zzz");
- qCDebug(shared) << " currentLocalTime:" << currentLocalTime.toString("yyyy-MM-dd hh:mm:ss.zzz");
- }
- } else {
- now = TIME_REFERENCE + usecsElapsed + ::usecTimestampNowAdjust;
- }
-
- if (wantDebug) {
- QDateTime currentLocalTime = QDateTime::currentDateTime();
-
- quint64 msecsNow = now / 1000; // usecs to msecs
- QDateTime nowAsString;
- nowAsString.setMSecsSinceEpoch(msecsNow);
-
- quint64 msecsTimeReference = TIME_REFERENCE / 1000; // usecs to msecs
- QDateTime timeReferenceAsString;
- timeReferenceAsString.setMSecsSinceEpoch(msecsTimeReference);
-
- qCDebug(shared) << "usecTimestampNow() - details... ";
- qCDebug(shared) << " TIME_REFERENCE:" << TIME_REFERENCE;
- qCDebug(shared) << " timeReferenceAsString:" << timeReferenceAsString.toString("yyyy-MM-dd hh:mm:ss.zzz");
- qCDebug(shared) << " usecTimestampNowAdjust:" << usecTimestampNowAdjust;
- qCDebug(shared) << " nsecsElapsed:" << nsecsElapsed;
- qCDebug(shared) << " usecsElapsed:" << usecsElapsed;
- qCDebug(shared) << " now:" << now;
- qCDebug(shared) << " msecsNow:" << msecsNow;
- qCDebug(shared) << " nowAsString:" << nowAsString.toString("yyyy-MM-dd hh:mm:ss.zzz");
- qCDebug(shared) << " currentLocalTime:" << currentLocalTime.toString("yyyy-MM-dd hh:mm:ss.zzz");
- }
-
- return now;
+ using namespace std::chrono;
+ static const auto unixEpoch = system_clock::from_time_t(0);
+ return duration_cast(system_clock::now() - unixEpoch).count() + usecTimestampNowAdjust;
}
float secTimestampNow() {
diff --git a/scripts/system/commerce/wallet.js b/scripts/system/commerce/wallet.js
index 89bcc97444..5b91afea33 100644
--- a/scripts/system/commerce/wallet.js
+++ b/scripts/system/commerce/wallet.js
@@ -529,6 +529,35 @@ function isReturnedDataEmpty(data) {
return historyArray.length === 0;
}
+var DEVELOPER_MENU = "Developer";
+var MARKETPLACE_ITEM_TESTER_LABEL = "Marketplace Item Tester";
+var MARKETPLACE_ITEM_TESTER_QML_SOURCE = "hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml";
+function installMarketplaceItemTester() {
+ if (!Menu.menuExists(DEVELOPER_MENU)) {
+ Menu.addMenu(DEVELOPER_MENU);
+ }
+ if (!Menu.menuItemExists(DEVELOPER_MENU, MARKETPLACE_ITEM_TESTER_LABEL)) {
+ Menu.addMenuItem({
+ menuName: DEVELOPER_MENU,
+ menuItemName: MARKETPLACE_ITEM_TESTER_LABEL,
+ isCheckable: false
+ });
+ }
+
+ Menu.menuItemEvent.connect(function (menuItem) {
+ if (menuItem === MARKETPLACE_ITEM_TESTER_LABEL) {
+ ui.open(MARKETPLACE_ITEM_TESTER_QML_SOURCE);
+ }
+ });
+}
+
+function uninstallMarketplaceItemTester() {
+ if (Menu.menuExists(DEVELOPER_MENU) &&
+ Menu.menuItemExists(DEVELOPER_MENU, MARKETPLACE_ITEM_TESTER_LABEL)
+ ) {
+ Menu.removeMenuItem(DEVELOPER_MENU, MARKETPLACE_ITEM_TESTER_LABEL);
+ }
+}
var BUTTON_NAME = "WALLET";
var WALLET_QML_SOURCE = "hifi/commerce/wallet/Wallet.qml";
@@ -549,7 +578,9 @@ function startup() {
notificationPollCaresAboutSince: true
});
GlobalServices.myUsernameChanged.connect(onUsernameChanged);
+ installMarketplaceItemTester();
}
+
var isUpdateOverlaysWired = false;
function off() {
Users.usernameFromIDReply.disconnect(usernameFromIDReply);
@@ -564,9 +595,11 @@ function off() {
}
removeOverlays();
}
+
function shutdown() {
GlobalServices.myUsernameChanged.disconnect(onUsernameChanged);
deleteSendMoneyParticleEffect();
+ uninstallMarketplaceItemTester();
off();
}
diff --git a/scripts/system/controllers/controllerModules/highlightNearbyEntities.js b/scripts/system/controllers/controllerModules/highlightNearbyEntities.js
index 3a33082f64..bc09ebee7a 100644
--- a/scripts/system/controllers/controllerModules/highlightNearbyEntities.js
+++ b/scripts/system/controllers/controllerModules/highlightNearbyEntities.js
@@ -37,7 +37,7 @@
this.highlightedEntities = [];
this.parameters = dispatcherUtils.makeDispatcherModuleParameters(
- 120,
+ 480,
this.hand === dispatcherUtils.RIGHT_HAND ? ["rightHand"] : ["leftHand"],
[],
100);
diff --git a/scripts/system/controllers/controllerModules/inEditMode.js b/scripts/system/controllers/controllerModules/inEditMode.js
index 2bdd89f141..d590545532 100644
--- a/scripts/system/controllers/controllerModules/inEditMode.js
+++ b/scripts/system/controllers/controllerModules/inEditMode.js
@@ -29,7 +29,7 @@ Script.include("/~/system/libraries/utils.js");
this.reticleMaxY;
this.parameters = makeDispatcherModuleParameters(
- 200,
+ 160,
this.hand === RIGHT_HAND ? ["rightHand", "rightHandEquip", "rightHandTrigger"] : ["leftHand", "leftHandEquip", "leftHandTrigger"],
[],
100,
diff --git a/scripts/system/controllers/controllerModules/inVREditMode.js b/scripts/system/controllers/controllerModules/inVREditMode.js
index 02863cf935..7b78d5e1c4 100644
--- a/scripts/system/controllers/controllerModules/inVREditMode.js
+++ b/scripts/system/controllers/controllerModules/inVREditMode.js
@@ -21,7 +21,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
this.disableModules = false;
var NO_HAND_LASER = -1; // Invalid hand parameter so that default laser is not displayed.
this.parameters = makeDispatcherModuleParameters(
- 240, // Not too high otherwise the tablet laser doesn't work.
+ 200, // Not too high otherwise the tablet laser doesn't work.
this.hand === RIGHT_HAND
? ["rightHand", "rightHandEquip", "rightHandTrigger"]
: ["leftHand", "leftHandEquip", "leftHandTrigger"],
diff --git a/scripts/system/controllers/controllerModules/nearActionGrabEntity.js b/scripts/system/controllers/controllerModules/nearActionGrabEntity.js
index 27c1b458b8..a8de76aebd 100644
--- a/scripts/system/controllers/controllerModules/nearActionGrabEntity.js
+++ b/scripts/system/controllers/controllerModules/nearActionGrabEntity.js
@@ -26,7 +26,7 @@ Script.include("/~/system/libraries/cloneEntityUtils.js");
this.hapticTargetID = null;
this.parameters = makeDispatcherModuleParameters(
- 140,
+ 500,
this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"],
[],
100);
diff --git a/scripts/system/controllers/controllerModules/nearGrabHyperLinkEntity.js b/scripts/system/controllers/controllerModules/nearGrabHyperLinkEntity.js
index 366fcd3032..962ae89bb9 100644
--- a/scripts/system/controllers/controllerModules/nearGrabHyperLinkEntity.js
+++ b/scripts/system/controllers/controllerModules/nearGrabHyperLinkEntity.js
@@ -21,7 +21,7 @@
this.hyperlink = "";
this.parameters = makeDispatcherModuleParameters(
- 125,
+ 485,
this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"],
[],
100);
diff --git a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js
index f805dbf60e..cc88371441 100644
--- a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js
+++ b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js
@@ -57,7 +57,7 @@ Script.include("/~/system/libraries/controllers.js");
this.cloneAllowed = true;
this.parameters = makeDispatcherModuleParameters(
- 140,
+ 500,
this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"],
[],
100);
diff --git a/scripts/system/controllers/controllerModules/nearTrigger.js b/scripts/system/controllers/controllerModules/nearTrigger.js
index f1126dedc3..6a9cd9fbcd 100644
--- a/scripts/system/controllers/controllerModules/nearTrigger.js
+++ b/scripts/system/controllers/controllerModules/nearTrigger.js
@@ -29,7 +29,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
this.startSent = false;
this.parameters = makeDispatcherModuleParameters(
- 120,
+ 480,
this.hand === RIGHT_HAND ? ["rightHandTrigger", "rightHand"] : ["leftHandTrigger", "leftHand"],
[],
100);
diff --git a/scripts/system/controllers/controllerModules/webSurfaceLaserInput.js b/scripts/system/controllers/controllerModules/webSurfaceLaserInput.js
index 4e36355621..2412e2fa1c 100644
--- a/scripts/system/controllers/controllerModules/webSurfaceLaserInput.js
+++ b/scripts/system/controllers/controllerModules/webSurfaceLaserInput.js
@@ -121,7 +121,7 @@ Script.include("/~/system/libraries/controllers.js");
controllerData.triggerValues[this.otherHand] <= TRIGGER_OFF_VALUE;
var allowThisModule = !otherModuleRunning || isTriggerPressed;
- if (allowThisModule && this.isPointingAtTriggerable(controllerData, isTriggerPressed, false)) {
+ if ((allowThisModule && this.isPointingAtTriggerable(controllerData, isTriggerPressed, false)) && !this.grabModuleWantsNearbyOverlay(controllerData)) {
this.updateAllwaysOn();
if (isTriggerPressed) {
this.dominantHandOverride = true; // Override dominant hand.
diff --git a/scripts/system/edit.js b/scripts/system/edit.js
index 88ae2852be..b1f01e1ea9 100644
--- a/scripts/system/edit.js
+++ b/scripts/system/edit.js
@@ -2134,9 +2134,7 @@ var PropertiesTool = function (opts) {
var onWebEventReceived = function(data) {
try {
data = JSON.parse(data);
- }
- catch(e) {
- print('Edit.js received web event that was not valid json.');
+ } catch(e) {
return;
}
var i, properties, dY, diff, newPosition;
diff --git a/scripts/system/html/js/entityList.js b/scripts/system/html/js/entityList.js
index 23de49b613..7cc5937536 100644
--- a/scripts/system/html/js/entityList.js
+++ b/scripts/system/html/js/entityList.js
@@ -164,7 +164,10 @@ function loaded() {
selectedEntities.forEach(function(entityID) {
if (selection.indexOf(entityID) === -1) {
- entitiesByID[entityID].el.className = '';
+ let entity = entitiesByID[entityID];
+ if (entity !== undefined) {
+ entity.el.className = '';
+ }
}
});
@@ -388,15 +391,18 @@ function loaded() {
let notFound = false;
selectedEntities.forEach(function(id) {
- entitiesByID[id].el.className = '';
+ let entity = entitiesByID[id];
+ if (entity !== undefined) {
+ entity.el.className = '';
+ }
});
selectedEntities = [];
for (let i = 0; i < selectedIDs.length; i++) {
let id = selectedIDs[i];
selectedEntities.push(id);
- if (id in entitiesByID) {
- let entity = entitiesByID[id];
+ let entity = entitiesByID[id];
+ if (entity !== undefined) {
entity.el.className = 'selected';
} else {
notFound = true;
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)) {
diff --git a/scripts/system/libraries/gridTool.js b/scripts/system/libraries/gridTool.js
index 3a114f23c7..6f62742e8f 100644
--- a/scripts/system/libraries/gridTool.js
+++ b/scripts/system/libraries/gridTool.js
@@ -267,7 +267,6 @@ GridTool = function(opts) {
try {
data = JSON.parse(data);
} catch (e) {
- print("gridTool.js: Error parsing JSON: " + e.name + " data " + data);
return;
}
diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js
index 27fa929390..85cd499d20 100644
--- a/scripts/system/marketplaces/marketplaces.js
+++ b/scripts/system/marketplaces/marketplaces.js
@@ -20,13 +20,14 @@ var AppUi = Script.require('appUi');
Script.include("/~/system/libraries/gridTool.js");
Script.include("/~/system/libraries/connectionUtils.js");
-var METAVERSE_SERVER_URL = Account.metaverseServerURL;
-var MARKETPLACES_URL = Script.resolvePath("../html/marketplaces.html");
-var MARKETPLACES_INJECT_SCRIPT_URL = Script.resolvePath("../html/js/marketplacesInject.js");
var MARKETPLACE_CHECKOUT_QML_PATH = "hifi/commerce/checkout/Checkout.qml";
+var MARKETPLACE_INSPECTIONCERTIFICATE_QML_PATH = "hifi/commerce/inspectionCertificate/InspectionCertificate.qml";
+var MARKETPLACE_ITEM_TESTER_QML_PATH = "hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml";
var MARKETPLACE_PURCHASES_QML_PATH = "hifi/commerce/purchases/Purchases.qml";
var MARKETPLACE_WALLET_QML_PATH = "hifi/commerce/wallet/Wallet.qml";
-var MARKETPLACE_INSPECTIONCERTIFICATE_QML_PATH = "hifi/commerce/inspectionCertificate/InspectionCertificate.qml";
+var MARKETPLACES_INJECT_SCRIPT_URL = Script.resolvePath("../html/js/marketplacesInject.js");
+var MARKETPLACES_URL = Script.resolvePath("../html/marketplaces.html");
+var METAVERSE_SERVER_URL = Account.metaverseServerURL;
var REZZING_SOUND = SoundCache.getSound(Script.resolvePath("../assets/sounds/rezzing.wav"));
// Event bridge messages.
@@ -755,7 +756,7 @@ function deleteSendAssetParticleEffect() {
}
sendAssetRecipient = null;
}
-
+
var savedDisablePreviewOption = Menu.isOptionChecked("Disable Preview");
var UI_FADE_TIMEOUT_MS = 150;
function maybeEnableHMDPreview() {
@@ -767,6 +768,13 @@ function maybeEnableHMDPreview() {
}, UI_FADE_TIMEOUT_MS);
}
+var resourceObjectsInTest = [];
+function signalNewResourceObjectInTest(resourceObject) {
+ ui.tablet.sendToQml({
+ method: "newResourceObjectInTest",
+ resourceObject: resourceObject });
+}
+
var onQmlMessageReceived = function onQmlMessageReceived(message) {
if (message.messageSrc === "HTML") {
return;
@@ -816,8 +824,20 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) {
break;
case 'checkout_rezClicked':
case 'purchases_rezClicked':
+ case 'tester_rezClicked':
rezEntity(message.itemHref, message.itemType);
break;
+ case 'tester_newResourceObject':
+ var resourceObject = message.resourceObject;
+ resourceObjectsInTest[resourceObject.id] = resourceObject;
+ signalNewResourceObjectInTest(resourceObject);
+ break;
+ case 'tester_updateResourceObjectAssetType':
+ resourceObjectsInTest[message.objectId].assetType = message.assetType;
+ break;
+ case 'tester_deleteResourceObject':
+ delete resourceObjectsInTest[message.objectId];
+ break;
case 'header_marketplaceImageClicked':
case 'purchases_backClicked':
ui.open(message.referrerURL, MARKETPLACES_INJECT_SCRIPT_URL);
@@ -840,10 +860,6 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) {
openLoginWindow();
break;
case 'disableHmdPreview':
- if (!savedDisablePreviewOption) {
- savedDisablePreviewOption = Menu.isOptionChecked("Disable Preview");
- }
-
if (!savedDisablePreviewOption) {
DesktopPreviewProvider.setPreviewDisabledReason("SECURE_SCREEN");
Menu.setIsOptionChecked("Disable Preview", true);
@@ -960,34 +976,65 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) {
}
};
+function pushResourceObjectsInTest() {
+ var maxObjectId = -1;
+ for (var objectId in resourceObjectsInTest) {
+ signalNewResourceObjectInTest(resourceObjectsInTest[objectId]);
+ maxObjectId = (maxObjectId < objectId) ? parseInt(objectId) : maxObjectId;
+ }
+ // N.B. Thinking about removing the following sendToQml? Be sure
+ // that the marketplace item tester QML has heard from us, at least
+ // so that it can indicate to the user that all of the resoruce
+ // objects in test have been transmitted to it.
+ ui.tablet.sendToQml({ method: "nextObjectIdInTest", id: maxObjectId + 1 });
+}
+
// Function Name: onTabletScreenChanged()
//
// Description:
// -Called when the TabletScriptingInterface::screenChanged() signal is emitted. The "type" argument can be either the string
// value of "Home", "Web", "Menu", "QML", or "Closed". The "url" argument is only valid for Web and QML.
-var onMarketplaceScreen = false;
-var onWalletScreen = false;
+
var onCommerceScreen = false;
var onInspectionCertificateScreen = false;
+var onMarketplaceItemTesterScreen = false;
+var onMarketplaceScreen = false;
+var onWalletScreen = false;
var onTabletScreenChanged = function onTabletScreenChanged(type, url) {
ui.setCurrentVisibleScreenMetadata(type, url);
onMarketplaceScreen = type === "Web" && url.indexOf(MARKETPLACE_URL) !== -1;
onInspectionCertificateScreen = type === "QML" && url.indexOf(MARKETPLACE_INSPECTIONCERTIFICATE_QML_PATH) !== -1;
var onWalletScreenNow = url.indexOf(MARKETPLACE_WALLET_QML_PATH) !== -1;
- var onCommerceScreenNow = type === "QML" &&
- (url.indexOf(MARKETPLACE_CHECKOUT_QML_PATH) !== -1 || url === MARKETPLACE_PURCHASES_QML_PATH ||
- onInspectionCertificateScreen);
+ var onCommerceScreenNow = type === "QML" && (
+ url.indexOf(MARKETPLACE_CHECKOUT_QML_PATH) !== -1 ||
+ url === MARKETPLACE_PURCHASES_QML_PATH ||
+ url.indexOf(MARKETPLACE_INSPECTIONCERTIFICATE_QML_PATH) !== -1);
+ var onMarketplaceItemTesterScreenNow = (
+ url.indexOf(MARKETPLACE_ITEM_TESTER_QML_PATH) !== -1 ||
+ url === MARKETPLACE_ITEM_TESTER_QML_PATH);
- // exiting wallet or commerce screen
- if ((!onWalletScreenNow && onWalletScreen) || (!onCommerceScreenNow && onCommerceScreen)) {
+ if ((!onWalletScreenNow && onWalletScreen) ||
+ (!onCommerceScreenNow && onCommerceScreen) ||
+ (!onMarketplaceItemTesterScreenNow && onMarketplaceScreen)
+ ) {
+ // exiting wallet, commerce, or marketplace item tester screen
maybeEnableHMDPreview();
}
onCommerceScreen = onCommerceScreenNow;
onWalletScreen = onWalletScreenNow;
- wireQmlEventBridge(onMarketplaceScreen || onCommerceScreen || onWalletScreen);
+ onMarketplaceItemTesterScreen = onMarketplaceItemTesterScreenNow;
+
+ wireQmlEventBridge(
+ onMarketplaceScreen ||
+ onCommerceScreen ||
+ onWalletScreen ||
+ onMarketplaceItemTesterScreen);
if (url === MARKETPLACE_PURCHASES_QML_PATH) {
+ // FIXME? Is there a race condition here in which the event
+ // bridge may not be up yet? If so, Script.setTimeout(..., 750)
+ // may help avoid the condition.
ui.tablet.sendToQml({
method: 'updatePurchases',
referrerURL: referrerURL,
@@ -1024,6 +1071,14 @@ var onTabletScreenChanged = function onTabletScreenChanged(type, url) {
});
off();
}
+
+ if (onMarketplaceItemTesterScreen) {
+ // Why setTimeout? The QML event bridge, wired above, requires a
+ // variable amount of time to come up, in practice less than
+ // 750ms.
+ Script.setTimeout(pushResourceObjectsInTest, 750);
+ }
+
console.debug(ui.buttonName + " app reports: Tablet screen changed.\nNew screen type: " + type +
"\nNew screen URL: " + url + "\nCurrent app open status: " + ui.isOpen + "\n");
};