Annual refactor

This commit is contained in:
MM20
2023-10-28 14:20:16 +02:00
parent 4f64652aae
commit 957358c79a
110 changed files with 1786 additions and 1663 deletions
@@ -1,4 +1,4 @@
package de.mm20.launcher2.search.data
package de.mm20.launcher2.calendar
import android.content.ContentUris
import android.content.Context
@@ -6,50 +6,33 @@ import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.provider.CalendarContract
import de.mm20.launcher2.icons.ColorLayer
import de.mm20.launcher2.icons.StaticLauncherIcon
import de.mm20.launcher2.icons.TextLayer
import de.mm20.launcher2.ktx.tryStartActivity
import de.mm20.launcher2.search.SavableSearchable
import de.mm20.launcher2.search.CalendarEvent
import de.mm20.launcher2.search.SearchableSerializer
import java.net.URLEncoder
import java.text.SimpleDateFormat
data class CalendarEvent(
internal data class AndroidCalendarEvent(
override val label: String,
val id: Long,
val color: Int,
val startTime: Long,
val endTime: Long,
val allDay: Boolean,
val location: String,
val attendees: List<String>,
val description: String,
override val color: Int,
override val startTime: Long,
override val endTime: Long,
override val allDay: Boolean,
override val location: String?,
override val attendees: List<String>,
override val description: String?,
val calendar: Long,
override val labelOverride: String? = null,
) : SavableSearchable {
) : CalendarEvent {
override val domain: String = Domain
override val key: String
get() = "$domain://$id"
override val preferDetailsOverLaunch: Boolean = true
override fun overrideLabel(label: String): CalendarEvent {
override fun overrideLabel(label: String): AndroidCalendarEvent {
return this.copy(labelOverride = label)
}
override fun getPlaceholderIcon(context: Context): StaticLauncherIcon {
val df = SimpleDateFormat("dd")
return StaticLauncherIcon(
foregroundLayer = TextLayer(
text = df.format(startTime),
color = color
),
backgroundLayer = ColorLayer(color)
)
}
private fun getLaunchIntent(): Intent {
val uri = ContentUris.withAppendedId(CalendarContract.Events.CONTENT_URI, id)
return Intent(Intent.ACTION_VIEW).setData(uri).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
@@ -59,7 +42,8 @@ data class CalendarEvent(
return context.tryStartActivity(getLaunchIntent(), options)
}
fun openLocation(context: Context) {
override fun openLocation(context: Context) {
if (location == null) return
context.tryStartActivity(
Intent(Intent.ACTION_VIEW)
.setData(
@@ -76,6 +60,10 @@ data class CalendarEvent(
)
}
override fun getSerializer(): SearchableSerializer {
return CalendarEventSerializer()
}
companion object {
const val Domain = "calendar"
}
@@ -6,8 +6,8 @@ import android.provider.CalendarContract
import androidx.core.database.getStringOrNull
import de.mm20.launcher2.permissions.PermissionGroup
import de.mm20.launcher2.permissions.PermissionsManager
import de.mm20.launcher2.search.data.CalendarEvent
import de.mm20.launcher2.search.data.UserCalendar
import de.mm20.launcher2.search.CalendarEvent
import de.mm20.launcher2.search.SearchableRepository
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
@@ -18,16 +18,16 @@ import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.withContext
import org.koin.core.component.KoinComponent
import java.util.Calendar
interface CalendarRepository {
fun search(query: String): Flow<ImmutableList<CalendarEvent>>
fun getUpcomingEvents(
excludeCalendars: List<Long>,
excludeAllDayEvents: Boolean
): Flow<List<CalendarEvent>>
interface CalendarRepository: SearchableRepository<CalendarEvent> {
fun findMany(
from: Long = System.currentTimeMillis(),
to: Long = from + 14 * 24 * 60 * 60 * 1000L,
excludeCalendars: List<Long> = emptyList(),
excludeAllDayEvents: Boolean = false,
limit: Int = 999,
): Flow<ImmutableList<CalendarEvent>>
suspend fun getCalendars(): List<UserCalendar>
}
@@ -60,16 +60,43 @@ internal class CalendarRepositoryImpl(
}
override fun findMany(
from: Long,
to: Long,
excludeCalendars: List<Long>,
excludeAllDayEvents: Boolean,
limit: Int,
) = channelFlow<ImmutableList<CalendarEvent>> {
val hasPermission = permissionsManager.hasPermission(PermissionGroup.Calendar)
hasPermission.collectLatest {
if (it) {
val events = withContext(Dispatchers.IO) {
queryCalendarEvents(
query = "",
intervalStart = from,
intervalEnd = to,
limit = limit,
excludeAllDayEvents = excludeAllDayEvents,
excludeCalendars = excludeCalendars
)
}
send(events.toImmutableList())
} else {
send(persistentListOf())
}
}
}
private suspend fun queryCalendarEvents(
query: String,
query: String?,
intervalStart: Long,
intervalEnd: Long,
limit: Int = 10,
excludeAllDayEvents: Boolean = false,
excludeCalendars: List<Long> = emptyList(),
): List<CalendarEvent> {
): List<AndroidCalendarEvent> {
val results = withContext(Dispatchers.IO) {
val results = mutableListOf<CalendarEvent>()
val results = mutableListOf<AndroidCalendarEvent>()
val builder = CalendarContract.Instances.CONTENT_URI.buildUpon()
ContentUris.appendId(builder, intervalStart)
ContentUris.appendId(builder, intervalEnd)
@@ -86,10 +113,10 @@ internal class CalendarRepositoryImpl(
CalendarContract.Instances.DESCRIPTION
)
val selection = mutableListOf<String>()
if (query.isNotEmpty()) selection.add("${CalendarContract.Instances.TITLE} LIKE ?")
if (query != null) selection.add("${CalendarContract.Instances.TITLE} LIKE ?")
if (excludeCalendars.isNotEmpty()) selection.add("${CalendarContract.Instances.CALENDAR_ID} NOT IN (${excludeCalendars.joinToString()})")
if (excludeAllDayEvents) selection.add("${CalendarContract.Instances.ALL_DAY} = 0")
val selArgs = if (query.isBlank()) null else arrayOf("%$query%")
val selArgs = if (query != null) null else arrayOf("%$query%")
val sort =
"${CalendarContract.Instances.BEGIN} ASC" + if (limit > -1) " LIMIT $limit" else ""
val cursor = context.contentResolver.query(
@@ -128,7 +155,7 @@ internal class CalendarRepositoryImpl(
} else {
0
}
val event = CalendarEvent(
val event = AndroidCalendarEvent(
label = cursor.getStringOrNull(1) ?: "",
id = cursor.getLong(0),
color = cursor.getInt(5),
@@ -150,32 +177,6 @@ internal class CalendarRepositoryImpl(
return results
}
override fun getUpcomingEvents(
excludeCalendars: List<Long>,
excludeAllDayEvents: Boolean,
): Flow<List<CalendarEvent>> = channelFlow {
val hasPermission = permissionsManager.hasPermission(PermissionGroup.Calendar)
hasPermission.collectLatest {
if (it) {
val now = System.currentTimeMillis()
val end = now + 14 * 24 * 60 * 60 * 1000L
val events = withContext(Dispatchers.IO) {
queryCalendarEvents(
query = "",
intervalStart = now,
intervalEnd = end,
limit = 700,
excludeAllDayEvents = excludeAllDayEvents,
excludeCalendars = excludeCalendars
)
}
send(events)
} else {
send(emptyList())
}
}
}
override suspend fun getCalendars(): List<UserCalendar> {
if (!permissionsManager.checkPermissionOnce(PermissionGroup.Calendar)) return emptyList()
return withContext(Dispatchers.IO) {
@@ -10,13 +10,12 @@ import androidx.core.database.getStringOrNull
import de.mm20.launcher2.search.SavableSearchable
import de.mm20.launcher2.search.SearchableDeserializer
import de.mm20.launcher2.search.SearchableSerializer
import de.mm20.launcher2.search.data.CalendarEvent
import org.json.JSONObject
import java.util.*
class CalendarEventSerializer: SearchableSerializer {
override fun serialize(searchable: SavableSearchable): String {
searchable as CalendarEvent
searchable as AndroidCalendarEvent
val json = JSONObject()
json.put("id", searchable.id)
return json.toString()
@@ -27,7 +26,7 @@ class CalendarEventSerializer: SearchableSerializer {
}
class CalendarEventDeserializer(val context: Context): SearchableDeserializer {
override fun deserialize(serialized: String): SavableSearchable? {
override suspend fun deserialize(serialized: String): SavableSearchable? {
if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_CALENDAR) != PackageManager.PERMISSION_GRANTED) return null
val json = JSONObject(serialized)
val id = json.getLong("id")
@@ -86,7 +85,7 @@ class CalendarEventDeserializer(val context: Context): SearchableDeserializer {
} else {
0
}
return CalendarEvent(
return AndroidCalendarEvent(
label = title,
id = id,
color = color,
@@ -1,8 +1,14 @@
package de.mm20.launcher2.calendar
import de.mm20.launcher2.search.CalendarEvent
import de.mm20.launcher2.search.SearchableDeserializer
import de.mm20.launcher2.search.SearchableRepository
import org.koin.android.ext.koin.androidContext
import org.koin.core.qualifier.named
import org.koin.dsl.module
val calendarModule = module {
single<CalendarRepository> { CalendarRepositoryImpl(androidContext(), get()) }
factory<SearchableRepository<CalendarEvent>>(named<CalendarEvent>()) { CalendarRepositoryImpl(androidContext(), get()) }
factory<CalendarRepository> { CalendarRepositoryImpl(androidContext(), get()) }
factory<SearchableDeserializer>(named(AndroidCalendarEvent.Domain)) { CalendarEventDeserializer(androidContext()) }
}