Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/testBranch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ jobs:
uses: android-actions/setup-android@v3

- name: Generate kover coverage report
env:
ADMOB_APP_ID: ${{ secrets.ADMOB_APP_ID }}
ADMOB_BANNER_ID: ${{ secrets.ADMOB_BANNER_ID }}
run: |
chmod +x ./gradlew
./gradlew koverXmlReport
Expand Down
3 changes: 2 additions & 1 deletion app/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
/build
/build
.env
6 changes: 5 additions & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ android {
targetSdk = 35
versionCode = 1
versionName = "1.0"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
resValue("string", "admob_app_id", System.getenv("ADMOB_APP_ID") ?: "")
resValue("string", "admob_banner_id", System.getenv("ADMOB_BANNER_ID") ?: "")
}

buildTypes {
Expand Down Expand Up @@ -160,4 +161,7 @@ dependencies {
kspTest(libs.hilt.android.compiler)
androidTestImplementation(libs.hilt.android.testing)
kspAndroidTest(libs.hilt.android.compiler)

// 구글 광고
implementation(libs.play.services.ads)
}
4 changes: 4 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
android:theme="@style/Theme.NotiManager"
android:enableOnBackInvokedCallback="true"
tools:targetApi="35">
<meta-data
android:name="com.google.android.gms.ads.APPLICATION_ID"
android:value="@string/admob_app_id"/>

<activity
android:name=".presentation.ui.activity.MainActivity"
android:exported="true"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.example.notimanager.presentation.ui.activity

import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
Expand All @@ -15,14 +17,18 @@ import com.example.notimanager.presentation.stateholder.viewmodel.NotificationSe
import com.example.notimanager.presentation.ui.navigation.AppNavHost
import com.example.notimanager.presentation.ui.navigation.HandleBackPress
import com.example.notimanager.presentation.ui.theme.AppTheme
import com.google.android.gms.ads.MobileAds
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch

@AndroidEntryPoint
class MainActivity : ComponentActivity() {
private val serviceViewModel: NotificationServicePermissionViewModel by viewModels()
private val notificationViewModel: NotificationPermissionViewModel by viewModels()
private lateinit var navController: NavController

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

Expand Down Expand Up @@ -56,6 +62,12 @@ class MainActivity : ComponentActivity() {
putExtra("isGroupSummary", true)
}
startService(serviceIntent)

// 광고를 위한 백그라운드 스레드
CoroutineScope(Dispatchers.IO).launch {
// Google Mobile Ads SDK 초기화
MobileAds.initialize(this@MainActivity) { }
}
}

override fun onResume() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.example.notimanager.presentation.ui.ads

import android.content.Context
import com.google.android.gms.ads.AdListener
import com.google.android.gms.ads.AdLoader
import com.google.android.gms.ads.LoadAdError
import com.google.android.gms.ads.nativead.NativeAd
import com.google.android.gms.ads.nativead.NativeAdOptions

fun createNativeAdLoader(context: Context, onAdLoaded: (NativeAd) -> Unit, onAdFailedToLoad: (LoadAdError) -> Unit): AdLoader {
return AdLoader.Builder(context, "ca-app-pub-3940256099942544/2247696110")
.forNativeAd { ad: NativeAd ->
onAdLoaded(ad) // 광고가 로드되었을 때 호출
}
.withAdListener(object : AdListener() {
override fun onAdFailedToLoad(adError: LoadAdError) {
onAdFailedToLoad(adError) // 광고 로드 실패 시 호출
}
})
.withNativeAdOptions(NativeAdOptions.Builder().build())
.build()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.example.notimanager.presentation.ui.ads

import android.app.Activity
import android.content.Context
import android.os.Build
import android.view.WindowMetrics
import com.example.notimanager.R
import com.google.android.gms.ads.AdRequest
import com.google.android.gms.ads.AdSize
import com.google.android.gms.ads.AdView

object AdsUtil{
// Get the ad size with screen width.
private fun getAdSize(context: Context): AdSize {
val displayMetrics = context.resources.displayMetrics
val adWidthPixels =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
val windowMetrics: WindowMetrics = (context as? Activity)?.windowManager!!.currentWindowMetrics
windowMetrics.bounds.width()
} else {
displayMetrics.widthPixels
}
val density = displayMetrics.density
val adWidth = (adWidthPixels / density).toInt()
return AdSize.getCurrentOrientationAnchoredAdaptiveBannerAdSize(context, adWidth)
}

fun getAdView(context: Context): AdView{
val unitId = context.getString(R.string.admob_banner_id)
val adView = AdView(context)
adView.adUnitId = unitId
adView.setAdSize(getAdSize(context))

val adRequest = AdRequest.Builder().build()
adView.loadAd(adRequest)
return adView
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.example.notimanager.presentation.ui.ads

import android.view.View
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.viewinterop.AndroidView
import com.google.android.gms.ads.nativead.NativeAd
import com.google.android.gms.ads.nativead.NativeAdView

@Composable
fun NotiNativeAdView(
ad: NativeAd,
adContent: @Composable (ad: NativeAd, contentView: View) -> Unit,
) {
val contentViewId by remember { mutableIntStateOf(View.generateViewId()) }
val adViewId by remember { mutableIntStateOf(View.generateViewId()) }
AndroidView(
factory = { context ->
val contentView = ComposeView(context).apply {
id = contentViewId
}
NativeAdView(context).apply {
id = adViewId
addView(contentView)
}
},
update = { view ->
val adView = view.findViewById<NativeAdView>(adViewId)
val contentView = view.findViewById<ComposeView>(contentViewId)

adView.setNativeAd(ad)
adView.callToActionView = contentView
contentView.setContent { adContent(ad, contentView) }
}
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package com.example.notimanager.presentation.ui.ads

import android.util.Log
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Badge
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
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.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.example.notimanager.R
import com.example.notimanager.common.objects.DateFormatter.toBitmap
import com.example.notimanager.presentation.ui.component.common.AppIconView
import com.google.android.gms.ads.nativead.NativeAd

@Composable
fun NotiNativeAds(nativeAd: NativeAd) {
NotiNativeAdView(ad = nativeAd) { ad, view ->
Row(
modifier = Modifier.padding(16.dp),
verticalAlignment = Alignment.CenterVertically
){
AppIconView(ad.icon?.drawable?.toBitmap())
Column {
// 광고 뱃지와 함께 광고 제목
Row(){
Image(
painter = painterResource(id = R.drawable.ad_badge),
contentDescription = "ad badge",
modifier = Modifier.size(16.dp)
)
ad.headline?.let {
Text(
text = it,
style = MaterialTheme.typography.bodySmall.copy(fontWeight = FontWeight.Bold),
)
}
}
ad.body?.let {
Text(
text = it,
style = MaterialTheme.typography.bodySmall,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
}
TextButton(
content = { ad.callToAction?.let { Text(text = it) } },
onClick = { view.performClick() },
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,5 @@ fun DateFormatterView(
Text(apply)
}
}

}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
package com.example.notimanager.presentation.ui.component.list

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
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.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.HorizontalDivider
Expand All @@ -10,13 +15,19 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.navigation.NavController
import com.example.notimanager.presentation.stateholder.state.NotificationAppPriorityState
import com.example.notimanager.presentation.stateholder.state.NotificationAppState
import com.example.notimanager.presentation.stateholder.viewmodel.NotificationAppPriorityViewModel
import com.example.notimanager.presentation.stateholder.viewmodel.NotificationAppViewModel
import com.example.notimanager.presentation.ui.ads.AdsUtil.getAdView
import com.example.notimanager.presentation.ui.component.item.NotificationAppItemView

@Composable
Expand All @@ -30,6 +41,10 @@ fun NotificationAppListView(
var currentNotiPriority by remember { mutableStateOf(priorityState.notificationAppList) }
var currentNoti by remember { mutableStateOf(notificationAppState.notificationAppList) }

// 광고
val context = LocalContext.current
val adView = getAdView(context)

LaunchedEffect(priorityState.notificationAppList) {
if (!priorityState.isLoading) {
currentNotiPriority = priorityState.notificationAppList
Expand All @@ -42,41 +57,48 @@ fun NotificationAppListView(
}
}

LazyColumn(
Modifier.fillMaxSize()
) {
items(currentNotiPriority) { notification ->
NotificationAppItemView(
notification = notification,
onClick = {
navController
.navigate(
"titleScreen/${notification.appName}"
)
},
viewModel = viewModel,
priorityViewModel = priorityViewModel
)
}
if (currentNotiPriority.isNotEmpty()){
item {
HorizontalDivider()
Box(modifier = Modifier.fillMaxSize()) {
LazyColumn(
modifier = Modifier
.fillMaxSize()
.padding(bottom = 56.dp) // AndroidView의 높이만큼 패딩 추가
) {
items(currentNotiPriority) { notification ->
NotificationAppItemView(
notification = notification,
onClick = {
navController.navigate("titleScreen/${notification.appName}")
},
viewModel = viewModel,
priorityViewModel = priorityViewModel
)
}
if (currentNotiPriority.isNotEmpty()) {
item {
HorizontalDivider()
}
}
}


items(currentNoti) { notification ->
NotificationAppItemView(
notification = notification,
onClick = {
navController
.navigate(
"titleScreen/${notification.appName}"
)
},
viewModel = viewModel,
priorityViewModel = priorityViewModel
)
items(currentNoti) { notification ->
NotificationAppItemView(
notification = notification,
onClick = {
navController.navigate("titleScreen/${notification.appName}")
},
viewModel = viewModel,
priorityViewModel = priorityViewModel
)
}
}

AndroidView(
factory = { adView },
update = {},
modifier = Modifier
.align(Alignment.BottomCenter)
.fillMaxWidth()
.height(56.dp)
)
}

}
Loading
Loading