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:
parent
cdad623e96
commit
c00039b008
6 changed files with 222 additions and 34 deletions
58
app/src/main/java/eu/steffo/twom/login/ErrorText.kt
Normal file
58
app/src/main/java/eu/steffo/twom/login/ErrorText.kt
Normal 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,
|
||||||
|
)
|
||||||
|
}
|
|
@ -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()
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
11
app/src/main/java/eu/steffo/twom/login/LoginStep.kt
Normal file
11
app/src/main/java/eu/steffo/twom/login/LoginStep.kt
Normal 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),
|
||||||
|
}
|
|
@ -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%")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue