Compare commits
1 Commits
master
...
composeTes
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
16eecf6c08 |
1
.idea/gradle.xml
generated
1
.idea/gradle.xml
generated
@@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
|
||||
1
.idea/misc.xml
generated
1
.idea/misc.xml
generated
@@ -1,4 +1,3 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
|
||||
|
||||
@@ -72,4 +72,6 @@ dependencies {
|
||||
|
||||
implementation("androidx.compose.foundation:foundation:1.6.0") // Use your current Compose version
|
||||
implementation("androidx.compose.material3:material3:1.2.1") // <-- Fix/Reconfirm Material 3
|
||||
|
||||
implementation("io.coil-kt:coil-compose:2.6.0")
|
||||
}
|
||||
@@ -27,3 +27,4 @@ class MainActivity : ComponentActivity() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.placeholder.sherpai2.data.photos
|
||||
|
||||
data class Photo(
|
||||
val id: String,
|
||||
val uri: String,
|
||||
val title: String? = null,
|
||||
val timestamp: Long
|
||||
)
|
||||
|
||||
data class Album(
|
||||
val id: String,
|
||||
val title: String,
|
||||
val photos: List<Photo>
|
||||
)
|
||||
|
||||
data class AlbumPhoto(
|
||||
val id: Int,
|
||||
val imageUrl: String,
|
||||
val description: String
|
||||
)
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.placeholder.sherpai2.data.photos
|
||||
|
||||
import com.placeholder.sherpai2.data.photos.Photo
|
||||
|
||||
object SamplePhotoSource {
|
||||
|
||||
fun loadPhotos(): List<Photo> {
|
||||
return listOf(
|
||||
Photo(
|
||||
id = "1",
|
||||
uri = "file:///sdcard/Pictures/20200115_181335.jpg",
|
||||
title = "Sample One",
|
||||
timestamp = System.currentTimeMillis()
|
||||
),
|
||||
Photo(
|
||||
id = "2",
|
||||
uri = "file:///sdcard/Pictures/20200115_181417.jpg",
|
||||
title = "Sample Two",
|
||||
timestamp = System.currentTimeMillis()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
// /sdcard/Pictures/20200115_181335.jpg
|
||||
// /sdcard/Pictures/20200115_181417.jpg
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.placeholder.sherpai2.data.repo
|
||||
|
||||
import com.placeholder.sherpai2.data.photos.Photo
|
||||
import com.placeholder.sherpai2.data.photos.SamplePhotoSource
|
||||
|
||||
class PhotoRepository {
|
||||
|
||||
fun getPhotos(): List<Photo> {
|
||||
return SamplePhotoSource.loadPhotos()
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,11 @@ import androidx.compose.ui.graphics.vector.ImageVector
|
||||
*/
|
||||
sealed class AppDestinations(val route: String, val icon: ImageVector, val label: String) {
|
||||
// Core Functional Sections
|
||||
object Tour : AppDestinations(
|
||||
route = "Tour",
|
||||
icon = Icons.Default.PhotoLibrary,
|
||||
label = "Tour"
|
||||
)
|
||||
object Search : AppDestinations("search", Icons.Default.Search, "Search")
|
||||
object Models : AppDestinations("models", Icons.Default.Layers, "Models")
|
||||
object Inventory : AppDestinations("inv", Icons.Default.Inventory2, "Inv")
|
||||
@@ -23,6 +28,7 @@ sealed class AppDestinations(val route: String, val icon: ImageVector, val label
|
||||
|
||||
// Lists used by the AppDrawerContent to render the menu sections easily
|
||||
val mainDrawerItems = listOf(
|
||||
AppDestinations.Tour,
|
||||
AppDestinations.Search,
|
||||
AppDestinations.Models,
|
||||
AppDestinations.Inventory,
|
||||
|
||||
@@ -16,7 +16,7 @@ fun MainScreen() {
|
||||
val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed)
|
||||
val scope = rememberCoroutineScope()
|
||||
// State to track which screen is currently visible
|
||||
//var currentScreen by remember { mutableStateOf(AppDestinations.Search) }
|
||||
// var currentScreen by remember { mutableStateOf(AppDestinations.Search) }
|
||||
var currentScreen: AppDestinations by remember { mutableStateOf(AppDestinations.Search) }
|
||||
|
||||
// ModalNavigationDrawer provides the left sidebar UI/UX
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
// In presentation/MainContentArea.kt
|
||||
package com.placeholder.sherpai2.presentation
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.placeholder.sherpai2.navigation.AppDestinations
|
||||
import com.placeholder.sherpai2.ui.theme.SherpAI2Theme
|
||||
|
||||
@Composable
|
||||
fun MainContentArea(currentScreen: AppDestinations, modifier: Modifier = Modifier) {
|
||||
@@ -21,7 +26,8 @@ fun MainContentArea(currentScreen: AppDestinations, modifier: Modifier = Modifie
|
||||
) {
|
||||
// Swaps the UI content based on the selected screen from the drawer
|
||||
when (currentScreen) {
|
||||
AppDestinations.Search -> SimplePlaceholder("Search Screen: Find your models and data.")
|
||||
AppDestinations.Tour -> TwinAlbumScreen("New Collections." , "Classics")
|
||||
AppDestinations.Search -> SimplePlaceholder("Search for your photo.")
|
||||
AppDestinations.Models -> SimplePlaceholder("Models Screen: Manage your LoRA/embeddings.")
|
||||
AppDestinations.Inventory -> SimplePlaceholder("Inventory Screen: View all collected data.")
|
||||
AppDestinations.Train -> SimplePlaceholder("Train Screen: Start the LoRA adaptation process.")
|
||||
@@ -40,3 +46,59 @@ private fun SimplePlaceholder(text: String) {
|
||||
modifier = Modifier.padding(16.dp)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
fun AlbumPreview(title: String, color: Color) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(8.dp)
|
||||
.aspectRatio(1f) // Makes it a square
|
||||
.background(color, shape = RoundedCornerShape(12.dp)),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(text = title, color = Color.White, fontWeight = FontWeight.Bold)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun TwinAlbumScreen(albumNameA: String, albumNameB: String) {
|
||||
Column(modifier = Modifier.fillMaxSize()) {
|
||||
// --- Top 40% Section ---
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(0.30f) // This takes exactly 40% of vertical space
|
||||
.padding(16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(2.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
// These two occupy the 40% area
|
||||
Box(modifier = Modifier.weight(1f)) {
|
||||
AlbumPreview("Mackenzie Hazer", Color.Blue)
|
||||
}
|
||||
Box(modifier = Modifier.weight(1f)) {
|
||||
AlbumPreview("Winifred", Color.Magenta)
|
||||
}
|
||||
}
|
||||
|
||||
// --- Bottom 60% Section ---
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(0.6f) // This takes the remaining 60%
|
||||
.background(Color.LightGray.copy(alpha = 0.2f))
|
||||
) {
|
||||
Text("Other content goes here", modifier = Modifier.align(Alignment.Center))
|
||||
}
|
||||
}
|
||||
}
|
||||
@Preview
|
||||
@Composable
|
||||
fun PreviewTwinTopRow() {
|
||||
SherpAI2Theme {
|
||||
TwinAlbumScreen("Album A", "Album B")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.placeholder.sherpai2.presentation
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
import coil.compose.rememberAsyncImagePainter
|
||||
|
||||
import com.placeholder.sherpai2.data.photos.Photo
|
||||
import com.placeholder.sherpai2.data.photos.SamplePhotoSource
|
||||
|
||||
@Composable
|
||||
fun PhotoListScreen(
|
||||
photos: List<Photo>
|
||||
) {
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentPadding = PaddingValues(8.dp)
|
||||
) {
|
||||
items(photos, key = { it.id }) { photo ->
|
||||
Image(
|
||||
painter = rememberAsyncImagePainter(photo.uri),
|
||||
contentDescription = photo.title,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(200.dp)
|
||||
.padding(bottom = 8.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
package com.placeholder.sherpai2.ui.tourscreen
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.placeholder.sherpai2.data.photos.Album
|
||||
import com.placeholder.sherpai2.ui.theme.SherpAI2Theme
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.unit.dp
|
||||
import coil.compose.AsyncImage
|
||||
import com.placeholder.sherpai2.presentation.AlbumPreview
|
||||
|
||||
class GalleryScreen {
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun GalleryContent(topAlbums: List<Album>) {
|
||||
Column(modifier = Modifier.fillMaxSize()) {
|
||||
// --- Top 40% Section ---
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(0.4f)
|
||||
.padding(8.dp)
|
||||
) {
|
||||
// We use .getOrNull to safely check if the albums exist in the list
|
||||
val firstAlbum = topAlbums.getOrNull(0)
|
||||
val secondAlbum = topAlbums.getOrNull(1)
|
||||
|
||||
if (firstAlbum != null) {
|
||||
AlbumPreviewBoxRandom(album = firstAlbum, modifier = Modifier.weight(1f))
|
||||
} else {
|
||||
Box(modifier = Modifier.weight(1f)) // Empty placeholder
|
||||
}
|
||||
|
||||
if (secondAlbum != null) {
|
||||
AlbumPreviewBoxRandom(album = secondAlbum, modifier = Modifier.weight(1f))
|
||||
} else {
|
||||
Box(modifier = Modifier.weight(1f)) // Empty placeholder
|
||||
}
|
||||
}
|
||||
|
||||
// --- Bottom 60% Section ---
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(0.6f)
|
||||
) {
|
||||
Text("Lower Content Area", modifier = Modifier.align(Alignment.Center))
|
||||
}
|
||||
}
|
||||
}
|
||||
// --- STATEFUL (Use this for Production) ---
|
||||
@Composable
|
||||
fun GalleryScreen(viewModel: GalleryViewModel = viewModel()) {
|
||||
val albumList by viewModel.albums
|
||||
GalleryContent(topAlbums = albumList)
|
||||
}
|
||||
|
||||
// --- PREVIEW ---
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
fun GalleryPreview() {
|
||||
SherpAI2Theme {
|
||||
// Feed mock data into the Stateless version
|
||||
GalleryContent(topAlbums = listOf(/* Fake Album Objects */))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
fun AlbumPreviewBox(
|
||||
album: Album,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Card(
|
||||
modifier = modifier
|
||||
.padding(8.dp)
|
||||
.aspectRatio(1f), // Keeps it square
|
||||
shape = RoundedCornerShape(12.dp),
|
||||
elevation = CardDefaults.cardElevation(defaultElevation = 4.dp)
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(Color.DarkGray), // Background until image is loaded
|
||||
contentAlignment = Alignment.BottomStart
|
||||
) {
|
||||
// Title Overlay
|
||||
Text(
|
||||
text = album.title,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.background(Color.Black.copy(alpha = 0.5f))
|
||||
.padding(8.dp),
|
||||
color = Color.White,
|
||||
style = MaterialTheme.typography.labelLarge
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AlbumPreviewBoxRandom(album: Album, modifier: Modifier = Modifier) {
|
||||
Card(
|
||||
modifier = modifier.padding(8.dp).aspectRatio(1f),
|
||||
shape = RoundedCornerShape(12.dp)
|
||||
) {
|
||||
Row(modifier = Modifier.fillMaxSize()) {
|
||||
// Loop through the 3 random photos in the album
|
||||
album.photos.forEach { photo ->
|
||||
AsyncImage(
|
||||
model = photo.uri,
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.fillMaxHeight(),
|
||||
contentScale = ContentScale.Crop // Fills the space nicely
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package com.placeholder.sherpai2.ui.tourscreen
|
||||
|
||||
import android.os.Environment
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.placeholder.sherpai2.data.photos.AlbumPhoto
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.placeholder.sherpai2.data.photos.Album // Import your data model
|
||||
import com.placeholder.sherpai2.data.photos.Photo
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
|
||||
class GalleryViewModel : ViewModel() {
|
||||
// This is the 'albums' reference your UI is looking for
|
||||
private val _albums = mutableStateOf<List<Album>>(emptyList())
|
||||
val albums: State<List<Album>> = _albums
|
||||
|
||||
init {
|
||||
fetchInitialData()
|
||||
}
|
||||
|
||||
private fun fetchInitialData() {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
val directory = File("/sdcard/photos") // Or: Environment.getExternalStorageDirectory()
|
||||
|
||||
if (directory.exists() && directory.isDirectory) {
|
||||
val photoFiles = directory.listFiles { file ->
|
||||
file.extension.lowercase() in listOf("jpg", "jpeg", "png")
|
||||
} ?: emptyArray()
|
||||
|
||||
// Map files to your Photo data class
|
||||
val loadedPhotos = photoFiles.map { file ->
|
||||
Photo(
|
||||
id = file.absolutePath,
|
||||
uri = file.toURI().toString(),
|
||||
title = file.nameWithoutExtension,
|
||||
timestamp = file.lastModified()
|
||||
)
|
||||
}
|
||||
|
||||
// Create an Album object with these photos
|
||||
val mainAlbum = Album(
|
||||
id = "local_photos",
|
||||
title = "Phone Gallery",
|
||||
photos = loadedPhotos
|
||||
)
|
||||
|
||||
// Update the UI State on the Main Thread
|
||||
withContext(Dispatchers.Main) {
|
||||
_albums.value = listOf(mainAlbum)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun fetchInitialDataRandom() {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
val root = Environment.getExternalStorageDirectory()
|
||||
val directory = File(root, "photos")
|
||||
|
||||
if (directory.exists() && directory.isDirectory) {
|
||||
// 1. Filter specifically for .jpg files
|
||||
val photoFiles = directory.listFiles { file ->
|
||||
file.extension.lowercase() == "jpg"
|
||||
} ?: emptyArray()
|
||||
|
||||
// 2. Shuffle the list and take only the first 3
|
||||
val randomPhotos = photoFiles.asSequence()
|
||||
.shuffled()
|
||||
.take(3)
|
||||
.map { file ->
|
||||
Photo(
|
||||
id = file.absolutePath,
|
||||
uri = file.toURI().toString(),
|
||||
title = file.nameWithoutExtension,
|
||||
timestamp = file.lastModified()
|
||||
)
|
||||
}.toList()
|
||||
|
||||
// 3. Update the state with these 3 random photos
|
||||
val mainAlbum = Album(
|
||||
id = "random_selection",
|
||||
title = "Random Picks",
|
||||
photos = randomPhotos
|
||||
)
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
_albums.value = listOf(mainAlbum)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package com.placeholder.sherpai2.ui.tourscreen.components
|
||||
|
||||
class AlbumBox {
|
||||
}
|
||||
Reference in New Issue
Block a user