Migrate search settings

This commit is contained in:
MM20
2022-01-10 21:48:29 +01:00
parent d91fc525f8
commit 5834e7e8c4
22 changed files with 964 additions and 371 deletions
@@ -1,7 +1,13 @@
package de.mm20.launcher2.calendar
import android.content.ContentUris
import android.content.Context
import android.provider.CalendarContract
import androidx.core.database.getStringOrNull
import de.mm20.launcher2.hiddenitems.HiddenItemsRepository
import de.mm20.launcher2.permissions.PermissionGroup
import de.mm20.launcher2.permissions.PermissionsManager
import de.mm20.launcher2.preferences.LauncherDataStore
import de.mm20.launcher2.preferences.LauncherPreferences
import de.mm20.launcher2.search.data.CalendarEvent
import kotlinx.coroutines.Dispatchers
@@ -9,6 +15,10 @@ import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.channels.trySendBlocking
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.withContext
import org.koin.core.component.KoinComponent
import org.koin.core.component.get
import org.koin.core.component.inject
import java.util.*
interface CalendarRepository {
fun search(query: String): Flow<List<CalendarEvent>>
@@ -19,34 +29,131 @@ interface CalendarRepository {
class CalendarRepositoryImpl(
private val context: Context,
hiddenItemsRepository: HiddenItemsRepository
) : CalendarRepository {
) : CalendarRepository, KoinComponent {
private val dataStore: LauncherDataStore by inject()
private val permissionsManager: PermissionsManager by inject()
private val hiddenItems = hiddenItemsRepository.hiddenItemsKeys
override fun search(query: String): Flow<List<CalendarEvent>> = channelFlow {
if (query.isBlank()) {
if (query.isBlank() || query.length < 3) {
send(emptyList())
return@channelFlow
}
val events = withContext(Dispatchers.IO) {
val now = System.currentTimeMillis()
CalendarEvent.search(
context,
query,
intervalStart = now,
intervalEnd = now + 14 * 24 * 60 * 60 * 1000L,
)
}
hiddenItems.collectLatest { hiddenItems ->
val calendarResults = withContext(Dispatchers.IO) {
events.filter { !hiddenItems.contains(it.key) }
val hasPermission = permissionsManager.hasPermission(PermissionGroup.Calendar)
val searchCalendar = dataStore.data.map { it.calendarSearch.enabled }
combine(hasPermission, searchCalendar) { permission, search ->
permission && search
}.map {
if (it) {
val now = System.currentTimeMillis()
queryCalendarEvents(
query,
intervalStart = now,
intervalEnd = now + 14 * 24 * 60 * 60 * 1000L,
)
} else {
emptyList()
}
send(calendarResults)
}.flatMapLatest { events ->
hiddenItems.map { hidden ->
events.filter { !hidden.contains(it.key) }
}
}.collectLatest {
send(it)
}
}
private suspend fun queryCalendarEvents(
query: String,
intervalStart: Long,
intervalEnd: Long,
limit: Int = 10,
excludeAllDayEvents: Boolean = false,
excludeCalendars: List<Long> = emptyList(),
): List<CalendarEvent> {
val results = withContext(Dispatchers.IO) {
val results = mutableListOf<CalendarEvent>()
val builder = CalendarContract.Instances.CONTENT_URI.buildUpon()
ContentUris.appendId(builder, intervalStart)
ContentUris.appendId(builder, intervalEnd)
val uri = builder.build()
val projection = arrayOf(
CalendarContract.Instances.EVENT_ID,
CalendarContract.Instances.TITLE,
CalendarContract.Instances.BEGIN,
CalendarContract.Instances.END,
CalendarContract.Instances.ALL_DAY,
CalendarContract.Instances.DISPLAY_COLOR,
CalendarContract.Instances.EVENT_LOCATION,
CalendarContract.Instances.CALENDAR_ID,
CalendarContract.Instances.DESCRIPTION
)
val selection = mutableListOf<String>()
if (query.isNotEmpty()) 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 sort =
"${CalendarContract.Instances.BEGIN} ASC" + if (limit > -1) " LIMIT $limit" else ""
val cursor = context.contentResolver.query(
uri,
projection,
selection.joinToString(separator = " AND "),
selArgs,
sort
) ?: return@withContext mutableListOf()
val proj = arrayOf(
CalendarContract.Attendees.EVENT_ID,
CalendarContract.Attendees.ATTENDEE_NAME,
CalendarContract.Attendees.ATTENDEE_EMAIL
)
val s = "${CalendarContract.Attendees.ATTENDEE_NAME} COLLATE NOCASE ASC"
while (cursor.moveToNext()) {
val sel = "${CalendarContract.Attendees.EVENT_ID} = ${cursor.getLong(0)}"
val cur = context.contentResolver.query(
CalendarContract.Attendees.CONTENT_URI,
proj, sel, null, s
) ?: return@withContext mutableListOf()
val attendees = mutableListOf<String>()
while (cur.moveToNext()) {
attendees.add(cur.getString(1).takeUnless { it.isNullOrBlank() }
?: cur.getString(2))
}
cur.close()
val allday = cursor.getInt(4) > 0
val begin = cursor.getLong(2)
val tzOffset = if (allday) {
Calendar.getInstance().timeZone.getOffset(begin)
} else {
0
}
val event = CalendarEvent(
label = cursor.getString(1) ?: "",
id = cursor.getLong(0),
color = cursor.getInt(5),
startTime = begin - tzOffset,
endTime = cursor.getLong(3) - tzOffset - if (allday) 1 else 0,
allDay = allday,
location = cursor.getString(6) ?: "",
attendees = attendees,
description = cursor.getStringOrNull(8)
?: "",
calendar = cursor.getLong(7)
)
results.add(event)
}
cursor.close()
return@withContext results
}
return results
}
override fun getUpcomingEvents(): Flow<List<CalendarEvent>> = channelFlow {
val unselectedCalendars = callbackFlow {
val unregister =
@@ -70,23 +177,26 @@ class CalendarRepositoryImpl(
}
}
merge(unselectedCalendars, hideAlldayEvents, hiddenItems).collectLatest {
val now = System.currentTimeMillis()
val end = now + 14 * 24 * 60 * 60 * 1000L
val events = withContext(Dispatchers.IO) {
CalendarEvent.search(
context = context,
query = "",
intervalStart = now,
intervalEnd = end,
limit = 700,
hideAllDayEvents = LauncherPreferences.instance.calendarHideAllday,
unselectedCalendars = LauncherPreferences.instance.unselectedCalendars
).filter {
!hiddenItems.value.contains(it.key)
hideAlldayEvents.collectLatest { hideAllday ->
unselectedCalendars.collectLatest { unselected ->
hiddenItems.collectLatest { hidden ->
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 = hideAllday,
excludeCalendars = unselected
).filter {
!hiddenItems.value.contains(it.key)
}
}
send(events)
}
}
send(events)
}
}
@@ -80,99 +80,6 @@ class CalendarEvent(
}
companion object: KoinComponent {
fun search(
context: Context,
query: String,
intervalStart: Long,
intervalEnd: Long,
limit: Int = 10,
hideAllDayEvents: Boolean = false,
unselectedCalendars: List<Long> = emptyList(),
hiddenEvents: List<Long> = emptyList()
): List<CalendarEvent> {
val permissionsManager: PermissionsManager = get()
val results = mutableListOf<CalendarEvent>()
if (!query.isEmpty() && query.length < 3) return results
if (!LauncherPreferences.instance.searchCalendars) return listOf()
if (!permissionsManager.checkPermissionOnce(PermissionGroup.Calendar)) {
return emptyList()
}
val builder = CalendarContract.Instances.CONTENT_URI.buildUpon()
ContentUris.appendId(builder, intervalStart)
ContentUris.appendId(builder, intervalEnd)
val uri = builder.build()
val projection = arrayOf(
CalendarContract.Instances.EVENT_ID,
CalendarContract.Instances.TITLE,
CalendarContract.Instances.BEGIN,
CalendarContract.Instances.END,
CalendarContract.Instances.ALL_DAY,
CalendarContract.Instances.DISPLAY_COLOR,
CalendarContract.Instances.EVENT_LOCATION,
CalendarContract.Instances.CALENDAR_ID,
CalendarContract.Instances.DESCRIPTION
)
val selection = mutableListOf<String>()
if (query.isNotEmpty()) selection.add("${CalendarContract.Instances.TITLE} LIKE ?")
if (hiddenEvents.isNotEmpty()) selection.add("${CalendarContract.Instances.EVENT_ID} NOT IN (${hiddenEvents.joinToString()})")
if (unselectedCalendars.isNotEmpty()) selection.add("${CalendarContract.Instances.CALENDAR_ID} NOT IN (${unselectedCalendars.joinToString()})")
if (hideAllDayEvents) selection.add("${CalendarContract.Instances.ALL_DAY} = 0")
val selArgs = if (query.isBlank()) null else arrayOf("%$query%")
val sort =
"${CalendarContract.Instances.BEGIN} ASC" + if (limit > -1) " LIMIT $limit" else ""
val cursor = context.contentResolver.query(
uri,
projection,
selection.joinToString(separator = " AND "),
selArgs,
sort
)
?: return mutableListOf()
val proj = arrayOf(
CalendarContract.Attendees.EVENT_ID,
CalendarContract.Attendees.ATTENDEE_NAME,
CalendarContract.Attendees.ATTENDEE_EMAIL
)
val s = "${CalendarContract.Attendees.ATTENDEE_NAME} COLLATE NOCASE ASC"
while (cursor.moveToNext()) {
val sel = "${CalendarContract.Attendees.EVENT_ID} = ${cursor.getLong(0)}"
val cur = context.contentResolver.query(
CalendarContract.Attendees.CONTENT_URI,
proj, sel, null, s
) ?: return mutableListOf()
val attendees = mutableListOf<String>()
while (cur.moveToNext()) {
attendees.add(cur.getString(1).takeUnless { it.isNullOrBlank() }
?: cur.getString(2))
}
cur.close()
val allday = cursor.getInt(4) > 0
val begin = cursor.getLong(2)
val tzOffset = if (allday) {
Calendar.getInstance().timeZone.getOffset(begin)
} else {
0
}
val event = CalendarEvent(
label = cursor.getString(1) ?: "",
id = cursor.getLong(0),
color = cursor.getInt(5),
startTime = begin - tzOffset,
endTime = cursor.getLong(3) - tzOffset - if (allday) 1 else 0,
allDay = allday,
location = cursor.getString(6) ?: "",
attendees = attendees,
description = cursor.getStringOrNull(8)
?: "",
calendar = cursor.getLong(7)
)
results.add(event)
}
cursor.close()
return results
}
fun getCalendars(context: Context): List<UserCalendar> {
val calendars = mutableListOf<UserCalendar>()