From 5c8407b3277c0ab550728487b38f2ba389a52280 Mon Sep 17 00:00:00 2001 From: Ankush Bose <46471379+bosankus@users.noreply.github.com> Date: Mon, 15 Nov 2021 15:21:58 +0530 Subject: [PATCH] WIP #80: UI created for Feedback screen. Existing not removed yet. --- app/build.gradle | 40 ++- app/src/main/AndroidManifest.xml | 18 +- .../sonali/todo/view/common/Color.kt | 15 ++ .../sonali/todo/view/common/Shape.kt | 11 + .../sonali/todo/view/common/Theme.kt | 35 +++ .../sonali/todo/view/common/Type.kt | 15 ++ .../feedback/FeedbackComposableFragment.kt | 30 +++ .../view/feedback/FeedbackComposeScreen.kt | 227 ++++++++++++++++++ .../sonali/todo/view/fragment/TaskFragment.kt | 46 ++-- app/src/main/res/drawable/ic_info.xml | 10 + app/src/main/res/navigation/nav_graph.xml | 11 + app/src/main/res/values/strings.xml | 3 +- app/src/main/res/values/themes.xml | 11 + build.gradle | 4 + 14 files changed, 434 insertions(+), 42 deletions(-) create mode 100644 app/src/main/java/tech/androidplay/sonali/todo/view/common/Color.kt create mode 100644 app/src/main/java/tech/androidplay/sonali/todo/view/common/Shape.kt create mode 100644 app/src/main/java/tech/androidplay/sonali/todo/view/common/Theme.kt create mode 100644 app/src/main/java/tech/androidplay/sonali/todo/view/common/Type.kt create mode 100644 app/src/main/java/tech/androidplay/sonali/todo/view/feedback/FeedbackComposableFragment.kt create mode 100644 app/src/main/java/tech/androidplay/sonali/todo/view/feedback/FeedbackComposeScreen.kt create mode 100644 app/src/main/res/drawable/ic_info.xml create mode 100644 app/src/main/res/values/themes.xml diff --git a/app/build.gradle b/app/build.gradle index 3265672..75e49ad 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -20,6 +20,9 @@ android { buildConfigField "String", "QUOTE_API_KEY", "\"API-KEY\"" multiDexEnabled true testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + vectorDrawables { + useSupportLibrary true + } } buildTypes { @@ -32,6 +35,7 @@ android { buildFeatures { dataBinding true viewBinding true + compose true } compileOptions { @@ -40,17 +44,22 @@ android { } composeOptions { - kotlinCompilerExtensionVersion '1.0.5' + kotlinCompilerExtensionVersion compose_version + kotlinCompilerVersion kotlin_version } - kotlinOptions { - jvmTarget = JavaVersion.VERSION_11.toString() + jvmTarget = '1.8' + useIR = true } + packagingOptions { exclude 'META-INF/AL2.0' exclude 'META-INF/LGPL2.1' exclude 'META-INF/atomicfu.kotlin_module' + resources { + excludes += '/META-INF/{AL2.0,LGPL2.1}' + } } } @@ -75,6 +84,7 @@ dependencies { androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' androidTestImplementation "com.google.truth:truth:1.1" + androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version" // CanaryLeak debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.7' @@ -133,9 +143,6 @@ dependencies { implementation "androidx.navigation:navigation-fragment-ktx:$nav_version" implementation "androidx.navigation:navigation-ui-ktx:$nav_version" - /*// Paging Library - implementation "androidx.paging:paging-runtime-ktx:2.1.2"*/ - // DI - Hilt implementation "androidx.hilt:hilt-lifecycle-viewmodel:$hilt_jetpack_version" implementation "com.google.dagger:hilt-android:$hilt_version" @@ -148,6 +155,8 @@ dependencies { // Lottie Animation implementation "com.airbnb.android:lottie:$lottie_version" + // Lottie for compose + implementation "com.airbnb.android:lottie-compose:$lottie_version" // Shimmer effect implementation "com.facebook.shimmer:shimmer:$shimmer_version" @@ -158,5 +167,24 @@ dependencies { // Image Picker implementation "com.github.dhaval2404:imagepicker:$picker_version" + + /*JETPACK COMPOSE DEPENDENCIES*/ + implementation "androidx.compose.ui:ui:$compose_version" + // Compose compiler + implementation "androidx.compose.compiler:compiler:1.1.0-beta02" + // Integration with activities + implementation 'androidx.activity:activity-compose:1.4.0' + // Compose Material Design + implementation 'androidx.compose.material:material:1.0.5' + // Animations + implementation 'androidx.compose.animation:animation:1.0.5' + // Tooling support (Previews, etc.) + implementation "androidx.compose.ui:ui-tooling-preview:1.0.5" + implementation 'androidx.compose.ui:ui-tooling:1.0.5' + // Integration with ViewModels + implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.4.0' + // When using a AppCompat theme - Reuse existing view theme + implementation "com.google.accompanist:accompanist-appcompat-theme:0.16.0" + } apply plugin: 'com.google.gms.google-services' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 6a48710..51b6a1b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -8,24 +8,29 @@ - + - - + @@ -47,7 +52,6 @@ - @@ -59,9 +63,7 @@ - - + tools:node="remove"> \ No newline at end of file diff --git a/app/src/main/java/tech/androidplay/sonali/todo/view/common/Color.kt b/app/src/main/java/tech/androidplay/sonali/todo/view/common/Color.kt new file mode 100644 index 0000000..ed27445 --- /dev/null +++ b/app/src/main/java/tech/androidplay/sonali/todo/view/common/Color.kt @@ -0,0 +1,15 @@ +package tech.androidplay.sonali.todo.view.common + +import androidx.compose.ui.graphics.* + +val WhiteOff = Color(0xFFFAFAFA) +val White100 = Color(0xFFFFFFFF) +val Black100 = Color(0XFF000000) +val GreyDark = Color(0xFF444444) +val GreyMedium = Color(0xFF9E9E9E) +val GreyLight = Color(0xEDE0E0E0) +val AppBlue = Color(0xFF3068DE) +val AppBlueDark = Color(0xFF3425D6) +val OrangeYellow1 = Color(0xfff0bd28) +val OrangeYellow2 = Color(0xfff1c746) +val OrangeYellow3 = Color(0xfff4cf65) diff --git a/app/src/main/java/tech/androidplay/sonali/todo/view/common/Shape.kt b/app/src/main/java/tech/androidplay/sonali/todo/view/common/Shape.kt new file mode 100644 index 0000000..2691ad7 --- /dev/null +++ b/app/src/main/java/tech/androidplay/sonali/todo/view/common/Shape.kt @@ -0,0 +1,11 @@ +package tech.androidplay.sonali.todo.view.common + +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Shapes +import androidx.compose.ui.unit.dp + +val Shapes = Shapes( + small = RoundedCornerShape(4.dp), + medium = RoundedCornerShape(4.dp), + large = RoundedCornerShape(0.dp) +) \ No newline at end of file diff --git a/app/src/main/java/tech/androidplay/sonali/todo/view/common/Theme.kt b/app/src/main/java/tech/androidplay/sonali/todo/view/common/Theme.kt new file mode 100644 index 0000000..c7b8c4d --- /dev/null +++ b/app/src/main/java/tech/androidplay/sonali/todo/view/common/Theme.kt @@ -0,0 +1,35 @@ +package tech.androidplay.sonali.todo.view.common + +import androidx.compose.foundation.* +import androidx.compose.material.* +import androidx.compose.runtime.* + +private val DarkColorPalette = darkColors( + primary = AppBlue, + primaryVariant = Black100, + surface = Black100, + secondary = White100 +) + +private val LightColorPalette = lightColors( + primary = AppBlue, + primaryVariant = White100, + surface = White100, + secondary = GreyDark +) + +@Composable +fun AheadTheme( + darkTheme: Boolean = isSystemInDarkTheme(), + content: @Composable() () -> Unit +) { + val colors = if (darkTheme) DarkColorPalette + else LightColorPalette + + MaterialTheme( + colors = colors, + typography = Typography, + content = content, + shapes = Shapes + ) +} \ No newline at end of file diff --git a/app/src/main/java/tech/androidplay/sonali/todo/view/common/Type.kt b/app/src/main/java/tech/androidplay/sonali/todo/view/common/Type.kt new file mode 100644 index 0000000..f5d1f62 --- /dev/null +++ b/app/src/main/java/tech/androidplay/sonali/todo/view/common/Type.kt @@ -0,0 +1,15 @@ +package tech.androidplay.sonali.todo.view.common + +import androidx.compose.material.Typography +import androidx.compose.ui.text.* +import androidx.compose.ui.text.font.* +import androidx.compose.ui.unit.* + + +val Typography = Typography( + body1 = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 16.sp + ) +) \ No newline at end of file diff --git a/app/src/main/java/tech/androidplay/sonali/todo/view/feedback/FeedbackComposableFragment.kt b/app/src/main/java/tech/androidplay/sonali/todo/view/feedback/FeedbackComposableFragment.kt new file mode 100644 index 0000000..6b46874 --- /dev/null +++ b/app/src/main/java/tech/androidplay/sonali/todo/view/feedback/FeedbackComposableFragment.kt @@ -0,0 +1,30 @@ +package tech.androidplay.sonali.todo.view.feedback + +import android.os.* +import android.view.* +import androidx.compose.ui.platform.* +import androidx.fragment.app.* +import kotlinx.coroutines.* +import tech.androidplay.sonali.todo.view.common.* +import tech.androidplay.sonali.todo.viewmodel.* + +@ExperimentalCoroutinesApi +class FeedbackComposableFragment() : Fragment() { + + private val viewModel: FeedbackViewModel by viewModels() + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + return ComposeView(requireContext()).apply { + setContent { + AheadTheme { + FeedbackComposeScreen() + } + } + } + } +} + diff --git a/app/src/main/java/tech/androidplay/sonali/todo/view/feedback/FeedbackComposeScreen.kt b/app/src/main/java/tech/androidplay/sonali/todo/view/feedback/FeedbackComposeScreen.kt new file mode 100644 index 0000000..803b5e5 --- /dev/null +++ b/app/src/main/java/tech/androidplay/sonali/todo/view/feedback/FeedbackComposeScreen.kt @@ -0,0 +1,227 @@ +package tech.androidplay.sonali.todo.view.feedback + +import androidx.compose.foundation.* +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.* +import androidx.compose.material.* +import androidx.compose.runtime.* +import androidx.compose.ui.* +import androidx.compose.ui.draw.* +import androidx.compose.ui.graphics.* +import androidx.compose.ui.res.* +import androidx.compose.ui.text.font.* +import androidx.compose.ui.text.style.* +import androidx.compose.ui.tooling.preview.* +import androidx.compose.ui.unit.* +import com.airbnb.lottie.compose.* +import tech.androidplay.sonali.todo.R +import tech.androidplay.sonali.todo.view.common.* + +@Preview(showBackground = true) +@Composable +fun FeedbackComposeScreen() { + Box( + modifier = Modifier + .background(White100) + .fillMaxSize() + ) { + Column { + ToolBarLayout() + AppLogoPart() + FeedbackInformationLayout() + FeedbackInputLayout() + FeedbackSubmitLayout() + } + } +} + + +@Composable +fun ToolBarLayout() { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.fillMaxWidth() + ) { + IconButton(modifier = Modifier.size(60.dp).padding(all = 16.dp), + onClick = { } + ) { + Icon( + painterResource(R.drawable.ic_back), + contentDescription = "Go back from feedback screen.", + tint = GreyDark, + ) + } + Text( + text = "Feedback", + style = MaterialTheme.typography.h6, + color = GreyDark, + ) + val composition by rememberLottieComposition(LottieCompositionSpec.Asset("lottie_auth.json")) + val progress by animateLottieCompositionAsState(composition) + LottieAnimation( + composition = composition, + progress = progress, + modifier = Modifier + .height(90.dp) + .width(90.dp) + .align(Alignment.CenterVertically) + .padding(all = 16.dp) + ) + } +} + + +@Composable +fun AppLogoPart() { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.padding(start = 16.dp, end = 16.dp, bottom = 10.dp) + ) { + Image( + painter = painterResource(R.drawable.ic_app_icon), + contentDescription = "App logo", + modifier = Modifier.size(40.dp) + ) + Text( + text = "How did you like this app?", + modifier = Modifier.padding(start = 8.dp), + style = MaterialTheme.typography.h6, + fontFamily = FontFamily(Font(R.font.calibre, FontWeight.Medium)), + fontSize = 22.sp, + color = GreyDark + ) + } +} + + +@Composable +fun FeedbackInformationLayout(color: Color = GreyLight) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .padding(start = 16.dp, end = 16.dp) + .clip(RoundedCornerShape(10.dp)) + .background(color) + .padding(horizontal = 15.dp, vertical = 20.dp) + .fillMaxWidth() + ) { + Icon( + painterResource(R.drawable.ic_info), + contentDescription = "Feedback info icon", + modifier = Modifier.padding(bottom = 10.dp), + tint = GreyMedium + ) + Text( + text = stringResource(R.string.feedback_story), + modifier = Modifier.padding(start = 8.dp), + style = MaterialTheme.typography.h6, + fontFamily = FontFamily(Font(R.font.calibre, FontWeight.Medium)), + fontSize = 18.sp, + color = GreyDark + ) + } +} + +@Composable +fun FeedbackInputLayout() { + Column( + verticalArrangement = Arrangement.Center, + modifier = Modifier + .padding(start = 16.dp, end = 16.dp, top = 36.dp) + .fillMaxWidth() + ) { + val maxChar = 100 + val titleTextState = remember { mutableStateOf("") } + Text( + text = "What you think about this app?", + style = MaterialTheme.typography.h6, + fontFamily = FontFamily(Font(R.font.calibre, FontWeight.Medium)), + fontSize = 18.sp, + textAlign = TextAlign.Start, + color = GreyDark, + ) + TextField( + modifier = Modifier.fillMaxWidth().padding(top = 5.dp), + shape = RoundedCornerShape(10.dp), + value = titleTextState.value, + onValueChange = { if (it.length <= maxChar) titleTextState.value = it }, + singleLine = true, + colors = TextFieldDefaults.textFieldColors( + backgroundColor = GreyLight, + cursorColor = Black100, + disabledLabelColor = GreyMedium, + focusedIndicatorColor = Color.Transparent, + unfocusedIndicatorColor = Color.Transparent + ) + ) + Text( + text = "${titleTextState.value.length} / $maxChar", + textAlign = TextAlign.End, + color = GreyLight, + style = MaterialTheme.typography.caption, //use the caption text style + modifier = Modifier.fillMaxWidth() + ) + + val descriptionTextState = remember { mutableStateOf("") } + Text( + text = "Can you give little more info?", + style = MaterialTheme.typography.h6, + fontFamily = FontFamily(Font(R.font.calibre, FontWeight.Medium)), + fontSize = 18.sp, + textAlign = TextAlign.Start, + color = GreyDark, + modifier = Modifier.fillMaxWidth().padding(top = 16.dp), + ) + TextField( + modifier = Modifier.fillMaxWidth().padding(top = 5.dp), + shape = RoundedCornerShape(10.dp), + value = descriptionTextState.value, + onValueChange = { if (it.length <= maxChar) descriptionTextState.value = it }, + singleLine = true, + colors = TextFieldDefaults.textFieldColors( + backgroundColor = GreyLight, + cursorColor = Black100, + disabledLabelColor = GreyMedium, + focusedIndicatorColor = Color.Transparent, + unfocusedIndicatorColor = Color.Transparent + ) + ) + Text( + text = "${descriptionTextState.value.length} / $maxChar", + textAlign = TextAlign.End, + color = GreyLight, + style = MaterialTheme.typography.caption, //use the caption text style + modifier = Modifier.fillMaxWidth() + ) + } +} + + +@Composable +fun FeedbackSubmitLayout() { + Button( + modifier = Modifier + .padding(start = 16.dp, end = 16.dp, top = 16.dp) + .fillMaxWidth(), + elevation = ButtonDefaults.elevation( + defaultElevation = 0.dp, + pressedElevation = 0.dp, + ), + shape = RoundedCornerShape(5.dp), + border = BorderStroke(1.dp, AppBlue), + colors = ButtonDefaults.buttonColors( + backgroundColor = Color.Transparent, + contentColor = AppBlue + ), + onClick = { } + ) { + Text( + text = "Submit", + fontFamily = FontFamily(Font(R.font.calibre, FontWeight.Medium)), + fontSize = 18.sp, + modifier = Modifier.padding(horizontal = 5.dp, vertical = 5.dp) + ) + } +} + + diff --git a/app/src/main/java/tech/androidplay/sonali/todo/view/fragment/TaskFragment.kt b/app/src/main/java/tech/androidplay/sonali/todo/view/fragment/TaskFragment.kt index f9b4d58..1c285da 100644 --- a/app/src/main/java/tech/androidplay/sonali/todo/view/fragment/TaskFragment.kt +++ b/app/src/main/java/tech/androidplay/sonali/todo/view/fragment/TaskFragment.kt @@ -1,30 +1,27 @@ package tech.androidplay.sonali.todo.view.fragment -import android.annotation.SuppressLint -import android.app.AlertDialog -import android.content.SharedPreferences -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.PopupMenu -import androidx.activity.addCallback -import androidx.databinding.DataBindingUtil +import android.annotation.* +import android.app.* +import android.content.* +import android.os.* +import android.view.* +import android.widget.* +import androidx.activity.* +import androidx.databinding.* +import androidx.fragment.app.* import androidx.fragment.app.Fragment -import androidx.fragment.app.viewModels -import androidx.navigation.fragment.findNavController -import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.InternalCoroutinesApi +import androidx.navigation.fragment.* +import dagger.hilt.android.* +import kotlinx.coroutines.* import tech.androidplay.sonali.todo.R -import tech.androidplay.sonali.todo.databinding.FragmentTaskBinding +import tech.androidplay.sonali.todo.databinding.* import tech.androidplay.sonali.todo.utils.* import tech.androidplay.sonali.todo.utils.Constants.IS_FIRST_TIME import tech.androidplay.sonali.todo.utils.UIHelper.showSnack -import tech.androidplay.sonali.todo.view.adapter.main_adapter.TodoAdapter -import tech.androidplay.sonali.todo.view.adapter.viewpager_adapter.ViewPagerAdapter -import tech.androidplay.sonali.todo.viewmodel.TaskViewModel -import javax.inject.Inject +import tech.androidplay.sonali.todo.view.adapter.main_adapter.* +import tech.androidplay.sonali.todo.view.adapter.viewpager_adapter.* +import tech.androidplay.sonali.todo.viewmodel.* +import javax.inject.* /** * Created by Androidplay @@ -41,9 +38,6 @@ class TaskFragment : Fragment(R.layout.fragment_task) { private var binding: FragmentTaskBinding? = null - @Inject - lateinit var appEventTracking: AppEventTracking - @Inject lateinit var dialog: AlertDialog.Builder @@ -144,10 +138,8 @@ class TaskFragment : Fragment(R.layout.fragment_task) { when (it.itemId) { R.id.menu_logout -> logOutUser() R.id.menu_share_app -> shareApp() - R.id.menu_feedback -> { - appEventTracking.trackFeedbackIntention() - findNavController().navigate(R.id.action_taskFragment_to_feedbackFragment) - } + R.id.menu_feedback -> + findNavController().navigate(R.id.action_taskFragment_to_feedbackComposableFragment) } true } diff --git a/app/src/main/res/drawable/ic_info.xml b/app/src/main/res/drawable/ic_info.xml new file mode 100644 index 0000000..17255b7 --- /dev/null +++ b/app/src/main/res/drawable/ic_info.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/navigation/nav_graph.xml b/app/src/main/res/navigation/nav_graph.xml index 6e71ade..0cd936a 100644 --- a/app/src/main/res/navigation/nav_graph.xml +++ b/app/src/main/res/navigation/nav_graph.xml @@ -68,6 +68,9 @@ app:exitAnim="@anim/fade_in_animation" app:popEnterAnim="@anim/fade_out_animation" app:popExitAnim="@anim/fade_in_animation"/> + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 043f8e5..ce178ad 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -26,7 +26,7 @@ Feedback What change you want to see? Describe to make us understand - We are on a journey to think ahead & make your experience better. Please share with us how you felt while using the app. + We take feedback anonymously to measure liking statistics. Your feedback will help us to make your experience more better. Suggest Overdue Upload your task image @@ -92,5 +92,6 @@ Priority Attachment + FeedbackActivity diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml new file mode 100644 index 0000000..c8318f5 --- /dev/null +++ b/app/src/main/res/values/themes.xml @@ -0,0 +1,11 @@ + + + + +