mirror of
https://github.com/ARMSX2/ARMSX2.git
synced 2026-08-24 16:50:16 -07:00
Fix Discord stuck on "Connecting" (crash loop in :discord)
connect() resolves the Discord app through DiscordSocialSdkInit.getEngineActivity() and hands the result straight to Context.getPackageManager() with no null check. That static was only ever set by DiscordAuthActivity, which runs during sign-in -- so every launch with a cached token started the SDK with it null, and :discord died with an NPE. Android restarted the service, which started again and died again. The app process never saw any of it, because the protocol is poll-only: the UI just sat on "Connecting" forever. Binding the SDK only during sign-in was wrong independently of that. The SDK's statics are per-process and Android restarts :discord whenever it likes, so the binding has to be re-established on demand rather than assumed to survive. Handing the Activity over is now separate from opening the browser: DiscordAuthActivity takes an EXTRA_AUTHORIZE flag and always binds, and the service will not call start() until a binding exists, launching the invisible Activity itself when there is none.
This commit is contained in:
@@ -48,6 +48,26 @@ import android.util.Log
|
||||
* here; the app process learns the outcome from the next state poll like any other change.
|
||||
*/
|
||||
class DiscordAuthActivity : Activity() {
|
||||
companion object {
|
||||
/** Intent extra: also open the browser sign-in, rather than only handing over the Activity. */
|
||||
const val EXTRA_AUTHORIZE = "authorize"
|
||||
|
||||
/**
|
||||
* True once setEngineActivity has run in THIS process.
|
||||
*
|
||||
* The SDK keeps the Activity in a static, and statics are per-process, so this resets
|
||||
* every time Android restarts :discord -- which it does freely. Anything that needs the
|
||||
* SDK must check this rather than assume sign-in already happened.
|
||||
*/
|
||||
@Volatile
|
||||
var engineBound = false
|
||||
private set
|
||||
|
||||
/** Ran once, when the Activity below hands itself to the SDK. Set by DiscordService. */
|
||||
@Volatile
|
||||
var onEngineBound: (() -> Unit)? = null
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
@@ -65,10 +85,20 @@ class DiscordAuthActivity : Activity() {
|
||||
.getMethod("setEngineActivity", Activity::class.java)
|
||||
.invoke(null, this)
|
||||
}
|
||||
.onSuccess {
|
||||
engineBound = true
|
||||
val waiting = onEngineBound
|
||||
onEngineBound = null
|
||||
waiting?.invoke()
|
||||
}
|
||||
.onFailure { Log.w("ARMSX2DiscordSvc", "setEngineActivity failed: ${it.message}") }
|
||||
|
||||
runCatching { DiscordNative.authorize() }
|
||||
.onFailure { Log.w("ARMSX2DiscordSvc", "authorize failed: ${it.message}") }
|
||||
// Only when asked. Handing the Activity over is now also done on a plain start, and that
|
||||
// must not drag the browser up with it.
|
||||
if (intent?.getBooleanExtra(EXTRA_AUTHORIZE, false) == true) {
|
||||
runCatching { DiscordNative.authorize() }
|
||||
.onFailure { Log.w("ARMSX2DiscordSvc", "authorize failed: ${it.message}") }
|
||||
}
|
||||
|
||||
finish()
|
||||
overridePendingTransition(0, 0)
|
||||
|
||||
@@ -99,10 +99,7 @@ class DiscordService : Service() {
|
||||
when (msg.what) {
|
||||
DiscordIpc.MSG_START -> {
|
||||
if (!loaded) return
|
||||
val token = msg.data?.getString(DiscordIpc.DATA_TOKEN).orEmpty()
|
||||
runCatching { DiscordNative.start(token) }
|
||||
.onSuccess { started = true }
|
||||
.onFailure { Log.w(TAG, "start failed: ${it.message}") }
|
||||
startWhenEngineBound(msg.data?.getString(DiscordIpc.DATA_TOKEN).orEmpty())
|
||||
}
|
||||
|
||||
DiscordIpc.MSG_AUTHORIZE -> {
|
||||
@@ -112,6 +109,7 @@ class DiscordService : Service() {
|
||||
// reference the SDK here cannot use. DiscordAuthActivity exists only to be that.
|
||||
runCatching {
|
||||
val i = Intent(this, DiscordAuthActivity::class.java)
|
||||
.putExtra(DiscordAuthActivity.EXTRA_AUTHORIZE, true)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
startActivity(i)
|
||||
}.onFailure { Log.w(TAG, "auth activity failed: ${it.message}") }
|
||||
@@ -140,6 +138,45 @@ class DiscordService : Service() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the SDK, but not before it has an Activity in this process.
|
||||
*
|
||||
* The SDK's connect() resolves the Discord app through
|
||||
* DiscordSocialSdkInit.getEngineActivity() and passes the result straight to
|
||||
* Context.getPackageManager() with no null check, so starting before that Activity exists
|
||||
* takes :discord down with an NPE. Android then restarts the service, which starts again and
|
||||
* dies again -- a crash loop the app process cannot see, because it only ever polls for state.
|
||||
* The UI sat on "Connecting" forever.
|
||||
*
|
||||
* The Activity used to be created only by the sign-in flow, so this hit every launch that had
|
||||
* a cached token -- i.e. every launch after the first. It is not specific to signing in: the
|
||||
* SDK's statics are per-process and Android restarts :discord whenever it likes, so the
|
||||
* binding has to be re-established on demand rather than assumed.
|
||||
*/
|
||||
private fun startWhenEngineBound(token: String) {
|
||||
if (DiscordAuthActivity.engineBound) {
|
||||
doStart(token)
|
||||
return
|
||||
}
|
||||
DiscordAuthActivity.onEngineBound = { handler.post { doStart(token) } }
|
||||
runCatching {
|
||||
startActivity(
|
||||
Intent(this, DiscordAuthActivity::class.java)
|
||||
.putExtra(DiscordAuthActivity.EXTRA_AUTHORIZE, false)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
|
||||
)
|
||||
}.onFailure {
|
||||
DiscordAuthActivity.onEngineBound = null
|
||||
Log.w(TAG, "could not hand the SDK an Activity: ${it.message}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun doStart(token: String) {
|
||||
runCatching { DiscordNative.start(token) }
|
||||
.onSuccess { started = true }
|
||||
.onFailure { Log.w(TAG, "start failed: ${it.message}") }
|
||||
}
|
||||
|
||||
/** One snapshot, on request. Everything the app's UI renders comes through here. */
|
||||
private fun reply(to: Messenger?) {
|
||||
val target = to ?: return
|
||||
|
||||
Reference in New Issue
Block a user