diff --git a/app/build.gradle b/app/build.gradle
index 69bebf0414..e519b94313 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -174,6 +174,11 @@ android {
buildFeatures {
viewBinding true
+ compose true
+ }
+
+ composeOptions {
+ kotlinCompilerExtensionVersion = '1.3.2'
}
defaultConfig {
@@ -589,6 +594,8 @@ dependencies {
implementation libs.rxdogtag
androidTestUtil testLibs.androidx.test.orchestrator
+
+ implementation project(':core-ui')
}
def getLastCommitTimestamp() {
diff --git a/app/src/main/java/org/thoughtcrime/securesms/compose/ComposeBottomSheetDialogFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/compose/ComposeBottomSheetDialogFragment.kt
new file mode 100644
index 0000000000..cec767dff9
--- /dev/null
+++ b/app/src/main/java/org/thoughtcrime/securesms/compose/ComposeBottomSheetDialogFragment.kt
@@ -0,0 +1,32 @@
+package org.thoughtcrime.securesms.compose
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.platform.ComposeView
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.ViewCompositionStrategy
+import org.signal.core.ui.theme.SignalTheme
+import org.thoughtcrime.securesms.components.FixedRoundedCornerBottomSheetDialogFragment
+import org.thoughtcrime.securesms.util.DynamicTheme
+
+abstract class ComposeBottomSheetDialogFragment : FixedRoundedCornerBottomSheetDialogFragment() {
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
+ return ComposeView(requireContext()).apply {
+ setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
+ setContent {
+ SignalTheme(
+ isDarkMode = DynamicTheme.isDarkTheme(LocalContext.current)
+ ) {
+ SheetContent()
+ }
+ }
+ }
+ }
+
+ @Composable
+ abstract fun SheetContent()
+}
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
index 34c29d6948..0cb63faa3a 100644
--- a/app/src/main/res/values/themes.xml
+++ b/app/src/main/res/values/themes.xml
@@ -170,6 +170,7 @@
- @color/signal_colorOnBackground
- @color/signal_colorOutline
- @style/Signal.Text.BodyMedium
+ - @style/Signal.Text.BodyLarge
- @style/Signal.Text.LabelLarge
- @style/Signal.SearchView
@@ -258,6 +259,7 @@
- @color/signal_colorOnBackground
- @color/signal_colorOutline
- @style/Signal.Text.BodyMedium
+ - @style/Signal.Text.BodyLarge
- @style/Signal.Text.LabelLarge
- @style/Signal.Widget.Button.Large.Tonal
diff --git a/core-ui/build.gradle b/core-ui/build.gradle
new file mode 100644
index 0000000000..cfc5538446
--- /dev/null
+++ b/core-ui/build.gradle
@@ -0,0 +1,22 @@
+apply from: "$rootProject.projectDir/signalModule.gradle"
+
+android {
+ buildFeatures {
+ compose true
+ }
+
+ composeOptions {
+ kotlinCompilerExtensionVersion = '1.3.2'
+ }
+}
+
+dependencies {
+ lintChecks project(':lintchecks')
+
+ def composeBom = platform(libs.androidx.compose.bom)
+ api composeBom
+ androidTestApi composeBom
+ api libs.androidx.compose.material3
+ api libs.androidx.compose.ui.tooling.preview
+ debugApi libs.androidx.compose.ui.tooling.core
+}
diff --git a/core-ui/src/main/AndroidManifest.xml b/core-ui/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000..49f27e02e6
--- /dev/null
+++ b/core-ui/src/main/AndroidManifest.xml
@@ -0,0 +1,6 @@
+
+
+
+
\ No newline at end of file
diff --git a/core-ui/src/main/java/org/signal/core/ui/theme/SignalTheme.kt b/core-ui/src/main/java/org/signal/core/ui/theme/SignalTheme.kt
new file mode 100644
index 0000000000..bbcc14c490
--- /dev/null
+++ b/core-ui/src/main/java/org/signal/core/ui/theme/SignalTheme.kt
@@ -0,0 +1,119 @@
+package org.signal.core.ui.theme
+
+import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Typography
+import androidx.compose.material3.darkColorScheme
+import androidx.compose.material3.lightColorScheme
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.font.FontStyle
+import androidx.compose.ui.unit.sp
+
+private val typography = Typography().apply {
+ copy(
+ headlineLarge = headlineLarge.copy(
+ lineHeight = 40.sp,
+ letterSpacing = 0.sp
+ ),
+ headlineMedium = headlineMedium.copy(
+ lineHeight = 36.sp,
+ letterSpacing = 0.sp
+ ),
+ titleLarge = titleLarge.copy(
+ lineHeight = 28.sp,
+ letterSpacing = 0.sp
+ ),
+ titleMedium = titleMedium.copy(
+ fontFamily = FontFamily.SansSerif,
+ fontStyle = FontStyle.Normal,
+ fontSize = 18.sp,
+ lineHeight = 24.sp,
+ letterSpacing = 0.0125.sp
+ ),
+ titleSmall = titleSmall.copy(
+ fontSize = 16.sp,
+ lineHeight = 22.sp,
+ letterSpacing = 0.0125.sp
+ ),
+ bodyLarge = bodyLarge.copy(
+ lineHeight = 22.sp,
+ letterSpacing = 0.0125.sp
+ ),
+ bodyMedium = bodyMedium.copy(
+ lineHeight = 20.sp,
+ letterSpacing = 0.0107.sp
+ ),
+ bodySmall = bodySmall.copy(
+ fontSize = 13.sp,
+ lineHeight = 16.sp,
+ letterSpacing = 0.0192.sp
+ ),
+ labelLarge = labelLarge.copy(
+ lineHeight = 20.sp,
+ letterSpacing = 0.0107.sp
+ ),
+ labelMedium = labelMedium.copy(
+ fontSize = 13.sp,
+ lineHeight = 16.sp,
+ letterSpacing = 0.0192.sp
+ ),
+ labelSmall = labelSmall.copy(
+ lineHeight = 16.sp,
+ letterSpacing = 0.025.sp
+ )
+ )
+}
+
+private val lightColorScheme = lightColorScheme(
+ primary = Color(0xFF2C58C3),
+ primaryContainer = Color(0xFFD2DFFB),
+ secondary = Color(0xFF586071),
+ secondaryContainer = Color(0xFFDCE5F9),
+ surface = Color(0xFFFBFCFF),
+ surfaceVariant = Color(0xFFE7EBF3),
+ background = Color(0xFFFBFCFF),
+ error = Color(0xFFBA1B1B),
+ errorContainer = Color(0xFFFFDAD4),
+ onPrimary = Color(0xFFFFFFFF),
+ onPrimaryContainer = Color(0xFF051845),
+ onSecondary = Color(0xFFFFFFFF),
+ onSecondaryContainer = Color(0xFF151D2C),
+ onSurface = Color(0xFF1B1B1D),
+ onSurfaceVariant = Color(0xFF545863),
+ onBackground = Color(0xFF1B1D1D),
+ outline = Color(0xFF808389)
+)
+
+private val darkColorScheme = darkColorScheme(
+ primary = Color(0xFFB6C5FA),
+ primaryContainer = Color(0xFF464B5C),
+ secondary = Color(0xFFC1C6DD),
+ secondaryContainer = Color(0xFF414659),
+ surface = Color(0xFF1B1C1F),
+ surfaceVariant = Color(0xFF303133),
+ background = Color(0xFF1B1C1F),
+ error = Color(0xFFFFB4A9),
+ errorContainer = Color(0xFF930006),
+ onPrimary = Color(0xFF1E2438),
+ onPrimaryContainer = Color(0xFFDBE1FC),
+ onSecondary = Color(0xFF2A3042),
+ onSecondaryContainer = Color(0xFFDCE1F9),
+ onSurface = Color(0xFFE2E1E5),
+ onSurfaceVariant = Color(0xFFBEBFC5),
+ onBackground = Color(0xFFE2E1E5),
+ outline = Color(0xFF5C5E65)
+)
+
+@Composable
+fun SignalTheme(
+ isDarkMode: Boolean,
+ content: @Composable () -> Unit
+) {
+ MaterialTheme(
+ colorScheme = if (isDarkMode) darkColorScheme else lightColorScheme,
+ typography = typography,
+ content = content
+ )
+}
diff --git a/dependencies.gradle b/dependencies.gradle
index 57aa989330..b4939f4614 100644
--- a/dependencies.gradle
+++ b/dependencies.gradle
@@ -15,6 +15,12 @@ dependencyResolutionManagement {
version('libsignal-client', '0.21.1')
version('mp4parser', '1.9.39')
+ // Compose
+ alias('androidx-compose-bom').to('androidx.compose:compose-bom:2022.12.00')
+ alias('androidx-compose-material3').to('androidx.compose.material3', 'material3').withoutVersion()
+ alias('androidx-compose-ui-tooling-preview').to('androidx.compose.ui', 'ui-tooling-preview').withoutVersion()
+ alias('androidx-compose-ui-tooling-core').to('androidx.compose.ui', 'ui-tooling').withoutVersion()
+
// Desugaring
alias('android-tools-desugar').to('com.android.tools:desugar_jdk_libs:1.1.5')
diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml
index eb9325d6db..8f95fb4bd8 100644
--- a/gradle/verification-metadata.xml
+++ b/gradle/verification-metadata.xml
@@ -36,6 +36,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
+
+
+
+
+
+
+
+
@@ -78,6 +86,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
+
+
+
+
+
+
+
+
@@ -208,6 +224,166 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -313,6 +489,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
+
+
+
+
+
+
+
+
@@ -818,6 +1002,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
+
+
+
+
+
+
+
+
@@ -4176,6 +4368,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
+
+
+
+
+
+
+
+
@@ -4235,6 +4435,9 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
+
+
+
diff --git a/settings.gradle b/settings.gradle
index 3abff5bf88..274bb3bbdd 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -24,6 +24,7 @@ include ':qr'
include ':qr-app'
include ':sticky-header-grid'
include ':photoview'
+include ':core-ui'
project(':app').name = 'Signal-Android'
project(':paging').projectDir = file('paging/lib')