diff --git a/app/build.gradle.kts b/app/build.gradle.kts index d2eed970..a73000b7 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -30,4 +30,6 @@ dependencies { implementation(projects.feature.home) implementation(projects.core.designsystem) + + implementation(projects.widget) } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 243ce8bd..b5a9f67c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -16,6 +16,17 @@ android:theme="@style/Theme.DroidKnights2023" tools:targetApi="31"> + + + + + + + diff --git a/core/designsystem/build.gradle.kts b/core/designsystem/build.gradle.kts index a6829feb..817da814 100644 --- a/core/designsystem/build.gradle.kts +++ b/core/designsystem/build.gradle.kts @@ -13,4 +13,6 @@ dependencies { implementation(libs.landscapist.bom) implementation(libs.landscapist.coil) implementation(libs.landscapist.placeholder) + + implementation(libs.androidx.glance) } diff --git a/core/designsystem/src/main/java/com/droidknights/app2023/core/designsystem/theme/Theme.kt b/core/designsystem/src/main/java/com/droidknights/app2023/core/designsystem/theme/Theme.kt index 1298f9b6..66a10a88 100644 --- a/core/designsystem/src/main/java/com/droidknights/app2023/core/designsystem/theme/Theme.kt +++ b/core/designsystem/src/main/java/com/droidknights/app2023/core/designsystem/theme/Theme.kt @@ -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, @@ -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 } } @@ -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 + ) +} diff --git a/feature/main/build.gradle.kts b/feature/main/build.gradle.kts index c9b47813..72d66d50 100644 --- a/feature/main/build.gradle.kts +++ b/feature/main/build.gradle.kts @@ -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) diff --git a/feature/main/src/main/java/com/droidknights/app2023/feature/main/MainActivity.kt b/feature/main/src/main/java/com/droidknights/app2023/feature/main/MainActivity.kt index 37ed9593..63a1ea10 100644 --- a/feature/main/src/main/java/com/droidknights/app2023/feature/main/MainActivity.kt +++ b/feature/main/src/main/java/com/droidknights/app2023/feature/main/MainActivity.kt @@ -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 = 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) } ) } diff --git a/feature/main/src/main/java/com/droidknights/app2023/feature/main/MainViewModel.kt b/feature/main/src/main/java/com/droidknights/app2023/feature/main/MainViewModel.kt index edb0208d..494c3fd4 100644 --- a/feature/main/src/main/java/com/droidknights/app2023/feature/main/MainViewModel.kt +++ b/feature/main/src/main/java/com/droidknights/app2023/feature/main/MainViewModel.kt @@ -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) } diff --git a/feature/session/build.gradle.kts b/feature/session/build.gradle.kts index 0d8627a0..047f488f 100644 --- a/feature/session/build.gradle.kts +++ b/feature/session/build.gradle.kts @@ -8,4 +8,5 @@ android { dependencies { implementation(libs.kotlinx.immutable) + implementation(projects.widget) } diff --git a/feature/session/src/main/java/com/droidknights/app2023/feature/session/SessionDetailScreen.kt b/feature/session/src/main/java/com/droidknights/app2023/feature/session/SessionDetailScreen.kt index 0f0155e8..202c1683 100644 --- a/feature/session/src/main/java/com/droidknights/app2023/feature/session/SessionDetailScreen.kt +++ b/feature/session/src/main/java/com/droidknights/app2023/feature/session/SessionDetailScreen.kt @@ -1,5 +1,6 @@ package com.droidknights.app2023.feature.session +import android.util.Log import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -29,6 +30,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview @@ -50,6 +52,7 @@ import com.droidknights.app2023.core.model.Room import com.droidknights.app2023.core.model.Session import com.droidknights.app2023.core.model.Speaker import com.droidknights.app2023.core.model.Tag +import com.droidknights.app2023.sendWidgetUpdateCommand import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.toPersistentList import kotlinx.coroutines.delay @@ -64,6 +67,14 @@ internal fun SessionDetailScreen( val scrollState = rememberScrollState() val sessionUiState by viewModel.sessionUiState.collectAsStateWithLifecycle() val effect by viewModel.sessionUiEffect.collectAsStateWithLifecycle() + + val context = LocalContext.current + + LaunchedEffect(effect) { + if(effect is SessionDetailEffect.ShowToastForBookmarkState) { + sendWidgetUpdateCommand(context) + } + } Column( modifier = Modifier .fillMaxSize() diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 55a6a3ee..b8a23c27 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -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" } @@ -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" } @@ -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] diff --git a/settings.gradle.kts b/settings.gradle.kts index 232f5f93..ead104c5 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -34,4 +34,6 @@ include( ":feature:setting", ":feature:contributor", ":feature:bookmark", + + ":widget" ) diff --git a/widget/.gitignore b/widget/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/widget/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/widget/build.gradle.kts b/widget/build.gradle.kts new file mode 100644 index 00000000..897f9247 --- /dev/null +++ b/widget/build.gradle.kts @@ -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"))) +} \ No newline at end of file diff --git a/widget/src/main/kotlin/com/droidknights/app2023/Action.kt b/widget/src/main/kotlin/com/droidknights/app2023/Action.kt new file mode 100644 index 00000000..98f87b2c --- /dev/null +++ b/widget/src/main/kotlin/com/droidknights/app2023/Action.kt @@ -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 + ) + ) +) \ No newline at end of file diff --git a/widget/src/main/kotlin/com/droidknights/app2023/Command.kt b/widget/src/main/kotlin/com/droidknights/app2023/Command.kt new file mode 100644 index 00000000..e53a4b98 --- /dev/null +++ b/widget/src/main/kotlin/com/droidknights/app2023/Command.kt @@ -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 + ) + ) +} \ No newline at end of file diff --git a/widget/src/main/kotlin/com/droidknights/app2023/DroidKnightsWidget.kt b/widget/src/main/kotlin/com/droidknights/app2023/DroidKnightsWidget.kt new file mode 100644 index 00000000..cb8d8a79 --- /dev/null +++ b/widget/src/main/kotlin/com/droidknights/app2023/DroidKnightsWidget.kt @@ -0,0 +1,75 @@ +package com.droidknights.app2023 + +import android.content.Context +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.unit.dp +import androidx.datastore.preferences.core.stringSetPreferencesKey +import androidx.glance.GlanceId +import androidx.glance.GlanceModifier +import androidx.glance.GlanceTheme +import androidx.glance.appwidget.GlanceAppWidget +import androidx.glance.appwidget.lazy.LazyColumn +import androidx.glance.appwidget.lazy.items +import androidx.glance.appwidget.provideContent +import androidx.glance.background +import androidx.glance.currentState +import androidx.glance.layout.Alignment +import androidx.glance.layout.Column +import androidx.glance.layout.Spacer +import androidx.glance.layout.fillMaxSize +import androidx.glance.layout.height +import androidx.glance.layout.padding +import com.droidknights.app2023.DroidKnightsWidgetReceiver.Companion.KEY_SESSION_IDS +import com.droidknights.app2023.core.designsystem.theme.KnightsGlanceTheme +import com.droidknights.app2023.core.model.Session +import com.droidknights.app2023.di.WidgetModule +import dagger.hilt.EntryPoints + +class DroidKnightsWidget : GlanceAppWidget() { + + companion object { + const val KEY_SESSION_ID = "KEY_SESSION_ID" + } + + override suspend fun provideGlance(context: Context, id: GlanceId) { + val widgetModule: WidgetModule = EntryPoints.get( + context.applicationContext, + WidgetModule::class.java + ) + + provideContent { + KnightsGlanceTheme { + val state = currentState(stringSetPreferencesKey(KEY_SESSION_IDS)) + var list: List by remember(state) { mutableStateOf(listOf()) } + + LaunchedEffect(state) { + list = arrayListOf().apply { + state?.forEach { + this.add(widgetModule.getSessionUseCase().invoke(it)) + } + } + } + + Column( + modifier = GlanceModifier + .fillMaxSize() + .padding(start = 16.dp, top = 16.dp, bottom = 16.dp) + .background(GlanceTheme.colors.surface), + horizontalAlignment = Alignment.CenterHorizontally + ) { + WidgetTitle() + Spacer(modifier = GlanceModifier.height(16.dp)) + LazyColumn { + items(list) { + WidgetSessionCard(it) + } + } + } + } + } + } +} \ No newline at end of file diff --git a/widget/src/main/kotlin/com/droidknights/app2023/DroidKnightsWidgetReceiver.kt b/widget/src/main/kotlin/com/droidknights/app2023/DroidKnightsWidgetReceiver.kt new file mode 100644 index 00000000..56138a57 --- /dev/null +++ b/widget/src/main/kotlin/com/droidknights/app2023/DroidKnightsWidgetReceiver.kt @@ -0,0 +1,76 @@ +package com.droidknights.app2023 + +import android.appwidget.AppWidgetManager +import android.content.Context +import android.content.Intent +import androidx.datastore.preferences.core.stringSetPreferencesKey +import androidx.glance.appwidget.GlanceAppWidget +import androidx.glance.appwidget.GlanceAppWidgetManager +import androidx.glance.appwidget.GlanceAppWidgetReceiver +import androidx.glance.appwidget.state.updateAppWidgetState +import androidx.glance.appwidget.updateAll +import androidx.glance.state.PreferencesGlanceStateDefinition +import com.droidknights.app2023.di.WidgetModule +import dagger.hilt.EntryPoints +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch + +@AndroidEntryPoint +class DroidKnightsWidgetReceiver : GlanceAppWidgetReceiver() { + + companion object { + const val KEY_SESSION_IDS = "SESSION_IDS" + } + + override val glanceAppWidget: GlanceAppWidget = DroidKnightsWidget() + + override fun onUpdate( + context: Context, + appWidgetManager: AppWidgetManager, + appWidgetIds: IntArray + ) { + getBookmarkedSessionAndUpdateWidget(context, glanceAppWidget) + super.onUpdate(context, appWidgetManager, appWidgetIds) + } + + override fun onReceive(context: Context, intent: Intent) { + getBookmarkedSessionAndUpdateWidget(context, glanceAppWidget) + super.onReceive(context, intent) + } +} + +private fun getBookmarkedSessionAndUpdateWidget( + context: Context, + glanceAppWidget: GlanceAppWidget +) { + val widgetModule: WidgetModule = EntryPoints.get( + context.applicationContext, + WidgetModule::class.java + ) + + CoroutineScope(Dispatchers.IO).launch { + val glanceIds = GlanceAppWidgetManager(context).getGlanceIds(DroidKnightsWidget::class.java) + widgetModule.getBookmarkedSessionsUseCase().invoke().collect { list -> + glanceIds.forEach { glanceId -> + updateAppWidgetState( + context = context, + definition = PreferencesGlanceStateDefinition, + glanceId = glanceId + ) { + val set: Set = mutableSetOf().apply { + list.forEach { session -> + this.add(session.id) + } + } + it.toMutablePreferences().apply { + this[stringSetPreferencesKey(DroidKnightsWidgetReceiver.KEY_SESSION_IDS)] = + set + } + } + } + glanceAppWidget.updateAll(context) + } + } +} \ No newline at end of file diff --git a/widget/src/main/kotlin/com/droidknights/app2023/WidgetSessionCard.kt b/widget/src/main/kotlin/com/droidknights/app2023/WidgetSessionCard.kt new file mode 100644 index 00000000..80c54c5c --- /dev/null +++ b/widget/src/main/kotlin/com/droidknights/app2023/WidgetSessionCard.kt @@ -0,0 +1,67 @@ +package com.droidknights.app2023 + +import androidx.compose.runtime.Composable +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.glance.GlanceModifier +import androidx.glance.GlanceTheme +import androidx.glance.LocalContext +import androidx.glance.action.clickable +import androidx.glance.appwidget.cornerRadius +import androidx.glance.background +import androidx.glance.layout.Box +import androidx.glance.layout.Column +import androidx.glance.layout.Row +import androidx.glance.layout.Spacer +import androidx.glance.layout.fillMaxWidth +import androidx.glance.layout.padding +import androidx.glance.layout.width +import androidx.glance.text.Text +import androidx.glance.text.TextDefaults +import com.droidknights.app2023.core.model.Session +import kotlinx.datetime.toJavaLocalDateTime + +@Composable +fun WidgetSessionCard(session: Session) { + val context = LocalContext.current + + Box(modifier = GlanceModifier.padding(bottom = 16.dp, end = 16.dp)) { + Column( + modifier = GlanceModifier.padding(16.dp).fillMaxWidth() + .cornerRadius(12.dp).background(GlanceTheme.colors.tertiaryContainer).clickable( + actionStartActivityWithSessionId(context, session.id) + ) + ) { + Text( + session.title, + style = TextDefaults.defaultTextStyle.copy( + fontSize = 16.sp, + color = GlanceTheme.colors.onTertiaryContainer + ) + ) + Row { + Text( + session.toTimeString(), + style = TextDefaults.defaultTextStyle.copy( + fontSize = 14.sp, + color = GlanceTheme.colors.onTertiaryContainer + ) + ) + Spacer(modifier = GlanceModifier.width(4.dp)) + Text( + // FIXME : 2명 이상 발표자 있는 case에 대해 정상 동작하도록 수정 필요 + session.speakers.first().name, + style = TextDefaults.defaultTextStyle.copy( + fontSize = 14.sp, + color = GlanceTheme.colors.onTertiaryContainer + ) + ) + } + } + } +} + +private fun Session.toTimeString(): String = + "${startTime.toJavaLocalDateTime().toLocalTime()}" + + " ~ " + + "${endTime.toJavaLocalDateTime().toLocalTime()}" \ No newline at end of file diff --git a/widget/src/main/kotlin/com/droidknights/app2023/WidgetTitle.kt b/widget/src/main/kotlin/com/droidknights/app2023/WidgetTitle.kt new file mode 100644 index 00000000..24ca2ad4 --- /dev/null +++ b/widget/src/main/kotlin/com/droidknights/app2023/WidgetTitle.kt @@ -0,0 +1,27 @@ +package com.droidknights.app2023 + +import androidx.compose.runtime.Composable +import androidx.compose.ui.unit.sp +import androidx.glance.GlanceModifier +import androidx.glance.GlanceTheme +import androidx.glance.LocalContext +import androidx.glance.action.clickable +import androidx.glance.text.Text +import androidx.glance.text.TextDefaults +import com.droidknights.app2023.widget.R + +@Composable +fun WidgetTitle() { + val context = LocalContext.current + + Text( + context.getString(R.string.widget_title), + style = TextDefaults.defaultTextStyle.copy( + fontSize = 24.sp, + color = GlanceTheme.colors.onSurface + ), + modifier = GlanceModifier.clickable( + actionLaunchIntentForPackage(context) + ) + ) +} \ No newline at end of file diff --git a/widget/src/main/kotlin/com/droidknights/app2023/di/WidgetModule.kt b/widget/src/main/kotlin/com/droidknights/app2023/di/WidgetModule.kt new file mode 100644 index 00000000..5a094033 --- /dev/null +++ b/widget/src/main/kotlin/com/droidknights/app2023/di/WidgetModule.kt @@ -0,0 +1,15 @@ +package com.droidknights.app2023.di + +import com.droidknights.app2023.core.domain.usecase.GetBookmarkedSessionsUseCase +import com.droidknights.app2023.core.domain.usecase.GetSessionUseCase +import dagger.hilt.EntryPoint +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent + +@EntryPoint +@InstallIn(SingletonComponent::class) +interface WidgetModule { + fun getBookmarkedSessionsUseCase(): GetBookmarkedSessionsUseCase + + fun getSessionUseCase(): GetSessionUseCase +} \ No newline at end of file diff --git a/widget/src/main/res/values/strings.xml b/widget/src/main/res/values/strings.xml new file mode 100644 index 00000000..723e9471 --- /dev/null +++ b/widget/src/main/res/values/strings.xml @@ -0,0 +1,4 @@ + + + Droid Knights 2023 + \ No newline at end of file diff --git a/widget/src/main/res/xml-v31/widget_info.xml b/widget/src/main/res/xml-v31/widget_info.xml new file mode 100644 index 00000000..702b8d42 --- /dev/null +++ b/widget/src/main/res/xml-v31/widget_info.xml @@ -0,0 +1,9 @@ + + \ No newline at end of file diff --git a/widget/src/main/res/xml/widget_info.xml b/widget/src/main/res/xml/widget_info.xml new file mode 100644 index 00000000..791111e2 --- /dev/null +++ b/widget/src/main/res/xml/widget_info.xml @@ -0,0 +1,9 @@ + + \ No newline at end of file