1
Fork 0
mirror of https://github.com/Steffo99/twom.git synced 2024-11-21 23:54:26 +00:00

Create HomeserverFragment and relative classes

This commit is contained in:
Steffo 2023-11-15 20:10:08 +01:00
parent 00a60cac5d
commit 368b1e6fbf
Signed by: steffo
GPG key ID: 2A24051445686895
12 changed files with 428 additions and 41 deletions

View file

@ -1,6 +1,43 @@
<component name="InspectionProjectProfileManager"> <component name="InspectionProjectProfileManager">
<profile version="1.0"> <profile version="1.0">
<option name="myName" value="Project Default" /> <option name="myName" value="Project Default" />
<inspection_tool class="ControlFlowWithEmptyBody" enabled="true" level="WARNING" enabled_by_default="true" editorAttributes="WARNING_ATTRIBUTES" />
<inspection_tool class="PreviewAnnotationInFunctionWithParameters" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewApiLevelMustBeValid" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewFontScaleMustBeGreaterThanZero" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewMultipleParameterProviders" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewPickerAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false"> <inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
<option name="processCode" value="true" /> <option name="processCode" value="true" />
<option name="processLiterals" value="true" /> <option name="processLiterals" value="true" />

View file

@ -0,0 +1,7 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="PROJECT_PROFILE" value="Default" />
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>

View file

@ -62,8 +62,9 @@ dependencies {
implementation("androidx.navigation:navigation-ui-ktx:2.7.5") implementation("androidx.navigation:navigation-ui-ktx:2.7.5")
implementation("androidx.compose.material3:material3:1.1.2") implementation("androidx.compose.material3:material3:1.1.2")
implementation("org.matrix.android:matrix-android-sdk2:1.5.30") implementation("org.matrix.android:matrix-android-sdk2:1.5.30")
implementation("com.squareup.okhttp3:okhttp:4.12.0")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2") implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2")
implementation("androidx.activity:activity-compose:1.7.0") implementation("androidx.activity:activity-compose:1.8.0")
implementation(platform("androidx.compose:compose-bom:2023.03.00")) implementation(platform("androidx.compose:compose-bom:2023.03.00"))
implementation("androidx.compose.ui:ui") implementation("androidx.compose.ui:ui")
implementation("androidx.compose.ui:ui-graphics") implementation("androidx.compose.ui:ui-graphics")

View file

@ -1,7 +1,10 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<application <application
android:allowBackup="true" android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules" android:dataExtractionRules="@xml/data_extraction_rules"
@ -9,8 +12,27 @@
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:label="@string/app_name" android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:supportsRtl="true">
>
<!--
Remove WorkManagerInitializer Provider because we are using on-demand initialization of WorkManager
https://github.com/vector-im/element-android/blob/1a941149ab2eb5f725ff5297391c756bc694a60c/vector-app/src/main/AndroidManifest.xml#L42C1-L57C20
-->
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">
<meta-data
android:name="androidx.work.WorkManagerInitializer"
android:value="androidx.startup"
tools:node="remove" />
<!-- We init the lib ourself in EmojiCompatWrapper -->
<meta-data
android:name="androidx.emoji2.text.EmojiCompatInitializer"
tools:node="remove" />
</provider>
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:exported="true"> android:exported="true">

View file

@ -3,47 +3,14 @@ package eu.steffo.twom
import android.os.Bundle import android.os.Bundle
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.fillMaxSize import eu.steffo.twom.ui.fragment.LoginFragment
import androidx.compose.material3.Divider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalDrawerSheet
import androidx.compose.material3.ModalNavigationDrawer
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import eu.steffo.twom.ui.theme.TwoMTheme
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
actionBar?.hide()
setContent { setContent { LoginFragment() }
TwoMTheme {
// A surface container using the 'background' color from the theme
ModalNavigationDrawer(
drawerContent = {
Drawer()
}
) {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
Text(text = "Garasbici")
}
}
}
}
} }
} }
@Preview
@Composable
fun Drawer() {
ModalDrawerSheet {
Text(text = "garasauto")
Divider()
}
}

View file

@ -0,0 +1,134 @@
package eu.steffo.twom.ui.fragment
import android.net.Uri
import android.util.Log
import android.webkit.URLUtil
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import eu.steffo.twom.R
import eu.steffo.twom.ui.input.HomeserverField
import eu.steffo.twom.ui.input.HomeserverFieldState
import eu.steffo.twom.ui.scaffold.LocalMatrix
import eu.steffo.twom.ui.scaffold.TwoMMatrixProvider
import eu.steffo.twom.ui.scaffold.TwoMTopAppBar
import eu.steffo.twom.ui.theme.TwoMTheme
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
@Composable
fun LoginFragment() {
TwoMMatrixProvider {
TwoMTheme {
LoginContents()
}
}
}
@Composable
@Preview
fun LoginContents() {
val scope = rememberCoroutineScope()
val matrix = LocalMatrix.current
var homeserver by rememberSaveable { mutableStateOf("") }
var homeserverFieldState by rememberSaveable { mutableStateOf<HomeserverFieldState>(HomeserverFieldState.Empty) }
var homeserverUrlValid by rememberSaveable { mutableStateOf<Boolean?>(null) }
var homeserverFlowValid by rememberSaveable { mutableStateOf<Boolean?>(null) }
TwoMTopAppBar {
Column(
modifier = Modifier.padding(it)
) {
Row(
modifier = Modifier.padding(start = 20.dp, end = 20.dp, bottom = 10.dp)
) {
Text(LocalContext.current.getString(R.string.login_welcome_text))
}
Row(
modifier = Modifier.padding(start = 20.dp, end = 20.dp, bottom = 10.dp)
) {
HomeserverField(
modifier = Modifier.fillMaxWidth(),
value = homeserver,
onValueChange = OnValueChange@{
homeserver = it
homeserverUrlValid = null
homeserverFlowValid = null
homeserverFieldState = HomeserverFieldState.Empty
if(homeserver == "") {
return@OnValueChange
}
if(!(URLUtil.isHttpUrl(homeserver) || URLUtil.isHttpsUrl(homeserver))) {
homeserverUrlValid = false
return@OnValueChange
}
homeserverUrlValid = true
val uri = Uri.parse(homeserver)
scope.launch ValidateFlows@{
homeserverFieldState = HomeserverFieldState.Waiting
delay(500L)
if(homeserver != it) return@ValidateFlows
val authenticationService = matrix!!.authenticationService()
homeserverFieldState = HomeserverFieldState.Validating
try {
authenticationService.getLoginFlow(HomeServerConnectionConfig(
homeServerUri = uri,
))
}
catch(e: Throwable) {
Log.e("LoginFragment", "Failed to get flows for homeserver", e)
homeserverFieldState = HomeserverFieldState.Done
homeserverFlowValid = false
return@ValidateFlows
}
homeserverFieldState = HomeserverFieldState.Done
homeserverFlowValid = true
}
},
state = homeserverFieldState,
error =
if(homeserverUrlValid == false) LocalContext.current.getString(R.string.homeserver_error_malformedurl)
else if(homeserverFlowValid == false) LocalContext.current.getString(R.string.homeserver_error_notmatrix)
else null
,
enabled = (LocalMatrix.current != null),
)
}
Spacer(modifier = Modifier.weight(1f))
Row(
modifier = Modifier.padding(start = 20.dp, end = 20.dp, top = 10.dp, bottom = 10.dp)
) {
Button(
onClick = {},
enabled = homeserverUrlValid == true && homeserverFlowValid == true,
modifier = Modifier.fillMaxWidth(),
) {
Text(LocalContext.current.getString(R.string.login_button_continue_text))
}
}
}
}
}

View file

@ -0,0 +1,64 @@
package eu.steffo.twom.ui.input
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Build
import androidx.compose.material.icons.filled.Check
import androidx.compose.material.icons.filled.Create
import androidx.compose.material.icons.filled.Send
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview
import eu.steffo.twom.R
enum class HomeserverFieldState {
Empty,
Waiting,
Validating,
Done,
}
@Composable
@Preview
fun HomeserverField(
modifier: Modifier = Modifier,
value: String = "",
onValueChange: (String) -> Unit = {},
enabled: Boolean = true,
state: HomeserverFieldState = HomeserverFieldState.Empty,
error: String? = null,
) {
TextField(
modifier = modifier,
value = value,
onValueChange = onValueChange,
enabled = enabled,
singleLine = true,
label = {
Text(LocalContext.current.getString(R.string.homeserver_label))
},
placeholder = {
Text(LocalContext.current.getString(R.string.homeserver_placeholder))
},
trailingIcon = {
Icon(
when(state) {
HomeserverFieldState.Empty -> Icons.Default.Build
HomeserverFieldState.Waiting -> Icons.Default.Create
HomeserverFieldState.Validating -> Icons.Default.Send
HomeserverFieldState.Done -> Icons.Default.Check
},
LocalContext.current.getString(R.string.homeserver_trailingicon_validating_description)
)
},
supportingText = {
Text(error ?: LocalContext.current.getString(R.string.homeserver_supporting))
},
isError = (error != null)
)
}

View file

@ -0,0 +1,74 @@
package eu.steffo.twom.ui.scaffold
import android.content.Context
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.ui.platform.LocalContext
import eu.steffo.twom.R
import org.matrix.android.sdk.api.Matrix
import org.matrix.android.sdk.api.MatrixConfiguration
import org.matrix.android.sdk.api.provider.RoomDisplayNameFallbackProvider
val LocalMatrix = compositionLocalOf<Matrix?> { null }
@Composable
fun TwoMMatrixProvider(
content: @Composable () -> Unit = {},
) {
val matrix = Matrix(
context = LocalContext.current,
matrixConfiguration = MatrixConfiguration(
applicationFlavor = "TwoM",
roomDisplayNameFallbackProvider = TwoMRoomDisplayNameFallbackProvider(LocalContext.current)
)
)
CompositionLocalProvider(LocalMatrix provides matrix) {
content()
}
}
class TwoMRoomDisplayNameFallbackProvider(
val context: Context
) : RoomDisplayNameFallbackProvider {
override fun getNameFor1member(name: String): String {
return context.getString(R.string.room_name_fallback_members_1).format(name)
}
override fun getNameFor2members(name1: String, name2: String): String {
return context.getString(R.string.room_name_fallback_members_2).format(name1, name2)
}
override fun getNameFor3members(name1: String, name2: String, name3: String): String {
return context.getString(R.string.room_name_fallback_members_3).format(name1, name2, name3)
}
override fun getNameFor4members(
name1: String,
name2: String,
name3: String,
name4: String
): String {
return context.getString(R.string.room_name_fallback_members_4).format(name1, name2, name3, name4)
}
override fun getNameFor4membersAndMore(
name1: String,
name2: String,
name3: String,
remainingCount: Int
): String {
return context.getString(R.string.room_name_fallback_members_more).format(name1, name2, name3, remainingCount)
}
override fun getNameForEmptyRoom(isDirect: Boolean, leftMemberNames: List<String>): String {
return context.getString(R.string.room_name_fallback_members_0)
}
override fun getNameForRoomInvite(): String {
return context.getString(R.string.room_name_fallback_invite)
}
}

View file

@ -0,0 +1,36 @@
package eu.steffo.twom.ui.scaffold
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalDrawerSheet
import androidx.compose.material3.ModalNavigationDrawer
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
@Composable
fun TwoMNavigationDrawer(
modifier: Modifier = Modifier,
content: @Composable () -> Unit = {},
) {
ModalNavigationDrawer(
drawerContent = {
Drawer()
}
) {
Surface(
modifier = modifier,
color = MaterialTheme.colorScheme.background
) {
content()
}
}
}
@Composable
fun Drawer() {
ModalDrawerSheet {
Text("garasauto")
}
}

View file

@ -0,0 +1,26 @@
package eu.steffo.twom.ui.scaffold
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.material3.CenterAlignedTopAppBar
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
import eu.steffo.twom.R
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TwoMTopAppBar(
content: @Composable (innerPadding: PaddingValues) -> Unit = {},
) {
Scaffold(
topBar = {
CenterAlignedTopAppBar(
title = { Text(LocalContext.current.getString(R.string.app_name)) }
)
}
) {
content(it)
}
}

View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<resources></resources>

View file

@ -1,3 +1,20 @@
<resources> <resources>
<string name="app_name">TwoM</string> <string name="app_name">TwoM</string>
<string name="homeserver_placeholder">https://candy.steffo.eu</string>
<string name="homeserver_label">Homeserver</string>
<string name="homeserver_supporting">The URL of the homeserver providing you with Matrix access.</string>
<string name="homeserver_error_malformedurl">The homeserver URL you provided is not a valid URL.</string>
<string name="room_name_fallback_members_0">Is this even a party?</string>
<string name="room_name_fallback_members_1">%1$s\'s party</string>
<string name="room_name_fallback_members_2">%1$s and %2$s\'s party</string>
<string name="room_name_fallback_members_3">%1$s, %2$s, and %3$s\'s party</string>
<string name="room_name_fallback_members_4">%1$s, %2$s, %3$s, %4$s\'s party</string>
<string name="room_name_fallback_members_more">%1$s, %2$s, %3$s and %4$d others\' party</string>
<string name="room_name_fallback_invite">Party invite</string>
<string name="login_button_continue_text">Continue</string>
<string name="login_welcome_text">Welcome to TwoM! To start using the app, you\'ll need to select the Matrix homeserver you want to use.</string>
<string name="homeserver_error_notmatrix">The homeserver URL you provided does not point to a Matrix homeserver.</string>
<string name="homeserver_trailingicon_validating_description">Checking if the URL points to a Matrix homeserver…</string>
<string name="homeserver_trailingicon_waiting_description">Waiting for you to stop writing…</string>
<string name="homeserver_trailingicon_waiting_done">All checks complete.</string>
</resources> </resources>