From c33ab5670c1847ae2d158c511379c3f5fa6a3aa5 Mon Sep 17 00:00:00 2001 From: veerkakar17 Date: Sun, 22 Mar 2026 16:11:39 -0400 Subject: [PATCH 1/3] Added news component --- PennMobile/build.gradle | 4 + .../pennmobile/home/adapters/HomeAdapter.kt | 149 +--------------- .../pennmobile/home/classes/NewsCompose.kt | 130 ++++++++++++++ .../home/viewholders/HomeNewsCardHolder.kt | 13 +- .../src/main/res/layout/home_news_card.xml | 160 +----------------- .../res/layout/news_card_compose_view.xml | 16 ++ gradle/libs.versions.toml | 8 +- 7 files changed, 172 insertions(+), 308 deletions(-) create mode 100644 PennMobile/src/main/java/com/pennapps/labs/pennmobile/home/classes/NewsCompose.kt create mode 100644 PennMobile/src/main/res/layout/news_card_compose_view.xml diff --git a/PennMobile/build.gradle b/PennMobile/build.gradle index 0139198f1..249140ce4 100644 --- a/PennMobile/build.gradle +++ b/PennMobile/build.gradle @@ -61,6 +61,7 @@ dependencies { // If you want the foundation layout, use the bundle or the specific activity compose you have defined implementation libs.androidx.activity.compose implementation libs.androidx.material3.android + implementation libs.androidx.compose.ui.tooling.preview // implementation libs.androidx.foundation.layout androidTestImplementation libs.androidx.espresso.core @@ -120,6 +121,7 @@ dependencies { implementation libs.bundles.ui implementation libs.bundles.google implementation libs.bundles.hilt + debugImplementation libs.androidx.compose.ui.tooling ksp libs.androidx.room.compiler ksp libs.androidx.hilt.compiler @@ -129,6 +131,8 @@ dependencies { implementation libs.androidx.hilt.android ksp libs.androidx.hilt.compiler + + implementation libs.coil.compose } String getPlatformClientID() { diff --git a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/home/adapters/HomeAdapter.kt b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/home/adapters/HomeAdapter.kt index 643113cb9..3acf0b8ea 100644 --- a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/home/adapters/HomeAdapter.kt +++ b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/home/adapters/HomeAdapter.kt @@ -18,6 +18,7 @@ import androidx.browser.customtabs.CustomTabsClient import androidx.browser.customtabs.CustomTabsIntent import androidx.browser.customtabs.CustomTabsServiceConnection import androidx.browser.customtabs.CustomTabsSession +import androidx.compose.ui.platform.ComposeView import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.content.ContextCompat.getColor import androidx.core.content.ContextCompat.startActivity @@ -57,6 +58,7 @@ import com.pennapps.labs.pennmobile.home.classes.Poll import com.pennapps.labs.pennmobile.home.classes.PollCell import com.pennapps.labs.pennmobile.home.classes.Post import com.pennapps.labs.pennmobile.home.classes.PostCell +import com.pennapps.labs.pennmobile.home.classes.newsComposableComponent import com.pennapps.labs.pennmobile.home.fragments.NewsFragment import com.pennapps.labs.pennmobile.home.viewholders.HomeBaseHolder import com.pennapps.labs.pennmobile.home.viewholders.HomeCalendarHolder @@ -583,149 +585,10 @@ class HomeAdapter( ) { val article = cell.article - if (article.imageUrl.isNullOrEmpty()) { - holder.itemView.visibility = View.GONE - holder.newsCardContainer.visibility = View.GONE - holder.homeNewsImageView.setImageDrawable(null) - holder.homeNewsTitle.text = "" - holder.homeNewsSubtitle.text = "" - holder.homeNewsTimestamp.text = "" - dataModel.notifyNewsBlurLoaded() - return - } - - holder.homeNewsTitle.text = article.title - holder.homeNewsSubtitle.text = article.subtitle - - holder.homeNewsTimestamp.text = article.timestamp?.trim() - - Glide - .with(mContext) - .load(article.imageUrl) - .fitCenter() - .centerCrop() - .into(holder.homeNewsImageView) - -// /** Adds dynamically generated accent color from the fetched image to the news card */ - var accentColor: Int = getColor(mContext, R.color.black) - mActivity.lifecycleScope.launch(Dispatchers.Default) { - Log.d("HomeAdapter", "Image Url is ${article.imageUrl}") - val bitmap = - withContext(Dispatchers.IO) { - Glide - .with(mContext) - .load(article.imageUrl) - .submit() - .get() - }.toBitmap() - - // Create palette from bitmap - fun createPaletteSync(bitmap: Bitmap): Palette = Palette.from(bitmap).generate() - val vibrantSwatch: Palette.Swatch? = createPaletteSync(bitmap).darkVibrantSwatch - vibrantSwatch?.rgb?.let { accentColor = it } - - mActivity.runOnUiThread { - // Change all the components to match the accent color palette - vibrantSwatch?.titleTextColor?.let { - DrawableCompat.setTint( - DrawableCompat.wrap(holder.newsCardLogo.drawable), - ColorUtils.setAlphaComponent(it, 150), - ) - DrawableCompat.setTint( - DrawableCompat.wrap(holder.newsInfoIcon.drawable), - it, - ) - DrawableCompat.setTint( - DrawableCompat.wrap(holder.dotDivider.drawable), - it, - ) - holder.newsButton.setTextColor(ColorUtils.setAlphaComponent(it, 150)) - DrawableCompat.setTint( - DrawableCompat.wrap(holder.newsButton.background), - it, - ) - holder.homeNewsTitle.setTextColor( - ColorUtils.setAlphaComponent( - it, - 150, - ), - ) - holder.homeNewsSubtitle.setTextColor(it) - holder.homeNewsTimestamp.setTextColor(it) - } - holder.newsCardContainer.background = - BitmapDrawable( - holder.itemBinding.root.resources, - bitmap, - ) - holder.newsBlurView - .setOverlayColor(ColorUtils.setAlphaComponent(accentColor, 150)) - - // tell model that the news blur view has been loaded - dataModel.notifyNewsBlurLoaded() - } - } - - // Logic for the more info button on the news card - holder.newsInfoIcon.setOnClickListener { - when (holder.homeNewsSubtitle.visibility) { - View.GONE -> { - holder.homeNewsSubtitle.visibility = View.VISIBLE - holder.homeNewsTitle.setPadding(0, 0, 0, 0) - holder.newsBlurView - .setOverlayColor(ColorUtils.setAlphaComponent(accentColor, 250)) - } - - View.VISIBLE -> { - holder.homeNewsSubtitle.visibility = View.GONE - holder.homeNewsTitle.setPadding(0, 0, 0, convertToDp(mContext, 8f)) - holder.newsBlurView - .setOverlayColor(ColorUtils.setAlphaComponent(accentColor, 150)) - } - } - } - - // Sets up blur view on news card - holder.newsBlurView - .setupWith(holder.newsCardContainer, RenderScriptBlur(mContext)) - .setFrameClearDrawable(ColorDrawable(getColor(mContext, R.color.white))) - .setBlurRadius(25f) - - holder.newsButton.setOnClickListener { - val url = article?.articleUrl - - val connection = NewsCustomTabsServiceConnection() - builder = CustomTabsIntent.Builder() - share = Intent(Intent.ACTION_SEND) - share?.type = "text/plain" - builder?.setToolbarColor(0x3E50B4) - builder?.setStartAnimations( - mContext, - androidx.appcompat.R.anim.abc_popup_enter, - androidx.appcompat.R.anim.abc_popup_exit, - ) - CustomTabsClient.bindCustomTabsService( - mContext, - NewsFragment.CUSTOM_TAB_PACKAGE_NAME, - connection, - ) - - if (mContext.isChromeCustomTabsSupported()) { - share?.putExtra(Intent.EXTRA_TEXT, url) - builder?.addMenuItem( - "Share", - PendingIntent.getActivity( - mContext, - 0, - share, - PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE, - ), - ) - customTabsIntent = builder?.build() - customTabsIntent?.launchUrl(mActivity, Uri.parse(url)) - } else { - val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(url)) - startActivity(mContext, browserIntent, null) + val composeView = holder.itemView.findViewById(R.id.news_compose_view) + composeView.apply { + setContent { + newsComposableComponent(article) } } } diff --git a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/home/classes/NewsCompose.kt b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/home/classes/NewsCompose.kt new file mode 100644 index 000000000..ec0a20c9f --- /dev/null +++ b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/home/classes/NewsCompose.kt @@ -0,0 +1,130 @@ +package com.pennapps.labs.pennmobile.home.classes + +import android.content.Context +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Button +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.blur +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalUriHandler +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import coil.compose.rememberAsyncImagePainter + +@Composable +fun newsComposableComponent(article: Article) { + val uriHandler = LocalUriHandler.current + var url = "https://www.thedp.com/" + article.articleUrl?.let { + url = article.articleUrl + } + + Column( + modifier = + Modifier + .fillMaxWidth() + .aspectRatio(1f) + .padding(16.dp) + .clickable { uriHandler.openUri(url) } + .clip(RoundedCornerShape(16.dp)), + ) { + Box( + modifier = + Modifier + .weight(1f) + .fillMaxWidth(), + ) { + Image( + painter = rememberAsyncImagePainter(article.imageUrl), + contentDescription = "News Image", + contentScale = ContentScale.Crop, + modifier = + Modifier + .matchParentSize() + .fillMaxWidth(), + ) + } + Box( + modifier = + Modifier + .fillMaxWidth(), + ) { + Image( + painter = rememberAsyncImagePainter(article.imageUrl), + contentDescription = "News Image", + contentScale = ContentScale.Crop, + alignment = Alignment.TopCenter, + modifier = + Modifier + .matchParentSize() + .blur(16.dp) + .fillMaxWidth(), + ) + Box( + modifier = + Modifier + .matchParentSize() + .background(Color.Black.copy(alpha = 0.5f)), + ) + Box( + modifier = + Modifier + .fillMaxWidth() + .padding(12.dp), + ) { + Column( + verticalArrangement = Arrangement.spacedBy(6.dp), + ) { + Row( + modifier = Modifier.fillMaxWidth(), + ) { + Text(text = " THE DAILY PENNSYLVANIAN", color = Color.LightGray) + Spacer(Modifier.weight(1f)) + + var time = "1 hour ago" + article.timestamp?.let { + time = article.timestamp + } + Text(time, color = Color.LightGray) + } + + var title = "sample title here" + article.title?.let { + title = article.title + } + + Text( + text = title, + color = Color.White, + fontWeight = FontWeight.Bold, + fontSize = 18.sp, + ) + + var description = "sample description here" + article.subtitle?.let { + description = article.subtitle + } + Text(text = description, color = Color.White) + } + } + } + } +} diff --git a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/home/viewholders/HomeNewsCardHolder.kt b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/home/viewholders/HomeNewsCardHolder.kt index 8a500c43c..5946cfedf 100644 --- a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/home/viewholders/HomeNewsCardHolder.kt +++ b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/home/viewholders/HomeNewsCardHolder.kt @@ -5,15 +5,4 @@ import com.pennapps.labs.pennmobile.databinding.HomeNewsCardBinding class HomeNewsCardHolder( val itemBinding: HomeNewsCardBinding, -) : RecyclerView.ViewHolder(itemBinding.root) { - var homeNewsTitle = itemBinding.homeNewsTitle - var homeNewsSubtitle = itemBinding.homeNewsSubtitle - var homeNewsTimestamp = itemBinding.homeNewsTimestamp - var homeNewsImageView = itemBinding.homeNewsIv - var newsCardLogo = itemBinding.newsCardLogo - var newsInfoIcon = itemBinding.newsInfoIcon - var newsBlurView = itemBinding.blurView - var newsCardContainer = itemBinding.newsCardContainer - var newsButton = itemBinding.button - var dotDivider = itemBinding.dotDivider -} +) : RecyclerView.ViewHolder(itemBinding.root) diff --git a/PennMobile/src/main/res/layout/home_news_card.xml b/PennMobile/src/main/res/layout/home_news_card.xml index 60df85168..74de0ae52 100644 --- a/PennMobile/src/main/res/layout/home_news_card.xml +++ b/PennMobile/src/main/res/layout/home_news_card.xml @@ -1,160 +1,16 @@ + android:layout_height="wrap_content"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintBottom_toBottomOf="parent" /> - - \ No newline at end of file + diff --git a/PennMobile/src/main/res/layout/news_card_compose_view.xml b/PennMobile/src/main/res/layout/news_card_compose_view.xml new file mode 100644 index 000000000..74de0ae52 --- /dev/null +++ b/PennMobile/src/main/res/layout/news_card_compose_view.xml @@ -0,0 +1,16 @@ + + + + + + diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a56d39fb6..57d2173f6 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -62,6 +62,10 @@ rxandroidVersion = "1.2.1" rxjava1 = "1.3.8" rxJavaCoroutineConverter = "1.8.0" testng = "7.8.0" +uiToolingPreview = "1.10.2" +uiTooling = "1.10.2" +coil = "2.6.0" + [libraries] adapter-rxjava = { module = "com.squareup.retrofit2:adapter-rxjava", version.ref = "adapterRxjava" } @@ -137,7 +141,9 @@ reactivex-rxandroid = { module = "io.reactivex:rxandroid", version.ref = "rxandr retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" } rxjava = { module = "io.reactivex:rxjava", version.ref = "rxjava1" } testng = { module = "org.testng:testng", version.ref = "testng" } - +androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview", version.ref = "uiToolingPreview" } +androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling", version.ref = "uiTooling" } +coil-compose = { module = "io.coil-kt:coil-compose", version.ref = "coil" } [plugins] android-application = { id = "com.android.application", version.ref = "agp" } From b4a90177b5f8d122798c2d62869af9b7f7ceaee9 Mon Sep 17 00:00:00 2001 From: veerkakar17 Date: Sun, 22 Mar 2026 18:25:19 -0400 Subject: [PATCH 2/3] Style Fixes for Ktlint --- .../labs/pennmobile/home/adapters/HomeAdapter.kt | 4 ++-- .../pennapps/labs/pennmobile/home/classes/NewsCompose.kt | 9 ++++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/home/adapters/HomeAdapter.kt b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/home/adapters/HomeAdapter.kt index 3acf0b8ea..0b9ffca90 100644 --- a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/home/adapters/HomeAdapter.kt +++ b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/home/adapters/HomeAdapter.kt @@ -54,11 +54,11 @@ import com.pennapps.labs.pennmobile.home.classes.CalendarCell import com.pennapps.labs.pennmobile.home.classes.CalendarEvent import com.pennapps.labs.pennmobile.home.classes.HomepageDataModel import com.pennapps.labs.pennmobile.home.classes.NewsCell +import com.pennapps.labs.pennmobile.home.classes.NewsComposableComponent import com.pennapps.labs.pennmobile.home.classes.Poll import com.pennapps.labs.pennmobile.home.classes.PollCell import com.pennapps.labs.pennmobile.home.classes.Post import com.pennapps.labs.pennmobile.home.classes.PostCell -import com.pennapps.labs.pennmobile.home.classes.newsComposableComponent import com.pennapps.labs.pennmobile.home.fragments.NewsFragment import com.pennapps.labs.pennmobile.home.viewholders.HomeBaseHolder import com.pennapps.labs.pennmobile.home.viewholders.HomeCalendarHolder @@ -588,7 +588,7 @@ class HomeAdapter( val composeView = holder.itemView.findViewById(R.id.news_compose_view) composeView.apply { setContent { - newsComposableComponent(article) + NewsComposableComponent(article) } } } diff --git a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/home/classes/NewsCompose.kt b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/home/classes/NewsCompose.kt index ec0a20c9f..22711a5ca 100644 --- a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/home/classes/NewsCompose.kt +++ b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/home/classes/NewsCompose.kt @@ -30,7 +30,10 @@ import androidx.compose.ui.unit.sp import coil.compose.rememberAsyncImagePainter @Composable -fun newsComposableComponent(article: Article) { +fun NewsComposableComponent( + article: Article, + modifier: Modifier = Modifier, +) { val uriHandler = LocalUriHandler.current var url = "https://www.thedp.com/" article.articleUrl?.let { @@ -43,8 +46,8 @@ fun newsComposableComponent(article: Article) { .fillMaxWidth() .aspectRatio(1f) .padding(16.dp) - .clickable { uriHandler.openUri(url) } - .clip(RoundedCornerShape(16.dp)), + .clip(RoundedCornerShape(16.dp)) + .clickable { uriHandler.openUri(url) }, ) { Box( modifier = From f8af86caa83e232004dfc1101fb826a3a4ca732c Mon Sep 17 00:00:00 2001 From: veerkakar17 Date: Sun, 5 Apr 2026 18:36:57 -0400 Subject: [PATCH 3/3] Fixed spacing for news component --- .../labs/pennmobile/home/classes/NewsCompose.kt | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/home/classes/NewsCompose.kt b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/home/classes/NewsCompose.kt index 22711a5ca..331a3ac5c 100644 --- a/PennMobile/src/main/java/com/pennapps/labs/pennmobile/home/classes/NewsCompose.kt +++ b/PennMobile/src/main/java/com/pennapps/labs/pennmobile/home/classes/NewsCompose.kt @@ -10,8 +10,10 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Button @@ -26,6 +28,7 @@ import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.em import androidx.compose.ui.unit.sp import coil.compose.rememberAsyncImagePainter @@ -62,7 +65,8 @@ fun NewsComposableComponent( modifier = Modifier .matchParentSize() - .fillMaxWidth(), + .fillMaxWidth() + .fillMaxHeight(), ) } Box( @@ -91,7 +95,7 @@ fun NewsComposableComponent( modifier = Modifier .fillMaxWidth() - .padding(12.dp), + .padding(16.dp), ) { Column( verticalArrangement = Arrangement.spacedBy(6.dp), @@ -118,14 +122,15 @@ fun NewsComposableComponent( text = title, color = Color.White, fontWeight = FontWeight.Bold, - fontSize = 18.sp, + fontSize = 16.sp, + lineHeight = 1.25.em, ) var description = "sample description here" article.subtitle?.let { description = article.subtitle } - Text(text = description, color = Color.White) + Text(text = description, color = Color.White, lineHeight = 1.15.em) } } }