diff --git a/app/build.gradle b/app/build.gradle index fd0d5670f4..ff64a65c7f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -47,6 +47,7 @@ android { dependencies { implementation project(":libraries:ui:theme") + implementation project(":libraries:ui:screens:login") implementation 'androidx.core:core-ktx:1.9.0' implementation "androidx.compose.ui:ui:$compose_version" diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 449a658744..b9e4d22fdd 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -11,12 +11,10 @@ android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.ElementX" - tools:targetApi="31"> + tools:targetApi="33"> + android:exported="true"> diff --git a/app/src/main/java/io/element/android/x/MainActivity.kt b/app/src/main/java/io/element/android/x/MainActivity.kt index 1e26862210..83797b5271 100644 --- a/app/src/main/java/io/element/android/x/MainActivity.kt +++ b/app/src/main/java/io/element/android/x/MainActivity.kt @@ -1,43 +1,16 @@ package io.element.android.x +import android.content.Intent import android.os.Bundle import androidx.activity.ComponentActivity -import androidx.activity.compose.setContent -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.material3.MaterialTheme -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 io.element.android.x.ui.theme.ElementXTheme +import io.element.android.x.ui.screen.login.LoginActivity class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContent { - ElementXTheme { - // A surface container using the 'background' color from the theme - Surface( - modifier = Modifier.fillMaxSize(), - color = MaterialTheme.colorScheme.background - ) { - Greeting("Android") - } - } - } + // Just start the LoginActivity for now. + // TODO if a session exist, start the room list + startActivity(Intent(this, LoginActivity::class.java)) + finish() } } - -@Composable -fun Greeting(name: String) { - Text(text = "Hello $name!") -} - -@Preview(showBackground = true) -@Composable -fun DefaultPreview() { - ElementXTheme { - Greeting("Android") - } -} \ No newline at end of file diff --git a/libraries/ui/screens/login/build.gradle b/libraries/ui/screens/login/build.gradle new file mode 100644 index 0000000000..3bf2b03084 --- /dev/null +++ b/libraries/ui/screens/login/build.gradle @@ -0,0 +1,57 @@ +plugins { + id 'com.android.library' + id 'org.jetbrains.kotlin.android' +} + +android { + namespace 'io.element.android.x.ui.screen.login' + compileSdk 33 + + defaultConfig { + minSdk 29 + targetSdk 33 + + consumerProguardFiles "consumer-rules.pro" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + buildFeatures { + compose true + } + composeOptions { + kotlinCompilerExtensionVersion compose_version + } + kotlinOptions { + jvmTarget = '1.8' + } +} + +dependencies { + implementation project(":libraries:ui:theme") + + implementation 'androidx.core:core-ktx:1.9.0' + implementation 'androidx.appcompat:appcompat:1.5.1' + + implementation 'com.google.android.material:material:1.6.1' + + implementation "androidx.compose.ui:ui:$compose_version" + implementation 'androidx.compose.material3:material3:1.0.0-rc01' + implementation "androidx.compose.ui:ui-tooling-preview:$compose_version" + debugImplementation "androidx.compose.ui:ui-tooling:$compose_version" + debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_version" + + implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.1' + implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.5.1" + implementation 'androidx.activity:activity-compose:1.6.0' + + implementation 'androidx.fragment:fragment-ktx:1.5.3' +} \ No newline at end of file diff --git a/libraries/ui/screens/login/src/main/AndroidManifest.xml b/libraries/ui/screens/login/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..eb40e709cb --- /dev/null +++ b/libraries/ui/screens/login/src/main/AndroidManifest.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/libraries/ui/screens/login/src/main/java/io/element/android/x/ui/screen/login/LoginActions.kt b/libraries/ui/screens/login/src/main/java/io/element/android/x/ui/screen/login/LoginActions.kt new file mode 100644 index 0000000000..2cd1b76adb --- /dev/null +++ b/libraries/ui/screens/login/src/main/java/io/element/android/x/ui/screen/login/LoginActions.kt @@ -0,0 +1,7 @@ +package io.element.android.x.ui.screen.login + +sealed interface LoginActions { + data class SetLogin(val login: String) : LoginActions + data class SetPassword(val password: String) : LoginActions + object Submit : LoginActions +} diff --git a/libraries/ui/screens/login/src/main/java/io/element/android/x/ui/screen/login/LoginActivity.kt b/libraries/ui/screens/login/src/main/java/io/element/android/x/ui/screen/login/LoginActivity.kt new file mode 100644 index 0000000000..799a1c025c --- /dev/null +++ b/libraries/ui/screens/login/src/main/java/io/element/android/x/ui/screen/login/LoginActivity.kt @@ -0,0 +1,62 @@ +package io.element.android.x.ui.screen.login + +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.activity.viewModels +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.runtime.collectAsState +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import io.element.android.x.ui.theme.ElementXTheme +import io.element.android.x.ui.theme.components.VectorButton +import io.element.android.x.ui.theme.components.VectorTextField + +class LoginActivity : ComponentActivity() { + + private val viewModel: LoginViewModel by viewModels() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + setContent { + ElementXTheme { + // A surface container using the 'background' color from the theme + Surface( + modifier = Modifier + .fillMaxSize() + .padding(16.dp), + color = MaterialTheme.colorScheme.background + ) { + Column( + modifier = Modifier.fillMaxSize() + ) { + val state = viewModel.state.collectAsState().value + VectorTextField( + value = state.login, + onValueChange = { + viewModel.handle(LoginActions.SetLogin(it)) + }) + VectorTextField( + value = state.password, + onValueChange = { + viewModel.handle(LoginActions.SetPassword(it)) + } + ) + VectorButton( + text = "Submit", + onClick = { + viewModel.handle(LoginActions.Submit) + }, + enabled = state.submitEnabled, + ) + } + } + } + } + } +} diff --git a/libraries/ui/screens/login/src/main/java/io/element/android/x/ui/screen/login/LoginViewModel.kt b/libraries/ui/screens/login/src/main/java/io/element/android/x/ui/screen/login/LoginViewModel.kt new file mode 100644 index 0000000000..565d7d8a38 --- /dev/null +++ b/libraries/ui/screens/login/src/main/java/io/element/android/x/ui/screen/login/LoginViewModel.kt @@ -0,0 +1,37 @@ +package io.element.android.x.ui.screen.login + +import androidx.lifecycle.ViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow + +class LoginViewModel : ViewModel() { + + private val _state = MutableStateFlow(LoginViewState()) + val state = _state.asStateFlow() + + fun handle(action: LoginActions) { + when (action) { + is LoginActions.SetLogin -> handleSetName(action) + is LoginActions.SetPassword -> handleSetPassword(action) + LoginActions.Submit -> handleSubmit() + } + } + + private fun handleSubmit() { + // TODO + } + + private fun handleSetPassword(action: LoginActions.SetPassword) { + _state.value = _state.value.copy( + password = action.password, + submitEnabled = _state.value.login.isNotEmpty() && action.password.isNotEmpty() + ) + } + + private fun handleSetName(action: LoginActions.SetLogin) { + _state.value = _state.value.copy( + login = action.login, + submitEnabled = action.login.isNotEmpty() && _state.value.password.isNotEmpty() + ) + } +} \ No newline at end of file diff --git a/libraries/ui/screens/login/src/main/java/io/element/android/x/ui/screen/login/LoginViewState.kt b/libraries/ui/screens/login/src/main/java/io/element/android/x/ui/screen/login/LoginViewState.kt new file mode 100644 index 0000000000..c0ccc5f86c --- /dev/null +++ b/libraries/ui/screens/login/src/main/java/io/element/android/x/ui/screen/login/LoginViewState.kt @@ -0,0 +1,7 @@ +package io.element.android.x.ui.screen.login + +data class LoginViewState( + val login: String = "", + val password: String = "", + val submitEnabled: Boolean = false, +) diff --git a/libraries/ui/theme/build.gradle b/libraries/ui/theme/build.gradle index e8821bc478..fcc9ed818b 100644 --- a/libraries/ui/theme/build.gradle +++ b/libraries/ui/theme/build.gradle @@ -45,4 +45,6 @@ dependencies { implementation "androidx.compose.ui:ui-tooling-preview:$compose_version" debugImplementation "androidx.compose.ui:ui-tooling:$compose_version" debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_version" + + implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.5.1" } \ No newline at end of file diff --git a/libraries/ui/theme/src/main/java/io/element/android/x/ui/theme/components/VectorButton.kt b/libraries/ui/theme/src/main/java/io/element/android/x/ui/theme/components/VectorButton.kt new file mode 100644 index 0000000000..6a237fc857 --- /dev/null +++ b/libraries/ui/theme/src/main/java/io/element/android/x/ui/theme/components/VectorButton.kt @@ -0,0 +1,16 @@ +package io.element.android.x.ui.theme.components + +import androidx.compose.material3.Button +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable + + +@Composable +fun VectorButton(text: String, enabled: Boolean, onClick: () -> Unit) { + Button( + onClick = onClick, + enabled = enabled, + ) { + Text(text = text) + } +} diff --git a/libraries/ui/theme/src/main/java/io/element/android/x/ui/theme/components/VectorTextField.kt b/libraries/ui/theme/src/main/java/io/element/android/x/ui/theme/components/VectorTextField.kt new file mode 100644 index 0000000000..a46e61a1b5 --- /dev/null +++ b/libraries/ui/theme/src/main/java/io/element/android/x/ui/theme/components/VectorTextField.kt @@ -0,0 +1,18 @@ +package io.element.android.x.ui.theme.components + +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.OutlinedTextField +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier + + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun VectorTextField(value: String, onValueChange: (String) -> Unit) { + OutlinedTextField( + value = value, + onValueChange = onValueChange, + modifier = Modifier.fillMaxWidth() + ) +} diff --git a/settings.gradle b/settings.gradle index a795454907..763f4cc500 100644 --- a/settings.gradle +++ b/settings.gradle @@ -15,3 +15,4 @@ dependencyResolutionManagement { rootProject.name = "ElementX" include ':app' include ':libraries:ui:theme' +include ':libraries:ui:screens:login'