Skip to content

Commit

Permalink
[droidknights#259, 위젯 기능 추가]
Browse files Browse the repository at this point in the history
[작업내용]
  - Glance Library 추가 (targetSDK 버전 제한으로 beta01 버전 적용)
  - 북마크된 세션 표시 위젯 기능 개발
    - 타이틀 클릭 시 앱 실행
    - 앱 내 세션 북마크 추가 시 위젯에 리스트 업데이트 하도록 구현
    - 위젯에서 세션 클릭 시 앱 - 해당 세션 상세 정보 화면 이동
    - 세션 카드 - 타이틀, 시작 ~ 종료 시간, 발표자 표시

[TODO]
  - 발표자가 2인 이상일 경우 이름 표시할 수 있도록 로직 수정 (현재 발표자가 2인 이상인 경우 없음)
  - 위젯 내 북마크 해제 버튼 추가
  • Loading branch information
jeongth9446 committed Sep 7, 2023
1 parent 955bbba commit eec8ed8
Show file tree
Hide file tree
Showing 23 changed files with 470 additions and 2 deletions.
2 changes: 2 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,6 @@ dependencies {
implementation(projects.feature.home)

implementation(projects.core.designsystem)

implementation(projects.widget)
}
11 changes: 11 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,17 @@
android:theme="@style/Theme.DroidKnights2023"
tools:targetApi="31">

<receiver
android:name=".DroidKnightsWidgetReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/widget_info" />
</receiver>

</application>

</manifest>
2 changes: 2 additions & 0 deletions core/designsystem/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@ dependencies {
implementation(libs.landscapist.bom)
implementation(libs.landscapist.coil)
implementation(libs.landscapist.placeholder)

implementation(libs.androidx.glance)
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ import androidx.compose.runtime.compositionLocalOf
import androidx.compose.ui.platform.LocalInspectionMode
import androidx.compose.ui.platform.LocalView
import androidx.core.view.WindowCompat
import androidx.glance.GlanceTheme
import androidx.glance.color.ColorProvider
import androidx.glance.color.colorProviders

private val DarkColorScheme = darkColorScheme(
primary = White,
Expand Down Expand Up @@ -86,7 +89,8 @@ fun KnightsTheme(
SideEffect {
val window = (view.context as Activity).window
WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = !darkTheme
WindowCompat.getInsetsController(window, view).isAppearanceLightNavigationBars = !darkTheme
WindowCompat.getInsetsController(window, view).isAppearanceLightNavigationBars =
!darkTheme
}
}

Expand All @@ -106,3 +110,69 @@ object KnightsTheme {
@Composable
get() = LocalTypography.current
}

private val WidgetColorProviers = colorProviders(
primary = ColorProvider(LightColorScheme.primary, DarkColorScheme.primary),
onPrimary = ColorProvider(LightColorScheme.onPrimary, DarkColorScheme.onPrimary),
primaryContainer = ColorProvider(
LightColorScheme.primaryContainer,
DarkColorScheme.primaryContainer
),
onPrimaryContainer = ColorProvider(
LightColorScheme.onPrimaryContainer,
DarkColorScheme.onPrimaryContainer
),
inversePrimary = ColorProvider(LightColorScheme.inversePrimary, DarkColorScheme.inversePrimary),
secondary = ColorProvider(LightColorScheme.secondary, DarkColorScheme.secondary),
onSecondary = ColorProvider(LightColorScheme.onSecondary, DarkColorScheme.onSecondary),
secondaryContainer = ColorProvider(
LightColorScheme.secondaryContainer,
DarkColorScheme.secondaryContainer
),
onSecondaryContainer = ColorProvider(
LightColorScheme.onSecondaryContainer,
DarkColorScheme.onSecondaryContainer
),
tertiary = ColorProvider(LightColorScheme.tertiary, DarkColorScheme.tertiary),
onTertiary = ColorProvider(LightColorScheme.onTertiary, DarkColorScheme.onTertiary),
tertiaryContainer = ColorProvider(
LightColorScheme.tertiaryContainer,
DarkColorScheme.tertiaryContainer
),
onTertiaryContainer = ColorProvider(
LightColorScheme.onTertiaryContainer,
DarkColorScheme.onTertiaryContainer
),
error = ColorProvider(LightColorScheme.error, DarkColorScheme.error),
onError = ColorProvider(LightColorScheme.onError, DarkColorScheme.onError),
errorContainer = ColorProvider(LightColorScheme.errorContainer, DarkColorScheme.errorContainer),
onErrorContainer = ColorProvider(
LightColorScheme.onErrorContainer,
DarkColorScheme.onErrorContainer
),
surface = ColorProvider(LightColorScheme.surface, DarkColorScheme.surface),
onSurface = ColorProvider(LightColorScheme.onSurface, DarkColorScheme.onSurface),
inverseSurface = ColorProvider(LightColorScheme.inverseSurface, DarkColorScheme.inverseSurface),
inverseOnSurface = ColorProvider(
LightColorScheme.inverseOnSurface,
DarkColorScheme.inverseOnSurface
),
outline = ColorProvider(LightColorScheme.outline, DarkColorScheme.outline),
background = ColorProvider(LightColorScheme.background, DarkColorScheme.background),
onBackground = ColorProvider(LightColorScheme.onBackground, DarkColorScheme.onBackground),
surfaceVariant = ColorProvider(LightColorScheme.surfaceVariant, DarkColorScheme.surfaceVariant),
onSurfaceVariant = ColorProvider(
LightColorScheme.onSurfaceVariant,
DarkColorScheme.onSurfaceVariant
)
)

@Composable
fun KnightsGlanceTheme(
content: @Composable () -> Unit,
) {
GlanceTheme(
colors = WidgetColorProviers,
content = content
)
}
2 changes: 2 additions & 0 deletions feature/main/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ dependencies {
implementation(projects.feature.session)
implementation(projects.feature.bookmark)

implementation(projects.widget)

implementation(libs.androidx.core.ktx)
implementation(libs.androidx.appcompat)
implementation(libs.androidx.activity.compose)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,45 @@ import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.core.view.WindowCompat
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.droidknights.app2023.DroidKnightsWidget.Companion.KEY_SESSION_ID
import com.droidknights.app2023.core.designsystem.theme.KnightsTheme
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.MutableStateFlow

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
private val viewModel: MainViewModel by viewModels()
private val sessionIdFromWidget: MutableStateFlow<String?> = MutableStateFlow(null)

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

intent.getStringExtra(KEY_SESSION_ID)?.let {
sessionIdFromWidget.value = it
intent.removeExtra(KEY_SESSION_ID)
}

WindowCompat.setDecorFitsSystemWindows(window, false)

setContent {
val isDarkTheme by viewModel.isDarkTheme.collectAsStateWithLifecycle(false, this)

val navigator: MainNavigator = rememberMainNavigator()
val sessionId = sessionIdFromWidget.collectAsStateWithLifecycle().value

LaunchedEffect(sessionId) {
sessionId?.let {
navigator.navigateSessionDetail(it)
}
}

KnightsTheme(darkTheme = isDarkTheme) {
MainScreen(
navigator = navigator,
onChangeDarkTheme = { isDarkTheme -> viewModel.updateIsDarkTheme(isDarkTheme) }
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
package com.droidknights.app2023.feature.main

import android.app.Application
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.droidknights.app2023.core.data.repository.SettingsRepository
import com.droidknights.app2023.sendWidgetUpdateCommand
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import javax.inject.Inject

@HiltViewModel
class MainViewModel @Inject constructor(
private val application: Application,
private val settingsRepository: SettingsRepository,
) : ViewModel() {
val isDarkTheme = settingsRepository.getIsDarkTheme()

init {
sendWidgetUpdateCommand(application)
}

fun updateIsDarkTheme(isDarkTheme: Boolean) = viewModelScope.launch {
settingsRepository.updateIsDarkTheme(isDarkTheme)
}
Expand Down
1 change: 1 addition & 0 deletions feature/session/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ android {

dependencies {
implementation(libs.kotlinx.immutable)
implementation(projects.widget)
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package com.droidknights.app2023.feature.session

import android.app.Application
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.droidknights.app2023.core.domain.usecase.BookmarkSessionUseCase
import com.droidknights.app2023.core.domain.usecase.GetBookmarkedSessionIdsUseCase
import com.droidknights.app2023.core.domain.usecase.GetSessionUseCase
import com.droidknights.app2023.sendWidgetUpdateCommand
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
Expand All @@ -14,6 +16,7 @@ import javax.inject.Inject

@HiltViewModel
class SessionDetailViewModel @Inject constructor(
private val application: Application,
private val getSessionUseCase: GetSessionUseCase,
private val getBookmarkedSessionIdsUseCase: GetBookmarkedSessionIdsUseCase,
private val bookmarkSessionUseCase: BookmarkSessionUseCase,
Expand Down Expand Up @@ -57,6 +60,7 @@ class SessionDetailViewModel @Inject constructor(
viewModelScope.launch {
val bookmark = uiState.bookmarked
bookmarkSessionUseCase(uiState.session.id, !bookmark)
sendWidgetUpdateCommand(application)
_sessionUiEffect.value = SessionDetailEffect.ShowToastForBookmarkState(!bookmark)
}
}
Expand Down
7 changes: 6 additions & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ androidxDatastore = "1.0.0"
ossLicenses = "17.0.1"
ossLicensesPlugin = "0.10.6"

androidxGlance = "1.0.0-beta01"

[libraries]
android-gradlePlugin = { group = "com.android.tools.build", name = "gradle", version.ref = "androidGradlePlugin" }
android-desugarJdkLibs = { group = "com.android.tools", name = "desugar_jdk_libs", version.ref = "androidDesugarJdkLibs" }
Expand Down Expand Up @@ -83,7 +85,7 @@ landscapist-coil = { group = "com.github.skydoves", name = "landscapist-coil" }
landscapist-placeholder = { group = "com.github.skydoves", name = "landscapist-placeholder" }
landscapist-animation = { group = "com.github.skydoves", name = "landscapist-animation" }

compose-shimmer = { group = "com.valentinilk.shimmer", name = "compose-shimmer", version.ref = "composeShimmer"}
compose-shimmer = { group = "com.valentinilk.shimmer", name = "compose-shimmer", version.ref = "composeShimmer" }

junit4 = { group = "junit", name = "junit", version.ref = "junit4" }
junit-vintage-engine = { group = "org.junit.vintage", name = "junit-vintage-engine", version.ref = "junitVintageEngine" }
Expand All @@ -110,6 +112,9 @@ verify-detektPlugin = { module = "io.gitlab.arturbosch.detekt:detekt-gradle-plug
androidx-datastore = { group = "androidx.datastore", name = "datastore-preferences", version.ref = "androidxDatastore" }
oss-licenses-plugin = { group = "com.google.android.gms", name = "oss-licenses-plugin", version.ref = "ossLicensesPlugin" }

androidx-glance = { group = "androidx.glance", name = "glance", version.ref = "androidxGlance" }
androidx-glance-appwidget = { group = "androidx.glance", name = "glance-appwidget", version.ref = "androidxGlance" }

[bundles]

[plugins]
Expand Down
2 changes: 2 additions & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,6 @@ include(
":feature:setting",
":feature:contributor",
":feature:bookmark",

":widget"
)
1 change: 1 addition & 0 deletions widget/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
18 changes: 18 additions & 0 deletions widget/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
plugins {
id("droidknights.android.library")
id("droidknights.android.compose")
}

android {
namespace = "com.droidknights.app2023.widget"
}

dependencies {
implementation(libs.androidx.glance)
implementation(libs.androidx.glance.appwidget)

implementation(projects.core.designsystem)

implementation(projects.core.domain)
implementation(project(mapOf("path" to ":core:model")))
}
24 changes: 24 additions & 0 deletions widget/src/main/kotlin/com/droidknights/app2023/Action.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.droidknights.app2023

import android.content.Context
import android.content.Intent
import androidx.glance.action.Action
import androidx.glance.appwidget.action.actionStartActivity

fun actionStartActivityWithSessionId(context: Context, sessionId: String): Action =
actionStartActivity(
Intent(
context.packageManager.getLaunchIntentForPackage(
context.packageName
)
).putExtra(DroidKnightsWidget.KEY_SESSION_ID, sessionId)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
)

fun actionLaunchIntentForPackage(context: Context): Action = actionStartActivity(
Intent(
context.packageManager.getLaunchIntentForPackage(
context.packageName
)
)
)
16 changes: 16 additions & 0 deletions widget/src/main/kotlin/com/droidknights/app2023/Command.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.droidknights.app2023

import android.appwidget.AppWidgetManager
import android.content.Context
import android.content.Intent

fun sendWidgetUpdateCommand(context: Context) {
context.sendBroadcast(
Intent(
context,
DroidKnightsWidgetReceiver::class.java
).setAction(
AppWidgetManager.ACTION_APPWIDGET_UPDATE
)
)
}
Loading

0 comments on commit eec8ed8

Please sign in to comment.