Browse Source

Merge remote-tracking branch 'origin/develop' into develop

feature/bma/flipper
Benoit Marty 2 years ago
parent
commit
3ceae18521
  1. 29
      .idea/inspectionProfiles/Project_Default.xml
  2. 25
      .idea/jarRepositories.xml
  3. 3
      .idea/misc.xml
  4. 42
      app/build.gradle
  5. 2
      app/src/main/java/io/element/android/x/ElementXApplication.kt
  6. 46
      app/src/main/java/io/element/android/x/MainActivity.kt
  7. 2
      app/src/main/java/io/element/android/x/MainViewModel.kt
  8. 28
      app/src/main/java/io/element/android/x/Navigation.kt
  9. 0
      app/src/main/res/drawable-v24/ic_launcher_foreground.xml
  10. 0
      app/src/main/res/drawable/ic_launcher_background.xml
  11. 0
      app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
  12. 0
      app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
  13. 0
      app/src/main/res/mipmap-hdpi/ic_launcher.webp
  14. 0
      app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
  15. 0
      app/src/main/res/mipmap-mdpi/ic_launcher.webp
  16. 0
      app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
  17. 0
      app/src/main/res/mipmap-xhdpi/ic_launcher.webp
  18. 0
      app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
  19. 0
      app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
  20. 0
      app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
  21. 0
      app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
  22. 0
      app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
  23. 0
      app/src/main/res/values/colors.xml
  24. 0
      app/src/main/res/values/strings.xml
  25. 0
      app/src/main/res/values/themes.xml
  26. 0
      app/src/main/res/xml/backup_rules.xml
  27. 0
      app/src/main/res/xml/data_extraction_rules.xml
  28. 8
      build.gradle
  29. 1
      features/login/.gitignore
  30. 18
      features/login/build.gradle.kts
  31. 0
      features/login/consumer-rules.pro
  32. 21
      features/login/proguard-rules.pro
  33. 24
      features/login/src/androidTest/java/io/element/android/x/features/login/ExampleInstrumentedTest.kt
  34. 0
      features/login/src/main/AndroidManifest.xml
  35. 2
      features/login/src/main/java/io/element/android/x/features/login/LoginActions.kt
  36. 133
      features/login/src/main/java/io/element/android/x/features/login/LoginScreen.kt
  37. 51
      features/login/src/main/java/io/element/android/x/features/login/LoginViewModel.kt
  38. 10
      features/login/src/main/java/io/element/android/x/features/login/LoginViewState.kt
  39. 22
      features/login/src/main/res/drawable/element_logo_green.xml
  40. 17
      features/login/src/test/java/io/element/android/x/features/login/ExampleUnitTest.kt
  41. 1
      features/messages/.gitignore
  42. 41
      features/messages/build.gradle
  43. 0
      features/messages/consumer-rules.pro
  44. 21
      features/messages/proguard-rules.pro
  45. 24
      features/messages/src/androidTest/java/io/element/android/x/features/messages/ExampleInstrumentedTest.kt
  46. 4
      features/messages/src/main/AndroidManifest.xml
  47. 17
      features/messages/src/test/java/io/element/android/x/features/messages/ExampleUnitTest.kt
  48. 1
      features/roomlist/.gitignore
  49. 18
      features/roomlist/build.gradle.kts
  50. 0
      features/roomlist/consumer-rules.pro
  51. 21
      features/roomlist/proguard-rules.pro
  52. 24
      features/roomlist/src/androidTest/java/io/element/android/x/features/roomlist/ExampleInstrumentedTest.kt
  53. 4
      features/roomlist/src/main/AndroidManifest.xml
  54. 2
      features/roomlist/src/main/java/io/element/android/x/features/roomlist/MatrixUser.kt
  55. 3
      features/roomlist/src/main/java/io/element/android/x/features/roomlist/RoomListActions.kt
  56. 70
      features/roomlist/src/main/java/io/element/android/x/features/roomlist/RoomListScreen.kt
  57. 11
      features/roomlist/src/main/java/io/element/android/x/features/roomlist/RoomListViewModel.kt
  58. 2
      features/roomlist/src/main/java/io/element/android/x/features/roomlist/RoomListViewState.kt
  59. 17
      features/roomlist/src/test/java/io/element/android/x/features/roomlist/ExampleUnitTest.kt
  60. 67
      gradle/libs.versions.toml
  61. 8
      libraries/core/build.gradle
  62. 1
      libraries/designsystem/.gitignore
  63. 12
      libraries/designsystem/build.gradle.kts
  64. 0
      libraries/designsystem/consumer-rules.pro
  65. 21
      libraries/designsystem/proguard-rules.pro
  66. 4
      libraries/designsystem/src/main/AndroidManifest.xml
  67. 2
      libraries/designsystem/src/main/java/io/element/android/x/designsystem/Color.kt
  68. 2
      libraries/designsystem/src/main/java/io/element/android/x/designsystem/Theme.kt
  69. 2
      libraries/designsystem/src/main/java/io/element/android/x/designsystem/Type.kt
  70. 0
      libraries/designsystem/src/main/java/io/element/android/x/designsystem/components/Avatar.kt
  71. 2
      libraries/designsystem/src/main/java/io/element/android/x/designsystem/components/VectorButton.kt
  72. 2
      libraries/designsystem/src/main/java/io/element/android/x/designsystem/components/VectorTextField.kt
  73. 0
      libraries/matrix/build.gradle
  74. 0
      libraries/matrix/src/main/AndroidManifest.xml
  75. 3
      libraries/matrix/src/main/java/io/element/android/x/matrix/LogTag.kt
  76. 6
      libraries/matrix/src/main/java/io/element/android/x/matrix/Matrix.kt
  77. 4
      libraries/matrix/src/main/java/io/element/android/x/matrix/MatrixClient.kt
  78. 2
      libraries/matrix/src/main/java/io/element/android/x/matrix/MatrixInstance.kt
  79. 2
      libraries/matrix/src/main/java/io/element/android/x/matrix/RoomWrapper.kt
  80. 10
      libraries/matrix/src/main/java/io/element/android/x/matrix/store/SessionStore.kt
  81. 4
      libraries/matrix/src/main/java/io/element/android/x/matrix/util/Error.kt
  82. 3
      libraries/sdk/matrix/src/main/java/io/element/android/x/sdk/matrix/LogTag.kt
  83. 61
      libraries/ui/screens/login/build.gradle
  84. 129
      libraries/ui/screens/login/src/main/java/io/element/android/x/ui/screen/login/LoginActivity.kt
  85. 22
      libraries/ui/screens/login/src/main/res/drawable/element_logo_green.xml
  86. 62
      libraries/ui/screens/roomlist/build.gradle
  87. 126
      libraries/ui/screens/roomlist/src/main/java/io/element/android/x/ui/screen/roomlist/RoomListActivity.kt
  88. 52
      libraries/ui/theme/build.gradle
  89. 14
      plugins/build.gradle.kts
  90. 10
      plugins/settings.gradle.kts
  91. 17
      plugins/src/main/java/Versions.kt
  92. 50
      plugins/src/main/java/extension/CommonExtension.kt
  93. 29
      plugins/src/main/java/io.element.android-compose.gradle.kts
  94. 12
      plugins/src/main/java/io.element.android-library.gradle.kts
  95. 16
      settings.gradle.kts

29
.idea/inspectionProfiles/Project_Default.xml

@ -0,0 +1,29 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="PreviewAnnotationInFunctionWithParameters" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewFontScaleMustBeGreaterThanZero" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewMultipleParameterProviders" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewPickerAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
</profile>
</component>

25
.idea/jarRepositories.xml

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="MavenRepo" />
<option name="name" value="MavenRepo" />
<option name="url" value="https://repo.maven.apache.org/maven2/" />
</remote-repository>
<remote-repository>
<option name="id" value="Google" />
<option name="name" value="Google" />
<option name="url" value="https://dl.google.com/dl/android/maven2/" />
</remote-repository>
</component>
</project>

3
.idea/misc.xml

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="Android Studio default JDK" project-jdk-type="JavaSDK"> <component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="11" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" /> <output url="file://$PROJECT_DIR$/build/classes" />
</component> </component>
<component name="ProjectType"> <component name="ProjectType">

42
app/build.gradle

@ -1,6 +1,7 @@
plugins { plugins {
id 'com.android.application' id 'com.android.application'
id 'org.jetbrains.kotlin.android' id 'org.jetbrains.kotlin.android'
id 'com.google.devtools.ksp' version '1.7.20-1.0.7'
} }
android { android {
@ -36,7 +37,7 @@ android {
compose true compose true
} }
composeOptions { composeOptions {
kotlinCompilerExtensionVersion compose_version kotlinCompilerExtensionVersion "1.3.2"
} }
packagingOptions { packagingOptions {
resources { resources {
@ -44,6 +45,14 @@ android {
} }
} }
applicationVariants.all { variant ->
kotlin.sourceSets {
getByName(variant.name) {
kotlin.srcDir("build/generated/ksp/${variant.name}/kotlin")
}
}
}
signingConfigs { signingConfigs {
debug { debug {
keyAlias 'androiddebugkey' keyAlias 'androiddebugkey'
@ -55,19 +64,26 @@ android {
} }
dependencies { dependencies {
implementation project(":libraries:ui:theme") implementation project(":libraries:designsystem")
implementation project(":libraries:ui:screens:login") implementation project(":libraries:matrix")
implementation project(":libraries:ui:screens:roomlist") implementation project(":features:login")
implementation project(":libraries:sdk:matrix") implementation project(":features:roomlist")
implementation 'io.github.raamcosta.compose-destinations:core:1.7.23-beta'
ksp 'io.github.raamcosta.compose-destinations:ksp:1.7.23-beta'
def composeBom = platform('androidx.compose:compose-bom:2022.10.00')
implementation composeBom
androidTestImplementation composeBom
implementation 'androidx.core:core-ktx:1.9.0' implementation 'androidx.core:core-ktx:1.9.0'
implementation "androidx.compose.ui:ui:$compose_version" implementation "androidx.compose.ui:ui"
implementation 'androidx.compose.material3:material3:1.0.0-rc01' implementation 'androidx.compose.material3:material3'
implementation "androidx.compose.ui:ui-tooling-preview:$compose_version" implementation "androidx.compose.ui:ui-tooling-preview"
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.1' implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.1'
implementation 'androidx.activity:activity-compose:1.6.0' implementation 'androidx.activity:activity-compose:1.6.1'
debugImplementation "androidx.compose.ui:ui-tooling:$compose_version" implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.5.1'
debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_version" debugImplementation "androidx.compose.ui:ui-tooling"
debugImplementation "androidx.compose.ui:ui-test-manifest"
implementation 'com.airbnb.android:mavericks-compose:2.7.0' implementation 'com.airbnb.android:mavericks-compose:3.0.1'
} }

2
app/src/main/java/io/element/android/x/ElementXApplication.kt

@ -2,7 +2,7 @@ package io.element.android.x
import android.app.Application import android.app.Application
import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.Mavericks
import io.element.android.x.sdk.matrix.MatrixInstance import io.element.android.x.matrix.MatrixInstance
class ElementXApplication : Application() { class ElementXApplication : Application() {

46
app/src/main/java/io/element/android/x/MainActivity.kt

@ -1,41 +1,41 @@
package io.element.android.x package io.element.android.x
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.compose.setContent
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.lifecycle.lifecycleScope import androidx.compose.runtime.Composable
import io.element.android.x.ui.screen.login.LoginActivity import com.ramcosta.composedestinations.DestinationsNavHost
import io.element.android.x.ui.screen.roomlist.RoomListActivity import com.ramcosta.composedestinations.rememberNavHostEngine
import kotlinx.coroutines.launch import io.element.android.x.designsystem.ElementXTheme
import io.element.android.x.destinations.LoginScreenNavigationDestination
import kotlinx.coroutines.runBlocking
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {
private val launcher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if (it.resultCode == RESULT_OK) {
// Launch the room Activity and finish
startRoomActivityAndFinish()
} else {
finish()
}
}
private val viewModel: MainViewModel by viewModels() private val viewModel: MainViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
lifecycleScope.launch { setContent {
if (viewModel.hasSession()) { ElementXTheme {
startRoomActivityAndFinish() MainScreen(viewModel = viewModel)
} else {
launcher.launch(Intent(this@MainActivity, LoginActivity::class.java))
} }
} }
} }
}
private fun startRoomActivityAndFinish() { @Composable
startActivity(Intent(this, RoomListActivity::class.java)) private fun MainScreen(viewModel: MainViewModel) {
finish() val engine = rememberNavHostEngine()
val navController = engine.rememberNavController()
val startRoute = runBlocking {
if (!viewModel.hasSession()) LoginScreenNavigationDestination else NavGraphs.root.startRoute
} }
DestinationsNavHost(
engine = engine,
navController = navController,
navGraph = NavGraphs.root,
startRoute = startRoute
)
} }

2
app/src/main/java/io/element/android/x/MainViewModel.kt

@ -1,7 +1,7 @@
package io.element.android.x package io.element.android.x
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import io.element.android.x.sdk.matrix.MatrixInstance import io.element.android.x.matrix.MatrixInstance
class MainViewModel : ViewModel() { class MainViewModel : ViewModel() {
private val matrix = MatrixInstance.getInstance() private val matrix = MatrixInstance.getInstance()

28
app/src/main/java/io/element/android/x/Navigation.kt

@ -0,0 +1,28 @@
package io.element.android.x
import androidx.compose.runtime.Composable
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootNavGraph
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import io.element.android.x.destinations.RoomListScreenNavigationDestination
import io.element.android.x.features.login.LoginScreen
import io.element.android.x.features.roomlist.RoomListScreen
@Destination
@Composable
fun LoginScreenNavigation(navigator: DestinationsNavigator) {
LoginScreen(
onLoginWithSuccess = {
navigator.clearBackStack(RoomListScreenNavigationDestination)
}
)
}
@RootNavGraph(start = true)
@Destination
@Composable
fun RoomListScreenNavigation() {
RoomListScreen()
}

0
libraries/ui/theme/src/main/res/drawable-v24/ic_launcher_foreground.xml → app/src/main/res/drawable-v24/ic_launcher_foreground.xml

0
libraries/ui/theme/src/main/res/drawable/ic_launcher_background.xml → app/src/main/res/drawable/ic_launcher_background.xml

0
libraries/ui/theme/src/main/res/mipmap-anydpi-v26/ic_launcher.xml → app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml

0
libraries/ui/theme/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml → app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml

0
libraries/ui/theme/src/main/res/mipmap-hdpi/ic_launcher.webp → app/src/main/res/mipmap-hdpi/ic_launcher.webp

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

0
libraries/ui/theme/src/main/res/mipmap-hdpi/ic_launcher_round.webp → app/src/main/res/mipmap-hdpi/ic_launcher_round.webp

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

0
libraries/ui/theme/src/main/res/mipmap-mdpi/ic_launcher.webp → app/src/main/res/mipmap-mdpi/ic_launcher.webp

Before

Width:  |  Height:  |  Size: 982 B

After

Width:  |  Height:  |  Size: 982 B

0
libraries/ui/theme/src/main/res/mipmap-mdpi/ic_launcher_round.webp → app/src/main/res/mipmap-mdpi/ic_launcher_round.webp

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

0
libraries/ui/theme/src/main/res/mipmap-xhdpi/ic_launcher.webp → app/src/main/res/mipmap-xhdpi/ic_launcher.webp

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

0
libraries/ui/theme/src/main/res/mipmap-xhdpi/ic_launcher_round.webp → app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

0
libraries/ui/theme/src/main/res/mipmap-xxhdpi/ic_launcher.webp → app/src/main/res/mipmap-xxhdpi/ic_launcher.webp

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

0
libraries/ui/theme/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp → app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 5.8 KiB

0
libraries/ui/theme/src/main/res/mipmap-xxxhdpi/ic_launcher.webp → app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

0
libraries/ui/theme/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp → app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 7.6 KiB

0
libraries/ui/theme/src/main/res/values/colors.xml → app/src/main/res/values/colors.xml

0
libraries/ui/theme/src/main/res/values/strings.xml → app/src/main/res/values/strings.xml

0
libraries/ui/theme/src/main/res/values/themes.xml → app/src/main/res/values/themes.xml

0
libraries/ui/theme/src/main/res/xml/backup_rules.xml → app/src/main/res/xml/backup_rules.xml

0
libraries/ui/theme/src/main/res/xml/data_extraction_rules.xml → app/src/main/res/xml/data_extraction_rules.xml

8
build.gradle

@ -1,12 +1,8 @@
buildscript { // Top-level build file where you can add configuration options common to all sub-projects/modules.
ext {
compose_version = '1.3.0-rc01'
}
}// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins { plugins {
id 'com.android.application' version '7.3.0' apply false id 'com.android.application' version '7.3.0' apply false
id 'com.android.library' version '7.3.0' apply false id 'com.android.library' version '7.3.0' apply false
id 'org.jetbrains.kotlin.android' version '1.7.10' apply false id 'org.jetbrains.kotlin.android' version '1.7.20' apply false
} }
task clean(type: Delete) { task clean(type: Delete) {

1
features/login/.gitignore vendored

@ -0,0 +1 @@
/build

18
features/login/build.gradle.kts

@ -0,0 +1,18 @@
plugins {
id("io.element.android-compose")
}
android {
namespace = "io.element.android.x.features.login"
}
dependencies {
implementation(project(":libraries:core"))
implementation(project(":libraries:matrix"))
implementation(project(":libraries:designsystem"))
implementation("com.airbnb.android:mavericks-compose:3.0.1")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.3")
androidTestImplementation("androidx.test.espresso:espresso-core:3.4.0")
}

0
features/login/consumer-rules.pro

21
features/login/proguard-rules.pro vendored

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.kts.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

24
features/login/src/androidTest/java/io/element/android/x/features/login/ExampleInstrumentedTest.kt

@ -0,0 +1,24 @@
package io.element.android.x.features.login
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("io.element.android.x.features.login.test", appContext.packageName)
}
}

0
libraries/ui/theme/src/main/AndroidManifest.xml → features/login/src/main/AndroidManifest.xml

2
libraries/ui/screens/login/src/main/java/io/element/android/x/ui/screen/login/LoginActions.kt → features/login/src/main/java/io/element/android/x/features/login/LoginActions.kt

@ -1,4 +1,4 @@
package io.element.android.x.ui.screen.login package io.element.android.x.features.login
sealed interface LoginActions { sealed interface LoginActions {
data class SetHomeserver(val homeserver: String) : LoginActions data class SetHomeserver(val homeserver: String) : LoginActions

133
features/login/src/main/java/io/element/android/x/features/login/LoginScreen.kt

@ -0,0 +1,133 @@
@file:OptIn(ExperimentalMaterial3Api::class)
package io.element.android.x.features.login
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.compose.collectAsState
import com.airbnb.mvrx.compose.mavericksViewModel
import io.element.android.x.designsystem.components.VectorButton
@Composable
fun LoginScreen(
viewModel: LoginViewModel = mavericksViewModel(),
onLoginWithSuccess: () -> Unit = { }
) {
val state: LoginViewState by viewModel.collectAsState()
LoginContent(
state = state,
onHomeserverChanged = { viewModel.handle(LoginActions.SetHomeserver(it)) },
onLoginChanged = { viewModel.handle(LoginActions.SetLogin(it)) },
onPasswordChanged = { viewModel.handle(LoginActions.SetPassword(it)) },
onSubmitClicked = { viewModel.handle(LoginActions.Submit) },
onLoginWithSuccess = onLoginWithSuccess
)
}
@Composable
fun LoginContent(
state: LoginViewState,
onHomeserverChanged: (String) -> Unit,
onLoginChanged: (String) -> Unit,
onPasswordChanged: (String) -> Unit,
onSubmitClicked: () -> Unit,
onLoginWithSuccess: () -> Unit
) {
Surface(color = MaterialTheme.colorScheme.background) {
Box(
modifier = Modifier
.fillMaxSize()
.padding(16.dp)
) {
Column(
modifier = Modifier.fillMaxSize(),
) {
val isError = state.isLoggedIn is Fail
Image(
painterResource(id = R.drawable.element_logo_green),
contentDescription = null,
modifier = Modifier
.align(Alignment.CenterHorizontally)
.padding(40.dp)
)
OutlinedTextField(
value = state.homeserver,
modifier = Modifier.fillMaxWidth(),
onValueChange = {
onHomeserverChanged(it)
},
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Uri,
),
)
OutlinedTextField(
value = state.login,
modifier = Modifier
.fillMaxWidth()
.padding(top = 8.dp),
onValueChange = {
onLoginChanged(it)
},
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Text,
),
)
OutlinedTextField(
value = state.password,
modifier = Modifier
.fillMaxWidth()
.padding(top = 8.dp),
onValueChange = {
onPasswordChanged(it)
},
isError = isError,
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Password,
imeAction = ImeAction.Send,
),
)
if (isError) {
Text(
text = (state.isLoggedIn as? Fail)?.toString().orEmpty(),
color = MaterialTheme.colorScheme.error,
style = MaterialTheme.typography.bodySmall,
modifier = Modifier.padding(start = 16.dp)
)
}
VectorButton(
text = "Submit",
onClick = {
onSubmitClicked()
},
enabled = state.submitEnabled,
modifier = Modifier
.align(Alignment.End)
.padding(top = 16.dp)
)
if (state.isLoggedIn is Loading) {
// FIXME This does not work, we never enter this if block
CircularProgressIndicator(
modifier = Modifier.align(Alignment.CenterHorizontally)
)
}
if (state.isLoggedIn is Success) {
onLoginWithSuccess()
}
}
}
}
}

51
libraries/ui/screens/login/src/main/java/io/element/android/x/ui/screen/login/LoginViewModel.kt → features/login/src/main/java/io/element/android/x/features/login/LoginViewModel.kt

@ -1,11 +1,11 @@
package io.element.android.x.ui.screen.login package io.element.android.x.features.login
import android.util.Log import android.util.Log
import com.airbnb.mvrx.Fail import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Loading import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.MavericksViewModel import com.airbnb.mvrx.MavericksViewModel
import com.airbnb.mvrx.Success import com.airbnb.mvrx.Success
import io.element.android.x.sdk.matrix.MatrixInstance import io.element.android.x.matrix.MatrixInstance
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
class LoginViewModel(initialState: LoginViewState) : class LoginViewModel(initialState: LoginViewState) :
@ -13,28 +13,6 @@ class LoginViewModel(initialState: LoginViewState) :
private val matrix = MatrixInstance.getInstance() private val matrix = MatrixInstance.getInstance()
init {
observeState()
}
private fun observeState() {
onEach(
LoginViewState::homeserver,
LoginViewState::login,
LoginViewState::password,
LoginViewState::isLoggedIn,
) { homeserver, login, password, isLoggedIn ->
setState {
copy(
submitEnabled = homeserver.isNotEmpty() &&
login.isNotEmpty() &&
password.isNotEmpty() &&
isLoggedIn !is Loading
)
}
}
}
fun handle(action: LoginActions) { fun handle(action: LoginActions) {
when (action) { when (action) {
is LoginActions.SetHomeserver -> handleSetHomeserver(action) is LoginActions.SetHomeserver -> handleSetHomeserver(action)
@ -44,14 +22,6 @@ class LoginViewModel(initialState: LoginViewState) :
} }
} }
private fun handleSetHomeserver(action: LoginActions.SetHomeserver) {
setState {
copy(
homeserver = action.homeserver
)
}
}
private fun handleSubmit() = withState { state -> private fun handleSubmit() = withState { state ->
viewModelScope.launch { viewModelScope.launch {
setState { copy(isLoggedIn = Loading()) } setState { copy(isLoggedIn = Loading()) }
@ -65,20 +35,15 @@ class LoginViewModel(initialState: LoginViewState) :
} }
} }
private fun handleSetPassword(action: LoginActions.SetPassword) { private fun handleSetHomeserver(action: LoginActions.SetHomeserver) {
setState { setState { copy(homeserver = action.homeserver) }
copy(
password = action.password
)
} }
private fun handleSetPassword(action: LoginActions.SetPassword) {
setState { copy(password = action.password) }
} }
private fun handleSetName(action: LoginActions.SetLogin) { private fun handleSetName(action: LoginActions.SetLogin) {
setState { setState { copy(login = action.login) }
copy(
login = action.login
)
}
} }
} }

10
libraries/ui/screens/login/src/main/java/io/element/android/x/ui/screen/login/LoginViewState.kt → features/login/src/main/java/io/element/android/x/features/login/LoginViewState.kt

@ -1,4 +1,4 @@
package io.element.android.x.ui.screen.login package io.element.android.x.features.login
import com.airbnb.mvrx.Async import com.airbnb.mvrx.Async
import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.MavericksState
@ -8,6 +8,10 @@ data class LoginViewState(
val homeserver: String = "matrix.org", val homeserver: String = "matrix.org",
val login: String = "", val login: String = "",
val password: String = "", val password: String = "",
val submitEnabled: Boolean = false,
val isLoggedIn: Async<Unit> = Uninitialized, val isLoggedIn: Async<Unit> = Uninitialized,
) : MavericksState ) : MavericksState {
val submitEnabled = homeserver.isNotEmpty() && login.isNotEmpty() && password.isNotEmpty()
}

22
features/login/src/main/res/drawable/element_logo_green.xml

@ -0,0 +1,22 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="64dp"
android:height="64dp"
android:viewportWidth="64"
android:viewportHeight="64">
<path
android:pathData="M23.04,3.84C23.04,1.7192 24.7593,0 26.88,0C41.0185,0 52.48,11.4615 52.48,25.6C52.48,27.7208 50.7608,29.44 48.64,29.44C46.5193,29.44 44.8,27.7208 44.8,25.6C44.8,15.7031 36.777,7.68 26.88,7.68C24.7593,7.68 23.04,5.9608 23.04,3.84Z"
android:fillColor="#0DBD8B"
android:fillType="evenOdd"/>
<path
android:pathData="M40.96,60.16C40.96,62.2808 39.2407,64 37.12,64C22.9815,64 11.52,52.5385 11.52,38.4C11.52,36.2792 13.2392,34.56 15.36,34.56C17.4807,34.56 19.2,36.2792 19.2,38.4C19.2,48.2969 27.223,56.32 37.12,56.32C39.2407,56.32 40.96,58.0392 40.96,60.16Z"
android:fillColor="#0DBD8B"
android:fillType="evenOdd"/>
<path
android:pathData="M3.84,40.96C1.7192,40.96 -0,39.2407 -0,37.12C-0,22.9815 11.4615,11.52 25.6,11.52C27.7208,11.52 29.44,13.2392 29.44,15.36C29.44,17.4807 27.7208,19.2 25.6,19.2C15.7031,19.2 7.68,27.223 7.68,37.12C7.68,39.2407 5.9608,40.96 3.84,40.96Z"
android:fillColor="#0DBD8B"
android:fillType="evenOdd"/>
<path
android:pathData="M60.16,23.04C62.2808,23.04 64,24.7593 64,26.88C64,41.0185 52.5385,52.48 38.4,52.48C36.2792,52.48 34.56,50.7608 34.56,48.64C34.56,46.5193 36.2792,44.8 38.4,44.8C48.2969,44.8 56.32,36.777 56.32,26.88C56.32,24.7593 58.0392,23.04 60.16,23.04Z"
android:fillColor="#0DBD8B"
android:fillType="evenOdd"/>
</vector>

17
features/login/src/test/java/io/element/android/x/features/login/ExampleUnitTest.kt

@ -0,0 +1,17 @@
package io.element.android.x.features.login
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}

1
features/messages/.gitignore vendored

@ -0,0 +1 @@
/build

41
features/messages/build.gradle

@ -0,0 +1,41 @@
plugins {
id 'com.android.library'
id 'org.jetbrains.kotlin.android'
}
android {
namespace 'io.element.android.x.features.messages'
compileSdk 32
defaultConfig {
minSdk 24
targetSdk 32
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
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
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'com.google.android.material:material:1.5.0'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}

0
features/messages/consumer-rules.pro

21
features/messages/proguard-rules.pro vendored

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

24
features/messages/src/androidTest/java/io/element/android/x/features/messages/ExampleInstrumentedTest.kt

@ -0,0 +1,24 @@
package io.element.android.x.features.messages
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("io.element.android.x.features.messages.test", appContext.packageName)
}
}

4
libraries/ui/screens/login/src/main/AndroidManifest.xml → features/messages/src/main/AndroidManifest.xml

@ -1,8 +1,4 @@
<?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">
<application>
<activity android:name="LoginActivity" />
</application>
</manifest> </manifest>

17
features/messages/src/test/java/io/element/android/x/features/messages/ExampleUnitTest.kt

@ -0,0 +1,17 @@
package io.element.android.x.features.messages
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}

1
features/roomlist/.gitignore vendored

@ -0,0 +1 @@
/build

18
features/roomlist/build.gradle.kts

@ -0,0 +1,18 @@
plugins {
id("io.element.android-compose")
}
android {
namespace = "io.element.android.x.features.roomlist"
}
dependencies {
implementation(project(":libraries:core"))
implementation(project(":libraries:matrix"))
implementation(project(":libraries:designsystem"))
implementation("com.airbnb.android:mavericks-compose:3.0.1")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.3")
androidTestImplementation("androidx.test.espresso:espresso-core:3.4.0")
}

0
features/roomlist/consumer-rules.pro

21
features/roomlist/proguard-rules.pro vendored

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

24
features/roomlist/src/androidTest/java/io/element/android/x/features/roomlist/ExampleInstrumentedTest.kt

@ -0,0 +1,24 @@
package io.element.android.x.features.roomlist
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("io.element.android.x.features.roomlist.test", appContext.packageName)
}
}

4
libraries/ui/screens/roomlist/src/main/AndroidManifest.xml → features/roomlist/src/main/AndroidManifest.xml

@ -1,8 +1,4 @@
<?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">
<application>
<activity android:name="RoomListActivity" />
</application>
</manifest> </manifest>

2
libraries/ui/screens/roomlist/src/main/java/io/element/android/x/ui/screen/roomlist/MatrixUser.kt → features/roomlist/src/main/java/io/element/android/x/features/roomlist/MatrixUser.kt

@ -1,4 +1,4 @@
package io.element.android.x.ui.screen.roomlist package io.element.android.x.features.roomlist
data class MatrixUser( data class MatrixUser(
val username: String? = null, val username: String? = null,

3
libraries/ui/screens/roomlist/src/main/java/io/element/android/x/ui/screen/roomlist/RoomListActions.kt → features/roomlist/src/main/java/io/element/android/x/features/roomlist/RoomListActions.kt

@ -1,7 +1,6 @@
package io.element.android.x.ui.screen.roomlist package io.element.android.x.features.roomlist
sealed interface RoomListActions { sealed interface RoomListActions {
object Init : RoomListActions
object LoadMore : RoomListActions object LoadMore : RoomListActions
object Logout : RoomListActions object Logout : RoomListActions
} }

70
features/roomlist/src/main/java/io/element/android/x/features/roomlist/RoomListScreen.kt

@ -0,0 +1,70 @@
package io.element.android.x.features.roomlist
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.compose.collectAsState
import com.airbnb.mvrx.compose.mavericksViewModel
import org.matrix.rustcomponents.sdk.Room
@Composable
fun RoomListScreen(
viewModel: RoomListViewModel = mavericksViewModel(),
onRoomClicked: (String) -> Unit = { }
) {
val state by viewModel.collectAsState()
RoomListContent(state, onRoomClicked)
}
@Composable
fun RoomListContent(
state: RoomListViewState,
onRoomClicked: (String) -> Unit
) {
Surface(color = MaterialTheme.colorScheme.background) {
Column(
modifier = Modifier.fillMaxSize()
) {
val rooms = state.rooms
if (rooms is Success) {
LazyColumn {
items(rooms()) { room ->
RoomItem(room = room) {
onRoomClicked(it)
}
}
}
}
}
}
}
@Composable
private fun RoomItem(
modifier: Modifier = Modifier,
room: Room,
onClick: (String) -> Unit
) {
Row(verticalAlignment = Alignment.CenterVertically,
modifier = modifier
.clickable {
onClick(room.id())
}
.fillMaxWidth()
) {
Column(modifier = modifier.padding(8.dp)) {
Text(text = "Room: ${room.name() ?: room.id()}")
Text(text = if (room.isDirect()) "Direct" else "Room")
}
}
}

11
libraries/ui/screens/roomlist/src/main/java/io/element/android/x/ui/screen/roomlist/RoomListViewModel.kt → features/roomlist/src/main/java/io/element/android/x/features/roomlist/RoomListViewModel.kt

@ -1,12 +1,12 @@
package io.element.android.x.ui.screen.roomlist package io.element.android.x.features.roomlist
import com.airbnb.mvrx.Fail import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Loading import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.MavericksViewModel import com.airbnb.mvrx.MavericksViewModel
import com.airbnb.mvrx.Success import com.airbnb.mvrx.Success
import io.element.android.x.core.data.tryOrNull import io.element.android.x.core.data.tryOrNull
import io.element.android.x.sdk.matrix.MatrixClient import io.element.android.x.matrix.MatrixClient
import io.element.android.x.sdk.matrix.MatrixInstance import io.element.android.x.matrix.MatrixInstance
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.matrix.rustcomponents.sdk.Room import org.matrix.rustcomponents.sdk.Room
import org.matrix.rustcomponents.sdk.StoppableSpawn import org.matrix.rustcomponents.sdk.StoppableSpawn
@ -19,9 +19,12 @@ class RoomListViewModel(initialState: RoomListViewState) :
private var sync: StoppableSpawn? = null private var sync: StoppableSpawn? = null
private val matrix = MatrixInstance.getInstance() private val matrix = MatrixInstance.getInstance()
init {
handleInit()
}
fun handle(action: RoomListActions) { fun handle(action: RoomListActions) {
when (action) { when (action) {
RoomListActions.Init -> handleInit()
RoomListActions.LoadMore -> TODO() RoomListActions.LoadMore -> TODO()
RoomListActions.Logout -> handleLogout() RoomListActions.Logout -> handleLogout()
} }

2
libraries/ui/screens/roomlist/src/main/java/io/element/android/x/ui/screen/roomlist/RoomListViewState.kt → features/roomlist/src/main/java/io/element/android/x/features/roomlist/RoomListViewState.kt

@ -1,4 +1,4 @@
package io.element.android.x.ui.screen.roomlist package io.element.android.x.features.roomlist
import com.airbnb.mvrx.Async import com.airbnb.mvrx.Async
import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.MavericksState

17
features/roomlist/src/test/java/io/element/android/x/features/roomlist/ExampleUnitTest.kt

@ -0,0 +1,17 @@
package io.element.android.x.features.roomlist
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}

67
gradle/libs.versions.toml

@ -0,0 +1,67 @@
[versions]
# Project
android_gradle_plugin = "7.3.1"
kotlin = "1.7.20"
# AndroidX
material = "1.6.1"
corektx = "1.9.0"
datastore = "1.0.0"
# Compose
compose_compiler = "1.3.2"
compose_bom = "2022.10.00"
# Coroutines
coroutines = "1.6.4"
# Accompanist
accompanist = "0.27.0"
# Test
test_junit = "4.13.2"
test_runner = "1.4.0"
test_core = "1.4.0"
test_mockk = "1.13.2"
test_uiautomator = "2.2.0"
test_junitext = "1.1.3"
test_barista = "4.2.0"
test_hamcrest = "2.2"
test_orchestrator = "1.4.1"
[libraries]
# Project
android_gradle_plugin = { module = "com.android.tools.build:gradle", version.ref = "android_gradle_plugin" }
kotlin_gradle_plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
# AndroidX
androidx_material = { module = "com.google.android.material:material", version.ref = "material" }
androidx_corektx = { module = "androidx.core:core-ktx", version.ref = "corektx" }
androidx_datastore = { module = "androidx.datastore:datastore-preferences", version.ref = "datastore" }
androidx_compose_bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose_bom" }
androidx_compose_foundation = { group = "androidx.compose.foundation", name = "foundation" }
# Coroutines
coroutines_core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" }
coroutines_test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "coroutines" }
# Accompanist
accompanist_animation = { module = "com.google.accompanist:accompanist-navigation-animation", version.ref = "accompanist" }
accompanist_permission = { module = "com.google.accompanist:accompanist-permissions", version.ref = "accompanist" }
accompanist_material = { module = "com.google.accompanist:accompanist-navigation-material", version.ref = "accompanist" }
# Test
test_junit = { module = "junit:junit", version.ref = "test_junit" }
test_runner = { module = "androidx.test:runner", version.ref = "test_runner" }
test_core = { module = "androidx.test:core", version.ref = "test_core" }
test_corektx = { module = "androidx.test:core-ktx", version.ref = "test_core" }
test_uiautomator = { module = "androidx.test.uiautomator:uiautomator", version.ref = "test_uiautomator" }
test_junitext = { module = "androidx.test.ext:junit", version.ref = "test_junitext" }
test_mockk = { module = "io.mockk:mockk", version.ref = "test_mockk" }
test_barista = { module = "com.adevinta.android:barista", version.ref = "test_barista" }
test_hamcrest = { module = "org.hamcrest:hamcrest", version.ref = "test_hamcrest" }
test_orchestrator = { module = "androidx.test:orchestrator", version.ref = "test_orchestrator" }
[bundles]

8
libraries/core/build.gradle

@ -28,7 +28,7 @@ android {
compose true compose true
} }
composeOptions { composeOptions {
kotlinCompilerExtensionVersion compose_version kotlinCompilerExtensionVersion "1.3.2"
} }
kotlinOptions { kotlinOptions {
jvmTarget = '1.8' jvmTarget = '1.8'
@ -39,13 +39,13 @@ dependencies {
implementation 'androidx.core:core-ktx:1.9.0' implementation 'androidx.core:core-ktx:1.9.0'
implementation 'androidx.appcompat:appcompat:1.5.1' implementation 'androidx.appcompat:appcompat:1.5.1'
implementation 'com.google.android.material:material:1.6.1' implementation 'com.google.android.material:material:1.7.0'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.1' implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.1'
implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.5.1" implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.5.1"
implementation 'androidx.activity:activity-compose:1.6.0' implementation 'androidx.activity:activity-compose:1.6.1'
implementation 'androidx.fragment:fragment-ktx:1.5.3' implementation 'androidx.fragment:fragment-ktx:1.5.3'
implementation 'com.airbnb.android:mavericks-compose:2.7.0' implementation 'com.airbnb.android:mavericks-compose:3.0.1'
} }

1
libraries/designsystem/.gitignore vendored

@ -0,0 +1 @@
/build

12
libraries/designsystem/build.gradle.kts

@ -0,0 +1,12 @@
plugins {
id("io.element.android-compose")
}
android {
namespace = "io.element.android.x.libraries.designsystem"
dependencies {
// Should not be there, but this is a POC
implementation("io.coil-kt:coil-compose:2.2.1")
}
}

0
libraries/designsystem/consumer-rules.pro

21
libraries/designsystem/proguard-rules.pro vendored

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.kts.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

4
libraries/designsystem/src/main/AndroidManifest.xml

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
</manifest>

2
libraries/ui/theme/src/main/java/io/element/android/x/ui/theme/Color.kt → libraries/designsystem/src/main/java/io/element/android/x/designsystem/Color.kt

@ -1,4 +1,4 @@
package io.element.android.x.ui.theme package io.element.android.x.designsystem
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color

2
libraries/ui/theme/src/main/java/io/element/android/x/ui/theme/Theme.kt → libraries/designsystem/src/main/java/io/element/android/x/designsystem/Theme.kt

@ -1,4 +1,4 @@
package io.element.android.x.ui.theme package io.element.android.x.designsystem
import android.app.Activity import android.app.Activity
import android.os.Build import android.os.Build

2
libraries/ui/theme/src/main/java/io/element/android/x/ui/theme/Type.kt → libraries/designsystem/src/main/java/io/element/android/x/designsystem/Type.kt

@ -1,4 +1,4 @@
package io.element.android.x.ui.theme package io.element.android.x.designsystem
import androidx.compose.material3.Typography import androidx.compose.material3.Typography
import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.TextStyle

0
libraries/ui/theme/src/main/java/io/element/android/x/ui/theme/components/Avatar.kt → libraries/designsystem/src/main/java/io/element/android/x/designsystem/components/Avatar.kt

2
libraries/ui/theme/src/main/java/io/element/android/x/ui/theme/components/VectorButton.kt → libraries/designsystem/src/main/java/io/element/android/x/designsystem/components/VectorButton.kt

@ -1,4 +1,4 @@
package io.element.android.x.ui.theme.components package io.element.android.x.designsystem.components
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.Text import androidx.compose.material3.Text

2
libraries/ui/theme/src/main/java/io/element/android/x/ui/theme/components/VectorTextField.kt → libraries/designsystem/src/main/java/io/element/android/x/designsystem/components/VectorTextField.kt

@ -1,4 +1,4 @@
package io.element.android.x.ui.theme.components package io.element.android.x.designsystem.components
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api

0
libraries/sdk/matrix/build.gradle → libraries/matrix/build.gradle

0
libraries/sdk/matrix/src/main/AndroidManifest.xml → libraries/matrix/src/main/AndroidManifest.xml

3
libraries/matrix/src/main/java/io/element/android/x/matrix/LogTag.kt

@ -0,0 +1,3 @@
package io.element.android.x.matrix
internal const val LOG_TAG = "Matrix"

6
libraries/sdk/matrix/src/main/java/io/element/android/x/sdk/matrix/Matrix.kt → libraries/matrix/src/main/java/io/element/android/x/matrix/Matrix.kt

@ -1,8 +1,8 @@
package io.element.android.x.sdk.matrix package io.element.android.x.matrix
import android.content.Context import android.content.Context
import io.element.android.x.sdk.matrix.store.SessionStore import io.element.android.x.matrix.store.SessionStore
import io.element.android.x.sdk.matrix.util.logError import io.element.android.x.matrix.util.logError
import org.matrix.rustcomponents.sdk.AuthenticationService import org.matrix.rustcomponents.sdk.AuthenticationService
import org.matrix.rustcomponents.sdk.ClientBuilder import org.matrix.rustcomponents.sdk.ClientBuilder
import java.io.File import java.io.File

4
libraries/sdk/matrix/src/main/java/io/element/android/x/sdk/matrix/MatrixClient.kt → libraries/matrix/src/main/java/io/element/android/x/matrix/MatrixClient.kt

@ -1,7 +1,7 @@
package io.element.android.x.sdk.matrix package io.element.android.x.matrix
import android.util.Log import android.util.Log
import io.element.android.x.sdk.matrix.store.SessionStore import io.element.android.x.matrix.store.SessionStore
import org.matrix.rustcomponents.sdk.* import org.matrix.rustcomponents.sdk.*
class MatrixClient internal constructor( class MatrixClient internal constructor(

2
libraries/sdk/matrix/src/main/java/io/element/android/x/sdk/matrix/MatrixInstance.kt → libraries/matrix/src/main/java/io/element/android/x/matrix/MatrixInstance.kt

@ -1,4 +1,4 @@
package io.element.android.x.sdk.matrix package io.element.android.x.matrix
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context import android.content.Context

2
libraries/sdk/matrix/src/main/java/io/element/android/x/sdk/matrix/RoomWrapper.kt → libraries/matrix/src/main/java/io/element/android/x/matrix/RoomWrapper.kt

@ -1,4 +1,4 @@
package io.element.android.x.sdk.matrix package io.element.android.x.matrix
import android.util.Log import android.util.Log
import org.matrix.rustcomponents.sdk.Client import org.matrix.rustcomponents.sdk.Client

10
libraries/sdk/matrix/src/main/java/io/element/android/x/sdk/matrix/store/SessionStore.kt → libraries/matrix/src/main/java/io/element/android/x/matrix/store/SessionStore.kt

@ -1,4 +1,4 @@
package io.element.android.x.sdk.matrix.store package io.element.android.x.matrix.store
import android.content.Context import android.content.Context
import androidx.datastore.core.DataStore import androidx.datastore.core.DataStore
@ -10,6 +10,10 @@ import kotlinx.coroutines.flow.firstOrNull
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "elementx_sessions") private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "elementx_sessions")
private val userIdPreference = stringPreferencesKey("userId")
// TODO It contains the access token, so it has to be stored in a more secured storage.
// I would expect the Rust SDK to provide a more obscure token.
private val restoreTokenPreference = stringPreferencesKey("restoreToken")
internal class SessionStore( internal class SessionStore(
context: Context context: Context
@ -21,10 +25,6 @@ internal class SessionStore(
private val store = context.dataStore private val store = context.dataStore
private val userIdPreference = stringPreferencesKey("userId")
// TODO It contains the access token, so it has to be stored in a more secured storage.
// I would expect the Rust SDK to provide a more obscure token.
private val restoreTokenPreference = stringPreferencesKey("restoreToken")
suspend fun storeData(sessionData: SessionData) { suspend fun storeData(sessionData: SessionData) {
store.edit { prefs -> store.edit { prefs ->

4
libraries/sdk/matrix/src/main/java/io/element/android/x/sdk/matrix/util/Error.kt → libraries/matrix/src/main/java/io/element/android/x/matrix/util/Error.kt

@ -1,7 +1,7 @@
package io.element.android.x.sdk.matrix.util package io.element.android.x.matrix.util
import android.util.Log import android.util.Log
import io.element.android.x.sdk.matrix.LOG_TAG import io.element.android.x.matrix.LOG_TAG
import org.matrix.rustcomponents.sdk.ClientException import org.matrix.rustcomponents.sdk.ClientException
fun logError(throwable: Throwable) { fun logError(throwable: Throwable) {

3
libraries/sdk/matrix/src/main/java/io/element/android/x/sdk/matrix/LogTag.kt

@ -1,3 +0,0 @@
package io.element.android.x.sdk.matrix
internal const val LOG_TAG = "Matrix"

61
libraries/ui/screens/login/build.gradle

@ -1,61 +0,0 @@
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:core")
implementation project(":libraries:ui:theme")
implementation project(":libraries:sdk:matrix")
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'
implementation 'com.airbnb.android:mavericks-compose:2.7.0'
}

129
libraries/ui/screens/login/src/main/java/io/element/android/x/ui/screen/login/LoginActivity.kt

@ -1,129 +0,0 @@
package io.element.android.x.ui.screen.login
import android.app.Activity
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.*
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.compose.collectAsState
import com.airbnb.mvrx.compose.mavericksViewModel
import io.element.android.x.ui.theme.ElementXTheme
import io.element.android.x.ui.theme.components.VectorButton
class LoginActivity : ComponentActivity() {
@OptIn(ExperimentalMaterial3Api::class)
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
) {
Box(modifier = Modifier.fillMaxSize()) {
Column(
modifier = Modifier.fillMaxSize(),
) {
val viewModel: LoginViewModel = mavericksViewModel()
val state by viewModel.collectAsState()
val isError = state.isLoggedIn is Fail
Image(
painterResource(id = R.drawable.element_logo_green),
contentDescription = null,
modifier = Modifier
.align(Alignment.CenterHorizontally)
.padding(40.dp)
)
OutlinedTextField(
value = state.homeserver,
modifier = Modifier.fillMaxWidth(),
onValueChange = {
viewModel.handle(LoginActions.SetHomeserver(it))
},
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Uri,
),
)
OutlinedTextField(
value = state.login,
modifier = Modifier
.fillMaxWidth()
.padding(top = 8.dp),
onValueChange = {
viewModel.handle(LoginActions.SetLogin(it))
},
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Text,
),
)
OutlinedTextField(
value = state.password,
modifier = Modifier
.fillMaxWidth()
.padding(top = 8.dp),
onValueChange = {
viewModel.handle(LoginActions.SetPassword(it))
},
isError = isError,
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Password,
imeAction = ImeAction.Send,
),
)
if (isError) {
Text(
text = (state.isLoggedIn as? Fail)?.toString().orEmpty(),
color = MaterialTheme.colorScheme.error,
style = MaterialTheme.typography.bodySmall,
modifier = Modifier.padding(start = 16.dp)
)
}
VectorButton(
text = "Submit",
onClick = {
viewModel.handle(LoginActions.Submit)
},
enabled = state.submitEnabled,
modifier = Modifier
.align(Alignment.End)
.padding(top = 16.dp)
)
if (state.isLoggedIn is Loading) {
// FIXME This does not work, we never enter this if block
CircularProgressIndicator(
modifier = Modifier.align(Alignment.CenterHorizontally)
)
}
if (state.isLoggedIn is Success) {
openRoomList()
}
}
}
}
}
}
}
private fun openRoomList() {
setResult(Activity.RESULT_OK)
finish()
}
}

22
libraries/ui/screens/login/src/main/res/drawable/element_logo_green.xml

@ -1,22 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="64dp"
android:height="64dp"
android:viewportWidth="64"
android:viewportHeight="64">
<path
android:pathData="M23.04,3.84C23.04,1.7192 24.7593,0 26.88,0C41.0185,0 52.48,11.4615 52.48,25.6C52.48,27.7208 50.7608,29.44 48.64,29.44C46.5193,29.44 44.8,27.7208 44.8,25.6C44.8,15.7031 36.777,7.68 26.88,7.68C24.7593,7.68 23.04,5.9608 23.04,3.84Z"
android:fillColor="#0DBD8B"
android:fillType="evenOdd"/>
<path
android:pathData="M40.96,60.16C40.96,62.2808 39.2407,64 37.12,64C22.9815,64 11.52,52.5385 11.52,38.4C11.52,36.2792 13.2392,34.56 15.36,34.56C17.4807,34.56 19.2,36.2792 19.2,38.4C19.2,48.2969 27.223,56.32 37.12,56.32C39.2407,56.32 40.96,58.0392 40.96,60.16Z"
android:fillColor="#0DBD8B"
android:fillType="evenOdd"/>
<path
android:pathData="M3.84,40.96C1.7192,40.96 -0,39.2407 -0,37.12C-0,22.9815 11.4615,11.52 25.6,11.52C27.7208,11.52 29.44,13.2392 29.44,15.36C29.44,17.4807 27.7208,19.2 25.6,19.2C15.7031,19.2 7.68,27.223 7.68,37.12C7.68,39.2407 5.9608,40.96 3.84,40.96Z"
android:fillColor="#0DBD8B"
android:fillType="evenOdd"/>
<path
android:pathData="M60.16,23.04C62.2808,23.04 64,24.7593 64,26.88C64,41.0185 52.5385,52.48 38.4,52.48C36.2792,52.48 34.56,50.7608 34.56,48.64C34.56,46.5193 36.2792,44.8 38.4,44.8C48.2969,44.8 56.32,36.777 56.32,26.88C56.32,24.7593 58.0392,23.04 60.16,23.04Z"
android:fillColor="#0DBD8B"
android:fillType="evenOdd"/>
</vector>

62
libraries/ui/screens/roomlist/build.gradle

@ -1,62 +0,0 @@
plugins {
id 'com.android.library'
id 'org.jetbrains.kotlin.android'
}
android {
namespace 'io.element.android.x.ui.screen.roomlist'
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:core")
implementation project(":libraries:ui:theme")
implementation project(":libraries:sdk:matrix")
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'
implementation 'com.airbnb.android:mavericks-compose:2.7.0'
implementation 'io.coil-kt:coil-compose:2.2.1'
}

126
libraries/ui/screens/roomlist/src/main/java/io/element/android/x/ui/screen/roomlist/RoomListActivity.kt

@ -1,126 +0,0 @@
package io.element.android.x.ui.screen.roomlist
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.Image
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ExitToApp
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp
import coil.compose.rememberAsyncImagePainter
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.compose.collectAsState
import com.airbnb.mvrx.compose.mavericksViewModel
import io.element.android.x.ui.theme.ElementXTheme
import io.element.android.x.ui.theme.components.Avatar
import org.matrix.rustcomponents.sdk.Room
class RoomListActivity : ComponentActivity() {
private var initDone = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ElementXTheme {
val viewModel: RoomListViewModel = mavericksViewModel()
if (!initDone) {
initDone = true
viewModel.handle(RoomListActions.Init)
}
val state = viewModel.collectAsState()
// 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()
) {
OptionMenu(state.value.user, viewModel)
val rooms = state.value.rooms
if (rooms is Success) {
LazyColumn {
items(rooms()) { room ->
RoomCompose(room) {
Toast.makeText(
this@RoomListActivity,
"Room $it clicked!",
Toast.LENGTH_SHORT
).show()
}
}
}
}
}
}
if (state.value.logoutAction is Success) {
finish()
}
}
}
}
@Composable
private fun RoomCompose(room: Room, onClick: (String) -> Unit) {
Row(
modifier = Modifier
.fillMaxWidth()
.clickable { onClick.invoke(room.id()) },
) {
Image(
painter = rememberAsyncImagePainter(model = room.avatarUrl()),
contentDescription = null,
modifier = Modifier.size(32.dp),
)
Spacer(modifier = Modifier.width(8.dp))
Column(
modifier = Modifier
.fillMaxWidth()
.height(32.dp),
) {
Text(text = "Room: ${room.name() ?: room.id()}")
Text(text = if (room.isDirect()) "Direct" else "Room")
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun OptionMenu(matrixUser: MatrixUser, viewModel: RoomListViewModel) {
TopAppBar(
title = {
Row(
modifier = Modifier.fillMaxWidth()
) {
Avatar(data = matrixUser.avatarData)
Spacer(modifier = Modifier.width(8.dp))
Text("${matrixUser.username}")
}
},
actions = {
IconButton(
onClick = { viewModel.handle(RoomListActions.Logout) }
) {
Icon(Icons.Default.ExitToApp, contentDescription = "logout")
}
}
)
}
}

52
libraries/ui/theme/build.gradle

@ -1,52 +0,0 @@
plugins {
id 'com.android.library'
id 'org.jetbrains.kotlin.android'
}
android {
namespace 'io.element.android.x.theme'
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 '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-viewmodel-compose:2.5.1"
implementation 'io.coil-kt:coil-compose:2.2.1'
}

14
plugins/build.gradle.kts

@ -0,0 +1,14 @@
plugins {
`kotlin-dsl`
`kotlin-dsl-precompiled-script-plugins`
}
repositories {
mavenCentral()
google()
}
dependencies {
implementation(libs.android.gradle.plugin)
implementation(libs.kotlin.gradle.plugin)
}

10
plugins/settings.gradle.kts

@ -0,0 +1,10 @@
dependencyResolutionManagement {
repositories {
mavenCentral()
}
versionCatalogs {
create("libs") {
from(files("../gradle/libs.versions.toml"))
}
}
}

17
plugins/src/main/java/Versions.kt

@ -0,0 +1,17 @@
import org.gradle.api.JavaVersion
import org.gradle.api.artifacts.VersionCatalog
import org.gradle.jvm.toolchain.JavaLanguageVersion
val VersionCatalog.composeVersion: String
get() = findVersion("compose_compiler").get().requiredVersion
object Versions {
const val versionCode = 100100
const val versionName = "0.1.0"
const val compileSdk = 33
const val targetSdk = 33
const val minSdk = 24
val javaCompileVersion = JavaVersion.VERSION_11
val javaLanguageVersion: JavaLanguageVersion = JavaLanguageVersion.of(11)
}

50
plugins/src/main/java/extension/CommonExtension.kt

@ -0,0 +1,50 @@
package extension
import Versions
import com.android.build.api.dsl.CommonExtension
import com.android.build.api.dsl.LibraryExtension
import composeVersion
import org.gradle.api.artifacts.VersionCatalog
fun CommonExtension<*, *, *, *>.androidConfig() {
defaultConfig {
compileSdk = Versions.compileSdk
minSdk = Versions.minSdk
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
testOptions {
unitTests.isReturnDefaultValues = true
}
}
fun CommonExtension<*, *, *, *>.composeConfig() {
buildFeatures {
compose = true
}
composeOptions {
kotlinCompilerExtensionVersion = "1.3.2"
}
packagingOptions {
resources.excludes.apply {
add("META-INF/AL2.0")
add("META-INF/LGPL2.1")
}
}
}
fun LibraryExtension.proguardConfig() {
buildTypes {
getByName("release") {
isMinifyEnabled = true
proguardFiles("proguard-android.txt", "proguard-rules.pro")
consumerProguardFiles("proguard-rules.pro")
}
}
}

29
plugins/src/main/java/io.element.android-compose.gradle.kts

@ -0,0 +1,29 @@
import extension.androidConfig
import extension.composeConfig
import extension.proguardConfig
plugins {
id("com.android.library")
id("kotlin-android")
}
android {
androidConfig()
proguardConfig()
composeConfig()
}
dependencies {
implementation(platform("androidx.compose:compose-bom:2022.10.00"))
implementation("androidx.compose.ui:ui")
implementation("androidx.compose.material3:material3")
implementation("androidx.compose.ui:ui-tooling-preview")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.5.1")
implementation("androidx.activity:activity-compose:1.6.1")
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.5.1")
implementation("com.airbnb.android:mavericks-compose:3.0.1")
debugImplementation("androidx.compose.ui:ui-tooling")
debugImplementation("androidx.compose.ui:ui-test-manifest")
}

12
plugins/src/main/java/io.element.android-library.gradle.kts

@ -0,0 +1,12 @@
import extension.androidConfig
import extension.proguardConfig
plugins {
id("com.android.library")
id("kotlin-android")
}
android {
androidConfig()
proguardConfig()
}

16
settings.gradle → settings.gradle.kts

@ -1,5 +1,6 @@
pluginManagement { pluginManagement {
repositories { repositories {
includeBuild("plugins")
gradlePluginPortal() gradlePluginPortal()
google() google()
mavenCentral() mavenCentral()
@ -11,14 +12,15 @@ dependencyResolutionManagement {
google() google()
mavenCentral() mavenCentral()
flatDir { flatDir {
dirs 'libraries/sdk/matrix/libs' dirs("libraries/matrix/libs")
} }
} }
} }
rootProject.name = "ElementX" rootProject.name = "ElementX"
include ':app' include(":app")
include ':libraries:core' include(":libraries:core")
include ':libraries:ui:theme' include(":libraries:matrix")
include ':libraries:ui:screens:login' include(":features:login")
include ':libraries:ui:screens:roomlist' include(":features:roomlist")
include ':libraries:sdk:matrix' include(":features:messages")
include(":libraries:designsystem")
Loading…
Cancel
Save