Skip to content

Commit

Permalink
Merge pull request #6 from KNUTICE/main
Browse files Browse the repository at this point in the history
[PR] Release 1.0.0 Alpha
  • Loading branch information
doyoonkim3312 authored Oct 9, 2024
2 parents 7d0c05b + fee5804 commit cddfef1
Show file tree
Hide file tree
Showing 48 changed files with 588 additions and 42 deletions.
3 changes: 3 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ dependencies {
implementation(libs.retrofit)
implementation(libs.converter.gson)

// Jsoup HTML Parser Library
implementation(libs.jsoup)

}

// Allow references to generated code
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:roundIcon="@mipmap/ic_launcher"
android:supportsRtl="true"
android:theme="@style/Theme.KNUTICE"
tools:targetApi="31">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ import com.doyoonkim.knutice.model.NoticeCategory
import com.doyoonkim.knutice.model.NoticesPerPage
import com.doyoonkim.knutice.model.TopThreeNotices
import dagger.hilt.android.scopes.ActivityRetainedScoped
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.channelFlow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.launch
import javax.inject.Inject

/*
Expand Down Expand Up @@ -59,4 +61,10 @@ class NoticeLocalRepository @Inject constructor(
}
}.flowOn(Dispatchers.IO)
}

fun getFullNoticeContent(url: String): Flow<String> {
return flow<String> {
emit(remoteSource.getFullNoticeContent(url).await())
}.flowOn(Dispatchers.IO)
}
}
13 changes: 13 additions & 0 deletions app/src/main/java/com/doyoonkim/knutice/data/NoticeRemoteSource.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ import com.doyoonkim.knutice.model.NoticeCategory
import com.doyoonkim.knutice.model.NoticesPerPage
import com.doyoonkim.knutice.model.TopThreeNotices
import com.example.knutice.BuildConfig
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import org.jsoup.Jsoup
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.http.GET
Expand Down Expand Up @@ -36,6 +41,14 @@ class NoticeRemoteSource @Inject constructor() {
}
}

suspend fun getFullNoticeContent(url: String): Deferred<String> =
CoroutineScope(Dispatchers.IO).async {
Jsoup.connect(url)
.get()
.getElementsByClass("bbs-view-content bbs-view-content-skin05")
.text() ?: "Unable to receive full notice content"
}

}

interface KnuticeService {
Expand Down
10 changes: 10 additions & 0 deletions app/src/main/java/com/doyoonkim/knutice/domain/CrawlFullContent.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.doyoonkim.knutice.domain

import com.doyoonkim.knutice.viewModel.DetailedContentState
import kotlinx.coroutines.flow.Flow

interface CrawlFullContent {

fun getFullContentFromSource(title: String, info: String, url: String): Flow<DetailedContentState>

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.doyoonkim.knutice.domain

import com.doyoonkim.knutice.data.NoticeLocalRepository
import com.doyoonkim.knutice.viewModel.DetailedContentState
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import javax.inject.Inject

class CrawlFullContentImpl @Inject constructor(
private val repository: NoticeLocalRepository
): CrawlFullContent {
override fun getFullContentFromSource(
title: String,
info: String,
url: String
): Flow<DetailedContentState> {
return repository.getFullNoticeContent(url)
.map {
DetailedContentState(
title = title,
info = info,
fullContent = it,
fullContentUrl = url
)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.doyoonkim.knutice.navigation

import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
Expand All @@ -10,6 +12,7 @@ import com.doyoonkim.knutice.model.Destination
import com.doyoonkim.knutice.model.NoticeCategory
import com.doyoonkim.knutice.presentation.CategorizedNotification
import com.doyoonkim.knutice.presentation.MoreCategorizedNotification
import com.doyoonkim.knutice.presentation.UserPreference
import com.doyoonkim.knutice.viewModel.MainActivityViewModel

@Composable
Expand Down Expand Up @@ -58,5 +61,14 @@ fun MainNavigator(
)
MoreCategorizedNotification(category = NoticeCategory.EVENT_NEWS)
}

composable(Destination.SETTINGS.name) {
viewModel.updateState(
updatedCurrentLocation = Destination.SETTINGS
)
UserPreference(
Modifier.padding(top = 20.dp, start = 10.dp, end = 10.dp)
)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.doyoonkim.knutice.presentation

import android.content.res.Configuration
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
Expand Down Expand Up @@ -34,6 +35,7 @@ import com.doyoonkim.knutice.ui.theme.notificationType3
import com.doyoonkim.knutice.ui.theme.notificationType4
import com.doyoonkim.knutice.ui.theme.subTitle
import com.doyoonkim.knutice.viewModel.CategorizedNotificationViewModel
import com.doyoonkim.knutice.viewModel.DetailedContentState
import com.example.knutice.R

@Composable
Expand All @@ -54,33 +56,51 @@ fun CategorizedNotification(
NotificationPreviewList (
listTitle = stringResource(R.string.general_news),
titleColor = MaterialTheme.colorScheme.notificationType1,
contents = uiState.notificationGeneral
) {
navController.navigate(Destination.MORE_GENERAL.name)
contents = uiState.notificationGeneral,
onMoreClicked = { navController.navigate(Destination.MORE_GENERAL.name) }
) { title, info, url ->
viewModel.getFullNoticeContent(title, info, url)
}

NotificationPreviewList(
listTitle = stringResource(R.string.academic_news),
titleColor = MaterialTheme.colorScheme.notificationType2,
contents = uiState.notificationAcademic
) {
navController.navigate(Destination.MORE_ACADEMIC.name)
contents = uiState.notificationAcademic,
onMoreClicked = { navController.navigate(Destination.MORE_ACADEMIC.name) }
) { title, info, url ->
viewModel.getFullNoticeContent(title, info, url)
}

NotificationPreviewList(
listTitle = stringResource(R.string.scholarship_news),
titleColor = MaterialTheme.colorScheme.notificationType3,
contents = uiState.notificationScholarship
) {
navController.navigate(Destination.MORE_SCHOLARSHIP.name)
contents = uiState.notificationScholarship,
onMoreClicked = { navController.navigate(Destination.MORE_SCHOLARSHIP.name) }
) { title, info, url ->
viewModel.getFullNoticeContent(title, info, url)
}

NotificationPreviewList(
listTitle = stringResource(R.string.event_news),
titleColor = MaterialTheme.colorScheme.notificationType4,
contents = uiState.notificationEvent
contents = uiState.notificationEvent,
onMoreClicked = { navController.navigate(Destination.MORE_EVENT.name) }
) { title, info, url ->
viewModel.getFullNoticeContent(title, info, url)
}
}

AnimatedVisibility(
visible = uiState.isDetailedViewOpened
) {
DetailedNoticeContent(
modifier = Modifier.padding(10.dp),
requested = uiState.requestedContent
) {
navController.navigate(Destination.MORE_EVENT)
viewModel.updateState(
updatedIsDetailedViewOpened = false,
updatedRequestedContent = DetailedContentState()
)
}
}
}
Expand All @@ -91,7 +111,8 @@ fun NotificationPreviewList(
listTitle: String = "List Title goes here",
titleColor: Color = Color.Unspecified,
contents: List<Notice> = listOf(),
onMoreClicked: () -> Unit = { }
onMoreClicked: () -> Unit = { },
onNoticeClicked: (String, String, String) -> Unit
) {
Column(
modifier = Modifier.fillMaxWidth()
Expand Down Expand Up @@ -126,7 +147,9 @@ fun NotificationPreviewList(
NotificationPreviewCard(
notificationTitle = content.title,
notificationInfo = "[${content.departName}] ${content.timestamp}"
) { }
) {
onNoticeClicked(content.title, content.departName, content.url)
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package com.doyoonkim.knutice.presentation

import android.content.Intent
import android.net.Uri
import androidx.compose.foundation.Image
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.padding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonColors
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
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.graphics.ColorFilter
import androidx.compose.ui.platform.LocalContext
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.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.doyoonkim.knutice.ui.theme.buttonContainer
import com.doyoonkim.knutice.ui.theme.containerBackground
import com.doyoonkim.knutice.ui.theme.title
import com.doyoonkim.knutice.viewModel.DetailedContentState
import com.example.knutice.R

@Composable
fun DetailedNoticeContent(
modifier: Modifier = Modifier,
requested: DetailedContentState = DetailedContentState(),
onCloseRequested: () -> Unit = { }
) {
val localContext = LocalContext.current

Surface(
modifier = modifier.fillMaxWidth(),
shape = RoundedCornerShape(10.dp),
color = MaterialTheme.colorScheme.containerBackground
) {
Column(
Modifier.padding(15.dp)
) {
Row(
Modifier.fillMaxWidth()
.weight(0.5f),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.Bottom
) {
Text(
modifier = Modifier.weight(5f).wrapContentHeight(),
text = requested.title,
fontSize = 28.sp,
fontWeight = FontWeight.SemiBold,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
IconButton(
modifier = Modifier.weight(1f).wrapContentSize(),
onClick = { onCloseRequested() }
) {
Image(
painter = painterResource(R.drawable.baseline_close_24),
contentDescription = "Close Button",
colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.title)
)
}
}

Text(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.weight(0.5f),
text = requested.info,
fontSize = 16.sp,
fontWeight = FontWeight.Normal
)

Text(
modifier = Modifier.fillMaxWidth()
.weight(8f),
text = requested.fullContent,
fontSize = 18.sp,
fontWeight = FontWeight.Medium,
)

Button(
onClick = {
val webIntent = Intent(Intent.ACTION_VIEW, Uri.parse(requested.fullContentUrl))
localContext.startActivity(webIntent)
},
shape = RoundedCornerShape(15.dp),
modifier = Modifier.fillMaxWidth()
.padding(start = 15.dp, end = 15.dp, top = 5.dp, bottom = 5.dp)
.weight(0.7f),
colors = ButtonColors(
containerColor = MaterialTheme.colorScheme.buttonContainer,
contentColor = Color.White,
disabledContentColor = Color.Unspecified,
disabledContainerColor = Color.Unspecified
)
) {
Text(
modifier = Modifier.fillMaxWidth(),
text = stringResource(R.string.btn_more_on_browser),
textAlign = TextAlign.Center,
fontSize = 14.sp,
fontWeight = FontWeight.SemiBold
)
}
}
}
}


@Preview
@Composable
fun DetailedNoticeContent_Preview() {
DetailedNoticeContent(
requested = DetailedContentState(
title = "Test",
info = "Test",
fullContent = "Full Content"
)
)
}
Loading

0 comments on commit cddfef1

Please sign in to comment.