From d055e92070c660c2d2d0a33a5c8b2aedf8531675 Mon Sep 17 00:00:00 2001 From: Nguyen Quang Minh Date: Thu, 28 Nov 2024 01:14:24 +0700 Subject: [PATCH] =?UTF-8?q?feat(study=20set):=20g=C3=A1n=20mark=20AI=20n?= =?UTF-8?q?=E1=BA=BFu=20nh=C6=B0=20study=20set=20t=E1=BA=A1o=20t=E1=BB=AB?= =?UTF-8?q?=20AI=20-=20s=E1=BB=ADa=20c=C3=A1c=20l=E1=BB=97i=20li=C3=AAn=20?= =?UTF-8?q?quan=20=C4=91=E1=BA=BFn=20logic=20v=C3=A0=20giao=20di=E1=BB=87n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../flashcard/StudySetFlashCardResponseDto.kt | 2 + .../StudySetFlashCardResponseMapper.kt | 6 ++- .../StudySetFlashCardResponseModel.kt | 1 + .../component/AddStudySetToClassItem.kt | 38 +++++++++++++++---- .../CreateStudySetAITab.kt | 27 ++++++++++++- .../component/AddStudySetToFolderItem.kt | 38 +++++++++++++++---- .../presentation/app/home/HomeScreen.kt | 3 +- .../app/home/components/StudySetHomeItem.kt | 38 ++++++++++++++++--- .../app/library/classes/ListClassesScreen.kt | 1 + .../app/library/component/SearchTextField.kt | 1 - .../app/library/folder/ListFolderScreen.kt | 1 + .../library/study_set/ListStudySetScreen.kt | 1 + .../study_set/component/StudySetItem.kt | 37 +++++++++++++++--- .../presentation/app/search/SearchScreen.kt | 11 +++--- .../study_set/detail/StudySetDetailScreen.kt | 6 ++- .../study_set/detail/StudySetDetailUiState.kt | 1 + .../detail/StudySetDetailViewModel.kt | 1 + .../detail/component/StudySetDetailTopBar.kt | 28 +++++++++++++- .../detail/info/StudySetInfoScreen.kt | 34 ++++++++++++++++- .../detail/info/StudySetInfoScreenArgs.kt | 1 + .../detail/info/StudySetInfoUiState.kt | 1 + .../detail/info/StudySetViewModel.kt | 4 +- .../study_set/detail/material/CardDetail.kt | 16 +++++++- .../detail/material/MaterialTabScreen.kt | 2 + .../auth/component/AuthTextField.kt | 1 + .../com/pwhs/quickmem/util/ads/AdsUtil.kt | 7 +--- app/src/main/res/values-vi/strings.xml | 2 + app/src/main/res/values/strings.xml | 2 + 28 files changed, 263 insertions(+), 48 deletions(-) diff --git a/app/src/main/java/com/pwhs/quickmem/data/dto/flashcard/StudySetFlashCardResponseDto.kt b/app/src/main/java/com/pwhs/quickmem/data/dto/flashcard/StudySetFlashCardResponseDto.kt index e29949f7..ef62209b 100644 --- a/app/src/main/java/com/pwhs/quickmem/data/dto/flashcard/StudySetFlashCardResponseDto.kt +++ b/app/src/main/java/com/pwhs/quickmem/data/dto/flashcard/StudySetFlashCardResponseDto.kt @@ -32,6 +32,8 @@ data class StudySetFlashCardResponseDto( val writeStatus: String = WriteStatus.NONE.status, @SerializedName("isStarred") val isStarred: Boolean, + @SerializedName("isAIGenerated") + val isAIGenerated: Boolean, @SerializedName("createdAt") val createdAt: String, @SerializedName("updatedAt") diff --git a/app/src/main/java/com/pwhs/quickmem/data/mapper/flashcard/StudySetFlashCardResponseMapper.kt b/app/src/main/java/com/pwhs/quickmem/data/mapper/flashcard/StudySetFlashCardResponseMapper.kt index 9a8c6c16..68eca9e9 100644 --- a/app/src/main/java/com/pwhs/quickmem/data/mapper/flashcard/StudySetFlashCardResponseMapper.kt +++ b/app/src/main/java/com/pwhs/quickmem/data/mapper/flashcard/StudySetFlashCardResponseMapper.kt @@ -17,7 +17,8 @@ fun StudySetFlashCardResponseDto.toModel() = StudySetFlashCardResponseModel( writeStatus = writeStatus, createdAt = createdAt, updatedAt = updatedAt, - isStarred = isStarred + isStarred = isStarred, + isAIGenerated = isAIGenerated ) fun StudySetFlashCardResponseModel.toDto() = StudySetFlashCardResponseDto( @@ -34,5 +35,6 @@ fun StudySetFlashCardResponseModel.toDto() = StudySetFlashCardResponseDto( writeStatus = writeStatus, createdAt = createdAt, updatedAt = updatedAt, - isStarred = isStarred + isStarred = isStarred, + isAIGenerated = isAIGenerated ) \ No newline at end of file diff --git a/app/src/main/java/com/pwhs/quickmem/domain/model/flashcard/StudySetFlashCardResponseModel.kt b/app/src/main/java/com/pwhs/quickmem/domain/model/flashcard/StudySetFlashCardResponseModel.kt index 663d5c14..731b58c8 100644 --- a/app/src/main/java/com/pwhs/quickmem/domain/model/flashcard/StudySetFlashCardResponseModel.kt +++ b/app/src/main/java/com/pwhs/quickmem/domain/model/flashcard/StudySetFlashCardResponseModel.kt @@ -13,6 +13,7 @@ data class StudySetFlashCardResponseModel( val definitionImageURL: String? = null, val hint: String? = null, val explanation: String? = null, + val isAIGenerated: Boolean = false, val rating: String = Rating.NOT_STUDIED.name, val flipStatus: String = FlipCardStatus.NONE.name, val quizStatus: String = QuizStatus.NONE.status, diff --git a/app/src/main/java/com/pwhs/quickmem/presentation/app/classes/add_study_set/component/AddStudySetToClassItem.kt b/app/src/main/java/com/pwhs/quickmem/presentation/app/classes/add_study_set/component/AddStudySetToClassItem.kt index d2f5dbfb..5bb35327 100644 --- a/app/src/main/java/com/pwhs/quickmem/presentation/app/classes/add_study_set/component/AddStudySetToClassItem.kt +++ b/app/src/main/java/com/pwhs/quickmem/presentation/app/classes/add_study_set/component/AddStudySetToClassItem.kt @@ -8,6 +8,7 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyColumn @@ -17,8 +18,10 @@ import androidx.compose.material3.CardDefaults import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme.colorScheme +import androidx.compose.material3.MaterialTheme.typography import androidx.compose.material3.Scaffold import androidx.compose.material3.Text +import androidx.compose.material3.VerticalDivider import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -26,6 +29,7 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow @@ -106,15 +110,35 @@ fun AddStudySetToClassItem( } } ) - Text( - studySet.subject?.name - ?: SubjectModel.defaultSubjects[0].name, - style = MaterialTheme.typography.bodySmall.copy( - color = colorScheme.onSurface.copy( - alpha = 0.6f + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(8.dp), + ) { + Text( + text = studySet?.subject?.name ?: SubjectModel.defaultSubjects[0].name, + style = typography.bodySmall.copy( + color = colorScheme.onSurface.copy(alpha = 0.6f) ) ) - ) + if(studySet?.isAIGenerated == true) { + VerticalDivider( + modifier = Modifier.height(12.dp) + ) + Text( + stringResource(R.string.txt_ai_generated), + style = typography.bodySmall.copy( + color = colorScheme.onSurface.copy(alpha = 0.6f) + ), + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + Icon( + painter = painterResource(R.drawable.ic_generative_ai), + contentDescription = stringResource(R.string.txt_ai_generated), + modifier = Modifier.size(16.dp) + ) + } + } Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp), diff --git a/app/src/main/java/com/pwhs/quickmem/presentation/app/explore/create_study_set_ai/CreateStudySetAITab.kt b/app/src/main/java/com/pwhs/quickmem/presentation/app/explore/create_study_set_ai/CreateStudySetAITab.kt index dc7db20c..67227987 100644 --- a/app/src/main/java/com/pwhs/quickmem/presentation/app/explore/create_study_set_ai/CreateStudySetAITab.kt +++ b/app/src/main/java/com/pwhs/quickmem/presentation/app/explore/create_study_set_ai/CreateStudySetAITab.kt @@ -31,6 +31,7 @@ import androidx.compose.material3.TextFieldDefaults.colors import androidx.compose.material3.VerticalDivider import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -51,6 +52,11 @@ import com.pwhs.quickmem.core.data.enums.LanguageCode import com.pwhs.quickmem.core.data.enums.QuestionType import com.pwhs.quickmem.ui.theme.QuickMemTheme import com.pwhs.quickmem.util.ads.AdsUtil +import com.revenuecat.purchases.CustomerInfo +import com.revenuecat.purchases.Purchases +import com.revenuecat.purchases.PurchasesError +import com.revenuecat.purchases.interfaces.ReceiveCustomerInfoCallback +import timber.log.Timber @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -76,13 +82,32 @@ fun CreateStudySetAITab( mutableStateOf(false) } val context = LocalContext.current + var customer: CustomerInfo? by remember { mutableStateOf(null) } + LaunchedEffect(key1 = true) { + Purchases.sharedInstance.getCustomerInfo(object : ReceiveCustomerInfoCallback { + override fun onError(error: PurchasesError) { + Timber.e("Error getting customer info: $error") + } + + override fun onReceived(customerInfo: CustomerInfo) { + Timber.d("Customer info: $customerInfo") + customer = customerInfo + } + + }) + } Scaffold( floatingActionButton = { if (title.isNotEmpty()) { FloatingActionButton( onClick = { - AdsUtil.rewardedAd(context) { + val isSubscribed = customer?.activeSubscriptions?.isNotEmpty() == true + if (isSubscribed) { onCreateStudySet() + } else { + AdsUtil.rewardedAd(context) { + onCreateStudySet() + } } }, ) { diff --git a/app/src/main/java/com/pwhs/quickmem/presentation/app/folder/add_study_set/component/AddStudySetToFolderItem.kt b/app/src/main/java/com/pwhs/quickmem/presentation/app/folder/add_study_set/component/AddStudySetToFolderItem.kt index 6363b92e..9f942f1e 100644 --- a/app/src/main/java/com/pwhs/quickmem/presentation/app/folder/add_study_set/component/AddStudySetToFolderItem.kt +++ b/app/src/main/java/com/pwhs/quickmem/presentation/app/folder/add_study_set/component/AddStudySetToFolderItem.kt @@ -8,6 +8,7 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyColumn @@ -17,8 +18,10 @@ import androidx.compose.material3.CardDefaults import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme.colorScheme +import androidx.compose.material3.MaterialTheme.typography import androidx.compose.material3.Scaffold import androidx.compose.material3.Text +import androidx.compose.material3.VerticalDivider import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -26,6 +29,7 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow @@ -106,15 +110,35 @@ fun AddStudySetToFolderItem( } } ) - Text( - studySet.subject?.name - ?: SubjectModel.defaultSubjects[0].name, - style = MaterialTheme.typography.bodySmall.copy( - color = colorScheme.onSurface.copy( - alpha = 0.6f + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(8.dp), + ) { + Text( + text = studySet?.subject?.name ?: SubjectModel.defaultSubjects[0].name, + style = typography.bodySmall.copy( + color = colorScheme.onSurface.copy(alpha = 0.6f) ) ) - ) + if(studySet.isAIGenerated == true) { + VerticalDivider( + modifier = Modifier.height(12.dp) + ) + Text( + stringResource(R.string.txt_ai_generated), + style = typography.bodySmall.copy( + color = colorScheme.onSurface.copy(alpha = 0.6f) + ), + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + Icon( + painter = painterResource(R.drawable.ic_generative_ai), + contentDescription = stringResource(R.string.txt_ai_generated), + modifier = Modifier.size(16.dp) + ) + } + } Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp), diff --git a/app/src/main/java/com/pwhs/quickmem/presentation/app/home/HomeScreen.kt b/app/src/main/java/com/pwhs/quickmem/presentation/app/home/HomeScreen.kt index 2f112735..9ba2f384 100644 --- a/app/src/main/java/com/pwhs/quickmem/presentation/app/home/HomeScreen.kt +++ b/app/src/main/java/com/pwhs/quickmem/presentation/app/home/HomeScreen.kt @@ -29,7 +29,6 @@ import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.FabPosition import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.LargeTopAppBar @@ -269,7 +268,7 @@ private fun Home( modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp) ) }, - expandedHeight = 180.dp, + expandedHeight = 160.dp, collapsedHeight = 56.dp, title = { Card( diff --git a/app/src/main/java/com/pwhs/quickmem/presentation/app/home/components/StudySetHomeItem.kt b/app/src/main/java/com/pwhs/quickmem/presentation/app/home/components/StudySetHomeItem.kt index aa73c403..df75b9ab 100644 --- a/app/src/main/java/com/pwhs/quickmem/presentation/app/home/components/StudySetHomeItem.kt +++ b/app/src/main/java/com/pwhs/quickmem/presentation/app/home/components/StudySetHomeItem.kt @@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width @@ -13,16 +14,20 @@ import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults.cardColors import androidx.compose.material3.CardDefaults.elevatedCardElevation +import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.MaterialTheme.colorScheme import androidx.compose.material3.MaterialTheme.typography import androidx.compose.material3.Scaffold import androidx.compose.material3.Text +import androidx.compose.material3.VerticalDivider import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.font.FontWeight @@ -92,12 +97,35 @@ fun StudySetHomeItem( } } ) - Text( - text = studySet?.subject?.name ?: SubjectModel.defaultSubjects[0].name, - style = typography.bodySmall.copy( - color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f) + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(8.dp), + ) { + Text( + text = studySet?.subject?.name ?: SubjectModel.defaultSubjects[0].name, + style = typography.bodySmall.copy( + color = colorScheme.onSurface.copy(alpha = 0.6f) + ) ) - ) + if (studySet?.isAIGenerated == true) { + VerticalDivider( + modifier = Modifier.height(12.dp) + ) + Text( + stringResource(R.string.txt_ai_generated), + style = typography.bodySmall.copy( + color = colorScheme.onSurface.copy(alpha = 0.6f) + ), + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + Icon( + painter = painterResource(R.drawable.ic_generative_ai), + contentDescription = stringResource(R.string.txt_ai_generated), + modifier = Modifier.size(16.dp) + ) + } + } Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp), diff --git a/app/src/main/java/com/pwhs/quickmem/presentation/app/library/classes/ListClassesScreen.kt b/app/src/main/java/com/pwhs/quickmem/presentation/app/library/classes/ListClassesScreen.kt index 13aac03e..9a38804a 100644 --- a/app/src/main/java/com/pwhs/quickmem/presentation/app/library/classes/ListClassesScreen.kt +++ b/app/src/main/java/com/pwhs/quickmem/presentation/app/library/classes/ListClassesScreen.kt @@ -130,6 +130,7 @@ fun ListClassesScreen( item { if (classes.isNotEmpty()) { SearchTextField( + modifier = Modifier.padding(horizontal = 16.dp), searchQuery = searchQuery, onSearchQueryChange = { searchQuery = it }, placeholder = stringResource(R.string.txt_search_classes), diff --git a/app/src/main/java/com/pwhs/quickmem/presentation/app/library/component/SearchTextField.kt b/app/src/main/java/com/pwhs/quickmem/presentation/app/library/component/SearchTextField.kt index 83f706d8..4e330431 100644 --- a/app/src/main/java/com/pwhs/quickmem/presentation/app/library/component/SearchTextField.kt +++ b/app/src/main/java/com/pwhs/quickmem/presentation/app/library/component/SearchTextField.kt @@ -38,7 +38,6 @@ fun SearchTextField( Box( modifier = modifier .fillMaxWidth() - .padding(horizontal = 16.dp) ) { TextField( value = searchQuery, diff --git a/app/src/main/java/com/pwhs/quickmem/presentation/app/library/folder/ListFolderScreen.kt b/app/src/main/java/com/pwhs/quickmem/presentation/app/library/folder/ListFolderScreen.kt index 0a959618..d2f46332 100644 --- a/app/src/main/java/com/pwhs/quickmem/presentation/app/library/folder/ListFolderScreen.kt +++ b/app/src/main/java/com/pwhs/quickmem/presentation/app/library/folder/ListFolderScreen.kt @@ -130,6 +130,7 @@ fun ListFolderScreen( item { if (folders.isNotEmpty()) { SearchTextField( + modifier = Modifier.padding(horizontal = 16.dp), searchQuery = searchQuery, onSearchQueryChange = { searchQuery = it }, placeholder = stringResource(R.string.txt_search_folders), diff --git a/app/src/main/java/com/pwhs/quickmem/presentation/app/library/study_set/ListStudySetScreen.kt b/app/src/main/java/com/pwhs/quickmem/presentation/app/library/study_set/ListStudySetScreen.kt index 16fcca08..6ecce094 100644 --- a/app/src/main/java/com/pwhs/quickmem/presentation/app/library/study_set/ListStudySetScreen.kt +++ b/app/src/main/java/com/pwhs/quickmem/presentation/app/library/study_set/ListStudySetScreen.kt @@ -134,6 +134,7 @@ fun ListStudySetScreen( LazyColumn { item { SearchTextField( + modifier = Modifier.padding(horizontal = 16.dp), searchQuery = searchQuery, onSearchQueryChange = { searchQuery = it }, placeholder = stringResource(R.string.txt_search_study_sets) diff --git a/app/src/main/java/com/pwhs/quickmem/presentation/app/library/study_set/component/StudySetItem.kt b/app/src/main/java/com/pwhs/quickmem/presentation/app/library/study_set/component/StudySetItem.kt index d4e019cb..d0c30276 100644 --- a/app/src/main/java/com/pwhs/quickmem/presentation/app/library/study_set/component/StudySetItem.kt +++ b/app/src/main/java/com/pwhs/quickmem/presentation/app/library/study_set/component/StudySetItem.kt @@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyColumn @@ -17,15 +18,18 @@ import androidx.compose.material3.CardDefaults.elevatedCardElevation import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.MaterialTheme.colorScheme import androidx.compose.material3.MaterialTheme.typography import androidx.compose.material3.Scaffold import androidx.compose.material3.Text +import androidx.compose.material3.VerticalDivider import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.font.FontWeight @@ -96,12 +100,35 @@ fun StudySetItem( } } ) - Text( - text = studySet?.subject?.name ?: SubjectModel.defaultSubjects[0].name, - style = typography.bodySmall.copy( - color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f) + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(8.dp), + ) { + Text( + text = studySet?.subject?.name ?: SubjectModel.defaultSubjects[0].name, + style = typography.bodySmall.copy( + color = colorScheme.onSurface.copy(alpha = 0.6f) + ) ) - ) + if(studySet?.isAIGenerated == true) { + VerticalDivider( + modifier = Modifier.height(12.dp) + ) + Text( + stringResource(R.string.txt_ai_generated), + style = typography.bodySmall.copy( + color = colorScheme.onSurface.copy(alpha = 0.6f) + ), + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + Icon( + painter = painterResource(R.drawable.ic_generative_ai), + contentDescription = stringResource(R.string.txt_ai_generated), + modifier = Modifier.size(16.dp) + ) + } + } Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp), diff --git a/app/src/main/java/com/pwhs/quickmem/presentation/app/search/SearchScreen.kt b/app/src/main/java/com/pwhs/quickmem/presentation/app/search/SearchScreen.kt index a4defa97..8e771376 100644 --- a/app/src/main/java/com/pwhs/quickmem/presentation/app/search/SearchScreen.kt +++ b/app/src/main/java/com/pwhs/quickmem/presentation/app/search/SearchScreen.kt @@ -95,7 +95,7 @@ fun SearchScreen( Search( modifier = modifier, query = uiState.query, - listResult = uiState.listResult, + listSearchQuery = uiState.listResult, errorMessage = uiState.error, onQueryChange = { viewModel.onEvent(SearchUiAction.OnQueryChanged(it)) }, onNavigateBack = { navigator.navigateUp() }, @@ -117,7 +117,7 @@ fun SearchScreen( private fun Search( modifier: Modifier = Modifier, onNavigateBack: () -> Unit = {}, - listResult: List = emptyList(), + listSearchQuery: List = emptyList(), query: String = "", errorMessage: String = "", onClearAll: () -> Unit = {}, @@ -133,6 +133,7 @@ private fun Search( LargeTopAppBar( title = { SearchTextField( + modifier = Modifier.padding(horizontal = 8.dp), searchQuery = query, onSearchQueryChange = onQueryChange, errorMessage = errorMessage, @@ -159,7 +160,7 @@ private fun Search( .padding(innerPadding), contentAlignment = Alignment.TopCenter ) { - if (listResult.isEmpty()) { + if (listSearchQuery.isEmpty()) { Column( horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(8.dp), @@ -182,7 +183,7 @@ private fun Search( } } else { SearchRecentList( - listResult = listResult, + listResult = listSearchQuery, onSearchRecent = { query -> onSearchRecentClick(query) }, onDelete = { query -> onDeleteQuery(query) }, onClearAll = onClearAll @@ -198,7 +199,7 @@ private fun SearchScreenPreview() { QuickMemTheme { Search( query = "", - listResult = emptyList(), + listSearchQuery = emptyList(), onQueryChange = {}, onSearch = {}, onClearAll = {}, diff --git a/app/src/main/java/com/pwhs/quickmem/presentation/app/study_set/detail/StudySetDetailScreen.kt b/app/src/main/java/com/pwhs/quickmem/presentation/app/study_set/detail/StudySetDetailScreen.kt index d4e0b7ac..e795ee20 100644 --- a/app/src/main/java/com/pwhs/quickmem/presentation/app/study_set/detail/StudySetDetailScreen.kt +++ b/app/src/main/java/com/pwhs/quickmem/presentation/app/study_set/detail/StudySetDetailScreen.kt @@ -280,6 +280,7 @@ fun StudySetDetailScreen( navigator.navigate( StudySetInfoScreenDestination( title = uiState.title, + isAIGenerated = uiState.isAIGenerated, description = uiState.description, isPublic = uiState.isPublic, authorUsername = uiState.user.username, @@ -295,6 +296,7 @@ fun StudySetDetailScreen( onResetProgress = { viewModel.onEvent(StudySetDetailUiAction.OnResetProgressClicked(uiState.id)) }, + isAIGenerated = uiState.isAIGenerated, onNavigateToQuiz = { navigator.navigate( LearnByQuizScreenDestination( @@ -377,6 +379,7 @@ fun StudySetDetail( linkShareCode: String = "", color: Color = Color.Blue, flashCardCount: Int = 0, + isAIGenerated: Boolean = false, studyTime: GetStudyTimeByStudySetResponseModel? = null, userResponse: UserResponseModel = UserResponseModel(), flashCards: List = emptyList(), @@ -439,7 +442,8 @@ fun StudySetDetail( val shareIntent = Intent.createChooser(sendIntent, null) context.startActivity(shareIntent) }, - isOwner = isOwner + isOwner = isOwner, + isAIGenerated = isAIGenerated ) } ) { innerPadding -> diff --git a/app/src/main/java/com/pwhs/quickmem/presentation/app/study_set/detail/StudySetDetailUiState.kt b/app/src/main/java/com/pwhs/quickmem/presentation/app/study_set/detail/StudySetDetailUiState.kt index b906faa4..f55f5b82 100644 --- a/app/src/main/java/com/pwhs/quickmem/presentation/app/study_set/detail/StudySetDetailUiState.kt +++ b/app/src/main/java/com/pwhs/quickmem/presentation/app/study_set/detail/StudySetDetailUiState.kt @@ -15,6 +15,7 @@ data class StudySetDetailUiState( val colorModel: ColorModel = ColorModel(), val subject: SubjectModel = SubjectModel(), val flashCardCount: Int = 0, + val isAIGenerated: Boolean = false, val flashCards: List = emptyList(), val idOfFlashCardSelected: String = "", val isPublic: Boolean = false, diff --git a/app/src/main/java/com/pwhs/quickmem/presentation/app/study_set/detail/StudySetDetailViewModel.kt b/app/src/main/java/com/pwhs/quickmem/presentation/app/study_set/detail/StudySetDetailViewModel.kt index 85aec9a9..24222f91 100644 --- a/app/src/main/java/com/pwhs/quickmem/presentation/app/study_set/detail/StudySetDetailViewModel.kt +++ b/app/src/main/java/com/pwhs/quickmem/presentation/app/study_set/detail/StudySetDetailViewModel.kt @@ -116,6 +116,7 @@ class StudySetDetailViewModel @Inject constructor( colorModel = resource.data.color, linkShareCode = resource.data.linkShareCode ?: "", isLoading = false, + isAIGenerated = resource.data.isAIGenerated ?: false, isOwner = isOwner, ) } diff --git a/app/src/main/java/com/pwhs/quickmem/presentation/app/study_set/detail/component/StudySetDetailTopBar.kt b/app/src/main/java/com/pwhs/quickmem/presentation/app/study_set/detail/component/StudySetDetailTopBar.kt index cb08af0f..daf18daf 100644 --- a/app/src/main/java/com/pwhs/quickmem/presentation/app/study_set/detail/component/StudySetDetailTopBar.kt +++ b/app/src/main/java/com/pwhs/quickmem/presentation/app/study_set/detail/component/StudySetDetailTopBar.kt @@ -29,6 +29,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow @@ -44,6 +45,7 @@ fun StudySetDetailTopAppBar( modifier: Modifier = Modifier, isOwner: Boolean, title: String, + isAIGenerated: Boolean, color: Color, userResponse: UserResponseModel, flashCardCount: Int, @@ -64,6 +66,28 @@ fun StudySetDetailTopAppBar( maxLines = 1, overflow = TextOverflow.Ellipsis ) + if (isAIGenerated) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Start, + modifier = Modifier.padding(top = 4.dp) + ) { + Text( + stringResource(R.string.txt_ai_generated), + style = typography.bodyMedium.copy( + color = colorScheme.secondary + ), + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + Icon( + painter = painterResource(R.drawable.ic_generative_ai), + contentDescription = stringResource(R.string.txt_ai_generated), + modifier = Modifier.size(16.dp) + ) + } + } + Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Start, @@ -105,7 +129,7 @@ fun StudySetDetailTopAppBar( colors = TopAppBarDefaults.topAppBarColors( containerColor = Color.Transparent, ), - expandedHeight = 120.dp, + expandedHeight = if(isAIGenerated) 165.dp else 120.dp, collapsedHeight = 56.dp, navigationIcon = { IconButton( @@ -129,7 +153,7 @@ fun StudySetDetailTopAppBar( contentDescription = stringResource(R.string.txt_share) ) } - if (isOwner){ + if (isOwner) { IconButton( onClick = onAddFlashcard ) { diff --git a/app/src/main/java/com/pwhs/quickmem/presentation/app/study_set/detail/info/StudySetInfoScreen.kt b/app/src/main/java/com/pwhs/quickmem/presentation/app/study_set/detail/info/StudySetInfoScreen.kt index 96b73c8d..d39c8997 100644 --- a/app/src/main/java/com/pwhs/quickmem/presentation/app/study_set/detail/info/StudySetInfoScreen.kt +++ b/app/src/main/java/com/pwhs/quickmem/presentation/app/study_set/detail/info/StudySetInfoScreen.kt @@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.icons.Icons.AutoMirrored.Filled import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material3.Card @@ -22,6 +23,7 @@ import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.res.stringResource @@ -57,6 +59,7 @@ fun StudySetInfoScreen( creationDate = uiState.creationDate, description = uiState.description, isPublic = uiState.isPublic, + isAIGenerated = uiState.isAIGenerated, onNavigateUp = { navigator.navigateUp() } ) } @@ -70,7 +73,8 @@ fun StudySetInfo( creationDate: String, description: String, isPublic: Boolean, - onNavigateUp: () -> Unit = {} + onNavigateUp: () -> Unit = {}, + isAIGenerated: Boolean = false ) { Scaffold( modifier = modifier, @@ -125,7 +129,9 @@ fun StudySetInfo( AsyncImage( model = authorAvatarUrl, contentDescription = stringResource(R.string.txt_user_avatar), - modifier = Modifier.size(30.dp), + modifier = Modifier + .size(30.dp) + .clip(CircleShape), contentScale = ContentScale.Crop ) @@ -212,6 +218,30 @@ fun StudySetInfo( ) } } + + if (isAIGenerated) { + Column { + Text( + text = stringResource(R.string.txt_ai_generated), + style = typography.titleMedium.copy( + fontWeight = FontWeight.Bold + ) + ) + + Card( + modifier = Modifier.fillMaxWidth(), + colors = CardDefaults.cardColors( + containerColor = Color.White + ), + ) { + Text( + text = stringResource(R.string.txt_yes), + style = typography.bodyLarge, + modifier = Modifier.padding(10.dp) + ) + } + } + } } } diff --git a/app/src/main/java/com/pwhs/quickmem/presentation/app/study_set/detail/info/StudySetInfoScreenArgs.kt b/app/src/main/java/com/pwhs/quickmem/presentation/app/study_set/detail/info/StudySetInfoScreenArgs.kt index 9706ccbf..2ad32a8f 100644 --- a/app/src/main/java/com/pwhs/quickmem/presentation/app/study_set/detail/info/StudySetInfoScreenArgs.kt +++ b/app/src/main/java/com/pwhs/quickmem/presentation/app/study_set/detail/info/StudySetInfoScreenArgs.kt @@ -4,6 +4,7 @@ data class StudySetInfoScreenArgs( val title: String, val description: String, val isPublic: Boolean, + val isAIGenerated: Boolean, val authorUsername: String, val authorAvatarUrl: String, val creationDate: String, diff --git a/app/src/main/java/com/pwhs/quickmem/presentation/app/study_set/detail/info/StudySetInfoUiState.kt b/app/src/main/java/com/pwhs/quickmem/presentation/app/study_set/detail/info/StudySetInfoUiState.kt index 52678b87..6e1941ea 100644 --- a/app/src/main/java/com/pwhs/quickmem/presentation/app/study_set/detail/info/StudySetInfoUiState.kt +++ b/app/src/main/java/com/pwhs/quickmem/presentation/app/study_set/detail/info/StudySetInfoUiState.kt @@ -4,6 +4,7 @@ data class StudySetInfoUiState( val title: String = "", val description: String = "", val isPublic: Boolean = false, + val isAIGenerated: Boolean = false, val authorUsername: String = "", val authorAvatarUrl: String = "", val creationDate: String = "", diff --git a/app/src/main/java/com/pwhs/quickmem/presentation/app/study_set/detail/info/StudySetViewModel.kt b/app/src/main/java/com/pwhs/quickmem/presentation/app/study_set/detail/info/StudySetViewModel.kt index cdbbcf77..47bcafc2 100644 --- a/app/src/main/java/com/pwhs/quickmem/presentation/app/study_set/detail/info/StudySetViewModel.kt +++ b/app/src/main/java/com/pwhs/quickmem/presentation/app/study_set/detail/info/StudySetViewModel.kt @@ -20,6 +20,7 @@ class StudySetViewModel @Inject constructor( val authorUsername = savedStateHandle.get("authorUsername") ?: "" val authorAvatarUrl = savedStateHandle.get("authorAvatarUrl") ?: "" val creationDate = savedStateHandle.get("creationDate") ?: "" + val isAIGenerated = savedStateHandle.get("isAIGenerated") ?: false _uiState.value = StudySetInfoUiState( title = title, @@ -27,7 +28,8 @@ class StudySetViewModel @Inject constructor( isPublic = isPublic, authorUsername = authorUsername, authorAvatarUrl = authorAvatarUrl, - creationDate = creationDate + creationDate = creationDate, + isAIGenerated = isAIGenerated ) } } \ No newline at end of file diff --git a/app/src/main/java/com/pwhs/quickmem/presentation/app/study_set/detail/material/CardDetail.kt b/app/src/main/java/com/pwhs/quickmem/presentation/app/study_set/detail/material/CardDetail.kt index 8b97f621..fb830c03 100644 --- a/app/src/main/java/com/pwhs/quickmem/presentation/app/study_set/detail/material/CardDetail.kt +++ b/app/src/main/java/com/pwhs/quickmem/presentation/app/study_set/detail/material/CardDetail.kt @@ -3,6 +3,7 @@ package com.pwhs.quickmem.presentation.app.study_set.detail.material import android.speech.tts.TextToSpeech import androidx.compose.animation.AnimatedContent import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -18,6 +19,7 @@ import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme.colorScheme +import androidx.compose.material3.MaterialTheme.typography import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect @@ -26,6 +28,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.runtime.getValue import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment.Companion.CenterVertically import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -33,6 +36,7 @@ import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import coil.compose.AsyncImage import com.pwhs.quickmem.R @@ -50,7 +54,9 @@ fun CardDetail( onToggleStarClick: (Boolean) -> Unit = { }, onMenuClick: () -> Unit = {}, imageURL: String? = null, - isOwner: Boolean = false + isOwner: Boolean = false, + isAIGenerated: Boolean = false, + color: Color = colorScheme.primary ) { // TextToSpeech state val context = LocalContext.current @@ -164,6 +170,14 @@ fun CardDetail( modifier = Modifier.padding(vertical = 8.dp) ) Row { + if (isAIGenerated) { + Icon( + painter = painterResource(R.drawable.ic_generative_ai), + contentDescription = stringResource(R.string.txt_ai_generated), + modifier = Modifier.size(30.dp), + tint = color + ) + } Spacer(modifier = Modifier.weight(1f)) IconButton( onClick = {} diff --git a/app/src/main/java/com/pwhs/quickmem/presentation/app/study_set/detail/material/MaterialTabScreen.kt b/app/src/main/java/com/pwhs/quickmem/presentation/app/study_set/detail/material/MaterialTabScreen.kt index 209b6acb..13f15b42 100644 --- a/app/src/main/java/com/pwhs/quickmem/presentation/app/study_set/detail/material/MaterialTabScreen.kt +++ b/app/src/main/java/com/pwhs/quickmem/presentation/app/study_set/detail/material/MaterialTabScreen.kt @@ -282,10 +282,12 @@ fun MaterialTabScreen( items(flashCards) { flashCards -> CardDetail( isOwner = isOwner, + color = studySetColor, front = flashCards.term, back = flashCards.definition, isStarred = flashCards.isStarred, imageURL = flashCards.definitionImageURL, + isAIGenerated = flashCards.isAIGenerated, onToggleStarClick = { isStarred -> onToggleStarClick(flashCards.id, isStarred) }, diff --git a/app/src/main/java/com/pwhs/quickmem/presentation/auth/component/AuthTextField.kt b/app/src/main/java/com/pwhs/quickmem/presentation/auth/component/AuthTextField.kt index fe850d6f..75358cbf 100644 --- a/app/src/main/java/com/pwhs/quickmem/presentation/auth/component/AuthTextField.kt +++ b/app/src/main/java/com/pwhs/quickmem/presentation/auth/component/AuthTextField.kt @@ -73,6 +73,7 @@ fun AuthTextField( readOnly = readOnly, enabled = enabled, maxLines = 1, + singleLine = true, keyboardOptions = KeyboardOptions.Default.copy( imeAction = imeAction, keyboardType = when (type) { diff --git a/app/src/main/java/com/pwhs/quickmem/util/ads/AdsUtil.kt b/app/src/main/java/com/pwhs/quickmem/util/ads/AdsUtil.kt index efccedc0..5df99f9c 100644 --- a/app/src/main/java/com/pwhs/quickmem/util/ads/AdsUtil.kt +++ b/app/src/main/java/com/pwhs/quickmem/util/ads/AdsUtil.kt @@ -75,20 +75,15 @@ object AdsUtil { AdRequest.Builder().build(), object : RewardedAdLoadCallback() { override fun onAdLoaded(rewardedAd: RewardedAd) { - // The interstitial ad is loaded - Toast.makeText(context, "Ad Loaded", Toast.LENGTH_SHORT).show() if (context is MainActivity) { rewardedAd.show(context) { - // The user earned the reward onAdWatched() } } - Toast.makeText(context, rewardedAd.rewardItem.type, Toast.LENGTH_SHORT).show() } override fun onAdFailedToLoad(adError: LoadAdError) { - // The interstitial ad failed to load - Toast.makeText(context, adError.message, Toast.LENGTH_SHORT).show() + onAdWatched() } } ) diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index c9d55e56..66c4643e 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -256,4 +256,6 @@ Giáo viên Xin chào! Hãy bắt đầu nào. Chào mừng trở lại! Đăng nhập vào tài khoản của bạn + Được tạo bởi AI + Đúng \ 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 e37cf89f..5b627a73 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -259,4 +259,6 @@ You must be at least 20 years old to change role. Please come back on %1$s. Hello there! Let\'s get started. Welcome back! Log in to your account + AI Generated + Yes \ No newline at end of file