Merge branch 'master' of http://github.com/highfidelity/hifi into ao
|
@ -133,6 +133,10 @@ dependencies {
|
|||
|
||||
implementation 'com.android.support.constraint:constraint-layout:1.0.2'
|
||||
implementation 'com.android.support:design:26.1.0'
|
||||
compile 'com.android.support:support-v4:26.1.0'
|
||||
compile 'com.android.support:appcompat-v7:26.1.0'
|
||||
compile 'com.android.support:support-vector-drawable:26.1.0'
|
||||
|
||||
implementation 'com.android.support:appcompat-v7:26.1.0'
|
||||
compile 'com.android.support:recyclerview-v7:26.1.0'
|
||||
compile 'com.android.support:cardview-v7:26.1.0'
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
|
||||
<uses-feature android:name="android.hardware.sensor.accelerometer" android:required="true"/>
|
||||
<uses-feature android:name="android.hardware.sensor.gyroscope" android:required="true"/>
|
||||
|
||||
|
@ -75,6 +76,15 @@
|
|||
android:enabled="true"
|
||||
android:exported="false"
|
||||
android:process=":breakpad_uploader"/>
|
||||
|
||||
<receiver
|
||||
android:name=".receiver.HeadsetStateReceiver"
|
||||
android:enabled="true"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.HEADSET_PLUG" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
</application>
|
||||
|
||||
<uses-feature android:name="android.software.vr.mode" android:required="true"/>
|
||||
|
|
|
@ -156,7 +156,7 @@ JNIEXPORT void Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeOnCrea
|
|||
JavaVM* jvm;
|
||||
env->GetJavaVM(&jvm);
|
||||
|
||||
QObject::connect(&AndroidHelper::instance(), &AndroidHelper::androidActivityRequested, [jvm](const QString& a, const bool backToScene, QList<QString> args) {
|
||||
QObject::connect(&AndroidHelper::instance(), &AndroidHelper::androidActivityRequested, [jvm](const QString& a, const bool backToScene, QMap<QString, QString> args) {
|
||||
JNIEnv* myNewEnv;
|
||||
JavaVMAttachArgs jvmArgs;
|
||||
jvmArgs.version = JNI_VERSION_1_6; // choose your JNI version
|
||||
|
@ -182,9 +182,11 @@ JNIEXPORT void Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeOnCrea
|
|||
jmethodID mapClassConstructor = myNewEnv->GetMethodID(hashMapClass, "<init>", "()V");
|
||||
jobject hashmap = myNewEnv->NewObject(hashMapClass, mapClassConstructor);
|
||||
jmethodID mapClassPut = myNewEnv->GetMethodID(hashMapClass, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
|
||||
for (const QString& arg: args) {
|
||||
QAndroidJniObject jArg = QAndroidJniObject::fromString(arg);
|
||||
myNewEnv->CallObjectMethod(hashmap, mapClassPut, QAndroidJniObject::fromString("url").object<jstring>(), jArg.object<jstring>());
|
||||
QMap<QString, QString>::iterator i;
|
||||
for (i = args.begin(); i != args.end(); ++i) {
|
||||
QAndroidJniObject jKey = QAndroidJniObject::fromString(i.key());
|
||||
QAndroidJniObject jValue = QAndroidJniObject::fromString(i.value());
|
||||
myNewEnv->CallObjectMethod(hashmap, mapClassPut, jKey.object<jstring>(), jValue.object<jstring>());
|
||||
}
|
||||
__interfaceActivity.callMethod<void>("openAndroidActivity", "(Ljava/lang/String;ZLjava/util/HashMap;)V", string.object<jstring>(), jBackToScene, hashmap);
|
||||
if (attachedHere) {
|
||||
|
@ -255,6 +257,16 @@ JNIEXPORT jstring JNICALL Java_io_highfidelity_hifiinterface_fragment_HomeFragme
|
|||
return env->NewStringUTF(lastLocation.toString().toLatin1().data());
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_io_highfidelity_hifiinterface_fragment_LoginFragment_nativeCancelLogin(JNIEnv *env, jobject instance) {
|
||||
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
|
||||
QObject::disconnect(accountManager.data(), &AccountManager::loginComplete, nullptr, nullptr);
|
||||
QObject::disconnect(accountManager.data(), &AccountManager::loginFailed, nullptr, nullptr);
|
||||
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_io_highfidelity_hifiinterface_fragment_LoginFragment_nativeLogin(JNIEnv *env, jobject instance,
|
||||
jstring username_, jstring password_,
|
||||
|
@ -273,17 +285,23 @@ Java_io_highfidelity_hifiinterface_fragment_LoginFragment_nativeLogin(JNIEnv *en
|
|||
|
||||
QObject::connect(accountManager.data(), &AccountManager::loginComplete, [](const QUrl& authURL) {
|
||||
jboolean jSuccess = (jboolean) true;
|
||||
__loginCompletedListener.callMethod<void>("handleLoginCompleted", "(Z)V", jSuccess);
|
||||
if (__loginCompletedListener.isValid()) {
|
||||
__loginCompletedListener.callMethod<void>("handleLoginCompleted", "(Z)V", jSuccess);
|
||||
}
|
||||
});
|
||||
|
||||
QObject::connect(accountManager.data(), &AccountManager::loginFailed, []() {
|
||||
jboolean jSuccess = (jboolean) false;
|
||||
__loginCompletedListener.callMethod<void>("handleLoginCompleted", "(Z)V", jSuccess);
|
||||
if (__loginCompletedListener.isValid()) {
|
||||
__loginCompletedListener.callMethod<void>("handleLoginCompleted", "(Z)V", jSuccess);
|
||||
}
|
||||
});
|
||||
|
||||
QObject::connect(accountManager.data(), &AccountManager::usernameChanged, [](const QString& username) {
|
||||
QAndroidJniObject string = QAndroidJniObject::fromString(username);
|
||||
__usernameChangedListener.callMethod<void>("handleUsernameChanged", "(Ljava/lang/String;)V", string.object<jstring>());
|
||||
if (__usernameChangedListener.isValid()) {
|
||||
__usernameChangedListener.callMethod<void>("handleUsernameChanged", "(Ljava/lang/String;)V", string.object<jstring>());
|
||||
}
|
||||
});
|
||||
|
||||
QMetaObject::invokeMethod(accountManager.data(), "requestAccessToken",
|
||||
|
@ -355,5 +373,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<bool> 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<bool> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
@ -38,8 +39,10 @@ import java.lang.reflect.Field;
|
|||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
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 +58,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 +155,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 +167,7 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW
|
|||
} else {
|
||||
nativeEnterBackground();
|
||||
}
|
||||
unregisterReceiver(headsetStateReceiver);
|
||||
//gvrApi.pauseTracking();
|
||||
}
|
||||
|
||||
|
@ -183,6 +190,7 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW
|
|||
nativeEnterForeground();
|
||||
surfacesWorkaround();
|
||||
keepInterfaceRunning = false;
|
||||
registerReceiver(headsetStateReceiver, new IntentFilter(Intent.ACTION_HEADSET_PLUG));
|
||||
//gvrApi.resumeTracking();
|
||||
}
|
||||
|
||||
|
@ -296,14 +304,22 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW
|
|||
switch (activityName) {
|
||||
case "Home":
|
||||
case "Privacy Policy":
|
||||
case "Login": {
|
||||
nativeBeforeEnterBackground();
|
||||
Intent intent = new Intent(this, MainActivity.class);
|
||||
intent.putExtra(MainActivity.EXTRA_FRAGMENT, activityName);
|
||||
intent.putExtra(MainActivity.EXTRA_BACK_TO_SCENE, backToScene);
|
||||
startActivity(intent);
|
||||
break;
|
||||
}
|
||||
case "Login":
|
||||
nativeBeforeEnterBackground();
|
||||
Intent loginIntent = new Intent(this, MainActivity.class);
|
||||
loginIntent.putExtra(MainActivity.EXTRA_FRAGMENT, activityName);
|
||||
loginIntent.putExtra(MainActivity.EXTRA_BACK_TO_SCENE, backToScene);
|
||||
if (args != null && args.containsKey(DOMAIN_URL)) {
|
||||
loginIntent.putExtra(DOMAIN_URL, (String) args.get(DOMAIN_URL));
|
||||
}
|
||||
startActivity(loginIntent);
|
||||
break;
|
||||
case "WebView":
|
||||
runOnUiThread(() -> {
|
||||
webSlidingDrawer.setVisibility(View.VISIBLE);
|
||||
|
|
|
@ -29,10 +29,14 @@ import android.widget.TextView;
|
|||
import com.squareup.picasso.Callback;
|
||||
import com.squareup.picasso.Picasso;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
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,
|
||||
|
@ -44,6 +48,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||
public static final String DEFAULT_FRAGMENT = "Home";
|
||||
public static final String EXTRA_FRAGMENT = "fragment";
|
||||
public static final String EXTRA_BACK_TO_SCENE = "backToScene";
|
||||
public static final String EXTRA_BACK_TO_URL = "url";
|
||||
|
||||
private String TAG = "HighFidelity";
|
||||
|
||||
|
@ -61,6 +66,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||
private MenuItem mPeopleMenuItem;
|
||||
|
||||
private boolean backToScene;
|
||||
private String backToUrl;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
|
@ -80,6 +86,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);
|
||||
|
@ -102,8 +110,17 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||
loadFragment(DEFAULT_FRAGMENT);
|
||||
}
|
||||
|
||||
if (getIntent().hasExtra(EXTRA_BACK_TO_SCENE)) {
|
||||
backToScene = getIntent().getBooleanExtra(EXTRA_BACK_TO_SCENE, false);
|
||||
backToScene = getIntent().getBooleanExtra(EXTRA_BACK_TO_SCENE, false);
|
||||
backToUrl = getIntent().getStringExtra(EXTRA_BACK_TO_URL);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -151,6 +168,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 +265,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;
|
||||
}
|
||||
|
@ -278,7 +305,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||
}
|
||||
|
||||
private void goToLastLocation() {
|
||||
goToDomain("");
|
||||
goToDomain(backToUrl != null? backToUrl : "");
|
||||
}
|
||||
|
||||
private void goToDomain(String domainUrl) {
|
||||
|
|
|
@ -4,12 +4,10 @@ import android.app.Activity;
|
|||
import android.app.Fragment;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
@ -19,8 +17,13 @@ import android.widget.Button;
|
|||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.qtproject.qt5.android.QtNative;
|
||||
|
||||
import io.highfidelity.hifiinterface.R;
|
||||
|
||||
import static org.qtproject.qt5.android.QtActivityDelegate.ApplicationActive;
|
||||
import static org.qtproject.qt5.android.QtActivityDelegate.ApplicationInactive;
|
||||
|
||||
public class LoginFragment extends Fragment {
|
||||
|
||||
private EditText mUsername;
|
||||
|
@ -32,6 +35,7 @@ public class LoginFragment extends Fragment {
|
|||
private ProgressDialog mDialog;
|
||||
|
||||
public native void nativeLogin(String username, String password, Activity usernameChangedListener);
|
||||
public native void nativeCancelLogin();
|
||||
|
||||
private LoginFragment.OnLoginInteractionListener mListener;
|
||||
|
||||
|
@ -55,44 +59,6 @@ public class LoginFragment extends Fragment {
|
|||
mLoginButton = rootView.findViewById(R.id.loginButton);
|
||||
mForgotPassword = rootView.findViewById(R.id.forgotPassword);
|
||||
|
||||
mUsername.addTextChangedListener(new TextWatcher() {
|
||||
boolean ignoreNextChange = false;
|
||||
boolean hadBlankSpace = false;
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence charSequence, int start, int count, int after) {
|
||||
hadBlankSpace = charSequence.length() > 0 && charSequence.charAt(charSequence.length()-1) == ' ';
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence charSequence, int start, int count, int after) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable editable) {
|
||||
if (!ignoreNextChange) {
|
||||
ignoreNextChange = true;
|
||||
boolean spaceFound = false;
|
||||
for (int i = 0; i < editable.length(); i++) {
|
||||
if (editable.charAt(i) == ' ') {
|
||||
spaceFound=true;
|
||||
editable.delete(i, i + 1);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
if (hadBlankSpace && !spaceFound && editable.length() > 0) {
|
||||
editable.delete(editable.length()-1, editable.length());
|
||||
}
|
||||
|
||||
editable.append(' ');
|
||||
ignoreNextChange = false;
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
mLoginButton.setOnClickListener(view -> login());
|
||||
|
||||
mForgotPassword.setOnClickListener(view -> forgotPassword());
|
||||
|
@ -125,10 +91,19 @@ public class LoginFragment extends Fragment {
|
|||
mListener = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
// This hack intends to keep Qt threads running even after the app comes from background
|
||||
QtNative.setApplicationState(ApplicationActive);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
cancelActivityIndicator();
|
||||
// Leave the Qt app paused
|
||||
QtNative.setApplicationState(ApplicationInactive);
|
||||
hideKeyboard();
|
||||
}
|
||||
|
||||
|
@ -164,7 +139,15 @@ public class LoginFragment extends Fragment {
|
|||
mDialog = new ProgressDialog(getContext());
|
||||
}
|
||||
mDialog.setMessage(getString(R.string.logging_in));
|
||||
mDialog.setCancelable(false);
|
||||
mDialog.setCancelable(true);
|
||||
mDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialogInterface) {
|
||||
nativeCancelLogin();
|
||||
cancelActivityIndicator();
|
||||
mLoginButton.setEnabled(true);
|
||||
}
|
||||
});
|
||||
mDialog.show();
|
||||
}
|
||||
|
||||
|
@ -184,7 +167,6 @@ public class LoginFragment extends Fragment {
|
|||
}
|
||||
|
||||
public void handleLoginCompleted(boolean success) {
|
||||
Log.d("[LOGIN]", "handleLoginCompleted " + success);
|
||||
getActivity().runOnUiThread(() -> {
|
||||
mLoginButton.setEnabled(true);
|
||||
cancelActivityIndicator();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
27
android/app/src/main/res/drawable/ic_eye_noshow.xml
Normal file
|
@ -0,0 +1,27 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="36dp"
|
||||
android:height="22dp"
|
||||
android:viewportWidth="36"
|
||||
android:viewportHeight="22">
|
||||
|
||||
<path
|
||||
android:fillColor="#3D3D3D"
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M3.59534,11.0156 C6.16042,13.4128,9.65987,15.5898,13.6042,16.1774 C17.686,16.7856,22.4164,15.7196,27.3057,11.0659 C22.0721,6.07309,17.0642,5.14115,12.9153,5.90073 C8.99427,6.61859,5.69298,8.87688,3.59534,11.0156 Z M12.455,3.27591 C17.7727,2.30235,23.9836,3.74895,30.1053,10.1333 L31,11.0664 L30.1053,11.9994 C24.3636,17.9875,18.4774,19.5983,13.2276,18.8161 C8.06048,18.0463,3.70384,14.9892,0.837069,11.9994 L0,11.1265 L0.778477,10.1986 C3.05338,7.48717,7.2318,4.23217,12.455,3.27591 Z" />
|
||||
<path
|
||||
android:fillColor="#3D3D3D"
|
||||
android:pathData="M15.6539,7.11119 C17.6719,7.11119,19.3078,8.81726,19.3078,10.9218 C19.3078,13.0263,17.6719,14.7324,15.6539,14.7324 C13.6359,14.7324,12,13.0263,12,10.9218 C12,8.81726,13.6359,7.11119,15.6539,7.11119 Z" />
|
||||
<!--path
|
||||
android:fillColor="#000000"
|
||||
android:strokeColor="#ffffff"
|
||||
android:strokeWidth="2.7"
|
||||
android:strokeLineCap="round"
|
||||
android:pathData="M27,2.90919 L8.90919,21" /-->
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:strokeColor="#3D3D3D"
|
||||
android:strokeWidth="3"
|
||||
android:strokeLineCap="round"
|
||||
android:pathData="M25,2.12132 L7.12132,20" />
|
||||
</vector>
|
15
android/app/src/main/res/drawable/ic_eye_show.xml
Normal file
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="36dp"
|
||||
android:height="16dp"
|
||||
android:viewportWidth="36"
|
||||
android:viewportHeight="16">
|
||||
|
||||
<path
|
||||
android:fillColor="#3D3D3D"
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M3.59534,8.01564 C6.16042,10.4128,9.65987,12.5898,13.6042,13.1774 C17.686,13.7856,22.4164,12.7196,27.3057,8.06585 C22.0721,3.07309,17.0642,2.14115,12.9153,2.90073 C8.99427,3.61859,5.69298,5.87688,3.59534,8.01564 Z M12.455,0.275915 C17.7727,-0.697651,23.9836,0.748949,30.1053,7.13329 L31,8.06636 L30.1053,8.99944 C24.3636,14.9875,18.4774,16.5983,13.2276,15.8161 C8.06048,15.0463,3.70384,11.9892,0.837069,8.99944 L0,8.12646 L0.778477,7.1986 C3.05338,4.48717,7.2318,1.23217,12.455,0.275915 Z" />
|
||||
<path
|
||||
android:fillColor="#3D3D3D"
|
||||
android:pathData="M15.6441,4.11118 C17.6621,4.11118,19.298,5.81725,19.298,7.92179 C19.298,10.0263,17.6621,11.7324,15.6441,11.7324 C13.6261,11.7324,11.9902,10.0263,11.9902,7.92179 C11.9902,5.81725,13.6261,4.11118,15.6441,4.11118 Z" />
|
||||
</vector>
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_checked="true" android:drawable="@drawable/ic_eye_noshow"/>
|
||||
<item android:drawable="@drawable/ic_eye_show" />
|
||||
</selector>
|
|
@ -41,38 +41,51 @@
|
|||
android:paddingTop="14dp"
|
||||
android:ems="10"
|
||||
android:fontFamily="@font/raleway"
|
||||
android:textSize="14sp"
|
||||
android:textSize="17sp"
|
||||
android:inputType="textEmailAddress"
|
||||
android:textStyle="italic"
|
||||
android:textColor="@color/editTextColor"
|
||||
android:textColorHint="@color/editTextColor"
|
||||
android:gravity="right|center_vertical"
|
||||
android:gravity="left|center_vertical"
|
||||
app:layout_constraintTop_toBottomOf="@id/header"
|
||||
android:layout_marginTop="70dp"
|
||||
android:hint="@string/username_or_email" />
|
||||
|
||||
<EditText
|
||||
|
||||
<android.support.design.widget.TextInputLayout
|
||||
android:id="@+id/passwordLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="46dp"
|
||||
android:layout_marginRight="46dp"
|
||||
app:passwordToggleTint="@color/showPasswordColor"
|
||||
app:passwordToggleEnabled="true"
|
||||
app:hintAnimationEnabled="false"
|
||||
app:passwordToggleDrawable="@drawable/selector_show_password"
|
||||
app:hintEnabled="false"
|
||||
app:layout_constraintTop_toBottomOf="@id/username"
|
||||
android:layout_marginTop="13dp"
|
||||
>
|
||||
<android.support.design.widget.TextInputEditText
|
||||
android:id="@+id/password"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="35dp"
|
||||
android:layout_marginLeft="46dp"
|
||||
android:layout_marginRight="46dp"
|
||||
android:background="@drawable/rounded_edit"
|
||||
android:padding="7dp"
|
||||
android:paddingRight="12dp"
|
||||
android:drawablePadding="55dp"
|
||||
android:paddingTop="14dp"
|
||||
android:drawableEnd="@drawable/ic_eye_noshow"
|
||||
android:ems="10"
|
||||
android:fontFamily="@font/raleway"
|
||||
android:textSize="14sp"
|
||||
android:inputType="textPassword"
|
||||
android:textSize="17sp"
|
||||
android:textStyle="italic"
|
||||
android:textColor="@color/editTextColor"
|
||||
android:textColorHint="@color/editTextColor"
|
||||
android:gravity="right|center_vertical"
|
||||
app:layout_constraintTop_toBottomOf="@id/username"
|
||||
android:gravity="left|center_vertical"
|
||||
android:imeOptions="actionDone"
|
||||
android:hint="@string/password"
|
||||
android:layout_marginTop="13dp"
|
||||
android:imeOptions="actionDone"/>
|
||||
android:inputType="textPassword" />
|
||||
</android.support.design.widget.TextInputLayout>
|
||||
|
||||
<Button
|
||||
android:id="@+id/loginButton"
|
||||
|
@ -90,7 +103,7 @@
|
|||
android:textAllCaps="false"
|
||||
android:textSize="15sp"
|
||||
app:layout_constraintRight_toRightOf="@id/username"
|
||||
app:layout_constraintTop_toBottomOf="@id/password"
|
||||
app:layout_constraintTop_toBottomOf="@id/passwordLayout"
|
||||
app:layout_goneMarginTop="4dp"/>
|
||||
|
||||
<TextView
|
||||
|
@ -102,7 +115,7 @@
|
|||
android:text="@string/forgot_password"
|
||||
android:textStyle="italic"
|
||||
android:paddingRight="10dp"
|
||||
app:layout_constraintLeft_toLeftOf="@id/password"
|
||||
app:layout_constraintLeft_toLeftOf="@id/passwordLayout"
|
||||
app:layout_constraintTop_toTopOf="@id/loginButton"
|
||||
app:layout_constraintRight_toLeftOf="@id/loginButton"
|
||||
android:textColor="@color/colorButton1"/>
|
||||
|
|
|
@ -9,4 +9,9 @@
|
|||
android:id="@+id/action_people"
|
||||
android:title="@string/people"
|
||||
/>
|
||||
<item
|
||||
android:id="@+id/action_debug_settings"
|
||||
android:title="@string/settings"
|
||||
android:visible="false"
|
||||
/>
|
||||
</menu>
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
<color name="colorAccent">#54D7FD</color>
|
||||
<color name="backgroundEditText">#E3E3E3</color>
|
||||
<color name="editTextColor">#575757</color>
|
||||
<color name="showPasswordColor">#3D3D3D</color>
|
||||
<color name="tabs">#1EB5EC</color>
|
||||
<color name="colorButton1">#00B4EF</color>
|
||||
<color name="backgroundDark">#333333</color>
|
||||
|
|
|
@ -10,8 +10,8 @@
|
|||
<string name="popular">POPULAR</string>
|
||||
<string name="bookmarks">BOOKMARKS</string>
|
||||
<string name="goto_url_hint">Type a domain url</string>
|
||||
<string name="username_or_email">Username or email\u00A0</string>
|
||||
<string name="password">Password\u00A0</string>
|
||||
<string name="username_or_email">Username or email</string>
|
||||
<string name="password">Password</string>
|
||||
<string name="login">Login</string>
|
||||
<string name="logout">Logout</string>
|
||||
<string name="forgot_password">Forgot password?\u00A0</string>
|
||||
|
@ -29,4 +29,9 @@
|
|||
<string name="tagFragmentLogin">tagFragmentLogin</string>
|
||||
<string name="tagFragmentPolicy">tagFragmentPolicy</string>
|
||||
<string name="tagFragmentPeople">tagFragmentPeople</string>
|
||||
<string name="tagSettings">tagSettings</string>
|
||||
<string name="settings">Settings</string>
|
||||
<string name="AEC">AEC</string>
|
||||
<string name="acoustic_echo_cancellation">Acoustic Echo Cancellation</string>
|
||||
<string name="settings_developer">Developer</string>
|
||||
</resources>
|
||||
|
|
11
android/app/src/main/res/xml/settings.xml
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<PreferenceCategory
|
||||
android:title="@string/settings_developer"
|
||||
android:key="pref_key_developer">
|
||||
<SwitchPreference
|
||||
android:key="aec"
|
||||
android:title="@string/AEC"
|
||||
android:summary="@string/acoustic_echo_cancellation" />
|
||||
</PreferenceCategory>
|
||||
</PreferenceScreen>
|
|
@ -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 = [
|
||||
|
|
|
@ -1110,7 +1110,36 @@ function moveTableRow(row, move_up) {
|
|||
}
|
||||
|
||||
// we need to fire a change event on one of the remaining inputs so that the sidebar badge is updated
|
||||
badgeForDifferences($(table))
|
||||
badgeForDifferences($(table));
|
||||
|
||||
// figure out which group this row is in
|
||||
var panelParentID = row.closest('.panel').attr('id');
|
||||
|
||||
// get the short name for the setting from the table
|
||||
var tableShortName = row.closest('table').data('short-name');
|
||||
|
||||
var changed = tableHasChanged(panelParentID, tableShortName);
|
||||
$(table).find('.' + Settings.DATA_ROW_CLASS).each(function(){
|
||||
var hiddenInput = $(this).find('td.' + Settings.DATA_COL_CLASS + ' input');
|
||||
if (changed) {
|
||||
hiddenInput.attr('data-changed', true);
|
||||
} else {
|
||||
hiddenInput.removeAttr('data-changed');
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function tableHasChanged(panelParentID, tableShortName) {
|
||||
// get a JSON representation of that section
|
||||
var panelSettingJSON = form2js(panelParentID, ".", false, cleanupFormValues, true)[panelParentID][tableShortName]
|
||||
if (Settings.initialValues[panelParentID]) {
|
||||
var initialPanelSettingJSON = Settings.initialValues[panelParentID][tableShortName]
|
||||
} else {
|
||||
var initialPanelSettingJSON = {};
|
||||
}
|
||||
|
||||
return !_.isEqual(panelSettingJSON, initialPanelSettingJSON);
|
||||
}
|
||||
|
||||
function updateDataChangedForSiblingRows(row, forceTrue) {
|
||||
|
@ -1123,16 +1152,8 @@ function updateDataChangedForSiblingRows(row, forceTrue) {
|
|||
// get the short name for the setting from the table
|
||||
var tableShortName = row.closest('table').data('short-name')
|
||||
|
||||
// get a JSON representation of that section
|
||||
var panelSettingJSON = form2js(panelParentID, ".", false, cleanupFormValues, true)[panelParentID][tableShortName]
|
||||
if (Settings.initialValues[panelParentID]) {
|
||||
var initialPanelSettingJSON = Settings.initialValues[panelParentID][tableShortName]
|
||||
} else {
|
||||
var initialPanelSettingJSON = {};
|
||||
}
|
||||
|
||||
// if they are equal, we don't need data-changed
|
||||
isTrue = !_.isEqual(panelSettingJSON, initialPanelSettingJSON)
|
||||
isTrue = tableHasChanged(panelParentID, tableShortName);
|
||||
} else {
|
||||
isTrue = true
|
||||
}
|
||||
|
@ -1140,9 +1161,9 @@ function updateDataChangedForSiblingRows(row, forceTrue) {
|
|||
row.siblings('.' + Settings.DATA_ROW_CLASS).each(function(){
|
||||
var hiddenInput = $(this).find('td.' + Settings.DATA_COL_CLASS + ' input')
|
||||
if (isTrue) {
|
||||
hiddenInput.attr('data-changed', isTrue)
|
||||
hiddenInput.attr('data-changed', isTrue);
|
||||
} else {
|
||||
hiddenInput.removeAttr('data-changed')
|
||||
hiddenInput.removeAttr('data-changed');
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -2906,7 +2906,7 @@ void DomainServer::updateReplicationNodes(ReplicationServerDirection direction)
|
|||
// collect them in a vector to separately remove them with handleKillNode (since eachNode has a read lock and
|
||||
// we cannot recursively take the write lock required by handleKillNode)
|
||||
std::vector<SharedNodePointer> nodesToKill;
|
||||
nodeList->eachNode([this, direction, replicationNodesInSettings, replicationDirection, &nodesToKill](const SharedNodePointer& otherNode) {
|
||||
nodeList->eachNode([direction, replicationNodesInSettings, replicationDirection, &nodesToKill](const SharedNodePointer& otherNode) {
|
||||
if ((direction == Upstream && NodeType::isUpstream(otherNode->getType()))
|
||||
|| (direction == Downstream && NodeType::isDownstream(otherNode->getType()))) {
|
||||
bool nodeInSettings = find(replicationNodesInSettings.cbegin(), replicationNodesInSettings.cend(),
|
||||
|
|
57
interface/resources/icons/tablet-icons/goto-a-msg.svg
Normal file
|
@ -0,0 +1,57 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 50 50"
|
||||
style="enable-background:new 0 0 50 50;"
|
||||
xml:space="preserve"
|
||||
id="svg2"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="goto-a.svg"><metadata
|
||||
id="metadata14"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs12" /><sodipodi:namedview
|
||||
pagecolor="#ff0000"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="852"
|
||||
inkscape:window-height="480"
|
||||
id="namedview10"
|
||||
showgrid="false"
|
||||
inkscape:zoom="4.72"
|
||||
inkscape:cx="25"
|
||||
inkscape:cy="25"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg2" /><style
|
||||
type="text/css"
|
||||
id="style4">
|
||||
.st0{fill:#FFFFFF;}
|
||||
.st1{fill:#EF3B4E;}
|
||||
</style>
|
||||
<circle class="st1" cx="44.1" cy="6" r="5.6"/>
|
||||
<g
|
||||
id="Layer_2" /><g
|
||||
id="Layer_1"
|
||||
style="fill:#000000;fill-opacity:1"><path
|
||||
class="st0"
|
||||
d="M47.2,41.3l-9.1-9.1c-0.8-0.8-1.9-1.1-3-1l-2.4-2.4c1.8-2.6,2.8-5.7,2.8-9c0-8.9-7.2-16.1-16.1-16.1 S3.3,11,3.3,19.8c0,8.9,7.2,16.1,16.1,16.1c4.1,0,7.8-1.5,10.6-4l2.2,2.2c-0.2,1.1,0.1,2.2,1,3l9.1,9.1c1.4,1.4,3.6,1.4,4.9,0 C48.5,44.9,48.5,42.7,47.2,41.3z M19.4,32.2c-6.8,0-12.3-5.5-12.3-12.3c0-6.8,5.5-12.3,12.3-12.3s12.3,5.5,12.3,12.3 C31.8,26.6,26.2,32.2,19.4,32.2z"
|
||||
id="path8"
|
||||
style="fill:#000000;fill-opacity:1" /></g></svg>
|
After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 911 B After Width: | Height: | Size: 911 B |
83
interface/resources/icons/tablet-icons/people-a-msg.svg
Normal file
|
@ -0,0 +1,83 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 50 50"
|
||||
style="enable-background:new 0 0 50 50;"
|
||||
xml:space="preserve"
|
||||
id="svg2"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="people-a.svg"><metadata
|
||||
id="metadata24"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs22" /><sodipodi:namedview
|
||||
pagecolor="#ff0000"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="852"
|
||||
inkscape:window-height="480"
|
||||
id="namedview20"
|
||||
showgrid="false"
|
||||
inkscape:zoom="4.72"
|
||||
inkscape:cx="25"
|
||||
inkscape:cy="25"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg2" /><style
|
||||
type="text/css"
|
||||
id="style4">
|
||||
.st0{fill:#FFFFFF;}
|
||||
.st1{fill:#EF3B4E;}
|
||||
</style>
|
||||
<circle class="st1" cx="44.1" cy="6" r="5.6"/>
|
||||
<g
|
||||
id="Layer_2" /><g
|
||||
id="Layer_1"
|
||||
style="fill:#000000;fill-opacity:1"><circle
|
||||
class="st0"
|
||||
cx="25.6"
|
||||
cy="14.8"
|
||||
r="8.1"
|
||||
id="circle8"
|
||||
style="fill:#000000;fill-opacity:1" /><path
|
||||
class="st0"
|
||||
d="M31.4,27h-11c-4.6,0-8.2,3.9-8.2,8.5v4.3c3.8,2.8,8.1,4.5,13.1,4.5c5.6,0,10.6-2.1,14.4-5.6v-3.2 C39.6,30.9,35.9,27,31.4,27z"
|
||||
id="path10"
|
||||
style="fill:#000000;fill-opacity:1" /><circle
|
||||
class="st0"
|
||||
cx="41.6"
|
||||
cy="17.1"
|
||||
r="3.5"
|
||||
id="circle12"
|
||||
style="fill:#000000;fill-opacity:1" /><path
|
||||
class="st0"
|
||||
d="M43.9,23.9h-4.1c-0.9,0-1.7,0.4-2.3,1c1.2,0.6,2.3,1.6,3.1,2.5c1,1.2,1.5,2.7,1.7,4.3c0.3,0.9,0.4,1.8,0.2,2.8 v0.6c1.6-2.2,3.3-6,4-8.1v-0.3C46.5,25.7,45.3,23.9,43.9,23.9z"
|
||||
id="path14"
|
||||
style="fill:#000000;fill-opacity:1" /><circle
|
||||
class="st0"
|
||||
cx="9.4"
|
||||
cy="18"
|
||||
r="3.5"
|
||||
id="circle16"
|
||||
style="fill:#000000;fill-opacity:1" /><path
|
||||
class="st0"
|
||||
d="M8.5,35.7c-0.1-0.7-0.1-1.4,0-2.1l0.1-0.2c0-0.2,0-0.4,0-0.6c0-0.2,0-0.3,0.1-0.5c0-0.2,0-0.3,0-0.5 c0.2-2.1,1.6-4.4,3.2-5.7c0.4-0.4,0.9-0.6,1.4-0.9c-0.5-0.5-1.3-0.7-2-0.7H7c-1.4,0-2.6,1.8-2.4,2.7v0.3 C5.1,29.8,6.8,33.5,8.5,35.7z"
|
||||
id="path18"
|
||||
style="fill:#000000;fill-opacity:1" /></g></svg>
|
After Width: | Height: | Size: 2.9 KiB |
24
interface/resources/icons/tablet-icons/people-i-msg.svg
Normal file
|
@ -0,0 +1,24 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
.st1{fill:#EF3B4E;}
|
||||
</style>
|
||||
<circle class="st1" cx="44.1" cy="6" r="5.6"/>
|
||||
<g id="Layer_2">
|
||||
</g>
|
||||
<g id="Layer_1">
|
||||
<circle class="st0" cx="25.6" cy="14.8" r="8.1"/>
|
||||
<path class="st0" d="M31.4,27h-11c-4.6,0-8.2,3.9-8.2,8.5v4.3c3.8,2.8,8.1,4.5,13.1,4.5c5.6,0,10.6-2.1,14.4-5.6v-3.2
|
||||
C39.6,30.9,35.9,27,31.4,27z"/>
|
||||
<circle class="st0" cx="41.6" cy="17.1" r="3.5"/>
|
||||
<path class="st0" d="M43.9,23.9h-4.1c-0.9,0-1.7,0.4-2.3,1c1.2,0.6,2.3,1.6,3.1,2.5c1,1.2,1.5,2.7,1.7,4.3c0.3,0.9,0.4,1.8,0.2,2.8
|
||||
v0.6c1.6-2.2,3.3-6,4-8.1v-0.3C46.5,25.7,45.3,23.9,43.9,23.9z"/>
|
||||
<circle class="st0" cx="9.4" cy="18" r="3.5"/>
|
||||
<path class="st0" d="M8.5,35.7c-0.1-0.7-0.1-1.4,0-2.1l0.1-0.2c0-0.2,0-0.4,0-0.6c0-0.2,0-0.3,0.1-0.5c0-0.2,0-0.3,0-0.5
|
||||
c0.2-2.1,1.6-4.4,3.2-5.7c0.4-0.4,0.9-0.6,1.4-0.9c-0.5-0.5-1.3-0.7-2-0.7H7c-1.4,0-2.6,1.8-2.4,2.7v0.3
|
||||
C5.1,29.8,6.8,33.5,8.5,35.7z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
6
interface/resources/icons/tablet-icons/wallet-a-msg.svg
Normal file
|
@ -0,0 +1,6 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0px" y="0px" viewBox="0 0 96 96" style="enable-background:new 0 0 96 96;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st1{fill:#EF3B4E;}
|
||||
</style>
|
||||
<circle class="st1" cx="84.6" cy="11.5" r="10.75"/>
|
||||
<g><path d="M2.4,70.5c0,6.1,4.9,11,11,11H76c6.1,0,11-4.9,11-11V59.6c3.7-0.7,6.6-3.9,6.6-7.9v-7.5c0-3.9-2.8-7.2-6.6-7.9V25.5 c0-6.1-4.9-11-11-11H13.4c-6.1,0-11,4.9-11,11V70.5z M87.6,51.8c0,1.1-0.9,2-2,2H72.2c-2.8,0-5-2.2-5-5v-1.5c0-2.8,2.2-5,5-5h13.3 c1.1,0,2,0.9,2,2V51.8z M8.4,25.5c0-2.8,2.2-5,5-5H76c2.8,0,5,2.2,5,5v10.7h-8.7c-6.1,0-11,4.9-11,11v1.5c0,6.1,4.9,11,11,11H81 v10.7c0,2.8-2.2,5-5,5H13.4c-2.8,0-5-2.2-5-5V25.5z"></path></g></svg>
|
After Width: | Height: | Size: 755 B |
16
interface/resources/icons/tablet-icons/wallet-i-msg.svg
Normal file
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 96 96" style="enable-background:new 0 0 96 96;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
.st1{fill:#EF3B4E;}
|
||||
</style>
|
||||
<circle class="st1" cx="84.6" cy="11.5" r="10.75"/>
|
||||
<g>
|
||||
<path class="st0" d="M2.4,70.5c0,6.1,4.9,11,11,11H76c6.1,0,11-4.9,11-11V59.6c3.7-0.7,6.6-3.9,6.6-7.9v-7.5c0-3.9-2.8-7.2-6.6-7.9
|
||||
V25.5c0-6.1-4.9-11-11-11H13.4c-6.1,0-11,4.9-11,11C2.4,25.5,2.4,70.5,2.4,70.5z M87.6,51.8c0,1.1-0.9,2-2,2H72.2c-2.8,0-5-2.2-5-5
|
||||
v-1.5c0-2.8,2.2-5,5-5h13.3c1.1,0,2,0.9,2,2L87.6,51.8L87.6,51.8z M8.4,25.5c0-2.8,2.2-5,5-5H76c2.8,0,5,2.2,5,5v10.7h-8.7
|
||||
c-6.1,0-11,4.9-11,11v1.5c0,6.1,4.9,11,11,11H81v10.7c0,2.8-2.2,5-5,5H13.4c-2.8,0-5-2.2-5-5V25.5z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 984 B |
|
@ -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
|
||||
|
|
|
@ -23,6 +23,7 @@ Item {
|
|||
property bool failAfterSignUp: false
|
||||
|
||||
function login() {
|
||||
flavorText.visible = false
|
||||
mainTextContainer.visible = false
|
||||
toggleLoading(true)
|
||||
loginDialog.login(usernameField.text, passwordField.text)
|
||||
|
@ -43,7 +44,7 @@ Item {
|
|||
|
||||
function resize() {
|
||||
var targetWidth = Math.max(titleWidth, form.contentWidth);
|
||||
var targetHeight = hifi.dimensions.contentSpacing.y + mainTextContainer.height +
|
||||
var targetHeight = hifi.dimensions.contentSpacing.y + flavorText.height + mainTextContainer.height +
|
||||
4 * hifi.dimensions.contentSpacing.y + form.height;
|
||||
|
||||
if (additionalInformation.visible) {
|
||||
|
@ -106,14 +107,15 @@ Item {
|
|||
ShortcutText {
|
||||
id: mainTextContainer
|
||||
anchors {
|
||||
top: parent.top
|
||||
top: flavorText.bottom
|
||||
left: parent.left
|
||||
margins: 0
|
||||
topMargin: hifi.dimensions.contentSpacing.y
|
||||
topMargin: 1.5 * hifi.dimensions.contentSpacing.y
|
||||
}
|
||||
|
||||
visible: false
|
||||
text: qsTr("Username or password incorrect.")
|
||||
height: flavorText.height - 20
|
||||
wrapMode: Text.WordWrap
|
||||
color: hifi.colors.redAccent
|
||||
lineHeight: 1
|
||||
|
@ -128,7 +130,7 @@ Item {
|
|||
|
||||
anchors {
|
||||
top: mainTextContainer.bottom
|
||||
topMargin: 2 * hifi.dimensions.contentSpacing.y
|
||||
topMargin: 1.5 * hifi.dimensions.contentSpacing.y
|
||||
}
|
||||
spacing: 2 * hifi.dimensions.contentSpacing.y
|
||||
|
||||
|
@ -139,6 +141,7 @@ Item {
|
|||
focus: true
|
||||
placeholderText: "Username or Email"
|
||||
activeFocusOnPress: true
|
||||
onHeightChanged: d.resize(); onWidthChanged: d.resize();
|
||||
|
||||
ShortcutText {
|
||||
z: 10
|
||||
|
@ -172,7 +175,7 @@ Item {
|
|||
width: parent.width
|
||||
placeholderText: "Password"
|
||||
activeFocusOnPress: true
|
||||
echoMode: TextInput.Password
|
||||
echoMode: passwordFieldMouseArea.showPassword ? TextInput.Normal : TextInput.Password
|
||||
onHeightChanged: d.resize(); onWidthChanged: d.resize();
|
||||
|
||||
ShortcutText {
|
||||
|
@ -212,29 +215,28 @@ Item {
|
|||
|
||||
Image {
|
||||
id: showPasswordImage
|
||||
y: (passwordField.height - (passwordField.height * 16 / 23)) / 2
|
||||
width: passwordField.width - (passwordField.width - (((passwordField.height) * 31/23)))
|
||||
width: passwordField.height * 16 / 23
|
||||
height: passwordField.height * 16 / 23
|
||||
anchors {
|
||||
right: parent.right
|
||||
rightMargin: 3
|
||||
rightMargin: 8
|
||||
top: parent.top
|
||||
topMargin: passwordFieldMouseArea.showPassword ? 6 : 8
|
||||
bottom: parent.bottom
|
||||
bottomMargin: passwordFieldMouseArea.showPassword ? 5 : 8
|
||||
}
|
||||
source: passwordFieldMouseArea.showPassword ? "../../images/eyeClosed.svg" : "../../images/eyeOpen.svg"
|
||||
MouseArea {
|
||||
id: passwordFieldMouseArea
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.LeftButton
|
||||
property bool showPassword: false
|
||||
onClicked: {
|
||||
showPassword = !showPassword;
|
||||
}
|
||||
}
|
||||
source: "../../images/eyeOpen.svg"
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: passwordFieldMouseArea
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.LeftButton
|
||||
property bool showPassword: false
|
||||
onClicked: {
|
||||
showPassword = !showPassword;
|
||||
passwordField.echoMode = showPassword ? TextInput.Normal : TextInput.Password;
|
||||
showPasswordImage.source = showPassword ? "../../images/eyeClosed.svg" : "../../images/eyeOpen.svg";
|
||||
showPasswordImage.height = showPassword ? passwordField.height : passwordField.height * 16 / 23;
|
||||
showPasswordImage.y = showPassword ? 0 : (passwordField.height - showPasswordImage.height) / 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onReturnPressed: linkAccountBody.login()
|
||||
|
@ -284,7 +286,7 @@ Item {
|
|||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: 200
|
||||
|
||||
text: qsTr(loginDialog.isSteamRunning() ? "Link Account" : "Login")
|
||||
text: qsTr(loginDialog.isSteamRunning() ? "Link Account" : "Log in")
|
||||
color: hifi.buttons.blue
|
||||
|
||||
onClicked: linkAccountBody.login()
|
||||
|
@ -336,6 +338,7 @@ Item {
|
|||
|
||||
if (failAfterSignUp) {
|
||||
mainTextContainer.text = "Account created successfully."
|
||||
flavorText.visible = true
|
||||
mainTextContainer.visible = true
|
||||
}
|
||||
|
||||
|
@ -374,6 +377,7 @@ Item {
|
|||
UserActivityLogger.logAction("encourageLoginDialog", data);
|
||||
Settings.setValue("loginDialogPoppedUp", false);
|
||||
}
|
||||
flavorText.visible = true
|
||||
mainTextContainer.visible = true
|
||||
toggleLoading(false)
|
||||
}
|
||||
|
|
|
@ -271,6 +271,8 @@ Rectangle {
|
|||
connectionsUserModel.getFirstPage();
|
||||
}
|
||||
activeTab = "connectionsTab";
|
||||
connectionsOnlineDot.visible = false;
|
||||
pal.sendToScript({method: 'hideNotificationDot'});
|
||||
connectionsHelpText.color = hifi.colors.blueAccent;
|
||||
}
|
||||
}
|
||||
|
@ -298,6 +300,16 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
}
|
||||
Rectangle {
|
||||
id: connectionsOnlineDot;
|
||||
visible: false;
|
||||
width: 10;
|
||||
height: width;
|
||||
radius: width;
|
||||
color: "#EF3B4E"
|
||||
anchors.left: parent.left;
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
}
|
||||
// "CONNECTIONS" text
|
||||
RalewaySemiBold {
|
||||
id: connectionsTabSelectorText;
|
||||
|
@ -305,7 +317,11 @@ Rectangle {
|
|||
// Text size
|
||||
size: hifi.fontSizes.tabularData;
|
||||
// Anchors
|
||||
anchors.fill: parent;
|
||||
anchors.left: connectionsOnlineDot.visible ? connectionsOnlineDot.right : parent.left;
|
||||
anchors.leftMargin: connectionsOnlineDot.visible ? 4 : 0;
|
||||
anchors.top: parent.top;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.right: parent.right;
|
||||
// Style
|
||||
font.capitalization: Font.AllUppercase;
|
||||
color: activeTab === "connectionsTab" ? hifi.colors.blueAccent : hifi.colors.baseGray;
|
||||
|
@ -326,7 +342,7 @@ Rectangle {
|
|||
anchors.left: connectionsTabSelectorTextContainer.left;
|
||||
anchors.top: connectionsTabSelectorTextContainer.top;
|
||||
anchors.topMargin: 1;
|
||||
anchors.leftMargin: connectionsTabSelectorTextMetrics.width + 42;
|
||||
anchors.leftMargin: connectionsTabSelectorTextMetrics.width + 42 + connectionsOnlineDot.width + connectionsTabSelectorText.anchors.leftMargin;
|
||||
RalewayRegular {
|
||||
id: connectionsHelpText;
|
||||
text: "[?]";
|
||||
|
@ -1267,6 +1283,9 @@ Rectangle {
|
|||
case 'http.response':
|
||||
http.handleHttpResponse(message);
|
||||
break;
|
||||
case 'changeConnectionsDotStatus':
|
||||
connectionsOnlineDot.visible = message.shouldShowDot;
|
||||
break;
|
||||
default:
|
||||
console.log('Unrecognized message:', JSON.stringify(message));
|
||||
}
|
||||
|
|
|
@ -314,7 +314,7 @@ Rectangle {
|
|||
}
|
||||
};
|
||||
|
||||
wearableUpdated(getCurrentWearable().id, jointIndex, properties);
|
||||
wearableUpdated(getCurrentWearable().id, wearablesCombobox.currentIndex, properties);
|
||||
}
|
||||
|
||||
onCurrentIndexChanged: {
|
||||
|
|
|
@ -24,6 +24,7 @@ Item {
|
|||
|
||||
fragmentShader: {
|
||||
"
|
||||
#version 150 core
|
||||
varying highp vec2 qt_TexCoord0;
|
||||
uniform lowp sampler2D source;
|
||||
uniform lowp sampler2D mask;
|
||||
|
|
|
@ -552,6 +552,10 @@ Rectangle {
|
|||
// Alignment
|
||||
horizontalAlignment: Text.AlignLeft;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
onLinkActivated: {
|
||||
// Only case is to go to the bank.
|
||||
sendToScript({method: 'gotoBank'});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1107,25 +1111,32 @@ Rectangle {
|
|||
}
|
||||
|
||||
function handleBuyAgainLogic() {
|
||||
// If you can buy this item again...
|
||||
if (canBuyAgain()) {
|
||||
// If you can't afford another copy of the item...
|
||||
if (root.balanceAfterPurchase < 0) {
|
||||
// If you already own the item...
|
||||
if (root.alreadyOwned) {
|
||||
buyText.text = "<b>Your Wallet does not have sufficient funds to purchase this item again.</b>";
|
||||
// Else if you don't already own the item...
|
||||
} else {
|
||||
buyText.text = "<b>Your Wallet does not have sufficient funds to purchase this item.</b>";
|
||||
}
|
||||
buyTextContainer.color = "#FFC3CD";
|
||||
buyTextContainer.border.color = "#F3808F";
|
||||
buyGlyph.text = hifi.glyphs.alert;
|
||||
buyGlyph.size = 54;
|
||||
// If you CAN afford another copy of the item...
|
||||
// General rules, implemented in various scattered places in this file:
|
||||
// 1. If you already own the item, a viewInMyPurchasesButton is visible,
|
||||
// and the buyButton is visible (and says "Buy it again") ONLY if it is a type you canBuyAgain.
|
||||
// 2. Separately,
|
||||
// a. If you don't have enough money to buy, the buyText becomes visible and tells you, and the buyButton is disabled.
|
||||
// b. Otherwise, if the item is a content set and you don't have rez permission, the buyText becomes visible and tells you so.
|
||||
|
||||
// If you can't afford another copy of the item...
|
||||
if (root.balanceAfterPurchase < 0) {
|
||||
// If you already own the item...
|
||||
if (!root.alreadyOwned) {
|
||||
buyText.text = "<b>Your Wallet does not have sufficient funds to purchase this item.</b>";
|
||||
// Else if you don't already own the item...
|
||||
} else if (canBuyAgain()) {
|
||||
buyText.text = "<b>Your Wallet does not have sufficient funds to purchase this item again.</b>";
|
||||
} else {
|
||||
handleContentSets();
|
||||
buyText.text = "<b>While you do not have sufficient funds to buy this, you already have this item.</b>"
|
||||
}
|
||||
buyText.text += " Visit <a href='#'>Bank of High Fidelity</a> to get more HFC."
|
||||
buyTextContainer.color = "#FFC3CD";
|
||||
buyTextContainer.border.color = "#F3808F";
|
||||
buyGlyph.text = hifi.glyphs.alert;
|
||||
buyGlyph.size = 54;
|
||||
// If you CAN afford another copy of the item...
|
||||
} else {
|
||||
handleContentSets();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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(resource, assetType){
|
||||
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]()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 45 KiB |
|
@ -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;
|
||||
|
|
|
@ -93,7 +93,7 @@ Rectangle {
|
|||
console.log("Failed to get Available Updates", result.data.message);
|
||||
} else {
|
||||
sendToScript({method: 'purchases_availableUpdatesReceived', numUpdates: result.data.updates.length });
|
||||
root.numUpdatesAvailable = result.data.updates.length;
|
||||
root.numUpdatesAvailable = result.total_entries;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -408,9 +408,7 @@ Rectangle {
|
|||
|
||||
Connections {
|
||||
onSendSignalToWallet: {
|
||||
if (msg.method === 'walletReset' || msg.method === 'passphraseReset') {
|
||||
sendToScript(msg);
|
||||
} else if (msg.method === 'walletSecurity_changeSecurityImage') {
|
||||
if (msg.method === 'walletSecurity_changeSecurityImage') {
|
||||
securityImageChange.initModel();
|
||||
root.activeView = "securityImageChange";
|
||||
}
|
||||
|
@ -831,6 +829,7 @@ Rectangle {
|
|||
Commerce.getWalletAuthenticatedStatus(); // before writing security image, ensures that salt/account password is set.
|
||||
Commerce.chooseSecurityImage(securityImagePath);
|
||||
Commerce.generateKeyPair();
|
||||
followReferrer({ referrer: walletSetup.referrer });
|
||||
}
|
||||
|
||||
function addLeadingZero(n) {
|
||||
|
@ -838,7 +837,7 @@ Rectangle {
|
|||
}
|
||||
|
||||
function followReferrer(msg) {
|
||||
if (msg.referrer === '' || msg.referrer === 'marketplace cta') {
|
||||
if (msg.referrer === '') {
|
||||
root.activeView = "initialize";
|
||||
Commerce.getWalletStatus();
|
||||
} else if (msg.referrer === 'purchases') {
|
||||
|
|
|
@ -45,14 +45,6 @@ Item {
|
|||
onHistoryResult : {
|
||||
transactionHistoryModel.handlePage(null, result);
|
||||
}
|
||||
|
||||
onAvailableUpdatesResult: {
|
||||
if (result.status !== 'success') {
|
||||
console.log("Failed to get Available Updates", result.data.message);
|
||||
} else {
|
||||
sendToScript({method: 'wallet_availableUpdatesReceived', numUpdates: result.data.updates.length });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
|
|
|
@ -28,7 +28,7 @@ Item {
|
|||
property string activeView: "step_1";
|
||||
property string lastPage;
|
||||
property bool hasShownSecurityImageTip: false;
|
||||
property string referrer;
|
||||
property string referrer: '';
|
||||
property string keyFilePath;
|
||||
property date startingTimestamp;
|
||||
property string setupAttemptID;
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
//
|
||||
#include "AndroidHelper.h"
|
||||
#include <QDebug>
|
||||
#include <AudioClient.h>
|
||||
#include "Application.h"
|
||||
|
||||
#if defined(qApp)
|
||||
|
@ -18,12 +19,13 @@
|
|||
#define qApp (static_cast<Application*>(QCoreApplication::instance()))
|
||||
|
||||
AndroidHelper::AndroidHelper() {
|
||||
qRegisterMetaType<QAudio::Mode>("QAudio::Mode");
|
||||
}
|
||||
|
||||
AndroidHelper::~AndroidHelper() {
|
||||
}
|
||||
|
||||
void AndroidHelper::requestActivity(const QString &activityName, const bool backToScene, QList<QString> args) {
|
||||
void AndroidHelper::requestActivity(const QString &activityName, const bool backToScene, QMap<QString, QString> args) {
|
||||
emit androidActivityRequested(activityName, backToScene, args);
|
||||
}
|
||||
|
||||
|
@ -47,8 +49,10 @@ void AndroidHelper::performHapticFeedback(int duration) {
|
|||
emit hapticFeedbackRequested(duration);
|
||||
}
|
||||
|
||||
void AndroidHelper::showLoginDialog() {
|
||||
emit androidActivityRequested("Login", true);
|
||||
void AndroidHelper::showLoginDialog(QUrl url) {
|
||||
QMap<QString, QString> args;
|
||||
args["url"] = url.toString();
|
||||
emit androidActivityRequested("Login", true, args);
|
||||
}
|
||||
|
||||
void AndroidHelper::processURL(const QString &url) {
|
||||
|
@ -56,3 +60,12 @@ void AndroidHelper::processURL(const QString &url) {
|
|||
qApp->acceptURL(url);
|
||||
}
|
||||
}
|
||||
|
||||
void AndroidHelper::notifyHeadsetOn(bool pluggedIn) {
|
||||
#if defined (Q_OS_ANDROID)
|
||||
auto audioClient = DependencyManager::get<AudioClient>();
|
||||
if (audioClient) {
|
||||
QMetaObject::invokeMethod(audioClient.data(), "setHeadsetPluggedIn", Q_ARG(bool, pluggedIn));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
#define hifi_Android_Helper_h
|
||||
|
||||
#include <QObject>
|
||||
#include <QMap>
|
||||
#include <QUrl>
|
||||
|
||||
class AndroidHelper : public QObject {
|
||||
Q_OBJECT
|
||||
|
@ -21,7 +23,7 @@ public:
|
|||
static AndroidHelper instance;
|
||||
return instance;
|
||||
}
|
||||
void requestActivity(const QString &activityName, const bool backToScene, QList<QString> args = QList<QString>());
|
||||
void requestActivity(const QString &activityName, const bool backToScene, QMap<QString, QString> args = QMap<QString, QString>());
|
||||
void notifyLoadComplete();
|
||||
void notifyEnterForeground();
|
||||
void notifyBeforeEnterBackground();
|
||||
|
@ -29,15 +31,16 @@ public:
|
|||
|
||||
void performHapticFeedback(int duration);
|
||||
void processURL(const QString &url);
|
||||
void notifyHeadsetOn(bool pluggedIn);
|
||||
|
||||
AndroidHelper(AndroidHelper const&) = delete;
|
||||
void operator=(AndroidHelper const&) = delete;
|
||||
|
||||
public slots:
|
||||
void showLoginDialog();
|
||||
void showLoginDialog(QUrl url);
|
||||
|
||||
signals:
|
||||
void androidActivityRequested(const QString &activityName, const bool backToScene, QList<QString> args = QList<QString>());
|
||||
void androidActivityRequested(const QString &activityName, const bool backToScene, QMap<QString, QString> args = QMap<QString, QString>());
|
||||
void qtAppLoadComplete();
|
||||
void enterForeground();
|
||||
void beforeEnterBackground();
|
||||
|
|
|
@ -269,7 +269,7 @@ class RenderEventHandler : public QObject {
|
|||
public:
|
||||
RenderEventHandler() {
|
||||
// Transfer to a new thread
|
||||
moveToNewNamedThread(this, "RenderThread", [this](QThread* renderThread) {
|
||||
moveToNewNamedThread(this, "RenderThread", [](QThread* renderThread) {
|
||||
hifi::qt::addBlockingForbiddenThread("Render", renderThread);
|
||||
qApp->_lastTimeRendered.start();
|
||||
}, std::bind(&RenderEventHandler::initialize, this), QThread::HighestPriority);
|
||||
|
@ -963,7 +963,7 @@ Q_GUI_EXPORT void qt_gl_set_global_share_context(QOpenGLContext *context);
|
|||
|
||||
Setting::Handle<int> sessionRunTime{ "sessionRunTime", 0 };
|
||||
|
||||
const float DEFAULT_HMD_TABLET_SCALE_PERCENT = 70.0f;
|
||||
const float DEFAULT_HMD_TABLET_SCALE_PERCENT = 60.0f;
|
||||
const float DEFAULT_DESKTOP_TABLET_SCALE_PERCENT = 75.0f;
|
||||
const bool DEFAULT_DESKTOP_TABLET_BECOMES_TOOLBAR = true;
|
||||
const bool DEFAULT_HMD_TABLET_BECOMES_TOOLBAR = false;
|
||||
|
@ -976,7 +976,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
_window(new MainWindow(desktop())),
|
||||
_sessionRunTimer(startupTimer),
|
||||
_previousSessionCrashed(setupEssentials(argc, argv, runningMarkerExisted)),
|
||||
_undoStackScriptingInterface(&_undoStack),
|
||||
_entitySimulation(new PhysicalEntitySimulation()),
|
||||
_physicsEngine(new PhysicsEngine(Vectors::ZERO)),
|
||||
_entityClipboard(new EntityTree()),
|
||||
|
@ -996,7 +995,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
_enableProcessOctreeThread(true),
|
||||
_lastNackTime(usecTimestampNow()),
|
||||
_lastSendDownstreamAudioStats(usecTimestampNow()),
|
||||
_aboutToQuit(false),
|
||||
_notifiedPacketVersionMismatchThisDomain(false),
|
||||
_maxOctreePPS(maxOctreePacketsPerSecond.get()),
|
||||
_lastFaceTrackerUpdate(0),
|
||||
|
@ -1236,7 +1234,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
auto dialogsManager = DependencyManager::get<DialogsManager>();
|
||||
#if defined(Q_OS_ANDROID)
|
||||
connect(accountManager.data(), &AccountManager::authRequired, this, []() {
|
||||
AndroidHelper::instance().showLoginDialog();
|
||||
auto addressManager = DependencyManager::get<AddressManager>();
|
||||
AndroidHelper::instance().showLoginDialog(addressManager->currentAddress());
|
||||
});
|
||||
#else
|
||||
connect(accountManager.data(), &AccountManager::authRequired, dialogsManager.data(), &DialogsManager::showLoginDialog);
|
||||
|
@ -1691,21 +1690,21 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
return DependencyManager::get<OffscreenUi>()->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;
|
||||
|
@ -1755,14 +1754,22 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
// we can unlock the desktop repositioning code, since all the positions will be
|
||||
// relative to the desktop size for this plugin
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
offscreenUi->getDesktop()->setProperty("repositionLocked", false);
|
||||
connect(offscreenUi.data(), &OffscreenUi::desktopReady, []() {
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
auto desktop = offscreenUi->getDesktop();
|
||||
if (desktop) {
|
||||
desktop->setProperty("repositionLocked", false);
|
||||
}
|
||||
});
|
||||
|
||||
// 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
|
||||
|
@ -2307,7 +2314,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
QTimer* checkLoginTimer = new QTimer(this);
|
||||
checkLoginTimer->setInterval(CHECK_LOGIN_TIMER);
|
||||
checkLoginTimer->setSingleShot(true);
|
||||
connect(checkLoginTimer, &QTimer::timeout, this, [this]() {
|
||||
connect(checkLoginTimer, &QTimer::timeout, this, []() {
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
auto dialogsManager = DependencyManager::get<DialogsManager>();
|
||||
if (!accountManager->isLoggedIn()) {
|
||||
|
@ -2881,9 +2888,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" },
|
||||
|
@ -3086,7 +3094,6 @@ void Application::onDesktopRootContextCreated(QQmlContext* surfaceContext) {
|
|||
surfaceContext->setContextProperty("DialogsManager", _dialogsManagerScriptingInterface);
|
||||
surfaceContext->setContextProperty("FaceTracker", DependencyManager::get<DdeFaceTracker>().data());
|
||||
surfaceContext->setContextProperty("AvatarManager", DependencyManager::get<AvatarManager>().data());
|
||||
surfaceContext->setContextProperty("UndoStack", &_undoStackScriptingInterface);
|
||||
surfaceContext->setContextProperty("LODManager", DependencyManager::get<LODManager>().data());
|
||||
surfaceContext->setContextProperty("HMD", DependencyManager::get<HMDScriptingInterface>().data());
|
||||
surfaceContext->setContextProperty("Scene", DependencyManager::get<SceneScriptingInterface>().data());
|
||||
|
@ -3427,7 +3434,12 @@ void Application::handleSandboxStatus(QNetworkReply* reply) {
|
|||
int urlIndex = arguments().indexOf(HIFI_URL_COMMAND_LINE_KEY);
|
||||
QString addressLookupString;
|
||||
if (urlIndex != -1) {
|
||||
addressLookupString = arguments().value(urlIndex + 1);
|
||||
QUrl url(arguments().value(urlIndex + 1));
|
||||
if (url.scheme() == URL_SCHEME_HIFIAPP) {
|
||||
Setting::Handle<QVariant>("startUpApp").set(url.path());
|
||||
} else {
|
||||
addressLookupString = url.toString();
|
||||
}
|
||||
}
|
||||
|
||||
static const QString SENT_TO_PREVIOUS_LOCATION = "previous_location";
|
||||
|
@ -3498,13 +3510,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<NodeList>()->getDomainHandler().getInterstitialModeEnabled();
|
||||
if (enableInterstitial) {
|
||||
if (_interstitialMode != interstitialMode) {
|
||||
_interstitialMode = interstitialMode;
|
||||
|
||||
DependencyManager::get<AudioClient>()->setAudioPaused(_interstitialMode);
|
||||
DependencyManager::get<AvatarManager>()->setMyAvatarDataPacketsPaused(_interstitialMode);
|
||||
DependencyManager::get<AudioClient>()->setAudioPaused(_interstitialMode);
|
||||
DependencyManager::get<AvatarManager>()->setMyAvatarDataPacketsPaused(_interstitialMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4664,8 +4677,14 @@ void Application::idle() {
|
|||
|
||||
checkChangeCursor();
|
||||
|
||||
Stats::getInstance()->updateStats();
|
||||
AnimStats::getInstance()->updateStats();
|
||||
auto stats = Stats::getInstance();
|
||||
if (stats) {
|
||||
stats->updateStats();
|
||||
}
|
||||
auto animStats = AnimStats::getInstance();
|
||||
if (animStats) {
|
||||
animStats->updateStats();
|
||||
}
|
||||
|
||||
// Normally we check PipelineWarnings, but since idle will often take more than 10ms we only show these idle timing
|
||||
// details if we're in ExtraDebugging mode. However, the ::update() and its subcomponents will show their timing
|
||||
|
@ -6746,8 +6765,6 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe
|
|||
|
||||
scriptEngine->registerGlobalObject("AvatarManager", DependencyManager::get<AvatarManager>().data());
|
||||
|
||||
scriptEngine->registerGlobalObject("UndoStack", &_undoStackScriptingInterface);
|
||||
|
||||
scriptEngine->registerGlobalObject("LODManager", DependencyManager::get<LODManager>().data());
|
||||
|
||||
scriptEngine->registerGlobalObject("Paths", DependencyManager::get<PathUtils>().data());
|
||||
|
@ -7674,6 +7691,9 @@ void Application::openUrl(const QUrl& url) const {
|
|||
if (!url.isEmpty()) {
|
||||
if (url.scheme() == URL_SCHEME_HIFI) {
|
||||
DependencyManager::get<AddressManager>()->handleLookupString(url.toString());
|
||||
} else if (url.scheme() == URL_SCHEME_HIFIAPP) {
|
||||
QmlCommerce commerce;
|
||||
commerce.openSystemApp(url.path());
|
||||
} else {
|
||||
// address manager did not handle - ask QDesktopServices to handle
|
||||
QDesktopServices::openUrl(url);
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
#include <QtGui/QImage>
|
||||
|
||||
#include <QtWidgets/QApplication>
|
||||
#include <QtWidgets/QUndoStack>
|
||||
|
||||
#include <ThreadHelpers.h>
|
||||
#include <AbstractScriptingServicesInterface.h>
|
||||
|
@ -70,7 +69,6 @@
|
|||
#include "ui/OctreeStatsDialog.h"
|
||||
#include "ui/OverlayConductor.h"
|
||||
#include "ui/overlays/Overlays.h"
|
||||
#include "UndoStackScriptingInterface.h"
|
||||
|
||||
#include "workload/GameWorkload.h"
|
||||
|
||||
|
@ -189,7 +187,6 @@ public:
|
|||
|
||||
const OctreePacketProcessor& getOctreePacketProcessor() const { return _octreeProcessor; }
|
||||
QSharedPointer<EntityTreeRenderer> getEntities() const { return DependencyManager::get<EntityTreeRenderer>(); }
|
||||
QUndoStack* getUndoStack() { return &_undoStack; }
|
||||
MainWindow* getWindow() const { return _window; }
|
||||
EntityTreePointer getEntityClipboard() const { return _entityClipboard; }
|
||||
EntityEditPacketSender* getEntityEditPacketSender() { return &_entityEditSender; }
|
||||
|
@ -432,7 +429,7 @@ public slots:
|
|||
|
||||
void setIsServerlessMode(bool serverlessDomain);
|
||||
void loadServerlessDomain(QUrl domainURL, bool errorDomain = false);
|
||||
void setIsInterstitialMode(bool interstialMode);
|
||||
void setIsInterstitialMode(bool interstitialMode);
|
||||
|
||||
void updateVerboseLogging();
|
||||
|
||||
|
@ -560,6 +557,8 @@ private:
|
|||
MainWindow* _window;
|
||||
QElapsedTimer& _sessionRunTimer;
|
||||
|
||||
bool _aboutToQuit { false };
|
||||
|
||||
bool _previousSessionCrashed;
|
||||
|
||||
DisplayPluginPointer _displayPlugin;
|
||||
|
@ -569,9 +568,6 @@ private:
|
|||
|
||||
bool _activatingDisplayPlugin { false };
|
||||
|
||||
QUndoStack _undoStack;
|
||||
UndoStackScriptingInterface _undoStackScriptingInterface;
|
||||
|
||||
uint32_t _renderFrameCount { 0 };
|
||||
|
||||
// Frame Rate Measurement
|
||||
|
@ -651,8 +647,6 @@ private:
|
|||
quint64 _lastNackTime;
|
||||
quint64 _lastSendDownstreamAudioStats;
|
||||
|
||||
bool _aboutToQuit;
|
||||
|
||||
bool _notifiedPacketVersionMismatchThisDomain;
|
||||
|
||||
ConditionalGuard _settingsGuard;
|
||||
|
|
|
@ -157,7 +157,10 @@ void Application::paintGL() {
|
|||
renderArgs._context->enableStereo(false);
|
||||
|
||||
{
|
||||
Stats::getInstance()->setRenderDetails(renderArgs._details);
|
||||
auto stats = Stats::getInstance();
|
||||
if (stats) {
|
||||
stats->setRenderDetails(renderArgs._details);
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t lastPaintDuration = usecTimestampNow() - lastPaintBegin;
|
||||
|
|
|
@ -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<NodeList>()->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<DialogsManager>()->setDomainConnectionFailureVisibility(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -53,4 +59,8 @@ void ConnectionMonitor::startTimer() {
|
|||
|
||||
void ConnectionMonitor::stopTimer() {
|
||||
_timer.stop();
|
||||
bool enableInterstitial = DependencyManager::get<NodeList>()->getDomainHandler().getInterstitialModeEnabled();
|
||||
if (!enableInterstitial) {
|
||||
DependencyManager::get<DialogsManager>()->setDomainConnectionFailureVisibility(false);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
#endif // hifi_ConnectionMonitor_h
|
||||
|
|
|
@ -90,19 +90,6 @@ Menu::Menu() {
|
|||
// Edit menu ----------------------------------
|
||||
MenuWrapper* editMenu = addMenu("Edit");
|
||||
|
||||
// Edit > Undo
|
||||
QUndoStack* undoStack = qApp->getUndoStack();
|
||||
QAction* undoAction = undoStack->createUndoAction(editMenu);
|
||||
undoAction->setShortcut(Qt::CTRL | Qt::Key_Z);
|
||||
addActionToQMenuAndActionHash(editMenu, undoAction);
|
||||
|
||||
// Edit > Redo
|
||||
QAction* redoAction = undoStack->createRedoAction(editMenu);
|
||||
redoAction->setShortcut(Qt::CTRL | Qt::SHIFT | Qt::Key_Z);
|
||||
addActionToQMenuAndActionHash(editMenu, redoAction);
|
||||
|
||||
editMenu->addSeparator();
|
||||
|
||||
// Edit > Cut
|
||||
auto cutAction = addActionToQMenuAndActionHash(editMenu, "Cut", QKeySequence::Cut);
|
||||
connect(cutAction, &QAction::triggered, [] {
|
||||
|
|
|
@ -48,6 +48,10 @@ AvatarActionHold::~AvatarActionHold() {
|
|||
myAvatar->removeHoldAction(this);
|
||||
}
|
||||
}
|
||||
auto ownerEntity = _ownerEntity.lock();
|
||||
if (ownerEntity) {
|
||||
ownerEntity->setTransitingWithAvatar(false);
|
||||
}
|
||||
|
||||
#if WANT_DEBUG
|
||||
qDebug() << "AvatarActionHold::~AvatarActionHold" << (void*)this;
|
||||
|
@ -131,6 +135,15 @@ bool AvatarActionHold::getTarget(float deltaTimeStep, glm::quat& rotation, glm::
|
|||
glm::vec3 palmPosition;
|
||||
glm::quat palmRotation;
|
||||
|
||||
bool isTransitingWithAvatar = holdingAvatar->getTransit()->isTransiting();
|
||||
if (isTransitingWithAvatar != _isTransitingWithAvatar) {
|
||||
_isTransitingWithAvatar = isTransitingWithAvatar;
|
||||
auto ownerEntity = _ownerEntity.lock();
|
||||
if (ownerEntity) {
|
||||
ownerEntity->setTransitingWithAvatar(_isTransitingWithAvatar);
|
||||
}
|
||||
}
|
||||
|
||||
if (holdingAvatar->isMyAvatar()) {
|
||||
std::shared_ptr<MyAvatar> myAvatar = avatarManager->getMyAvatar();
|
||||
|
||||
|
@ -404,11 +417,14 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) {
|
|||
_kinematicSetVelocity = kinematicSetVelocity;
|
||||
_ignoreIK = ignoreIK;
|
||||
_active = true;
|
||||
|
||||
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
|
||||
auto ownerEntity = _ownerEntity.lock();
|
||||
if (ownerEntity) {
|
||||
ownerEntity->setDynamicDataDirty(true);
|
||||
ownerEntity->setDynamicDataNeedsTransmit(true);
|
||||
ownerEntity->setDynamicDataNeedsTransmit(true);
|
||||
ownerEntity->setTransitingWithAvatar(myAvatar->getTransit()->isTransiting());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -59,6 +59,8 @@ private:
|
|||
bool _kinematicSetVelocity { false };
|
||||
bool _previousSet { false };
|
||||
bool _ignoreIK { false };
|
||||
bool _isTransitingWithAvatar { false };
|
||||
|
||||
glm::vec3 _previousPositionalTarget;
|
||||
glm::quat _previousRotationalTarget;
|
||||
|
||||
|
|
|
@ -78,6 +78,15 @@ AvatarManager::AvatarManager(QObject* parent) :
|
|||
removeAvatar(nodeID, KillAvatarReason::AvatarIgnored);
|
||||
}
|
||||
});
|
||||
|
||||
const float AVATAR_TRANSIT_TRIGGER_DISTANCE = 1.0f;
|
||||
const int AVATAR_TRANSIT_FRAME_COUNT = 11; // Based on testing
|
||||
const int AVATAR_TRANSIT_FRAMES_PER_METER = 1; // Based on testing
|
||||
|
||||
_transitConfig._totalFrames = AVATAR_TRANSIT_FRAME_COUNT;
|
||||
_transitConfig._triggerDistance = AVATAR_TRANSIT_TRIGGER_DISTANCE;
|
||||
_transitConfig._framesPerMeter = AVATAR_TRANSIT_FRAMES_PER_METER;
|
||||
_transitConfig._isDistanceBased = true;
|
||||
}
|
||||
|
||||
AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWeakPointer<Node>& mixerWeakPointer) {
|
||||
|
@ -129,6 +138,10 @@ void AvatarManager::updateMyAvatar(float deltaTime) {
|
|||
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
||||
PerformanceWarning warn(showWarnings, "AvatarManager::updateMyAvatar()");
|
||||
|
||||
AvatarTransit::Status status = _myAvatar->updateTransit(deltaTime, _myAvatar->getNextPosition(), _transitConfig);
|
||||
bool sendFirstTransitPackage = (status == AvatarTransit::Status::START_TRANSIT);
|
||||
bool blockTransitData = (status == AvatarTransit::Status::TRANSITING);
|
||||
|
||||
_myAvatar->update(deltaTime);
|
||||
render::Transaction transaction;
|
||||
_myAvatar->updateRenderItem(transaction);
|
||||
|
@ -137,9 +150,13 @@ void AvatarManager::updateMyAvatar(float deltaTime) {
|
|||
quint64 now = usecTimestampNow();
|
||||
quint64 dt = now - _lastSendAvatarDataTime;
|
||||
|
||||
if (dt > MIN_TIME_BETWEEN_MY_AVATAR_DATA_SENDS && !_myAvatarDataPacketsPaused) {
|
||||
|
||||
if (sendFirstTransitPackage || (dt > MIN_TIME_BETWEEN_MY_AVATAR_DATA_SENDS && !_myAvatarDataPacketsPaused && !blockTransitData)) {
|
||||
// send head/hand data to the avatar mixer and voxel server
|
||||
PerformanceTimer perfTimer("send");
|
||||
if (sendFirstTransitPackage) {
|
||||
_myAvatar->overrideNextPackagePositionData(_myAvatar->getTransit()->getEndPosition());
|
||||
}
|
||||
_myAvatar->sendAvatarDataPacket();
|
||||
_lastSendAvatarDataTime = now;
|
||||
_myAvatarSendRate.increment();
|
||||
|
@ -234,11 +251,13 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
|||
const SortableAvatar& sortData = *it;
|
||||
const auto avatar = std::static_pointer_cast<OtherAvatar>(sortData.getAvatar());
|
||||
|
||||
// TODO: to help us scale to more avatars it would be nice to not have to poll orb state here
|
||||
// if the geometry is loaded then turn off the orb
|
||||
// TODO: to help us scale to more avatars it would be nice to not have to poll this stuff every update
|
||||
if (avatar->getSkeletonModel()->isLoaded()) {
|
||||
// remove the orb if it is there
|
||||
avatar->removeOrb();
|
||||
if (avatar->needsPhysicsUpdate()) {
|
||||
_avatarsToChangeInPhysics.insert(avatar);
|
||||
}
|
||||
} else {
|
||||
avatar->updateOrbPosition();
|
||||
}
|
||||
|
@ -256,6 +275,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
|||
if (inView && avatar->hasNewJointData()) {
|
||||
numAvatarsUpdated++;
|
||||
}
|
||||
avatar->_transit.update(deltaTime, avatar->_globalPosition, _transitConfig);
|
||||
avatar->simulate(deltaTime, inView);
|
||||
avatar->updateRenderItem(renderTransaction);
|
||||
avatar->updateSpaceProxy(workloadTransaction);
|
||||
|
@ -456,31 +476,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<Avatar>(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<AvatarSharedPointer> removedAvatars;
|
||||
|
||||
{
|
||||
QWriteLocker locker(&_hashLock);
|
||||
|
||||
removedAvatars.reserve(_avatarHash.size());
|
||||
|
||||
auto avatarIterator = _avatarHash.begin();
|
||||
while (avatarIterator != _avatarHash.end()) {
|
||||
auto avatar = std::static_pointer_cast<Avatar>(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<OtherAvatar>(avatarIterator.value());
|
||||
|
@ -803,7 +829,7 @@ void AvatarManager::setAvatarSortCoefficient(const QString& name, const QScriptV
|
|||
}
|
||||
}
|
||||
|
||||
QVariantMap AvatarManager::getPalData(const QList<QString> specificAvatarIdentifiers) {
|
||||
QVariantMap AvatarManager::getPalData(const QList<QString> specificAvatarIdentifiers) {
|
||||
QJsonArray palData;
|
||||
|
||||
auto avatarMap = getHashCopy();
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include "MyAvatar.h"
|
||||
#include "OtherAvatar.h"
|
||||
|
||||
|
||||
using SortedAvatar = std::pair<float, std::shared_ptr<Avatar>>;
|
||||
|
||||
/**jsdoc
|
||||
|
@ -204,7 +205,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<AvatarSharedPointer> _avatarsToFade;
|
||||
|
||||
|
@ -227,6 +233,8 @@ private:
|
|||
mutable std::mutex _spaceLock;
|
||||
workload::SpacePointer _space;
|
||||
std::vector<int32_t> _spaceProxiesToDelete;
|
||||
|
||||
AvatarTransit::TransitConfig _transitConfig;
|
||||
};
|
||||
|
||||
#endif // hifi_AvatarManager_h
|
||||
|
|
|
@ -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),
|
||||
|
@ -114,11 +116,27 @@ MyAvatar::MyAvatar(QThread* thread) :
|
|||
_bodySensorMatrix(),
|
||||
_goToPending(false),
|
||||
_goToSafe(true),
|
||||
_goToFeetAjustment(false),
|
||||
_goToPosition(),
|
||||
_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<ClientTraitsHandler>(new ClientTraitsHandler(this));
|
||||
|
||||
|
@ -481,7 +499,7 @@ void MyAvatar::update(float deltaTime) {
|
|||
setCurrentStandingHeight(computeStandingHeightMode(getControllerPoseInAvatarFrame(controller::Action::HEAD)));
|
||||
setAverageHeadRotation(computeAverageHeadRotation(getControllerPoseInAvatarFrame(controller::Action::HEAD)));
|
||||
|
||||
if (_drawAverageFacingEnabled) {
|
||||
if (_drawAverageFacingEnabled) {
|
||||
auto sensorHeadPose = getControllerPoseInSensorFrame(controller::Action::HEAD);
|
||||
glm::vec3 worldHeadPos = transformPoint(getSensorToWorldMatrix(), sensorHeadPose.getTranslation());
|
||||
glm::vec3 worldFacingAverage = transformVectorFast(getSensorToWorldMatrix(), glm::vec3(_headControllerFacingMovingAverage.x, 0.0f, _headControllerFacingMovingAverage.y));
|
||||
|
@ -509,6 +527,12 @@ void MyAvatar::update(float deltaTime) {
|
|||
_physicsSafetyPending = getCollisionsEnabled();
|
||||
_characterController.recomputeFlying(); // In case we've gone to into the sky.
|
||||
}
|
||||
if (_goToFeetAjustment && _skeletonModelLoaded) {
|
||||
auto feetAjustment = getWorldPosition() - getWorldFeetPosition();
|
||||
_goToPosition = getWorldPosition() + feetAjustment;
|
||||
setWorldPosition(_goToPosition);
|
||||
_goToFeetAjustment = false;
|
||||
}
|
||||
if (_physicsSafetyPending && qApp->isPhysicsEnabled() && _characterController.isEnabledAndReady()) {
|
||||
// When needed and ready, arrange to check and fix.
|
||||
_physicsSafetyPending = false;
|
||||
|
@ -615,9 +639,8 @@ void MyAvatar::updateChildCauterization(SpatiallyNestablePointer object, bool ca
|
|||
|
||||
void MyAvatar::simulate(float deltaTime) {
|
||||
PerformanceTimer perfTimer("simulate");
|
||||
|
||||
animateScaleChanges(deltaTime);
|
||||
|
||||
|
||||
setFlyingEnabled(getFlyingEnabled());
|
||||
|
||||
if (_cauterizationNeedsUpdate) {
|
||||
|
@ -905,6 +928,7 @@ void MyAvatar::updateSensorToWorldMatrix() {
|
|||
updateJointFromController(controller::Action::RIGHT_HAND, _controllerRightHandMatrixCache);
|
||||
|
||||
if (hasSensorToWorldScaleChanged) {
|
||||
setTransitScale(sensorToWorldScale);
|
||||
emit sensorToWorldScaleChanged(sensorToWorldScale);
|
||||
}
|
||||
|
||||
|
@ -1135,88 +1159,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<QUuid> idHandle(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "avatarEntityData"
|
||||
<< QString::number(avatarEntityIndex + 1) << "id", QUuid());
|
||||
_avatarEntityIDSettings.push_back(idHandle);
|
||||
Setting::Handle<QByteArray> 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<HMDScriptingInterface>();
|
||||
_avatarEntitiesLock.withReadLock([&] {
|
||||
for (auto entityID : _avatarEntityData.keys()) {
|
||||
if (hmdInterface->getCurrentTabletFrameID() == entityID) {
|
||||
// don't persist the tablet between domains / sessions
|
||||
continue;
|
||||
}
|
||||
QList<QUuid> avatarEntityIDs = _avatarEntityData.keys();
|
||||
unsigned int avatarEntityCount = avatarEntityIDs.size();
|
||||
unsigned int previousAvatarEntityCount = _avatarEntityCountSetting.get(0);
|
||||
resizeAvatarEntitySettingHandles(std::max<unsigned int>(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 +1330,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<bool> 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 +1428,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();
|
||||
|
@ -1748,6 +1735,7 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) {
|
|||
|
||||
_headBoneSet.clear();
|
||||
_cauterizationNeedsUpdate = true;
|
||||
_skeletonModelLoaded = false;
|
||||
|
||||
std::shared_ptr<QMetaObject::Connection> skeletonConnection = std::make_shared<QMetaObject::Connection>();
|
||||
*skeletonConnection = QObject::connect(_skeletonModel.get(), &SkeletonModel::skeletonLoaded, [this, skeletonModelChangeCount, skeletonConnection]() {
|
||||
|
@ -1765,6 +1753,7 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) {
|
|||
_skeletonModel->setCauterizeBoneSet(_headBoneSet);
|
||||
_fstAnimGraphOverrideUrl = _skeletonModel->getGeometry()->getAnimGraphOverrideUrl();
|
||||
initAnimGraph();
|
||||
_skeletonModelLoaded = true;
|
||||
}
|
||||
QObject::disconnect(*skeletonConnection);
|
||||
});
|
||||
|
@ -2899,10 +2888,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 +2900,6 @@ void MyAvatar::restrictScaleFromDomainSettings(const QJsonObject& domainSettings
|
|||
|
||||
setModelScale(_targetScale);
|
||||
rebuildCollisionShape();
|
||||
settings.endGroup();
|
||||
|
||||
_haveReceivedHeightLimitsFromDomain = true;
|
||||
}
|
||||
|
@ -2925,10 +2910,7 @@ void MyAvatar::leaveDomain() {
|
|||
}
|
||||
|
||||
void MyAvatar::saveAvatarScale() {
|
||||
Settings settings;
|
||||
settings.beginGroup("Avatar");
|
||||
settings.setValue("scale", _targetScale);
|
||||
settings.endGroup();
|
||||
_scaleSetting.set(_targetScale);
|
||||
}
|
||||
|
||||
void MyAvatar::clearScaleRestriction() {
|
||||
|
@ -2972,46 +2954,10 @@ void MyAvatar::goToLocation(const QVariant& propertiesVar) {
|
|||
}
|
||||
|
||||
void MyAvatar::goToFeetLocation(const glm::vec3& newPosition,
|
||||
bool hasOrientation, const glm::quat& newOrientation,
|
||||
bool shouldFaceLocation) {
|
||||
|
||||
qCDebug(interfaceapp).nospace() << "MyAvatar goToFeetLocation - moving to " << newPosition.x << ", "
|
||||
<< newPosition.y << ", " << newPosition.z;
|
||||
|
||||
ShapeInfo shapeInfo;
|
||||
computeShapeInfo(shapeInfo);
|
||||
glm::vec3 halfExtents = shapeInfo.getHalfExtents();
|
||||
glm::vec3 localFeetPos = shapeInfo.getOffset() - glm::vec3(0.0f, halfExtents.y + halfExtents.x, 0.0f);
|
||||
glm::mat4 localFeet = createMatFromQuatAndPos(Quaternions::IDENTITY, localFeetPos);
|
||||
|
||||
glm::mat4 worldFeet = createMatFromQuatAndPos(Quaternions::IDENTITY, newPosition);
|
||||
|
||||
glm::mat4 avatarMat = worldFeet * glm::inverse(localFeet);
|
||||
|
||||
glm::vec3 adjustedPosition = extractTranslation(avatarMat);
|
||||
|
||||
_goToPending = true;
|
||||
_goToPosition = adjustedPosition;
|
||||
_goToOrientation = getWorldOrientation();
|
||||
if (hasOrientation) {
|
||||
qCDebug(interfaceapp).nospace() << "MyAvatar goToFeetLocation - new orientation is "
|
||||
<< newOrientation.x << ", " << newOrientation.y << ", " << newOrientation.z << ", " << newOrientation.w;
|
||||
|
||||
// orient the user to face the target
|
||||
glm::quat quatOrientation = cancelOutRollAndPitch(newOrientation);
|
||||
|
||||
if (shouldFaceLocation) {
|
||||
quatOrientation = newOrientation * glm::angleAxis(PI, Vectors::UP);
|
||||
|
||||
// move the user a couple units away
|
||||
const float DISTANCE_TO_USER = 2.0f;
|
||||
_goToPosition = adjustedPosition - quatOrientation * IDENTITY_FORWARD * DISTANCE_TO_USER;
|
||||
}
|
||||
|
||||
_goToOrientation = quatOrientation;
|
||||
}
|
||||
|
||||
emit transformChanged();
|
||||
bool hasOrientation, const glm::quat& newOrientation,
|
||||
bool shouldFaceLocation) {
|
||||
_goToFeetAjustment = true;
|
||||
goToLocation(newPosition, hasOrientation, newOrientation, shouldFaceLocation);
|
||||
}
|
||||
|
||||
void MyAvatar::goToLocation(const glm::vec3& newPosition,
|
||||
|
|
|
@ -550,6 +550,7 @@ public:
|
|||
float getHMDRollControlRate() const { return _hmdRollControlRate; }
|
||||
|
||||
// get/set avatar data
|
||||
void resizeAvatarEntitySettingHandles(unsigned int avatarEntityIndex);
|
||||
void saveData();
|
||||
void loadData();
|
||||
|
||||
|
@ -1114,6 +1115,8 @@ public:
|
|||
virtual QVariantList getAttachmentsVariant() const override;
|
||||
virtual void setAttachmentsVariant(const QVariantList& variant) override;
|
||||
|
||||
glm::vec3 getNextPosition() { return _goToPending ? _goToPosition : getWorldPosition(); };
|
||||
|
||||
public slots:
|
||||
|
||||
/**jsdoc
|
||||
|
@ -1731,6 +1734,7 @@ private:
|
|||
bool _goToPending { false };
|
||||
bool _physicsSafetyPending { false };
|
||||
bool _goToSafe { true };
|
||||
bool _goToFeetAjustment { false };
|
||||
glm::vec3 _goToPosition;
|
||||
glm::quat _goToOrientation;
|
||||
|
||||
|
@ -1806,6 +1810,24 @@ private:
|
|||
|
||||
bool _haveReceivedHeightLimitsFromDomain { false };
|
||||
int _disableHandTouchCount { 0 };
|
||||
bool _skeletonModelLoaded { false };
|
||||
|
||||
Setting::Handle<QString> _dominantHandSetting;
|
||||
Setting::Handle<float> _headPitchSetting;
|
||||
Setting::Handle<float> _scaleSetting;
|
||||
Setting::Handle<float> _yawSpeedSetting;
|
||||
Setting::Handle<float> _pitchSpeedSetting;
|
||||
Setting::Handle<QUrl> _fullAvatarURLSetting;
|
||||
Setting::Handle<QUrl> _fullAvatarModelNameSetting;
|
||||
Setting::Handle<QUrl> _animGraphURLSetting;
|
||||
Setting::Handle<QString> _displayNameSetting;
|
||||
Setting::Handle<QUrl> _collisionSoundURLSetting;
|
||||
Setting::Handle<bool> _useSnapTurnSetting;
|
||||
Setting::Handle<float> _userHeightSetting;
|
||||
Setting::Handle<bool> _flyingHMDSetting;
|
||||
Setting::Handle<int> _avatarEntityCountSetting;
|
||||
std::vector<Setting::Handle<QUuid>> _avatarEntityIDSettings;
|
||||
std::vector<Setting::Handle<QByteArray>> _avatarEntityDataSettings;
|
||||
};
|
||||
|
||||
QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode);
|
||||
|
|
|
@ -119,6 +119,11 @@ bool OtherAvatar::shouldBeInPhysicsSimulation() const {
|
|||
return (_workloadRegion < workload::Region::R3 && !isDead());
|
||||
}
|
||||
|
||||
bool OtherAvatar::needsPhysicsUpdate() const {
|
||||
constexpr uint32_t FLAGS_OF_INTEREST = Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS | Simulation::DIRTY_POSITION;
|
||||
return (_motionState && (bool)(_motionState->getIncomingDirtyFlags() & FLAGS_OF_INTEREST));
|
||||
}
|
||||
|
||||
void OtherAvatar::rebuildCollisionShape() {
|
||||
if (_motionState) {
|
||||
_motionState->addDirtyFlags(Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS);
|
||||
|
|
|
@ -43,6 +43,7 @@ public:
|
|||
|
||||
void setWorkloadRegion(uint8_t region);
|
||||
bool shouldBeInPhysicsSimulation() const;
|
||||
bool needsPhysicsUpdate() const;
|
||||
|
||||
friend AvatarManager;
|
||||
|
||||
|
|
|
@ -47,6 +47,54 @@ QmlCommerce::QmlCommerce() {
|
|||
_appsPath = PathUtils::getAppDataPath() + "Apps/";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void QmlCommerce::openSystemApp(const QString& appName) {
|
||||
static QMap<QString, QString> systemApps {
|
||||
{"GOTO", "hifi/tablet/TabletAddressDialog.qml"},
|
||||
{"PEOPLE", "hifi/Pal.qml"},
|
||||
{"WALLET", "hifi/commerce/wallet/Wallet.qml"},
|
||||
{"MARKET", "/marketplace.html"}
|
||||
};
|
||||
|
||||
static QMap<QString, QString> systemInject{
|
||||
{"MARKET", "/scripts/system/html/js/marketplacesInject.js"}
|
||||
};
|
||||
|
||||
|
||||
auto tablet = dynamic_cast<TabletProxy*>(
|
||||
DependencyManager::get<TabletScriptingInterface>()->getTablet("com.highfidelity.interface.tablet.system"));
|
||||
|
||||
QMap<QString, QString>::const_iterator appPathIter = systemApps.find(appName);
|
||||
if (appPathIter != systemApps.end()) {
|
||||
if (appPathIter->contains(".qml", Qt::CaseInsensitive)) {
|
||||
tablet->loadQMLSource(*appPathIter);
|
||||
}
|
||||
else if (appPathIter->contains(".html", Qt::CaseInsensitive)) {
|
||||
QMap<QString, QString>::const_iterator injectIter = systemInject.find(appName);
|
||||
if (appPathIter == systemInject.end()) {
|
||||
tablet->gotoWebScreen(NetworkingConstants::METAVERSE_SERVER_URL().toString() + *appPathIter);
|
||||
}
|
||||
else {
|
||||
QString inject = "file:///" + qApp->applicationDirPath() + *injectIter;
|
||||
tablet->gotoWebScreen(NetworkingConstants::METAVERSE_SERVER_URL().toString() + *appPathIter, inject);
|
||||
}
|
||||
}
|
||||
else {
|
||||
qCDebug(commerce) << "Attempted to open unknown type of URL!";
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
qCDebug(commerce) << "Attempted to open unknown APP!";
|
||||
return;
|
||||
}
|
||||
|
||||
DependencyManager::get<HMDScriptingInterface>()->openTablet();
|
||||
}
|
||||
|
||||
|
||||
void QmlCommerce::getWalletStatus() {
|
||||
auto wallet = DependencyManager::get<Wallet>();
|
||||
wallet->getWalletStatus();
|
||||
|
@ -199,12 +247,18 @@ void QmlCommerce::transferAssetToUsername(const QString& username,
|
|||
}
|
||||
|
||||
void QmlCommerce::replaceContentSet(const QString& itemHref, const QString& certificateID) {
|
||||
auto ledger = DependencyManager::get<Ledger>();
|
||||
ledger->updateLocation(certificateID, DependencyManager::get<AddressManager>()->getPlaceName(), true);
|
||||
if (!certificateID.isEmpty()) {
|
||||
auto ledger = DependencyManager::get<Ledger>();
|
||||
ledger->updateLocation(
|
||||
certificateID,
|
||||
DependencyManager::get<AddressManager>()->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 +282,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;
|
||||
}
|
||||
|
||||
|
@ -353,7 +408,7 @@ bool QmlCommerce::openApp(const QString& itemHref) {
|
|||
// Read from the file to know what .html or .qml document to open
|
||||
QFile appFile(_appsPath + "/" + appHref.fileName());
|
||||
if (!appFile.open(QIODevice::ReadOnly)) {
|
||||
qCDebug(commerce) << "Couldn't open local .app.json file.";
|
||||
qCDebug(commerce) << "Couldn't open local .app.json file:" << appFile;
|
||||
return false;
|
||||
}
|
||||
QJsonDocument appFileJsonDocument = QJsonDocument::fromJson(appFile.readAll());
|
||||
|
|
|
@ -24,6 +24,7 @@ class QmlCommerce : public QObject {
|
|||
|
||||
public:
|
||||
QmlCommerce();
|
||||
void openSystemApp(const QString& appPath);
|
||||
|
||||
signals:
|
||||
void walletStatusResult(uint walletStatus);
|
||||
|
|
|
@ -176,7 +176,7 @@ int main(int argc, const char* argv[]) {
|
|||
if (socket.waitForConnected(LOCAL_SERVER_TIMEOUT_MS)) {
|
||||
if (parser.isSet(urlOption)) {
|
||||
QUrl url = QUrl(parser.value(urlOption));
|
||||
if (url.isValid() && url.scheme() == URL_SCHEME_HIFI) {
|
||||
if (url.isValid() && (url.scheme() == URL_SCHEME_HIFI || url.scheme() == URL_SCHEME_HIFIAPP)) {
|
||||
qDebug() << "Writing URL to local socket";
|
||||
socket.write(url.toString().toUtf8());
|
||||
if (!socket.waitForBytesWritten(5000)) {
|
||||
|
|
|
@ -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<RayPickResult>(pickResult);
|
||||
if (!rayPickResult) {
|
||||
return std::make_shared<RayPickResult>();
|
||||
}
|
||||
return std::make_shared<RayPickResult>(*rayPickResult.get());
|
||||
}
|
||||
|
||||
QVariantMap LaserPointer::toVariantMap() const {
|
||||
QVariantMap qVariantMap;
|
||||
|
||||
|
|
|
@ -47,6 +47,8 @@ public:
|
|||
static std::shared_ptr<StartEndRenderState> 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;
|
||||
|
|
|
@ -30,6 +30,14 @@ ParabolaPointer::ParabolaPointer(const QVariant& rayProps, const RenderStateMap&
|
|||
{
|
||||
}
|
||||
|
||||
PickResultPointer ParabolaPointer::getPickResultCopy(const PickResultPointer& pickResult) const {
|
||||
auto parabolaPickResult = std::dynamic_pointer_cast<ParabolaPickResult>(pickResult);
|
||||
if (!parabolaPickResult) {
|
||||
return std::make_shared<ParabolaPickResult>();
|
||||
}
|
||||
return std::make_shared<ParabolaPickResult>(*parabolaPickResult.get());
|
||||
}
|
||||
|
||||
void ParabolaPointer::editRenderStatePath(const std::string& state, const QVariant& pathProps) {
|
||||
auto renderState = std::static_pointer_cast<RenderState>(_renderStates[state]);
|
||||
if (renderState) {
|
||||
|
@ -382,9 +390,8 @@ void ParabolaPointer::RenderState::ParabolaRenderItem::updateBounds() {
|
|||
|
||||
const gpu::PipelinePointer ParabolaPointer::RenderState::ParabolaRenderItem::getParabolaPipeline() {
|
||||
if (!_parabolaPipeline || !_transparentParabolaPipeline) {
|
||||
gpu::ShaderPointer program = gpu::Shader::createProgram(shader::render_utils::program::parabola);
|
||||
|
||||
{
|
||||
gpu::ShaderPointer program = gpu::Shader::createProgram(shader::render_utils::program::parabola);
|
||||
auto state = std::make_shared<gpu::State>();
|
||||
state->setDepthTest(true, true, gpu::LESS_EQUAL);
|
||||
state->setBlendFunction(false,
|
||||
|
@ -396,6 +403,7 @@ const gpu::PipelinePointer ParabolaPointer::RenderState::ParabolaRenderItem::get
|
|||
}
|
||||
|
||||
{
|
||||
gpu::ShaderPointer program = gpu::Shader::createProgram(shader::render_utils::program::parabola_translucent);
|
||||
auto state = std::make_shared<gpu::State>();
|
||||
state->setDepthTest(true, true, gpu::LESS_EQUAL);
|
||||
state->setBlendFunction(true,
|
||||
|
|
|
@ -102,6 +102,8 @@ public:
|
|||
static std::shared_ptr<StartEndRenderState> 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;
|
||||
|
|
|
@ -27,6 +27,7 @@ class StartEndRenderState {
|
|||
public:
|
||||
StartEndRenderState() {}
|
||||
StartEndRenderState(const OverlayID& startID, const OverlayID& endID);
|
||||
virtual ~StartEndRenderState() {}
|
||||
|
||||
const OverlayID& getStartID() const { return _startID; }
|
||||
const OverlayID& getEndID() const { return _endID; }
|
||||
|
|
|
@ -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<StylusPickResult>(pickResult);
|
||||
if (!stylusPickResult) {
|
||||
return std::make_shared<StylusPickResult>();
|
||||
}
|
||||
return std::make_shared<StylusPickResult>(*stylusPickResult.get());
|
||||
}
|
||||
|
||||
Pointer::PickedObject StylusPointer::getHoveredObject(const PickResultPointer& pickResult) {
|
||||
auto stylusPickResult = std::static_pointer_cast<const StylusPickResult>(pickResult);
|
||||
if (!stylusPickResult) {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -57,18 +57,24 @@ class QScriptEngine;
|
|||
* @property {Uuid} tabletScreenID - The UUID of the tablet's screen overlay.
|
||||
* @property {Uuid} homeButtonID - The UUID of the tablet's "home" button overlay.
|
||||
* @property {Uuid} homeButtonHighlightID - The UUID of the tablet's "home" button highlight overlay.
|
||||
* @property {Uuid} miniTabletID - The UUID of the mini tablet's body model overlay. <code>null</code> if not in HMD mode.
|
||||
* @property {Uuid} miniTabletScreenID - The UUID of the mini tablet's screen overlay. <code>null</code> if not in HMD mode.
|
||||
* @property {number} miniTabletHand - The hand that the mini tablet is displayed on: <code>0</code> for left hand,
|
||||
* <code>1</code> for right hand, <code>-1</code> if not in HMD mode.
|
||||
*/
|
||||
class HMDScriptingInterface : public AbstractHMDScriptingInterface, public Dependency {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(glm::vec3 position READ getPosition)
|
||||
Q_PROPERTY(glm::quat orientation READ getOrientation)
|
||||
Q_PROPERTY(bool mounted READ isMounted NOTIFY mountedChanged)
|
||||
Q_PROPERTY(bool showTablet READ getShouldShowTablet)
|
||||
Q_PROPERTY(bool tabletContextualMode READ getTabletContextualMode)
|
||||
Q_PROPERTY(QUuid tabletID READ getCurrentTabletFrameID WRITE setCurrentTabletFrameID)
|
||||
Q_PROPERTY(QUuid homeButtonID READ getCurrentHomeButtonID WRITE setCurrentHomeButtonID)
|
||||
Q_PROPERTY(QUuid tabletScreenID READ getCurrentTabletScreenID WRITE setCurrentTabletScreenID)
|
||||
Q_PROPERTY(QUuid homeButtonHighlightID READ getCurrentHomeButtonHighlightID WRITE setCurrentHomeButtonHighlightID)
|
||||
Q_PROPERTY(QUuid miniTabletID READ getCurrentMiniTabletID WRITE setCurrentMiniTabletID)
|
||||
Q_PROPERTY(QUuid miniTabletScreenID READ getCurrentMiniTabletScreenID WRITE setCurrentMiniTabletScreenID)
|
||||
Q_PROPERTY(int miniTabletHand READ getCurrentMiniTabletHand WRITE setCurrentMiniTabletHand)
|
||||
|
||||
public:
|
||||
|
||||
|
@ -350,7 +356,7 @@ public:
|
|||
static QScriptValue getHUDLookAtPosition2D(QScriptContext* context, QScriptEngine* engine);
|
||||
static QScriptValue getHUDLookAtPosition3D(QScriptContext* context, QScriptEngine* engine);
|
||||
|
||||
bool isMounted() const;
|
||||
bool isMounted() const override;
|
||||
|
||||
void toggleShouldShowTablet();
|
||||
void setShouldShowTablet(bool value);
|
||||
|
@ -369,6 +375,15 @@ public:
|
|||
void setCurrentTabletScreenID(QUuid tabletID) { _tabletScreenID = tabletID; }
|
||||
QUuid getCurrentTabletScreenID() const { return _tabletScreenID; }
|
||||
|
||||
void setCurrentMiniTabletID(QUuid miniTabletID) { _miniTabletID = miniTabletID; }
|
||||
QUuid getCurrentMiniTabletID() const { return _miniTabletID; }
|
||||
|
||||
void setCurrentMiniTabletScreenID(QUuid miniTabletScreenID) { _miniTabletScreenID = miniTabletScreenID; }
|
||||
QUuid getCurrentMiniTabletScreenID() const { return _miniTabletScreenID; }
|
||||
|
||||
void setCurrentMiniTabletHand(int miniTabletHand) { _miniTabletHand = miniTabletHand; }
|
||||
int getCurrentMiniTabletHand() const { return _miniTabletHand; }
|
||||
|
||||
private:
|
||||
bool _showTablet { false };
|
||||
bool _tabletContextualMode { false };
|
||||
|
@ -377,6 +392,9 @@ private:
|
|||
QUuid _homeButtonID;
|
||||
QUuid _tabletEntityID;
|
||||
QUuid _homeButtonHighlightID;
|
||||
QUuid _miniTabletID;
|
||||
QUuid _miniTabletScreenID;
|
||||
int _miniTabletHand { -1 };
|
||||
|
||||
// Get the position of the HMD
|
||||
glm::vec3 getPosition() const;
|
||||
|
|
|
@ -134,7 +134,8 @@ void WindowScriptingInterface::openUrl(const QUrl& url) {
|
|||
DependencyManager::get<AddressManager>()->handleLookupString(url.toString());
|
||||
} else {
|
||||
#if defined(Q_OS_ANDROID)
|
||||
QList<QString> args = { url.toString() };
|
||||
QMap<QString, QString> args;
|
||||
args["url"] = url.toString();
|
||||
AndroidHelper::instance().requestActivity("WebView", true, args);
|
||||
#else
|
||||
// address manager did not handle - ask QDesktopServices to handle
|
||||
|
@ -180,6 +181,14 @@ void WindowScriptingInterface::setPreviousBrowseAssetLocation(const QString& loc
|
|||
Setting::Handle<QVariant>(LAST_BROWSE_ASSETS_LOCATION_SETTING).set(location);
|
||||
}
|
||||
|
||||
bool WindowScriptingInterface::getInterstitialModeEnabled() const {
|
||||
return DependencyManager::get<NodeList>()->getDomainHandler().getInterstitialModeEnabled();
|
||||
}
|
||||
|
||||
void WindowScriptingInterface::setInterstitialModeEnabled(bool enableInterstitialMode) {
|
||||
DependencyManager::get<NodeList>()->getDomainHandler().setInterstitialModeEnabled(enableInterstitialMode);
|
||||
}
|
||||
|
||||
bool WindowScriptingInterface::isPointOnDesktopWindow(QVariant point) {
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
return offscreenUi->isPointOnDesktopWindow(point);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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<QString, qint64> _animVarChangedTimers; // last time animVar value has changed.
|
||||
|
||||
QStringList _animStateMachines;
|
||||
|
||||
QString _positionText;
|
||||
QString _rotationText;
|
||||
QString _velocityText;
|
||||
};
|
||||
|
||||
#endif // hifi_AnimStats_h
|
||||
|
|
|
@ -75,7 +75,14 @@ void OverlayConductor::centerUI() {
|
|||
|
||||
void OverlayConductor::update(float dt) {
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
bool currentVisible = !offscreenUi->getDesktop()->property("pinned").toBool();
|
||||
if (!offscreenUi) {
|
||||
return;
|
||||
}
|
||||
auto desktop = offscreenUi->getDesktop();
|
||||
if (!desktop) {
|
||||
return;
|
||||
}
|
||||
bool currentVisible = !desktop->property("pinned").toBool();
|
||||
|
||||
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
// centerUI when hmd mode is first enabled and mounted
|
||||
|
|
|
@ -74,7 +74,7 @@ void Circle3DOverlay::render(RenderArgs* args) {
|
|||
|
||||
const float FULL_CIRCLE = 360.0f;
|
||||
const float SLICES = 180.0f; // The amount of segment to create the circle
|
||||
const float SLICE_ANGLE = FULL_CIRCLE / SLICES;
|
||||
const float SLICE_ANGLE_RADIANS = glm::radians(FULL_CIRCLE / SLICES);
|
||||
const float MAX_COLOR = 255.0f;
|
||||
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
|
@ -111,28 +111,38 @@ void Circle3DOverlay::render(RenderArgs* args) {
|
|||
vec4 innerEndColor = vec4(toGlm(_innerEndColor), _innerEndAlpha) * pulseModifier;
|
||||
vec4 outerEndColor = vec4(toGlm(_outerEndColor), _outerEndAlpha) * pulseModifier;
|
||||
|
||||
const auto startAtRadians = glm::radians(_startAt);
|
||||
const auto endAtRadians = glm::radians(_endAt);
|
||||
|
||||
const auto totalRange = _endAt - _startAt;
|
||||
if (_innerRadius <= 0) {
|
||||
_solidPrimitive = gpu::TRIANGLE_FAN;
|
||||
points << vec2();
|
||||
colors << innerStartColor;
|
||||
for (float angle = _startAt; angle <= _endAt; angle += SLICE_ANGLE) {
|
||||
float range = (angle - _startAt) / (_endAt - _startAt);
|
||||
float angleRadians = glm::radians(angle);
|
||||
for (float angleRadians = startAtRadians; angleRadians < endAtRadians; angleRadians += SLICE_ANGLE_RADIANS) {
|
||||
float range = (angleRadians - startAtRadians) / totalRange;
|
||||
points << glm::vec2(cosf(angleRadians) * _outerRadius, sinf(angleRadians) * _outerRadius);
|
||||
colors << glm::mix(outerStartColor, outerEndColor, range);
|
||||
}
|
||||
points << glm::vec2(cosf(endAtRadians) * _outerRadius, sinf(endAtRadians) * _outerRadius);
|
||||
colors << outerEndColor;
|
||||
|
||||
} else {
|
||||
_solidPrimitive = gpu::TRIANGLE_STRIP;
|
||||
for (float angle = _startAt; angle <= _endAt; angle += SLICE_ANGLE) {
|
||||
float range = (angle - _startAt) / (_endAt - _startAt);
|
||||
for (float angleRadians = startAtRadians; angleRadians < endAtRadians; angleRadians += SLICE_ANGLE_RADIANS) {
|
||||
float range = (angleRadians - startAtRadians) / totalRange;
|
||||
|
||||
float angleRadians = glm::radians(angle);
|
||||
points << glm::vec2(cosf(angleRadians) * _innerRadius, sinf(angleRadians) * _innerRadius);
|
||||
colors << glm::mix(innerStartColor, innerEndColor, range);
|
||||
|
||||
points << glm::vec2(cosf(angleRadians) * _outerRadius, sinf(angleRadians) * _outerRadius);
|
||||
colors << glm::mix(outerStartColor, outerEndColor, range);
|
||||
}
|
||||
points << glm::vec2(cosf(endAtRadians) * _innerRadius, sinf(endAtRadians) * _innerRadius);
|
||||
colors << innerEndColor;
|
||||
|
||||
points << glm::vec2(cosf(endAtRadians) * _outerRadius, sinf(endAtRadians) * _outerRadius);
|
||||
colors << outerEndColor;
|
||||
}
|
||||
geometryCache->updateVertices(_quadVerticesID, points, colors);
|
||||
}
|
||||
|
@ -147,29 +157,28 @@ void Circle3DOverlay::render(RenderArgs* args) {
|
|||
if (geometryChanged) {
|
||||
QVector<glm::vec2> points;
|
||||
|
||||
float angle = _startAt;
|
||||
float angleInRadians = glm::radians(angle);
|
||||
glm::vec2 firstPoint(cosf(angleInRadians) * _outerRadius, sinf(angleInRadians) * _outerRadius);
|
||||
const auto startAtRadians = glm::radians(_startAt);
|
||||
const auto endAtRadians = glm::radians(_endAt);
|
||||
|
||||
float angleRadians = startAtRadians;
|
||||
glm::vec2 firstPoint(cosf(angleRadians) * _outerRadius, sinf(angleRadians) * _outerRadius);
|
||||
points << firstPoint;
|
||||
|
||||
while (angle < _endAt) {
|
||||
angle += SLICE_ANGLE;
|
||||
angleInRadians = glm::radians(angle);
|
||||
glm::vec2 thisPoint(cosf(angleInRadians) * _outerRadius, sinf(angleInRadians) * _outerRadius);
|
||||
while (angleRadians < endAtRadians) {
|
||||
angleRadians += SLICE_ANGLE_RADIANS;
|
||||
glm::vec2 thisPoint(cosf(angleRadians) * _outerRadius, sinf(angleRadians) * _outerRadius);
|
||||
points << thisPoint;
|
||||
|
||||
if (getIsDashedLine()) {
|
||||
angle += SLICE_ANGLE / 2.0f; // short gap
|
||||
angleInRadians = glm::radians(angle);
|
||||
glm::vec2 dashStartPoint(cosf(angleInRadians) * _outerRadius, sinf(angleInRadians) * _outerRadius);
|
||||
angleRadians += SLICE_ANGLE_RADIANS / 2.0f; // short gap
|
||||
glm::vec2 dashStartPoint(cosf(angleRadians) * _outerRadius, sinf(angleRadians) * _outerRadius);
|
||||
points << dashStartPoint;
|
||||
}
|
||||
}
|
||||
|
||||
// get the last slice portion....
|
||||
angle = _endAt;
|
||||
angleInRadians = glm::radians(angle);
|
||||
glm::vec2 lastPoint(cosf(angleInRadians) * _outerRadius, sinf(angleInRadians) * _outerRadius);
|
||||
angleRadians = endAtRadians;
|
||||
glm::vec2 lastPoint(cosf(angleRadians) * _outerRadius, sinf(angleRadians) * _outerRadius);
|
||||
points << lastPoint;
|
||||
geometryCache->updateVertices(_lineVerticesID, points, vec4(toGlm(getColor()), getAlpha()));
|
||||
}
|
||||
|
|
|
@ -252,12 +252,6 @@ bool ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityIt
|
|||
void ContextOverlayInterface::contextOverlays_mousePressOnOverlay(const OverlayID& overlayID, const PointerEvent& event) {
|
||||
if (overlayID == _contextOverlayID && event.getButton() == PointerEvent::PrimaryButton) {
|
||||
qCDebug(context_overlay) << "Clicked Context Overlay. Entity ID:" << _currentEntityWithContextOverlay << "Overlay ID:" << overlayID;
|
||||
Setting::Handle<bool> _settingSwitch{ "commerce", true };
|
||||
if (_settingSwitch.get()) {
|
||||
openInspectionCertificate();
|
||||
} else {
|
||||
openMarketplace();
|
||||
}
|
||||
emit contextOverlayClicked(_currentEntityWithContextOverlay);
|
||||
_contextOverlayJustClicked = true;
|
||||
}
|
||||
|
@ -390,34 +384,6 @@ void ContextOverlayInterface::requestOwnershipVerification(const QUuid& entityID
|
|||
}
|
||||
}
|
||||
|
||||
static const QString INSPECTION_CERTIFICATE_QML_PATH = "hifi/commerce/inspectionCertificate/InspectionCertificate.qml";
|
||||
void ContextOverlayInterface::openInspectionCertificate() {
|
||||
// lets open the tablet to the inspection certificate QML
|
||||
if (!_currentEntityWithContextOverlay.isNull() && _entityMarketplaceID.length() > 0) {
|
||||
setLastInspectedEntity(_currentEntityWithContextOverlay);
|
||||
auto tablet = dynamic_cast<TabletProxy*>(_tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"));
|
||||
tablet->loadQMLSource(INSPECTION_CERTIFICATE_QML_PATH);
|
||||
_hmdScriptingInterface->openTablet();
|
||||
}
|
||||
}
|
||||
|
||||
static const QString MARKETPLACE_BASE_URL = NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/marketplace/items/";
|
||||
|
||||
void ContextOverlayInterface::openMarketplace() {
|
||||
// lets open the tablet and go to the current item in
|
||||
// the marketplace (if the current entity has a
|
||||
// marketplaceID)
|
||||
if (!_currentEntityWithContextOverlay.isNull() && _entityMarketplaceID.length() > 0) {
|
||||
auto tablet = dynamic_cast<TabletProxy*>(_tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"));
|
||||
// construct the url to the marketplace item
|
||||
QString url = MARKETPLACE_BASE_URL + _entityMarketplaceID;
|
||||
QString MARKETPLACES_INJECT_SCRIPT_PATH = "file:///" + qApp->applicationDirPath() + "/scripts/system/html/js/marketplacesInject.js";
|
||||
tablet->gotoWebScreen(url, MARKETPLACES_INJECT_SCRIPT_PATH);
|
||||
_hmdScriptingInterface->openTablet();
|
||||
_isInMarketplaceInspectionMode = true;
|
||||
}
|
||||
}
|
||||
|
||||
void ContextOverlayInterface::enableEntityHighlight(const EntityItemID& entityItemID) {
|
||||
_selectionScriptingInterface->addToSelectedItemsList("contextOverlayHighlightList", "entity", entityItemID);
|
||||
}
|
||||
|
|
|
@ -94,8 +94,6 @@ private:
|
|||
|
||||
bool _isInMarketplaceInspectionMode { false };
|
||||
|
||||
void openInspectionCertificate();
|
||||
void openMarketplace();
|
||||
void enableEntityHighlight(const EntityItemID& entityItemID);
|
||||
void disableEntityHighlight(const EntityItemID& entityItemID);
|
||||
|
||||
|
|
|
@ -114,7 +114,6 @@ void Text3DOverlay::render(RenderArgs* args) {
|
|||
|
||||
float scaleFactor = (maxHeight / FIXED_FONT_SCALING_RATIO) * _lineHeight;
|
||||
|
||||
glm::vec2 clipMinimum(0.0f, 0.0f);
|
||||
glm::vec2 clipDimensions((dimensions.x - (_leftMargin + _rightMargin)) / scaleFactor,
|
||||
(dimensions.y - (_topMargin + _bottomMargin)) / scaleFactor);
|
||||
|
||||
|
@ -296,4 +295,4 @@ QSizeF Text3DOverlay::textSize(const QString& text) const {
|
|||
float pointToWorldScale = (maxHeight / FIXED_FONT_SCALING_RATIO) * _lineHeight;
|
||||
|
||||
return QSizeF(extents.x, extents.y) * pointToWorldScale;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -140,14 +140,19 @@ std::map<QString, QString> 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<QString, QString> 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));
|
||||
}
|
||||
|
|
|
@ -53,7 +53,6 @@
|
|||
#include "AudioHelpers.h"
|
||||
|
||||
#if defined(Q_OS_ANDROID)
|
||||
#define VOICE_RECOGNITION "voicerecognition"
|
||||
#include <QtAndroidExtras/QAndroidJniObject>
|
||||
#endif
|
||||
|
||||
|
@ -101,6 +100,13 @@ QList<QAudioDeviceInfo> 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<bool> enableAEC(SETTING_AEC_KEY, false);
|
||||
bool aecEnabled = enableAEC.get();
|
||||
auto audioClient = DependencyManager::get<AudioClient>();
|
||||
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<jstring>("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<bool> 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) {
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#define hifi_AudioHRTF_h
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
static const int HRTF_AZIMUTHS = 72; // 360 / 5-degree steps
|
||||
static const int HRTF_TAPS = 64; // minimum-phase FIR coefficients
|
||||
|
@ -56,6 +57,27 @@ public:
|
|||
void setGainAdjustment(float gain) { _gainAdjust = HRTF_GAIN * gain; };
|
||||
float getGainAdjustment() { return _gainAdjust; }
|
||||
|
||||
// clear internal state, but retain settings
|
||||
void reset() {
|
||||
// FIR history
|
||||
memset(_firState, 0, sizeof(_firState));
|
||||
|
||||
// integer delay history
|
||||
memset(_delayState, 0, sizeof(_delayState));
|
||||
|
||||
// biquad history
|
||||
memset(_bqState, 0, sizeof(_bqState));
|
||||
|
||||
// parameter history
|
||||
_azimuthState = 0.0f;
|
||||
_distanceState = 0.0f;
|
||||
_gainState = 0.0f;
|
||||
|
||||
// _gainAdjust is retained
|
||||
|
||||
_silentState = true;
|
||||
}
|
||||
|
||||
private:
|
||||
AudioHRTF(const AudioHRTF&) = delete;
|
||||
AudioHRTF& operator=(const AudioHRTF&) = delete;
|
||||
|
@ -88,7 +110,7 @@ private:
|
|||
// global and local gain adjustment
|
||||
float _gainAdjust = HRTF_GAIN;
|
||||
|
||||
bool _silentState = false;
|
||||
bool _silentState = true;
|
||||
};
|
||||
|
||||
#endif // AudioHRTF_h
|
||||
|
|
|
@ -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) :
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -113,6 +113,80 @@ void Avatar::setShowNamesAboveHeads(bool show) {
|
|||
showNamesAboveHeads = show;
|
||||
}
|
||||
|
||||
AvatarTransit::Status AvatarTransit::update(float deltaTime, const glm::vec3& avatarPosition, const AvatarTransit::TransitConfig& config) {
|
||||
glm::vec3 currentPosition = _isTransiting ? _currentPosition : avatarPosition;
|
||||
float oneFrameDistance = glm::length(currentPosition - _lastPosition);
|
||||
const float MAX_TRANSIT_DISTANCE = 30.0f;
|
||||
float scaledMaxTransitDistance = MAX_TRANSIT_DISTANCE * _scale;
|
||||
if (oneFrameDistance > config._triggerDistance && oneFrameDistance < scaledMaxTransitDistance && !_isTransiting) {
|
||||
start(deltaTime, _lastPosition, currentPosition, config);
|
||||
}
|
||||
_lastPosition = currentPosition;
|
||||
_status = updatePosition(deltaTime);
|
||||
return _status;
|
||||
}
|
||||
|
||||
void AvatarTransit::start(float deltaTime, const glm::vec3& startPosition, const glm::vec3& endPosition, const AvatarTransit::TransitConfig& config) {
|
||||
_startPosition = startPosition;
|
||||
_endPosition = endPosition;
|
||||
|
||||
_transitLine = endPosition - startPosition;
|
||||
_totalDistance = glm::length(_transitLine);
|
||||
_easeType = config._easeType;
|
||||
const float REFERENCE_FRAMES_PER_SECOND = 30.0f;
|
||||
|
||||
int transitFrames = (!config._isDistanceBased) ? config._totalFrames : config._framesPerMeter * _totalDistance;
|
||||
_totalTime = (float)transitFrames / REFERENCE_FRAMES_PER_SECOND;
|
||||
_currentTime = 0.0f;
|
||||
_isTransiting = true;
|
||||
}
|
||||
|
||||
float AvatarTransit::getEaseValue(AvatarTransit::EaseType type, float value) {
|
||||
switch (type) {
|
||||
case EaseType::NONE:
|
||||
return value;
|
||||
break;
|
||||
case EaseType::EASE_IN:
|
||||
return value * value;
|
||||
break;
|
||||
case EaseType::EASE_OUT:
|
||||
return value * (2.0f - value);
|
||||
break;
|
||||
case EaseType::EASE_IN_OUT:
|
||||
return (value < 0.5f) ? 2.0f * value * value : -1.0f + (4.0f - 2.0f * value) * value;
|
||||
break;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
AvatarTransit::Status AvatarTransit::updatePosition(float deltaTime) {
|
||||
Status status = Status::IDLE;
|
||||
if (_isTransiting) {
|
||||
float nextTime = _currentTime + deltaTime;
|
||||
glm::vec3 newPosition;
|
||||
if (nextTime >= _totalTime) {
|
||||
_currentPosition = _endPosition;
|
||||
_isTransiting = false;
|
||||
status = Status::END_TRANSIT;
|
||||
} else {
|
||||
if (_currentTime == 0) {
|
||||
status = Status::START_TRANSIT;
|
||||
} else {
|
||||
status = Status::TRANSITING;
|
||||
}
|
||||
float percentageIntoTransit = nextTime / _totalTime;
|
||||
_currentPosition = _startPosition + getEaseValue(_easeType, percentageIntoTransit) * _transitLine;
|
||||
}
|
||||
_currentTime = nextTime;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
bool AvatarTransit::getNextPosition(glm::vec3& nextPosition) {
|
||||
nextPosition = _currentPosition;
|
||||
return _isTransiting;
|
||||
}
|
||||
|
||||
Avatar::Avatar(QThread* thread) :
|
||||
_voiceSphereID(GeometryCache::UNKNOWN_ID)
|
||||
{
|
||||
|
@ -449,7 +523,18 @@ void Avatar::relayJointDataToChildren() {
|
|||
|
||||
void Avatar::simulate(float deltaTime, bool inView) {
|
||||
PROFILE_RANGE(simulation, "simulate");
|
||||
|
||||
|
||||
if (_transit.isTransiting()) {
|
||||
glm::vec3 nextPosition;
|
||||
if (_transit.getNextPosition(nextPosition)) {
|
||||
_globalPosition = nextPosition;
|
||||
_globalPositionChanged = usecTimestampNow();
|
||||
if (!hasParent()) {
|
||||
setLocalPosition(nextPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_simulationRate.increment();
|
||||
if (inView) {
|
||||
_simulationInViewRate.increment();
|
||||
|
@ -460,7 +545,7 @@ void Avatar::simulate(float deltaTime, bool inView) {
|
|||
PROFILE_RANGE(simulation, "updateJoints");
|
||||
if (inView) {
|
||||
Head* head = getHead();
|
||||
if (_hasNewJointData) {
|
||||
if (_hasNewJointData || _transit.isTransiting()) {
|
||||
_skeletonModel->getRig().copyJointsFromJointData(_jointData);
|
||||
glm::mat4 rootTransform = glm::scale(_skeletonModel->getScale()) * glm::translate(_skeletonModel->getOffset());
|
||||
_skeletonModel->getRig().computeExternalPoses(rootTransform);
|
||||
|
@ -1881,6 +1966,22 @@ float Avatar::getUnscaledEyeHeightFromSkeleton() const {
|
|||
}
|
||||
}
|
||||
|
||||
AvatarTransit::Status Avatar::updateTransit(float deltaTime, const glm::vec3& avatarPosition, const AvatarTransit::TransitConfig& config) {
|
||||
std::lock_guard<std::mutex> lock(_transitLock);
|
||||
return _transit.update(deltaTime, avatarPosition, config);
|
||||
}
|
||||
|
||||
void Avatar::setTransitScale(float scale) {
|
||||
std::lock_guard<std::mutex> lock(_transitLock);
|
||||
return _transit.setScale(scale);
|
||||
}
|
||||
|
||||
void Avatar::overrideNextPackagePositionData(const glm::vec3& position) {
|
||||
std::lock_guard<std::mutex> lock(_transitLock);
|
||||
_overrideGlobalPosition = true;
|
||||
_globalPositionOverride = position;
|
||||
}
|
||||
|
||||
void Avatar::addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName) {
|
||||
std::lock_guard<std::mutex> lock(_materialsLock);
|
||||
_materials[parentMaterialName].push(material);
|
||||
|
|
|
@ -50,6 +50,62 @@ enum ScreenTintLayer {
|
|||
|
||||
class Texture;
|
||||
|
||||
class AvatarTransit {
|
||||
public:
|
||||
enum Status {
|
||||
IDLE = 0,
|
||||
START_TRANSIT,
|
||||
TRANSITING,
|
||||
END_TRANSIT
|
||||
};
|
||||
|
||||
enum EaseType {
|
||||
NONE = 0,
|
||||
EASE_IN,
|
||||
EASE_OUT,
|
||||
EASE_IN_OUT
|
||||
};
|
||||
|
||||
struct TransitConfig {
|
||||
TransitConfig() {};
|
||||
int _totalFrames { 0 };
|
||||
int _framesPerMeter { 0 };
|
||||
bool _isDistanceBased { false };
|
||||
float _triggerDistance { 0 };
|
||||
EaseType _easeType { EaseType::EASE_OUT };
|
||||
};
|
||||
|
||||
AvatarTransit() {};
|
||||
Status update(float deltaTime, const glm::vec3& avatarPosition, const TransitConfig& config);
|
||||
Status getStatus() { return _status; }
|
||||
bool isTransiting() { return _isTransiting; }
|
||||
glm::vec3 getCurrentPosition() { return _currentPosition; }
|
||||
bool getNextPosition(glm::vec3& nextPosition);
|
||||
glm::vec3 getEndPosition() { return _endPosition; }
|
||||
float getTransitTime() { return _totalTime; }
|
||||
void setScale(float scale) { _scale = scale; }
|
||||
|
||||
private:
|
||||
Status updatePosition(float deltaTime);
|
||||
void start(float deltaTime, const glm::vec3& startPosition, const glm::vec3& endPosition, const TransitConfig& config);
|
||||
float getEaseValue(AvatarTransit::EaseType type, float value);
|
||||
bool _isTransiting { false };
|
||||
|
||||
glm::vec3 _startPosition;
|
||||
glm::vec3 _endPosition;
|
||||
glm::vec3 _currentPosition;
|
||||
|
||||
glm::vec3 _lastPosition;
|
||||
|
||||
glm::vec3 _transitLine;
|
||||
float _totalDistance { 0.0f };
|
||||
float _totalTime { 0.0f };
|
||||
float _currentTime { 0.0f };
|
||||
EaseType _easeType { EaseType::EASE_OUT };
|
||||
Status _status { Status::IDLE };
|
||||
float _scale { 1.0f };
|
||||
};
|
||||
|
||||
class Avatar : public AvatarData, public scriptable::ModelProvider {
|
||||
Q_OBJECT
|
||||
|
||||
|
@ -370,6 +426,13 @@ public:
|
|||
|
||||
virtual scriptable::ScriptableModelBase getScriptableModel() override;
|
||||
|
||||
std::shared_ptr<AvatarTransit> getTransit() { return std::make_shared<AvatarTransit>(_transit); };
|
||||
|
||||
AvatarTransit::Status updateTransit(float deltaTime, const glm::vec3& avatarPosition, const AvatarTransit::TransitConfig& config);
|
||||
void setTransitScale(float scale);
|
||||
|
||||
void overrideNextPackagePositionData(const glm::vec3& position);
|
||||
|
||||
signals:
|
||||
void targetScaleChanged(float targetScale);
|
||||
|
||||
|
@ -505,6 +568,7 @@ protected:
|
|||
RateCounter<> _skeletonModelSimulationRate;
|
||||
RateCounter<> _jointDataSimulationRate;
|
||||
|
||||
|
||||
protected:
|
||||
class AvatarEntityDataHash {
|
||||
public:
|
||||
|
@ -528,6 +592,9 @@ protected:
|
|||
bool _reconstructSoftEntitiesJointMap { false };
|
||||
float _modelScale { 1.0f };
|
||||
|
||||
AvatarTransit _transit;
|
||||
std::mutex _transitLock;
|
||||
|
||||
static int _jointConesID;
|
||||
|
||||
int _voiceSphereID;
|
||||
|
|
|
@ -369,7 +369,12 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
|||
|
||||
if (hasAvatarGlobalPosition) {
|
||||
auto startSection = destinationBuffer;
|
||||
AVATAR_MEMCPY(_globalPosition);
|
||||
if (_overrideGlobalPosition) {
|
||||
AVATAR_MEMCPY(_globalPositionOverride);
|
||||
} else {
|
||||
AVATAR_MEMCPY(_globalPosition);
|
||||
}
|
||||
|
||||
|
||||
int numBytes = destinationBuffer - startSection;
|
||||
|
||||
|
@ -2088,6 +2093,10 @@ void AvatarData::sendAvatarDataPacket(bool sendAll) {
|
|||
}
|
||||
}
|
||||
|
||||
if (_overrideGlobalPosition) {
|
||||
_overrideGlobalPosition = false;
|
||||
}
|
||||
|
||||
doneEncoding(cullSmallData);
|
||||
|
||||
static AvatarDataSequenceNumber sequenceNumber = 0;
|
||||
|
@ -2829,8 +2838,10 @@ void RayToAvatarIntersectionResultFromScriptValue(const QScriptValue& object, Ra
|
|||
value.extraInfo = object.property("extraInfo").toVariant().toMap();
|
||||
}
|
||||
|
||||
// these coefficients can be changed via JS for experimental tuning
|
||||
// use AvatatManager.setAvatarSortCoefficient("name", value) by a user with domain kick-rights
|
||||
float AvatarData::_avatarSortCoefficientSize { 8.0f };
|
||||
float AvatarData::_avatarSortCoefficientCenter { 4.0f };
|
||||
float AvatarData::_avatarSortCoefficientCenter { 0.25f };
|
||||
float AvatarData::_avatarSortCoefficientAge { 1.0f };
|
||||
|
||||
QScriptValue AvatarEntityMapToScriptValue(QScriptEngine* engine, const AvatarEntityMap& value) {
|
||||
|
|
|
@ -1372,7 +1372,8 @@ protected:
|
|||
// where Entities are located. This is currently only used by the mixer to decide how often to send
|
||||
// updates about one avatar to another.
|
||||
glm::vec3 _globalPosition { 0, 0, 0 };
|
||||
|
||||
glm::vec3 _globalPositionOverride { 0, 0, 0 };
|
||||
bool _overrideGlobalPosition { false };
|
||||
|
||||
quint64 _globalPositionChanged { 0 };
|
||||
quint64 _avatarBoundingBoxChanged { 0 };
|
||||
|
|
|
@ -66,6 +66,22 @@ void AvatarReplicas::removeReplicas(const QUuid& parentID) {
|
|||
}
|
||||
}
|
||||
|
||||
std::vector<AvatarSharedPointer> AvatarReplicas::takeReplicas(const QUuid& parentID) {
|
||||
std::vector<AvatarSharedPointer> 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];
|
||||
|
@ -250,6 +266,7 @@ AvatarSharedPointer AvatarHashMap::parseAvatarData(QSharedPointer<ReceivedMessag
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// have the matching (or new) avatar parse the data from the packet
|
||||
int bytesRead = avatar->parseDataFromBuffer(byteArray);
|
||||
message->seek(positionBeforeRead + bytesRead);
|
||||
|
@ -300,7 +317,6 @@ void AvatarHashMap::processAvatarIdentityPacket(QSharedPointer<ReceivedMessage>
|
|||
// In this case, the "sendingNode" is the Avatar Mixer.
|
||||
avatar->processAvatarIdentity(message->getMessage(), identityChanged, displayNameChanged);
|
||||
_replicas.processAvatarIdentity(identityUUID, message->getMessage(), identityChanged, displayNameChanged);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -313,6 +329,7 @@ void AvatarHashMap::processBulkAvatarTraits(QSharedPointer<ReceivedMessage> mess
|
|||
// grab the avatar so we can ask it to process trait data
|
||||
bool isNewAvatar;
|
||||
auto avatar = newOrExistingAvatar(avatarID, sendingNode, isNewAvatar);
|
||||
|
||||
// read the first trait type for this avatar
|
||||
AvatarTraits::TraitType traitType;
|
||||
message->readPrimitive(&traitType);
|
||||
|
@ -386,24 +403,31 @@ void AvatarHashMap::processKillAvatar(QSharedPointer<ReceivedMessage> message, S
|
|||
}
|
||||
|
||||
void AvatarHashMap::removeAvatar(const QUuid& sessionUUID, KillAvatarReason removalReason) {
|
||||
QWriteLocker locker(&_hashLock);
|
||||
std::vector<AvatarSharedPointer> 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 +445,18 @@ void AvatarHashMap::sessionUUIDChanged(const QUuid& sessionUUID, const QUuid& ol
|
|||
}
|
||||
|
||||
void AvatarHashMap::clearOtherAvatars() {
|
||||
QWriteLocker locker(&_hashLock);
|
||||
QList<AvatarSharedPointer> 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<AvatarSharedPointer> 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;
|
||||
|
|
|
@ -98,6 +98,7 @@ enum Hand {
|
|||
class InputDevice {
|
||||
public:
|
||||
InputDevice(const QString& name) : _name(name) {}
|
||||
virtual ~InputDevice() {}
|
||||
|
||||
using Pointer = std::shared_ptr<InputDevice>;
|
||||
|
||||
|
|
|
@ -24,6 +24,8 @@ public:
|
|||
AndConditional(Conditional::Pointer& first, Conditional::Pointer& second)
|
||||
: _children({ first, second }) {}
|
||||
|
||||
virtual ~AndConditional() {}
|
||||
|
||||
virtual bool satisfied() override;
|
||||
|
||||
private:
|
||||
|
|
|
@ -18,6 +18,7 @@ namespace controller {
|
|||
class EndpointConditional : public Conditional {
|
||||
public:
|
||||
EndpointConditional(Endpoint::Pointer endpoint) : _endpoint(endpoint) {}
|
||||
virtual ~EndpointConditional() {}
|
||||
virtual bool satisfied() override { return _endpoint && _endpoint->peek() != 0.0f; }
|
||||
private:
|
||||
Endpoint::Pointer _endpoint;
|
||||
|
|
|
@ -19,6 +19,7 @@ namespace controller {
|
|||
using Pointer = std::shared_ptr<NotConditional>;
|
||||
|
||||
NotConditional(Conditional::Pointer operand) : _operand(operand) { }
|
||||
virtual ~NotConditional() {}
|
||||
|
||||
virtual bool satisfied() override;
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ class ClampFilter : public Filter {
|
|||
REGISTER_FILTER_CLASS(ClampFilter);
|
||||
public:
|
||||
ClampFilter(float min = 0.0, float max = 1.0) : _min(min), _max(max) {};
|
||||
virtual ~ClampFilter() {}
|
||||
virtual float apply(float value) const override {
|
||||
return glm::clamp(value, _min, _max);
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ class ConstrainToIntegerFilter : public Filter {
|
|||
REGISTER_FILTER_CLASS(ConstrainToIntegerFilter);
|
||||
public:
|
||||
ConstrainToIntegerFilter() {};
|
||||
virtual ~ConstrainToIntegerFilter() {}
|
||||
|
||||
virtual float apply(float value) const override {
|
||||
return glm::sign(value);
|
||||
|
|
|
@ -18,6 +18,7 @@ class ConstrainToPositiveIntegerFilter : public Filter {
|
|||
REGISTER_FILTER_CLASS(ConstrainToPositiveIntegerFilter);
|
||||
public:
|
||||
ConstrainToPositiveIntegerFilter() {};
|
||||
virtual ~ConstrainToPositiveIntegerFilter() {};
|
||||
|
||||
virtual float apply(float value) const override {
|
||||
return (value <= 0.0f) ? 0.0f : 1.0f;
|
||||
|
|
|
@ -18,6 +18,7 @@ class DeadZoneFilter : public Filter {
|
|||
REGISTER_FILTER_CLASS(DeadZoneFilter);
|
||||
public:
|
||||
DeadZoneFilter(float min = 0.0) : _min(min) {};
|
||||
virtual ~DeadZoneFilter() {}
|
||||
|
||||
virtual float apply(float value) const override;
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ namespace controller {
|
|||
ExponentialSmoothingFilter() {}
|
||||
ExponentialSmoothingFilter(float rotationConstant, float translationConstant) :
|
||||
_translationConstant(translationConstant), _rotationConstant(rotationConstant) {}
|
||||
virtual ~ExponentialSmoothingFilter() {}
|
||||
|
||||
float apply(float value) const override { return value; }
|
||||
Pose apply(Pose value) const override;
|
||||
|
|
|
@ -18,6 +18,7 @@ class HysteresisFilter : public Filter {
|
|||
REGISTER_FILTER_CLASS(HysteresisFilter);
|
||||
public:
|
||||
HysteresisFilter(float min = 0.25, float max = 0.75);
|
||||
virtual ~HysteresisFilter() {}
|
||||
virtual float apply(float value) const override;
|
||||
|
||||
virtual Pose apply(Pose value) const override { return value; }
|
||||
|
|
|
@ -19,6 +19,7 @@ class InvertFilter : public ScaleFilter {
|
|||
public:
|
||||
using ScaleFilter::parseParameters;
|
||||
InvertFilter() : ScaleFilter(-1.0f) {}
|
||||
virtual ~InvertFilter() {}
|
||||
|
||||
virtual bool parseParameters(const QJsonArray& parameters) { return true; }
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ namespace controller {
|
|||
LowVelocityFilter() {}
|
||||
LowVelocityFilter(float rotationConstant, float translationConstant) :
|
||||
_translationConstant(translationConstant), _rotationConstant(rotationConstant) {}
|
||||
virtual ~LowVelocityFilter() {}
|
||||
|
||||
float apply(float value) const override { return value; }
|
||||
Pose apply(Pose newPose) const override;
|
||||
|
|