diff --git a/app/src/main/java/com/omixlab/lckcontrol/ui/login/LoginViewModel.kt b/app/src/main/java/com/omixlab/lckcontrol/ui/login/LoginViewModel.kt index 5dd7dbe..2800a1d 100644 --- a/app/src/main/java/com/omixlab/lckcontrol/ui/login/LoginViewModel.kt +++ b/app/src/main/java/com/omixlab/lckcontrol/ui/login/LoginViewModel.kt @@ -14,7 +14,6 @@ import com.meta.horizon.platform.ovr.models.UserProof import com.meta.horizon.platform.ovr.requests.Request import com.meta.horizon.platform.ovr.requests.Users import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow @@ -43,35 +42,43 @@ class LoginViewModel @Inject constructor( _isLoading.value = true _error.value = null try { - // Initialize Platform SDK + // Initialize Platform SDK (async, wait for completion via message pump) if (!Core.isInitialized()) { - Log.d(TAG, "Initializing Platform SDK with appId=$QUEST_APP_ID") - val initResult = Core.initialize(QUEST_APP_ID, activity.applicationContext) - Log.d(TAG, "Core.initialize returned: $initResult") + Log.d(TAG, "Async initializing Platform SDK with appId=$QUEST_APP_ID") + val initResult = awaitWithPump { Core.asyncInitialize(QUEST_APP_ID, activity.applicationContext) } + Log.d(TAG, "Platform SDK initialized: $initResult") + } else { + Log.d(TAG, "Platform SDK already initialized") } - Log.d(TAG, "Core.isInitialized=${Core.isInitialized()}") - // Check cached user ID first (synchronous) - val cachedUserId = Core.getLoggedInUserID() - Log.d(TAG, "Core.getLoggedInUserID() = $cachedUserId") + // Get logged-in user (async with message pump) + Log.d(TAG, "Requesting logged-in user...") + val user = awaitWithPump { Users.getLoggedInUser() } + val numericId = user.getID().toString() + val oculusId = user.oculusID + Log.d(TAG, "User: id=$numericId displayName=${user.displayName} oculusId=$oculusId") - if (cachedUserId == 0L) { - throw Exception("Platform SDK returned user ID 0. " + - "Make sure the app is installed from the Horizon store (not sideloaded) " + + // Use numeric ID if available (requires Data Use Checkup), fall back to oculusId + val userId = if (user.getID() != 0L) numericId else oculusId + if (userId.isNullOrEmpty()) { + throw Exception("Platform SDK returned no user identifier. " + + "Make sure the app is installed from the Horizon store " + "and your account is a test user for this app.") } - // Get full user via async call with manual message pump - val user = awaitWithPump { Users.getLoggedInUser() } - val userId = user.getID().toString() - Log.d(TAG, "User: id=$userId displayName=${user.displayName}") - - // Get user proof (nonce) - val proof = awaitWithPump { Users.getUserProof() } - val nonce = proof.value - Log.d(TAG, "UserProof nonce=$nonce") + // Get user proof (nonce) — may fail without DUC approval + var nonce = "" + try { + Log.d(TAG, "Requesting user proof...") + val proof = awaitWithPump { Users.getUserProof() } + nonce = proof.value + Log.d(TAG, "UserProof nonce length=${nonce.length}") + } catch (e: Exception) { + Log.w(TAG, "getUserProof failed (DUC pending?), proceeding without nonce", e) + } // Send to backend for verification + Log.d(TAG, "Sending to backend: userId=$userId hasNonce=${nonce.isNotEmpty()}") val response = apiService.metaCallback( MetaCallbackRequest( userId = userId, @@ -82,6 +89,7 @@ class LoginViewModel @Inject constructor( // Save session tokens tokenStore.saveSession(response.accessToken, response.refreshToken) + Log.d(TAG, "Login successful!") } catch (e: Exception) { Log.e(TAG, "Quest login failed", e) _error.value = e.message ?: "Login failed" @@ -97,7 +105,7 @@ class LoginViewModel @Inject constructor( /** * Awaits a Platform SDK request by manually pumping the message queue. - * The SDK requires Request.runCallbacks() to be called for callbacks to fire. + * The SDK delivers responses via Core.popSDKMessage() which must be polled. */ private suspend fun awaitWithPump( block: () -> Request, @@ -105,25 +113,30 @@ class LoginViewModel @Inject constructor( var completed = false block() .onSuccess { result: T -> + Log.d(TAG, "SDK request succeeded: ${result?.javaClass?.simpleName}") if (!completed) { completed = true cont.resume(result) } } .onError { error -> + Log.e(TAG, "SDK request failed: code=${error.code} http=${error.httpCode} msg=${error.message}") if (!completed) { completed = true - cont.resumeWithException(Exception(error.message)) + cont.resumeWithException(Exception("SDK error ${error.code}: ${error.message}")) } } // Pump messages on a background thread Thread { - val timeout = System.currentTimeMillis() + 10_000 // 10s timeout + val timeout = System.currentTimeMillis() + 15_000 // 15s timeout + var msgCount = 0 while (!completed && System.currentTimeMillis() < timeout) { try { val msg = Core.popSDKMessage() if (msg != null) { + msgCount++ + Log.d(TAG, "Pumped message #$msgCount type=${msg.type} isError=${msg.isError}") Request.handleMessage(msg) } } catch (e: Exception) { @@ -131,9 +144,10 @@ class LoginViewModel @Inject constructor( } Thread.sleep(50) } + Log.d(TAG, "Message pump done: completed=$completed msgs=$msgCount") if (!completed) { completed = true - cont.resumeWithException(Exception("Platform SDK request timed out")) + cont.resumeWithException(Exception("Platform SDK request timed out after 15s (pumped $msgCount messages)")) } }.start() }