show OS icon in home file manager

This commit is contained in:
2026-03-10 13:06:07 +05:00
parent 0136b7b9f2
commit 9aa79e02bc
11 changed files with 108 additions and 13 deletions

View File

@@ -1,8 +1,10 @@
package sh.sar.isodroid.ui.components
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
@@ -10,11 +12,13 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Album
import androidx.compose.material.icons.filled.Folder
@@ -27,12 +31,48 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import coil.compose.AsyncImage
import coil.decode.SvgDecoder
import coil.request.ImageRequest
import sh.sar.isodroid.data.IsoFile
/**
* Finds a matching OS icon filename for a given file by dynamically checking available icons.
* Returns the icon filename (e.g., "archlinux.svg") if a match is found, null otherwise.
* When multiple matches exist (e.g., "nixos" and "linux"), prioritizes:
* 1. Longest match (most specific)
* 2. Earliest position in filename (if same length)
*/
private fun findOsIcon(context: android.content.Context, filename: String): String? {
return try {
// Dynamically load available icon files from assets
val availableIcons = context.assets.list("osicons")
?.filter { it.endsWith(".svg", ignoreCase = true) }
?.map { it.removeSuffix(".svg").lowercase() }
?: emptyList()
val lowerFilename = filename.lowercase()
// Find ALL matching icons, then return the best match
availableIcons
.filter { lowerFilename.contains(it) }
.maxWithOrNull(compareBy(
{ it.length }, // Prefer longer matches
{ -lowerFilename.indexOf(it) } // Then prefer earlier position (negated for descending)
))
?.let { "$it.svg" }
} catch (e: Exception) {
null
}
}
@Composable
fun FileBrowser(
files: List<IsoFile>,
@@ -95,6 +135,7 @@ fun FileBrowser(
name = file.name,
size = file.formattedSize,
isIso = file.name.lowercase().endsWith(".iso"),
isImg = file.name.lowercase().endsWith(".img"),
onClick = { onFileClick(file) },
onLongClick = {
hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress)
@@ -114,9 +155,14 @@ private fun FileItem(
size: String,
isDirectory: Boolean = false,
isIso: Boolean = true,
isImg: Boolean = false,
onClick: () -> Unit,
onLongClick: (() -> Unit)?
) {
val context = LocalContext.current
val osIcon = if (!isDirectory) findOsIcon(context, name) else null
val iconTint = MaterialTheme.colorScheme.primary
Card(
modifier = Modifier
.fillMaxWidth()
@@ -134,20 +180,59 @@ private fun FileItem(
.padding(12.dp),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
imageVector = when {
isDirectory -> Icons.Default.Folder
isIso -> Icons.Default.Album
else -> Icons.Default.InsertDriveFile
},
contentDescription = null,
tint = when {
isDirectory -> MaterialTheme.colorScheme.primary
isIso -> MaterialTheme.colorScheme.primary
else -> MaterialTheme.colorScheme.secondary
},
// Icon with file type indicator
Box(
modifier = Modifier.size(40.dp)
)
) {
if (osIcon != null) {
// Show OS icon from SVG
AsyncImage(
model = ImageRequest.Builder(context)
.data("file:///android_asset/osicons/$osIcon")
.decoderFactory(SvgDecoder.Factory())
.build(),
contentDescription = name,
modifier = Modifier.fillMaxSize(),
colorFilter = ColorFilter.tint(iconTint)
)
// Small file type indicator in bottom-right corner
Icon(
imageVector = when {
isIso -> Icons.Default.Album
isImg -> Icons.Default.InsertDriveFile
else -> Icons.Default.InsertDriveFile
},
contentDescription = null,
tint = MaterialTheme.colorScheme.onPrimary,
modifier = Modifier
.size(16.dp)
.align(Alignment.BottomEnd)
.offset(x = 2.dp, y = 2.dp)
.clip(CircleShape)
.background(MaterialTheme.colorScheme.primary)
.padding(2.dp)
)
} else {
// Fallback to standard Material icons
Icon(
imageVector = when {
isDirectory -> Icons.Default.Folder
isIso -> Icons.Default.Album
isImg -> Icons.Default.InsertDriveFile
else -> Icons.Default.InsertDriveFile
},
contentDescription = null,
tint = when {
isDirectory -> MaterialTheme.colorScheme.primary
isIso -> MaterialTheme.colorScheme.primary
else -> MaterialTheme.colorScheme.secondary
},
modifier = Modifier.fillMaxSize()
)
}
}
Spacer(modifier = Modifier.width(12.dp))
Column(modifier = Modifier.weight(1f)) {
Text(