Merge branch 'master' into NOverlays

This commit is contained in:
Sam Gondelman 2018-12-06 11:29:06 -08:00 committed by GitHub
commit b690faeb12
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
619 changed files with 13362 additions and 8007 deletions

View file

@ -1,6 +1,6 @@
set(TARGET_NAME native-lib)
setup_hifi_library()
link_hifi_libraries(shared task networking gl gpu qml image fbx render-utils physics entities octree ${PLATFORM_GL_BACKEND})
link_hifi_libraries(shared task networking gl gpu qml image fbx hfm render-utils physics entities octree ${PLATFORM_GL_BACKEND})
target_opengl()
target_bullet()

View file

@ -51,6 +51,9 @@ android {
debug {
buildConfigField "String", "BACKTRACE_URL", "\"" + (System.getenv("CMAKE_BACKTRACE_URL") ? System.getenv("CMAKE_BACKTRACE_URL") : '') + "\""
buildConfigField "String", "BACKTRACE_TOKEN", "\"" + (System.getenv("CMAKE_BACKTRACE_TOKEN") ? System.getenv("CMAKE_BACKTRACE_TOKEN") : '') + "\""
buildConfigField "String", "OAUTH_CLIENT_ID", "\"" + (System.getenv("OAUTH_CLIENT_ID") ? System.getenv("OAUTH_CLIENT_ID") : '') + "\""
buildConfigField "String", "OAUTH_CLIENT_SECRET", "\"" + (System.getenv("OAUTH_CLIENT_SECRET") ? System.getenv("OAUTH_CLIENT_SECRET") : '') + "\""
buildConfigField "String", "OAUTH_REDIRECT_URI", "\"" + (System.getenv("OAUTH_REDIRECT_URI") ? System.getenv("OAUTH_REDIRECT_URI") : '') + "\""
}
release {
minifyEnabled false
@ -61,6 +64,9 @@ android {
project.hasProperty("HIFI_ANDROID_KEY_PASSWORD")? signingConfigs.release : null
buildConfigField "String", "BACKTRACE_URL", "\"" + (System.getenv("CMAKE_BACKTRACE_URL") ? System.getenv("CMAKE_BACKTRACE_URL") : '') + "\""
buildConfigField "String", "BACKTRACE_TOKEN", "\"" + (System.getenv("CMAKE_BACKTRACE_TOKEN") ? System.getenv("CMAKE_BACKTRACE_TOKEN") : '') + "\""
buildConfigField "String", "OAUTH_CLIENT_ID", "\"" + (System.getenv("OAUTH_CLIENT_ID") ? System.getenv("OAUTH_CLIENT_ID") : '') + "\""
buildConfigField "String", "OAUTH_CLIENT_SECRET", "\"" + (System.getenv("OAUTH_CLIENT_SECRET") ? System.getenv("OAUTH_CLIENT_SECRET") : '') + "\""
buildConfigField "String", "OAUTH_REDIRECT_URI", "\"" + (System.getenv("OAUTH_REDIRECT_URI") ? System.getenv("OAUTH_REDIRECT_URI") : '') + "\""
}
}

View file

@ -71,6 +71,10 @@
android:screenOrientation="portrait"
android:theme="@style/Theme.AppCompat.Translucent.NoActionBar" />
<activity android:name=".LoginMenuActivity"
android:screenOrientation="portrait"
android:theme="@style/Theme.AppCompat.Translucent.NoActionBar" />
<service
android:name=".BreakpadUploaderService"
android:enabled="true"

View file

@ -24,6 +24,9 @@
#include <udt/PacketHeaders.h>
#include <SettingHandle.h>
#define AUTO_LOGOUT_SETTING_NAME "wallet/autoLogout"
#define WALLET_USERNAME_SETTING_NAME "wallet/savedUsername"
QAndroidJniObject __interfaceActivity;
QAndroidJniObject __loginCompletedListener;
QAndroidJniObject __signupCompletedListener;
@ -210,11 +213,13 @@ JNIEXPORT void Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeOnDest
JNIEXPORT void Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeGotoUrl(JNIEnv* env, jobject obj, jstring url) {
QAndroidJniObject jniUrl("java/lang/String", "(Ljava/lang/String;)V", url);
DependencyManager::get<AddressManager>()->loadSettings(jniUrl.toString());
AndroidHelper::instance().muteMic();
}
JNIEXPORT void Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeGoToUser(JNIEnv* env, jobject obj, jstring username) {
QAndroidJniObject jniUsername("java/lang/String", "(Ljava/lang/String;)V", username);
DependencyManager::get<AddressManager>()->goToUser(jniUsername.toString(), false);
AndroidHelper::instance().muteMic();
}
JNIEXPORT void Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeOnPause(JNIEnv* env, jobject obj) {
@ -259,7 +264,56 @@ JNIEXPORT jstring JNICALL Java_io_highfidelity_hifiinterface_fragment_HomeFragme
}
JNIEXPORT void JNICALL
Java_io_highfidelity_hifiinterface_fragment_LoginFragment_nativeCancelLogin(JNIEnv *env, jobject instance) {
Java_io_highfidelity_hifiinterface_HifiUtils_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_HifiUtils_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 jboolean JNICALL
Java_io_highfidelity_hifiinterface_HifiUtils_isUserLoggedIn(JNIEnv *env, jobject instance) {
return DependencyManager::get<AccountManager>()->isLoggedIn();
}
JNIEXPORT jboolean JNICALL
Java_io_highfidelity_hifiinterface_HifiUtils_isKeepingLoggedIn(JNIEnv *env, jobject instance) {
Setting::Handle<bool> setting(AUTO_LOGOUT_SETTING_NAME, true);
return !setting.get();
}
JNIEXPORT void JNICALL
Java_io_highfidelity_hifiinterface_fragment_LoginFragment_cancelLogin(JNIEnv *env, jobject instance) {
auto accountManager = DependencyManager::get<AccountManager>();
@ -269,17 +323,16 @@ Java_io_highfidelity_hifiinterface_fragment_LoginFragment_nativeCancelLogin(JNIE
}
JNIEXPORT void JNICALL
Java_io_highfidelity_hifiinterface_fragment_SignupFragment_nativeCancelLogin(JNIEnv *env,
Java_io_highfidelity_hifiinterface_fragment_SignupFragment_cancelLogin(JNIEnv *env,
jobject instance) {
Java_io_highfidelity_hifiinterface_fragment_LoginFragment_nativeCancelLogin(env, instance);
Java_io_highfidelity_hifiinterface_fragment_LoginFragment_cancelLogin(env, instance);
}
JNIEXPORT void JNICALL
Java_io_highfidelity_hifiinterface_fragment_LoginFragment_nativeLogin(JNIEnv *env, jobject instance,
Java_io_highfidelity_hifiinterface_fragment_LoginFragment_login(JNIEnv *env, jobject instance,
jstring username_, jstring password_,
jobject usernameChangedListener) {
jboolean keepLoggedIn) {
const char *c_username = env->GetStringUTFChars(username_, 0);
const char *c_password = env->GetStringUTFChars(password_, 0);
QString username = QString(c_username);
@ -290,7 +343,53 @@ Java_io_highfidelity_hifiinterface_fragment_LoginFragment_nativeLogin(JNIEnv *en
auto accountManager = DependencyManager::get<AccountManager>();
__loginCompletedListener = QAndroidJniObject(instance);
__usernameChangedListener = QAndroidJniObject(usernameChangedListener);
QObject::connect(accountManager.data(), &AccountManager::loginComplete, [username, keepLoggedIn](const QUrl& authURL) {
jboolean jSuccess = (jboolean) true;
if (__loginCompletedListener.isValid()) {
__loginCompletedListener.callMethod<void>("handleLoginCompleted", "(Z)V", jSuccess);
}
Setting::Handle<QVariant>(AUTO_LOGOUT_SETTING_NAME).set(!keepLoggedIn);
QString usernameToSave = keepLoggedIn ? username : "";
Setting::Handle<QVariant>(WALLET_USERNAME_SETTING_NAME).set(usernameToSave);
});
QObject::connect(accountManager.data(), &AccountManager::loginFailed, []() {
jboolean jSuccess = (jboolean) false;
if (__loginCompletedListener.isValid()) {
__loginCompletedListener.callMethod<void>("handleLoginCompleted", "(Z)V", jSuccess);
}
});
QMetaObject::invokeMethod(accountManager.data(), "requestAccessToken",
Q_ARG(const QString&, username), Q_ARG(const QString&, password));
}
JNIEXPORT void JNICALL
Java_io_highfidelity_hifiinterface_fragment_LoginFragment_retrieveAccessToken(JNIEnv *env,
jobject instance,
jstring authCode_,
jstring clientId_,
jstring clientSecret_,
jstring redirectUri_) {
const char *c_authCode = env->GetStringUTFChars(authCode_, 0);
const char *c_clientId = env->GetStringUTFChars(clientId_, 0);
const char *c_clientSecret = env->GetStringUTFChars(clientSecret_, 0);
const char *c_redirectUri = env->GetStringUTFChars(redirectUri_, 0);
QString authCode = QString(c_authCode);
QString clientId = QString(c_clientId);
QString clientSecret = QString(c_clientSecret);
QString redirectUri = QString(c_redirectUri);
env->ReleaseStringUTFChars(authCode_, c_authCode);
env->ReleaseStringUTFChars(clientId_, c_clientId);
env->ReleaseStringUTFChars(clientSecret_, c_clientSecret);
env->ReleaseStringUTFChars(redirectUri_, c_redirectUri);
auto accountManager = DependencyManager::get<AccountManager>();
__loginCompletedListener = QAndroidJniObject(instance); // TODO: use a different listener?
QObject::connect(accountManager.data(), &AccountManager::loginComplete, [](const QUrl& authURL) {
jboolean jSuccess = (jboolean) true;
@ -306,24 +405,19 @@ Java_io_highfidelity_hifiinterface_fragment_LoginFragment_nativeLogin(JNIEnv *en
}
});
QObject::connect(accountManager.data(), &AccountManager::usernameChanged, [](const QString& username) {
QAndroidJniObject string = QAndroidJniObject::fromString(username);
if (__usernameChangedListener.isValid()) {
__usernameChangedListener.callMethod<void>("handleUsernameChanged", "(Ljava/lang/String;)V", string.object<jstring>());
}
});
QMetaObject::invokeMethod(accountManager.data(), "requestAccessTokenWithAuthCode",
Q_ARG(const QString&, authCode), Q_ARG(const QString&, clientId),
Q_ARG(const QString&, clientSecret), Q_ARG(const QString&, redirectUri));
QMetaObject::invokeMethod(accountManager.data(), "requestAccessToken",
Q_ARG(const QString&, username), Q_ARG(const QString&, password));
}
JNIEXPORT void JNICALL
Java_io_highfidelity_hifiinterface_fragment_SignupFragment_nativeLogin(JNIEnv *env,
Java_io_highfidelity_hifiinterface_fragment_SignupFragment_login(JNIEnv *env,
jobject instance,
jstring username_,
jstring password_,
jobject usernameChangedListener) {
Java_io_highfidelity_hifiinterface_fragment_LoginFragment_nativeLogin(env, instance, username_, password_, usernameChangedListener);
jboolean keepLoggedIn) {
Java_io_highfidelity_hifiinterface_fragment_LoginFragment_login(env, instance, username_, password_, keepLoggedIn);
}
JNIEXPORT void Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeInitAfterAppLoaded(JNIEnv* env, jobject obj) {
@ -331,7 +425,7 @@ JNIEXPORT void Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeInitAf
}
JNIEXPORT void JNICALL
Java_io_highfidelity_hifiinterface_fragment_SignupFragment_nativeSignup(JNIEnv *env, jobject instance,
Java_io_highfidelity_hifiinterface_fragment_SignupFragment_signup(JNIEnv *env, jobject instance,
jstring email_, jstring username_,
jstring password_) {
@ -359,8 +453,6 @@ Java_io_highfidelity_hifiinterface_fragment_SignupFragment_nativeSignup(JNIEnv *
});
QObject::connect(&AndroidHelper::instance(), &AndroidHelper::handleSignupFailed, [](QString errorString) {
jboolean jSuccess = (jboolean) false;
jstring jError = QAndroidJniObject::fromString(errorString).object<jstring>();
if (__signupCompletedListener.isValid()) {
QAndroidJniObject string = QAndroidJniObject::fromString(errorString);
__signupCompletedListener.callMethod<void>("handleSignupFailed", "(Ljava/lang/String;)V", string.object<jstring>());
@ -371,19 +463,13 @@ Java_io_highfidelity_hifiinterface_fragment_SignupFragment_nativeSignup(JNIEnv *
}
JNIEXPORT void JNICALL
Java_io_highfidelity_hifiinterface_fragment_SignupFragment_nativeCancelSignup(JNIEnv *env, jobject instance) {
Java_io_highfidelity_hifiinterface_fragment_SignupFragment_cancelSignup(JNIEnv *env, jobject instance) {
QObject::disconnect(&AndroidHelper::instance(), &AndroidHelper::handleSignupCompleted, nullptr, nullptr);
QObject::disconnect(&AndroidHelper::instance(), &AndroidHelper::handleSignupFailed, nullptr, nullptr);
__signupCompletedListener = nullptr;
}
JNIEXPORT jboolean JNICALL
Java_io_highfidelity_hifiinterface_fragment_FriendsFragment_nativeIsLoggedIn(JNIEnv *env, jobject instance) {
auto accountManager = DependencyManager::get<AccountManager>();
return accountManager->isLoggedIn();
}
JNIEXPORT jstring JNICALL
Java_io_highfidelity_hifiinterface_fragment_FriendsFragment_nativeGetAccessToken(JNIEnv *env, jobject instance) {
auto accountManager = DependencyManager::get<AccountManager>();
@ -406,23 +492,40 @@ Java_io_highfidelity_hifiinterface_SplashActivity_registerLoadCompleteListener(J
});
}
JNIEXPORT jboolean JNICALL
Java_io_highfidelity_hifiinterface_MainActivity_nativeIsLoggedIn(JNIEnv *env, jobject instance) {
return DependencyManager::get<AccountManager>()->isLoggedIn();
}
JNIEXPORT void JNICALL
Java_io_highfidelity_hifiinterface_MainActivity_nativeLogout(JNIEnv *env, jobject instance) {
Java_io_highfidelity_hifiinterface_MainActivity_logout(JNIEnv *env, jobject instance) {
DependencyManager::get<AccountManager>()->logout();
}
JNIEXPORT jstring JNICALL
Java_io_highfidelity_hifiinterface_MainActivity_nativeGetDisplayName(JNIEnv *env,
Java_io_highfidelity_hifiinterface_MainActivity_getUsername(JNIEnv *env,
jobject instance) {
QString username = DependencyManager::get<AccountManager>()->getAccountInfo().getUsername();
return env->NewStringUTF(username.toLatin1().data());
}
JNIEXPORT void JNICALL
Java_io_highfidelity_hifiinterface_MainActivity_setUsernameChangedListener(JNIEnv *env,
jobject instance,
jobject usernameChangedListener) {
__usernameChangedListener = QAndroidJniObject(usernameChangedListener);
if (!__usernameChangedListener.isValid()) {
return;
}
auto accountManager = DependencyManager::get<AccountManager>();
QObject::connect(accountManager.data(), &AccountManager::usernameChanged, [](const QString& username) {
QAndroidJniObject string = QAndroidJniObject::fromString(username);
if (__usernameChangedListener.isValid()) {
__usernameChangedListener.callMethod<void>("handleUsernameChanged", "(Ljava/lang/String;)V", string.object<jstring>());
}
});
}
JNIEXPORT void JNICALL
Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeBeforeEnterBackground(JNIEnv *env, jobject obj) {
AndroidHelper::instance().notifyBeforeEnterBackground();
@ -443,46 +546,6 @@ 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,

View file

@ -64,4 +64,10 @@ public class HifiUtils {
public native String protocolVersionSignature();
public native boolean isUserLoggedIn();
public native void updateHifiSetting(String group, String key, boolean value);
public native boolean getHifiSettingBoolean(String group, String key, boolean defaultValue);
public native boolean isKeepingLoggedIn();
}

View file

@ -76,7 +76,7 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW
private static boolean inVrMode;
private boolean nativeEnterBackgroundCallEnqueued = false;
private SlidingDrawer webSlidingDrawer;
private SlidingDrawer mWebSlidingDrawer;
// private GvrApi gvrApi;
// Opaque native pointer to the Application C++ object.
// This object is owned by the InterfaceActivity instance and passed to the native methods.
@ -116,17 +116,6 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW
//nativeGvrApi =
nativeOnCreate(this, assetManager /*, gvrApi.getNativeGvrContext()*/);
Point size = new Point();
getWindowManager().getDefaultDisplay().getRealSize(size);
try {
PackageInfo pInfo = this.getPackageManager().getPackageInfo(getPackageName(), 0);
String version = pInfo.versionName;
// setAppVersion(version);
} catch (PackageManager.NameNotFoundException e) {
Log.e("GVR", "Error getting application version", e);
}
final View rootView = getWindow().getDecorView().findViewById(android.R.id.content);
// This is a workaround to hide the menu bar when the virtual keyboard is shown from Qt
@ -137,26 +126,6 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW
});
startActivity(new Intent(this, SplashActivity.class));
mVibrator = (Vibrator) this.getSystemService(VIBRATOR_SERVICE);
FrameLayout mainLayout = findViewById(android.R.id.content);
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
webSlidingDrawer = (SlidingDrawer) inflater.inflate(R.layout.web_drawer, mainLayout, false);
QtLayout qtLayout = (QtLayout) mainLayout.getChildAt(0);
QtLayout.LayoutParams layoutParams = new QtLayout.LayoutParams(webSlidingDrawer.getLayoutParams());
webSlidingDrawer.setOnDrawerCloseListener(() -> {
WebViewFragment webViewFragment = (WebViewFragment) getFragmentManager().findFragmentByTag("webViewFragment");
webViewFragment.close();
});
int widthPx = Math.max(size.x, size.y);
int heightPx = Math.min(size.x, size.y);
layoutParams.x = (int) (widthPx - WEB_DRAWER_RIGHT_MARGIN * getResources().getDisplayMetrics().xdpi / NORMAL_DPI);
layoutParams.y = (int) (heightPx - WEB_DRAWER_BOTTOM_MARGIN * getResources().getDisplayMetrics().ydpi / NORMAL_DPI);
layoutParams.resolveLayoutDirection(View.LAYOUT_DIRECTION_RTL);
qtLayout.addView(webSlidingDrawer, layoutParams);
webSlidingDrawer.setVisibility(View.GONE);
headsetStateReceiver = new HeadsetStateReceiver();
}
@ -289,14 +258,47 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
if (intent.hasExtra(DOMAIN_URL)) {
webSlidingDrawer.setVisibility(View.GONE);
hideWebDrawer();
nativeGotoUrl(intent.getStringExtra(DOMAIN_URL));
} else if (intent.hasExtra(EXTRA_GOTO_USERNAME)) {
webSlidingDrawer.setVisibility(View.GONE);
hideWebDrawer();
nativeGoToUser(intent.getStringExtra(EXTRA_GOTO_USERNAME));
}
}
private void hideWebDrawer() {
if (mWebSlidingDrawer != null) {
mWebSlidingDrawer.setVisibility(View.GONE);
}
}
public void showWebDrawer() {
if (mWebSlidingDrawer == null) {
FrameLayout mainLayout = findViewById(android.R.id.content);
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
QtLayout qtLayout = (QtLayout) mainLayout.getChildAt(0);
mWebSlidingDrawer = (SlidingDrawer) inflater.inflate(R.layout.web_drawer, mainLayout, false);
QtLayout.LayoutParams layoutParams = new QtLayout.LayoutParams(mWebSlidingDrawer.getLayoutParams());
mWebSlidingDrawer.setOnDrawerCloseListener(() -> {
WebViewFragment webViewFragment = (WebViewFragment) getFragmentManager().findFragmentByTag("webViewFragment");
webViewFragment.close();
});
Point size = new Point();
getWindowManager().getDefaultDisplay().getRealSize(size);
int widthPx = Math.max(size.x, size.y);
int heightPx = Math.min(size.x, size.y);
layoutParams.x = (int) (widthPx - WEB_DRAWER_RIGHT_MARGIN * getResources().getDisplayMetrics().xdpi / NORMAL_DPI);
layoutParams.y = (int) (heightPx - WEB_DRAWER_BOTTOM_MARGIN * getResources().getDisplayMetrics().ydpi / NORMAL_DPI);
layoutParams.resolveLayoutDirection(View.LAYOUT_DIRECTION_RTL);
qtLayout.addView(mWebSlidingDrawer, layoutParams);
}
mWebSlidingDrawer.setVisibility(View.VISIBLE);
}
public void openAndroidActivity(String activityName, boolean backToScene) {
openAndroidActivity(activityName, backToScene, null);
}
@ -313,29 +315,29 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW
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);
Intent loginIntent = new Intent(this, LoginMenuActivity.class);
loginIntent.putExtra(LoginMenuActivity.EXTRA_BACK_TO_SCENE, backToScene);
loginIntent.putExtra(LoginMenuActivity.EXTRA_BACK_ON_SKIP, true);
if (args != null && args.containsKey(DOMAIN_URL)) {
loginIntent.putExtra(DOMAIN_URL, (String) args.get(DOMAIN_URL));
loginIntent.putExtra(LoginMenuActivity.EXTRA_DOMAIN_URL, (String) args.get(DOMAIN_URL));
}
startActivity(loginIntent);
break;
case "WebView":
runOnUiThread(() -> {
webSlidingDrawer.setVisibility(View.VISIBLE);
if (!webSlidingDrawer.isOpened()) {
webSlidingDrawer.animateOpen();
showWebDrawer();
if (!mWebSlidingDrawer.isOpened()) {
mWebSlidingDrawer.animateOpen();
}
if (args != null && args.containsKey(WebViewActivity.WEB_VIEW_ACTIVITY_EXTRA_URL)) {
WebViewFragment webViewFragment = (WebViewFragment) getFragmentManager().findFragmentByTag("webViewFragment");
webViewFragment.loadUrl((String) args.get(WebViewActivity.WEB_VIEW_ACTIVITY_EXTRA_URL), true);
webViewFragment.setToolbarVisible(true);
webViewFragment.setCloseAction(() -> {
if (webSlidingDrawer.isOpened()) {
webSlidingDrawer.animateClose();
if (mWebSlidingDrawer.isOpened()) {
mWebSlidingDrawer.animateClose();
}
webSlidingDrawer.setVisibility(View.GONE);
hideWebDrawer();
});
}
});
@ -381,4 +383,7 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW
public void onExpand() {
keepInterfaceRunning = true;
}
@Override
public void onOAuthAuthorizeCallback(Uri uri) { }
}

View file

@ -0,0 +1,210 @@
package io.highfidelity.hifiinterface;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import io.highfidelity.hifiinterface.fragment.LoginFragment;
import io.highfidelity.hifiinterface.fragment.OnBackPressedListener;
import io.highfidelity.hifiinterface.fragment.SignupFragment;
import io.highfidelity.hifiinterface.fragment.StartMenuFragment;
public class LoginMenuActivity extends AppCompatActivity
implements StartMenuFragment.StartMenuInteractionListener,
LoginFragment.OnLoginInteractionListener,
SignupFragment.OnSignupInteractionListener {
/**
* Set EXTRA_FINISH_ON_BACK to finish the app when back button is pressed
*/
public static final String EXTRA_FINISH_ON_BACK = "finishOnBack";
/**
* Set EXTRA_BACK_TO_SCENE to back to the scene
*/
public static final String EXTRA_BACK_TO_SCENE = "backToScene";
/**
* Set EXTRA_BACK_ON_SKIP to finish this activity when skip button is pressed
*/
public static final String EXTRA_BACK_ON_SKIP = "backOnSkip";
public static final String EXTRA_DOMAIN_URL = "url";
private boolean finishOnBack;
private boolean backToScene;
private boolean backOnSkip;
private String domainUrlToBack;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_encourage_login);
finishOnBack = getIntent().getBooleanExtra(EXTRA_FINISH_ON_BACK, false);
backToScene = getIntent().getBooleanExtra(EXTRA_BACK_TO_SCENE, false);
domainUrlToBack = getIntent().getStringExtra(EXTRA_DOMAIN_URL);
backOnSkip = getIntent().getBooleanExtra(EXTRA_BACK_ON_SKIP, false);
if (savedInstanceState != null) {
finishOnBack = savedInstanceState.getBoolean(EXTRA_FINISH_ON_BACK, false);
backToScene = savedInstanceState.getBoolean(EXTRA_BACK_TO_SCENE, false);
backOnSkip = savedInstanceState.getBoolean(EXTRA_BACK_ON_SKIP, false);
domainUrlToBack = savedInstanceState.getString(EXTRA_DOMAIN_URL);
}
loadMenuFragment();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(EXTRA_FINISH_ON_BACK, finishOnBack);
outState.putBoolean(EXTRA_BACK_TO_SCENE, backToScene);
outState.putString(EXTRA_DOMAIN_URL, domainUrlToBack);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
finishOnBack = savedInstanceState.getBoolean(EXTRA_FINISH_ON_BACK, false);
backToScene = savedInstanceState.getBoolean(EXTRA_BACK_TO_SCENE, false);
backOnSkip = savedInstanceState.getBoolean(EXTRA_BACK_ON_SKIP, false);
domainUrlToBack = savedInstanceState.getString(EXTRA_DOMAIN_URL);
}
private void loadMenuFragment() {
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
Fragment fragment = StartMenuFragment.newInstance();
fragmentTransaction.replace(R.id.content_frame, fragment);
fragmentTransaction.addToBackStack(fragment.toString());
fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
fragmentTransaction.commit();
hideStatusBar();
}
@Override
protected void onResume() {
super.onResume();
hideStatusBar();
}
private void hideStatusBar() {
View decorView = getWindow().getDecorView();
// Hide the status bar.
int uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
decorView.setSystemUiVisibility(uiOptions);
}
@Override
public void onSignupButtonClicked() {
loadSignupFragment();
}
@Override
public void onLoginButtonClicked() {
loadLoginFragment(false);
}
@Override
public void onSkipLoginClicked() {
if (backOnSkip) {
onBackPressed();
} else {
loadMainActivity();
}
}
@Override
public void onSteamLoginButtonClicked() {
loadLoginFragment(true);
}
private void loadSignupFragment() {
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
Fragment fragment = SignupFragment.newInstance();
String tag = getString(R.string.tagFragmentSignup);
fragmentTransaction.replace(R.id.content_frame, fragment, tag);
fragmentTransaction.addToBackStack(tag);
fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
fragmentTransaction.commit();
hideStatusBar();
}
private void loadLoginFragment(boolean useOauth) {
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
Fragment fragment = LoginFragment.newInstance(useOauth);
String tag = getString(R.string.tagFragmentLogin);
fragmentTransaction.replace(R.id.content_frame, fragment, tag);
fragmentTransaction.addToBackStack(tag);
fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
fragmentTransaction.commit();
hideStatusBar();
}
@Override
public void onLoginCompleted() {
loadMainActivity();
}
@Override
public void onCancelLogin() {
getFragmentManager().popBackStack();
}
@Override
public void onCancelSignup() {
getFragmentManager().popBackStack();
}
private void loadMainActivity() {
finish();
if (backToScene) {
backToScene = false;
goToDomain(domainUrlToBack != null? domainUrlToBack : "");
} else {
startActivity(new Intent(this, MainActivity.class));
}
}
private void goToDomain(String domainUrl) {
Intent intent = new Intent(this, InterfaceActivity.class);
intent.putExtra(InterfaceActivity.DOMAIN_URL, domainUrl);
finish();
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent);
}
@Override
public void onSignupCompleted() {
loadMainActivity();
}
@Override
public void onBackPressed() {
FragmentManager fm = getFragmentManager();
int index = fm.getBackStackEntryCount() - 1;
if (index > 0) {
FragmentManager.BackStackEntry backEntry = fm.getBackStackEntryAt(index);
String tag = backEntry.getName();
Fragment topFragment = getFragmentManager().findFragmentByTag(tag);
if (!(topFragment instanceof OnBackPressedListener) ||
!((OnBackPressedListener) topFragment).doBack()) {
super.onBackPressed();
}
} else if (finishOnBack){
finishAffinity();
} else {
finish();
}
}
}

View file

@ -1,5 +1,6 @@
package io.highfidelity.hifiinterface;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
@ -29,23 +30,16 @@ 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.fragment.SignedInFragment;
import io.highfidelity.hifiinterface.fragment.SignupFragment;import io.highfidelity.hifiinterface.task.DownloadProfileImageTask;
import io.highfidelity.hifiinterface.fragment.SignupFragment;
import io.highfidelity.hifiinterface.task.DownloadProfileImageTask;
public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener,
LoginFragment.OnLoginInteractionListener,
HomeFragment.OnHomeInteractionListener,
FriendsFragment.OnHomeInteractionListener,
SignupFragment.OnSignupInteractionListener,
SignedInFragment.OnSignedInInteractionListener {
FriendsFragment.OnHomeInteractionListener {
private static final int PROFILE_PICTURE_PLACEHOLDER = R.drawable.default_profile_avatar;
public static final String DEFAULT_FRAGMENT = "Home";
@ -55,9 +49,9 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
private String TAG = "HighFidelity";
public native boolean nativeIsLoggedIn();
public native void nativeLogout();
public native String nativeGetDisplayName();
public native void logout();
public native void setUsernameChangedListener(Activity usernameChangedListener);
public native String getUsername();
private DrawerLayout mDrawerLayout;
private NavigationView mNavigationView;
@ -130,9 +124,6 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
private void loadFragment(String fragment) {
switch (fragment) {
case "Login":
loadLoginFragment();
break;
case "Home":
loadHomeFragment(true);
break;
@ -153,19 +144,10 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
loadFragment(fragment, getString(R.string.home), getString(R.string.tagFragmentHome), addToBackStack, true);
}
private void loadLoginFragment() {
Fragment fragment = LoginFragment.newInstance();
loadFragment(fragment, getString(R.string.login), getString(R.string.tagFragmentLogin), true, true);
}
private void loadSignedInFragment() {
Fragment fragment = SignedInFragment.newInstance();
loadFragment(fragment, getString(R.string.welcome), getString(R.string.tagFragmentSignedIn), true, true);
}
private void loadSignupFragment() {
Fragment fragment = SignupFragment.newInstance();
loadFragment(fragment, getString(R.string.signup), getString(R.string.tagFragmentSignup), true, false);
private void startLoginMenuActivity() {
Intent intent = new Intent(this, LoginMenuActivity.class);
intent.putExtra(LoginMenuActivity.EXTRA_BACK_ON_SKIP, true);
startActivity(intent);
}
private void loadPrivacyPolicyFragment() {
@ -223,7 +205,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
private void updateLoginMenu() {
if (nativeIsLoggedIn()) {
if (HifiUtils.getInstance().isUserLoggedIn()) {
mLoginPanel.setVisibility(View.GONE);
mProfilePanel.setVisibility(View.VISIBLE);
mLogoutOption.setVisibility(View.VISIBLE);
@ -239,7 +221,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
}
private void updateProfileHeader() {
updateProfileHeader(nativeGetDisplayName());
updateProfileHeader(getUsername());
}
private void updateProfileHeader(String username) {
if (!username.isEmpty()) {
@ -289,15 +271,22 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
@Override
protected void onStart() {
super.onStart();
setUsernameChangedListener(this);
updateLoginMenu();
}
@Override
protected void onStop() {
super.onStop();
setUsernameChangedListener(null);
}
public void onLoginClicked(View view) {
loadLoginFragment();
startLoginMenuActivity();
}
public void onLogoutClicked(View view) {
nativeLogout();
logout();
updateLoginMenu();
exitLoggedInFragment();
@ -338,42 +327,6 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
startActivity(intent);
}
@Override
public void onLoginCompleted() {
loadHomeFragment(false);
updateLoginMenu();
if (backToScene) {
backToScene = false;
goToLastLocation();
}
}
@Override
public void onGettingStarted() {
loadHomeFragment(false);
if (backToScene) {
backToScene = false;
goToLastLocation();
}
}
@Override
public void onLoginRequested() {
// go back from signup to login
onBackPressed();
}
@Override
public void onSignupRequested() {
loadSignupFragment();
}
@Override
public void onSignupCompleted() {
loadSignedInFragment();
updateLoginMenu();
}
public void handleUsernameChanged(String username) {
runOnUiThread(() -> updateProfileHeader(username));
}
@ -418,7 +371,6 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
public void onBackPressed() {
// if a fragment needs to internally manage back presses..
FragmentManager fm = getFragmentManager();
Log.d("[BACK]", "getBackStackEntryCount " + fm.getBackStackEntryCount());
Fragment friendsFragment = fm.findFragmentByTag(getString(R.string.tagFragmentPeople));
if (friendsFragment != null && friendsFragment instanceof FriendsFragment) {
if (((FriendsFragment) friendsFragment).onBackPressed()) {

View file

@ -3,7 +3,6 @@ package io.highfidelity.hifiinterface;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
public class SplashActivity extends Activity {
@ -37,7 +36,13 @@ public class SplashActivity extends Activity {
}
public void onAppLoadedComplete() {
startActivity(new Intent(this, MainActivity.class));
if (HifiUtils.getInstance().isUserLoggedIn()) {
startActivity(new Intent(this, MainActivity.class));
} else {
Intent menuIntent = new Intent(this, LoginMenuActivity.class);
menuIntent.putExtra(LoginMenuActivity.EXTRA_FINISH_ON_BACK, true);
startActivity(menuIntent);
}
SplashActivity.this.finish();
}
}

View file

@ -28,11 +28,18 @@ import java.net.MalformedURLException;
import java.net.URL;
import io.highfidelity.hifiinterface.fragment.WebViewFragment;
import io.highfidelity.hifiinterface.fragment.WebViewFragment.OnWebViewInteractionListener;
public class WebViewActivity extends Activity implements WebViewFragment.OnWebViewInteractionListener {
public class WebViewActivity extends Activity implements OnWebViewInteractionListener {
public static final String WEB_VIEW_ACTIVITY_EXTRA_URL = "url";
public static final String WEB_VIEW_ACTIVITY_EXTRA_CLEAR_COOKIES = "clear_cookies";
public static final String RESULT_OAUTH_CODE = "code";
public static final String RESULT_OAUTH_STATE = "state";
private static final String FRAGMENT_TAG = "WebViewActivity_WebFragment";
private static final String OAUTH_CODE = "code";
private static final String OAUTH_STATE = "state";
private native void nativeProcessURL(String url);
@ -47,14 +54,15 @@ public class WebViewActivity extends Activity implements WebViewFragment.OnWebVi
mActionBar = getActionBar();
mActionBar.setDisplayHomeAsUpEnabled(true);
loadWebViewFragment(getIntent().getStringExtra(WEB_VIEW_ACTIVITY_EXTRA_URL));
loadWebViewFragment(getIntent().getStringExtra(WEB_VIEW_ACTIVITY_EXTRA_URL), getIntent().getBooleanExtra(WEB_VIEW_ACTIVITY_EXTRA_CLEAR_COOKIES, false));
}
private void loadWebViewFragment(String url) {
private void loadWebViewFragment(String url, boolean clearCookies) {
WebViewFragment fragment = WebViewFragment.newInstance();
Bundle bundle = new Bundle();
bundle.putString(WebViewFragment.URL, url);
bundle.putBoolean(WebViewFragment.TOOLBAR_VISIBLE, false);
bundle.putBoolean(WebViewFragment.CLEAR_COOKIES, clearCookies);
fragment.setArguments(bundle);
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction ft = fragmentManager.beginTransaction();
@ -131,4 +139,13 @@ public class WebViewActivity extends Activity implements WebViewFragment.OnWebVi
@Override
public void onExpand() { }
@Override
public void onOAuthAuthorizeCallback(Uri uri) {
Intent result = new Intent();
result.putExtra(RESULT_OAUTH_CODE, uri.getQueryParameter(OAUTH_CODE));
result.putExtra(RESULT_OAUTH_STATE, uri.getQueryParameter(OAUTH_STATE));
setResult(Activity.RESULT_OK, result);
finish();
}
}

View file

@ -23,8 +23,6 @@ import io.highfidelity.hifiinterface.view.UserListAdapter;
public class FriendsFragment extends Fragment {
public native boolean nativeIsLoggedIn();
public native String nativeGetAccessToken();
private RecyclerView mUsersView;

View file

@ -2,41 +2,65 @@ package io.highfidelity.hifiinterface.fragment;
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.support.annotation.Nullable;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.TextView;
import org.qtproject.qt5.android.QtNative;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Random;
import io.highfidelity.hifiinterface.BuildConfig;
import io.highfidelity.hifiinterface.HifiUtils;
import io.highfidelity.hifiinterface.R;
import io.highfidelity.hifiinterface.WebViewActivity;
import static org.qtproject.qt5.android.QtActivityDelegate.ApplicationActive;
import static org.qtproject.qt5.android.QtActivityDelegate.ApplicationInactive;
public class LoginFragment extends Fragment {
public class LoginFragment extends Fragment
implements OnBackPressedListener {
private static final String ARG_USE_OAUTH = "use_oauth";
private static final String TAG = "Interface";
private final String OAUTH_CLIENT_ID = BuildConfig.OAUTH_CLIENT_ID;
private final String OAUTH_REDIRECT_URI = BuildConfig.OAUTH_REDIRECT_URI;
private final String OAUTH_AUTHORIZE_BASE_URL = "https://highfidelity.com/oauth/authorize";
private static final int OAUTH_AUTHORIZE_REQUEST = 1;
private EditText mUsername;
private EditText mPassword;
private TextView mError;
private TextView mForgotPassword;
private TextView mSignup;
private Button mLoginButton;
private CheckBox mKeepMeLoggedInCheckbox;
private ViewGroup mLoginForm;
private ViewGroup mLoggingInFrame;
private ViewGroup mLoggedInFrame;
private boolean mLoginInProgress;
private boolean mLoginSuccess;
private boolean mUseOauth;
private String mOauthState;
private ProgressDialog mDialog;
public native void login(String username, String password, boolean keepLoggedIn);
private native void retrieveAccessToken(String authCode, String clientId, String clientSecret, String redirectUri);
public native void nativeLogin(String username, String password, Activity usernameChangedListener);
public native void nativeCancelLogin();
public native void cancelLogin();
private LoginFragment.OnLoginInteractionListener mListener;
@ -44,11 +68,22 @@ public class LoginFragment extends Fragment {
// Required empty public constructor
}
public static LoginFragment newInstance() {
public static LoginFragment newInstance(boolean useOauth) {
LoginFragment fragment = new LoginFragment();
Bundle args = new Bundle();
args.putBoolean(ARG_USE_OAUTH, useOauth);
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mUseOauth = getArguments().getBoolean(ARG_USE_OAUTH, false);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
@ -58,22 +93,29 @@ public class LoginFragment extends Fragment {
mPassword = rootView.findViewById(R.id.password);
mError = rootView.findViewById(R.id.error);
mLoginButton = rootView.findViewById(R.id.loginButton);
mForgotPassword = rootView.findViewById(R.id.forgotPassword);
mSignup = rootView.findViewById(R.id.signupButton);
mLoginForm = rootView.findViewById(R.id.loginForm);
mLoggingInFrame = rootView.findViewById(R.id.loggingInFrame);
mLoggedInFrame = rootView.findViewById(R.id.loggedInFrame);
mKeepMeLoggedInCheckbox = rootView.findViewById(R.id.keepMeLoggedIn);
mLoginButton.setOnClickListener(view -> login());
rootView.findViewById(R.id.forgotPassword).setOnClickListener(view -> onForgotPasswordClicked());
mForgotPassword.setOnClickListener(view -> forgotPassword());
mSignup.setOnClickListener(view -> signup());
rootView.findViewById(R.id.cancel).setOnClickListener(view -> onCancelLogin());
mPassword.setOnEditorActionListener(
(textView, actionId, keyEvent) -> {
if (actionId == EditorInfo.IME_ACTION_DONE) {
mLoginButton.performClick();
return true;
}
return false;
});
rootView.findViewById(R.id.getStarted).setOnClickListener(view -> onGetStartedClicked());
mLoginButton.setOnClickListener(view -> onLoginButtonClicked());
rootView.findViewById(R.id.takeMeInWorld).setOnClickListener(view -> skipLogin());
mPassword.setOnEditorActionListener((textView, actionId, keyEvent) -> onPasswordEditorAction(textView, actionId, keyEvent));
mKeepMeLoggedInCheckbox.setChecked(HifiUtils.getInstance().isKeepingLoggedIn());
if (mUseOauth) {
openWebForAuthorization();
} else {
showLoginForm();
}
return rootView;
}
@ -104,13 +146,57 @@ public class LoginFragment extends Fragment {
@Override
public void onStop() {
super.onStop();
cancelActivityIndicator();
// Leave the Qt app paused
QtNative.setApplicationState(ApplicationInactive);
hideKeyboard();
}
public void login() {
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == OAUTH_AUTHORIZE_REQUEST) {
if (resultCode == Activity.RESULT_OK) {
String authCode = data.getStringExtra(WebViewActivity.RESULT_OAUTH_CODE);
String state = data.getStringExtra(WebViewActivity.RESULT_OAUTH_STATE);
if (state != null && state.equals(mOauthState) && mListener != null) {
mOauthState = null;
showActivityIndicator();
mLoginInProgress = true;
retrieveAccessToken(authCode, BuildConfig.OAUTH_CLIENT_ID, BuildConfig.OAUTH_CLIENT_SECRET, BuildConfig.OAUTH_REDIRECT_URI);
}
} else {
onCancelLogin();
}
}
}
private void onCancelLogin() {
if (mListener != null) {
mListener.onCancelLogin();
}
}
private boolean onPasswordEditorAction(TextView textView, int actionId, KeyEvent keyEvent) {
if (actionId == EditorInfo.IME_ACTION_DONE) {
mLoginButton.performClick();
return true;
}
return false;
}
private void skipLogin() {
if (mListener != null) {
mListener.onSkipLoginClicked();
}
}
private void onGetStartedClicked() {
if (mListener != null) {
mListener.onLoginCompleted();
}
}
public void onLoginButtonClicked() {
String username = mUsername.getText().toString().trim();
String password = mPassword.getText().toString();
hideKeyboard();
@ -120,13 +206,10 @@ public class LoginFragment extends Fragment {
mLoginButton.setEnabled(false);
hideError();
showActivityIndicator();
nativeLogin(username, password, getActivity());
}
}
public void signup() {
if (mListener != null) {
mListener.onSignupRequested();
mLoginInProgress = true;
mLoginSuccess = false;
boolean keepUserLoggedIn = mKeepMeLoggedInCheckbox.isChecked();
login(username, password, keepUserLoggedIn);
}
}
@ -138,33 +221,32 @@ public class LoginFragment extends Fragment {
}
}
private void forgotPassword() {
private void onForgotPasswordClicked() {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://highfidelity.com/users/password/new"));
startActivity(intent);
}
private void showActivityIndicator() {
if (mDialog == null) {
mDialog = new ProgressDialog(getContext());
}
mDialog.setMessage(getString(R.string.logging_in));
mDialog.setCancelable(true);
mDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialogInterface) {
nativeCancelLogin();
cancelActivityIndicator();
mLoginButton.setEnabled(true);
}
});
mDialog.show();
mLoginForm.setVisibility(View.GONE);
mLoggedInFrame.setVisibility(View.GONE);
mLoggingInFrame.setVisibility(View.VISIBLE);
mLoggingInFrame.bringToFront();
}
private void cancelActivityIndicator() {
if (mDialog != null) {
mDialog.cancel();
}
private void showLoginForm() {
mLoggingInFrame.setVisibility(View.GONE);
mLoggedInFrame.setVisibility(View.GONE);
mLoginForm.setVisibility(View.VISIBLE);
mLoginForm.bringToFront();
}
private void showLoggedInMessage() {
mLoginForm.setVisibility(View.GONE);
mLoggingInFrame.setVisibility(View.GONE);
mLoggedInFrame.setVisibility(View.VISIBLE);
mLoggedInFrame.bringToFront();
}
private void showError(String error) {
mError.setText(error);
mError.setVisibility(View.VISIBLE);
@ -176,22 +258,71 @@ public class LoginFragment extends Fragment {
}
public void handleLoginCompleted(boolean success) {
mLoginInProgress = false;
getActivity().runOnUiThread(() -> {
mLoginButton.setEnabled(true);
cancelActivityIndicator();
if (success) {
if (mListener != null) {
mListener.onLoginCompleted();
}
mLoginSuccess = true;
showLoggedInMessage();
} else {
showError(getString(R.string.login_username_or_password_incorrect));
if (!mUseOauth) {
showLoginForm();
showError(getString(R.string.login_username_or_password_incorrect));
} else {
openWebForAuthorization();
}
}
});
}
@Override
public boolean doBack() {
if (mLoginInProgress) {
cancelLogin();
showLoginForm();
mLoginInProgress = false;
mLoginButton.setEnabled(true);
return true;
} else if (mLoginSuccess) {
onGetStartedClicked();
return true;
} else {
return false;
}
}
private void updateOauthState() {
// as we only use oauth for steam that's ok for now
mOauthState = "steam-" + Long.toString(new Random().nextLong());
}
private String buildAuthorizeUrl() {
StringBuilder sb = new StringBuilder(OAUTH_AUTHORIZE_BASE_URL);
sb.append("?client_id=").append(OAUTH_CLIENT_ID);
try {
String redirectUri = URLEncoder.encode(OAUTH_REDIRECT_URI, "utf-8");
sb.append("&redirect_uri=").append(redirectUri);
} catch (UnsupportedEncodingException e) {
Log.e(TAG, "Cannot build oauth autorization url", e);
}
sb.append("&response_type=code&scope=owner");
sb.append("&state=").append(mOauthState);
return sb.toString();
}
private void openWebForAuthorization() {
Intent openUrlIntent = new Intent(getActivity(), WebViewActivity.class);
updateOauthState();
openUrlIntent.putExtra(WebViewActivity.WEB_VIEW_ACTIVITY_EXTRA_URL, buildAuthorizeUrl());
openUrlIntent.putExtra(WebViewActivity.WEB_VIEW_ACTIVITY_EXTRA_CLEAR_COOKIES, true);
startActivityForResult(openUrlIntent, OAUTH_AUTHORIZE_REQUEST);
}
public interface OnLoginInteractionListener {
void onLoginCompleted();
void onSignupRequested();
void onCancelLogin();
void onSkipLoginClicked();
}
}

View file

@ -0,0 +1,11 @@
package io.highfidelity.hifiinterface.fragment;
public interface OnBackPressedListener {
/**
* Processes the back pressed event and returns true if it was managed by this Fragment
* @return
*/
boolean doBack();
}

View file

@ -3,32 +3,35 @@ 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.preference.PreferenceManager;
import android.support.annotation.Nullable;
import io.highfidelity.hifiinterface.HifiUtils;
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";
private final boolean DEFAULT_AEC_ENABLED = true;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.settings);
boolean aecAvailable = AcousticEchoCanceler.isAvailable();
PreferenceManager.setDefaultValues(getContext(), R.xml.settings, false);
if (!AcousticEchoCanceler.isAvailable()) {
getPreferenceScreen().getPreferenceManager().findPreference("aec").setEnabled(false);
if (!aecAvailable) {
findPreference(PREFERENCE_KEY_AEC).setEnabled(false);
HifiUtils.getInstance().updateHifiSetting(HIFI_SETTINGS_ANDROID_GROUP, HIFI_SETTINGS_AEC_KEY, false);
}
getPreferenceScreen().getSharedPreferences().edit().putBoolean(PREFERENCE_KEY_AEC,
getHifiSettingBoolean(HIFI_SETTINGS_ANDROID_GROUP, HIFI_SETTINGS_AEC_KEY, false));
aecAvailable && HifiUtils.getInstance().getHifiSettingBoolean(HIFI_SETTINGS_ANDROID_GROUP, HIFI_SETTINGS_AEC_KEY, DEFAULT_AEC_ENABLED)).commit();
}
public static SettingsFragment newInstance() {
@ -46,15 +49,13 @@ public class SettingsFragment extends PreferenceFragment implements SharedPrefer
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));
HifiUtils.getInstance().updateHifiSetting(HIFI_SETTINGS_ANDROID_GROUP, HIFI_SETTINGS_AEC_KEY, sharedPreferences.getBoolean(key, false));
break;
default:
break;

View file

@ -1,73 +0,0 @@
package io.highfidelity.hifiinterface.fragment;
import android.app.Fragment;
import android.content.Context;
import android.os.Bundle;
import android.text.Html;
import android.text.Spanned;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import java.io.IOException;
import java.io.InputStream;
import io.highfidelity.hifiinterface.R;
public class SignedInFragment extends Fragment {
private Button mGetStartedButton;
private OnSignedInInteractionListener mListener;
public SignedInFragment() {
// Required empty public constructor
}
public static SignedInFragment newInstance() {
SignedInFragment fragment = new SignedInFragment();
return fragment;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_signedin, container, false);
mGetStartedButton = rootView.findViewById(R.id.getStarted);
mGetStartedButton.setOnClickListener(view -> {
getStarted();
});
return rootView;
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof SignedInFragment.OnSignedInInteractionListener) {
mListener = (SignedInFragment.OnSignedInInteractionListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnSignedInInteractionListener");
}
}
@Override
public void onDetach() {
super.onDetach();
mListener = null;
}
public void getStarted() {
if (mListener != null) {
mListener.onGettingStarted();
}
}
public interface OnSignedInInteractionListener {
void onGettingStarted();
}
}

View file

@ -1,42 +1,50 @@
package io.highfidelity.hifiinterface.fragment;
import android.app.Activity;
import android.app.Fragment;
import android.app.ProgressDialog;
import android.content.Context;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.TextView;
import org.qtproject.qt5.android.QtNative;
import io.highfidelity.hifiinterface.HifiUtils;
import io.highfidelity.hifiinterface.R;
import static org.qtproject.qt5.android.QtActivityDelegate.ApplicationActive;
import static org.qtproject.qt5.android.QtActivityDelegate.ApplicationInactive;
public class SignupFragment extends Fragment {
public class SignupFragment extends Fragment
implements OnBackPressedListener {
private EditText mEmail;
private EditText mUsername;
private EditText mPassword;
private TextView mError;
private TextView mCancelButton;
private TextView mActivityText;
private Button mSignupButton;
private CheckBox mKeepMeLoggedInCheckbox;
private ProgressDialog mDialog;
private ViewGroup mSignupForm;
private ViewGroup mLoggingInFrame;
private ViewGroup mLoggedInFrame;
public native void nativeSignup(String email, String username, String password); // move to SignupFragment
public native void nativeCancelSignup();
public native void nativeLogin(String username, String password, Activity usernameChangedListener);
public native void nativeCancelLogin();
private boolean mLoginInProgress;
private boolean mSignupInProgress;
private boolean mSignupSuccess;
public native void signup(String email, String username, String password); // move to SignupFragment
public native void cancelSignup();
public native void login(String username, String password, boolean keepLoggedIn);
public native void cancelLogin();
private SignupFragment.OnSignupInteractionListener mListener;
@ -59,18 +67,23 @@ public class SignupFragment extends Fragment {
mPassword = rootView.findViewById(R.id.password);
mError = rootView.findViewById(R.id.error);
mSignupButton = rootView.findViewById(R.id.signupButton);
mCancelButton = rootView.findViewById(R.id.cancelButton);
mActivityText = rootView.findViewById(R.id.activityText);
mKeepMeLoggedInCheckbox = rootView.findViewById(R.id.keepMeLoggedIn);
mSignupForm = rootView.findViewById(R.id.signupForm);
mLoggedInFrame = rootView.findViewById(R.id.loggedInFrame);
mLoggingInFrame = rootView.findViewById(R.id.loggingInFrame);
rootView.findViewById(R.id.cancel).setOnClickListener(view -> onCancelSignup());
mSignupButton.setOnClickListener(view -> signup());
mCancelButton.setOnClickListener(view -> login());
mPassword.setOnEditorActionListener(
(textView, actionId, keyEvent) -> {
if (actionId == EditorInfo.IME_ACTION_DONE) {
mSignupButton.performClick();
return true;
}
return false;
});
rootView.findViewById(R.id.getStarted).setOnClickListener(view -> onGetStartedClicked());
mPassword.setOnEditorActionListener((textView, actionId, keyEvent) -> onPasswordEditorAction(textView, actionId, keyEvent));
mKeepMeLoggedInCheckbox.setChecked(HifiUtils.getInstance().isKeepingLoggedIn());
return rootView;
}
@ -101,15 +114,22 @@ public class SignupFragment extends Fragment {
@Override
public void onStop() {
super.onStop();
cancelActivityIndicator();
// Leave the Qt app paused
QtNative.setApplicationState(ApplicationInactive);
hideKeyboard();
}
private void login() {
private boolean onPasswordEditorAction(TextView textView, int actionId, KeyEvent keyEvent) {
if (actionId == EditorInfo.IME_ACTION_DONE) {
mSignupButton.performClick();
return true;
}
return false;
}
private void onCancelSignup() {
if (mListener != null) {
mListener.onLoginRequested();
mListener.onCancelSignup();
}
}
@ -123,8 +143,11 @@ public class SignupFragment extends Fragment {
} else {
mSignupButton.setEnabled(false);
hideError();
mActivityText.setText(R.string.creating_account);
showActivityIndicator();
nativeSignup(email, username, password);
mSignupInProgress = true;
mSignupSuccess = false;
signup(email, username, password);
}
}
@ -137,23 +160,21 @@ public class SignupFragment extends Fragment {
}
private void showActivityIndicator() {
if (mDialog == null) {
mDialog = new ProgressDialog(getContext());
}
mDialog.setMessage(getString(R.string.creating_account));
mDialog.setCancelable(true);
mDialog.setOnCancelListener(dialogInterface -> {
nativeCancelSignup();
cancelActivityIndicator();
mSignupButton.setEnabled(true);
});
mDialog.show();
mSignupForm.setVisibility(View.GONE);
mLoggedInFrame.setVisibility(View.GONE);
mLoggingInFrame.setVisibility(View.VISIBLE);
}
private void cancelActivityIndicator() {
if (mDialog != null) {
mDialog.cancel();
}
private void showLoggedInMessage() {
mSignupForm.setVisibility(View.GONE);
mLoggingInFrame.setVisibility(View.GONE);
mLoggedInFrame.setVisibility(View.VISIBLE);
}
private void showSignupForm() {
mLoggingInFrame.setVisibility(View.GONE);
mLoggedInFrame.setVisibility(View.GONE);
mSignupForm.setVisibility(View.VISIBLE);
}
private void showError(String error) {
mError.setText(error);
@ -167,51 +188,73 @@ public class SignupFragment extends Fragment {
public interface OnSignupInteractionListener {
void onSignupCompleted();
void onLoginRequested();
void onCancelSignup();
}
private void onGetStartedClicked() {
if (mListener != null) {
mListener.onSignupCompleted();
}
}
public void handleSignupCompleted() {
mSignupInProgress = false;
String username = mUsername.getText().toString().trim();
String password = mPassword.getText().toString();
mDialog.setMessage(getString(R.string.logging_in));
mDialog.setCancelable(true);
mDialog.setOnCancelListener(dialogInterface -> {
nativeCancelLogin();
cancelActivityIndicator();
if (mListener != null) {
mListener.onLoginRequested();
}
getActivity().runOnUiThread(() -> {
mActivityText.setText(R.string.logging_in);
});
mDialog.show();
nativeLogin(username, password, getActivity());
mLoginInProgress = true;
boolean keepUserLoggedIn = mKeepMeLoggedInCheckbox.isChecked();
login(username, password, keepUserLoggedIn);
}
public void handleSignupFailed(String error) {
mSignupInProgress = false;
getActivity().runOnUiThread(() -> {
mSignupButton.setEnabled(true);
cancelActivityIndicator();
showSignupForm();
mError.setText(error);
mError.setVisibility(View.VISIBLE);
});
}
public void handleLoginCompleted(boolean success) {
mLoginInProgress = false;
getActivity().runOnUiThread(() -> {
mSignupButton.setEnabled(true);
cancelActivityIndicator();
if (success) {
if (mListener != null) {
mListener.onSignupCompleted();
}
mSignupSuccess = true;
showLoggedInMessage();
} else {
// Registration was successful but login failed.
// Let the user to login manually
mListener.onLoginRequested();
mListener.onCancelSignup();
showSignupForm();
}
});
}
@Override
public boolean doBack() {
if (mSignupInProgress) {
cancelSignup();
} else if (mLoginInProgress) {
cancelLogin();
}
if (mSignupInProgress || mLoginInProgress) {
showSignupForm();
mLoginInProgress = false;
mSignupInProgress = false;
mSignupButton.setEnabled(true);
return true;
} else if (mSignupSuccess) {
onGetStartedClicked();
return true;
} else {
return false;
}
}
}

View file

@ -0,0 +1,93 @@
package io.highfidelity.hifiinterface.fragment;
import android.app.Fragment;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import io.highfidelity.hifiinterface.R;
public class StartMenuFragment extends Fragment {
private String TAG = "HighFidelity";
private StartMenuInteractionListener mListener;
public StartMenuFragment() {
// Required empty public constructor
}
public static StartMenuFragment newInstance() {
StartMenuFragment fragment = new StartMenuFragment();
return fragment;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View rootView = inflater.inflate(R.layout.fragment_login_menu, container, false);
rootView.findViewById(R.id.signupButton).setOnClickListener(view -> {
if (mListener != null) {
mListener.onSignupButtonClicked();
}
});
rootView.findViewById(R.id.loginButton).setOnClickListener(view -> {
if (mListener != null) {
mListener.onLoginButtonClicked();
}
});
rootView.findViewById(R.id.steamLoginButton).setOnClickListener(view -> {
if (mListener != null) {
mListener.onSteamLoginButtonClicked();
}
});
rootView.findViewById(R.id.takeMeInWorld).setOnClickListener(view -> {
if (mListener != null) {
mListener.onSkipLoginClicked();
}
});
return rootView;
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof StartMenuInteractionListener) {
mListener = (StartMenuInteractionListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement StartMenuInteractionListener");
}
}
@Override
public void onDetach() {
super.onDetach();
mListener = null;
}
/**
* This interface must be implemented by activities that contain this
* fragment to allow an interaction in this fragment to be communicated
* to the activity and potentially other fragments contained in that
* activity.
* <p>
* See the Android Training lesson <a href=
* "http://developer.android.com/training/basics/fragments/communicating.html"
* >Communicating with Other Fragments</a> for more information.
*/
public interface StartMenuInteractionListener {
void onSignupButtonClicked();
void onLoginButtonClicked();
void onSkipLoginClicked();
void onSteamLoginButtonClicked();
}
}

View file

@ -4,9 +4,11 @@ import android.app.Fragment;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.net.http.SslError;
import android.os.Bundle;
import android.os.Handler;
import android.text.TextUtils;
import android.view.GestureDetector;
import android.view.KeyEvent;
import android.view.LayoutInflater;
@ -14,6 +16,7 @@ import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AlphaAnimation;
import android.webkit.CookieManager;
import android.webkit.SslErrorHandler;
import android.webkit.WebChromeClient;
import android.webkit.WebResourceError;
@ -25,6 +28,7 @@ import android.webkit.WebViewClient;
import android.widget.ProgressBar;
import android.widget.Toast;
import io.highfidelity.hifiinterface.BuildConfig;
import io.highfidelity.hifiinterface.R;
import io.highfidelity.hifiinterface.WebViewActivity;
@ -32,6 +36,7 @@ public class WebViewFragment extends Fragment implements GestureDetector.OnGestu
public static final String URL = "url";
public static final String TOOLBAR_VISIBLE = "toolbar_visible";
public static final String CLEAR_COOKIES = "clear_cookies";
private static final long DELAY_HIDE_TOOLBAR_MILLIS = 3000;
private static final long FADE_OUT_DURATION = 2000;
@ -41,6 +46,7 @@ public class WebViewFragment extends Fragment implements GestureDetector.OnGestu
private ProgressBar mProgressBar;
private String mUrl;
private boolean mToolbarVisible;
private boolean mClearCookies;
private OnWebViewInteractionListener mListener;
private Runnable mCloseAction;
@ -170,6 +176,7 @@ public class WebViewFragment extends Fragment implements GestureDetector.OnGestu
if (getArguments() != null) {
mUrl = getArguments().getString(URL);
mToolbarVisible = getArguments().getBoolean(TOOLBAR_VISIBLE);
mClearCookies = getArguments().getBoolean(CLEAR_COOKIES);
}
}
@ -179,6 +186,10 @@ public class WebViewFragment extends Fragment implements GestureDetector.OnGestu
View rootView = inflater.inflate(R.layout.fragment_web_view, container, false);
mProgressBar = rootView.findViewById(R.id.toolbarProgressBar);
myWebView = rootView.findViewById(R.id.web_view);
if (mClearCookies) {
CookieManager.getInstance().removeAllCookies(null);
}
mHandler = new Handler();
gestureDetector = new GestureDetector(this);
gestureDetector.setOnDoubleTapListener(new GestureDetector.OnDoubleTapListener() {
@ -251,6 +262,7 @@ public class WebViewFragment extends Fragment implements GestureDetector.OnGestu
void onWebLoaded(String url, SafenessLevel safenessLevel);
void onTitleReceived(String title);
void onExpand();
void onOAuthAuthorizeCallback(Uri uri);
}
@ -320,6 +332,18 @@ public class WebViewFragment extends Fragment implements GestureDetector.OnGestu
super.onLoadResource(view, url);
}
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
if (!TextUtils.isEmpty(BuildConfig.OAUTH_REDIRECT_URI) &&
request.getUrl().toString().startsWith(BuildConfig.OAUTH_REDIRECT_URI)) {
if (mListener != null) {
mListener.onOAuthAuthorizeCallback(request.getUrl());
}
return true;
}
return super.shouldOverrideUrlLoading(view, request);
}
}
class HiFiWebChromeClient extends WebChromeClient {

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="350dp"
android:width="340dp"
android:height="100dp"
android:viewportWidth="350"
android:viewportHeight="100">

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="11dp"
android:height="14dp"
android:viewportWidth="11"
android:viewportHeight="14">
<path
android:fillColor="#ffffff"
android:pathData="M2.98427,0.868092 C2.35913,0.324495,1.41169,0.390596,0.868092,1.01573 C0.324495,1.64087,0.390596,2.58831,1.01573,3.13191 L2.98427,0.868092 Z M8,7.21739 L8.93497,8.39035 L10.3436,7.26752 L8.98427,6.08548 L8,7.21739 Z M1.06503,10.827 C0.417224,11.3434,0.310672,12.2872,0.82704,12.935 C1.34341,13.5828,2.28716,13.6893,2.93497,13.173 L1.06503,10.827 Z M1.01573,3.13191 L7.01573,8.3493 L8.98427,6.08548 L2.98427,0.868092 L1.01573,3.13191 Z M7.06503,6.04443 L1.06503,10.827 L2.93497,13.173 L8.93497,8.39035 L7.06503,6.04443 Z" />
</vector>

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="21dp"
android:height="21dp"
android:viewportWidth="21"
android:viewportHeight="21">
<path
android:fillColor="#ffffff"
android:pathData="M10.4866,0 C4.92045,0,0.367395,4.32104,0,9.78734 L5.45262,11.9841 C5.93184,11.651,6.51333,11.4545,7.14145,11.4545 C7.27641,11.4545,7.4083,11.4666,7.53829,11.4841 L10.1047,7.72495 C10.1083,5.56672,11.861,3.81818,14.0229,3.81818 C16.1872,3.81818,17.9416,5.57035,17.9416,7.73182 C17.9416,9.89329,16.1872,11.6455,14.0229,11.6455 C14.021,11.6455,14.0189,11.6453,14.017,11.6453 L10.0936,14.2008 C10.0986,14.2712,10.1043,14.3419,10.1043,14.4136 C10.1043,16.048,8.77791,17.3727,7.14145,17.3727 C5.69539,17.3727,4.49304,16.3376,4.2325,14.969 L0.378099,13.3841 C1.6334,17.7801,5.6822,21,10.4866,21 C16.2931,21,21,16.2991,21,10.5 C21,4.70114,16.2931,0,10.4866,0 Z M7.14145,16.0364 C6.96655,16.0364,6.79833,16.0081,6.64044,15.9569 L6.63968,15.9589 L6.59151,15.939 C6.54506,15.9224,6.49975,15.9037,6.45541,15.8831 L5.15462,15.3483 C5.50614,16.0927,6.26253,16.6091,7.14145,16.6091 C8.35546,16.6091,9.33971,15.6263,9.33971,14.4136 C9.33971,13.201,8.35546,12.2182,7.14145,12.2182 C6.87269,12.2182,6.61636,12.2688,6.37818,12.357 L7.75448,12.9114 C7.76404,12.9154,7.77359,12.9188,7.78296,12.923 L7.89001,12.9662 L7.88714,12.9732 C8.40898,13.243,8.76625,13.7861,8.76625,14.4136 C8.76625,15.3098,8.03872,16.0364,7.14145,16.0364 Z M16.7946,7.73182 C16.7946,6.20302,15.5537,4.96364,14.0229,4.96364 C12.4922,4.96364,11.2512,6.20302,11.2512,7.73182 C11.2512,9.26062,12.4922,10.5,14.0229,10.5 C15.5537,10.5,16.7946,9.26062,16.7946,7.73182 Z M12.0158,7.73182 C12.0158,6.62474,12.9144,5.72727,14.0229,5.72727 C15.1314,5.72727,16.03,6.62474,16.03,7.73182 C16.03,8.8389,15.1314,9.73636,14.0229,9.73636 C12.9144,9.73636,12.0158,8.8389,12.0158,7.73182 Z" />
</vector>

View file

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:state_pressed="true" >
<shape android:shape="rectangle" >
<corners android:radius="4dip" />
<stroke android:width="1dip" android:color="@color/colorButton2" />
<solid android:color="@color/colorButton2"/>
</shape>
</item>
<item android:state_focused="true">
<shape android:shape="rectangle" >
<corners android:radius="4dip" />
<stroke android:width="1dip" android:color="@color/colorButton2" />
<solid android:color="@color/colorButton2"/>
</shape>
</item>
<item>
<shape android:shape="rectangle" >
<corners android:radius="4dip" />
<stroke android:width="1dip" android:color="@color/colorButton2" />
<solid android:color="@color/colorButton2"/>
</shape>
</item>
</selector>

View file

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:state_pressed="true" >
<shape android:shape="rectangle" >
<corners android:radius="4dip" />
<stroke android:width="1dip" android:color="@color/colorButton4" />
<solid android:color="@color/colorButton4"/>
</shape>
</item>
<item android:state_focused="true">
<shape android:shape="rectangle" >
<corners android:radius="4dip" />
<stroke android:width="1dip" android:color="@color/colorButton4" />
<solid android:color="@color/colorButton4"/>
</shape>
</item>
<item>
<shape android:shape="rectangle" >
<corners android:radius="4dip" />
<stroke android:width="1dip" android:color="@color/colorButton4" />
<solid android:color="@color/colorButton4"/>
</shape>
</item>
</selector>

View file

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" android:padding="9dp">
<corners android:radius="4dip" />
<stroke android:width="1dip" android:color="@android:color/black" />
<solid android:color="@color/backgroundEditText"/>
</shape>

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".LoginMenuActivity">
<FrameLayout
android:id="@+id/content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.constraint.ConstraintLayout>

View file

@ -6,6 +6,17 @@
android:layout_height="match_parent"
android:background="@color/backgroundLight">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/encourage_login_background"
android:scaleType="fitXY" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#B2000000" />
<ImageView
android:id="@+id/header"
android:layout_width="@dimen/header_hifi_width"
@ -17,126 +28,246 @@
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/hifi_header" />
<TextView
android:id="@+id/error"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="25dp"
android:layout_marginLeft="9dp"
android:layout_marginRight="9dp"
android:fontFamily="@font/raleway"
android:textColor="@color/colorLoginError"
android:textSize="14sp"
app:layout_constraintBottom_toTopOf="@id/username"
app:layout_constraintLeft_toLeftOf="@id/username"
android:visibility="invisible"/>
<EditText
android:id="@+id/username"
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:paddingTop="14dp"
android:ems="10"
android:fontFamily="@font/raleway"
android:textSize="17sp"
android:inputType="textEmailAddress"
android:textStyle="italic"
android:textColor="@color/editTextColor"
android:textColorHint="@color/editTextColor"
android:gravity="left|center_vertical"
app:layout_constraintTop_toBottomOf="@id/header"
android:layout_marginTop="70dp"
android:hint="@string/username_or_email" />
<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:background="@drawable/rounded_edit"
android:padding="7dp"
android:drawablePadding="55dp"
android:paddingTop="14dp"
android:drawableEnd="@drawable/ic_eye_noshow"
android:ems="10"
android:fontFamily="@font/raleway"
android:textSize="17sp"
android:textStyle="italic"
android:textColor="@color/editTextColor"
android:textColorHint="@color/editTextColor"
android:gravity="left|center_vertical"
android:imeOptions="actionDone"
android:hint="@string/password"
android:inputType="textPassword" />
</android.support.design.widget.TextInputLayout>
<Button
android:id="@+id/loginButton"
android:layout_width="154dp"
android:layout_height="38dp"
android:background="@drawable/rounded_button"
android:fontFamily="@font/raleway_semibold"
android:paddingBottom="0dp"
android:paddingTop="0dp"
android:text="@string/login"
android:textColor="@color/white_opaque"
android:textAllCaps="false"
android:textSize="18sp"
app:layout_constraintRight_toRightOf="@id/username"
app:layout_constraintTop_toBottomOf="@id/forgotPassword"
app:layout_goneMarginTop="4dp"/>
<TextView
android:id="@+id/forgotPassword"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="9dp"
android:paddingBottom="16dp"
android:fontFamily="@font/raleway_semibold"
android:textSize="14dp"
android:text="@string/forgot_password"
android:textStyle="italic"
app:layout_constraintRight_toRightOf="@id/passwordLayout"
app:layout_constraintTop_toBottomOf="@id/passwordLayout"
android:textColor="@color/colorButton1"/>
<Button
android:id="@+id/signupButton"
<android.support.constraint.ConstraintLayout
android:id="@+id/loggingInFrame"
android:layout_width="0dp"
app:layout_constraintWidth_default="spread"
android:layout_height="38dp"
android:background="@drawable/rounded_secondary_button"
android:fontFamily="@font/raleway_semibold"
android:paddingBottom="0dp"
android:paddingTop="0dp"
android:layout_marginRight="15dp"
android:text="@string/signup"
android:textColor="@color/white_opaque"
android:textAllCaps="false"
android:textSize="18sp"
app:layout_constraintLeft_toLeftOf="@id/passwordLayout"
app:layout_constraintTop_toTopOf="@id/loginButton"
app:layout_constraintRight_toLeftOf="@id/loginButton"
app:layout_goneMarginTop="4dp"/>
android:layout_height="0dp"
android:layout_marginTop="@dimen/login_form_margin_top"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/header"
app:layout_constraintBottom_toBottomOf="parent"
android:visibility="gone">
<TextView
android:id="@+id/loggingInText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/logging_in"
android:fontFamily="@font/raleway_bold"
android:textSize="24sp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:layout_marginTop="83dp"
/>
<ProgressBar
android:layout_width="101dp"
android:layout_height="101dp"
android:layout_marginTop="20dp"
app:layout_constraintTop_toBottomOf="@id/loggingInText"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:indeterminateTint="#00B4EF"
/>
</android.support.constraint.ConstraintLayout>
<android.support.constraint.ConstraintLayout
android:id="@+id/loggedInFrame"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="@dimen/login_form_margin_top"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/header"
app:layout_constraintBottom_toBottomOf="parent"
android:visibility="gone">
<TextView
android:id="@+id/loggedInText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/logged_in_welcome"
android:fontFamily="@font/raleway_bold"
android:textSize="24sp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:layout_marginTop="115dp"
/>
<Button
android:id="@+id/getStarted"
android:layout_width="@dimen/button_medium_width"
android:layout_height="@dimen/button_medium_height"
android:layout_marginTop="22dp"
android:background="@drawable/rounded_button_color1"
android:fontFamily="@font/raleway_bold"
android:text="@string/get_started"
android:textColor="@color/white_opaque"
android:textAllCaps="false"
android:textSize="@dimen/button_medium_text_size"
app:layout_constraintTop_toBottomOf="@id/loggedInText"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_goneMarginTop="4dp"/>
</android.support.constraint.ConstraintLayout>
<android.support.constraint.ConstraintLayout
android:id="@+id/loginForm"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="@dimen/login_form_margin_top"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/header"
app:layout_constraintBottom_toBottomOf="parent"
android:visibility="gone">
<TextView
android:id="@+id/error"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="25dp"
android:layout_marginLeft="9dp"
android:layout_marginRight="9dp"
android:fontFamily="sans-serif"
android:textColor="@color/colorLoginError"
android:textSize="14sp"
app:layout_constraintBottom_toTopOf="@id/username"
app:layout_constraintLeft_toLeftOf="@id/username"
android:visibility="invisible"/>
<EditText
android:id="@+id/username"
android:layout_width="match_parent"
android:layout_height="27dp"
android:layout_marginLeft="@dimen/login_margin"
android:layout_marginRight="@dimen/login_margin"
android:background="@color/white_opaque"
android:paddingLeft="@dimen/edit_text_padding"
android:ems="10"
android:fontFamily="sans-serif"
android:textSize="@dimen/login_edit_text_size"
android:inputType="textEmailAddress"
android:textColor="@color/editTextColor"
android:textColorHint="@color/editTextColor"
android:gravity="left|center_vertical"
android:hint="@string/username_or_email"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginTop="83dp"/>
<android.support.design.widget.TextInputLayout
android:id="@+id/passwordLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/login_margin"
android:layout_marginRight="@dimen/login_margin"
android:background="@color/white_opaque"
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="15dp"
>
<android.support.design.widget.TextInputEditText
android:id="@+id/password"
android:layout_width="match_parent"
android:layout_height="@dimen/login_edit_text_height"
android:background="@color/white_opaque"
android:paddingLeft="@dimen/edit_text_padding"
android:drawablePadding="45dp"
android:drawableEnd="@drawable/ic_eye_noshow"
android:ems="10"
android:fontFamily="sans-serif"
android:textSize="@dimen/login_edit_text_size"
android:textColor="@color/editTextColor"
android:textColorHint="@color/editTextColor"
android:gravity="left|center_vertical"
android:imeOptions="actionDone"
android:hint="@string/password"
android:inputType="textPassword" />
</android.support.design.widget.TextInputLayout>
<CheckBox
android:id="@+id/keepMeLoggedIn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="13dp"
android:layout_marginRight="66dp"
app:layout_constraintTop_toBottomOf="@id/passwordLayout"
app:layout_constraintRight_toRightOf="parent"
android:padding="0dp" />
<TextView
android:id="@+id/keepMeLoggedInLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/raleway_bold"
android:textSize="12sp"
android:text="@string/keep_me_logged_in"
app:layout_constraintRight_toLeftOf="@id/keepMeLoggedIn"
app:layout_constraintTop_toTopOf="@id/keepMeLoggedIn"
app:layout_constraintBottom_toBottomOf="@id/keepMeLoggedIn"
android:textColor="@color/white_opaque"/>
<Button
android:id="@+id/loginButton"
android:layout_width="@dimen/button_medium_width"
android:layout_height="@dimen/button_medium_height"
android:background="@drawable/rounded_button_color3"
android:fontFamily="@font/raleway_bold"
android:layout_marginTop="@dimen/button_medium_margin"
android:text="@string/log_in"
android:textColor="@color/white_opaque"
android:textAllCaps="false"
android:textSize="@dimen/button_medium_text_size"
app:layout_constraintRight_toRightOf="@id/username"
app:layout_constraintTop_toBottomOf="@id/keepMeLoggedIn" />
<TextView
android:id="@+id/cancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="8dp"
app:layout_constraintLeft_toLeftOf="@id/passwordLayout"
app:layout_constraintTop_toTopOf="@id/loginButton"
app:layout_constraintBottom_toBottomOf="@id/loginButton"
app:layout_constraintRight_toLeftOf="@id/loginButton"
android:textColor="@color/white_opaque"
android:fontFamily="@font/raleway_bold"
android:textSize="@dimen/button_medium_text_size"
android:text="@string/cancel_uppercase" />
<TextView
android:id="@+id/forgotPassword"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="sans-serif"
android:textSize="14sp"
android:layout_marginTop="18dp"
android:text="@string/cant_access_your_account"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/loginButton"
android:textColor="@color/colorButton1"/>
<TextView
android:id="@+id/takeMeInWorld"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/raleway_bold"
android:textSize="14sp"
android:layout_marginBottom="11dp"
android:padding="5dp"
android:text="@string/take_me_in_world"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:textColor="@color/white_opaque"/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_right_arrow"
android:layout_marginLeft="4dp"
app:layout_constraintLeft_toRightOf="@id/takeMeInWorld"
app:layout_constraintTop_toTopOf="@id/takeMeInWorld"
app:layout_constraintBottom_toBottomOf="@id/takeMeInWorld"
/>
</android.support.constraint.ConstraintLayout>

View file

@ -0,0 +1,122 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".fragment.StartMenuFragment">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/encourage_login_background"
android:scaleType="fitXY"
/>
<ImageView
android:id="@+id/header"
android:layout_width="@dimen/header_hifi_width"
android:layout_height="@dimen/header_hifi_height"
android:layout_marginTop="@dimen/header_hifi_margin_top"
android:contentDescription="HighFidelity"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/hifi_header" />
<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="@font/raleway_bold"
android:textSize="@dimen/login_menu_text_size"
android:layout_marginTop="37dp"
android:text="@string/be_anywere"
android:gravity="center"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/header"
/>
<Button
android:id="@+id/signupButton"
android:layout_width="@dimen/button_large_width"
android:layout_height="@dimen/button_large_height"
android:layout_marginTop="48dp"
android:background="@drawable/rounded_button_color1"
android:fontFamily="@font/raleway_bold"
android:text="@string/signup_uppercase"
android:textColor="@color/white_opaque"
android:textAllCaps="false"
android:textSize="@dimen/button_large_text_size"
app:layout_constraintTop_toBottomOf="@id/text"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="@dimen/login_menu_translucent_rectangle_height"
app:layout_constraintBottom_toBottomOf="parent" android:background="#B2000000">
<Button
android:id="@+id/loginButton"
android:layout_width="@dimen/button_large_width"
android:layout_height="@dimen/button_large_height"
android:background="@drawable/rounded_button_color3"
android:fontFamily="@font/raleway_bold"
android:layout_marginTop="@dimen/login_menu_button_margin_top"
android:text="@string/log_in"
android:textColor="@color/white_opaque"
android:textAllCaps="false"
android:textSize="@dimen/button_large_text_size"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"/>
<Button
android:id="@+id/steamLoginButton"
android:layout_width="@dimen/button_large_width"
android:layout_height="@dimen/button_large_height"
android:background="@drawable/rounded_button_color4"
android:fontFamily="@font/raleway_bold"
android:layout_marginTop="10dp"
android:text="@string/steam_log_in"
android:textAlignment="center"
android:textColor="@color/white_opaque"
android:textAllCaps="false"
android:textSize="@dimen/button_large_text_size"
android:drawableLeft="@drawable/ic_steam"
android:paddingLeft="38dp"
android:paddingRight="38dp"
app:layout_constraintTop_toBottomOf="@id/loginButton"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"/>
<TextView
android:id="@+id/takeMeInWorld"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/raleway_bold"
android:textSize="14sp"
android:layout_marginBottom="11dp"
android:padding="5dp"
android:text="@string/take_me_in_world"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:textColor="@color/white_opaque"/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_right_arrow"
android:layout_marginLeft="4dp"
app:layout_constraintLeft_toRightOf="@id/takeMeInWorld"
app:layout_constraintTop_toTopOf="@id/takeMeInWorld"
app:layout_constraintBottom_toBottomOf="@id/takeMeInWorld"
/>
</android.support.constraint.ConstraintLayout>
</android.support.constraint.ConstraintLayout>

View file

@ -1,63 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/backgroundLight">
<ImageView
android:id="@+id/header"
android:layout_width="@dimen/header_hifi_width"
android:layout_height="@dimen/header_hifi_height"
android:layout_marginTop="@dimen/header_hifi_margin_top"
android:contentDescription="HighFidelity"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/hifi_header" />
<TextView
android:id="@+id/welcome"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="50dp"
android:paddingLeft="86dp"
android:paddingRight="86dp"
android:fontFamily="@font/raleway"
android:textColor="@color/clearText"
android:textSize="24sp"
android:text="@string/signedin_welcome"
app:layout_constraintTop_toBottomOf="@id/header"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:gravity="center"
/>
<Button
android:id="@+id/getStarted"
android:layout_width="217dp"
android:layout_height="38dp"
android:layout_marginTop="30dp"
android:background="@drawable/rounded_button"
android:fontFamily="@font/raleway_semibold"
android:paddingBottom="0dp"
android:paddingLeft="25dp"
android:paddingRight="25dp"
android:paddingTop="0dp"
android:text="@string/get_started"
android:textColor="@color/white_opaque"
android:textAllCaps="false"
android:textSize="18sp"
app:layout_constraintTop_toBottomOf="@id/welcome"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_goneMarginTop="4dp"/>
</android.support.constraint.ConstraintLayout>

View file

@ -6,6 +6,18 @@
android:layout_height="match_parent"
android:background="@color/backgroundLight">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/encourage_login_background"
android:scaleType="fitXY" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#B2000000" />
<ImageView
android:id="@+id/header"
android:layout_width="@dimen/header_hifi_width"
@ -17,93 +29,168 @@
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/hifi_header" />
<android.support.constraint.ConstraintLayout
android:id="@+id/loggingInFrame"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="100dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/header"
app:layout_constraintBottom_toBottomOf="parent"
android:visibility="gone">
<TextView
android:id="@+id/activityText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/logging_in"
android:fontFamily="@font/raleway_bold"
android:textSize="24sp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:layout_marginTop="83dp"
/>
<ProgressBar
android:layout_width="101dp"
android:layout_height="101dp"
android:layout_marginTop="20dp"
app:layout_constraintTop_toBottomOf="@id/activityText"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:indeterminateTint="#00B4EF"
/>
</android.support.constraint.ConstraintLayout>
<android.support.constraint.ConstraintLayout
android:id="@+id/loggedInFrame"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="100dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/header"
app:layout_constraintBottom_toBottomOf="parent"
android:visibility="gone">
<TextView
android:id="@+id/loggedInText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/logged_in_welcome"
android:fontFamily="@font/raleway_bold"
android:textSize="24sp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:layout_marginTop="115dp"
/>
<Button
android:id="@+id/getStarted"
android:layout_width="@dimen/button_medium_width"
android:layout_height="@dimen/button_medium_height"
android:layout_marginTop="22dp"
android:background="@drawable/rounded_button_color1"
android:fontFamily="@font/raleway_bold"
android:text="@string/get_started"
android:textColor="@color/white_opaque"
android:textAllCaps="false"
android:textSize="@dimen/button_medium_text_size"
app:layout_constraintTop_toBottomOf="@id/loggedInText"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
</android.support.constraint.ConstraintLayout>
<android.support.constraint.ConstraintLayout
android:id="@+id/signupForm"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="100dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/header"
app:layout_constraintBottom_toBottomOf="parent"
android:visibility="visible">
<TextView
android:id="@+id/error"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:layout_marginLeft="9dp"
android:layout_marginRight="9dp"
android:fontFamily="@font/raleway"
android:fontFamily="sans-serif"
android:textColor="@color/colorLoginError"
android:textSize="14sp"
app:layout_constraintBottom_toTopOf="@id/email"
app:layout_constraintLeft_toLeftOf="@id/email"
app:layout_constraintRight_toRightOf="@id/email"
app:layout_constraintBottom_toTopOf="@id/username"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="@id/username"
app:layout_constraintRight_toRightOf="@id/username"
android:visibility="invisible"/>
<EditText
android:id="@+id/email"
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:paddingTop="14dp"
android:ems="10"
android:fontFamily="@font/raleway"
android:textSize="17sp"
android:inputType="textEmailAddress"
android:textStyle="italic"
android:textColor="@color/editTextColor"
android:textColorHint="@color/editTextColor"
android:gravity="left|center_vertical"
app:layout_constraintTop_toBottomOf="@id/header"
android:layout_marginTop="70dp"
android:hint="@string/email" />
<EditText
android:id="@+id/username"
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:paddingTop="14dp"
android:layout_height="@dimen/login_edit_text_height"
android:layout_marginLeft="@dimen/signup_margin"
android:layout_marginRight="@dimen/signup_margin"
android:background="@color/white_opaque"
android:paddingLeft="@dimen/edit_text_padding"
android:ems="10"
android:fontFamily="@font/raleway"
android:textSize="17sp"
android:fontFamily="sans-serif"
android:textSize="@dimen/login_edit_text_size"
android:inputType="text"
android:textStyle="italic"
android:textColor="@color/editTextColor"
android:textColorHint="@color/editTextColor"
android:gravity="left|center_vertical"
app:layout_constraintTop_toBottomOf="@id/email"
android:layout_marginTop="7dp"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginTop="70dp"
android:hint="@string/username" />
<EditText
android:id="@+id/email"
android:layout_width="match_parent"
android:layout_height="@dimen/login_edit_text_height"
android:layout_marginLeft="@dimen/signup_margin"
android:layout_marginRight="@dimen/signup_margin"
android:background="@color/white_opaque"
android:paddingLeft="@dimen/edit_text_padding"
android:ems="10"
android:fontFamily="sans-serif"
android:textSize="@dimen/login_edit_text_size"
android:inputType="textEmailAddress"
android:textColor="@color/editTextColor"
android:textColorHint="@color/editTextColor"
android:gravity="left|center_vertical"
app:layout_constraintTop_toBottomOf="@id/username"
android:layout_marginTop="14dp"
android:hint="@string/email" />
<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"
android:layout_marginLeft="@dimen/signup_margin"
android:layout_marginRight="@dimen/signup_margin"
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="7dp"
app:layout_constraintTop_toBottomOf="@id/email"
android:layout_marginTop="15dp"
>
<android.support.design.widget.TextInputEditText
android:id="@+id/password"
android:layout_width="match_parent"
android:layout_height="35dp"
android:background="@drawable/rounded_edit"
android:padding="7dp"
android:layout_height="27dp"
android:background="@color/white_opaque"
android:paddingLeft="@dimen/edit_text_padding"
android:drawablePadding="55dp"
android:paddingTop="14dp"
android:drawableEnd="@drawable/ic_eye_noshow"
android:ems="10"
android:fontFamily="@font/raleway"
android:textSize="17sp"
android:textStyle="italic"
android:fontFamily="sans-serif"
android:textSize="@dimen/login_edit_text_size"
android:textColor="@color/editTextColor"
android:textColorHint="@color/editTextColor"
android:gravity="left|center_vertical"
@ -112,40 +199,54 @@
android:inputType="textPassword" />
</android.support.design.widget.TextInputLayout>
<CheckBox
android:id="@+id/keepMeLoggedIn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="13dp"
android:layout_marginRight="66dp"
app:layout_constraintTop_toBottomOf="@id/passwordLayout"
app:layout_constraintRight_toRightOf="parent"
android:padding="0dp" />
<TextView
android:id="@+id/keepMeLoggedInLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/raleway_bold"
android:textSize="12sp"
android:text="@string/keep_me_logged_in"
app:layout_constraintRight_toLeftOf="@id/keepMeLoggedIn"
app:layout_constraintTop_toTopOf="@id/keepMeLoggedIn"
app:layout_constraintBottom_toBottomOf="@id/keepMeLoggedIn"
android:textColor="@color/white_opaque"/>
<Button
android:id="@+id/signupButton"
android:layout_width="154dp"
android:layout_height="38dp"
android:layout_marginTop="44dp"
android:background="@drawable/rounded_button"
android:fontFamily="@font/raleway_semibold"
android:paddingBottom="0dp"
android:paddingTop="0dp"
android:text="@string/signup"
android:layout_width="@dimen/button_medium_width"
android:layout_height="@dimen/button_medium_height"
android:layout_marginTop="10dp"
android:background="@drawable/rounded_button_color1"
android:fontFamily="@font/raleway_bold"
android:text="@string/signup_uppercase"
android:textColor="@color/white_opaque"
android:textAllCaps="false"
android:textSize="18sp"
android:textSize="@dimen/button_medium_text_size"
app:layout_constraintRight_toRightOf="@id/username"
app:layout_constraintTop_toBottomOf="@id/passwordLayout"
app:layout_goneMarginTop="4dp"/>
app:layout_constraintTop_toBottomOf="@id/keepMeLoggedIn" />
<Button
android:id="@+id/cancelButton"
android:layout_width="0dp"
app:layout_constraintWidth_default="spread"
android:layout_height="38dp"
android:background="@drawable/rounded_secondary_button"
android:fontFamily="@font/raleway_semibold"
android:paddingBottom="0dp"
android:paddingTop="0dp"
android:layout_marginRight="15dp"
android:text="@string/cancel"
android:textColor="@color/white_opaque"
android:textAllCaps="false"
android:textSize="18sp"
<TextView
android:id="@+id/cancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="@id/passwordLayout"
app:layout_constraintTop_toTopOf="@id/signupButton"
app:layout_constraintBottom_toBottomOf="@id/signupButton"
app:layout_constraintRight_toLeftOf="@id/signupButton"
app:layout_goneMarginTop="4dp"/>
android:textColor="@color/white_opaque"
android:fontFamily="@font/raleway_bold"
android:textSize="@dimen/button_medium_text_size"
android:text="@string/cancel_uppercase" />
</android.support.constraint.ConstraintLayout>
</android.support.constraint.ConstraintLayout>

View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="header_hifi_margin_top">35dp</dimen>
<dimen name="header_hifi_height">84dp</dimen>
<dimen name="header_hifi_width">340dp</dimen>
<dimen name="button_medium_width">171dp</dimen>
<dimen name="button_medium_height">42dp</dimen>
<dimen name="button_medium_margin">10dp</dimen>
<dimen name="login_edit_text_size">14sp</dimen>
<dimen name="button_large_text_size">18sp</dimen>
<dimen name="button_medium_text_size">18sp</dimen>
<dimen name="login_margin">72dp</dimen>
<dimen name="signup_margin">76dp</dimen>
<dimen name="login_form_margin_top">100dp</dimen>
<dimen name="login_edit_text_height">27dp</dimen>
<dimen name="button_large_width">238dp</dimen>
<dimen name="button_large_height">42dp</dimen>
<dimen name="login_menu_translucent_rectangle_height">270dp</dimen>
<dimen name="login_menu_button_margin_top">86dp</dimen>
<dimen name="login_menu_text_size">22sp</dimen>
<dimen name="login_edit_text_padding">16dp</dimen>
</resources>

View file

@ -10,6 +10,8 @@
<color name="tabs">#1EB5EC</color>
<color name="colorButton1">#00B4EF</color>
<color name="colorButton2">#828282</color>
<color name="colorButton3">#8F8F8F</color>
<color name="colorButton4">#434343</color>
<color name="backgroundDark">#333333</color>
<color name="backgroundLight">#4F4F4F</color>
<color name="backgroundSearch">#33999999</color>

View file

@ -33,10 +33,26 @@
<dimen name="domainMarginBottom">6dp</dimen>
<dimen name="domainNameHeight">64dp</dimen>
<dimen name="header_hifi_margin_top">56dp</dimen>
<dimen name="header_hifi_height">101dp</dimen>
<dimen name="header_hifi_width">425dp</dimen>
<dimen name="header_hifi_margin_top">32dp</dimen>
<dimen name="header_hifi_height">76dp</dimen>
<dimen name="header_hifi_width">306dp</dimen>
<dimen name="list_vertical_padding">8dp</dimen>
<dimen name="button_medium_width">150dp</dimen>
<dimen name="button_medium_height">38dp</dimen>
<dimen name="login_margin">65dp</dimen>
<dimen name="signup_margin">68dp</dimen>
<dimen name="login_form_margin_top">90dp</dimen>
<dimen name="button_medium_margin">9dp</dimen>
<dimen name="button_medium_text_size">16sp</dimen>
<dimen name="button_large_text_size">16sp</dimen>
<dimen name="login_edit_text_size">13sp</dimen>
<dimen name="login_edit_text_height">24dp</dimen>
<dimen name="button_large_width">214dp</dimen>
<dimen name="button_large_height">38dp</dimen>
<dimen name="login_menu_translucent_rectangle_height">300dp</dimen>
<dimen name="login_menu_button_margin_top">77dp</dimen>
<dimen name="login_menu_text_size">20sp</dimen>
<dimen name="login_edit_text_padding">14dp</dimen>
</resources>

View file

@ -16,9 +16,9 @@
<string name="password">Password</string>
<string name="login">Login</string>
<string name="logout">Logout</string>
<string name="forgot_password"><u>Forgot password?</u>\u00A0</string>
<string name="cant_access_your_account"><u>Can\u0027t access your account?</u></string>
<string name="login_username_or_password_incorrect">Username or password incorrect.</string>
<string name="logging_in">Logging into High Fidelity</string>
<string name="logging_in">Logging in</string>
<string name="search_hint"><i>Search for a place by name</i>\u00A0</string>
<string name="search_loading">Loading places…</string>
<string name="search_no_results">No places exist with that name</string>
@ -26,16 +26,20 @@
<string name="your_last_location">Your Last Location</string>
<string name="online">Online</string>
<string name="signup">Sign Up</string>
<string name="signup_uppercase">SIGN UP</string>
<string name="creating_account">Creating your High Fidelity account</string>
<string name="signup_email_username_or_password_incorrect">Email, username or password incorrect.</string>
<string name="signedin_welcome">You are now signed into High Fidelity</string>
<string name="logged_in_welcome">You are now logged in!</string>
<string name="welcome">Welcome</string>
<string name="cancel">Cancel</string>
<string name="get_started">Get Started</string>
<string name="cancel_uppercase">CANCEL</string>
<string name="get_started">GET STARTED</string>
<!-- tags -->
<string name="tagFragmentHome">tagFragmentHome</string>
<string name="tagFragmentLogin">tagFragmentLogin</string>
<string name="tagFragmentLoggingIn">tagFragmentLogginIn</string>
<string name="tagFragmentSignup">tagFragmentSignup</string>
<string name="tagFragmentPolicy">tagFragmentPolicy</string>
<string name="tagFragmentPeople">tagFragmentPeople</string>
@ -45,4 +49,9 @@
<string name="AEC">AEC</string>
<string name="acoustic_echo_cancellation">Acoustic Echo Cancellation</string>
<string name="settings_developer">Developer</string>
<string name="log_in">LOG IN</string>
<string name="keep_me_logged_in">Keep Me Logged In</string>
<string name="take_me_in_world">No thanks, take me in-world!</string>
<string name="be_anywere">BE ANYWHERE, WITH ANYONE \nRIGHT NOW</string>
<string name="steam_log_in">STEAM LOG IN</string>
</resources>

View file

@ -6,6 +6,7 @@
<SwitchPreference
android:key="aec"
android:title="@string/AEC"
android:summary="@string/acoustic_echo_cancellation" />
android:summary="@string/acoustic_echo_cancellation"
android:defaultValue="true" />
</PreferenceCategory>
</PreferenceScreen>

View file

@ -11,7 +11,7 @@ setup_memory_debugger()
# link in the shared libraries
link_hifi_libraries(
audio avatars octree gpu graphics fbx entities
audio avatars octree gpu graphics fbx hfm entities
networking animation recording shared script-engine embedded-webserver
controllers physics plugins midi image
)

View file

@ -656,6 +656,8 @@ void Agent::queryAvatars() {
ViewFrustum view;
view.setPosition(scriptedAvatar->getWorldPosition());
view.setOrientation(scriptedAvatar->getHeadOrientation());
view.setProjection(DEFAULT_FIELD_OF_VIEW_DEGREES, DEFAULT_ASPECT_RATIO,
DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP);
view.calculate();
ConicalViewFrustum conicalView { view };
@ -754,13 +756,13 @@ void Agent::processAgentAvatarAudio() {
const int16_t* nextSoundOutput = NULL;
if (_avatarSound) {
const QByteArray& soundByteArray = _avatarSound->getByteArray();
nextSoundOutput = reinterpret_cast<const int16_t*>(soundByteArray.data()
auto audioData = _avatarSound->getAudioData();
nextSoundOutput = reinterpret_cast<const int16_t*>(audioData->rawData()
+ _numAvatarSoundSentBytes);
int numAvailableBytes = (soundByteArray.size() - _numAvatarSoundSentBytes) > AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL
int numAvailableBytes = (audioData->getNumBytes() - _numAvatarSoundSentBytes) > AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL
? AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL
: soundByteArray.size() - _numAvatarSoundSentBytes;
: audioData->getNumBytes() - _numAvatarSoundSentBytes;
numAvailableSamples = (int16_t)numAvailableBytes / sizeof(int16_t);
@ -773,7 +775,7 @@ void Agent::processAgentAvatarAudio() {
}
_numAvatarSoundSentBytes += numAvailableBytes;
if (_numAvatarSoundSentBytes == soundByteArray.size()) {
if (_numAvatarSoundSentBytes == (int)audioData->getNumBytes()) {
// we're done with this sound object - so set our pointer back to NULL
// and our sent bytes back to zero
_avatarSound.clear();
@ -876,18 +878,30 @@ void Agent::aboutToFinish() {
DependencyManager::destroy<AudioInjectorManager>();
// destroy all other created dependencies
DependencyManager::destroy<ScriptCache>();
DependencyManager::destroy<ResourceCacheSharedItems>();
DependencyManager::destroy<SoundCacheScriptingInterface>();
DependencyManager::destroy<SoundCache>();
DependencyManager::destroy<AudioScriptingInterface>();
DependencyManager::destroy<RecordingScriptingInterface>();
DependencyManager::destroy<AnimationCacheScriptingInterface>();
DependencyManager::destroy<EntityScriptingInterface>();
DependencyManager::destroy<ResourceScriptingInterface>();
DependencyManager::destroy<UserActivityLoggerScriptingInterface>();
DependencyManager::destroy<ScriptCache>();
DependencyManager::destroy<SoundCache>();
DependencyManager::destroy<AnimationCache>();
DependencyManager::destroy<recording::Deck>();
DependencyManager::destroy<recording::Recorder>();
DependencyManager::destroy<recording::ClipCache>();
DependencyManager::destroy<AvatarHashMap>();
DependencyManager::destroy<AssignmentParentFinder>();
DependencyManager::destroy<MessagesClient>();
DependencyManager::destroy<ResourceManager>();
DependencyManager::destroy<ResourceCacheSharedItems>();
// drop our shared pointer to the script engine, then ask ScriptEngines to shutdown scripting
// this ensures that the ScriptEngine goes down before ScriptEngines
_scriptEngine.clear();

View file

@ -129,17 +129,12 @@ void AssignmentClient::stopAssignmentClient() {
QThread* currentAssignmentThread = _currentAssignment->thread();
// ask the current assignment to stop
BLOCKING_INVOKE_METHOD(_currentAssignment, "stop");
QMetaObject::invokeMethod(_currentAssignment, "stop");
// ask the current assignment to delete itself on its thread
_currentAssignment->deleteLater();
// when this thread is destroyed we don't need to run our assignment complete method
disconnect(currentAssignmentThread, &QThread::destroyed, this, &AssignmentClient::assignmentCompleted);
// wait on the thread from that assignment - it will be gone once the current assignment deletes
currentAssignmentThread->quit();
currentAssignmentThread->wait();
auto PROCESS_EVENTS_INTERVAL_MS = 100;
while (!currentAssignmentThread->wait(PROCESS_EVENTS_INTERVAL_MS)) {
QCoreApplication::processEvents();
}
}
}

View file

@ -89,7 +89,8 @@ AudioMixer::AudioMixer(ReceivedMessage& message) :
PacketType::NodeIgnoreRequest,
PacketType::RadiusIgnoreRequest,
PacketType::RequestsDomainListData,
PacketType::PerAvatarGainSet },
PacketType::PerAvatarGainSet,
PacketType::AudioSoloRequest },
this, "queueAudioPacket");
// packets whose consequences are global should be processed on the main thread
@ -434,7 +435,11 @@ void AudioMixer::start() {
QCoreApplication::processEvents();
}
int numToRetain = nodeList->size() * (1 - _throttlingRatio);
int numToRetain = -1;
assert(_throttlingRatio >= 0.0f && _throttlingRatio <= 1.0f);
if (_throttlingRatio > EPSILON) {
numToRetain = nodeList->size() * (1.0f - _throttlingRatio);
}
nodeList->nestedEach([&](NodeList::const_iterator cbegin, NodeList::const_iterator cend) {
// mix across slave threads
auto mixTimer = _mixTiming.timer();
@ -487,11 +492,8 @@ void AudioMixer::throttle(chrono::microseconds duration, int frame) {
// target different mix and backoff ratios (they also have different backoff rates)
// this is to prevent oscillation, and encourage throttling to find a steady state
const float TARGET = 0.9f;
// on a "regular" machine with 100 avatars, this is the largest value where
// - overthrottling can be recovered
// - oscillations will not occur after the recovery
const float BACKOFF_TARGET = 0.44f;
const float TARGET = _throttleStartTarget;
const float BACKOFF_TARGET = _throttleBackoffTarget;
// the mixer is known to struggle at about 80 on a "regular" machine
// so throttle 2/80 the streams to ensure smooth audio (throttling is linear)
@ -550,6 +552,24 @@ void AudioMixer::parseSettingsObject(const QJsonObject& settingsObject) {
_slavePool.setNumThreads(numThreads);
}
}
const QString THROTTLE_START_KEY = "throttle_start";
const QString THROTTLE_BACKOFF_KEY = "throttle_backoff";
float settingsThrottleStart = audioThreadingGroupObject[THROTTLE_START_KEY].toDouble(_throttleStartTarget);
float settingsThrottleBackoff = audioThreadingGroupObject[THROTTLE_BACKOFF_KEY].toDouble(_throttleBackoffTarget);
if (settingsThrottleBackoff > settingsThrottleStart) {
qCWarning(audio) << "Throttle backoff target cannot be higher than throttle start target. Using default values.";
} else if (settingsThrottleBackoff < 0.0f || settingsThrottleStart > 1.0f) {
qCWarning(audio) << "Throttle start and backoff targets must be greater than or equal to 0.0"
<< "and lesser than or equal to 1.0. Using default values.";
} else {
_throttleStartTarget = settingsThrottleStart;
_throttleBackoffTarget = settingsThrottleBackoff;
}
qCDebug(audio) << "Throttle Start:" << _throttleStartTarget << "Throttle Backoff:" << _throttleBackoffTarget;
}
if (settingsObject.contains(AUDIO_BUFFER_GROUP_KEY)) {

View file

@ -144,11 +144,13 @@ private:
static std::map<QString, CodecPluginPointer> _availableCodecs;
static QStringList _codecPreferenceOrder;
static std::vector<ZoneDescription> _audioZones;
static std::vector<ZoneSettings> _zoneSettings;
static std::vector<ReverbSettings> _zoneReverbSettings;
float _throttleStartTarget = 0.9f;
float _throttleBackoffTarget = 0.44f;
AudioMixerSlave::SharedData _workerSharedData;
};

View file

@ -98,6 +98,9 @@ int AudioMixerClientData::processPackets(ConcurrentAddedStreams& addedStreams) {
case PacketType::RadiusIgnoreRequest:
parseRadiusIgnoreRequest(packet, node);
break;
case PacketType::AudioSoloRequest:
parseSoloRequest(packet, node);
break;
default:
Q_UNREACHABLE();
}
@ -295,6 +298,25 @@ void AudioMixerClientData::parseRadiusIgnoreRequest(QSharedPointer<ReceivedMessa
}
}
void AudioMixerClientData::parseSoloRequest(QSharedPointer<ReceivedMessage> message, const SharedNodePointer& node) {
uint8_t addToSolo;
message->readPrimitive(&addToSolo);
while (message->getBytesLeftToRead()) {
// parse out the UUID being soloed from the packet
QUuid soloedUUID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID));
if (addToSolo) {
_soloedNodes.push_back(soloedUUID);
} else {
auto it = std::remove(std::begin(_soloedNodes), std::end(_soloedNodes), soloedUUID);
_soloedNodes.erase(it, std::end(_soloedNodes));
}
}
}
AvatarAudioStream* AudioMixerClientData::getAvatarAudioStream() {
auto it = std::find_if(_audioStreams.begin(), _audioStreams.end(), [](const SharedStreamPointer& stream){
return stream->getStreamIdentifier().isNull();
@ -315,6 +337,13 @@ void AudioMixerClientData::removeAgentAvatarAudioStream() {
if (it != _audioStreams.end()) {
_audioStreams.erase(it);
// Clear mixing structures so that they get recreated with up to date
// data if the stream comes back
setHasReceivedFirstMix(false);
_streams.skipped.clear();
_streams.inactive.clear();
_streams.active.clear();
}
}

View file

@ -65,6 +65,7 @@ public:
void parsePerAvatarGainSet(ReceivedMessage& message, const SharedNodePointer& node);
void parseNodeIgnoreRequest(QSharedPointer<ReceivedMessage> message, const SharedNodePointer& node);
void parseRadiusIgnoreRequest(QSharedPointer<ReceivedMessage> message, const SharedNodePointer& node);
void parseSoloRequest(QSharedPointer<ReceivedMessage> message, const SharedNodePointer& node);
// attempt to pop a frame from each audio stream, and return the number of streams from this client
int checkBuffersBeforeFrameSend();
@ -150,6 +151,9 @@ public:
const Node::IgnoredNodeIDs& getIgnoringNodeIDs() const { return _ignoringNodeIDs; }
const std::vector<QUuid>& getSoloedNodes() const { return _soloedNodes; }
bool getHasReceivedFirstMix() const { return _hasReceivedFirstMix; }
void setHasReceivedFirstMix(bool hasReceivedFirstMix) { _hasReceivedFirstMix = hasReceivedFirstMix; }
@ -209,6 +213,8 @@ private:
std::atomic_bool _isIgnoreRadiusEnabled { false };
std::vector<QUuid> _soloedNodes;
bool _hasReceivedFirstMix { false };
};

View file

@ -272,6 +272,10 @@ bool shouldBeSkipped(MixableStream& stream, const Node& listener,
return true;
}
if (!listenerData.getSoloedNodes().empty()) {
return !contains(listenerData.getSoloedNodes(), stream.nodeStreamID.nodeID);
}
bool shouldCheckIgnoreBox = (listenerAudioStream.isIgnoreBoxEnabled() ||
stream.positionalStream->isIgnoreBoxEnabled());
if (shouldCheckIgnoreBox &&
@ -310,6 +314,7 @@ bool AudioMixerSlave::prepareMix(const SharedNodePointer& listener) {
memset(_mixSamples, 0, sizeof(_mixSamples));
bool isThrottling = _numToRetain != -1;
bool isSoloing = !listenerData->getSoloedNodes().empty();
auto& streams = listenerData->getStreams();
@ -376,13 +381,14 @@ bool AudioMixerSlave::prepareMix(const SharedNodePointer& listener) {
stream.approximateVolume = approximateVolume(stream, listenerAudioStream);
} else {
if (shouldBeSkipped(stream, *listener, *listenerAudioStream, *listenerData)) {
addStream(stream, *listenerAudioStream, 0.0f);
addStream(stream, *listenerAudioStream, 0.0f, isSoloing);
streams.skipped.push_back(move(stream));
++stats.activeToSkipped;
return true;
}
addStream(stream, *listenerAudioStream, listenerData->getMasterAvatarGain());
addStream(stream, *listenerAudioStream, listenerData->getMasterAvatarGain(),
isSoloing);
if (shouldBeInactive(stream)) {
// To reduce artifacts we still call render to flush the HRTF for every silent
@ -417,7 +423,8 @@ bool AudioMixerSlave::prepareMix(const SharedNodePointer& listener) {
return true;
}
addStream(stream, *listenerAudioStream, listenerData->getMasterAvatarGain());
addStream(stream, *listenerAudioStream, listenerData->getMasterAvatarGain(),
isSoloing);
if (shouldBeInactive(stream)) {
// To reduce artifacts we still call render to flush the HRTF for every silent
@ -484,7 +491,7 @@ bool AudioMixerSlave::prepareMix(const SharedNodePointer& listener) {
void AudioMixerSlave::addStream(AudioMixerClientData::MixableStream& mixableStream,
AvatarAudioStream& listeningNodeStream,
float masterListenerGain) {
float masterListenerGain, bool isSoloing) {
++stats.totalMixes;
auto streamToAdd = mixableStream.positionalStream;
@ -495,9 +502,13 @@ void AudioMixerSlave::addStream(AudioMixerClientData::MixableStream& mixableStre
glm::vec3 relativePosition = streamToAdd->getPosition() - listeningNodeStream.getPosition();
float distance = glm::max(glm::length(relativePosition), EPSILON);
float gain = computeGain(masterListenerGain, listeningNodeStream, *streamToAdd, relativePosition, distance, isEcho);
float azimuth = isEcho ? 0.0f : computeAzimuth(listeningNodeStream, listeningNodeStream, relativePosition);
float gain = 1.0f;
if (!isSoloing) {
gain = computeGain(masterListenerGain, listeningNodeStream, *streamToAdd, relativePosition, distance, isEcho);
}
const int HRTF_DATASET_INDEX = 1;
if (!streamToAdd->lastPopSucceeded()) {

View file

@ -57,7 +57,7 @@ private:
bool prepareMix(const SharedNodePointer& listener);
void addStream(AudioMixerClientData::MixableStream& mixableStream,
AvatarAudioStream& listeningNodeStream,
float masterListenerGain);
float masterListenerGain, bool isSoloing);
void updateHRTFParameters(AudioMixerClientData::MixableStream& mixableStream,
AvatarAudioStream& listeningNodeStream,
float masterListenerGain);

View file

@ -152,6 +152,8 @@ void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message,
if (packetTraitVersion > instanceVersionRef) {
if (traitSize == AvatarTraits::DELETED_TRAIT_SIZE) {
_avatar->processDeletedTraitInstance(traitType, instanceID);
// Mixer doesn't need deleted IDs.
_avatar->getAndClearRecentlyDetachedIDs();
// to track a deleted instance but keep version information
// the avatar mixer uses the negative value of the sent version

View file

@ -416,7 +416,8 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
// NOTE: Here's where we determine if we are over budget and drop remaining avatars,
// or send minimal avatar data in uncommon case of PALIsOpen.
int minimRemainingAvatarBytes = minimumBytesPerAvatar * remainingAvatars;
bool overBudget = (identityBytesSent + numAvatarDataBytes + minimRemainingAvatarBytes) > maxAvatarBytesPerFrame;
auto frameByteEstimate = identityBytesSent + traitBytesSent + numAvatarDataBytes + minimRemainingAvatarBytes;
bool overBudget = frameByteEstimate > maxAvatarBytesPerFrame;
if (overBudget) {
if (PALIsOpen) {
_stats.overBudgetAvatars++;
@ -497,8 +498,11 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
_stats.avatarDataPackingElapsedTime +=
(quint64) chrono::duration_cast<chrono::microseconds>(endAvatarDataPacking - startAvatarDataPacking).count();
// use helper to add any changed traits to our packet list
traitBytesSent += addChangedTraitsToBulkPacket(nodeData, otherNodeData, *traitsPacketList);
if (!overBudget) {
// use helper to add any changed traits to our packet list
traitBytesSent += addChangedTraitsToBulkPacket(nodeData, otherNodeData, *traitsPacketList);
}
remainingAvatars--;
}

View file

@ -21,7 +21,7 @@
#include <GLMHelpers.h>
ScriptableAvatar::ScriptableAvatar() {
_clientTraitsHandler = std::unique_ptr<ClientTraitsHandler>(new ClientTraitsHandler(this));
_clientTraitsHandler.reset(new ClientTraitsHandler(this));
}
QByteArray ScriptableAvatar::toByteArrayStateful(AvatarDataDetail dataDetail, bool dropFaceTracking) {

View file

@ -583,15 +583,29 @@ void EntityScriptServer::handleOctreePacket(QSharedPointer<ReceivedMessage> mess
void EntityScriptServer::aboutToFinish() {
shutdownScriptEngine();
DependencyManager::get<EntityScriptingInterface>()->setEntityTree(nullptr);
DependencyManager::get<ResourceManager>()->cleanup();
DependencyManager::destroy<AudioScriptingInterface>();
DependencyManager::destroy<SoundCacheScriptingInterface>();
DependencyManager::destroy<ResourceScriptingInterface>();
DependencyManager::destroy<EntityScriptingInterface>();
DependencyManager::destroy<SoundCache>();
DependencyManager::destroy<ScriptCache>();
DependencyManager::destroy<ResourceManager>();
DependencyManager::destroy<ResourceCacheSharedItems>();
DependencyManager::destroy<MessagesClient>();
DependencyManager::destroy<AssignmentDynamicFactory>();
DependencyManager::destroy<AssignmentParentFinder>();
DependencyManager::destroy<AvatarHashMap>();
DependencyManager::get<ResourceManager>()->cleanup();
DependencyManager::destroy<PluginManager>();
DependencyManager::destroy<ResourceScriptingInterface>();
DependencyManager::destroy<EntityScriptingInterface>();
// cleanup the AudioInjectorManager (and any still running injectors)
DependencyManager::destroy<AudioInjectorManager>();

View file

@ -16,9 +16,9 @@ if (HIFI_MEMORY_DEBUGGING)
if (UNIX)
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
# for clang on Linux
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-omit-frame-pointer -fsanitize=undefined -fsanitize=address -fsanitize-recover=address")
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=undefined -fsanitize=address -fsanitize-recover=address")
SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=undefined -fsanitize=address -fsanitize-recover=address")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-omit-frame-pointer -shared-libasan -fsanitize=undefined -fsanitize=address -fsanitize-recover=address")
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -shared-libasan -fsanitize=undefined -fsanitize=address -fsanitize-recover=address")
SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -shared-libasan -fsanitize=undefined -fsanitize=address -fsanitize-recover=address")
else ()
# for gcc on Linux
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined -fsanitize=address -U_FORTIFY_SOURCE -fno-stack-protector -fno-omit-frame-pointer")

View file

@ -1012,6 +1012,24 @@
"placeholder": "1",
"default": "1",
"advanced": true
},
{
"name": "throttle_start",
"type": "double",
"label": "Throttle Start Target",
"help": "Target percentage of frame time to start throttling",
"placeholder": "0.9",
"default": 0.9,
"advanced": true
},
{
"name": "throttle_backoff",
"type": "double",
"label": "Throttle Backoff Target",
"help": "Target percentage of frame time to backoff throttling",
"placeholder": "0.44",
"default": 0.44,
"advanced": true
}
]
},

View file

@ -10,10 +10,85 @@ $(document).ready(function(){
function progressBarHTML(extraClass, label) {
var html = "<div class='progress'>";
html += "<div class='" + extraClass + " progress-bar progress-bar-success progress-bar-striped active' role='progressbar' aria-valuemin='0' aria-valuemax='100'>";
html += label + "<span class='sr-only'></span></div></div>";
html += "<span class='ongoing-msg'></span></div></div>";
return html;
}
function showUploadProgress(title) {
swal({
title: title,
text: progressBarHTML('upload-content-progress', 'Upload'),
html: true,
showConfirmButton: false,
allowEscapeKey: false
});
}
function uploadNextChunk(file, offset, id) {
if (offset == undefined) {
offset = 0;
}
if (id == undefined) {
// Identify this upload session
id = Math.round(Math.random() * 2147483647);
}
var fileSize = file.size;
var filename = file.name;
var CHUNK_SIZE = 1048576; // 1 MiB
var isFinal = Boolean(fileSize - offset <= CHUNK_SIZE);
var nextChunkSize = Math.min(fileSize - offset, CHUNK_SIZE);
var chunk = file.slice(offset, offset + nextChunkSize, file.type);
var chunkFormData = new FormData();
var formItemName = 'restore-file-chunk';
if (offset == 0) {
formItemName = isFinal ? 'restore-file-chunk-only' : 'restore-file-chunk-initial';
} else if (isFinal) {
formItemName = 'restore-file-chunk-final';
}
chunkFormData.append(formItemName, chunk, filename);
var ajaxParams = {
url: '/content/upload',
type: 'POST',
timeout: 30000, // 30 s
headers: {"X-Session-Id": id},
cache: false,
processData: false,
contentType: false,
data: chunkFormData
};
var ajaxObject = $.ajax(ajaxParams);
ajaxObject.fail(function (jqXHR, textStatus, errorThrown) {
showErrorMessage(
"Error",
"There was a problem restoring domain content.\n"
+ "Please ensure that the content archive or entity file is valid and try again."
);
});
updateProgressBars($('.upload-content-progress'), (offset + nextChunkSize) * 100 / fileSize);
if (!isFinal) {
ajaxObject.done(function (data, textStatus, jqXHR)
{ uploadNextChunk(file, offset + CHUNK_SIZE, id); });
} else {
ajaxObject.done(function(data, textStatus, jqXHR) {
isRestoring = true;
// immediately reload backup information since one should be restoring now
reloadBackupInformation();
swal.close();
});
}
}
function setupBackupUpload() {
// construct the HTML needed for the settings backup panel
var html = "<div class='form-group'><div id='" + UPLOAD_CONTENT_ALLOWED_DIV_ID + "'>";
@ -50,34 +125,10 @@ $(document).ready(function(){
"Restore content",
function() {
var files = $('#' + RESTORE_SETTINGS_FILE_ID).prop('files');
var file = files[0];
var fileFormData = new FormData();
fileFormData.append('restore-file', files[0]);
showSpinnerAlert("Uploading content to restore");
$.ajax({
url: '/content/upload',
type: 'POST',
timeout: 3600000, // Set timeout to 1h
cache: false,
processData: false,
contentType: false,
data: fileFormData
}).done(function(data, textStatus, jqXHR) {
isRestoring = true;
// immediately reload backup information since one should be restoring now
reloadBackupInformation();
swal.close();
}).fail(function(jqXHR, textStatus, errorThrown) {
showErrorMessage(
"Error",
"There was a problem restoring domain content.\n"
+ "Please ensure that the content archive or entity file is valid and try again."
);
});
showUploadProgress("Uploading " + file.name);
uploadNextChunk(file);
}
);
});
@ -168,6 +219,11 @@ $(document).ready(function(){
checkBackupStatus();
});
function updateProgressBars($progressBar, value) {
$progressBar.attr('aria-valuenow', value).attr('style', 'width: ' + value + '%');
$progressBar.find('.ongoing-msg').html(" " + Math.round(value) + "%");
}
function reloadBackupInformation() {
// make a GET request to get backup information to populate the table
$.ajax({
@ -204,11 +260,6 @@ $(document).ready(function(){
+ "<li><a class='" + BACKUP_DELETE_LINK_CLASS + "' href='#' target='_blank'>Delete</a></li></ul></div></td>";
}
function updateProgressBars($progressBar, value) {
$progressBar.attr('aria-valuenow', value).attr('style', 'width: ' + value + '%');
$progressBar.find('.sr-only').html(value + "% Complete");
}
// before we add any new rows and update existing ones
// remove our flag for active rows
$('.' + ACTIVE_BACKUP_ROW_CLASS).removeClass(ACTIVE_BACKUP_ROW_CLASS);

View file

@ -348,6 +348,27 @@ void DomainContentBackupManager::recoverFromUploadedBackup(MiniPromise::Promise
});
}
void DomainContentBackupManager::recoverFromUploadedFile(MiniPromise::Promise promise, QString uploadedFilename) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "recoverFromUploadedFile", Q_ARG(MiniPromise::Promise, promise),
Q_ARG(QString, uploadedFilename));
return;
}
qDebug() << "Recovering from uploaded file -" << uploadedFilename;
QFile uploadedFile(uploadedFilename);
QuaZip uploadedZip { &uploadedFile };
QString backupName = MANUAL_BACKUP_PREFIX + "uploaded.zip";
bool success = recoverFromBackupZip(backupName, uploadedZip);
promise->resolve({
{ "success", success }
});
}
std::vector<BackupItemInfo> DomainContentBackupManager::getAllBackups() {
QDir backupDir { _backupDirectory };

View file

@ -86,6 +86,7 @@ public slots:
void createManualBackup(MiniPromise::Promise promise, const QString& name);
void recoverFromBackup(MiniPromise::Promise promise, const QString& backupName);
void recoverFromUploadedBackup(MiniPromise::Promise promise, QByteArray uploadedBackup);
void recoverFromUploadedFile(MiniPromise::Promise promise, QString uploadedFilename);
void deleteBackup(MiniPromise::Promise promise, const QString& backupName);
signals:

View file

@ -2258,46 +2258,18 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
// check the file extension to see what kind of file this is
// to make sure we handle this filetype for a content restore
auto dispositionValue = QString(firstFormData.first.value("Content-Disposition"));
auto formDataFilenameRegex = QRegExp("filename=\"(.+)\"");
auto matchIndex = formDataFilenameRegex.indexIn(dispositionValue);
QRegExp formDataFieldsRegex(R":(name="(restore-file.*)".*filename="(.+)"):");
auto matchIndex = formDataFieldsRegex.indexIn(dispositionValue);
QString formItemName = "";
QString uploadedFilename = "";
if (matchIndex != -1) {
uploadedFilename = formDataFilenameRegex.cap(1);
}
if (uploadedFilename.endsWith(".json", Qt::CaseInsensitive)
|| uploadedFilename.endsWith(".json.gz", Qt::CaseInsensitive)) {
// invoke our method to hand the new octree file off to the octree server
QMetaObject::invokeMethod(this, "handleOctreeFileReplacement",
Qt::QueuedConnection, Q_ARG(QByteArray, firstFormData.second));
// respond with a 200 for success
connection->respond(HTTPConnection::StatusCode200);
} else if (uploadedFilename.endsWith(".zip", Qt::CaseInsensitive)) {
auto deferred = makePromise("recoverFromUploadedBackup");
deferred->then([connectionPtr, JSON_MIME_TYPE](QString error, QVariantMap result) {
if (!connectionPtr) {
return;
}
QJsonObject rootJSON;
auto success = result["success"].toBool();
rootJSON["success"] = success;
QJsonDocument docJSON(rootJSON);
connectionPtr->respond(success ? HTTPConnection::StatusCode200 : HTTPConnection::StatusCode400, docJSON.toJson(),
JSON_MIME_TYPE.toUtf8());
});
_contentManager->recoverFromUploadedBackup(deferred, firstFormData.second);
return true;
} else {
// we don't have handling for this filetype, send back a 400 for failure
connection->respond(HTTPConnection::StatusCode400);
formItemName = formDataFieldsRegex.cap(1);
uploadedFilename = formDataFieldsRegex.cap(2);
}
// Received a chunk
processPendingContent(connection, formItemName, uploadedFilename, firstFormData.second);
} else {
// respond with a 400 for failure
connection->respond(HTTPConnection::StatusCode400);
@ -2546,6 +2518,72 @@ bool DomainServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl &u
}
}
bool DomainServer::processPendingContent(HTTPConnection* connection, QString itemName, QString filename, QByteArray dataChunk) {
static const QString UPLOAD_SESSION_KEY { "X-Session-Id" };
QByteArray sessionIdBytes = connection->requestHeader(UPLOAD_SESSION_KEY);
int sessionId = sessionIdBytes.toInt();
bool newUpload = itemName == "restore-file" || itemName == "restore-file-chunk-initial" || itemName == "restore-file-chunk-only";
if (filename.endsWith(".zip", Qt::CaseInsensitive)) {
static const QString TEMPORARY_CONTENT_FILEPATH { QDir::tempPath() + "/hifiUploadContent_XXXXXX.zip" };
if (_pendingContentFiles.find(sessionId) == _pendingContentFiles.end()) {
if (!newUpload) {
return false;
}
std::unique_ptr<QTemporaryFile> newTemp(new QTemporaryFile(TEMPORARY_CONTENT_FILEPATH));
_pendingContentFiles[sessionId] = std::move(newTemp);
} else if (newUpload) {
qCDebug(domain_server) << "New upload received using existing session ID";
_pendingContentFiles[sessionId]->resize(0);
}
QTemporaryFile& _pendingFileContent = *_pendingContentFiles[sessionId];
if (!_pendingFileContent.open()) {
_pendingContentFiles.erase(sessionId);
connection->respond(HTTPConnection::StatusCode400);
return false;
}
_pendingFileContent.seek(_pendingFileContent.size());
_pendingFileContent.write(dataChunk);
_pendingFileContent.close();
// Respond immediately - will timeout if we wait for restore.
connection->respond(HTTPConnection::StatusCode200);
if (itemName == "restore-file" || itemName == "restore-file-chunk-final" || itemName == "restore-file-chunk-only") {
auto deferred = makePromise("recoverFromUploadedBackup");
deferred->then([this, sessionId](QString error, QVariantMap result) {
_pendingContentFiles.erase(sessionId);
});
_contentManager->recoverFromUploadedFile(deferred, _pendingFileContent.fileName());
}
} else if (filename.endsWith(".json", Qt::CaseInsensitive)
|| filename.endsWith(".json.gz", Qt::CaseInsensitive)) {
if (_pendingUploadedContents.find(sessionId) == _pendingUploadedContents.end() && !newUpload) {
qCDebug(domain_server) << "Json upload with invalid session ID received";
return false;
}
QByteArray& _pendingUploadedContent = _pendingUploadedContents[sessionId];
_pendingUploadedContent += dataChunk;
connection->respond(HTTPConnection::StatusCode200);
if (itemName == "restore-file" || itemName == "restore-file-chunk-final" || itemName == "restore-file-chunk-only") {
// invoke our method to hand the new octree file off to the octree server
QMetaObject::invokeMethod(this, "handleOctreeFileReplacement",
Qt::QueuedConnection, Q_ARG(QByteArray, _pendingUploadedContent));
_pendingUploadedContents.erase(sessionId);
}
} else {
connection->respond(HTTPConnection::StatusCode400);
return false;
}
return true;
}
HTTPSConnection* DomainServer::connectionFromReplyWithState(QNetworkReply* reply) {
// grab the UUID state property from the reply
QUuid stateUUID = reply->property(STATE_QUERY_KEY.toLocal8Bit()).toUuid();
@ -3411,20 +3449,11 @@ void DomainServer::maybeHandleReplacementEntityFile() {
}
void DomainServer::handleOctreeFileReplacement(QByteArray octreeFile) {
//Assume we have compressed data
auto compressedOctree = octreeFile;
QByteArray jsonOctree;
bool wasCompressed = gunzip(compressedOctree, jsonOctree);
if (!wasCompressed) {
// the source was not compressed, assume we were sent regular JSON data
jsonOctree = compressedOctree;
}
OctreeUtils::RawEntityData data;
if (data.readOctreeDataInfoFromData(jsonOctree)) {
if (data.readOctreeDataInfoFromData(octreeFile)) {
data.resetIdAndVersion();
QByteArray compressedOctree;
gzip(data.toByteArray(), compressedOctree);
// write the compressed octree data to a special file

View file

@ -20,6 +20,7 @@
#include <QtCore/QStringList>
#include <QtCore/QThread>
#include <QtCore/QUrl>
#include <QHostAddress>
#include <QAbstractNativeEventFilter>
#include <Assignment.h>
@ -209,6 +210,8 @@ private:
HTTPSConnection* connectionFromReplyWithState(QNetworkReply* reply);
bool processPendingContent(HTTPConnection* connection, QString itemName, QString filename, QByteArray dataChunk);
bool forwardMetaverseAPIRequest(HTTPConnection* connection,
const QString& metaversePath,
const QString& requestSubobject,
@ -281,6 +284,9 @@ private:
QHash<QUuid, QPointer<HTTPSConnection>> _pendingOAuthConnections;
std::unordered_map<int, QByteArray> _pendingUploadedContents;
std::unordered_map<int, std::unique_ptr<QTemporaryFile>> _pendingContentFiles;
QThread _assetClientThread;
};

View file

@ -206,7 +206,7 @@ endif()
link_hifi_libraries(
shared workload task octree ktx gpu gl procedural graphics graphics-scripting render
pointers
recording fbx networking model-networking entities avatars trackers
recording hfm fbx networking model-networking entities avatars trackers
audio audio-client animation script-engine physics
render-utils entities-renderer avatars-renderer ui qml auto-updater midi
controllers plugins image trackers

View file

@ -701,9 +701,9 @@
"y": 0.04787999764084816
},
"position": {
"x": -0.53203323516845703,
"x": -0.59333323516845703,
"y": 0.019300000742077827,
"z": -0.07286686894893646
"z": 0.037454843521118164
},
"modelURL": "meshes/keyboard/SM_key.fbx",
"texture": {
@ -752,7 +752,7 @@
"y": 0.04787999764084816
},
"position": {
"x": -0.59333323516845703,
"x": -0.65333323516845703,
"y": 0.019300000742077827,
"z": 0.037454843521118164
},
@ -777,9 +777,9 @@
"y": 0.04787999764084816
},
"position": {
"x": -0.5103323516845703,
"y": 0.019300000742077827,
"z": -0.127054843521118164
"x": -0.5503323516845703,
"y": 0.019300000742077827,
"z": -0.07282185554504395
},
"modelURL": "meshes/keyboard/SM_enter.fbx",
"texture": {
@ -1479,9 +1479,9 @@
"y": 0.04787999764084816
},
"position": {
"x": -0.53203323516845703,
"x": -0.59333323516845703,
"y": 0.019300000742077827,
"z": -0.07286686894893646
"z": 0.037454843521118164
},
"modelURL": "meshes/keyboard/SM_key.fbx",
"texture": {
@ -1530,7 +1530,7 @@
"y": 0.04787999764084816
},
"position": {
"x": -0.59333323516845703,
"x": -0.65333323516845703,
"y": 0.019300000742077827,
"z": 0.037454843521118164
},
@ -1555,9 +1555,9 @@
"y": 0.04787999764084816
},
"position": {
"x": -0.5103323516845703,
"y": 0.019300000742077827,
"z": -0.127054843521118164
"x": -0.5503323516845703,
"y": 0.019300000742077827,
"z": -0.07282185554504395
},
"modelURL": "meshes/keyboard/SM_enter.fbx",
"texture": {
@ -2305,9 +2305,9 @@
"y": 0.04787999764084816
},
"position": {
"x": -0.53203323516845703,
"x": -0.59333323516845703,
"y": 0.019300000742077827,
"z": -0.07286686894893646
"z": 0.037454843521118164
},
"modelURL": "meshes/keyboard/SM_key.fbx",
"texture": {
@ -2356,7 +2356,7 @@
"y": 0.04787999764084816
},
"position": {
"x": -0.59333323516845703,
"x": -0.65333323516845703,
"y": 0.019300000742077827,
"z": 0.037454843521118164
},
@ -2381,9 +2381,9 @@
"y": 0.04787999764084816
},
"position": {
"x": -0.5103323516845703,
"y": 0.019300000742077827,
"z": -0.127054843521118164
"x": -0.5503323516845703,
"y": 0.019300000742077827,
"z": -0.07282185554504395
},
"modelURL": "meshes/keyboard/SM_enter.fbx",
"texture": {

View file

@ -18,6 +18,11 @@
window.isKeyboardRaised = false;
window.isNumericKeyboard = false;
window.isPasswordField = false;
window.lastActiveInputElement = null;
function getActiveElement() {
return document.activeElement;
}
function shouldSetPasswordField() {
var nodeType = document.activeElement.type;
@ -65,10 +70,15 @@
var keyboardRaised = shouldRaiseKeyboard();
var numericKeyboard = shouldSetNumeric();
var passwordField = shouldSetPasswordField();
var activeInputElement = null;
// Only set the active input element when there is an input element focussed, otherwise it will scroll on body focus as well.
if (keyboardRaised) {
activeInputElement = getActiveElement();
}
if (isWindowFocused &&
(keyboardRaised !== window.isKeyboardRaised || numericKeyboard !== window.isNumericKeyboard
|| passwordField !== window.isPasswordField)) {
|| passwordField !== window.isPasswordField || activeInputElement !== window.lastActiveInputElement)) {
if (typeof EventBridge !== "undefined" && EventBridge !== null) {
EventBridge.emitWebEvent(
@ -90,6 +100,7 @@
window.isKeyboardRaised = keyboardRaised;
window.isNumericKeyboard = numericKeyboard;
window.isPasswordField = passwordField;
window.lastActiveInputElement = activeInputElement;
}
}, POLL_FREQUENCY);

View file

@ -66,7 +66,7 @@
<script>
var handControllerImageURL = null;
var index = 0;
var count = 5;
var count = 3;
function showKbm() {
document.getElementById("main_image").setAttribute("src", "img/tablet-help-keyboard.jpg");
@ -94,24 +94,14 @@
switch (index)
{
case 0:
handControllerImageURL = "img/tablet-help-oculus.jpg";
showHandControllers();
break;
case 1:
handControllerImageURL = "img/tablet-help-vive.jpg";
showHandControllers();
break;
case 2:
handControllerImageURL = "img/tablet-help-windowsMR.jpg";
showHandControllers();
break;
case 3:
showGamepad();
break;
case 4:
case 1:
showKbm();
break;
case 2:
showHandControllers();
break;
default:
}
}
@ -144,34 +134,33 @@
}
switch (params.handControllerName) {
case "oculus":
handControllerImageURL = "img/tablet-help-oculus.jpg";
index = 0;
break;
case "windowsMR":
handControllerImageURL = "img/tablet-help-windowsMR.jpg";
index = 2;
break;
case "vive":
default:
handControllerImageURL = "img/tablet-help-vive.jpg";
index = 1;
break;
case "oculus":
handControllerImageURL = "img/tablet-help-oculus.jpg";
break;
default:
handControllerImageURL = "";
count = 2;
}
switch (params.defaultTab) {
case "gamepad":
showGamepad();
index = 3;
break;
case "handControllers":
showHandControllers();
index = 2;
break;
case "gamepad":
showGamepad();
index = 0;
break;
case "kbm":
default:
showKbm();
index = 4;
index = 1;
}
}
</script>

View file

@ -1,4 +1,20 @@
<svg width="22" height="26" fill="none" version="1.1" viewBox="0 0 22 26" xmlns="http://www.w3.org/2000/svg">
<path d="M1 7L11 1L21 7M1 7L11 13M1 7V19L11 25M11 13L21 7M11 13V25M21 7V19L11 25" stroke="#000" stroke-linejoin="round" stroke-width="2"/>
<circle class="st1" cx="19.407" cy="2.5881" r="2.5846" fill="#ef3b4e" stroke-width=".24043"/>
</svg>
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, 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 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
<style type="text/css">
.st0{fill:#EF3B4E;}
</style>
<g>
<circle cx="27.8" cy="13.3" r="2.4"/>
<circle cx="19.7" cy="7.2" r="2.3"/>
<circle cx="9.4" cy="6.9" r="2.2"/>
<path d="M41.8,17.5l-8.9-5.2c0.1,0.3,0.1,0.7,0.1,1c0,1-0.3,1.8-0.8,2.6l5.1,2.9L25,26l-12.3-7.1l3.1-1.8c-0.4-0.7-0.7-1.6-0.7-2.5
c0-0.4,0.1-0.8,0.2-1.2l-7.1,4.1c-0.5,0.3-0.9,0.9-0.9,1.5v16.5c0,0.6,0.3,1.2,0.9,1.5l16,9.2c0.3,0.2,0.6,0.2,0.9,0.2
s0.6-0.1,0.9-0.2l16-9.2c0.5-0.3,0.9-0.9,0.9-1.5V19C42.7,18.4,42.3,17.8,41.8,17.5z M10.7,21.7L23.3,29v12.8l-12.5-7.2V21.7z
M39.2,34.5l-12.5,7.2V28.9l12.5-7.2V34.5z"/>
<circle cx="25" cy="20.3" r="2.8"/>
<circle cx="20" cy="14.6" r="2.4"/>
</g>
<circle class="st0" cx="44.1" cy="6" r="5.6"/>
</svg>

Before

Width:  |  Height:  |  Size: 356 B

After

Width:  |  Height:  |  Size: 1 KiB

View file

@ -1,3 +1,16 @@
<svg width="22" height="26" viewBox="0 0 22 26" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1 7L11 1L21 7M1 7L11 13M1 7V19L11 25M11 13L21 7M11 13V25M21 7V19L11 25" stroke="black" stroke-width="2" stroke-linejoin="round"/>
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, 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 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
<g>
<circle cx="27.8" cy="13.3" r="2.4"/>
<circle cx="19.7" cy="7.2" r="2.3"/>
<circle cx="9.4" cy="6.9" r="2.2"/>
<path d="M41.8,17.5l-8.9-5.2c0.1,0.3,0.1,0.7,0.1,1c0,1-0.3,1.8-0.8,2.6l5.1,2.9L25,26l-12.3-7.1l3.1-1.8c-0.4-0.7-0.7-1.6-0.7-2.5
c0-0.4,0.1-0.8,0.2-1.2l-7.1,4.1c-0.5,0.3-0.9,0.9-0.9,1.5v16.5c0,0.6,0.3,1.2,0.9,1.5l16,9.2c0.3,0.2,0.6,0.2,0.9,0.2
s0.6-0.1,0.9-0.2l16-9.2c0.5-0.3,0.9-0.9,0.9-1.5V19C42.7,18.4,42.3,17.8,41.8,17.5z M10.7,21.7L23.3,29v12.8l-12.5-7.2V21.7z
M39.2,34.5l-12.5,7.2V28.9l12.5-7.2V34.5z"/>
<circle cx="25" cy="20.3" r="2.8"/>
<circle cx="20" cy="14.6" r="2.4"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 243 B

After

Width:  |  Height:  |  Size: 966 B

View file

@ -1,4 +1,21 @@
<svg width="22" height="26" fill="none" version="1.1" viewBox="0 0 22 26" xmlns="http://www.w3.org/2000/svg">
<path d="M1 7L11 1L21 7M1 7L11 13M1 7V19L11 25M11 13L21 7M11 13V25M21 7V19L11 25" stroke="#fff" stroke-linejoin="round" stroke-width="2"/>
<circle class="st1" cx="19.41" cy="2.5828" r="2.5846" fill="#ef3b4e" stroke-width=".24043"/>
</svg>
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, 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 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
.st1{fill:#EF3B4E;}
</style>
<g>
<circle class="st0" cx="27.8" cy="13.3" r="2.4"/>
<circle class="st0" cx="19.7" cy="7.2" r="2.3"/>
<circle class="st0" cx="9.4" cy="6.9" r="2.2"/>
<path class="st0" d="M41.8,17.5l-8.9-5.2c0.1,0.3,0.1,0.7,0.1,1c0,1-0.3,1.8-0.8,2.6l5.1,2.9L25,26l-12.3-7.1l3.1-1.8
c-0.4-0.7-0.7-1.6-0.7-2.5c0-0.4,0.1-0.8,0.2-1.2l-7.1,4.1c-0.5,0.3-0.9,0.9-0.9,1.5v16.5c0,0.6,0.3,1.2,0.9,1.5l16,9.2
c0.3,0.2,0.6,0.2,0.9,0.2s0.6-0.1,0.9-0.2l16-9.2c0.5-0.3,0.9-0.9,0.9-1.5V19C42.7,18.4,42.3,17.8,41.8,17.5z M10.7,21.7L23.3,29
v12.8l-12.5-7.2V21.7z M39.2,34.5l-12.5,7.2V28.9l12.5-7.2V34.5z"/>
<circle class="st0" cx="25" cy="20.3" r="2.8"/>
<circle class="st0" cx="20" cy="14.6" r="2.4"/>
</g>
<circle class="st1" cx="44.1" cy="6" r="5.6"/>
</svg>

Before

Width:  |  Height:  |  Size: 355 B

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -1,3 +1,19 @@
<svg width="22" height="26" viewBox="0 0 22 26" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1 7L11 1L21 7M1 7L11 13M1 7V19L11 25M11 13L21 7M11 13V25M21 7V19L11 25" stroke="white" stroke-width="2" stroke-linejoin="round"/>
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, 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 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g>
<circle class="st0" cx="27.8" cy="13.3" r="2.4"/>
<circle class="st0" cx="19.7" cy="7.2" r="2.3"/>
<circle class="st0" cx="9.4" cy="6.9" r="2.2"/>
<path class="st0" d="M41.8,17.5l-8.9-5.2c0.1,0.3,0.1,0.7,0.1,1c0,1-0.3,1.8-0.8,2.6l5.1,2.9L25,26l-12.3-7.1l3.1-1.8
c-0.4-0.7-0.7-1.6-0.7-2.5c0-0.4,0.1-0.8,0.2-1.2l-7.1,4.1c-0.5,0.3-0.9,0.9-0.9,1.5v16.5c0,0.6,0.3,1.2,0.9,1.5l16,9.2
c0.3,0.2,0.6,0.2,0.9,0.2s0.6-0.1,0.9-0.2l16-9.2c0.5-0.3,0.9-0.9,0.9-1.5V19C42.7,18.4,42.3,17.8,41.8,17.5z M10.7,21.7L23.3,29
v12.8l-12.5-7.2V21.7z M39.2,34.5l-12.5,7.2V28.9l12.5-7.2V34.5z"/>
<circle class="st0" cx="25" cy="20.3" r="2.8"/>
<circle class="st0" cx="20" cy="14.6" r="2.4"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 243 B

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -50,6 +50,9 @@ Item {
StatText {
text: root.positionText
}
StatText {
text: root.recenterText
}
StatText {
text: "Anim Vars:--------------------------------------------------------------------------------"
}
@ -92,6 +95,9 @@ Item {
StatText {
text: root.rotationText
}
StatText {
text: root.sittingText
}
StatText {
text: "State Machines:---------------------------------------------------------------------------"
}
@ -122,6 +128,9 @@ Item {
StatText {
text: root.velocityText
}
StatText {
text: root.walkingText
}
StatText {
text: "Alpha Values:--------------------------------------------------------------------------"
}

View file

@ -114,7 +114,6 @@ ScrollingWindow {
sourceSize: Qt.size(width, height);
verticalAlignment: Image.AlignVCenter;
horizontalAlignment: Image.AlignHCenter
onSourceChanged: console.log("Icon url: " + source)
}
}
@ -249,10 +248,6 @@ ScrollingWindow {
root.loadingChanged(loadRequest.status);
}
onIconChanged: {
console.log("New icon: " + icon)
}
onWindowCloseRequested: {
root.destroy();
}

View file

@ -23,6 +23,8 @@ Item {
width: root.pane.width
property bool failAfterSignUp: false
onWidthChanged: d.resize();
function login() {
flavorText.visible = false
mainTextContainer.visible = false
@ -127,7 +129,7 @@ Item {
Column {
id: form
width: parent.width
onHeightChanged: d.resize(); onWidthChanged: d.resize();
onHeightChanged: d.resize();
anchors {
top: mainTextContainer.bottom

View file

@ -26,6 +26,8 @@ Item {
property bool interactive: false
property bool blurOnCtrlShift: true
StylesUIt.HifiConstants {
id: hifi
}
@ -34,10 +36,34 @@ Item {
webViewCore.stop();
}
Timer {
id: delayedUnfocuser
repeat: false
interval: 200
onTriggered: {
// The idea behind this is to delay unfocusing, so that fast lower/raise will not result actual unfocusing.
// Fast lower/raise happens every time keyboard is being re-raised (see the code below in OffscreenQmlSurface::setKeyboardRaised)
//
// if (raised) {
// item->setProperty("keyboardRaised", QVariant(!raised));
// }
//
// item->setProperty("keyboardRaised", QVariant(raised));
//
webViewCore.runJavaScript("if (document.activeElement) document.activeElement.blur();", function(result) {
console.log('unfocus completed: ', result);
});
}
}
function unfocus() {
webViewCore.runJavaScript("if (document.activeElement) document.activeElement.blur();", function(result) {
console.log('unfocus completed: ', result);
});
delayedUnfocuser.start();
}
function stopUnfocus() {
delayedUnfocuser.stop();
}
function onLoadingChanged(loadRequest) {
@ -156,8 +182,8 @@ Item {
}
Keys.onPressed: {
if ((event.modifiers & Qt.ShiftModifier) && (event.modifiers & Qt.ControlModifier)) {
webViewCore.focus = false;
if (blurOnCtrlShift && (event.modifiers & Qt.ShiftModifier) && (event.modifiers & Qt.ControlModifier)) {
webViewCore.focus = false;
}
}
}

View file

@ -13,6 +13,8 @@ Item {
onKeyboardRaisedChanged: {
if(!keyboardRaised) {
webroot.unfocus();
} else {
webroot.stopUnfocus();
}
}
property bool punctuationMode: false

View file

@ -17,6 +17,8 @@ Item {
onKeyboardRaisedChanged: {
if(!keyboardRaised) {
webroot.unfocus();
} else {
webroot.stopUnfocus();
}
}
property bool punctuationMode: false

View file

@ -15,11 +15,14 @@ Item {
onKeyboardRaisedChanged: {
if(!keyboardRaised) {
webroot.unfocus();
} else {
webroot.stopUnfocus();
}
}
property bool punctuationMode: false
property bool passwordField: false
property alias flickable: webroot.interactive
property alias blurOnCtrlShift: webroot.blurOnCtrlShift
function stop() {
webroot.stop();

View file

@ -44,14 +44,14 @@ Rectangle {
onPasswordChanged: {
var use3DKeyboard = (typeof MenuInterface === "undefined") ? false : MenuInterface.isOptionChecked("Use 3D Keyboard");
var use3DKeyboard = (typeof KeyboardScriptingInterface === "undefined") ? false : KeyboardScriptingInterface.use3DKeyboard;
if (use3DKeyboard) {
KeyboardScriptingInterface.password = password;
}
}
onRaisedChanged: {
var use3DKeyboard = (typeof MenuInterface === "undefined") ? false : MenuInterface.isOptionChecked("Use 3D Keyboard");
var use3DKeyboard = (typeof KeyboardScriptingInterface === "undefined") ? false : KeyboardScriptingInterface.use3DKeyboard;
if (!use3DKeyboard) {
keyboardBase.height = raised ? raisedHeight : 0;
keyboardBase.visible = raised;

View file

@ -83,8 +83,10 @@ SpinBox {
}
validator: DoubleValidator {
bottom: Math.min(spinBox.from, spinBox.to)
top: Math.max(spinBox.from, spinBox.to)
decimals: spinBox.decimals
bottom: Math.min(spinBox.realFrom, spinBox.realTo)
top: Math.max(spinBox.realFrom, spinBox.realTo)
notation: DoubleValidator.StandardNotation
}
textFromValue: function(value, locale) {
@ -97,20 +99,37 @@ SpinBox {
contentItem: TextInput {
id: spinboxText
z: 2
color: isLightColorScheme
? (spinBox.activeFocus ? hifi.colors.black : hifi.colors.lightGray)
: (spinBox.activeFocus ? hifi.colors.white : hifi.colors.lightGrayText)
selectedTextColor: hifi.colors.black
selectionColor: hifi.colors.primaryHighlight
text: spinBox.textFromValue(spinBox.value, spinBox.locale) + suffix
text: spinBox.textFromValue(spinBox.value, spinBox.locale)
inputMethodHints: spinBox.inputMethodHints
validator: spinBox.validator
verticalAlignment: Qt.AlignVCenter
leftPadding: spinBoxLabelInside.visible ? 30 : hifi.dimensions.textPadding
//rightPadding: hifi.dimensions.spinnerSize
width: spinBox.width - hifi.dimensions.spinnerSize
onEditingFinished: spinBox.editingFinished()
Text {
id: suffixText
x: metrics.advanceWidth(spinboxText.text + '*')
height: spinboxText.height
FontMetrics {
id: metrics
font: spinboxText.font
}
color: isLightColorScheme
? (spinBox.activeFocus ? hifi.colors.black : hifi.colors.lightGray)
: (spinBox.activeFocus ? hifi.colors.white : hifi.colors.lightGrayText)
text: suffix
verticalAlignment: Qt.AlignVCenter
}
}
up.indicator: Item {

View file

@ -80,8 +80,6 @@ ModalWindow {
property int clickedButton: OriginalDialogs.StandardButton.NoButton;
Component.onCompleted: {
console.log("Helper " + helper + " drives " + drives);
fileDialogItem.keyboardEnabled = HMD.active;
// HACK: The following lines force the model to initialize properly such that the go-up button
@ -809,7 +807,6 @@ ModalWindow {
}
}
console.log("Selecting " + selection)
selectedFile(selection);
root.destroy();
}

View file

@ -765,8 +765,7 @@ TabletModalWindow {
return;
}
}
console.log("Selecting " + selection)
selectedFile(selection);
root.destroy();
}

View file

@ -141,6 +141,7 @@ TabletModalWindow {
Component.onDestruction: {
loginKeyboard.raised = false;
KeyboardScriptingInterface.raised = false;
}
Component.onCompleted: {

View file

@ -22,8 +22,6 @@ Preference {
Component.onCompleted: {
dataTextField.text = preference.value;
console.log("MyAvatar modelName " + MyAvatar.getFullAvatarModelName())
console.log("Application : " + ApplicationInterface)
ApplicationInterface.fullAvatarURLChanged.connect(processNewAvatar);
}

View file

@ -70,7 +70,6 @@ Preference {
dir: fileDialogHelper.pathToUrl(preference.value)
});
browser.selectedFile.connect(function(fileUrl){
console.log(fileUrl);
dataTextField.text = fileDialogHelper.urlToPath(fileUrl);
});
}

View file

@ -62,7 +62,7 @@ Rectangle {
}
}
catch(err) {
console.error(err);
//console.error(err);
}
}
}

View file

@ -133,7 +133,7 @@ Item {
}
onStatusChanged: {
if (status == Image.Error) {
console.log("source: " + source + ": failed to load " + hifiUrl);
console.log("source: " + source + ": failed to load");
source = defaultThumbnail;
}
}
@ -236,25 +236,39 @@ Item {
property var hoverThunk: function () { };
property var unhoverThunk: function () { };
Rectangle {
anchors.fill: parent;
anchors.fill: parent
visible: root.hovered
color: "transparent";
border.width: 4; border.color: hifiStyleConstants.colors.primaryHighlight;
z: 1;
color: "transparent"
border.width: 4
border.color: hifiStyleConstants.colors.primaryHighlight
z: 1
}
MouseArea {
anchors.fill: parent;
acceptedButtons: Qt.LeftButton;
anchors.fill: parent
acceptedButtons: Qt.LeftButton
hoverEnabled: true
onContainsMouseChanged: {
// Use onContainsMouseChanged rather than onEntered and onExited because the latter aren't always
// triggered correctly - e.g., if drag rightwards from right hand side of a card to the next card
// onExited doesn't fire, in which case can end up with two cards highlighted.
if (containsMouse) {
Tablet.playSound(TabletEnums.ButtonHover);
hoverThunk();
} else {
unhoverThunk();
}
}
}
MouseArea {
// Separate MouseArea for click handling so that it doesn't interfere with hovering and interaction
// with containing ListView.
anchors.fill: parent
acceptedButtons: Qt.LeftButton
hoverEnabled: false
onClicked: {
Tablet.playSound(TabletEnums.ButtonClick);
goFunction("hifi://" + hifiUrl);
}
hoverEnabled: true;
onEntered: {
Tablet.playSound(TabletEnums.ButtonHover);
hoverThunk();
}
onExited: unhoverThunk();
}
StateImage {
id: actionIcon;

View file

@ -70,8 +70,8 @@ OriginalDesktop.Desktop {
anchors.horizontalCenter: settings.constrainToolbarToCenterX ? desktop.horizontalCenter : undefined;
// Literal 50 is overwritten by settings from previous session, and sysToolbar.x comes from settings when not constrained.
x: sysToolbar.x
buttonModel: tablet.buttons;
shown: tablet.toolbarMode;
buttonModel: tablet ? tablet.buttons : null;
shown: tablet ? tablet.toolbarMode : false;
}
Settings {
@ -103,19 +103,14 @@ OriginalDesktop.Desktop {
property bool autoAdd: false
function initWebviewProfileHandlers(profile) {
console.log("The webview url in desktop is: " + currentUrl);
downloadUrl = currentUrl;
if (webViewProfileSetup) return;
webViewProfileSetup = true;
profile.downloadRequested.connect(function(download){
console.log("Download start: " + download.state);
adaptedPath = File.convertUrlToPath(downloadUrl);
tempDir = File.getTempDir();
console.log("Temp dir created: " + tempDir);
download.path = tempDir + "/" + adaptedPath;
console.log("Path where object should download: " + download.path);
console.log("Auto add: " + autoAdd);
download.accept();
if (download.state === WebEngineDownloadItem.DownloadInterrupted) {
console.log("download failed to complete");

View file

@ -141,6 +141,7 @@ Column {
textSizeSmall: root.textSizeSmall;
stackShadowNarrowing: root.stackShadowNarrowing;
shadowHeight: root.stackedCardShadowHeight;
hoverThunk: function () {
hovered = true;
if(root.autoScrollTimerEnabled) {

View file

@ -1287,7 +1287,7 @@ Rectangle {
connectionsOnlineDot.visible = message.shouldShowDot;
break;
default:
console.log('Unrecognized message:', JSON.stringify(message));
console.log('Pal.qml: Unrecognized message');
}
}
function sortModel() {

View file

@ -20,7 +20,6 @@ Item {
// Public function for initiating an http request.
// REQUIRES parent to be root to have sendToScript!
function request(options, callback) {
console.debug('HttpRequest', JSON.stringify(options));
httpCalls[httpCounter] = callback;
var message = {method: 'http.request', params: options, id: httpCounter++, jsonrpc: "2.0"};
parent.sendToScript(message);
@ -33,7 +32,6 @@ Item {
return;
}
delete httpCalls[message.id];
console.debug('HttpRequest response', JSON.stringify(message));
callback(message.error, message.response);
}
}

View file

@ -362,7 +362,7 @@ Rectangle {
spectatorCameraPreview.visible = message.setting;
break;
default:
console.log('Unrecognized message from spectatorCamera.js:', JSON.stringify(message));
console.log('Unrecognized message from spectatorCamera.js');
}
}
signal sendToScript(var message);

View file

@ -261,7 +261,6 @@ Rectangle {
anchors.right: parent.right
onLinkActivated: {
popup.showSpecifyWearableUrl(function(url) {
console.debug('popup.showSpecifyWearableUrl: ', url);
addWearable(root.avatarName, url);
modified = true;
});

View file

@ -29,9 +29,6 @@ Item {
when: avatarUrl !== ''
value: avatarUrl
}
onSourceChanged: {
console.debug('avatarImage: source = ', source);
}
visible: avatarImage.status !== Image.Loading && avatarImage.status !== Image.Error
}

View file

@ -96,9 +96,6 @@ Rectangle {
AvatarThumbnail {
id: avatarThumbnail
avatarUrl: avatarImageUrl
onAvatarUrlChanged: {
console.debug('CreateFavoritesDialog: onAvatarUrlChanged: ', avatarUrl);
}
wearablesCount: avatarWearablesCount
}

View file

@ -17,9 +17,6 @@ Rectangle {
property alias dialogButtons: buttons
property string imageSource: null
onImageSourceChanged: {
console.debug('imageSource = ', imageSource)
}
property string button1color: hifi.buttons.noneBorderlessGray;
property string button1text: ''

View file

@ -19,6 +19,7 @@ import controlsUit 1.0 as HifiControlsUit
import "../../../controls" as HifiControls
import "../wallet" as HifiWallet
import "../common" as HifiCommerceCommon
import "../.." as HifiCommon
// references XXX from root context
@ -31,6 +32,7 @@ Rectangle {
property bool ownershipStatusReceived: false;
property bool balanceReceived: false;
property bool availableUpdatesReceived: false;
property bool itemInfoReceived: false;
property string baseItemName: "";
property string itemName;
property string itemId;
@ -181,11 +183,14 @@ Rectangle {
onItemIdChanged: {
root.ownershipStatusReceived = false;
root.itemInfoReceived = false;
Commerce.alreadyOwned(root.itemId);
root.availableUpdatesReceived = false;
root.currentUpdatesPage = 1;
Commerce.getAvailableUpdates(root.itemId);
itemPreviewImage.source = "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/previews/" + itemId + "/thumbnail/hifi-mp-" + itemId + ".jpg";
var MARKETPLACE_API_URL = Account.metaverseServerURL + "/api/v1/marketplace/items/";
http.request({uri: MARKETPLACE_API_URL + root.itemId}, updateCheckoutQMLFromHTTP);
}
onItemTypeChanged: {
@ -279,6 +284,7 @@ Rectangle {
ownershipStatusReceived = false;
balanceReceived = false;
availableUpdatesReceived = false;
itemInfoReceived = false;
Commerce.getWalletStatus();
}
}
@ -355,7 +361,7 @@ Rectangle {
Rectangle {
id: loading;
z: 997;
visible: !root.ownershipStatusReceived || !root.balanceReceived || !root.availableUpdatesReceived;
visible: !root.ownershipStatusReceived || !root.balanceReceived || !root.availableUpdatesReceived || !root.itemInfoReceived;
anchors.fill: parent;
color: hifi.colors.white;
@ -1063,10 +1069,33 @@ Rectangle {
}
}
}
HifiCommon.RootHttpRequest {
id: http;
}
//
// FUNCTION DEFINITIONS START
//
function updateCheckoutQMLFromHTTP(error, result) {
if (error || (result.status !== 'success')) {
// The QML will display a loading spinner forever if the user is stuck here.
console.log("Error in Checkout.qml when getting marketplace item info!");
return;
}
root.itemInfoReceived = true;
root.itemName = result.data.title;
root.itemPrice = result.data.cost;
root.itemHref = Account.metaverseServerURL + result.data.path;
root.itemAuthor = result.data.creator;
root.itemType = result.data.item_type || "unknown";
itemPreviewImage.source = result.data.thumbnail_url;
refreshBuyUI();
}
//
// Function Name: fromScript()
//
@ -1080,21 +1109,27 @@ Rectangle {
// Description:
// Called when a message is received from a script.
//
function fromScript(message) {
switch (message.method) {
case 'updateCheckoutQML':
root.itemId = message.params.itemId;
root.itemName = message.params.itemName.trim();
root.itemPrice = message.params.itemPrice;
root.itemHref = message.params.itemHref;
root.referrer = message.params.referrer;
root.itemAuthor = message.params.itemAuthor;
case 'updateCheckoutQMLItemID':
if (!message.params.itemId) {
console.log("A message with method 'updateCheckoutQMLItemID' was sent without an itemId!");
return;
}
// If we end up following the referrer (i.e. in case the wallet "isn't set up" or the user cancels),
// we want the user to be placed back on the individual item's page - thus we set the
// default of the referrer in this case to "itemPage".
root.referrer = message.params.referrer || "itemPage";
root.itemEdition = message.params.itemEdition || -1;
root.itemType = message.params.itemType || "unknown";
refreshBuyUI();
root.itemId = message.params.itemId;
break;
case 'http.response':
http.handleHttpResponse(message);
break;
default:
console.log('Unrecognized message from marketplaces.js:', JSON.stringify(message));
console.log('Checkout.qml: Unrecognized message from marketplaces.js');
}
}
signal sendToScript(var message);

View file

@ -25,14 +25,15 @@ Item {
id: root;
property bool isDisplayingNearby; // as opposed to 'connections'
// true when sending to 'nearby' or when a script raises the send asset dialog
property bool multiLineDisplay;
property string displayName;
property string userName;
property string profilePic;
property string textColor: hifi.colors.white;
Item {
visible: root.isDisplayingNearby;
visible: root.multiLineDisplay;
anchors.fill: parent;
RalewaySemiBold {
@ -71,7 +72,7 @@ Item {
}
Item {
visible: !root.isDisplayingNearby;
visible: !root.multiLineDisplay;
anchors.fill: parent;
Image {

View file

@ -39,7 +39,7 @@ Item {
property string sendingPubliclyEffectImage;
property var http;
property var listModelName;
property var keyboardContainer: nil;
property var keyboardContainer;
// This object is always used in a popup or full-screen Wallet section.
// This MouseArea is used to prevent a user from being
@ -56,7 +56,7 @@ Item {
// Background
Rectangle {
z: 1;
visible: root.assetName !== "" && sendAssetStep.visible;
visible: root.assetCertID !== "" && sendAssetStep.referrer !== "payIn" && sendAssetStep.visible;
anchors.top: parent.top;
anchors.topMargin: root.parentAppTitleBarHeight;
anchors.left: parent.left;
@ -84,7 +84,6 @@ Item {
if (sendPubliclyCheckbox.checked && sendAssetStep.referrer === "nearby") {
sendSignalToParent({
method: 'sendAsset_sendPublicly',
assetName: root.assetName,
recipient: sendAssetStep.selectedRecipientNodeID,
amount: parseInt(amountTextField.text),
effectImage: root.sendingPubliclyEffectImage
@ -108,6 +107,14 @@ Item {
root.nextActiveView = 'paymentFailure';
}
}
onCertificateInfoResult: {
if (result.status !== 'success') {
console.log("Failed to get certificate info", result.data.message);
} else {
root.assetName = result.data.marketplace_item_name;
}
}
}
Connections {
@ -155,7 +162,7 @@ Item {
Item {
id: userInfoContainer;
visible: root.assetName === "";
visible: root.assetCertID === "";
anchors.top: parent.top;
anchors.left: parent.left;
anchors.right: parent.right;
@ -251,7 +258,7 @@ Item {
LinearGradient {
anchors.fill: parent;
visible: root.assetName === "";
visible: root.assetCertID === "";
start: Qt.point(0, 0);
end: Qt.point(0, height);
gradient: Gradient {
@ -262,7 +269,7 @@ Item {
RalewaySemiBold {
id: sendAssetText;
text: root.assetName === "" ? "Send Money To:" : "Gift \"" + root.assetName + "\" To:";
text: root.assetCertID === "" ? "Send Money To:" : "Gift \"" + root.assetName + "\" To:";
// Anchors
anchors.top: parent.top;
anchors.topMargin: 26;
@ -405,7 +412,7 @@ Item {
HifiModels.PSFListModel {
id: connectionsModel;
http: root.http;
listModelName: root.listModelName;
listModelName: root.listModelName || "";
endpoint: "/api/v1/users?filter=connections";
itemsPerPage: 9;
listView: connectionsList;
@ -441,7 +448,7 @@ Item {
HiFiGlyphs {
id: closeGlyphButton_connections;
text: hifi.glyphs.close;
color: root.assetName === "" ? hifi.colors.lightGrayText : hifi.colors.baseGray;
color: root.assetCertID === "" ? hifi.colors.lightGrayText : hifi.colors.baseGray;
size: 26;
anchors.top: parent.top;
anchors.topMargin: 10;
@ -684,7 +691,7 @@ Item {
HiFiGlyphs {
id: closeGlyphButton_nearby;
text: hifi.glyphs.close;
color: root.assetName === "" ? hifi.colors.lightGrayText : hifi.colors.baseGray;
color: root.assetCertID === "" ? hifi.colors.lightGrayText : hifi.colors.baseGray;
size: 26;
anchors.top: parent.top;
anchors.topMargin: 10;
@ -760,7 +767,7 @@ Item {
RalewaySemiBold {
id: sendToText;
text: root.assetName === "" ? "Send to:" : "Gift to:";
text: root.assetCertID === "" ? "Send to:" : "Gift to:";
// Anchors
anchors.top: parent.top;
anchors.topMargin: 36;
@ -853,7 +860,7 @@ Item {
id: sendAssetStep;
z: 996;
property string referrer; // either "connections" or "nearby"
property string referrer; // either "connections", "nearby", or "payIn"
property string selectedRecipientNodeID;
property string selectedRecipientDisplayName;
property string selectedRecipientUserName;
@ -865,7 +872,8 @@ Item {
RalewaySemiBold {
id: sendAssetText_sendAssetStep;
text: root.assetName === "" ? "Send Money" : "Gift \"" + root.assetName + "\"";
text: sendAssetStep.referrer === "payIn" && root.assetCertID !== "" ? "Send \"" + root.assetName + "\":" :
(root.assetCertID === "" ? "Send Money To:" : "Gift \"" + root.assetName + "\" To:");
// Anchors
anchors.top: parent.top;
anchors.topMargin: 26;
@ -878,7 +886,7 @@ Item {
// Text size
size: 22;
// Style
color: root.assetName === "" ? hifi.colors.white : hifi.colors.black;
color: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ? hifi.colors.white : hifi.colors.black;
}
Item {
@ -893,7 +901,7 @@ Item {
RalewaySemiBold {
id: sendToText_sendAssetStep;
text: root.assetName === "" ? "Send to:" : "Gift to:";
text: (root.assetCertID === "" || sendAssetStep.referrer === "payIn") ? "Send to:" : "Gift to:";
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
@ -902,7 +910,7 @@ Item {
// Text size
size: 18;
// Style
color: root.assetName === "" ? hifi.colors.white : hifi.colors.black;
color: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ? hifi.colors.white : hifi.colors.black;
verticalAlignment: Text.AlignVCenter;
}
@ -912,25 +920,26 @@ Item {
anchors.right: changeButton.left;
anchors.rightMargin: 12;
height: parent.height;
textColor: root.assetName === "" ? hifi.colors.white : hifi.colors.black;
textColor: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ? hifi.colors.white : hifi.colors.black;
displayName: sendAssetStep.selectedRecipientDisplayName;
userName: sendAssetStep.selectedRecipientUserName;
profilePic: sendAssetStep.selectedRecipientProfilePic !== "" ? ((0 === sendAssetStep.selectedRecipientProfilePic.indexOf("http")) ?
sendAssetStep.selectedRecipientProfilePic : (Account.metaverseServerURL + sendAssetStep.selectedRecipientProfilePic)) : "";
isDisplayingNearby: sendAssetStep.referrer === "nearby";
multiLineDisplay: sendAssetStep.referrer === "nearby" || sendAssetStep.referrer === "payIn";
}
// "CHANGE" button
HifiControlsUit.Button {
id: changeButton;
color: root.assetName === "" ? hifi.buttons.none : hifi.buttons.white;
color: root.assetCertID === "" ? hifi.buttons.none : hifi.buttons.white;
colorScheme: hifi.colorSchemes.dark;
anchors.right: parent.right;
anchors.verticalCenter: parent.verticalCenter;
height: 35;
width: 100;
text: "CHANGE";
visible: sendAssetStep.referrer !== "payIn";
onClicked: {
if (sendAssetStep.referrer === "connections") {
root.nextActiveView = "chooseRecipientConnection";
@ -944,7 +953,7 @@ Item {
Item {
id: amountContainer;
visible: root.assetName === "";
visible: root.assetCertID === "";
anchors.top: sendToContainer.bottom;
anchors.topMargin: 2;
anchors.left: parent.left;
@ -970,8 +979,9 @@ Item {
HifiControlsUit.TextField {
id: amountTextField;
text: root.assetName === "" ? "" : "1";
colorScheme: root.assetName === "" ? hifi.colorSchemes.dark : hifi.colorSchemes.light;
readOnly: sendAssetStep.referrer === "payIn";
text: root.assetCertID === "" ? "" : "1";
colorScheme: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ? hifi.colorSchemes.dark : hifi.colorSchemes.light;
inputMethodHints: Qt.ImhDigitsOnly;
// Anchors
anchors.verticalCenter: parent.verticalCenter;
@ -980,8 +990,8 @@ Item {
height: 50;
// Style
leftPermanentGlyph: hifi.glyphs.hfc;
activeFocusOnPress: true;
activeFocusOnTab: true;
activeFocusOnPress: !amountTextField.readOnly;
activeFocusOnTab: !amountTextField.readOnly;
validator: IntValidator { bottom: 0; }
@ -1071,6 +1081,7 @@ Item {
TextArea {
id: optionalMessage;
readOnly: sendAssetStep.referrer === "payIn";
property int maximumLength: 72;
property string previousText: text;
placeholderText: "<i>Optional Public Message (" + maximumLength + " character limit)</i>";
@ -1081,12 +1092,13 @@ Item {
// Style
background: Rectangle {
anchors.fill: parent;
color: root.assetName === "" ? (optionalMessage.activeFocus ? hifi.colors.black : hifi.colors.baseGrayShadow) :
color: (root.assetCertID === "" || sendAssetStep.referrer === "payIn") ?
(optionalMessage.activeFocus && !optionalMessage.readOnly ? hifi.colors.black : hifi.colors.baseGrayShadow) :
(optionalMessage.activeFocus ? "#EFEFEF" : "#EEEEEE");
border.width: optionalMessage.activeFocus ? 1 : 0;
border.color: optionalMessage.activeFocus ? hifi.colors.primaryHighlight : hifi.colors.textFieldLightBackground;
border.width: optionalMessage.activeFocus && !optionalMessage.readOnly ? 1 : 0;
border.color: optionalMessage.activeFocus && !optionalMessage.readOnly ? hifi.colors.primaryHighlight : hifi.colors.textFieldLightBackground;
}
color: root.assetName === "" ? hifi.colors.white : hifi.colors.black;
color: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ? hifi.colors.white : hifi.colors.black;
textFormat: TextEdit.PlainText;
wrapMode: TextEdit.Wrap;
activeFocusOnPress: true;
@ -1122,7 +1134,8 @@ Item {
// Text size
size: 16;
// Style
color: optionalMessage.text.length === optionalMessage.maximumLength ? "#ea89a5" : (root.assetName === "" ? hifi.colors.lightGrayText : hifi.colors.baseGrayHighlight);
color: optionalMessage.text.length === optionalMessage.maximumLength ? "#ea89a5" :
(root.assetCertID === "" || sendAssetStep.referrer === "payIn" ? hifi.colors.lightGrayText : hifi.colors.baseGrayHighlight);
verticalAlignment: Text.AlignTop;
horizontalAlignment: Text.AlignRight;
}
@ -1167,7 +1180,7 @@ Item {
parent.color = hifi.colors.blueAccent;
}
onClicked: {
lightboxPopup.titleText = (root.assetName === "" ? "Send Effect" : "Gift Effect");
lightboxPopup.titleText = (root.assetCertID === "" ? "Send Effect" : "Gift Effect");
lightboxPopup.bodyImageSource = "sendAsset/images/send-money-effect-sm.jpg"; // Path relative to CommerceLightbox.qml
lightboxPopup.bodyText = "Enabling this option will create a particle effect between you and " +
"your recipient that is visible to everyone nearby.";
@ -1196,7 +1209,7 @@ Item {
// "CANCEL" button
HifiControlsUit.Button {
id: cancelButton_sendAssetStep;
color: root.assetName === "" ? hifi.buttons.noneBorderlessWhite : hifi.buttons.noneBorderlessGray;
color: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ? hifi.buttons.noneBorderlessWhite : hifi.buttons.noneBorderlessGray;
colorScheme: hifi.colorSchemes.dark;
anchors.right: sendButton.left;
anchors.rightMargin: 24;
@ -1205,8 +1218,12 @@ Item {
width: 100;
text: "CANCEL";
onClicked: {
resetSendAssetData();
root.nextActiveView = "sendAssetHome";
if (sendAssetStep.referrer === "payIn") {
sendToScript({method: "closeSendAsset"});
} else {
resetSendAssetData();
root.nextActiveView = "sendAssetHome";
}
}
}
@ -1214,7 +1231,7 @@ Item {
HifiControlsUit.Button {
id: sendButton;
color: hifi.buttons.blue;
colorScheme: root.assetName === "" ? hifi.colorSchemes.dark : hifi.colorSchemes.light;
colorScheme: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ? hifi.colorSchemes.dark : hifi.colorSchemes.light;
anchors.right: parent.right;
anchors.rightMargin: 0;
anchors.verticalCenter: parent.verticalCenter;
@ -1222,11 +1239,11 @@ Item {
width: 100;
text: "SUBMIT";
onClicked: {
if (root.assetName === "" && parseInt(amountTextField.text) > parseInt(balanceText.text)) {
if (root.assetCertID === "" && parseInt(amountTextField.text) > parseInt(balanceText.text)) {
amountTextField.focus = true;
amountTextField.error = true;
amountTextFieldError.text = "<i>amount exceeds available funds</i>";
} else if (root.assetName === "" && (amountTextField.text === "" || parseInt(amountTextField.text) < 1)) {
} else if (root.assetCertID === "" && (amountTextField.text === "" || parseInt(amountTextField.text) < 1)) {
amountTextField.focus = true;
amountTextField.error = true;
amountTextFieldError.text = "<i>invalid amount</i>";
@ -1236,7 +1253,7 @@ Item {
root.isCurrentlySendingAsset = true;
amountTextField.focus = false;
optionalMessage.focus = false;
if (sendAssetStep.referrer === "connections") {
if (sendAssetStep.referrer === "connections" || sendAssetStep.referrer === "payIn") {
Commerce.transferAssetToUsername(sendAssetStep.selectedRecipientUserName,
root.assetCertID,
parseInt(amountTextField.text),
@ -1317,18 +1334,18 @@ Item {
Rectangle {
anchors.top: parent.top;
anchors.topMargin: root.assetName === "" ? 15 : 125;
anchors.topMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ? 15 : 125;
anchors.left: parent.left;
anchors.leftMargin: root.assetName === "" ? 15 : 50;
anchors.leftMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ? 15 : 50;
anchors.right: parent.right;
anchors.rightMargin: root.assetName === "" ? 15 : 50;
anchors.rightMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ? 15 : 50;
anchors.bottom: parent.bottom;
anchors.bottomMargin: root.assetName === "" ? 15 : 125;
anchors.bottomMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ? 15 : 125;
color: "#FFFFFF";
RalewaySemiBold {
id: paymentSentText;
text: root.assetName === "" ? "Payment Sent" : "Gift Sent";
text: root.assetCertID === "" ? "Payment Sent" : (sendAssetStep.referrer === "payIn" ? "Item Sent" : "Gift Sent");
// Anchors
anchors.top: parent.top;
anchors.topMargin: 26;
@ -1346,7 +1363,7 @@ Item {
HiFiGlyphs {
id: closeGlyphButton_paymentSuccess;
visible: root.assetName === "";
visible: root.assetCertID === "" && sendAssetStep.referrer !== "payIn";
text: hifi.glyphs.close;
color: hifi.colors.lightGrayText;
size: 26;
@ -1364,10 +1381,14 @@ Item {
parent.text = hifi.glyphs.close;
}
onClicked: {
root.nextActiveView = "sendAssetHome";
resetSendAssetData();
if (root.assetName !== "") {
sendSignalToParent({method: "closeSendAsset"});
if (sendAssetStep.referrer === "payIn") {
sendToScript({method: "closeSendAsset"});
} else {
root.nextActiveView = "sendAssetHome";
resetSendAssetData();
if (root.assetName !== "") {
sendSignalToParent({method: "closeSendAsset"});
}
}
}
}
@ -1409,14 +1430,14 @@ Item {
userName: sendAssetStep.selectedRecipientUserName;
profilePic: sendAssetStep.selectedRecipientProfilePic !== "" ? ((0 === sendAssetStep.selectedRecipientProfilePic.indexOf("http")) ?
sendAssetStep.selectedRecipientProfilePic : (Account.metaverseServerURL + sendAssetStep.selectedRecipientProfilePic)) : "";
isDisplayingNearby: sendAssetStep.referrer === "nearby";
multiLineDisplay: sendAssetStep.referrer === "nearby" || sendAssetStep.referrer === "payIn";
}
}
Item {
id: giftContainer_paymentSuccess;
visible: root.assetName !== "";
visible: root.assetCertID !== "";
anchors.top: sendToContainer_paymentSuccess.bottom;
anchors.topMargin: 8;
anchors.left: parent.left;
@ -1427,7 +1448,7 @@ Item {
RalewaySemiBold {
id: gift_paymentSuccess;
text: "Gift:";
text: sendAssetStep.referrer === "payIn" ? "Item:" : "Gift:";
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
@ -1458,7 +1479,7 @@ Item {
Item {
id: amountContainer_paymentSuccess;
visible: root.assetName === "";
visible: root.assetCertID === "";
anchors.top: sendToContainer_paymentSuccess.bottom;
anchors.topMargin: 16;
anchors.left: parent.left;
@ -1513,7 +1534,7 @@ Item {
RalewaySemiBold {
id: optionalMessage_paymentSuccess;
visible: root.assetName === "";
visible: root.assetCertID === "";
text: optionalMessage.text;
// Anchors
anchors.top: amountContainer_paymentSuccess.visible ? amountContainer_paymentSuccess.bottom : sendToContainer_paymentSuccess.bottom;
@ -1535,18 +1556,22 @@ Item {
HifiControlsUit.Button {
id: closeButton;
color: hifi.buttons.blue;
colorScheme: root.assetName === "" ? hifi.colorSchemes.dark : hifi.colorSchemes.light;
colorScheme: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ? hifi.colorSchemes.dark : hifi.colorSchemes.light;
anchors.horizontalCenter: parent.horizontalCenter;
anchors.bottom: parent.bottom;
anchors.bottomMargin: root.assetName === "" ? 80 : 30;
anchors.bottomMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ? 80 : 30;
height: 50;
width: 120;
text: "Close";
onClicked: {
root.nextActiveView = "sendAssetHome";
resetSendAssetData();
if (root.assetName !== "") {
sendSignalToParent({method: "closeSendAsset"});
if (sendAssetStep.referrer === "payIn") {
sendToScript({method: "closeSendAsset"});
} else {
root.nextActiveView = "sendAssetHome";
resetSendAssetData();
if (root.assetName !== "") {
sendSignalToParent({method: "closeSendAsset"});
}
}
}
}
@ -1574,18 +1599,18 @@ Item {
Rectangle {
anchors.top: parent.top;
anchors.topMargin: root.assetName === "" ? 15 : 150;
anchors.topMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ? 15 : 150;
anchors.left: parent.left;
anchors.leftMargin: root.assetName === "" ? 15 : 50;
anchors.leftMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ? 15 : 50;
anchors.right: parent.right;
anchors.rightMargin: root.assetName === "" ? 15 : 50;
anchors.rightMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ? 15 : 50;
anchors.bottom: parent.bottom;
anchors.bottomMargin: root.assetName === "" ? 15 : 300;
anchors.bottomMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ? 15 : 300;
color: "#FFFFFF";
RalewaySemiBold {
id: paymentFailureText;
text: root.assetName === "" ? "Payment Failed" : "Failed";
text: root.assetCertID === "" && sendAssetStep.referrer !== "payIn" ? "Payment Failed" : "Failed";
// Anchors
anchors.top: parent.top;
anchors.topMargin: 26;
@ -1603,7 +1628,7 @@ Item {
HiFiGlyphs {
id: closeGlyphButton_paymentFailure;
visible: root.assetName === "";
visible: root.assetCertID === "" && sendAssetStep.referrer !== "payIn";
text: hifi.glyphs.close;
color: hifi.colors.lightGrayText;
size: 26;
@ -1632,7 +1657,8 @@ Item {
RalewaySemiBold {
id: paymentFailureDetailText;
text: "The recipient you specified was unable to receive your " + (root.assetName === "" ? "payment." : "gift.");
text: "The recipient you specified was unable to receive your " +
(root.assetCertID === "" ? "payment." : (sendAssetStep.referrer === "payIn" ? "item." : "gift."));
anchors.top: paymentFailureText.bottom;
anchors.topMargin: 20;
anchors.left: parent.left;
@ -1650,7 +1676,7 @@ Item {
Item {
id: sendToContainer_paymentFailure;
visible: root.assetName === "";
visible: root.assetCertID === "" || sendAssetStep.referrer === "payIn";
anchors.top: paymentFailureDetailText.bottom;
anchors.topMargin: 8;
anchors.left: parent.left;
@ -1685,13 +1711,13 @@ Item {
userName: sendAssetStep.selectedRecipientUserName;
profilePic: sendAssetStep.selectedRecipientProfilePic !== "" ? ((0 === sendAssetStep.selectedRecipientProfilePic.indexOf("http")) ?
sendAssetStep.selectedRecipientProfilePic : (Account.metaverseServerURL + sendAssetStep.selectedRecipientProfilePic)) : "";
isDisplayingNearby: sendAssetStep.referrer === "nearby";
multiLineDisplay: sendAssetStep.referrer === "nearby" || sendAssetStep.referrer === "payIn";
}
}
Item {
id: amountContainer_paymentFailure;
visible: root.assetName === "";
visible: root.assetCertID === "";
anchors.top: sendToContainer_paymentFailure.bottom;
anchors.topMargin: 16;
anchors.left: parent.left;
@ -1746,7 +1772,7 @@ Item {
RalewaySemiBold {
id: optionalMessage_paymentFailure;
visible: root.assetName === "";
visible: root.assetCertID === "" || sendAssetStep.referrer === "payIn";
text: optionalMessage.text;
// Anchors
anchors.top: amountContainer_paymentFailure.visible ? amountContainer_paymentFailure.bottom : sendToContainer_paymentFailure.bottom;
@ -1768,19 +1794,23 @@ Item {
HifiControlsUit.Button {
id: closeButton_paymentFailure;
color: hifi.buttons.noneBorderless;
colorScheme: root.assetName === "" ? hifi.colorSchemes.dark : hifi.colorSchemes.light;
colorScheme: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ? hifi.colorSchemes.dark : hifi.colorSchemes.light;
anchors.right: retryButton_paymentFailure.left;
anchors.rightMargin: 12;
anchors.bottom: parent.bottom;
anchors.bottomMargin: root.assetName === "" ? 80 : 30;
anchors.bottomMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ? 80 : 30;
height: 50;
width: 120;
text: "Cancel";
onClicked: {
root.nextActiveView = "sendAssetHome";
resetSendAssetData();
if (root.assetName !== "") {
sendSignalToParent({method: "closeSendAsset"});
if (sendAssetStep.referrer === "payIn") {
sendToScript({method: "closeSendAsset"});
} else {
root.nextActiveView = "sendAssetHome";
resetSendAssetData();
if (root.assetName !== "") {
sendSignalToParent({method: "closeSendAsset"});
}
}
}
}
@ -1789,17 +1819,17 @@ Item {
HifiControlsUit.Button {
id: retryButton_paymentFailure;
color: hifi.buttons.blue;
colorScheme: root.assetName === "" ? hifi.colorSchemes.dark : hifi.colorSchemes.light;
colorScheme: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ? hifi.colorSchemes.dark : hifi.colorSchemes.light;
anchors.right: parent.right;
anchors.rightMargin: 12;
anchors.bottom: parent.bottom;
anchors.bottomMargin: root.assetName === "" ? 80 : 30;
anchors.bottomMargin: root.assetCertID === "" || sendAssetStep.referrer === "payIn" ? 80 : 30;
height: 50;
width: 120;
text: "Retry";
onClicked: {
root.isCurrentlySendingAsset = true;
if (sendAssetStep.referrer === "connections") {
if (sendAssetStep.referrer === "connections" || sendAssetStep.referrer === "payIn") {
Commerce.transferAssetToUsername(sendAssetStep.selectedRecipientUserName,
root.assetCertID,
parseInt(amountTextField.text),
@ -1866,11 +1896,32 @@ Item {
case 'updateSelectedRecipientUsername':
sendAssetStep.selectedRecipientUserName = message.userName;
break;
case 'updateSendAssetQML':
root.assetName = "";
root.assetCertID = message.assetCertID || "";
if (root.assetCertID === "") {
amountTextField.text = message.amount || 1;
} else {
amountTextField.text = "";
Commerce.certificateInfo(root.assetCertID);
}
sendAssetStep.referrer = "payIn";
sendAssetStep.selectedRecipientNodeID = "";
sendAssetStep.selectedRecipientDisplayName = "Determined by script:";
sendAssetStep.selectedRecipientUserName = message.username;
optionalMessage.text = message.message || "No Message Provided";
root.nextActiveView = "sendAssetStep";
break;
case 'inspectionCertificate_resetCert':
// NOP
break;
default:
console.log('SendAsset: Unrecognized message from wallet.js:', JSON.stringify(message));
console.log('SendAsset: Unrecognized message from wallet.js');
}
}
signal sendSignalToParent(var msg);
signal sendToScript(var message);
//
// FUNCTION DEFINITIONS END
//

View file

@ -597,7 +597,7 @@ Rectangle {
resetCert(true);
break;
default:
console.log('Unrecognized message from marketplaces.js:', JSON.stringify(message));
console.log('InspectionCertificate.qml: Unrecognized message from marketplaces.js');
}
}
signal sendToScript(var message);

View file

@ -1070,7 +1070,7 @@ Rectangle {
http.handleHttpResponse(message);
break;
default:
console.log('Unrecognized message from marketplaces.js:', JSON.stringify(message));
console.log('Purchases.qml: Unrecognized message from marketplaces.js');
}
}
signal sendToScript(var message);

View file

@ -250,7 +250,7 @@ At the moment, there is currently no way to convert HFC to other currencies. Sta
function fromScript(message) {
switch (message.method) {
default:
console.log('Unrecognized message from wallet.js:', JSON.stringify(message));
console.log('Help.qml: Unrecognized message from wallet.js');
}
}
signal sendSignalToWallet(var msg);

View file

@ -183,7 +183,7 @@ Item {
function fromScript(message) {
switch (message.method) {
default:
console.log('Unrecognized message from wallet.js:', JSON.stringify(message));
console.log('NeedsLogIn.qml: Unrecognized message from wallet.js');
}
}
signal sendSignalToWallet(var msg);

View file

@ -536,8 +536,8 @@ Rectangle {
Rectangle {
id: exchangeMoneyMessagesWaitingLight;
visible: parent.messagesWaiting;
anchors.right: exchangeMoneyTabIcon.left;
anchors.rightMargin: 9;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.top: exchangeMoneyTabIcon.top;
anchors.topMargin: 4;
height: 10;
@ -803,7 +803,7 @@ Rectangle {
}
break;
default:
console.log('Unrecognized message from wallet.js:', JSON.stringify(message));
console.log('Wallet.qml: Unrecognized message from wallet.js');
}
}
signal sendToScript(var message);

View file

@ -130,17 +130,14 @@ ScrollingWindow {
}
function loadScript(script) {
console.log("Load script " + script);
scripts.loadOneScript(script);
}
function reloadScript(script) {
console.log("Reload script " + script);
scripts.stopScript(script, true);
}
function stopScript(script) {
console.log("Stop script " + script);
scripts.stopScript(script);
}

View file

@ -120,17 +120,14 @@ Rectangle {
}
function loadScript(script) {
console.log("Load script " + script);
scripts.loadOneScript(script);
}
function reloadScript(script) {
console.log("Reload script " + script);
scripts.stopScript(script, true);
}
function stopScript(script) {
console.log("Stop script " + script);
scripts.stopScript(script);
}
@ -158,7 +155,6 @@ Rectangle {
console.log("Stop all scripts");
for (var index = 0; index < runningScriptsModel.count; index++) {
var url = runningScriptsModel.get(index).url;
console.log(url);
var fileName = url.substring(url.lastIndexOf('/')+1);
if (canEditScript(fileName)) {
scripts.stopScript(url);

Some files were not shown because too many files have changed in this diff Show more