diff --git a/android/app/build.gradle b/android/app/build.gradle index 76f5acfaea..91ab54cf22 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -53,6 +53,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 @@ -63,6 +66,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") : '') + "\"" } } diff --git a/android/app/src/main/cpp/native.cpp b/android/app/src/main/cpp/native.cpp index 63e116abcc..4d60912831 100644 --- a/android/app/src/main/cpp/native.cpp +++ b/android/app/src/main/cpp/native.cpp @@ -363,6 +363,52 @@ Java_io_highfidelity_hifiinterface_fragment_LoginFragment_login(JNIEnv *env, job 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; + if (__loginCompletedListener.isValid()) { + __loginCompletedListener.callMethod<void>("handleLoginCompleted", "(Z)V", jSuccess); + } + }); + + QObject::connect(accountManager.data(), &AccountManager::loginFailed, []() { + jboolean jSuccess = (jboolean) false; + if (__loginCompletedListener.isValid()) { + __loginCompletedListener.callMethod<void>("handleLoginCompleted", "(Z)V", jSuccess); + } + }); + + 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)); + +} + JNIEXPORT void JNICALL Java_io_highfidelity_hifiinterface_fragment_SignupFragment_login(JNIEnv *env, jobject instance, diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java b/android/app/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java index 5250d2582b..7db158a100 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java @@ -142,6 +142,12 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); webSlidingDrawer = (SlidingDrawer) inflater.inflate(R.layout.web_drawer, mainLayout, false); QtLayout qtLayout = (QtLayout) mainLayout.getChildAt(0); + if (qtLayout == null) { + Log.d("[QTLAYOUT-NULL]" , "Mainlayout children: " + mainLayout.getChildCount()); + for (int i=0; i< mainLayout.getChildCount(); i++) { + Log.d("[QTLAYOUT-NULL]" , "Child " + i + ": " + mainLayout.getChildAt(i)); + } + } QtLayout.LayoutParams layoutParams = new QtLayout.LayoutParams(webSlidingDrawer.getLayoutParams()); webSlidingDrawer.setOnDrawerCloseListener(() -> { WebViewFragment webViewFragment = (WebViewFragment) getFragmentManager().findFragmentByTag("webViewFragment"); @@ -380,4 +386,7 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW public void onExpand() { keepInterfaceRunning = true; } + + @Override + public void onOAuthAuthorizeCallback(Uri uri) { } } diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/LoginMenuActivity.java b/android/app/src/main/java/io/highfidelity/hifiinterface/LoginMenuActivity.java index 67ff772573..63f74ac3e2 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/LoginMenuActivity.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/LoginMenuActivity.java @@ -92,7 +92,7 @@ public class LoginMenuActivity extends AppCompatActivity @Override public void onLoginButtonClicked() { - loadLoginFragment(); + loadLoginFragment(false); } @Override @@ -100,6 +100,10 @@ public class LoginMenuActivity extends AppCompatActivity loadMainActivity(); } + @Override + public void onSteamLoginButtonClicked() { + loadLoginFragment(true); + } private void loadSignupFragment() { FragmentManager fragmentManager = getFragmentManager(); @@ -113,10 +117,10 @@ public class LoginMenuActivity extends AppCompatActivity hideStatusBar(); } - private void loadLoginFragment() { + private void loadLoginFragment(boolean useOauth) { FragmentManager fragmentManager = getFragmentManager(); FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); - Fragment fragment = LoginFragment.newInstance(); + Fragment fragment = LoginFragment.newInstance(useOauth); String tag = getString(R.string.tagFragmentLogin); fragmentTransaction.replace(R.id.content_frame, fragment, tag); fragmentTransaction.addToBackStack(tag); diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/WebViewActivity.java b/android/app/src/main/java/io/highfidelity/hifiinterface/WebViewActivity.java index 5d65bcad51..e906c4b734 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/WebViewActivity.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/WebViewActivity.java @@ -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(); + } + } diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/LoginFragment.java b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/LoginFragment.java index eea65d425f..79fbb50737 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/LoginFragment.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/LoginFragment.java @@ -1,10 +1,13 @@ package io.highfidelity.hifiinterface.fragment; +import android.app.Activity; import android.app.Fragment; import android.content.Context; 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; @@ -18,8 +21,14 @@ 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; @@ -27,6 +36,14 @@ import static org.qtproject.qt5.android.QtActivityDelegate.ApplicationInactive; 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_AUTORIZE_REQUEST = 1; + private EditText mUsername; private EditText mPassword; private TextView mError; @@ -37,8 +54,12 @@ public class LoginFragment extends Fragment private ViewGroup mLoggedInFrame; private boolean mLoginInProgress; private boolean mLoginSuccess; + private boolean mUseOauth; + private String mOauthState; 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 cancelLogin(); private LoginFragment.OnLoginInteractionListener mListener; @@ -47,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) { @@ -79,6 +111,11 @@ public class LoginFragment extends Fragment mKeepMeLoggedInCheckbox.setChecked(HifiUtils.getInstance().isKeepingLoggedIn()); + if (mUseOauth) { + openWebForAuthorization(); + } else { + showLoginForm(); + } return rootView; } @@ -109,12 +146,33 @@ public class LoginFragment extends Fragment @Override public void onStop() { super.onStop(); - showLoginForm(); + if (!mUseOauth) { + showLoginForm(); + } // Leave the Qt app paused QtNative.setApplicationState(ApplicationInactive); hideKeyboard(); } + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode == OAUTH_AUTORIZE_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(); @@ -207,8 +265,12 @@ public class LoginFragment extends Fragment mLoginSuccess = true; showLoggedInMessage(); } else { - showLoginForm(); - showError(getString(R.string.login_username_or_password_incorrect)); + if (!mUseOauth) { + showLoginForm(); + showError(getString(R.string.login_username_or_password_incorrect)); + } else { + openWebForAuthorization(); + } } }); } @@ -229,6 +291,33 @@ public class LoginFragment extends Fragment } } + private void updateOauthState() { + mOauthState = 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_AUTORIZE_REQUEST); + } + + public interface OnLoginInteractionListener { void onLoginCompleted(); void onCancelLogin(); diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/StartMenuFragment.java b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/StartMenuFragment.java index 22614fa300..fe77991962 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/StartMenuFragment.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/StartMenuFragment.java @@ -1,8 +1,8 @@ package io.highfidelity.hifiinterface.fragment; +import android.app.Fragment; import android.content.Context; import android.os.Bundle; -import android.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -11,6 +11,7 @@ import io.highfidelity.hifiinterface.R; public class StartMenuFragment extends Fragment { + private String TAG = "HighFidelity"; private StartMenuInteractionListener mListener; public StartMenuFragment() { @@ -39,6 +40,12 @@ public class StartMenuFragment extends Fragment { } }); + rootView.findViewById(R.id.steamLoginButton).setOnClickListener(view -> { + if (mListener != null) { + mListener.onSteamLoginButtonClicked(); + } + }); + rootView.findViewById(R.id.takeMeInWorld).setOnClickListener(view -> { if (mListener != null) { mListener.onSkipLoginClicked(); @@ -81,5 +88,6 @@ public class StartMenuFragment extends Fragment { void onSignupButtonClicked(); void onLoginButtonClicked(); void onSkipLoginClicked(); + void onSteamLoginButtonClicked(); } } diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/WebViewFragment.java b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/WebViewFragment.java index 2d887d5a19..94aa6f29a3 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/WebViewFragment.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/WebViewFragment.java @@ -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.util.Log; 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,10 @@ import android.webkit.WebViewClient; import android.widget.ProgressBar; import android.widget.Toast; +import java.util.List; +import java.util.Set; + +import io.highfidelity.hifiinterface.BuildConfig; import io.highfidelity.hifiinterface.R; import io.highfidelity.hifiinterface.WebViewActivity; @@ -32,6 +39,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 +49,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 +179,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 +189,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 +265,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 +335,17 @@ public class WebViewFragment extends Fragment implements GestureDetector.OnGestu super.onLoadResource(view, url); } } + + @Override + public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { + if (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 { diff --git a/android/app/src/main/res/drawable/ic_steam.xml b/android/app/src/main/res/drawable/ic_steam.xml new file mode 100644 index 0000000000..9b739c1f73 --- /dev/null +++ b/android/app/src/main/res/drawable/ic_steam.xml @@ -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> \ No newline at end of file diff --git a/android/app/src/main/res/drawable/rounded_button_color4.xml b/android/app/src/main/res/drawable/rounded_button_color4.xml new file mode 100644 index 0000000000..679bf24513 --- /dev/null +++ b/android/app/src/main/res/drawable/rounded_button_color4.xml @@ -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> \ No newline at end of file diff --git a/android/app/src/main/res/layout/fragment_login.xml b/android/app/src/main/res/layout/fragment_login.xml index 2f48424ae8..343822e243 100644 --- a/android/app/src/main/res/layout/fragment_login.xml +++ b/android/app/src/main/res/layout/fragment_login.xml @@ -111,7 +111,7 @@ app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@id/header" app:layout_constraintBottom_toBottomOf="parent" - android:visibility="visible"> + android:visibility="gone"> <TextView android:id="@+id/error" android:layout_width="wrap_content" diff --git a/android/app/src/main/res/layout/fragment_login_menu.xml b/android/app/src/main/res/layout/fragment_login_menu.xml index cda055c883..7484713b6b 100644 --- a/android/app/src/main/res/layout/fragment_login_menu.xml +++ b/android/app/src/main/res/layout/fragment_login_menu.xml @@ -74,6 +74,24 @@ tools:context=".fragment.StartMenuFragment"> app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent"/> + <Button + android:id="@+id/steamLoginButton" + android:layout_width="238dp" + android:layout_height="42dp" + 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="18sp" + 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" diff --git a/android/app/src/main/res/values/colors.xml b/android/app/src/main/res/values/colors.xml index 73b2d5d61d..fc5ce7ba16 100644 --- a/android/app/src/main/res/values/colors.xml +++ b/android/app/src/main/res/values/colors.xml @@ -11,6 +11,7 @@ <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> diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index 26ed105e91..671f171c3e 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -39,6 +39,7 @@ <!-- 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> @@ -52,4 +53,5 @@ <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> diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp index d99c0020da..b2af35f16c 100644 --- a/libraries/networking/src/AccountManager.cpp +++ b/libraries/networking/src/AccountManager.cpp @@ -540,6 +540,30 @@ void AccountManager::requestAccessToken(const QString& login, const QString& pas connect(requestReply, &QNetworkReply::finished, this, &AccountManager::requestAccessTokenFinished); } +void AccountManager::requestAccessTokenWithAuthCode(const QString& authCode, const QString& clientId, const QString& clientSecret, const QString& redirectUri) { + QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); + + QNetworkRequest request; + request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); + request.setHeader(QNetworkRequest::UserAgentHeader, _userAgentGetter()); + + QUrl grantURL = _authURL; + grantURL.setPath("/oauth/token"); + + QByteArray postData; + postData.append("grant_type=authorization_code&"); + postData.append("client_id=" + clientId + "&"); + postData.append("client_secret=" + clientSecret + "&"); + postData.append("code=" + authCode + "&"); + postData.append("redirect_uri=" + QUrl::toPercentEncoding(redirectUri)); + + request.setUrl(grantURL); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); + + QNetworkReply* requestReply = networkAccessManager.post(request, postData); + connect(requestReply, &QNetworkReply::finished, this, &AccountManager::requestAccessTokenFinished); +} + void AccountManager::requestAccessTokenWithSteam(QByteArray authSessionTicket) { QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); diff --git a/libraries/networking/src/AccountManager.h b/libraries/networking/src/AccountManager.h index f3b81cf1c9..3185a8677b 100644 --- a/libraries/networking/src/AccountManager.h +++ b/libraries/networking/src/AccountManager.h @@ -101,6 +101,7 @@ public: public slots: void requestAccessToken(const QString& login, const QString& password); void requestAccessTokenWithSteam(QByteArray authSessionTicket); + void requestAccessTokenWithAuthCode(const QString& authCode, const QString& clientId, const QString& clientSecret, const QString& redirectUri); void refreshAccessToken(); void requestAccessTokenFinished();