diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 5f8997ff..ab877c21 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -77,14 +77,6 @@ android { dataBinding = true } - buildFeatures { - compose = true - } - - composeOptions { - kotlinCompilerExtensionVersion = Lib.Androidx.composeVersion - } - packagingOptions { resources.excludes.add("META-INF/LICENSE.txt") resources.excludes.add("META-INF/NOTICE.txt") @@ -122,18 +114,11 @@ hilt { } dependencies { - - Lib.Androidx.list.forEach(::api) - Lib.Androidx.Compose.list.forEach(::api) - Lib.ThirdParty.list.forEach(::api) - Lib.Accompanist.list.forEach(::api) Lib.Google.list.forEach(::api) Lib.Kotlin.list.forEach(::api) + api(project(":ui-feature1")) - api(project(":ui-onboarding")) - api(project(":ui-authentication")) - api(project(":navigator")) api(project(":data")) api(project(":domain")) api(project(":common")) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 8aae06ce..fc0dff44 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -6,16 +6,16 @@ diff --git a/app/src/main/java/com/mutualmobile/praxis/di/AppModule.kt b/app/src/main/java/com/mutualmobile/praxis/di/AppModule.kt index 1914b29b..ec6541e4 100644 --- a/app/src/main/java/com/mutualmobile/praxis/di/AppModule.kt +++ b/app/src/main/java/com/mutualmobile/praxis/di/AppModule.kt @@ -1,8 +1,6 @@ package com.mutualmobile.praxis.di -import android.app.Application import android.content.Context -import dagger.Binds import dagger.Module import dagger.Provides import dagger.hilt.InstallIn diff --git a/app/src/main/java/com/mutualmobile/praxis/di/NavigationModule.kt b/app/src/main/java/com/mutualmobile/praxis/di/NavigationModule.kt index e0e88fc1..cc2dfbc5 100755 --- a/app/src/main/java/com/mutualmobile/praxis/di/NavigationModule.kt +++ b/app/src/main/java/com/mutualmobile/praxis/di/NavigationModule.kt @@ -1,7 +1,5 @@ package com.mutualmobile.praxis.di -import com.mutualmobile.praxis.navigator.ComposeNavigator -import com.mutualmobile.praxis.navigator.composenavigator.PraxisCloneComposeNavigator import dagger.Binds import dagger.Module import dagger.hilt.InstallIn @@ -12,7 +10,5 @@ import javax.inject.Singleton @InstallIn(SingletonComponent::class) abstract class NavigationModule { - @Binds - @Singleton - abstract fun provideComposeNavigator(praxisComposeNavigator: PraxisCloneComposeNavigator): ComposeNavigator + } diff --git a/app/src/main/java/com/mutualmobile/praxis/root/OnboardingActivity.kt b/app/src/main/java/com/mutualmobile/praxis/root/OnboardingActivity.kt deleted file mode 100644 index 243107ab..00000000 --- a/app/src/main/java/com/mutualmobile/praxis/root/OnboardingActivity.kt +++ /dev/null @@ -1,52 +0,0 @@ -package com.mutualmobile.praxis.root - -import android.os.Bundle -import androidx.activity.compose.setContent -import androidx.appcompat.app.AppCompatActivity -import androidx.compose.runtime.LaunchedEffect -import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen -import androidx.core.view.WindowCompat -import androidx.navigation.compose.NavHost -import androidx.navigation.compose.rememberNavController -import com.google.accompanist.insets.ProvideWindowInsets -import dagger.hilt.android.AndroidEntryPoint -import com.mutualmobile.praxis.navigator.ComposeNavigator -import com.mutualmobile.praxis.navigator.PraxisRoute -import com.mutualmobile.praxis.uionboarding.nav.onboardingNavigation -import com.praxis.feat.authentication.nav.authNavGraph -import javax.inject.Inject - -@AndroidEntryPoint -class OnboardingActivity : AppCompatActivity() { - - @Inject - lateinit var composeNavigator: ComposeNavigator - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - WindowCompat.setDecorFitsSystemWindows(window, false) - - installSplashScreen() - setContent { - val navController = rememberNavController() - - LaunchedEffect(Unit) { - composeNavigator.handleNavigationCommands(navController) - } - - ProvideWindowInsets(windowInsetsAnimationsEnabled = true) { - NavHost( - navController = navController, - startDestination = PraxisRoute.OnBoarding.name, - ) { - onboardingNavigation( - composeNavigator = composeNavigator, - ) - authNavGraph() - } - } - - - } - } -} \ No newline at end of file diff --git a/app/src/main/res/layout/view_random_photos.xml b/app/src/main/res/layout/view_random_photos.xml deleted file mode 100644 index a6946780..00000000 --- a/app/src/main/res/layout/view_random_photos.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/common/build.gradle.kts b/common/build.gradle.kts index e9bdb2fd..dc99b906 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -34,16 +34,11 @@ dependencies { Lib.Androidx.list.forEach(::implementation) - Lib.Androidx.Compose.list.forEach(::implementation) - Lib.ThirdParty.list.forEach(::implementation) - Lib.Accompanist.list.forEach(::implementation) Lib.Google.list.forEach(::implementation) Lib.Kotlin.list.forEach(::implementation) /*DI*/ implementation(Lib.Di.hilt) - implementation(Lib.Di.hiltNavigationCompose) - implementation(Lib.Di.viewmodel) kapt(Lib.Di.hiltCompiler) kapt(Lib.Di.hiltAndroidCompiler) diff --git a/commonui/build.gradle.kts b/commonui/build.gradle.kts index 73940cf7..770cfe9d 100644 --- a/commonui/build.gradle.kts +++ b/commonui/build.gradle.kts @@ -43,19 +43,12 @@ kapt { } dependencies { - - Lib.Androidx.list.forEach(::implementation) - Lib.Androidx.Compose.list.forEach(::implementation) - Lib.ThirdParty.list.forEach(::implementation) - Lib.Accompanist.list.forEach(::implementation) Lib.Google.list.forEach(::implementation) Lib.Kotlin.list.forEach(::implementation) /*DI*/ implementation(Lib.Di.hilt) - implementation(Lib.Di.hiltNavigationCompose) - implementation(Lib.Di.viewmodel) kapt(Lib.Di.hiltCompiler) kapt(Lib.Di.hiltAndroidCompiler) diff --git a/commonui/src/main/java/com/mutualmobile/praxis/commonui/keyboard/Keyboard.kt b/commonui/src/main/java/com/mutualmobile/praxis/commonui/keyboard/Keyboard.kt deleted file mode 100644 index 1192aa28..00000000 --- a/commonui/src/main/java/com/mutualmobile/praxis/commonui/keyboard/Keyboard.kt +++ /dev/null @@ -1,37 +0,0 @@ -package com.mutualmobile.praxis.commonui.keyboard - -import android.graphics.Rect -import android.view.ViewTreeObserver -import androidx.compose.runtime.* -import androidx.compose.ui.platform.LocalView - -sealed class Keyboard { - data class Opened(var height: Int) : Keyboard() - object Closed : Keyboard() -} - -@Composable -fun keyboardAsState(): State { - val keyboardState = remember { mutableStateOf(Keyboard.Closed) } - val view = LocalView.current - DisposableEffect(view) { - val onGlobalListener = ViewTreeObserver.OnGlobalLayoutListener { - val rect = Rect() - view.getWindowVisibleDisplayFrame(rect) - val screenHeight = view.rootView.height - val keypadHeight = screenHeight - rect.bottom - keyboardState.value = if (keypadHeight > screenHeight * 0.15) { - Keyboard.Opened(screenHeight - keypadHeight) - } else { - Keyboard.Closed - } - } - view.viewTreeObserver.addOnGlobalLayoutListener(onGlobalListener) - - onDispose { - view.viewTreeObserver.removeOnGlobalLayoutListener(onGlobalListener) - } - } - - return keyboardState -} \ No newline at end of file diff --git a/commonui/src/main/java/com/mutualmobile/praxis/commonui/material/DefaultSnackbar.kt b/commonui/src/main/java/com/mutualmobile/praxis/commonui/material/DefaultSnackbar.kt deleted file mode 100644 index f0406600..00000000 --- a/commonui/src/main/java/com/mutualmobile/praxis/commonui/material/DefaultSnackbar.kt +++ /dev/null @@ -1,48 +0,0 @@ -package com.mutualmobile.praxis.commonui.material - -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.wrapContentHeight -import androidx.compose.material.* -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import com.mutualmobile.praxis.commonui.theme.PraxisColorProvider -import com.mutualmobile.praxis.commonui.theme.PraxisTypography - -@Composable -fun DefaultSnackbar( - snackbarHostState: SnackbarHostState, - modifier: Modifier = Modifier, - onDismiss: () -> Unit = { } -) { - SnackbarHost( - hostState = snackbarHostState, - snackbar = { data -> - Snackbar( - content = { - Text( - text = data.message, - style = PraxisTypography.body1, - color = PraxisColorProvider.colors.textPrimary, - ) - }, - action = { - data.actionLabel?.let { actionLabel -> - TextButton(onClick = onDismiss) { - Text( - text = actionLabel, - color = PraxisColorProvider.colors.textPrimary, - style = PraxisTypography.body2 - ) - } - } - }, - backgroundColor = PraxisColorProvider.colors.uiBackground - ) - }, - modifier = modifier - .fillMaxWidth() - .wrapContentHeight(Alignment.Bottom) - ) -} \ No newline at end of file diff --git a/commonui/src/main/java/com/mutualmobile/praxis/commonui/material/PraxisSurfaceAppBar.kt b/commonui/src/main/java/com/mutualmobile/praxis/commonui/material/PraxisSurfaceAppBar.kt deleted file mode 100644 index a73c5721..00000000 --- a/commonui/src/main/java/com/mutualmobile/praxis/commonui/material/PraxisSurfaceAppBar.kt +++ /dev/null @@ -1,51 +0,0 @@ -package com.mutualmobile.praxis.commonui.material - -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.RowScope -import androidx.compose.material.* -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.unit.Dp -import com.mutualmobile.praxis.commonui.theme.PraxisSurface - -@Composable -fun PraxisSurfaceAppBar( - title: @Composable () -> Unit, - modifier: Modifier = Modifier, - navigationIcon: @Composable (() -> Unit)? = null, - actions: @Composable RowScope.() -> Unit = {}, - backgroundColor: Color = MaterialTheme.colors.primarySurface, - contentColor: Color = contentColorFor(backgroundColor), - elevation: Dp = AppBarDefaults.TopAppBarElevation, -) { - PraxisSurface( - color = backgroundColor, - contentColor = contentColor, - elevation = elevation - ) { - TopAppBar( - title, modifier, navigationIcon, actions, backgroundColor, contentColor, elevation - ) - } -} - -@Composable -fun PraxisSurfaceAppBar( - modifier: Modifier = Modifier, - backgroundColor: Color = MaterialTheme.colors.primarySurface, - contentColor: Color = contentColorFor(backgroundColor), - elevation: Dp = AppBarDefaults.TopAppBarElevation, - contentPadding: PaddingValues = AppBarDefaults.ContentPadding, - content: @Composable RowScope.() -> Unit -) { - PraxisSurface( - color = backgroundColor, - contentColor = contentColor, - elevation = elevation - ) { - TopAppBar( - modifier, backgroundColor, contentColor, elevation, contentPadding, content - ) - } -} \ No newline at end of file diff --git a/commonui/src/main/java/com/mutualmobile/praxis/commonui/reusable/SlackDragComposableView.kt b/commonui/src/main/java/com/mutualmobile/praxis/commonui/reusable/SlackDragComposableView.kt deleted file mode 100644 index 321d83cc..00000000 --- a/commonui/src/main/java/com/mutualmobile/praxis/commonui/reusable/SlackDragComposableView.kt +++ /dev/null @@ -1,249 +0,0 @@ -package com.mutualmobile.praxis.commonui.reusable - -import androidx.compose.animation.core.* -import androidx.compose.foundation.gestures.detectHorizontalDragGestures -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.offset -import androidx.compose.runtime.* -import androidx.compose.ui.Modifier -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.input.pointer.PointerInputChange -import androidx.compose.ui.input.pointer.consumePositionChange -import androidx.compose.ui.input.pointer.pointerInput -import androidx.compose.ui.platform.LocalConfiguration -import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.unit.IntOffset -import androidx.compose.ui.unit.dp -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch -import kotlin.math.roundToInt - -@Composable -fun PraxisDragComposableView( - isLeftNavOpen: Boolean, - isChatViewClosed: Boolean, - mainScreenOffset: Float, - chatScreenOffset: Float, - onOpenCloseLeftView: (Boolean) -> Unit, - onOpenCloseRightView: (Boolean) -> Unit, - mainScreenComposable: @Composable (modifier: Modifier) -> Unit, - leftViewComposable: @Composable (modifier: Modifier) -> Unit, - rightViewComposable: @Composable (modifier: Modifier) -> Unit, -) { - val sideNavOffX = remember { - Animatable(viewOffset(isLeftNavOpen, mainScreenOffset)) - } - val chatViewOffX = remember { - Animatable(viewOffset(isChatViewClosed, chatScreenOffset)) - } - - val coroutineScope = rememberCoroutineScope() - - InitialOffsetsSideEffect( - coroutineScope, - sideNavOffX, - isLeftNavOpen, - mainScreenOffset, - chatViewOffX, - isChatViewClosed, - chatScreenOffset - ) - - Box(Modifier.fillMaxWidth()) { - leftViewComposable(Modifier) - mainScreenComposable( - mainScreenModifier( - sideNavOffX, - mainScreenOffset, - coroutineScope, - onOpenCloseLeftView, - onOpenCloseRightView, - chatViewOffX, - chatScreenOffset - ) - ) - rightViewComposable( - chatScreenModifier( - chatViewOffX, - chatScreenOffset, - coroutineScope, - onOpenCloseRightView - ) - ) - } - - -} - -@Composable -private fun InitialOffsetsSideEffect( - coroutineScope: CoroutineScope, - sideNavOffX: Animatable, - isLeftNavOpen: Boolean, - mainScreenOffset: Float, - chatViewOffX: Animatable, - isChatViewClosed: Boolean, - chatScreenOffset: Float -) { - SideEffect { - coroutineScope.launch { - sideNavOffX.snapTo(viewOffset(isLeftNavOpen, mainScreenOffset)) - } - coroutineScope.launch { - chatViewOffX.snapTo(viewOffset(isChatViewClosed, chatScreenOffset)) - } - } -} - -private fun viewOffset(needsOpen: Boolean, offset: Float) = - if (needsOpen) offset else 0f - - -@Composable -private fun chatScreenModifier( - offsetX: Animatable, - requiredOffset: Float, - coroutineScope: CoroutineScope, - onOpen: (Boolean) -> Unit, -) = Modifier - .offset { - IntOffset( - (offsetX.value).roundToInt(), - 0 - ) - } - .pointerInput(Unit) { - detectHorizontalDragGestures({ - //start - }, { - rightViewEndTransition(offsetX, requiredOffset, coroutineScope, onOpen) - }, { - //cancel - - }, { change, dragAmount -> - // this moves the chat view left/right - val summedMain = Offset(x = offsetX.targetValue + dragAmount, y = 0f) - val newDragValueMain = Offset(x = summedMain.x.coerceIn(0f, requiredOffset), y = 0f) - change.consumePositionChange() - coroutineScope.launch { - offsetX.animateTo(newDragValueMain.x, animationSpec = tween(50)) - } - }) - } - -private fun mainScreenModifier( - offsetX: Animatable, - dragOffset: Float, - coroutineScope: CoroutineScope, - onOpenSideNav: (Boolean) -> Unit, - onOpenCloseRightView: (Boolean) -> Unit, - chatViewOffX: Animatable, - chatScreenOffset: Float, -) = Modifier - .offset { - IntOffset( - (offsetX.value).roundToInt(), - 0 - ) - } - .pointerInput(Unit) { - detectHorizontalDragGestures({ - //start - }, { - sideNavigationEndTransition( - offsetX, - dragOffset, - coroutineScope, - onOpenSideNav - ) - rightViewEndTransition( - chatViewOffX, - chatScreenOffset, - coroutineScope, - onOpenCloseRightView - ) - - }, { - //cancel - - }, { change, dragAmount -> - mainAnimateOffset( - offsetX, - dragAmount, - dragOffset, - change, - coroutineScope, - chatViewOffX, - chatScreenOffset - ) - }) - } - -fun rightViewEndTransition( - offsetX: Animatable, - requiredOffset: Float, - coroutineScope: CoroutineScope, - onOpen: (Boolean) -> Unit -) { - //end - if (offsetX.targetValue > requiredOffset / 2) { - coroutineScope.launch { - offsetX.animateTo(requiredOffset, animationSpec = tween(150)) - onOpen(true) - } - } else { - coroutineScope.launch { - offsetX.animateTo(0F, animationSpec = tween(150)) - onOpen(false) - } - } -} - -private fun sideNavigationEndTransition( - offsetX: Animatable, - dragOffset: Float, - coroutineScope: CoroutineScope, - onOpenSideNav: (Boolean) -> Unit -) { - if (offsetX.targetValue > dragOffset / 2) { - coroutineScope.launch { - offsetX.animateTo(dragOffset, animationSpec = tween(150)) - onOpenSideNav(true) - } - } else { - coroutineScope.launch { - offsetX.animateTo(0F, animationSpec = tween(150)) - onOpenSideNav(false) - } - } -} - -private fun mainAnimateOffset( - offsetX: Animatable, - dragAmount: Float, - mainDragOffset: Float, - change: PointerInputChange, - coroutineScope: CoroutineScope, - chatViewOffX: Animatable, - chatScreenOffset: Float -) { - // this moves the chat view from right to left/left to right - if (offsetX.targetValue <= 0f) { - val summedChat = Offset(x = chatViewOffX.targetValue + dragAmount, y = 0f) - val chatNewDragValueMain = Offset(x = summedChat.x.coerceIn(0f, chatScreenOffset), y = 0f) - change.consumePositionChange() - coroutineScope.launch { - chatViewOffX.animateTo(chatNewDragValueMain.x, animationSpec = tween(50)) - } - } - - // this moved the main view left/right - val summedMain = Offset(x = offsetX.targetValue + dragAmount, y = 0f) - val newDragValueMain = Offset(x = summedMain.x.coerceIn(0f, mainDragOffset), y = 0f) - change.consumePositionChange() - coroutineScope.launch { - offsetX.animateTo(newDragValueMain.x, animationSpec = tween(50)) - } - -} \ No newline at end of file diff --git a/commonui/src/main/java/com/mutualmobile/praxis/commonui/reusable/SlackImageBox.kt b/commonui/src/main/java/com/mutualmobile/praxis/commonui/reusable/SlackImageBox.kt deleted file mode 100644 index da069d44..00000000 --- a/commonui/src/main/java/com/mutualmobile/praxis/commonui/reusable/SlackImageBox.kt +++ /dev/null @@ -1,23 +0,0 @@ -package com.mutualmobile.praxis.commonui.reusable - -import androidx.compose.foundation.Image -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import coil.compose.rememberImagePainter -import coil.memory.MemoryCache -import coil.request.CachePolicy -import coil.transform.RoundedCornersTransformation - -@Composable -fun PraxisImageBox(modifier: Modifier, imageUrl: String) { - Image( - painter = rememberImagePainter( - data = imageUrl, - builder = { - transformations(RoundedCornersTransformation(12.0f, 12.0f, 12.0f, 12.0f)) - } - ), - contentDescription = null, - modifier = modifier - ) -} \ No newline at end of file diff --git a/commonui/src/main/java/com/mutualmobile/praxis/commonui/reusable/SlackListItem.kt b/commonui/src/main/java/com/mutualmobile/praxis/commonui/reusable/SlackListItem.kt deleted file mode 100644 index 285e8685..00000000 --- a/commonui/src/main/java/com/mutualmobile/praxis/commonui/reusable/SlackListItem.kt +++ /dev/null @@ -1,62 +0,0 @@ -package com.mutualmobile.praxis.commonui.reusable - -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.material.ExperimentalMaterialApi -import androidx.compose.material.Icon -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.unit.dp -import com.mutualmobile.praxis.commonui.theme.PraxisColorProvider -import com.mutualmobile.praxis.commonui.theme.PraxisTypography - -@OptIn(ExperimentalMaterialApi::class) -@Composable -fun PraxisListItem( - icon: ImageVector, - title: String, - trailingItem: ImageVector? = null, - onItemClick: () -> Unit = {} -) { - Row( - modifier = Modifier - .padding(8.dp) - .clickable { - onItemClick() - }, verticalAlignment = Alignment.CenterVertically - ) { - Icon( - imageVector = icon, - contentDescription = null, - tint = PraxisColorProvider.colors.textPrimary.copy(alpha = 0.4f), - modifier = Modifier - .size(28.dp) - .padding(4.dp) - ) - Text( - text = title, - style = PraxisTypography.subtitle1.copy( - color = PraxisColorProvider.colors.textPrimary.copy( - alpha = 0.8f - ) - ), modifier = Modifier - .weight(1f) - .padding(8.dp) - ) - trailingItem?.let { safeIcon -> - Icon( - imageVector = safeIcon, - contentDescription = null, - tint = PraxisColorProvider.colors.textPrimary.copy(alpha = 0.4f), - modifier = Modifier - .size(24.dp) - .padding(4.dp) - ) - } - } -} \ No newline at end of file diff --git a/commonui/src/main/java/com/mutualmobile/praxis/commonui/theme/Color.kt b/commonui/src/main/java/com/mutualmobile/praxis/commonui/theme/Color.kt deleted file mode 100644 index 875c335c..00000000 --- a/commonui/src/main/java/com/mutualmobile/praxis/commonui/theme/Color.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.mutualmobile.praxis.commonui.theme - -import androidx.compose.ui.graphics.Color - -val PraxisCloneColor = Color(0xff411540) -val DarkAppBarColor = Color(0xff1a1b1e) -val DarkBackground = Color(0xff1b1d21) -val FunctionalRed = Color(0xffd00036) -val FunctionalRedDark = Color(0xffea6d7e) -val PraxisLogoYellow = Color(0xffECB22E) -val LineColorLight = Color.Black.copy(alpha = 0.4f) -val LineColorDark = Color.LightGray.copy(alpha = 0.1f) -const val AlphaNearOpaque = 0.95f -const val AlphaNearTransparent = 0.15f diff --git a/commonui/src/main/java/com/mutualmobile/praxis/commonui/theme/PraxisSurface.kt b/commonui/src/main/java/com/mutualmobile/praxis/commonui/theme/PraxisSurface.kt deleted file mode 100644 index 65b341eb..00000000 --- a/commonui/src/main/java/com/mutualmobile/praxis/commonui/theme/PraxisSurface.kt +++ /dev/null @@ -1,79 +0,0 @@ -package com.mutualmobile.praxis.commonui.theme - -import androidx.compose.foundation.BorderStroke -import androidx.compose.foundation.background -import androidx.compose.foundation.border -import androidx.compose.foundation.layout.Box -import androidx.compose.material.LocalContentColor -import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.draw.shadow -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.RectangleShape -import androidx.compose.ui.graphics.Shape -import androidx.compose.ui.graphics.compositeOver -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp -import androidx.compose.ui.zIndex -import kotlin.math.ln - -/** - * An alternative to [androidx.compose.material.Surface] - */ -@Composable -fun PraxisSurface( - modifier: Modifier = Modifier, - shape: Shape = RectangleShape, - color: Color = PraxisColorProvider.colors.uiBackground, - contentColor: Color = PraxisColorProvider.colors.textSecondary, - border: BorderStroke? = null, - elevation: Dp = 0.dp, - content: @Composable () -> Unit -) { - Box( - modifier = modifier - .shadow(elevation = elevation, shape = shape, clip = false) - .zIndex(elevation.value) - .then(if (border != null) Modifier.border(border, shape) else Modifier) - .background( - color = getBackgroundColorForElevation(color, elevation), - shape = shape - ) - .clip(shape) - ) { - CompositionLocalProvider(LocalContentColor provides contentColor, content = content) - } -} - -@Composable -private fun getBackgroundColorForElevation( - color: Color, - elevation: Dp -): Color { - return if (elevation > 0.dp - ) { - color.withElevation(elevation) - } else { - color - } -} - -/** - * Applies a [Color.White] overlay to this color based on the [elevation]. This increases visibility - * of elevation for surfaces in a dark theme. - */ -private fun Color.withElevation(elevation: Dp): Color { - val foreground = calculateForeground(elevation) - return foreground.compositeOver(this) -} - -/** - * @return the alpha-modified [Color.White] to overlay on top of the surface color to produce - * the resultant color. - */ -private fun calculateForeground(elevation: Dp): Color { - val alpha = ((4.5f * ln(elevation.value + 1)) + 2f) / 20f - return Color.White.copy(alpha = alpha) -} \ No newline at end of file diff --git a/commonui/src/main/java/com/mutualmobile/praxis/commonui/theme/Shape.kt b/commonui/src/main/java/com/mutualmobile/praxis/commonui/theme/Shape.kt deleted file mode 100644 index 472db0ec..00000000 --- a/commonui/src/main/java/com/mutualmobile/praxis/commonui/theme/Shape.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.mutualmobile.praxis.commonui.theme - -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.Shapes -import androidx.compose.ui.unit.dp - -val PraxisShapes = Shapes( - small = RoundedCornerShape(4.dp), - medium = RoundedCornerShape(6.dp), - large = RoundedCornerShape(10.dp) -) \ No newline at end of file diff --git a/commonui/src/main/java/com/mutualmobile/praxis/commonui/theme/Theme.kt b/commonui/src/main/java/com/mutualmobile/praxis/commonui/theme/Theme.kt deleted file mode 100644 index fd0b2e5d..00000000 --- a/commonui/src/main/java/com/mutualmobile/praxis/commonui/theme/Theme.kt +++ /dev/null @@ -1,222 +0,0 @@ -package com.mutualmobile.praxis.commonui.theme - -import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.material.Colors -import androidx.compose.material.MaterialTheme -import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.runtime.SideEffect -import androidx.compose.runtime.Stable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.runtime.staticCompositionLocalOf -import androidx.compose.ui.graphics.Color -import com.google.accompanist.systemuicontroller.rememberSystemUiController - -private val LightColorPalette = PraxisCloneColorPalette( - brand = PraxisCloneColor, - accent = PraxisCloneColor, - uiBackground = Color.White, - textPrimary = Color.Black, - textSecondary = Color.DarkGray, - error = FunctionalRed, - statusBarColor = PraxisCloneColor, - isDark = false, - buttonColor = Color.Black, - buttonTextColor = Color.White, - darkBackground = DarkBackground, - appBarColor = PraxisCloneColor, - lineColor = LineColorLight, - bottomNavSelectedColor = Color.Black, - bottomNavUnSelectedColor = Color.LightGray, - appBarIconColor = Color.White, - appBarTextTitleColor = Color.White, - appBarTextSubTitleColor = Color.White, - sendButtonDisabled = Color.LightGray, - sendButtonEnabled = Color.Black -) - -private val DarkColorPalette = PraxisCloneColorPalette( - brand = PraxisCloneColor, - accent = DarkBackground, - uiBackground = DarkBackground, - textPrimary = Color.White, - textSecondary = Color.White, - error = FunctionalRedDark, - statusBarColor = PraxisCloneColor, - isDark = true, - buttonColor = Color.White, - buttonTextColor = Color.Black, - darkBackground = DarkBackground, - appBarColor = DarkAppBarColor, - lineColor = LineColorDark, - bottomNavSelectedColor = Color.White, - bottomNavUnSelectedColor = Color.Gray, - appBarIconColor = Color.White, - appBarTextTitleColor = Color.White, - appBarTextSubTitleColor = Color.White, - sendButtonDisabled = Color.White.copy(alpha = 0.4f), - sendButtonEnabled = Color.White -) - -@Composable -fun PraxisTheme( - isDarkTheme: Boolean = isSystemInDarkTheme(), - content: @Composable () -> Unit -) { - val colors = if (isDarkTheme) DarkColorPalette else LightColorPalette - val sysUiController = rememberSystemUiController() - - SideEffect { - sysUiController.setSystemBarsColor(color = colors.appBarColor) - sysUiController.setNavigationBarColor(color = colors.appBarColor) - } - - ProvidePraxisCloneColors(colors) { - MaterialTheme( - colors = debugColors(isDarkTheme), - typography = PraxisTypography, - shapes = PraxisShapes, - content = content - ) - } -} - -object PraxisColorProvider { - val colors: PraxisCloneColorPalette - @Composable - get() = LocalPraxisCloneColor.current -} - -/** - * PraxisClone custom Color Palette - */ -@Stable -class PraxisCloneColorPalette( - brand: Color, - accent: Color, - uiBackground: Color, - textPrimary: Color = brand, - textSecondary: Color, - error: Color, - statusBarColor: Color, - isDark: Boolean, - buttonColor: Color, - buttonTextColor: Color, - darkBackground: Color, - appBarColor: Color, - lineColor: Color, - bottomNavSelectedColor: Color, - bottomNavUnSelectedColor: Color, - appBarIconColor: Color, - appBarTextTitleColor: Color, - appBarTextSubTitleColor: Color, - sendButtonDisabled:Color, - sendButtonEnabled:Color -) { - var brand by mutableStateOf(brand) - private set - var accent by mutableStateOf(accent) - private set - var uiBackground by mutableStateOf(uiBackground) - private set - var statusBarColor by mutableStateOf(statusBarColor) - private set - var textPrimary by mutableStateOf(textPrimary) - private set - var textSecondary by mutableStateOf(textSecondary) - private set - var error by mutableStateOf(error) - private set - var isDark by mutableStateOf(isDark) - private set - var buttonColor by mutableStateOf(buttonColor) - private set - var buttonTextColor by mutableStateOf(buttonTextColor) - private set - var darkBackground by mutableStateOf(darkBackground) - private set - var appBarColor by mutableStateOf(appBarColor) - private set - var lineColor by mutableStateOf(lineColor) - private set - - var bottomNavSelectedColor by mutableStateOf(bottomNavSelectedColor) - private set - var bottomNavUnSelectedColor by mutableStateOf(bottomNavUnSelectedColor) - private set - var appBarIconColor by mutableStateOf(appBarIconColor) - private set - - var appBarTextTitleColor by mutableStateOf(appBarTextTitleColor) - private set - var appBarTextSubTitleColor by mutableStateOf(appBarTextSubTitleColor) - private set - var sendButtonDisabled by mutableStateOf(sendButtonDisabled) - private set - - var sendButtonEnabled by mutableStateOf(sendButtonEnabled) - private set - - - fun update(other: PraxisCloneColorPalette) { - brand = other.brand - uiBackground = other.uiBackground - textPrimary = other.textPrimary - textSecondary = other.textSecondary - error = other.error - statusBarColor = other.statusBarColor - isDark = other.isDark - buttonColor = other.buttonColor - buttonTextColor = other.buttonTextColor - darkBackground = other.darkBackground - appBarColor = other.appBarColor - lineColor = other.lineColor - bottomNavSelectedColor = other.bottomNavSelectedColor - bottomNavUnSelectedColor = other.bottomNavUnSelectedColor - appBarIconColor = other.appBarIconColor - appBarTextTitleColor = other.appBarTextTitleColor - appBarTextSubTitleColor = other.appBarTextSubTitleColor - sendButtonEnabled = other.sendButtonEnabled - sendButtonDisabled = other.sendButtonDisabled - } -} - -@Composable -fun ProvidePraxisCloneColors( - colors: PraxisCloneColorPalette, - content: @Composable () -> Unit -) { - val colorPalette = remember { colors } - colorPalette.update(colors) - CompositionLocalProvider(LocalPraxisCloneColor provides colorPalette, content = content) -} - -private val LocalPraxisCloneColor = staticCompositionLocalOf { - error("No PraxisCloneColorPalette provided") -} - -/** - * A Material [Colors] implementation which sets all colors to [debugColor] to discourage usage of - * [MaterialTheme.colors] in preference to [PraxisColorProvider.colors]. - */ -fun debugColors( - darkTheme: Boolean, - debugColor: Color = Color.Red -) = Colors( - primary = debugColor, - primaryVariant = debugColor, - secondary = debugColor, - secondaryVariant = debugColor, - background = debugColor, - surface = debugColor, - error = debugColor, - onPrimary = debugColor, - onSecondary = debugColor, - onBackground = debugColor, - onSurface = debugColor, - onError = debugColor, - isLight = !darkTheme -) \ No newline at end of file diff --git a/commonui/src/main/java/com/mutualmobile/praxis/commonui/theme/Type.kt b/commonui/src/main/java/com/mutualmobile/praxis/commonui/theme/Type.kt deleted file mode 100644 index 1b40ff3e..00000000 --- a/commonui/src/main/java/com/mutualmobile/praxis/commonui/theme/Type.kt +++ /dev/null @@ -1,39 +0,0 @@ -package com.mutualmobile.praxis.commonui.theme - -import androidx.compose.material.Typography -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.font.Font -import androidx.compose.ui.text.font.FontFamily -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.sp -import com.mutualmobile.praxis.commonui.R - -// Set of Material typography styles to start with - -val praxisFontFamily = - FontFamily( - Font(R.font.lato_bold, weight = FontWeight.Bold), - Font(R.font.lato_light, weight = FontWeight.Light), - Font(R.font.lato_regular) - ) - -val PraxisTypography = Typography( - defaultFontFamily = praxisFontFamily, - body1 = TextStyle( - fontFamily = praxisFontFamily, - fontWeight = FontWeight.Normal, - fontSize = 16.sp - ), - button = TextStyle( - fontFamily = praxisFontFamily, - fontWeight = FontWeight.W500, - fontSize = 14.sp - ), - caption = TextStyle( - fontFamily = praxisFontFamily, - fontWeight = FontWeight.Normal, - fontSize = 12.sp - ) - - -) \ No newline at end of file diff --git a/data/build.gradle.kts b/data/build.gradle.kts index 00e49bff..9b6615ee 100644 --- a/data/build.gradle.kts +++ b/data/build.gradle.kts @@ -36,8 +36,6 @@ dependencies { Lib.Networking.ktorList.forEach(::implementation) /*DI*/ implementation(Lib.Di.hilt) - implementation(Lib.Di.hiltNavigationCompose) - implementation(Lib.Di.viewmodel) kapt(Lib.Di.hiltCompiler) kapt(Lib.Di.hiltAndroidCompiler) diff --git a/navigator/build.gradle.kts b/navigator/build.gradle.kts deleted file mode 100644 index fe83c7e6..00000000 --- a/navigator/build.gradle.kts +++ /dev/null @@ -1,70 +0,0 @@ -plugins { - id(BuildPlugins.ANDROID_LIBRARY_PLUGIN) - id(BuildPlugins.KOTLIN_ANDROID_PLUGIN) - id(BuildPlugins.KOTLIN_KAPT) -} - -android { - compileSdk = AppVersions.COMPILE_SDK - - defaultConfig { - minSdk = (AppVersions.MIN_SDK) - targetSdk = (AppVersions.TARGET_SDK) - testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - } - - buildTypes { - getByName("release") { - isMinifyEnabled = 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" - } - - composeOptions { - kotlinCompilerExtensionVersion = Lib.Androidx.composeVersion - } -} - -// Required for annotation processing plugins like Dagger -kapt { - generateStubs = true - correctErrorTypes = true -} - -dependencies { - /*Kotlin*/ - - - Lib.Androidx.list.forEach(::implementation) - Lib.Androidx.Compose.list.forEach(::implementation) - Lib.ThirdParty.list.forEach(::implementation) - Lib.Accompanist.list.forEach(::implementation) - Lib.Google.list.forEach(::implementation) - Lib.Kotlin.list.forEach(::implementation) - - /*DI*/ - implementation(Lib.Di.hilt) - implementation(Lib.Di.hiltNavigationCompose) - implementation(Lib.Di.viewmodel) - kapt(Lib.Di.hiltCompiler) - kapt(Lib.Di.hiltAndroidCompiler) - - // Room - implementation(Lib.Room.roomKtx) - implementation(Lib.Room.roomRuntime) - add("kapt", Lib.Room.roomCompiler) - testImplementation(Lib.Room.testing) - - UnitTesting.list.forEach(::testImplementation) - DevDependencies.debugList.forEach(::debugImplementation) - DevDependencies.list.forEach(::implementation) -} \ No newline at end of file diff --git a/navigator/src/main/AndroidManifest.xml b/navigator/src/main/AndroidManifest.xml deleted file mode 100644 index 56cf1158..00000000 --- a/navigator/src/main/AndroidManifest.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/navigator/src/main/java/com/mutualmobile/praxis/navigator/NavigationCommand.kt b/navigator/src/main/java/com/mutualmobile/praxis/navigator/NavigationCommand.kt deleted file mode 100644 index 2c0b51bb..00000000 --- a/navigator/src/main/java/com/mutualmobile/praxis/navigator/NavigationCommand.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.mutualmobile.praxis.navigator - -import androidx.navigation.NavOptions - -sealed class NavigationCommand { - object NavigateUp : NavigationCommand() -} - -sealed class ComposeNavigationCommand : NavigationCommand() { - data class NavigateToRoute(val route: String, val options: NavOptions? = null) : - ComposeNavigationCommand() - - data class NavigateUpWithResult( - val key: String, - val result: T, - val route: String? = null - ) : ComposeNavigationCommand() - - data class PopUpToRoute(val route: String, val inclusive: Boolean) : ComposeNavigationCommand() -} \ No newline at end of file diff --git a/navigator/src/main/java/com/mutualmobile/praxis/navigator/NavigationKeys.kt b/navigator/src/main/java/com/mutualmobile/praxis/navigator/NavigationKeys.kt deleted file mode 100644 index ed9291ac..00000000 --- a/navigator/src/main/java/com/mutualmobile/praxis/navigator/NavigationKeys.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.mutualmobile.praxis.navigator - -object NavigationKeys { - - const val ForgotPassword = "forgotPassword" -} \ No newline at end of file diff --git a/navigator/src/main/java/com/mutualmobile/praxis/navigator/Navigator.kt b/navigator/src/main/java/com/mutualmobile/praxis/navigator/Navigator.kt deleted file mode 100644 index 4c4d7fbe..00000000 --- a/navigator/src/main/java/com/mutualmobile/praxis/navigator/Navigator.kt +++ /dev/null @@ -1,94 +0,0 @@ -package com.mutualmobile.praxis.navigator - -import androidx.lifecycle.LiveData -import androidx.lifecycle.Observer -import androidx.navigation.NavController -import androidx.navigation.NavOptionsBuilder -import kotlinx.coroutines.* -import kotlinx.coroutines.channels.Channel -import kotlinx.coroutines.flow.* - -abstract class Navigator { - val navigationCommands = MutableSharedFlow(extraBufferCapacity = Int.MAX_VALUE) - - // We use a StateFlow here to allow ViewModels to start observing navigation results before the initial composition, - // and still get the navigation result later - val navControllerFlow = MutableStateFlow(null) - - fun navigateUp() { - navigationCommands.tryEmit(NavigationCommand.NavigateUp) - } - -} - -abstract class ComposeNavigator : Navigator() { - abstract fun navigate(route: String, optionsBuilder: (NavOptionsBuilder.() -> Unit)? = null) - abstract fun observeResult(key: String, route: String? = null): Flow - abstract fun navigateBackWithResult(key: String, result: T, route: String?) - - abstract fun popUpTo(route: String, inclusive: Boolean) - abstract fun navigateAndClearBackStack(route: String) - - suspend fun handleNavigationCommands(navController: NavController) { - navigationCommands - .onSubscription { this@ComposeNavigator.navControllerFlow.value = navController } - .onCompletion { this@ComposeNavigator.navControllerFlow.value = null } - .collect { navController.handleComposeNavigationCommand(it) } - } - - private fun NavController.handleComposeNavigationCommand(navigationCommand: NavigationCommand) { - when (navigationCommand) { - is ComposeNavigationCommand.NavigateToRoute -> { - navigate(navigationCommand.route, navigationCommand.options) - } - NavigationCommand.NavigateUp -> navigateUp() - is ComposeNavigationCommand.PopUpToRoute -> popBackStack( - navigationCommand.route, - navigationCommand.inclusive - ) - is ComposeNavigationCommand.NavigateUpWithResult<*> -> { - navUpWithResult(navigationCommand) - } - else -> { - throw RuntimeException("can't handle this with ComposeNavigator") - } - } - } - - private fun NavController.navUpWithResult(navigationCommand: ComposeNavigationCommand.NavigateUpWithResult<*>) { - val backStackEntry = - navigationCommand.route?.let { getBackStackEntry(it) } - ?: previousBackStackEntry - backStackEntry?.savedStateHandle?.set( - navigationCommand.key, - navigationCommand.result - ) - - navigationCommand.route?.let { - popBackStack(it, false) - } ?: run { - navigateUp() - } - } -} - - -@OptIn(DelicateCoroutinesApi::class) -fun LiveData.asFlow(): Flow = flow { - val channel = Channel(Channel.CONFLATED) - val observer = Observer { - channel.trySend(it) - } - withContext(Dispatchers.Main.immediate) { - observeForever(observer) - } - try { - for (value in channel) { - emit(value) - } - } finally { - GlobalScope.launch(Dispatchers.Main.immediate) { - removeObserver(observer) - } - } -} \ No newline at end of file diff --git a/navigator/src/main/java/com/mutualmobile/praxis/navigator/Screens.kt b/navigator/src/main/java/com/mutualmobile/praxis/navigator/Screens.kt deleted file mode 100644 index 623b581d..00000000 --- a/navigator/src/main/java/com/mutualmobile/praxis/navigator/Screens.kt +++ /dev/null @@ -1,38 +0,0 @@ -package com.mutualmobile.praxis.navigator - -import androidx.navigation.NamedNavArgument -import androidx.navigation.NavType -import androidx.navigation.navArgument - -sealed class PraxisScreen( - val route: String, - val navArguments: List = emptyList() -) { - val name: String = route.appendArguments(navArguments) - - // onboarding - object GettingStarted : PraxisScreen("gettingStarted") - object SkipTypingScreen : PraxisScreen("SkipTypingUI") - object EmailAddressInputUI : PraxisScreen("EmailAddressInputUI") - object WorkspaceInputUI : PraxisScreen("WorkspaceInputUI") - - object Auth : PraxisScreen("auth") - object ForgotPassword : PraxisScreen("forgotPassword") -} - -sealed class PraxisRoute(val name: String) { - object OnBoarding : PraxisRoute("onboardingRoute") - object Auth : PraxisRoute("authenticationRoute") -} - -private fun String.appendArguments(navArguments: List): String { - val mandatoryArguments = navArguments.filter { it.argument.defaultValue == null } - .takeIf { it.isNotEmpty() } - ?.joinToString(separator = "/", prefix = "/") { "{${it.name}}" } - .orEmpty() - val optionalArguments = navArguments.filter { it.argument.defaultValue != null } - .takeIf { it.isNotEmpty() } - ?.joinToString(separator = "&", prefix = "?") { "${it.name}={${it.name}}" } - .orEmpty() - return "$this$mandatoryArguments$optionalArguments" -} \ No newline at end of file diff --git a/navigator/src/main/java/com/mutualmobile/praxis/navigator/composenavigator/ComposeNavigator.kt b/navigator/src/main/java/com/mutualmobile/praxis/navigator/composenavigator/ComposeNavigator.kt deleted file mode 100644 index 4fcebd70..00000000 --- a/navigator/src/main/java/com/mutualmobile/praxis/navigator/composenavigator/ComposeNavigator.kt +++ /dev/null @@ -1,64 +0,0 @@ -package com.mutualmobile.praxis.navigator.composenavigator - -import androidx.navigation.NavOptionsBuilder -import androidx.navigation.navOptions -import com.mutualmobile.praxis.navigator.ComposeNavigationCommand -import com.mutualmobile.praxis.navigator.ComposeNavigator -import com.mutualmobile.praxis.navigator.asFlow -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.* -import javax.inject.Inject - -class PraxisCloneComposeNavigator @Inject constructor(): ComposeNavigator() { - - override fun navigate(route: String, optionsBuilder: (NavOptionsBuilder.() -> Unit)?) { - val options = optionsBuilder?.let { navOptions(it) } - navigationCommands.tryEmit(ComposeNavigationCommand.NavigateToRoute(route, options)) - } - - override fun navigateAndClearBackStack(route: String) { - navigationCommands.tryEmit(ComposeNavigationCommand.NavigateToRoute(route, navOptions { - popUpTo(0) - })) - } - - override fun popUpTo(route: String, inclusive: Boolean) { - navigationCommands.tryEmit(ComposeNavigationCommand.PopUpToRoute(route, inclusive)) - } - - override fun navigateBackWithResult( - key: String, - result: T, - route: String? - ) { - navigationCommands.tryEmit( - ComposeNavigationCommand.NavigateUpWithResult( - key = key, - result = result, - route = route - ) - ) - } - - @OptIn(ExperimentalCoroutinesApi::class) - override fun observeResult(key: String, route: String?): Flow { - return navControllerFlow - .filterNotNull() - .flatMapLatest { navController -> - val backStackEntry = route?.let { navController.getBackStackEntry(it) } - ?: navController.currentBackStackEntry - - backStackEntry?.savedStateHandle?.let { savedStateHandle -> - savedStateHandle.getLiveData(key) - .asFlow() - .filter { it != null } - .onEach { - // Nullify the result to avoid resubmitting it - savedStateHandle.set(key, null) - } - } ?: emptyFlow() - } - } - - -} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 56f746d1..ede83467 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -2,13 +2,11 @@ include(":app") // Feature modules -include(":ui-onboarding") -include(":ui-authentication") +include(":ui-feature1") // Other modules include(":domain") include(":data") include(":common") include(":commonui") -include(":navigator") diff --git a/ui-authentication/.gitignore b/ui-authentication/.gitignore deleted file mode 100644 index 42afabfd..00000000 --- a/ui-authentication/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build \ No newline at end of file diff --git a/ui-authentication/consumer-rules.pro b/ui-authentication/consumer-rules.pro deleted file mode 100644 index e69de29b..00000000 diff --git a/ui-authentication/proguard-rules.pro b/ui-authentication/proguard-rules.pro deleted file mode 100644 index 481bb434..00000000 --- a/ui-authentication/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# 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 \ No newline at end of file diff --git a/ui-authentication/src/main/java/com/praxis/feat/authentication/nav/AuthNavGraph.kt b/ui-authentication/src/main/java/com/praxis/feat/authentication/nav/AuthNavGraph.kt deleted file mode 100644 index 777d2a56..00000000 --- a/ui-authentication/src/main/java/com/praxis/feat/authentication/nav/AuthNavGraph.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.praxis.feat.authentication.nav - -import androidx.navigation.NavGraphBuilder -import androidx.navigation.compose.composable -import androidx.navigation.compose.navigation -import com.mutualmobile.praxis.navigator.PraxisRoute -import com.mutualmobile.praxis.navigator.PraxisScreen -import com.praxis.feat.authentication.ui.AuthenticationUI -import com.praxis.feat.authentication.ui.ForgotPasswordUI - -fun NavGraphBuilder.authNavGraph() { - navigation( - startDestination = PraxisScreen.Auth.route, - route = PraxisRoute.Auth.name - ) { - composable(PraxisScreen.Auth.route) { - AuthenticationUI() - } - composable(PraxisScreen.ForgotPassword.route) { - ForgotPasswordUI() - } - } -} - diff --git a/ui-authentication/src/main/java/com/praxis/feat/authentication/ui/AuthenticationUI.kt b/ui-authentication/src/main/java/com/praxis/feat/authentication/ui/AuthenticationUI.kt deleted file mode 100644 index 7449c1e5..00000000 --- a/ui-authentication/src/main/java/com/praxis/feat/authentication/ui/AuthenticationUI.kt +++ /dev/null @@ -1,343 +0,0 @@ -package com.praxis.feat.authentication.ui - -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.foundation.Image -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.text.ClickableText -import androidx.compose.foundation.text.KeyboardActions -import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.material.* -import androidx.compose.runtime.* -import androidx.compose.ui.Alignment -import androidx.compose.ui.ExperimentalComposeUiApi -import androidx.compose.ui.Modifier -import androidx.compose.ui.focus.FocusRequester -import androidx.compose.ui.focus.focusRequester -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.platform.LocalSoftwareKeyboardController -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.text.SpanStyle -import androidx.compose.ui.text.buildAnnotatedString -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.input.ImeAction -import androidx.compose.ui.text.input.KeyboardType -import androidx.compose.ui.text.input.PasswordVisualTransformation -import androidx.compose.ui.text.withStyle -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.hilt.navigation.compose.hiltViewModel -import coil.compose.rememberImagePainter -import com.google.accompanist.insets.navigationBarsPadding -import com.google.accompanist.insets.navigationBarsWithImePadding -import com.google.accompanist.insets.statusBarsPadding -import com.mutualmobile.praxis.commonui.material.DefaultSnackbar -import com.mutualmobile.praxis.commonui.material.PraxisSurfaceAppBar -import com.mutualmobile.praxis.commonui.theme.* -import com.praxis.feat.authentication.R -import com.praxis.feat.authentication.vm.AuthVM -import com.praxis.feat.authentication.vm.streamProgress -import com.praxis.feat.authentication.vm.uri - -@Composable -fun AuthenticationUI( - authVM: AuthVM = hiltViewModel(), -) { - PraxisTheme { - val scaffoldState = rememberScaffoldState() - Scaffold( - backgroundColor = PraxisColorProvider.colors.uiBackground, - contentColor = PraxisColorProvider.colors.textSecondary, - modifier = Modifier - .statusBarsPadding() - .navigationBarsPadding(), - topBar = { - PraxisSurfaceAppBar( - title = { - Text( - text = "Authentication", - style = PraxisTypography.h5.copy( - color = Color.White, - fontWeight = FontWeight.Bold - ) - ) - }, - backgroundColor = PraxisColorProvider.colors.appBarColor, - ) - }, scaffoldState = scaffoldState, snackbarHost = { - scaffoldState.snackbarHostState - } - ) { innerPadding -> - Box(modifier = Modifier.padding(innerPadding)) { - AuthSurface( - authVM = authVM, scaffoldState = scaffoldState - ) - DefaultSnackbar(scaffoldState.snackbarHostState) { - authVM.snackBarState.value = "" - scaffoldState.snackbarHostState.currentSnackbarData?.dismiss() - } - } - - } - } -} - -@OptIn(ExperimentalComposeUiApi::class) -@Composable -private fun AuthSurface( - authVM: AuthVM, - scaffoldState: ScaffoldState, -) { - PraxisSurface( - modifier = Modifier - .fillMaxHeight() - .fillMaxWidth() - ) { - - val resetPasswordState by authVM.snackBarState.collectAsState() - val uiState by authVM.formUiState.collectAsState() - val randomPhotoState by authVM.randomPhotoState.collectAsState() - - Box() { - AnimatedVisibility(visible = uiState is AuthVM.UiState.SuccessState || randomPhotoState is AuthVM.UiState.Streaming) { - Column( - verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally - ) { - Box(Modifier.background(Color.Black)) { - Image( - painter = rememberImagePainter(randomPhotoState.uri()), - contentScale = ContentScale.Crop, - contentDescription = null, modifier = Modifier.fillMaxSize(), - ) - - Column( - Modifier.align(Alignment.BottomCenter), - verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally - ) { - Text( - text = randomPhotoState.streamProgress() ?: "", - style = PraxisTypography.subtitle1.copy(color = PraxisColorProvider.colors.appBarTextTitleColor) - ) - - RandomPhotoButton(authVM) - - LogoutButton(authVM) - } - } - - - } - } - Column( - Modifier - .padding(16.dp) - .navigationBarsWithImePadding() - .fillMaxWidth() - .fillMaxHeight(), - verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally - ) { - - val (focusRequester) = FocusRequester.createRefs() - - AnimatedVisibility(visible = randomPhotoState !is AuthVM.UiState.Streaming) { - Image( - painter = painterResource(id = R.mipmap.ic_launcher), - contentDescription = "Logo", Modifier.size(128.dp) - ) - } - - AnimatedVisibility(visible = uiState is AuthVM.UiState.Empty) { - EmailTF(authVM, focusRequester) - } - - AnimatedVisibility(visible = uiState is AuthVM.UiState.Empty) { - PasswordTF(authVM, focusRequester) - } - - AnimatedVisibility(visible = (uiState is AuthVM.UiState.LoadingState)) { - CircularProgressIndicator(modifier = Modifier.padding(8.dp)) - } - - AnimatedVisibility(visible = uiState is AuthVM.UiState.Empty) { - LoginButton(authVM = authVM) - } - - AnimatedVisibility(visible = uiState is AuthVM.UiState.Empty) { - ForgotPasswordText(authVM) - } - - if (resetPasswordState.isNotEmpty()) { - LaunchedEffect(scaffoldState) { - scaffoldState.snackbarHostState.showSnackbar( - message = resetPasswordState, - actionLabel = "Ok" - ) - } - } - } - } - - } -} - - -@Composable -fun RandomPhotoButton(authVM: AuthVM) { - Button( - onClick = { - authVM.fetchRandomPhoto() - }, Modifier.wrapContentWidth(), - colors = ButtonDefaults.buttonColors(backgroundColor = PraxisColorProvider.colors.buttonColor) - ) { - Text( - text = "Load Random Photo", - style = MaterialTheme.typography.body1.copy(color = PraxisColorProvider.colors.buttonTextColor) - ) - } -} - -@Composable -fun LogoutButton(authVM: AuthVM) { - Button( - onClick = { - authVM.logout() - }, Modifier.wrapContentWidth(), - colors = ButtonDefaults.buttonColors(backgroundColor = PraxisColorProvider.colors.buttonColor) - ) { - Text( - text = "Logout", - style = MaterialTheme.typography.body1.copy(color = PraxisColorProvider.colors.buttonTextColor) - ) - } -} - -@Composable -fun ForgotPasswordText(authVM: AuthVM) { - ClickableText(text = buildAnnotatedString { - - withStyle( - style = SpanStyle( - color = PraxisColorProvider.colors.textPrimary, - ) - ) { - append("Forgot Password? ") - } - - }, onClick = { - authVM.navigateForgotPassword() - }, modifier = Modifier.padding(8.dp)) -} - -@Composable -private fun LoginButton( - authVM: AuthVM, -) { - Button( - onClick = { - authVM.loginNow() - }, Modifier.fillMaxWidth(), - colors = ButtonDefaults.buttonColors(backgroundColor = PraxisColorProvider.colors.buttonColor) - ) { - Text( - text = "Login", - style = MaterialTheme.typography.body1.copy(color = PraxisColorProvider.colors.buttonTextColor) - ) - } -} - -@ExperimentalComposeUiApi -@Composable -private fun PasswordTF(authVM: AuthVM, focusRequester: FocusRequester) { - val credentials by authVM.credentials.collectAsState() - val keyboardController = LocalSoftwareKeyboardController.current - - TextField( - value = credentials.password ?: "", - onValueChange = { - authVM.credentials.value = credentials.copy(password = it) - }, - modifier = Modifier - .padding(16.dp) - .focusRequester(focusRequester) - .fillMaxWidth(), - label = { - Text( - text = "Password", - style = MaterialTheme.typography.body2.copy(color = PraxisColorProvider.colors.textPrimary) - ) - }, - shape = PraxisShapes.large, - keyboardActions = KeyboardActions( - onDone = { keyboardController?.hide() }), - keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), - leadingIcon = { - Image( - painter = painterResource(id = R.drawable.ic_eye), - contentDescription = "email" - ) - }, - colors = textFieldColors(), - maxLines = 1, - singleLine = true, - visualTransformation = PasswordVisualTransformation() - ) -} - -@ExperimentalComposeUiApi -@Composable -private fun EmailTF(authVM: AuthVM, focusRequester: FocusRequester) { - val credentials by authVM.credentials.collectAsState() - - TextField( - value = credentials.email ?: "", - onValueChange = { - authVM.credentials.value = credentials.copy(email = it) - }, - Modifier - .padding(16.dp) - .fillMaxWidth(), label = { - Text( - text = "Email", - style = MaterialTheme.typography.body2.copy(color = PraxisColorProvider.colors.textPrimary) - ) - }, - shape = PraxisShapes.large, - keyboardOptions = KeyboardOptions( - imeAction = ImeAction.Next, keyboardType = KeyboardType.Email - ), - keyboardActions = KeyboardActions( - onNext = { - focusRequester.requestFocus() - }, - ), - leadingIcon = { - Image( - painter = painterResource(id = R.drawable.ic_email), - contentDescription = "Email" - ) - }, - colors = textFieldColors(), - maxLines = 1, - singleLine = true - ) -} - -@Composable -private fun textFieldColors() = TextFieldDefaults.textFieldColors( - focusedIndicatorColor = Color.Transparent, - disabledIndicatorColor = Color.Transparent, - unfocusedIndicatorColor = Color.Transparent, - backgroundColor = PraxisColorProvider.colors.accent.copy(alpha = AlphaNearTransparent), -) - -@Preview("Light+Dark") -@Composable -fun PreviewAuth() { - PraxisTheme(isDarkTheme = true) { - AuthenticationUI() - } -} \ No newline at end of file diff --git a/ui-authentication/src/main/java/com/praxis/feat/authentication/ui/ForgotPasswordUI.kt b/ui-authentication/src/main/java/com/praxis/feat/authentication/ui/ForgotPasswordUI.kt deleted file mode 100644 index b444c5d3..00000000 --- a/ui-authentication/src/main/java/com/praxis/feat/authentication/ui/ForgotPasswordUI.kt +++ /dev/null @@ -1,126 +0,0 @@ -package com.praxis.feat.authentication.ui - -import androidx.compose.foundation.Image -import androidx.compose.foundation.layout.* -import androidx.compose.material.* -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.dp -import androidx.hilt.navigation.compose.hiltViewModel -import com.google.accompanist.insets.navigationBarsPadding -import com.google.accompanist.insets.statusBarsPadding -import com.mutualmobile.praxis.commonui.material.PraxisSurfaceAppBar -import com.mutualmobile.praxis.commonui.theme.* -import com.praxis.feat.authentication.R -import com.praxis.feat.authentication.vm.ForgotPasswordVM - -@Composable -fun ForgotPasswordUI(forgotPasswordVM: ForgotPasswordVM = hiltViewModel()){ - PraxisTheme() { - Scaffold( - backgroundColor = PraxisColorProvider.colors.uiBackground, - contentColor = PraxisColorProvider.colors.textSecondary, - modifier = Modifier - .statusBarsPadding() - .navigationBarsPadding(), - topBar = { - PraxisSurfaceAppBar( - title = { - Text( - text = "Forgot password", - style = PraxisTypography.h5.copy( - color = Color.White, - fontWeight = FontWeight.Bold - ) - ) - }, - backgroundColor = PraxisColorProvider.colors.appBarColor, - ) - }) { - ForgotPasswordSurface(forgotPasswordVM) - } - } - -} - -@Composable -private fun ForgotPasswordSurface(forgotPasswordVM: ForgotPasswordVM) { - PraxisSurface( - modifier = Modifier - .fillMaxHeight() - .fillMaxWidth() - ) { - Column( - Modifier - .padding(16.dp) - .fillMaxWidth() - .fillMaxHeight(), - verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally - ) { - Image( - painter = painterResource(id = R.mipmap.ic_launcher), - contentDescription = "Logo", Modifier.size(128.dp) - ) - - EmailTF(forgotPasswordVM) - - ForgotPasswordButton(forgotPasswordVM) - - } - } -} - -@Composable -private fun ForgotPasswordButton(forgotPasswordVM: ForgotPasswordVM) { - Button( - onClick = { - forgotPasswordVM.navigateBack() - }, Modifier.fillMaxWidth(), - colors = ButtonDefaults.buttonColors(backgroundColor = PraxisColorProvider.colors.buttonColor) - ) { - Text( - text = "Reset Password", - style = MaterialTheme.typography.body1.copy(color = PraxisColorProvider.colors.buttonTextColor) - ) - } -} - -@Composable -private fun EmailTF(forgotPasswordVM: ForgotPasswordVM) { - TextField( - value = forgotPasswordVM.email.value, onValueChange = { - forgotPasswordVM.email.value = it - }, - Modifier - .padding(16.dp) - .fillMaxWidth(), label = { - Text( - text = "Email", - style = MaterialTheme.typography.body2.copy(color = PraxisColorProvider.colors.textPrimary) - ) - }, - shape = PraxisShapes.large, - leadingIcon = { - Image( - painter = painterResource(id = R.drawable.ic_email), - contentDescription = "Email" - ) - }, - colors = textFieldColors(), - maxLines = 1, - singleLine = true - ) -} - -@Composable -private fun textFieldColors() = TextFieldDefaults.textFieldColors( - focusedIndicatorColor = Color.Transparent, - disabledIndicatorColor = Color.Transparent, - unfocusedIndicatorColor = Color.Transparent, - backgroundColor = PraxisColorProvider.colors.accent.copy(alpha = AlphaNearTransparent), -) \ No newline at end of file diff --git a/ui-authentication/src/main/java/com/praxis/feat/authentication/vm/AuthVM.kt b/ui-authentication/src/main/java/com/praxis/feat/authentication/vm/AuthVM.kt deleted file mode 100644 index ecb1eaf1..00000000 --- a/ui-authentication/src/main/java/com/praxis/feat/authentication/vm/AuthVM.kt +++ /dev/null @@ -1,148 +0,0 @@ -package com.praxis.feat.authentication.vm - -import android.net.Uri -import androidx.lifecycle.* -import com.mutualmobile.praxis.domain.model.StreamingFile -import com.mutualmobile.praxis.domain.usecases.FetchRandomPhotoUseCase -import com.mutualmobile.praxis.navigator.ComposeNavigator -import com.mutualmobile.praxis.navigator.NavigationKeys -import com.mutualmobile.praxis.navigator.PraxisScreen -import com.praxis.feat.authentication.ui.exceptions.FormValidationFailed -import com.praxis.feat.authentication.ui.model.LoginForm -import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.* -import kotlinx.coroutines.flow.* -import java.io.FileNotFoundException -import java.net.UnknownHostException -import javax.inject.Inject - -@HiltViewModel -class AuthVM @Inject constructor( - private val savedStateHandle: SavedStateHandle, - private val composeNavigator: ComposeNavigator, - private val fetchPhotoUseCase: FetchRandomPhotoUseCase -) : ViewModel() { - - private var fetchJob: Job? = null - - var credentials = MutableStateFlow(LoginForm()) - private set - var snackBarState = MutableStateFlow("") - private set - var formUiState = MutableStateFlow(UiState.Empty) - private set - var randomPhotoState = MutableStateFlow(UiState.Empty) - private set - - private val exceptionHandler = CoroutineExceptionHandler { _, throwable -> - when (throwable) { - is FormValidationFailed -> { - snackBarState.value = throwable.failType.message - } - is FileNotFoundException -> { - snackBarState.value = "File Not found" - } - is CancellationException -> { - when (throwable.cause) { - is UnknownHostException -> { - snackBarState.value = "No Network" - } - else -> { - snackBarState.value = "Some error happened" - } - } - } - else -> { - snackBarState.value = (throwable.message ?: "") - } - } - - formUiState.value = UiState.ErrorState(throwable) - formUiState.value = UiState.Empty - } - - init { - observePasswordReset() - } - - private fun observePasswordReset() { - composeNavigator.observeResult(NavigationKeys.ForgotPassword).onStart { - val message = savedStateHandle.get(NavigationKeys.ForgotPassword) - message?.let { - emit(it) - } - } - .onEach { snackBarState.value = it } - .launchIn(viewModelScope) - } - - fun loginNow() { - viewModelScope.launch(exceptionHandler) { - formUiState.value = UiState.LoadingState - credentials.value.validate() - snackBarState.value = "" - delay(1500) - formUiState.value = UiState.SuccessState("some-token") - fetchRandomPhoto() - } - } - - fun navigateForgotPassword() { - composeNavigator.navigate(PraxisScreen.ForgotPassword.route) - } - - fun logout() { - viewModelScope.launch(exceptionHandler) { - formUiState.value = UiState.LoadingState - randomPhotoState.value = UiState.Empty - delay(1500) - snackBarState.value = "Logged out!" - formUiState.value = UiState.Empty - } - } - - - fun fetchRandomPhoto() { - fetchJob?.cancel() - fetchJob = viewModelScope.launch(exceptionHandler) { - fetchPhotoUseCase.performStreaming(null).handleErrors().collect { streamingFile -> - randomPhotoState.value = UiState.Streaming(streamingFile) - } - } - } - - private fun Flow.handleErrors(): Flow = flow { - try { - collect { value -> emit(value) } - } catch (e: Throwable) { - formUiState.value = UiState.ErrorState(e) - } - } - - sealed class UiState { - object Empty : UiState() - data class Streaming(val result: StreamingFile) : UiState() - object LoadingState : UiState() - data class SuccessState( - val authToken: String, - ) : UiState() - - data class ErrorState(val throwable: Throwable) : UiState() - } -} - -fun AuthVM.UiState.uri(): Uri? { - return if (this is AuthVM.UiState.Streaming && this.result.isComplete) { - Uri.fromFile(this.result.file) - } else { - null - } -} - -fun AuthVM.UiState.streamProgress(): String? { - return if (this is AuthVM.UiState.Streaming) { - return "${this.result.progress} bytes downloaded." - } else { - null - } -} \ No newline at end of file diff --git a/ui-authentication/src/main/java/com/praxis/feat/authentication/vm/ForgotPasswordVM.kt b/ui-authentication/src/main/java/com/praxis/feat/authentication/vm/ForgotPasswordVM.kt deleted file mode 100644 index 1b9cb21c..00000000 --- a/ui-authentication/src/main/java/com/praxis/feat/authentication/vm/ForgotPasswordVM.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.praxis.feat.authentication.vm - -import androidx.compose.runtime.mutableStateOf -import androidx.lifecycle.ViewModel -import com.mutualmobile.praxis.navigator.ComposeNavigator -import com.mutualmobile.praxis.navigator.NavigationKeys -import com.mutualmobile.praxis.navigator.PraxisScreen -import dagger.hilt.android.lifecycle.HiltViewModel -import javax.inject.Inject - -@HiltViewModel -class ForgotPasswordVM @Inject constructor(private val navigator: ComposeNavigator) : ViewModel() { - var email = mutableStateOf("") - - fun navigateBack() { - navigator.navigateBackWithResult( - NavigationKeys.ForgotPassword, "Reset Password Done!", - PraxisScreen.Auth.route - ) - } -} \ No newline at end of file diff --git a/navigator/.gitignore b/ui-feature1/.gitignore similarity index 100% rename from navigator/.gitignore rename to ui-feature1/.gitignore diff --git a/ui-authentication/build.gradle.kts b/ui-feature1/build.gradle.kts similarity index 90% rename from ui-authentication/build.gradle.kts rename to ui-feature1/build.gradle.kts index f976ad7b..fd52f42e 100644 --- a/ui-authentication/build.gradle.kts +++ b/ui-feature1/build.gradle.kts @@ -24,7 +24,7 @@ android { } buildFeatures { - compose = true + viewBinding = true } composeOptions { @@ -59,15 +59,10 @@ dependencies { implementation(project(":data")) implementation(project(":domain")) implementation(project(":common")) - implementation(project(":navigator")) implementation(project(":commonui")) - Lib.Androidx.list.forEach(::implementation) - Lib.Androidx.Compose.list.forEach(::implementation) - Lib.ThirdParty.list.forEach(::implementation) - Lib.Accompanist.list.forEach(::implementation) Lib.Google.list.forEach(::implementation) Lib.Kotlin.list.forEach(::implementation) diff --git a/navigator/consumer-rules.pro b/ui-feature1/consumer-rules.pro similarity index 100% rename from navigator/consumer-rules.pro rename to ui-feature1/consumer-rules.pro diff --git a/navigator/proguard-rules.pro b/ui-feature1/proguard-rules.pro similarity index 100% rename from navigator/proguard-rules.pro rename to ui-feature1/proguard-rules.pro diff --git a/ui-authentication/src/main/AndroidManifest.xml b/ui-feature1/src/main/AndroidManifest.xml similarity index 100% rename from ui-authentication/src/main/AndroidManifest.xml rename to ui-feature1/src/main/AndroidManifest.xml diff --git a/ui-authentication/src/main/java/com/praxis/feat/authentication/services/Validatable.kt b/ui-feature1/src/main/java/com/praxis/feat/authentication/services/Validatable.kt similarity index 100% rename from ui-authentication/src/main/java/com/praxis/feat/authentication/services/Validatable.kt rename to ui-feature1/src/main/java/com/praxis/feat/authentication/services/Validatable.kt diff --git a/ui-feature1/src/main/java/com/praxis/feat/authentication/ui/MainActivity.kt b/ui-feature1/src/main/java/com/praxis/feat/authentication/ui/MainActivity.kt new file mode 100644 index 00000000..b8992056 --- /dev/null +++ b/ui-feature1/src/main/java/com/praxis/feat/authentication/ui/MainActivity.kt @@ -0,0 +1,99 @@ +package com.praxis.feat.authentication.ui + +import android.net.Uri +import androidx.appcompat.app.AppCompatActivity +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import androidx.activity.viewModels +import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen +import com.google.android.material.snackbar.BaseTransientBottomBar.LENGTH_SHORT +import com.google.android.material.snackbar.Snackbar +import com.mutualmobile.praxis.domain.model.StreamingFile +import com.praxis.feat.authentication.R +import com.praxis.feat.authentication.databinding.ViewRandomPhotosBinding +import com.praxis.feat.authentication.vm.RandomViewModel +import dagger.hilt.android.AndroidEntryPoint +import timber.log.Timber +import java.io.FileNotFoundException +import java.net.UnknownHostException +import kotlin.coroutines.cancellation.CancellationException + +@AndroidEntryPoint +class MainActivity : AppCompatActivity() { + private val viewModel: RandomViewModel by viewModels() + private lateinit var binding: ViewRandomPhotosBinding + + override fun onCreate(savedInstanceState: Bundle?) { + installSplashScreen() + super.onCreate(savedInstanceState) + binding = ViewRandomPhotosBinding.inflate(LayoutInflater.from(this)) + setContentView(binding.root) + viewModel.randomViewState.observe(this) { randomPhotoViewState -> + randomPhotoViewState?.let { + receiveViewState(randomPhotoViewState) + } + } + binding.randomPhotoButton.setOnClickListener { + viewModel.fetchRandomPhoto() + } + } + + private fun receiveViewState(randomPhotoViewState: RandomViewModel.RandomPhotoViewState) { + when (randomPhotoViewState) { + is RandomViewModel.RandomPhotoViewState.Empty -> { + Timber.e("No Data") + setImageForFile(randomPhotoViewState.result) + } + is RandomViewModel.RandomPhotoViewState.Exception -> { + randomPhotoViewState.throwable.printStackTrace() + Timber.e(randomPhotoViewState.throwable.message ?: "") + when (randomPhotoViewState.throwable) { + is FileNotFoundException -> { + showSnackbarMessage(getString(R.string.except_file_not_found)) + } + is CancellationException -> { + when (randomPhotoViewState.throwable.cause) { + is UnknownHostException -> { + showSnackbarMessage(getString(R.string.except_no_network)) + } + else -> { + showSnackbarMessage(getString(R.string.except_generic)) + } + } + } + else -> { + showSnackbarMessage(randomPhotoViewState.throwable.message ?: "") + } + } + } + is RandomViewModel.RandomPhotoViewState.Streaming -> { + binding.progressText.visibility = View.VISIBLE + + if (randomPhotoViewState.result.isComplete) { + setImageForFile(randomPhotoViewState.result) + } else { + binding.progressText.text = + getString(R.string.downloading_glue, randomPhotoViewState.result.progress) + } + Timber.d( + "${randomPhotoViewState.result.file.absolutePath} ${randomPhotoViewState.result.file.length()}" + ) + } + } + } + + private fun setImageForFile(file: StreamingFile?) { + binding.photoView.setImageResource(android.R.drawable.gallery_thumb) // to avoid redraw issues + file?.let { streamingFile -> + binding.photoView.setImageURI(Uri.fromFile(streamingFile.file)) + binding.progressText.visibility = View.VISIBLE + binding.progressText.text = + getString(R.string.downloaded_glue, streamingFile.file.length()) + } + } + + private fun showSnackbarMessage(message: String) { + Snackbar.make(binding.root, message, LENGTH_SHORT).show() + } +} \ No newline at end of file diff --git a/ui-authentication/src/main/java/com/praxis/feat/authentication/ui/exceptions/FormValidationFailed.kt b/ui-feature1/src/main/java/com/praxis/feat/authentication/ui/exceptions/FormValidationFailed.kt similarity index 100% rename from ui-authentication/src/main/java/com/praxis/feat/authentication/ui/exceptions/FormValidationFailed.kt rename to ui-feature1/src/main/java/com/praxis/feat/authentication/ui/exceptions/FormValidationFailed.kt diff --git a/ui-authentication/src/main/java/com/praxis/feat/authentication/ui/model/FailureType.kt b/ui-feature1/src/main/java/com/praxis/feat/authentication/ui/model/FailureType.kt similarity index 100% rename from ui-authentication/src/main/java/com/praxis/feat/authentication/ui/model/FailureType.kt rename to ui-feature1/src/main/java/com/praxis/feat/authentication/ui/model/FailureType.kt diff --git a/ui-authentication/src/main/java/com/praxis/feat/authentication/ui/model/LoginForm.kt b/ui-feature1/src/main/java/com/praxis/feat/authentication/ui/model/LoginForm.kt similarity index 100% rename from ui-authentication/src/main/java/com/praxis/feat/authentication/ui/model/LoginForm.kt rename to ui-feature1/src/main/java/com/praxis/feat/authentication/ui/model/LoginForm.kt diff --git a/ui-feature1/src/main/java/com/praxis/feat/authentication/vm/RandomViewModel.kt b/ui-feature1/src/main/java/com/praxis/feat/authentication/vm/RandomViewModel.kt new file mode 100644 index 00000000..95975bc5 --- /dev/null +++ b/ui-feature1/src/main/java/com/praxis/feat/authentication/vm/RandomViewModel.kt @@ -0,0 +1,73 @@ +package com.praxis.feat.authentication.vm + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.mutualmobile.praxis.domain.model.StreamingFile +import com.mutualmobile.praxis.domain.repositories.RandomFileService +import com.mutualmobile.praxis.domain.usecases.FetchRandomPhotoUseCase +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.CoroutineExceptionHandler +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +class RandomViewModel @Inject constructor( + private val fetchPhotoUseCase: FetchRandomPhotoUseCase, + private val randomFileService: RandomFileService +) : + ViewModel() { + private var fetchJob: Job? = null + private val viewState = MutableLiveData( + RandomPhotoViewState.Empty(null) + ) + + init { + initialState() + } + + private fun initialState() { + viewModelScope.launch { + val file = randomFileService.getTempFile() + val initialState = RandomPhotoViewState.Empty( + StreamingFile(file.length(), file, true) + ) + viewState.value = initialState + } + } + + val randomViewState: LiveData = viewState + + private val exceptionHandler: CoroutineExceptionHandler = + CoroutineExceptionHandler { _, throwable -> + viewState.value = RandomPhotoViewState.Exception(throwable) + } + + fun fetchRandomPhoto() { + fetchJob?.cancel() + fetchJob = viewModelScope.launch(exceptionHandler) { + fetchPhotoUseCase.performStreaming(null).handleErrors().collect { streamingFile -> + viewState.value = RandomPhotoViewState.Streaming(streamingFile) + } + } + } + + sealed class RandomPhotoViewState { + class Exception(val throwable: Throwable) : RandomPhotoViewState() + class Streaming(val result: StreamingFile) : RandomPhotoViewState() + class Empty(val result: StreamingFile?) : RandomPhotoViewState() + } + + private fun Flow.handleErrors(): Flow = flow { + try { + collect { value -> emit(value) } + } catch (e: Throwable) { + viewState.value = RandomPhotoViewState.Exception(e) + } + } +} \ No newline at end of file diff --git a/ui-feature1/src/main/res/layout/view_random_photos.xml b/ui-feature1/src/main/res/layout/view_random_photos.xml new file mode 100644 index 00000000..8626ad57 --- /dev/null +++ b/ui-feature1/src/main/res/layout/view_random_photos.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ui-feature1/src/main/res/values/colors.xml b/ui-feature1/src/main/res/values/colors.xml new file mode 100644 index 00000000..1aba7060 --- /dev/null +++ b/ui-feature1/src/main/res/values/colors.xml @@ -0,0 +1,4 @@ + + + #000000 + \ No newline at end of file diff --git a/ui-feature1/src/main/res/values/dimens.xml b/ui-feature1/src/main/res/values/dimens.xml new file mode 100644 index 00000000..25e630d8 --- /dev/null +++ b/ui-feature1/src/main/res/values/dimens.xml @@ -0,0 +1,5 @@ + + + 200dp + 8dp + \ No newline at end of file diff --git a/ui-feature1/src/main/res/values/strings.xml b/ui-feature1/src/main/res/values/strings.xml new file mode 100644 index 00000000..0529baf1 --- /dev/null +++ b/ui-feature1/src/main/res/values/strings.xml @@ -0,0 +1,9 @@ + + + Random Photo + Could not save the image, FileNotFound! + Downloaded Size %d bytes + Downloading %d bytes + No network available! + Some error occurred + \ No newline at end of file diff --git a/ui-authentication/src/test/java/com/praxis/feat/authentication/vm/AuthVMTest.kt b/ui-feature1/src/test/java/com/praxis/feat/authentication/vm/AuthVMTest.kt similarity index 100% rename from ui-authentication/src/test/java/com/praxis/feat/authentication/vm/AuthVMTest.kt rename to ui-feature1/src/test/java/com/praxis/feat/authentication/vm/AuthVMTest.kt diff --git a/ui-onboarding/.gitignore b/ui-onboarding/.gitignore deleted file mode 100644 index 42afabfd..00000000 --- a/ui-onboarding/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build \ No newline at end of file diff --git a/ui-onboarding/build.gradle.kts b/ui-onboarding/build.gradle.kts deleted file mode 100644 index ced77d9d..00000000 --- a/ui-onboarding/build.gradle.kts +++ /dev/null @@ -1,91 +0,0 @@ -plugins { - id(BuildPlugins.ANDROID_LIBRARY_PLUGIN) - id(BuildPlugins.KOTLIN_ANDROID_PLUGIN) - id(BuildPlugins.KOTLIN_KAPT) - id(BuildPlugins.DAGGER_HILT) - id(BuildPlugins.KOTLIN_PARCELABLE_PLUGIN) - id("org.jlleitschuh.gradle.ktlint") -} - -android { - compileSdk = AppVersions.COMPILE_SDK - - defaultConfig { - minSdk = (AppVersions.MIN_SDK) - targetSdk = (AppVersions.TARGET_SDK) - testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - } - - buildTypes { - getByName("release") { - isMinifyEnabled = false - proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") - } - } - - buildFeatures { - compose = true - } - - composeOptions { - kotlinCompilerExtensionVersion = Lib.Androidx.composeVersion - } - packagingOptions { - resources.excludes.add("META-INF/LICENSE.txt") - resources.excludes.add("META-INF/NOTICE.txt") - resources.excludes.add("LICENSE.txt") - resources.excludes.add( "/META-INF/{AL2.0,LGPL2.1}") - } - - compileOptions { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 - } - - kotlinOptions { - jvmTarget = "1.8" - } - -} - -// Required for annotation processing plugins like Dagger -kapt { - generateStubs = true - correctErrorTypes = true -} - -dependencies { - - - /*Kotlin*/ - implementation(project(":data")) - implementation(project(":domain")) - implementation(project(":common")) - implementation(project(":navigator")) - implementation(project(":commonui")) - - - Lib.Androidx.list.forEach(::implementation) - Lib.Androidx.Compose.list.forEach(::implementation) - Lib.ThirdParty.list.forEach(::implementation) - Lib.Accompanist.list.forEach(::implementation) - Lib.Google.list.forEach(::implementation) - Lib.Kotlin.list.forEach(::implementation) - - /*DI*/ - implementation(Lib.Di.hilt) - implementation(Lib.Di.hiltNavigationCompose) - implementation(Lib.Di.viewmodel) - kapt(Lib.Di.hiltCompiler) - kapt(Lib.Di.hiltAndroidCompiler) - - // Room - implementation(Lib.Room.roomKtx) - implementation(Lib.Room.roomRuntime) - kapt(Lib.Room.roomCompiler) - testImplementation(Lib.Room.testing) - - UnitTesting.list.forEach(::testImplementation) - DevDependencies.debugList.forEach(::debugImplementation) - DevDependencies.list.forEach(::implementation) -} \ No newline at end of file diff --git a/ui-onboarding/consumer-rules.pro b/ui-onboarding/consumer-rules.pro deleted file mode 100644 index e69de29b..00000000 diff --git a/ui-onboarding/proguard-rules.pro b/ui-onboarding/proguard-rules.pro deleted file mode 100644 index 481bb434..00000000 --- a/ui-onboarding/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# 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 \ No newline at end of file diff --git a/ui-onboarding/src/androidTest/java/com/mutualmobile/praxis/uionboarding/ExampleInstrumentedTest.kt b/ui-onboarding/src/androidTest/java/com/mutualmobile/praxis/uionboarding/ExampleInstrumentedTest.kt deleted file mode 100644 index abeba489..00000000 --- a/ui-onboarding/src/androidTest/java/com/mutualmobile/praxis/uionboarding/ExampleInstrumentedTest.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.mutualmobile.praxis.uionboarding - -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("com.mutualmobile.praxis.uionboarding.test", appContext.packageName) - } -} \ No newline at end of file diff --git a/ui-onboarding/src/main/AndroidManifest.xml b/ui-onboarding/src/main/AndroidManifest.xml deleted file mode 100644 index ad77223a..00000000 --- a/ui-onboarding/src/main/AndroidManifest.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/ui-onboarding/src/main/java/com/mutualmobile/praxis/uionboarding/compose/CommonInputUI.kt b/ui-onboarding/src/main/java/com/mutualmobile/praxis/uionboarding/compose/CommonInputUI.kt deleted file mode 100644 index 65592eb2..00000000 --- a/ui-onboarding/src/main/java/com/mutualmobile/praxis/uionboarding/compose/CommonInputUI.kt +++ /dev/null @@ -1,119 +0,0 @@ -package com.mutualmobile.praxis.uionboarding.compose - -import androidx.compose.foundation.layout.* -import androidx.compose.material.* -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.unit.dp -import androidx.constraintlayout.compose.ConstraintLayout -import com.google.accompanist.insets.navigationBarsPadding -import com.google.accompanist.insets.navigationBarsWithImePadding -import com.google.accompanist.insets.statusBarsPadding -import com.mutualmobile.praxis.commonui.theme.PraxisSurface -import com.mutualmobile.praxis.commonui.theme.PraxisTheme -import com.mutualmobile.praxis.commonui.theme.PraxisColorProvider -import com.mutualmobile.praxis.commonui.theme.PraxisTypography -import com.mutualmobile.praxis.navigator.ComposeNavigator -import com.mutualmobile.praxis.navigator.PraxisRoute -import com.mutualmobile.praxis.navigator.PraxisScreen - -@Composable -fun CommonInputUI( - composeNavigator: ComposeNavigator, - TopView: @Composable (modifier: Modifier) -> Unit, - subtitleText: String -) { - val scaffoldState = rememberScaffoldState() - - PraxisTheme { - Scaffold( - backgroundColor = PraxisColorProvider.colors.uiBackground, - contentColor = PraxisColorProvider.colors.textSecondary, - modifier = Modifier - .statusBarsPadding() - .navigationBarsPadding(), - scaffoldState = scaffoldState, - snackbarHost = { - scaffoldState.snackbarHostState - } - ) { innerPadding -> - Box(modifier = Modifier.padding(innerPadding)) { - PraxisSurface( - color = PraxisColorProvider.colors.uiBackground, - modifier = Modifier - ) { - ConstraintLayout( - modifier = Modifier - .padding(12.dp) - .navigationBarsWithImePadding() - .fillMaxHeight() - .fillMaxWidth() - ) { - // Create references for the composables to constrain - val (inputView, subtitle, button) = createRefs() - - TopView(modifier = Modifier.constrainAs(inputView) { - top.linkTo(parent.top) - bottom.linkTo(button.top) - start.linkTo(parent.start) - end.linkTo(parent.end) - }) - SubTitle(modifier = Modifier.constrainAs(subtitle) { - top.linkTo(inputView.bottom) - }, subtitleText) - NextButton(modifier = Modifier.constrainAs(button) { - bottom.linkTo(parent.bottom) - start.linkTo(parent.start) - end.linkTo(parent.end) - }, composeNavigator) - } - } - } - - } - } -} - -@Composable -fun NextButton(modifier: Modifier = Modifier, composeNavigator: ComposeNavigator) { - Button( - onClick = { - composeNavigator.navigate(PraxisRoute.Auth.name) { - this.popUpTo(PraxisRoute.OnBoarding.name) { - this.inclusive = true - } - } - }, - modifier - .fillMaxWidth() - .height(50.dp) - .padding(top = 8.dp), - colors = ButtonDefaults.buttonColors( - backgroundColor = PraxisColorProvider.colors.buttonColor - ) - ) { - Text( - text = "Next", - style = PraxisTypography.subtitle2.copy(color = PraxisColorProvider.colors.buttonTextColor) - ) - } -} - -@Composable -private fun SubTitle(modifier: Modifier = Modifier, subtitleText: String) { - Text( - subtitleText, - modifier = modifier - .fillMaxWidth() - .wrapContentWidth(align = Alignment.Start), - style = PraxisTypography.subtitle2.copy( - color = PraxisColorProvider.colors.textPrimary.copy(alpha = 0.8f), - fontWeight = FontWeight.Normal, - textAlign = TextAlign.Start - ) - ) -} - diff --git a/ui-onboarding/src/main/java/com/mutualmobile/praxis/uionboarding/compose/EmailInputView.kt b/ui-onboarding/src/main/java/com/mutualmobile/praxis/uionboarding/compose/EmailInputView.kt deleted file mode 100644 index cd109f50..00000000 --- a/ui-onboarding/src/main/java/com/mutualmobile/praxis/uionboarding/compose/EmailInputView.kt +++ /dev/null @@ -1,100 +0,0 @@ -package com.mutualmobile.praxis.uionboarding.compose - -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.text.KeyboardActions -import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.material.Icon -import androidx.compose.material.Text -import androidx.compose.material.TextField -import androidx.compose.material.TextFieldDefaults -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Email -import androidx.compose.runtime.* -import androidx.compose.ui.Alignment -import androidx.compose.ui.ExperimentalComposeUiApi -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalSoftwareKeyboardController -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.input.ImeAction -import androidx.compose.ui.text.input.KeyboardType -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.unit.dp -import com.mutualmobile.praxis.commonui.theme.PraxisColorProvider -import com.mutualmobile.praxis.commonui.theme.PraxisTypography - -@OptIn(ExperimentalComposeUiApi::class) -@Composable -fun EmailInputView(modifier: Modifier = Modifier) { - Column( - modifier = modifier - .fillMaxWidth() - .wrapContentWidth() - ) { - Text( - text = "Email", style = PraxisTypography.caption.copy( - color = PraxisColorProvider.colors.textPrimary.copy(alpha = 0.7f), - fontWeight = FontWeight.Normal, - textAlign = TextAlign.Start - ), modifier = Modifier.padding(bottom = 4.dp) - ) - Row( - modifier = modifier - .fillMaxWidth(), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.Start - ) { - EmailTF() - } - } - -} - -@ExperimentalComposeUiApi -@Composable -private fun EmailTF() { - var email by remember { mutableStateOf("") } - val keyboardController = LocalSoftwareKeyboardController.current - - TextField( - value = email, - onValueChange = { newEmail -> - email = newEmail - }, - textStyle = textStyleField(), - leadingIcon = { - Icon(imageVector = Icons.Default.Email, contentDescription = null, tint = PraxisColorProvider.colors.textPrimary) - }, - placeholder = { - Text( - text = "Your email address", - style = textStyleField(), - textAlign = TextAlign.Start - ) - }, - keyboardOptions = KeyboardOptions.Default.copy( - autoCorrect = false, - keyboardType = KeyboardType.Email, - imeAction = ImeAction.Done, - ), - keyboardActions = KeyboardActions(onDone = { keyboardController?.hide() }), - colors = textFieldColors(), - singleLine = true, - maxLines = 1 - ) -} - -@Composable -private fun textFieldColors() = TextFieldDefaults.textFieldColors( - backgroundColor = Color.Transparent, - cursorColor = PraxisColorProvider.colors.textPrimary, - unfocusedIndicatorColor = Color.Transparent, - focusedIndicatorColor = Color.Transparent -) - -@Composable -private fun textStyleField() = PraxisTypography.h6.copy( - color = PraxisColorProvider.colors.textPrimary, - fontWeight = FontWeight.Normal, - textAlign = TextAlign.Start -) \ No newline at end of file diff --git a/ui-onboarding/src/main/java/com/mutualmobile/praxis/uionboarding/compose/GettingStarted.kt b/ui-onboarding/src/main/java/com/mutualmobile/praxis/uionboarding/compose/GettingStarted.kt deleted file mode 100644 index 270d36b0..00000000 --- a/ui-onboarding/src/main/java/com/mutualmobile/praxis/uionboarding/compose/GettingStarted.kt +++ /dev/null @@ -1,210 +0,0 @@ -package com.mutualmobile.praxis.uionboarding.compose - -import androidx.compose.animation.* -import androidx.compose.foundation.Image -import androidx.compose.foundation.layout.* -import androidx.compose.material.* -import androidx.compose.runtime.* -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.text.SpanStyle -import androidx.compose.ui.text.buildAnnotatedString -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.text.withStyle -import androidx.compose.ui.unit.Density -import androidx.compose.ui.unit.dp -import com.google.accompanist.insets.statusBarsPadding -import com.google.accompanist.systemuicontroller.rememberSystemUiController -import com.mutualmobile.praxis.commonui.theme.* -import com.mutualmobile.praxis.navigator.ComposeNavigator -import com.mutualmobile.praxis.navigator.PraxisScreen -import com.mutualmobile.praxis.uionboarding.R - -@Composable -fun GettingStartedUI(composeNavigator: ComposeNavigator) { - PraxisTheme { - val scaffoldState = rememberScaffoldState() - val sysUiController = rememberSystemUiController() - - SideEffect { - sysUiController.setNavigationBarColor(color = PraxisCloneColor) - sysUiController.setSystemBarsColor(color = PraxisCloneColor) - } - - Scaffold( - backgroundColor = PraxisCloneColor, - contentColor = PraxisColorProvider.colors.textSecondary, - modifier = Modifier.statusBarsPadding(), scaffoldState = scaffoldState, snackbarHost = { - scaffoldState.snackbarHostState - } - ) { innerPadding -> - Box(modifier = Modifier.padding(innerPadding)) { - PraxisSurface( - color = PraxisCloneColor, - modifier = Modifier - .padding(28.dp) - ) { - Column( - verticalArrangement = Arrangement.SpaceAround, - horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier - .fillMaxHeight() - .fillMaxWidth() - ) { - IntroText(modifier = Modifier.padding(top = 12.dp)) - CenterImage() - Spacer(Modifier.padding(8.dp)) - GetStartedButton(composeNavigator) - } - - } - } - - } - } -} - -@Composable -private fun CenterImage() { - - var expanded by remember { mutableStateOf(false) } - - LaunchedEffect(Unit) { - expanded = !expanded - } - AnimatedVisibility( - visible = expanded, enter = ImageEnterTransition(), - exit = ImageExitTrans() - ) { - Image( - painter = painterResource(id = R.drawable.gettingstarted), - contentDescription = "Logo", - Modifier - ) - } -} - -@Composable -private fun ImageExitTrans() = shrinkOut() + fadeOut() - -@Composable -private fun ImageEnterTransition() = expandIn( - expandFrom = Alignment.Center -) + fadeIn( - // Fade in with the initial alpha of 0.3f. - initialAlpha = 0.3f -) - -@OptIn(ExperimentalAnimationApi::class) -@Composable -private fun GetStartedButton(composeNavigator: ComposeNavigator) { - var expanded by remember { mutableStateOf(false) } - - LaunchedEffect(Unit) { - expanded = !expanded - } - val density = LocalDensity.current - - AnimatedVisibility( - visible = expanded, enter = GetStartedEnterTransition(density), - exit = GetStartedExitTrans() - ) { - Button( - onClick = { - composeNavigator.navigate(PraxisScreen.SkipTypingScreen.name) - }, - Modifier - .fillMaxWidth() - .height(40.dp), - colors = ButtonDefaults.buttonColors(backgroundColor = Color.White) - ) { - Text( - text = "Get started", - style = PraxisTypography.subtitle2.copy(color = PraxisCloneColor) - ) - } - } -} - -@Composable -private fun GetStartedExitTrans() = slideOutVertically() + shrinkVertically() + fadeOut() - -@Composable -private fun GetStartedEnterTransition(density: Density) = - slideInVertically { - // Slide in from 40 dp from the bottom. - with(density) { +5680.dp.roundToPx() } - } + expandVertically( - // Expand from the top. - expandFrom = Alignment.Top - ) + fadeIn( - // Fade in with the initial alpha of 0.3f. - initialAlpha = 0.3f - ) - -@Composable -private fun IntroText(modifier: Modifier = Modifier) { - var expanded by remember { mutableStateOf(false) } - - LaunchedEffect(Unit) { - expanded = !expanded - } - val density = LocalDensity.current - - AnimatedVisibility( - visible = expanded, enter = IntroEnterTransition(density), - exit = IntroExitTransition() - ) { - Text( - text = buildAnnotatedString { - withStyle( - style = SpanStyle( - fontFamily = praxisFontFamily, - fontWeight = FontWeight.Bold, color = Color.White - ) - ) { - append("Picture this: a\n") - } - withStyle( - style = SpanStyle( - fontFamily = praxisFontFamily, - fontWeight = FontWeight.Bold, color = Color.White - ) - ) { - append("messaging app,\n") - } - withStyle( - style = SpanStyle( - PraxisLogoYellow, - fontFamily = praxisFontFamily, fontWeight = FontWeight.Bold - ) - ) { - append("but built for\nwork.") - } - }, - textAlign = TextAlign.Left, - modifier = modifier.fillMaxWidth(), - style = PraxisTypography.h4 - ) - } - -} - -@Composable -private fun IntroExitTransition() = slideOutHorizontally() + shrinkHorizontally() + fadeOut() - -@Composable -private fun IntroEnterTransition(density: Density) = slideInHorizontally { - // Slide in from 12580 dp from the left. - with(density) { -12580.dp.roundToPx() } -} + expandHorizontally( - // Expand from the top. - expandFrom = Alignment.Start -) + fadeIn( - // Fade in with the initial alpha of 0.3f. - initialAlpha = 0.3f -) diff --git a/ui-onboarding/src/main/java/com/mutualmobile/praxis/uionboarding/compose/ScreenInputUI.kt b/ui-onboarding/src/main/java/com/mutualmobile/praxis/uionboarding/compose/ScreenInputUI.kt deleted file mode 100644 index 11751dd1..00000000 --- a/ui-onboarding/src/main/java/com/mutualmobile/praxis/uionboarding/compose/ScreenInputUI.kt +++ /dev/null @@ -1,34 +0,0 @@ -package com.mutualmobile.praxis.uionboarding.compose - -import androidx.compose.runtime.* -import androidx.compose.ui.res.stringResource -import com.mutualmobile.praxis.commonui.theme.PraxisTheme -import com.mutualmobile.praxis.navigator.ComposeNavigator -import com.mutualmobile.praxis.uionboarding.R - -@Composable -fun EmailAddressInputUI(composeNavigator: ComposeNavigator) { - PraxisTheme() { - CommonInputUI( - composeNavigator, - { modifier -> - EmailInputView(modifier) - }, - stringResource(id = R.string.subtitle_this_email_slack) - ) - } -} - -@Composable -fun WorkspaceInputUI(composeNavigator: ComposeNavigator) { - PraxisTheme() { - CommonInputUI( - composeNavigator, - { - WorkspaceInputView(it) - }, - stringResource(id = R.string.subtitle_this_address_slack) - ) - } -} - diff --git a/ui-onboarding/src/main/java/com/mutualmobile/praxis/uionboarding/compose/SkipTypingScreen.kt b/ui-onboarding/src/main/java/com/mutualmobile/praxis/uionboarding/compose/SkipTypingScreen.kt deleted file mode 100644 index 15e9e023..00000000 --- a/ui-onboarding/src/main/java/com/mutualmobile/praxis/uionboarding/compose/SkipTypingScreen.kt +++ /dev/null @@ -1,167 +0,0 @@ -package com.mutualmobile.praxis.uionboarding.compose - -import androidx.compose.foundation.BorderStroke -import androidx.compose.foundation.Image -import androidx.compose.foundation.layout.* -import androidx.compose.material.* -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Clear -import androidx.compose.runtime.Composable -import androidx.compose.runtime.SideEffect -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.text.SpanStyle -import androidx.compose.ui.text.buildAnnotatedString -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.text.withStyle -import androidx.compose.ui.unit.dp -import com.google.accompanist.insets.statusBarsPadding -import com.google.accompanist.systemuicontroller.rememberSystemUiController -import com.mutualmobile.praxis.commonui.material.PraxisSurfaceAppBar -import com.mutualmobile.praxis.commonui.theme.* -import com.mutualmobile.praxis.navigator.ComposeNavigator -import com.mutualmobile.praxis.navigator.PraxisScreen -import com.mutualmobile.praxis.uionboarding.R - -@Composable -fun SkipTypingUI(composeNavigator: ComposeNavigator) { - PraxisTheme() { - val scaffoldState = rememberScaffoldState() - val sysUiController = rememberSystemUiController() - SideEffect { - sysUiController.setNavigationBarColor(color = PraxisCloneColor) - sysUiController.setSystemBarsColor(color = PraxisCloneColor) - } - Scaffold( - backgroundColor = PraxisCloneColor, - contentColor = PraxisColorProvider.colors.textSecondary, - modifier = Modifier.statusBarsPadding(), scaffoldState = scaffoldState, - topBar = { - PraxisSurfaceAppBar( - title = { - - }, - navigationIcon = { - IconButton(onClick = { - composeNavigator.navigateUp() - }) { - Icon( - imageVector = Icons.Filled.Clear, - contentDescription = "Clear", - modifier = Modifier.padding(start = 8.dp), tint = Color.White - ) - } - }, - backgroundColor = PraxisCloneColor, - elevation = 0.dp - ) - }, - snackbarHost = { - scaffoldState.snackbarHostState - } - ) { innerPadding -> - Box(modifier = Modifier.padding(innerPadding)) { - PraxisSurface( - color = PraxisCloneColor, - modifier = Modifier - .padding(28.dp) - ) { - Column( - verticalArrangement = Arrangement.SpaceAround, - horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier - .fillMaxHeight() - .fillMaxWidth() - ) { - Image( - painter = painterResource(id = R.drawable.gettingstarted), - contentDescription = "Logo", - Modifier - ) - TitleSubtitleText() - Spacer(Modifier.padding(8.dp)) - Column { - EmailMeMagicLink(composeNavigator) - Box(modifier = Modifier.height(12.dp)) - IWillSignInManually(composeNavigator) - } - - } - - } - } - - } - } - - -} - -@Composable -fun EmailMeMagicLink(composeNavigator: ComposeNavigator) { - OutlinedButton( - onClick = { - composeNavigator.navigate(PraxisScreen.EmailAddressInputUI.name) - }, - border = BorderStroke(1.dp, color = Color.White), - colors = ButtonDefaults.buttonColors(backgroundColor = Color.Transparent), - modifier = Modifier - .fillMaxWidth() - .height(40.dp), - ) { - Text( - text = "Email me a magic link", - style = PraxisTypography.subtitle2.copy(color = Color.White) - ) - } -} - -@Composable -private fun IWillSignInManually(composeNavigator: ComposeNavigator) { - Button( - onClick = { - composeNavigator.navigate(PraxisScreen.WorkspaceInputUI.name) - }, - Modifier - .fillMaxWidth() - .height(40.dp), - colors = ButtonDefaults.buttonColors(backgroundColor = Color.White) - ) { - Text( - text = "I'll sign in manually", - style = PraxisTypography.subtitle2.copy(color = PraxisCloneColor) - ) - } -} - -@Composable -private fun TitleSubtitleText(modifier: Modifier = Modifier) { - Text( - text = buildAnnotatedString { - withStyle( - style = SpanStyle( - fontFamily = praxisFontFamily, - fontWeight = FontWeight.Bold, - fontSize = PraxisTypography.h6.fontSize, color = Color.White - ) - ) { - append("Want to skip the typing ?\n\n") - } - withStyle( - style = SpanStyle( - fontFamily = praxisFontFamily, - fontWeight = FontWeight.Normal, - fontSize = PraxisTypography.subtitle2.fontSize, color = Color.White - ) - ) { - append("We can email you a magic sign-in link that adds all your workspaces at once") - } - }, - textAlign = TextAlign.Center, - modifier = modifier.fillMaxWidth(), - style = PraxisTypography.h4 - ) -} diff --git a/ui-onboarding/src/main/java/com/mutualmobile/praxis/uionboarding/compose/WorkspaceInputView.kt b/ui-onboarding/src/main/java/com/mutualmobile/praxis/uionboarding/compose/WorkspaceInputView.kt deleted file mode 100644 index 8c842c70..00000000 --- a/ui-onboarding/src/main/java/com/mutualmobile/praxis/uionboarding/compose/WorkspaceInputView.kt +++ /dev/null @@ -1,85 +0,0 @@ -package com.mutualmobile.praxis.uionboarding.compose - -import androidx.compose.foundation.layout.* -import androidx.compose.material.Text -import androidx.compose.material.TextField -import androidx.compose.material.TextFieldDefaults -import androidx.compose.runtime.* -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.unit.dp -import com.mutualmobile.praxis.commonui.theme.PraxisColorProvider -import com.mutualmobile.praxis.commonui.theme.PraxisTypography - -@Composable -fun WorkspaceInputView(modifier: Modifier) { - Column( - modifier = modifier - .fillMaxWidth() - .wrapContentWidth() - ) { - Text( - text = "Workspace URL", style = PraxisTypography.caption.copy( - color = PraxisColorProvider.colors.textPrimary.copy(alpha = 0.7f), - fontWeight = FontWeight.Normal, - textAlign = TextAlign.Start - ), modifier = Modifier.padding(bottom = 4.dp) - ) - Row( - modifier = modifier - .fillMaxWidth(), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.Start - ) { - WorkspaceTF() - } - } -} - - -@Composable -private fun WorkspaceTF() { - var workspace by remember { mutableStateOf("") } - - TextField( - value = workspace, - onValueChange = { newEmail -> - workspace = newEmail - }, - textStyle = textStyleField(), - leadingIcon = { - Text(text = "https://", style = textStyleField().copy(color = PraxisColorProvider.colors.textPrimary.copy(alpha = 0.4f))) - }, - trailingIcon = { - Text(".slack.com", style = textStyleField().copy(color = PraxisColorProvider.colors.textPrimary.copy(alpha = 0.4f))) - }, - placeholder = { - Text( - text = "your-workspace", - style = textStyleField(), - textAlign = TextAlign.Start - ) - }, - colors = textFieldColors(), - singleLine = true, - maxLines = 1, - ) -} - -@Composable -private fun textFieldColors() = TextFieldDefaults.textFieldColors( - backgroundColor = Color.Transparent, - cursorColor = PraxisColorProvider.colors.textPrimary, - unfocusedIndicatorColor = Color.Transparent, - focusedIndicatorColor = Color.Transparent -) - -@Composable -private fun textStyleField() = PraxisTypography.h6.copy( - color = PraxisColorProvider.colors.textPrimary.copy(alpha = 0.7f), - fontWeight = FontWeight.Normal, - textAlign = TextAlign.Start -) \ No newline at end of file diff --git a/ui-onboarding/src/main/java/com/mutualmobile/praxis/uionboarding/nav/OnboardingNavigation.kt b/ui-onboarding/src/main/java/com/mutualmobile/praxis/uionboarding/nav/OnboardingNavigation.kt deleted file mode 100644 index fd6f075e..00000000 --- a/ui-onboarding/src/main/java/com/mutualmobile/praxis/uionboarding/nav/OnboardingNavigation.kt +++ /dev/null @@ -1,36 +0,0 @@ -package com.mutualmobile.praxis.uionboarding.nav - - -import androidx.navigation.NavGraphBuilder -import androidx.navigation.compose.composable -import androidx.navigation.navigation -import com.mutualmobile.praxis.navigator.ComposeNavigator -import com.mutualmobile.praxis.navigator.PraxisRoute -import com.mutualmobile.praxis.navigator.PraxisScreen -import com.mutualmobile.praxis.uionboarding.compose.EmailAddressInputUI -import com.mutualmobile.praxis.uionboarding.compose.GettingStartedUI -import com.mutualmobile.praxis.uionboarding.compose.SkipTypingUI -import com.mutualmobile.praxis.uionboarding.compose.WorkspaceInputUI - -fun NavGraphBuilder.onboardingNavigation( - composeNavigator: ComposeNavigator, -) { - navigation( - startDestination = PraxisScreen.GettingStarted.name, - route = PraxisRoute.OnBoarding.name - ) { - composable(PraxisScreen.GettingStarted.name) { - GettingStartedUI(composeNavigator) - } - composable(PraxisScreen.SkipTypingScreen.name) { - SkipTypingUI(composeNavigator) - } - composable(PraxisScreen.WorkspaceInputUI.name) { - WorkspaceInputUI(composeNavigator) - } - composable(PraxisScreen.EmailAddressInputUI.name) { - EmailAddressInputUI(composeNavigator) - } - } - -} \ No newline at end of file diff --git a/ui-onboarding/src/main/res/drawable-hdpi/gettingstarted.png b/ui-onboarding/src/main/res/drawable-hdpi/gettingstarted.png deleted file mode 100644 index 317cec33..00000000 Binary files a/ui-onboarding/src/main/res/drawable-hdpi/gettingstarted.png and /dev/null differ diff --git a/ui-onboarding/src/main/res/drawable-xxhdpi/gettingstarted.png b/ui-onboarding/src/main/res/drawable-xxhdpi/gettingstarted.png deleted file mode 100644 index 4e7dad74..00000000 Binary files a/ui-onboarding/src/main/res/drawable-xxhdpi/gettingstarted.png and /dev/null differ diff --git a/ui-onboarding/src/main/res/drawable-xxxhdpi/gettingstarted.png b/ui-onboarding/src/main/res/drawable-xxxhdpi/gettingstarted.png deleted file mode 100644 index be9e3a14..00000000 Binary files a/ui-onboarding/src/main/res/drawable-xxxhdpi/gettingstarted.png and /dev/null differ diff --git a/ui-onboarding/src/main/res/values/strings.xml b/ui-onboarding/src/main/res/values/strings.xml deleted file mode 100644 index ca6a6999..00000000 --- a/ui-onboarding/src/main/res/values/strings.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - This is the address you use to sign in to Praxis - We\'ll send you an email that\'ll instantly sign you in. - \ No newline at end of file diff --git a/ui-onboarding/src/test/java/com/mutualmobile/praxis/uionboarding/ExampleUnitTest.kt b/ui-onboarding/src/test/java/com/mutualmobile/praxis/uionboarding/ExampleUnitTest.kt deleted file mode 100644 index c4d6e132..00000000 --- a/ui-onboarding/src/test/java/com/mutualmobile/praxis/uionboarding/ExampleUnitTest.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.mutualmobile.praxis.uionboarding - -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) - } -} \ No newline at end of file