1
Fork 0
mirror of https://github.com/Steffo99/twom.git synced 2024-11-28 19:14:24 +00:00

Complete basic login process

This commit is contained in:
Steffo 2023-11-22 18:45:35 +01:00
parent cdad623e96
commit c00039b008
Signed by: steffo
GPG key ID: 2A24051445686895
6 changed files with 222 additions and 34 deletions

View file

@ -0,0 +1,58 @@
package eu.steffo.twom.login
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.TextLayoutResult
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.TextUnit
@Composable
@Preview(showBackground = true)
fun ErrorText(
modifier: Modifier = Modifier,
text: String = "",
fontSize: TextUnit = TextUnit.Unspecified,
fontStyle: FontStyle? = null,
fontWeight: FontWeight? = null,
fontFamily: FontFamily? = null,
letterSpacing: TextUnit = TextUnit.Unspecified,
textDecoration: TextDecoration? = null,
textAlign: TextAlign? = null,
lineHeight: TextUnit = TextUnit.Unspecified,
overflow: TextOverflow = TextOverflow.Clip,
softWrap: Boolean = true,
maxLines: Int = Int.MAX_VALUE,
minLines: Int = 1,
onTextLayout: (TextLayoutResult) -> Unit = {},
style: TextStyle = LocalTextStyle.current
) {
Text(
modifier = modifier,
text = text,
color = MaterialTheme.colorScheme.error,
fontSize = fontSize,
fontStyle = fontStyle,
fontWeight = fontWeight,
fontFamily = fontFamily,
letterSpacing = letterSpacing,
textDecoration = textDecoration,
textAlign = textAlign,
lineHeight = lineHeight,
overflow = overflow,
softWrap = softWrap,
maxLines = maxLines,
minLines = minLines,
onTextLayout = onTextLayout,
style = style,
)
}

View file

@ -9,7 +9,16 @@ class LoginActivity : ComponentActivity() {
super.onStart() super.onStart()
setContent { setContent {
LoginActivityScaffold() LoginActivityScaffold(
onBack = {
setResult(RESULT_CANCELED)
finish()
},
onLogin = {
setResult(RESULT_OK)
finish()
},
)
} }
} }
} }

View file

@ -6,6 +6,8 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.LinearProgressIndicator import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ProgressIndicatorDefaults
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextField import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@ -21,9 +23,15 @@ import eu.steffo.twom.R
import eu.steffo.twom.matrix.TwoMMatrix import eu.steffo.twom.matrix.TwoMMatrix
import eu.steffo.twom.theme.TwoMPadding import eu.steffo.twom.theme.TwoMPadding
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
import org.matrix.android.sdk.api.auth.data.LoginFlowResult
import org.matrix.android.sdk.api.auth.login.LoginWizard
import org.matrix.android.sdk.api.auth.wellknown.WellknownResult
import org.matrix.android.sdk.api.failure.MatrixIdFailure
import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.Session
// TODO: Localize error messages
@Composable @Composable
@Preview(showBackground = true) @Preview(showBackground = true)
fun LoginActivityControl( fun LoginActivityControl(
@ -35,19 +43,15 @@ fun LoginActivityControl(
var username by rememberSaveable { mutableStateOf("") } var username by rememberSaveable { mutableStateOf("") }
var password by rememberSaveable { mutableStateOf("") } var password by rememberSaveable { mutableStateOf("") }
var loggingIn by rememberSaveable { mutableStateOf(false) } var loginStep by rememberSaveable { mutableStateOf(LoginStep.NONE) }
var errorMessage by rememberSaveable { mutableStateOf<String?>(null) }
Column(modifier) { Column(modifier) {
if (loggingIn) {
LinearProgressIndicator( LinearProgressIndicator(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
progress = loginStep.step.toFloat() / LoginStep.DONE.step.toFloat(),
color = if (errorMessage != null) MaterialTheme.colorScheme.error else ProgressIndicatorDefaults.linearColor
) )
} else {
LinearProgressIndicator(
modifier = Modifier.fillMaxWidth(),
progress = 0.0f,
)
}
Row(TwoMPadding.base) { Row(TwoMPadding.base) {
Text(LocalContext.current.getString(R.string.login_text)) Text(LocalContext.current.getString(R.string.login_text))
} }
@ -88,44 +92,120 @@ fun LoginActivityControl(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
onClick = { onClick = {
scope.launch Login@{ scope.launch Login@{
Log.d(this::class.qualifiedName, "Launching login wizard...") errorMessage = null
val wizard = TwoMMatrix.matrix.authenticationService().getLoginWizard()
// TODO: Which exceptions can this spawn? Log.d("Login", "Getting authentication service...")
Log.d(this::class.qualifiedName, "Trying to login as: $username") loginStep = LoginStep.SERVICE
loggingIn = true val auth = TwoMMatrix.matrix.authenticationService()
Log.d("Login", "Retrieving .well-known data for: $username")
loginStep = LoginStep.WELLKNOWN
lateinit var wellKnown: WellknownResult
try {
wellKnown = auth.getWellKnownData(username, null)
} catch (e: MatrixIdFailure.InvalidMatrixId) {
Log.d(
"Login",
"User seems to have input an invalid Matrix ID: $username",
e
)
errorMessage = "The input Matrix ID is invalid."
return@Login
} catch (e: Throwable) {
// TODO: It sure would be nice to know which exceptions can be thrown here.
Log.e(
"Login",
"Something went wrong while retrieving .well-known data for: $username",
e
)
errorMessage = e.message
return@Login
}
if (wellKnown !is WellknownResult.Prompt) {
Log.w(
"Login",
"Data is not .well-known for: $username"
)
errorMessage =
"Non .well-known Matrix servers are not currently supported by TwoM."
return@Login
}
Log.d("Login", "Retrieving login flows for: ${wellKnown.homeServerUrl}")
loginStep = LoginStep.FLOWS
lateinit var flows: LoginFlowResult
try {
flows = auth.getLoginFlow(
HomeServerConnectionConfig
.Builder()
.withHomeServerUri(wellKnown.homeServerUrl)
.build()
)
} catch (e: Throwable) {
// TODO: It sure would be nice to know which exceptions can be thrown here.
Log.e(
"Login",
"Something went wrong while retrieving login flows for: ${wellKnown.homeServerUrl}",
e
)
errorMessage = e.message
return@Login
}
Log.d("Login", "Creating login wizard...")
loginStep = LoginStep.WIZARD
lateinit var wizard: LoginWizard
try {
wizard = auth.getLoginWizard() // Why is this stateful? Aargh.
} catch (e: Throwable) {
// TODO: It sure would be nice to know which exceptions can be thrown here.
Log.e(
"Login",
"Something went wrong while creating the login wizard.",
e
)
errorMessage = e.message
return@Login
}
Log.d("Login", "Logging in as: $username")
loginStep = LoginStep.LOGIN
lateinit var session: Session lateinit var session: Session
// TODO: Why does this not catch the exception?
try { try {
session = wizard.login( session = wizard.login(
login = username, login = username,
password = password, password = password,
initialDeviceName = "Garasauto", // TODO initialDeviceName = "Garasauto", // TODO: Set a proper device name
deviceId = "Garasauto", // TODO deviceId = "Garasauto", // TODO: Set a proper device id
) )
} catch (e: RuntimeException) { } catch (e: Throwable) {
Log.e( Log.e(
this::class.qualifiedName, "Login",
"Something went wrong while logging in as: $username", "Something went wrong while logging in as: $username",
e e
) )
errorMessage = e.message
return@Login return@Login
} finally {
loggingIn = false
} }
Log.d( Log.d(
this::class.qualifiedName, "Login",
"Logged in with session id: ${session.sessionId}" "Logged in successfully with session id: ${session.sessionId}"
) )
loginStep = LoginStep.DONE
onLogin(session) onLogin(session)
} }
}, },
enabled = (username != ""), enabled = (username != "" && (loginStep == LoginStep.NONE || errorMessage != null)),
) { ) {
Text(LocalContext.current.getString(R.string.login_complete_text)) Text(LocalContext.current.getString(R.string.login_complete_text))
} }
} }
if (errorMessage != null) {
Row(TwoMPadding.base) {
ErrorText(text = errorMessage ?: "Unknown error")
}
}
} }
} }

View file

@ -23,8 +23,6 @@ import org.matrix.android.sdk.api.session.Session
@Preview @Preview
fun LoginActivityScaffold( fun LoginActivityScaffold(
onBack: () -> Unit = {}, onBack: () -> Unit = {},
selectedHomeserver: String? = null,
onSelectHomeserver: () -> Unit = {},
onLogin: (session: Session) -> Unit = {}, onLogin: (session: Session) -> Unit = {},
) { ) {
TwoMTheme { TwoMTheme {
@ -43,7 +41,10 @@ fun LoginActivityScaffold(
) )
} }
) { ) {
LoginActivityControl(Modifier.padding(it)) LoginActivityControl(
modifier = Modifier.padding(it),
onLogin = onLogin
)
} }
} }
} }

View file

@ -0,0 +1,11 @@
package eu.steffo.twom.login
enum class LoginStep(val step: Int) {
NONE(0),
SERVICE(1),
WELLKNOWN(2),
FLOWS(3),
WIZARD(4),
LOGIN(5),
DONE(6),
}

View file

@ -20,12 +20,15 @@ import androidx.compose.ui.platform.LocalContext
import eu.steffo.twom.R import eu.steffo.twom.R
import eu.steffo.twom.login.LoginActivity import eu.steffo.twom.login.LoginActivity
import eu.steffo.twom.theme.TwoMTheme import eu.steffo.twom.theme.TwoMTheme
import org.matrix.android.sdk.api.session.Session
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
class MatrixActivity : ComponentActivity() { class MatrixActivity : ComponentActivity() {
private lateinit var loginLauncher: ActivityResultLauncher<Intent> private lateinit var loginLauncher: ActivityResultLauncher<Intent>
private var session: Session? = null
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
actionBar?.hide() actionBar?.hide()
@ -33,8 +36,34 @@ class MatrixActivity : ComponentActivity() {
TwoMMatrix.ensureMatrix(applicationContext) TwoMMatrix.ensureMatrix(applicationContext)
loginLauncher = loginLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { registerForActivityResult(ActivityResultContracts.StartActivityForResult()) HandleResult@{
Log.d(this::class.qualifiedName, "LoginActivity has returned a result.") Log.d(this::class.qualifiedName, "LoginActivity has returned a result!")
when (it.resultCode) {
RESULT_OK -> {
session =
TwoMMatrix.matrix.authenticationService().getLastAuthenticatedSession()
setContent {
TwoMTheme {
Scaffold(
topBar = {
CenterAlignedTopAppBar(
title = {
Text(LocalContext.current.getString(R.string.app_name))
}
)
}
) {
Text(session?.myUserId ?: "No session", Modifier.padding(it))
}
}
}
}
}
if (it.resultCode == RESULT_CANCELED) {
return@HandleResult
}
} }
} }
@ -59,7 +88,7 @@ class MatrixActivity : ComponentActivity() {
loginLauncher.launch(Intent(applicationContext, LoginActivity::class.java)) loginLauncher.launch(Intent(applicationContext, LoginActivity::class.java))
} }
) { ) {
Text("") Text("plis login with matrics no scam 100%")
} }
} }
} }