Skip to content

Commit

Permalink
feat(androidApp): create widget to see next talks from the agenda.
Browse files Browse the repository at this point in the history
  • Loading branch information
GerardPaligot committed May 3, 2024
1 parent f207aa2 commit 46dad40
Show file tree
Hide file tree
Showing 30 changed files with 677 additions and 56 deletions.
3 changes: 3 additions & 0 deletions androidApp/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ android {
dependencies {
implementation(projects.themeM3.main.main)
implementation(projects.themeM3.main.mainDi)
implementation(projects.themeM3.navigation)
implementation(projects.widgets.widgetsFeature)
implementation(projects.shared.core)
implementation(projects.shared.coreDi)
implementation(projects.shared.resources)
Expand All @@ -88,6 +90,7 @@ dependencies {
implementation(libs.androidx.navigation.compose)
implementation(libs.androidx.profile)
implementation(libs.androidx.workmanager.ktx)
implementation(libs.bundles.androidx.glance)

implementation(libs.koin.core)
implementation(libs.koin.android)
Expand Down
10 changes: 10 additions & 0 deletions androidApp/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,16 @@
</intent-filter>
</receiver>

<receiver android:name=".widgets.AppWidgetReceiver"
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/agenda_widget_info" />
</receiver>

<provider
android:name="androidx.core.content.FileProvider"
android:authorities="org.gdglille.devfest.android.provider"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package org.gdglille.devfest.android.widgets

import android.content.Context
import android.content.Intent
import androidx.core.net.toUri
import androidx.datastore.preferences.core.MutablePreferences
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.glance.GlanceId
import androidx.glance.GlanceTheme
import androidx.glance.appwidget.GlanceAppWidget
import androidx.glance.appwidget.SizeMode
import androidx.glance.appwidget.action.actionStartActivity
import androidx.glance.appwidget.provideContent
import androidx.glance.currentState
import androidx.glance.state.GlanceStateDefinition
import androidx.glance.state.PreferencesGlanceStateDefinition
import kotlinx.datetime.Clock
import kotlinx.datetime.TimeZone
import kotlinx.datetime.toLocalDateTime
import org.gdglille.devfest.android.R
import org.gdglille.devfest.android.widgets.feature.SessionsWidget
import org.gdglille.devfest.repositories.AgendaRepository
import org.gdglille.devfest.repositories.EventRepository
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject

class AgendaAppWidget : GlanceAppWidget(), KoinComponent {
private val agendaRepository: AgendaRepository by inject()
private val eventRepository: EventRepository by inject()

override val sizeMode = SizeMode.Exact

override val stateDefinition: GlanceStateDefinition<*>
get() = PreferencesGlanceStateDefinition

override suspend fun provideGlance(context: Context, id: GlanceId) {
val date = Clock.System.now().toLocalDateTime(TimeZone.currentSystemDefault()).toString()
provideContent {
GlanceTheme {
val prefs = currentState<Preferences>()
SessionsWidget(
eventRepository = eventRepository,
agendaRepository = agendaRepository,
date = prefs.lastUpdate ?: date,
iconId = R.drawable.ic_launcher_foreground,
onUpdate = {
prefs.toMutablePreferences().apply {
this.lastUpdate =
Clock.System.now().toLocalDateTime(TimeZone.currentSystemDefault())
.toString()
}
update(context, id)
},
onItemClick = {
actionStartActivity(
intent = Intent(
Intent.ACTION_VIEW,
"c4h://event/schedules/$it".toUri()
)
)
}
)
}
}
}
}

var MutablePreferences.lastUpdate: String?
set(value) {
this[stringPreferencesKey("last_update")] = value ?: ""
}
get() = this[stringPreferencesKey("last_update")]

val Preferences.lastUpdate: String?
get() = this[stringPreferencesKey("last_update")]
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.gdglille.devfest.android.widgets

import androidx.glance.appwidget.GlanceAppWidget
import androidx.glance.appwidget.GlanceAppWidgetReceiver

class AppWidgetReceiver : GlanceAppWidgetReceiver() {
override val glanceAppWidget: GlanceAppWidget
get() = AgendaAppWidget()
}
13 changes: 13 additions & 0 deletions androidApp/src/main/res/xml/agenda_widget_info.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:initialLayout="@layout/glance_default_loading_layout"
android:minWidth="350dp"
android:minHeight="100dp"
android:targetCellWidth="5"
android:targetCellHeight="2"
android:minResizeWidth="350dp"
android:minResizeHeight="100dp"
android:maxResizeWidth="350dp"
android:maxResizeHeight="300dp"
android:resizeMode="vertical"
android:updatePeriodMillis="1800000" />
10 changes: 9 additions & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ androidx-compose-compiler = "1.5.8"
androidx-compose-adaptive = "1.0.0-alpha10"
androidx-compose-adaptive-navigation = "1.0.0-alpha05"
androidx-espresso = "3.5.1"
androidx-glance = "1.1.0-beta02"
androidx-glance-preview = "1.0.0-alpha06"
androidx-junit = "1.1.5"
androidx-lifecycle = "2.7.0"
androidx-navigation-compose = "2.7.6"
Expand Down Expand Up @@ -72,6 +74,8 @@ androidx-compose-material3-adaptive-navigation = { group = "androidx.compose.mat
androidx-compose-material3-adaptive-navigation-suite = { group = "androidx.compose.material3", name = "material3-adaptive-navigation-suite", version.ref = "androidx-compose-adaptive-navigation" }
androidx-compose-runtime-livedata = { group = "androidx.compose.runtime", name = "runtime-livedata" }
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "androidx-espresso" }
androidx-glance-appwidget = { group = "androidx.glance", name = "glance-appwidget", version.ref = "androidx-glance" }
androidx-glance-material3 = { group = "androidx.glance", name = "glance-material3", version.ref = "androidx-glance" }
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidx-junit" }
androidx-lifecycle-viewmodel-ktx = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "androidx-lifecycle" }
androidx-lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose" }
Expand Down Expand Up @@ -166,4 +170,8 @@ androidx-compose-adaptive = [
"androidx-compose-material3-adaptive",
"androidx-compose-material3-adaptive-layout",
"androidx-compose-material3-adaptive-navigation"
]
]
androidx-glance = [
"androidx-glance-appwidget",
"androidx-glance-material3"
]
3 changes: 3 additions & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,7 @@ include(":theme-m3:style:schedules")
include(":theme-m3:style:speakers")
include(":theme-m3:style:theme")
include(":ui-camera")
include(":widgets:widgets-feature")
include(":widgets:widgets-screens")
include(":widgets:widgets-ui")
include(":baselineprofile")
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@ package org.gdglille.devfest.database
import app.cash.sqldelight.coroutines.asFlow
import app.cash.sqldelight.coroutines.mapToList
import app.cash.sqldelight.coroutines.mapToOne
import app.cash.sqldelight.coroutines.mapToOneOrNull
import com.russhwolf.settings.ExperimentalSettingsApi
import com.russhwolf.settings.ObservableSettings
import com.russhwolf.settings.coroutines.getStringOrNullFlow
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.combineTransform
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
import org.gdglille.devfest.db.Conferences4HallDatabase
import org.gdglille.devfest.exceptions.EventSavedException
Expand Down Expand Up @@ -98,9 +101,12 @@ class EventDao(

fun deleteEventId() = settings.remove("EVENT_ID")

fun fetchEventId(): String =
fun getEventId(): String =
settings.getStringOrNull("EVENT_ID") ?: throw EventSavedException()

fun fetchEventId(): Flow<String> =
settings.getStringOrNullFlow("EVENT_ID").map { it ?: throw EventSavedException() }

fun fetchEvent(eventId: String): Flow<EventUi> = db.transactionWithResult {
return@transactionWithResult db.eventQueries.selectEvent(eventId, eventMapper).asFlow()
.combineTransform(
Expand All @@ -111,6 +117,13 @@ class EventDao(
}
}

fun fetchCurrentEvent(): Flow<EventInfoUi?> {
val eventId = settings.getStringOrNull("EVENT_ID") ?: return flow { emit(null) }
return db.eventQueries.selectEvent(eventId, eventMapper)
.asFlow()
.mapToOneOrNull(dispatcher)
}

fun fetchQAndA(eventId: String): Flow<ImmutableList<QuestionAndResponseUi>> = db.transactionWithResult {
return@transactionWithResult combine(
db.qAndAQueries.selectQAndA(eventId).asFlow().mapToList(dispatcher),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@ import cafe.adriel.lyricist.Lyricist
import com.russhwolf.settings.ExperimentalSettingsApi
import com.russhwolf.settings.ObservableSettings
import com.russhwolf.settings.coroutines.getBooleanFlow
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.ImmutableMap
import kotlinx.collections.immutable.toImmutableList
import kotlinx.collections.immutable.toImmutableMap
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
import kotlinx.datetime.toLocalDateTime
import org.gdglille.devfest.android.shared.resources.Strings
import org.gdglille.devfest.database.mappers.convertCategoryUi
import org.gdglille.devfest.database.mappers.convertFormatUi
Expand All @@ -25,6 +28,7 @@ import org.gdglille.devfest.models.ui.AgendaUi
import org.gdglille.devfest.models.ui.CategoryUi
import org.gdglille.devfest.models.ui.FiltersUi
import org.gdglille.devfest.models.ui.FormatUi
import org.gdglille.devfest.models.ui.TalkItemUi
import kotlin.coroutines.CoroutineContext

@FlowPreview
Expand Down Expand Up @@ -105,6 +109,31 @@ class ScheduleDao(
}
)

fun fetchNextTalks(eventId: String, date: String): Flow<ImmutableList<TalkItemUi>> {
val dateTime = date.toLocalDateTime()
return db.sessionQueries
.selectSessions(eventId)
.asFlow()
.mapToList(dispatcher)
.map { sessions ->
val nextAgenda = sessions
.filter { dateTime < it.start_time.toLocalDateTime() }
.map {
val speakers = if (it.talk_id != null) {
db.sessionQueries
.selectSpeakersByTalkId(eventId, it.talk_id)
.executeAsList()
} else {
emptyList()
}
it.convertTalkItemUi(speakers = speakers, strings = lyricist.strings)
}
.groupBy { it.startTime }
val toImmutableList = nextAgenda.values.first().toImmutableList()
return@map toImmutableList
}
}

fun fetchFilters(eventId: String): Flow<FiltersUi> {
return combine(
db.categoryQueries
Expand Down
Loading

0 comments on commit 46dad40

Please sign in to comment.