Reorganize Searchable data types

This commit is contained in:
MM20
2022-10-12 20:36:39 +02:00
parent a01b0aa03d
commit bcda89c211
98 changed files with 817 additions and 738 deletions
-1
View File
@@ -44,7 +44,6 @@ dependencies {
implementation(libs.koin.android)
implementation(project(":search"))
implementation(project(":preferences"))
implementation(project(":base"))
implementation(project(":ktx"))
@@ -1,12 +1,13 @@
package de.mm20.launcher2.files
import android.content.Context
import android.provider.DocumentsContract
import android.provider.MediaStore
import androidx.core.database.getStringOrNull
import de.mm20.launcher2.ktx.jsonObjectOf
import de.mm20.launcher2.permissions.PermissionGroup
import de.mm20.launcher2.permissions.PermissionsManager
import de.mm20.launcher2.search.PinnableSearchable
import de.mm20.launcher2.search.Searchable
import de.mm20.launcher2.search.SearchableDeserializer
import de.mm20.launcher2.search.SearchableSerializer
import de.mm20.launcher2.search.data.*
@@ -15,7 +16,7 @@ import org.koin.core.component.KoinComponent
import org.koin.core.component.get
class LocalFileSerializer : SearchableSerializer {
override fun serialize(searchable: Searchable): String {
override fun serialize(searchable: PinnableSearchable): String {
searchable as LocalFile
return jsonObjectOf(
"id" to searchable.id
@@ -29,7 +30,7 @@ class LocalFileSerializer : SearchableSerializer {
class LocalFileDeserializer(
val context: Context
) : SearchableDeserializer, KoinComponent {
override fun deserialize(serialized: String): Searchable? {
override fun deserialize(serialized: String): PinnableSearchable? {
val permissionsManager: PermissionsManager = get()
if (!permissionsManager.checkPermissionOnce(
PermissionGroup.ExternalStorage
@@ -74,7 +75,7 @@ class LocalFileDeserializer(
}
class GDriveFileSerializer : SearchableSerializer {
override fun serialize(searchable: Searchable): String {
override fun serialize(searchable: PinnableSearchable): String {
searchable as GDriveFile
return jsonObjectOf(
"id" to searchable.fileId,
@@ -103,7 +104,7 @@ class GDriveFileSerializer : SearchableSerializer {
}
class GDriveFileDeserializer : SearchableDeserializer {
override fun deserialize(serialized: String): Searchable? {
override fun deserialize(serialized: String): PinnableSearchable {
val json = JSONObject(serialized)
val id = json.getString("id")
val label = json.getString("label")
@@ -134,7 +135,7 @@ class GDriveFileDeserializer : SearchableDeserializer {
}
class OneDriveFileSerializer : SearchableSerializer {
override fun serialize(searchable: Searchable): String {
override fun serialize(searchable: PinnableSearchable): String {
searchable as OneDriveFile
return jsonObjectOf(
"id" to searchable.fileId,
@@ -161,7 +162,7 @@ class OneDriveFileSerializer : SearchableSerializer {
}
class OneDriveFileDeserializer : SearchableDeserializer {
override fun deserialize(serialized: String): Searchable? {
override fun deserialize(serialized: String): PinnableSearchable {
val json = JSONObject(serialized)
val fileId = json.getString("id")
val label = json.getString("label")
@@ -189,10 +190,10 @@ class OneDriveFileDeserializer : SearchableDeserializer {
}
class NextcloudFileSerializer : SearchableSerializer {
override fun serialize(searchable: Searchable): String {
override fun serialize(searchable: PinnableSearchable): String {
searchable as NextcloudFile
return jsonObjectOf(
"id" to searchable.id,
"id" to searchable.fileId,
"label" to searchable.label,
"path" to searchable.path,
"mimeType" to searchable.mimeType,
@@ -216,7 +217,7 @@ class NextcloudFileSerializer : SearchableSerializer {
}
class NextcloudFileDeserializer : SearchableDeserializer {
override fun deserialize(serialized: String): Searchable? {
override fun deserialize(serialized: String): PinnableSearchable {
val json = JSONObject(serialized)
val id = json.getLong("id")
val label = json.getString("label")
@@ -242,10 +243,10 @@ class NextcloudFileDeserializer : SearchableDeserializer {
}
class OwncloudFileSerializer : SearchableSerializer {
override fun serialize(searchable: Searchable): String {
override fun serialize(searchable: PinnableSearchable): String {
searchable as OwncloudFile
return jsonObjectOf(
"id" to searchable.id,
"id" to searchable.fileId,
"label" to searchable.label,
"path" to searchable.path,
"mimeType" to searchable.mimeType,
@@ -269,7 +270,7 @@ class OwncloudFileSerializer : SearchableSerializer {
}
class OwncloudFileDeserializer : SearchableDeserializer {
override fun deserialize(serialized: String): Searchable? {
override fun deserialize(serialized: String): PinnableSearchable {
val json = JSONObject(serialized)
val id = json.getLong("id")
val label = json.getString("label")
@@ -6,15 +6,18 @@ import de.mm20.launcher2.nextcloud.NextcloudApiHelper
import de.mm20.launcher2.owncloud.OwncloudClient
import de.mm20.launcher2.permissions.PermissionsManager
import de.mm20.launcher2.preferences.LauncherDataStore
import de.mm20.launcher2.search.SearchableRepository
import de.mm20.launcher2.search.data.File
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
interface FileRepository {
fun search(query: String): Flow<List<File>>
interface FileRepository: SearchableRepository<File> {
fun deleteFile(file: File)
}
@@ -60,21 +63,21 @@ internal class FileRepositoryImpl(
}
}
override fun search(query: String): Flow<List<File>> = channelFlow {
override fun search(query: String): Flow<ImmutableList<File>> = channelFlow {
if (query.isBlank()) {
send(emptyList())
send(persistentListOf())
return@channelFlow
}
providers.collectLatest { providers ->
if (providers.isEmpty()) {
send(emptyList())
send(persistentListOf())
return@collectLatest
}
val results = mutableListOf<File>()
for (provider in providers) {
results.addAll(provider.search(query))
send(results.toList())
send(results.toImmutableList())
}
}
}
@@ -1,25 +1,29 @@
package de.mm20.launcher2.search.data
import android.content.Context
import android.graphics.Color
import androidx.core.content.ContextCompat
import de.mm20.launcher2.files.R
import de.mm20.launcher2.icons.ColorLayer
import de.mm20.launcher2.icons.StaticLauncherIcon
import de.mm20.launcher2.icons.TintedIconLayer
import de.mm20.launcher2.search.PinnableSearchable
import de.mm20.launcher2.search.Searchable
import java.util.*
abstract class File(
val id: Long,
val path: String,
val mimeType: String,
val size: Long,
val isDirectory: Boolean,
interface File : PinnableSearchable {
val path: String
val mimeType: String
val size: Long
val isDirectory: Boolean
val metaData: List<Pair<Int, String>>
) : Searchable() {
abstract val isStoredInCloud: Boolean
open val providerIconRes: Int? = null
val isStoredInCloud: Boolean
override val preferDetailsOverLaunch: Boolean
get() = false
open val providerIconRes: Int?
get() = null
override fun getPlaceholderIcon(context: Context): StaticLauncherIcon {
val (resId, bgColor) = when {
@@ -124,7 +128,8 @@ abstract class File(
return context.getString(resource)
}
open val isDeletable: Boolean = false
open suspend fun delete(context: Context) {}
val isDeletable: Boolean
get() = false
suspend fun delete(context: Context) {}
}
@@ -3,30 +3,47 @@ package de.mm20.launcher2.search.data
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import de.mm20.launcher2.files.R
import de.mm20.launcher2.ktx.tryStartActivity
class GDriveFile(
data class GDriveFile(
val fileId: String,
override val label: String,
path: String,
mimeType: String,
size: Long,
isDirectory: Boolean,
metaData: List<Pair<Int, String>>,
override val path: String,
override val mimeType: String,
override val size: Long,
override val isDirectory: Boolean,
override val metaData: List<Pair<Int, String>>,
val directoryColor: String?,
val viewUri: String
) : File(0, path, mimeType, size, isDirectory, metaData) {
val viewUri: String,
override val labelOverride: String? = null,
) : File {
override val key: String = "gdrive://$fileId"
override fun overrideLabel(label: String): GDriveFile {
return this.copy(labelOverride = label)
}
override val domain: String = Domain
override val key: String = "$domain://$fileId"
override val isStoredInCloud = true
override val providerIconRes = R.drawable.ic_badge_gdrive
override fun getLaunchIntent(context: Context): Intent? {
private fun getLaunchIntent(): Intent {
return Intent(Intent.ACTION_VIEW).apply {
data = Uri.parse(viewUri)
flags = Intent.FLAG_ACTIVITY_NEW_TASK
}
}
override fun launch(context: Context, options: Bundle?): Boolean {
return context.tryStartActivity(getLaunchIntent(), options)
}
companion object {
const val Domain = "gdrive"
}
}
@@ -9,6 +9,7 @@ import android.location.Geocoder
import android.media.MediaMetadataRetriever
import android.media.ThumbnailUtils
import android.net.Uri
import android.os.Bundle
import android.provider.MediaStore
import android.text.format.DateUtils
import android.util.Size
@@ -17,25 +18,34 @@ import androidx.exifinterface.media.ExifInterface
import de.mm20.launcher2.files.R
import de.mm20.launcher2.icons.*
import de.mm20.launcher2.ktx.formatToString
import de.mm20.launcher2.ktx.tryStartActivity
import de.mm20.launcher2.media.ThumbnailUtilsCompat
import de.mm20.launcher2.search.PinnableSearchable
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.koin.core.component.KoinComponent
import java.io.IOException
import java.io.File as JavaIOFile
open class LocalFile(
id: Long,
path: String,
mimeType: String,
size: Long,
isDirectory: Boolean,
metaData: List<Pair<Int, String>>
) : File(id, path, mimeType, size, isDirectory, metaData) {
data class LocalFile(
val id: Long,
override val path: String,
override val mimeType: String,
override val size: Long,
override val isDirectory: Boolean,
override val metaData: List<Pair<Int, String>>,
override val labelOverride: String? = null
) : File {
override val label = path.substringAfterLast('/')
override val key = "file://$path"
override fun overrideLabel(label: String): LocalFile {
return this.copy(labelOverride = label)
}
override val domain: String = Domain
override val key = "$domain://$path"
override val isStoredInCloud = false
@@ -148,7 +158,7 @@ open class LocalFile(
}
override fun getLaunchIntent(context: Context): Intent? {
private fun getLaunchIntent(context: Context): Intent {
val uri = if (isDirectory) {
Uri.parse(path)
} else {
@@ -162,6 +172,10 @@ open class LocalFile(
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
override fun launch(context: Context, options: Bundle?): Boolean {
return context.tryStartActivity(getLaunchIntent(context), options)
}
override val isDeletable: Boolean
get() {
val file = java.io.File(path)
@@ -186,6 +200,9 @@ open class LocalFile(
companion object {
const val Domain = "file"
internal fun getMimetypeByFileExtension(extension: String): String {
return when (extension) {
"apk" -> "application/vnd.android.package-archive"
@@ -4,34 +4,50 @@ import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Bundle
import de.mm20.launcher2.files.R
import de.mm20.launcher2.ktx.tryStartActivity
class NextcloudFile(
fileId: Long,
data class NextcloudFile(
val fileId: Long,
override val label: String,
path: String,
mimeType: String,
size: Long,
isDirectory: Boolean,
override val path: String,
override val mimeType: String,
override val size: Long,
override val isDirectory: Boolean,
val server: String,
metaData: List<Pair<Int, String>>
) : File(fileId, path, mimeType, size, isDirectory, metaData) {
override val key: String = "nextcloud://$server/$fileId"
override val metaData: List<Pair<Int, String>>,
override val labelOverride: String? = null,
) : File {
override fun overrideLabel(label: String): NextcloudFile {
return this.copy(labelOverride = label)
}
override val domain: String = Domain
override val key: String = "$domain://$server/$fileId"
override val isStoredInCloud: Boolean
get() = true
override val providerIconRes = R.drawable.ic_badge_nextcloud
override fun getLaunchIntent(context: Context): Intent? {
private fun getLaunchIntent(context: Context): Intent {
return Intent(Intent.ACTION_VIEW).apply {
data = Uri.parse("$server/f/$id")
data = Uri.parse("$server/f/$fileId")
flags = Intent.FLAG_ACTIVITY_NEW_TASK
`package` = getNextcloudAppPackage(context)
}
}
override fun launch(context: Context, options: Bundle?): Boolean {
return context.tryStartActivity(getLaunchIntent(context), options)
}
companion object {
const val Domain = "nextcloud"
private fun getNextcloudAppPackage(context: Context): String? {
val candidates = listOf("com.nextcloud.client", "com.nextcloud.android.beta")
@@ -3,29 +3,46 @@ package de.mm20.launcher2.search.data
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import de.mm20.launcher2.files.R
import de.mm20.launcher2.ktx.tryStartActivity
class OneDriveFile(
data class OneDriveFile(
val fileId: String,
override val label: String,
path: String,
mimeType: String,
size: Long,
isDirectory: Boolean,
metaData: List<Pair<Int, String>>,
val webUrl: String
) : File(0, path, mimeType, size, isDirectory, metaData) {
override val path: String,
override val mimeType: String,
override val size: Long,
override val isDirectory: Boolean,
override val metaData: List<Pair<Int, String>>,
val webUrl: String,
override val labelOverride: String? = null,
) : File {
override val key: String = "onedrive://$fileId"
override fun overrideLabel(label: String): OneDriveFile {
return this.copy(labelOverride = label)
}
override val domain: String = Domain
override val key: String = "$domain://$fileId"
override val providerIconRes = R.drawable.ic_badge_onedrive
override val isStoredInCloud = true
override fun getLaunchIntent(context: Context): Intent? {
private fun getLaunchIntent(): Intent {
return Intent(Intent.ACTION_VIEW).apply {
data = Uri.parse(webUrl)
flags = Intent.FLAG_ACTIVITY_NEW_TASK
}
}
override fun launch(context: Context, options: Bundle?): Boolean {
return context.tryStartActivity(getLaunchIntent(), options)
}
companion object {
const val Domain = "onedrive"
}
}
@@ -3,29 +3,46 @@ package de.mm20.launcher2.search.data
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import de.mm20.launcher2.files.R
import de.mm20.launcher2.ktx.tryStartActivity
class OwncloudFile(
fileId: Long,
data class OwncloudFile(
val fileId: Long,
override val label: String,
path: String,
mimeType: String,
size: Long,
isDirectory: Boolean,
override val path: String,
override val mimeType: String,
override val size: Long,
override val isDirectory: Boolean,
val server: String,
metaData: List<Pair<Int, String>>
) : File(fileId, path, mimeType, size, isDirectory, metaData) {
override val key: String = "owncloud://$server/$fileId"
override val metaData: List<Pair<Int, String>>,
override val labelOverride: String? = null,
) : File {
override fun overrideLabel(label: String): OwncloudFile {
return this.copy(labelOverride = label)
}
override val domain: String = Domain
override val key: String = "$domain://$server/$fileId"
override val isStoredInCloud: Boolean
get() = true
override val providerIconRes = R.drawable.ic_badge_owncloud
override fun getLaunchIntent(context: Context): Intent? {
private fun getLaunchIntent(): Intent {
return Intent(Intent.ACTION_VIEW).apply {
data = Uri.parse("$server/f/$id")
data = Uri.parse("$server/f/$fileId")
flags = Intent.FLAG_ACTIVITY_NEW_TASK
}
}
override fun launch(context: Context, options: Bundle?): Boolean {
return context.tryStartActivity(getLaunchIntent(), options)
}
companion object {
const val Domain = "owncloud"
}
}