Compare commits
2 Commits
c458e08075
...
cleanup_co
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0e388e9a64 | ||
|
|
16eecf6c08 |
1
.idea/.name
generated
1
.idea/.name
generated
@@ -1 +0,0 @@
|
|||||||
SherpAI2
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
kotlin version: 2.0.21
|
|
||||||
error message: The daemon has terminated unexpectedly on startup attempt #1 with error code: 0. The daemon process output:
|
|
||||||
1. Kotlin compile daemon is ready
|
|
||||||
|
|
||||||
@@ -1,76 +1,77 @@
|
|||||||
|
// build.gradle.kts (Module: :app)
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
alias(libs.plugins.android.application)
|
// 1. Core Android and Kotlin plugins (MUST be first)
|
||||||
alias(libs.plugins.kotlin.android)
|
id("com.android.application")
|
||||||
alias(libs.plugins.kotlin.compose)
|
kotlin("android")
|
||||||
alias(libs.plugins.ksp)
|
|
||||||
alias(libs.plugins.hilt.android)
|
id("org.jetbrains.kotlin.plugin.compose") // Note: No version is specified here
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
|
// 2. Android Configuration
|
||||||
namespace = "com.placeholder.sherpai2"
|
namespace = "com.placeholder.sherpai2"
|
||||||
compileSdk = 35
|
compileSdk = 34
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId = "com.placeholder.sherpai2"
|
applicationId = "com.placeholder.sherpai2"
|
||||||
minSdk = 24
|
minSdk = 24
|
||||||
targetSdk = 35
|
targetSdk = 34
|
||||||
versionCode = 1
|
versionCode = 1
|
||||||
versionName = "1.0"
|
versionName = "1.0"
|
||||||
|
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTypes {
|
// 3. Kotlin & Java Settings
|
||||||
release {
|
|
||||||
isMinifyEnabled = false
|
|
||||||
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility = JavaVersion.VERSION_11
|
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||||
targetCompatibility = JavaVersion.VERSION_11
|
targetCompatibility = JavaVersion.VERSION_1_8
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlinOptions {
|
kotlinOptions {
|
||||||
jvmTarget = "11"
|
jvmTarget = "1.8"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 4. Jetpack Compose Configuration (Crucial!)
|
||||||
buildFeatures {
|
buildFeatures {
|
||||||
compose = true
|
compose = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
composeOptions {
|
||||||
|
kotlinCompilerExtensionVersion = "1.5.8" // Must match your Kotlin version
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
// AndroidX & Lifecycle
|
// --- CORE ANDROID & LIFECYCLE ---
|
||||||
implementation(libs.androidx.core.ktx)
|
implementation("androidx.core:core-ktx:1.12.0")
|
||||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
implementation("androidx.lifecycle:lifecycle-runtime-compose:2.7.0")
|
||||||
implementation(libs.androidx.lifecycle.viewmodel.compose)
|
implementation("androidx.activity:activity-compose:1.8.2") // Fixes 'activity' ref error
|
||||||
implementation(libs.androidx.activity.compose)
|
|
||||||
|
|
||||||
// Compose (Using BOM)
|
// --- JETPACK COMPOSE UI (Material 3) ---
|
||||||
implementation(platform(libs.androidx.compose.bom))
|
implementation("androidx.compose.ui:ui")
|
||||||
implementation(libs.androidx.compose.ui)
|
implementation("androidx.compose.ui:ui-graphics")
|
||||||
implementation(libs.androidx.compose.ui.graphics)
|
implementation("androidx.compose.ui:ui-tooling-preview")
|
||||||
implementation(libs.androidx.compose.ui.tooling.preview)
|
implementation("androidx.compose.material3:material3") // Fixes 'material3' ref error
|
||||||
implementation(libs.androidx.compose.material3)
|
|
||||||
implementation(libs.androidx.compose.material.icons)
|
|
||||||
|
|
||||||
// Navigation & Hilt Integration
|
// --- COMPOSE ICONS (Fixes 'material' and 'Icons' ref errors) ---
|
||||||
implementation(libs.androidx.navigation.compose)
|
// Uses direct string to avoid Version Catalog conflicts
|
||||||
implementation(libs.androidx.hilt.navigation.compose)
|
implementation("androidx.compose.material:material-icons-extended:1.6.0")
|
||||||
implementation(libs.hilt.android)
|
|
||||||
ksp(libs.hilt.compiler)
|
|
||||||
|
|
||||||
// Images
|
// --- STATE MANAGEMENT / COROUTINES ---
|
||||||
implementation(libs.coil.compose)
|
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.7.0")
|
||||||
|
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3")
|
||||||
|
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3")
|
||||||
|
|
||||||
// Tooling (Fixed to use TOML alias)
|
// --- TESTING ---
|
||||||
debugImplementation(libs.androidx.compose.ui.tooling)
|
testImplementation("junit:junit:4.13.2")
|
||||||
|
androidTestImplementation("androidx.test.ext:junit:1.1.5")
|
||||||
|
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
|
||||||
|
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
|
||||||
|
debugImplementation("androidx.compose.ui:ui-tooling")
|
||||||
|
debugImplementation("androidx.compose.ui:ui-test-manifest")
|
||||||
|
|
||||||
//backend2
|
implementation("androidx.compose.foundation:foundation:1.6.0") // Use your current Compose version
|
||||||
//room addon
|
implementation("androidx.compose.material3:material3:1.2.1") // <-- Fix/Reconfirm Material 3
|
||||||
implementation(libs.room.runtime)
|
|
||||||
implementation(libs.room.ktx)
|
implementation("io.coil-kt:coil-compose:2.6.0")
|
||||||
ksp(libs.room.compiler)
|
|
||||||
}
|
}
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
package com.placeholder.sherpai2.data.local
|
|
||||||
|
|
||||||
import androidx.room.Database
|
|
||||||
import androidx.room.RoomDatabase
|
|
||||||
import com.placeholder.sherpai2.data.local.dao.EventDao
|
|
||||||
import com.placeholder.sherpai2.data.local.dao.ImageAggregateDao
|
|
||||||
import com.placeholder.sherpai2.data.local.dao.ImageDao
|
|
||||||
import com.placeholder.sherpai2.data.local.dao.ImageEventDao
|
|
||||||
import com.placeholder.sherpai2.data.local.dao.ImagePersonDao
|
|
||||||
import com.placeholder.sherpai2.data.local.dao.ImageTagDao
|
|
||||||
import com.placeholder.sherpai2.data.local.dao.PersonDao
|
|
||||||
import com.placeholder.sherpai2.data.local.dao.TagDao
|
|
||||||
import com.placeholder.sherpai2.data.local.entity.EventEntity
|
|
||||||
import com.placeholder.sherpai2.data.local.entity.ImageEntity
|
|
||||||
import com.placeholder.sherpai2.data.local.entity.ImageEventEntity
|
|
||||||
import com.placeholder.sherpai2.data.local.entity.ImagePersonEntity
|
|
||||||
import com.placeholder.sherpai2.data.local.entity.ImageTagEntity
|
|
||||||
import com.placeholder.sherpai2.data.local.entity.PersonEntity
|
|
||||||
import com.placeholder.sherpai2.data.local.entity.TagEntity
|
|
||||||
|
|
||||||
@Database(
|
|
||||||
entities = [
|
|
||||||
ImageEntity::class,
|
|
||||||
TagEntity::class,
|
|
||||||
PersonEntity::class,
|
|
||||||
EventEntity::class,
|
|
||||||
ImageTagEntity::class,
|
|
||||||
ImagePersonEntity::class,
|
|
||||||
ImageEventEntity::class
|
|
||||||
],
|
|
||||||
version = 1,
|
|
||||||
exportSchema = true
|
|
||||||
)
|
|
||||||
|
|
||||||
abstract class AppDatabase : RoomDatabase() {
|
|
||||||
|
|
||||||
abstract fun imageDao(): ImageDao
|
|
||||||
abstract fun tagDao(): TagDao
|
|
||||||
abstract fun personDao(): PersonDao
|
|
||||||
abstract fun eventDao(): EventDao
|
|
||||||
|
|
||||||
abstract fun imageTagDao(): ImageTagDao
|
|
||||||
abstract fun imagePersonDao(): ImagePersonDao
|
|
||||||
abstract fun imageEventDao(): ImageEventDao
|
|
||||||
|
|
||||||
abstract fun imageAggregateDao(): ImageAggregateDao
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
package com.placeholder.sherpai2.data.local.dao
|
|
||||||
|
|
||||||
import androidx.room.Dao
|
|
||||||
import androidx.room.Insert
|
|
||||||
import androidx.room.OnConflictStrategy
|
|
||||||
import androidx.room.Query
|
|
||||||
import com.placeholder.sherpai2.data.local.entity.EventEntity
|
|
||||||
|
|
||||||
@Dao
|
|
||||||
interface EventDao {
|
|
||||||
|
|
||||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
|
||||||
suspend fun insert(event: EventEntity)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find events covering a timestamp.
|
|
||||||
*
|
|
||||||
* This is the backbone of auto-tagging by date.
|
|
||||||
*/
|
|
||||||
@Query("""
|
|
||||||
SELECT * FROM events
|
|
||||||
WHERE :timestamp BETWEEN startDate AND endDate
|
|
||||||
AND isHidden = 0
|
|
||||||
""")
|
|
||||||
suspend fun findEventsForTimestamp(timestamp: Long): List<EventEntity>
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
package com.placeholder.sherpai2.data.local.dao
|
|
||||||
|
|
||||||
import androidx.room.Dao
|
|
||||||
import androidx.room.Query
|
|
||||||
import androidx.room.Transaction
|
|
||||||
import com.placeholder.sherpai2.data.local.model.ImageWithEverything
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
|
||||||
|
|
||||||
@Dao
|
|
||||||
interface ImageAggregateDao {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Observe a fully-hydrated image object.
|
|
||||||
*
|
|
||||||
* This is what your detail screen should use.
|
|
||||||
*/
|
|
||||||
@Transaction
|
|
||||||
@Query("""
|
|
||||||
SELECT * FROM images
|
|
||||||
WHERE imageId = :imageId
|
|
||||||
""")
|
|
||||||
fun observeImageWithEverything(
|
|
||||||
imageId: String
|
|
||||||
): Flow<ImageWithEverything>
|
|
||||||
}
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
package com.placeholder.sherpai2.data.local.dao
|
|
||||||
|
|
||||||
import androidx.room.Dao
|
|
||||||
import androidx.room.Insert
|
|
||||||
import androidx.room.OnConflictStrategy
|
|
||||||
import androidx.room.Query
|
|
||||||
import com.placeholder.sherpai2.data.local.entity.ImageEntity
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
|
||||||
|
|
||||||
@Dao
|
|
||||||
interface ImageDao {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Insert images.
|
|
||||||
*
|
|
||||||
* IGNORE prevents duplicate insertion
|
|
||||||
* when sha256 or imageUri already exists.
|
|
||||||
*/
|
|
||||||
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
|
||||||
suspend fun insertImages(images: List<ImageEntity>)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get image by ID.
|
|
||||||
*/
|
|
||||||
@Query("SELECT * FROM images WHERE imageId = :imageId")
|
|
||||||
suspend fun getImageById(imageId: String): ImageEntity?
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stream images ordered by capture time (newest first).
|
|
||||||
*
|
|
||||||
* Flow is critical:
|
|
||||||
* - UI auto-updates
|
|
||||||
* - No manual refresh
|
|
||||||
*/
|
|
||||||
@Query("""
|
|
||||||
SELECT * FROM images
|
|
||||||
ORDER BY capturedAt DESC
|
|
||||||
""")
|
|
||||||
fun observeAllImages(): Flow<List<ImageEntity>>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetch images in a time range.
|
|
||||||
* Used for:
|
|
||||||
* - event auto-assignment
|
|
||||||
* - timeline views
|
|
||||||
*/
|
|
||||||
@Query("""
|
|
||||||
SELECT * FROM images
|
|
||||||
WHERE capturedAt BETWEEN :start AND :end
|
|
||||||
ORDER BY capturedAt ASC
|
|
||||||
""")
|
|
||||||
suspend fun getImagesInRange(
|
|
||||||
start: Long,
|
|
||||||
end: Long
|
|
||||||
): List<ImageEntity>
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
package com.placeholder.sherpai2.data.local.dao
|
|
||||||
|
|
||||||
import androidx.room.Dao
|
|
||||||
import androidx.room.Insert
|
|
||||||
import androidx.room.OnConflictStrategy
|
|
||||||
import androidx.room.Query
|
|
||||||
import com.placeholder.sherpai2.data.local.entity.ImageEventEntity
|
|
||||||
|
|
||||||
@Dao
|
|
||||||
interface ImageEventDao {
|
|
||||||
|
|
||||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
|
||||||
suspend fun upsert(entity: ImageEventEntity)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Images associated with an event.
|
|
||||||
*/
|
|
||||||
@Query("""
|
|
||||||
SELECT imageId FROM image_events
|
|
||||||
WHERE eventId = :eventId
|
|
||||||
""")
|
|
||||||
suspend fun findImagesForEvent(eventId: String): List<String>
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
package com.placeholder.sherpai2.data.local.dao
|
|
||||||
|
|
||||||
import androidx.room.Dao
|
|
||||||
import androidx.room.Insert
|
|
||||||
import androidx.room.OnConflictStrategy
|
|
||||||
import androidx.room.Query
|
|
||||||
import com.placeholder.sherpai2.data.local.entity.ImagePersonEntity
|
|
||||||
|
|
||||||
@Dao
|
|
||||||
interface ImagePersonDao {
|
|
||||||
|
|
||||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
|
||||||
suspend fun upsert(entity: ImagePersonEntity)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* All images containing a specific person.
|
|
||||||
*/
|
|
||||||
@Query("""
|
|
||||||
SELECT imageId FROM image_persons
|
|
||||||
WHERE personId = :personId
|
|
||||||
AND visibility = 'PUBLIC'
|
|
||||||
AND confirmed = 1
|
|
||||||
""")
|
|
||||||
suspend fun findImagesForPerson(personId: String): List<String>
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
package com.placeholder.sherpai2.data.local.dao
|
|
||||||
|
|
||||||
import androidx.room.Dao
|
|
||||||
import androidx.room.Insert
|
|
||||||
import androidx.room.OnConflictStrategy
|
|
||||||
import androidx.room.Query
|
|
||||||
import com.placeholder.sherpai2.data.local.entity.ImageTagEntity
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
|
||||||
|
|
||||||
@Dao
|
|
||||||
interface ImageTagDao {
|
|
||||||
|
|
||||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
|
||||||
suspend fun upsert(imageTag: ImageTagEntity)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Observe tags for an image.
|
|
||||||
*/
|
|
||||||
@Query("""
|
|
||||||
SELECT * FROM image_tags
|
|
||||||
WHERE imageId = :imageId
|
|
||||||
AND visibility != 'HIDDEN'
|
|
||||||
""")
|
|
||||||
fun observeTagsForImage(imageId: String): Flow<List<ImageTagEntity>>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find images by tag.
|
|
||||||
*
|
|
||||||
* This is your primary tag-search query.
|
|
||||||
*/
|
|
||||||
@Query("""
|
|
||||||
SELECT imageId FROM image_tags
|
|
||||||
WHERE tagId = :tagId
|
|
||||||
AND visibility = 'PUBLIC'
|
|
||||||
AND confidence >= :minConfidence
|
|
||||||
""")
|
|
||||||
suspend fun findImagesByTag(
|
|
||||||
tagId: String,
|
|
||||||
minConfidence: Float = 0.5f
|
|
||||||
): List<String>
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
package com.placeholder.sherpai2.data.local.dao
|
|
||||||
|
|
||||||
import androidx.room.Dao
|
|
||||||
import androidx.room.Insert
|
|
||||||
import androidx.room.OnConflictStrategy
|
|
||||||
import androidx.room.Query
|
|
||||||
import com.placeholder.sherpai2.data.local.entity.PersonEntity
|
|
||||||
|
|
||||||
@Dao
|
|
||||||
interface PersonDao {
|
|
||||||
|
|
||||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
|
||||||
suspend fun insert(person: PersonEntity)
|
|
||||||
|
|
||||||
@Query("SELECT * FROM persons WHERE personId = :personId")
|
|
||||||
suspend fun getById(personId: String): PersonEntity?
|
|
||||||
|
|
||||||
@Query("""
|
|
||||||
SELECT * FROM persons
|
|
||||||
WHERE isHidden = 0
|
|
||||||
ORDER BY displayName
|
|
||||||
""")
|
|
||||||
suspend fun getVisiblePeople(): List<PersonEntity>
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
package com.placeholder.sherpai2.data.local.dao
|
|
||||||
|
|
||||||
import androidx.room.Dao
|
|
||||||
import androidx.room.Insert
|
|
||||||
import androidx.room.OnConflictStrategy
|
|
||||||
import androidx.room.Query
|
|
||||||
import com.placeholder.sherpai2.data.local.entity.TagEntity
|
|
||||||
|
|
||||||
@Dao
|
|
||||||
interface TagDao {
|
|
||||||
|
|
||||||
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
|
||||||
suspend fun insert(tag: TagEntity)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolve a tag by value.
|
|
||||||
* Example: "park"
|
|
||||||
*/
|
|
||||||
@Query("SELECT * FROM tags WHERE value = :value LIMIT 1")
|
|
||||||
suspend fun getByValue(value: String): TagEntity?
|
|
||||||
|
|
||||||
@Query("SELECT * FROM tags")
|
|
||||||
suspend fun getAll(): List<TagEntity>
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
package com.placeholder.sherpai2.data.local.entity
|
|
||||||
|
|
||||||
import androidx.room.Entity
|
|
||||||
import androidx.room.Index
|
|
||||||
import androidx.room.PrimaryKey
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a meaningful event spanning a time range.
|
|
||||||
*
|
|
||||||
* Events allow auto-association of images by timestamp.
|
|
||||||
*/
|
|
||||||
@Entity(
|
|
||||||
tableName = "events",
|
|
||||||
indices = [
|
|
||||||
Index(value = ["startDate"]),
|
|
||||||
Index(value = ["endDate"])
|
|
||||||
]
|
|
||||||
)
|
|
||||||
data class EventEntity(
|
|
||||||
|
|
||||||
@PrimaryKey
|
|
||||||
val eventId: String,
|
|
||||||
|
|
||||||
val name: String,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inclusive start date (UTC millis).
|
|
||||||
*/
|
|
||||||
val startDate: Long,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inclusive end date (UTC millis).
|
|
||||||
*/
|
|
||||||
val endDate: Long,
|
|
||||||
|
|
||||||
val location: String?,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 0.0 – 1.0 importance weight
|
|
||||||
*/
|
|
||||||
val importance: Float,
|
|
||||||
|
|
||||||
val isHidden: Boolean
|
|
||||||
)
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
package com.placeholder.sherpai2.data.local.entity
|
|
||||||
|
|
||||||
import androidx.room.Entity
|
|
||||||
import androidx.room.Index
|
|
||||||
import androidx.room.PrimaryKey
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a single image on the device.
|
|
||||||
*
|
|
||||||
* This entity is intentionally immutable:
|
|
||||||
* - imageUri identifies where the image lives
|
|
||||||
* - sha256 prevents duplicates
|
|
||||||
* - capturedAt is the EXIF timestamp
|
|
||||||
*
|
|
||||||
* This table should be append-only.
|
|
||||||
*/
|
|
||||||
@Entity(
|
|
||||||
tableName = "images",
|
|
||||||
indices = [
|
|
||||||
Index(value = ["imageUri"], unique = true),
|
|
||||||
Index(value = ["sha256"], unique = true),
|
|
||||||
Index(value = ["capturedAt"])
|
|
||||||
]
|
|
||||||
)
|
|
||||||
data class ImageEntity(
|
|
||||||
|
|
||||||
@PrimaryKey
|
|
||||||
val imageId: String,
|
|
||||||
|
|
||||||
val imageUri: String,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cryptographic hash of image bytes.
|
|
||||||
* Used for deduplication and re-indexing.
|
|
||||||
*/
|
|
||||||
val sha256: String,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* EXIF timestamp (UTC millis).
|
|
||||||
*/
|
|
||||||
val capturedAt: Long,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* When this image was indexed into the app.
|
|
||||||
*/
|
|
||||||
val ingestedAt: Long,
|
|
||||||
|
|
||||||
val width: Int,
|
|
||||||
val height: Int,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* CAMERA | SCREENSHOT | IMPORTED
|
|
||||||
*/
|
|
||||||
val source: String
|
|
||||||
)
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
package com.placeholder.sherpai2.data.local.entity
|
|
||||||
|
|
||||||
import androidx.room.Entity
|
|
||||||
import androidx.room.ForeignKey
|
|
||||||
import androidx.room.Index
|
|
||||||
|
|
||||||
@Entity(
|
|
||||||
tableName = "image_events",
|
|
||||||
primaryKeys = ["imageId", "eventId"],
|
|
||||||
foreignKeys = [
|
|
||||||
ForeignKey(
|
|
||||||
entity = ImageEntity::class,
|
|
||||||
parentColumns = ["imageId"],
|
|
||||||
childColumns = ["imageId"],
|
|
||||||
onDelete = ForeignKey.CASCADE
|
|
||||||
),
|
|
||||||
ForeignKey(
|
|
||||||
entity = EventEntity::class,
|
|
||||||
parentColumns = ["eventId"],
|
|
||||||
childColumns = ["eventId"],
|
|
||||||
onDelete = ForeignKey.CASCADE
|
|
||||||
)
|
|
||||||
],
|
|
||||||
indices = [
|
|
||||||
Index("eventId")
|
|
||||||
]
|
|
||||||
)
|
|
||||||
data class ImageEventEntity(
|
|
||||||
|
|
||||||
val imageId: String,
|
|
||||||
val eventId: String,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* AUTO | MANUAL
|
|
||||||
*/
|
|
||||||
val source: String,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User override flag.
|
|
||||||
*/
|
|
||||||
val override: Boolean
|
|
||||||
)
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
package com.placeholder.sherpai2.data.local.entity
|
|
||||||
|
|
||||||
import androidx.room.Entity
|
|
||||||
import androidx.room.ForeignKey
|
|
||||||
import androidx.room.Index
|
|
||||||
|
|
||||||
@Entity(
|
|
||||||
tableName = "image_persons",
|
|
||||||
primaryKeys = ["imageId", "personId"],
|
|
||||||
foreignKeys = [
|
|
||||||
ForeignKey(
|
|
||||||
entity = ImageEntity::class,
|
|
||||||
parentColumns = ["imageId"],
|
|
||||||
childColumns = ["imageId"],
|
|
||||||
onDelete = ForeignKey.CASCADE
|
|
||||||
),
|
|
||||||
ForeignKey(
|
|
||||||
entity = PersonEntity::class,
|
|
||||||
parentColumns = ["personId"],
|
|
||||||
childColumns = ["personId"],
|
|
||||||
onDelete = ForeignKey.CASCADE
|
|
||||||
)
|
|
||||||
],
|
|
||||||
indices = [
|
|
||||||
Index("personId")
|
|
||||||
]
|
|
||||||
)
|
|
||||||
data class ImagePersonEntity(
|
|
||||||
|
|
||||||
val imageId: String,
|
|
||||||
val personId: String,
|
|
||||||
|
|
||||||
val confidence: Float,
|
|
||||||
val confirmed: Boolean,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* PUBLIC | PRIVATE
|
|
||||||
*/
|
|
||||||
val visibility: String
|
|
||||||
)
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
package com.placeholder.sherpai2.data.local.entity
|
|
||||||
|
|
||||||
import androidx.room.Entity
|
|
||||||
import androidx.room.ForeignKey
|
|
||||||
import androidx.room.Index
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Join table linking images to tags.
|
|
||||||
*
|
|
||||||
* This is NOT optional.
|
|
||||||
* Do not inline tag lists on ImageEntity.
|
|
||||||
*/
|
|
||||||
@Entity(
|
|
||||||
tableName = "image_tags",
|
|
||||||
primaryKeys = ["imageId", "tagId"],
|
|
||||||
foreignKeys = [
|
|
||||||
ForeignKey(
|
|
||||||
entity = ImageEntity::class,
|
|
||||||
parentColumns = ["imageId"],
|
|
||||||
childColumns = ["imageId"],
|
|
||||||
onDelete = ForeignKey.CASCADE
|
|
||||||
),
|
|
||||||
ForeignKey(
|
|
||||||
entity = TagEntity::class,
|
|
||||||
parentColumns = ["tagId"],
|
|
||||||
childColumns = ["tagId"],
|
|
||||||
onDelete = ForeignKey.CASCADE
|
|
||||||
)
|
|
||||||
],
|
|
||||||
indices = [
|
|
||||||
Index("tagId"),
|
|
||||||
Index("imageId")
|
|
||||||
]
|
|
||||||
)
|
|
||||||
data class ImageTagEntity(
|
|
||||||
|
|
||||||
val imageId: String,
|
|
||||||
val tagId: String,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* AUTO | MANUAL
|
|
||||||
*/
|
|
||||||
val source: String,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ML confidence (0–1).
|
|
||||||
*/
|
|
||||||
val confidence: Float,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* PUBLIC | PRIVATE | HIDDEN
|
|
||||||
*/
|
|
||||||
val visibility: String,
|
|
||||||
|
|
||||||
val createdAt: Long
|
|
||||||
)
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
package com.placeholder.sherpai2.data.local.entity
|
|
||||||
|
|
||||||
import androidx.room.Entity
|
|
||||||
import androidx.room.PrimaryKey
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a known person.
|
|
||||||
*
|
|
||||||
* People are separate from generic tags because:
|
|
||||||
* - face embeddings
|
|
||||||
* - privacy rules
|
|
||||||
* - identity merging
|
|
||||||
*/
|
|
||||||
@Entity(tableName = "persons")
|
|
||||||
data class PersonEntity(
|
|
||||||
|
|
||||||
@PrimaryKey
|
|
||||||
val personId: String,
|
|
||||||
|
|
||||||
val displayName: String,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reference to face embedding storage (ML layer).
|
|
||||||
*/
|
|
||||||
val faceEmbeddingId: String?,
|
|
||||||
|
|
||||||
val isHidden: Boolean,
|
|
||||||
|
|
||||||
val createdAt: Long
|
|
||||||
)
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
package com.placeholder.sherpai2.data.local.entity
|
|
||||||
|
|
||||||
import androidx.room.Entity
|
|
||||||
import androidx.room.PrimaryKey
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a conceptual tag.
|
|
||||||
*
|
|
||||||
* Tags are normalized so that:
|
|
||||||
* - "park" exists once
|
|
||||||
* - many images can reference it
|
|
||||||
*/
|
|
||||||
@Entity(tableName = "tags")
|
|
||||||
data class TagEntity(
|
|
||||||
|
|
||||||
@PrimaryKey
|
|
||||||
val tagId: String,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* GENERIC | SYSTEM | HIDDEN
|
|
||||||
*/
|
|
||||||
val type: String,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Human-readable value, e.g. "park", "sunset"
|
|
||||||
*/
|
|
||||||
val value: String,
|
|
||||||
|
|
||||||
val createdAt: Long
|
|
||||||
)
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
package com.placeholder.sherpai2.data.local.model
|
|
||||||
|
|
||||||
import androidx.room.Embedded
|
|
||||||
import androidx.room.Relation
|
|
||||||
import com.placeholder.sherpai2.data.local.entity.*
|
|
||||||
|
|
||||||
data class ImageWithEverything(
|
|
||||||
|
|
||||||
@Embedded
|
|
||||||
val image: ImageEntity,
|
|
||||||
|
|
||||||
@Relation(
|
|
||||||
parentColumn = "imageId",
|
|
||||||
entityColumn = "imageId"
|
|
||||||
)
|
|
||||||
val tags: List<ImageTagEntity>,
|
|
||||||
|
|
||||||
@Relation(
|
|
||||||
parentColumn = "imageId",
|
|
||||||
entityColumn = "imageId"
|
|
||||||
)
|
|
||||||
val persons: List<ImagePersonEntity>,
|
|
||||||
|
|
||||||
@Relation(
|
|
||||||
parentColumn = "imageId",
|
|
||||||
entityColumn = "imageId"
|
|
||||||
)
|
|
||||||
val events: List<ImageEventEntity>
|
|
||||||
)
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
package com.placeholder.sherpai2.data.local.model
|
|
||||||
|
|
||||||
import androidx.room.Embedded
|
|
||||||
import androidx.room.Relation
|
|
||||||
import com.placeholder.sherpai2.data.local.entity.ImageEntity
|
|
||||||
import com.placeholder.sherpai2.data.local.entity.ImageTagEntity
|
|
||||||
|
|
||||||
data class ImageWithTags(
|
|
||||||
|
|
||||||
@Embedded
|
|
||||||
val image: ImageEntity,
|
|
||||||
|
|
||||||
@Relation(
|
|
||||||
parentColumn = "imageId",
|
|
||||||
entityColumn = "imageId"
|
|
||||||
)
|
|
||||||
val tags: List<ImageTagEntity>
|
|
||||||
)
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
package com.placeholder.sherpai2.di
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.room.Room
|
|
||||||
import com.placeholder.sherpai2.data.local.AppDatabase
|
|
||||||
import dagger.Module
|
|
||||||
import dagger.Provides
|
|
||||||
import dagger.hilt.InstallIn
|
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
|
||||||
import dagger.hilt.components.SingletonComponent
|
|
||||||
import javax.inject.Singleton
|
|
||||||
|
|
||||||
@Module
|
|
||||||
@InstallIn(SingletonComponent::class)
|
|
||||||
object DatabaseModule {
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
fun provideDatabase(
|
|
||||||
@ApplicationContext context: Context
|
|
||||||
): AppDatabase {
|
|
||||||
return Room.databaseBuilder(
|
|
||||||
context,
|
|
||||||
AppDatabase::class.java,
|
|
||||||
"sherpai.db"
|
|
||||||
).build()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,5 +3,4 @@ plugins {
|
|||||||
alias(libs.plugins.android.application) apply false
|
alias(libs.plugins.android.application) apply false
|
||||||
alias(libs.plugins.kotlin.android) apply false
|
alias(libs.plugins.kotlin.android) apply false
|
||||||
alias(libs.plugins.kotlin.compose) apply false
|
alias(libs.plugins.kotlin.compose) apply false
|
||||||
alias(libs.plugins.ksp) apply false
|
|
||||||
}
|
}
|
||||||
@@ -1,62 +1,32 @@
|
|||||||
[versions]
|
[versions]
|
||||||
# Tooling
|
agp = "8.13.1"
|
||||||
agp = "8.7.3"
|
|
||||||
kotlin = "2.0.21"
|
kotlin = "2.0.21"
|
||||||
ksp = "2.0.21-1.0.28"
|
coreKtx = "1.17.0"
|
||||||
|
junit = "4.13.2"
|
||||||
# AndroidX / Lifecycle
|
junitVersion = "1.3.0"
|
||||||
coreKtx = "1.15.0"
|
espressoCore = "3.7.0"
|
||||||
lifecycle = "2.8.7"
|
lifecycleRuntimeKtx = "2.10.0"
|
||||||
activityCompose = "1.9.3"
|
activityCompose = "1.12.1"
|
||||||
composeBom = "2024.12.01"
|
composeBom = "2024.09.00"
|
||||||
navigationCompose = "2.8.5"
|
|
||||||
hiltNavigationCompose = "1.2.0"
|
|
||||||
|
|
||||||
# DI
|
|
||||||
hilt = "2.52"
|
|
||||||
|
|
||||||
# Images
|
|
||||||
coil = "2.7.0"
|
|
||||||
|
|
||||||
#backend2
|
|
||||||
#Room
|
|
||||||
room = "2.6.1"
|
|
||||||
|
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
||||||
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycle" }
|
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
||||||
androidx-lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "lifecycle" }
|
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
|
||||||
|
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
|
||||||
|
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
|
||||||
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
|
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
|
||||||
|
|
||||||
# Compose BOM & UI
|
|
||||||
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
|
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
|
||||||
androidx-compose-ui = { group = "androidx.compose.ui", name = "ui" }
|
androidx-compose-ui = { group = "androidx.compose.ui", name = "ui" }
|
||||||
androidx-compose-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
|
androidx-compose-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
|
||||||
androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" } # ADDED THIS
|
androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
|
||||||
androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
|
androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
|
||||||
|
androidx-compose-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
|
||||||
|
androidx-compose-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
|
||||||
androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" }
|
androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" }
|
||||||
androidx-compose-material-icons = { group = "androidx.compose.material", name = "material-icons-extended" }
|
|
||||||
|
|
||||||
# Navigation & Hilt
|
|
||||||
androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigationCompose" }
|
|
||||||
androidx-hilt-navigation-compose = { group = "androidx.hilt", name = "hilt-navigation-compose", version.ref = "hiltNavigationCompose" }
|
|
||||||
hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" }
|
|
||||||
hilt-compiler = { group = "com.google.dagger", name = "hilt-compiler", version.ref = "hilt" }
|
|
||||||
|
|
||||||
# Misc
|
|
||||||
coil-compose = { group = "io.coil-kt", name = "coil-compose", version.ref = "coil" }
|
|
||||||
|
|
||||||
#backend2
|
|
||||||
#Room
|
|
||||||
room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" }
|
|
||||||
room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" }
|
|
||||||
room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" }
|
|
||||||
|
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||||
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
||||||
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
|
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
|
||||||
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
|
|
||||||
hilt-android = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }
|
|
||||||
|
|||||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
|||||||
#Tue Dec 09 23:28:28 EST 2025
|
#Tue Dec 09 23:28:28 EST 2025
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|||||||
Reference in New Issue
Block a user