Reorganize Searchable data types
This commit is contained in:
@@ -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"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user